From 399af027e24da37c5a726d50612b9e8e62897ef0 Mon Sep 17 00:00:00 2001 From: denispeplin Date: Tue, 18 Sep 2012 13:52:03 +0400 Subject: [PATCH 0001/1412] Pass tds_version to TinyTds::Client.new --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 7ef7c2ba3..72915d0c3 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -420,6 +420,7 @@ def connect :username => config[:username], :password => config[:password], :database => config[:database], + :tds_version => config[:tds_version], :appname => appname, :login_timeout => login_timeout, :timeout => timeout, From cfebf08d6a3af57e2b4585ac50350bec6b2d06d9 Mon Sep 17 00:00:00 2001 From: Michel Grootjans Date: Mon, 14 Jan 2013 13:19:44 +0100 Subject: [PATCH 0002/1412] clear_table_cache before each rename_column Fixing issue https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/237 Not sure if this is the optimal solution, but it should work. --- .../connection_adapters/sqlserver/schema_statements.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index df7e78235..f1b0612b3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -80,6 +80,7 @@ def change_column_default(table_name, column_name, default) end def rename_column(table_name, column_name, new_column_name) + connection.schema_cache.clear_table_cache!(table_name) detect_column_for! table_name, column_name do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" end From 166c64ff279a1ca530a94662fb2b720eb1f30712 Mon Sep 17 00:00:00 2001 From: Michel Grootjans Date: Mon, 14 Jan 2013 13:30:41 +0100 Subject: [PATCH 0003/1412] ignore rubymine-generated files --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 1ce5ead37..0619b84bf 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ Gemfile.lock test/profile/output/* .rvmrc .rbenv-version +.idea \ No newline at end of file From ddc11abb4c2a35591b721c4770e8d143a4fbf451 Mon Sep 17 00:00:00 2001 From: Michel Grootjans Date: Mon, 14 Jan 2013 13:33:09 +0100 Subject: [PATCH 0004/1412] bugfix --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f1b0612b3..bef24312f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -80,7 +80,7 @@ def change_column_default(table_name, column_name, default) end def rename_column(table_name, column_name, new_column_name) - connection.schema_cache.clear_table_cache!(table_name) + schema_cache.clear_table_cache!(table_name) detect_column_for! table_name, column_name do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" end From 44cdf9b6d02a5d8c30531a449ab919df74a2b10d Mon Sep 17 00:00:00 2001 From: Paula Bianca Torre Date: Thu, 28 Feb 2013 17:13:13 +0800 Subject: [PATCH 0005/1412] Update regex for `RecordNotUnique` exception Fixes issue #256 `TinyTds::Error: Violation of UNIQUE KEY constraint ''. Cannot insert duplicate key in object ...` does not match the regex for `RecordNotUnique` exception. Signed-off-by: Paula Bianca Torre --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index fe48a8c64..5367f8876 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -389,7 +389,7 @@ def cs_equality_operator def translate_exception(e, message) case message - when /cannot insert duplicate key .* with unique index/i + when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i RecordNotUnique.new(message,e) when /conflicted with the foreign key constraint/i InvalidForeignKey.new(message,e) From 9eebe95a862b7b19cea1db019e61da717ef1be9c Mon Sep 17 00:00:00 2001 From: Matt Jarjoura Date: Wed, 3 Apr 2013 13:39:41 -0700 Subject: [PATCH 0006/1412] sqlserver_adapter: fix regex matching to catch if running on Azure --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index fe48a8c64..a1437eb10 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -203,7 +203,7 @@ def initialize(connection, logger, pool, config) connect @database_version = select_value 'SELECT @@version', 'SCHEMA' @database_year = begin - if @database_version =~ /Microsoft SQL Azure/i + if @database_version =~ /Azure/i @sqlserver_azure = true @database_version.match(/\s(\d{4})\s/)[1].to_i else From c21f97f378fd568cabd0e62b55cb51cb66a4ba9a Mon Sep 17 00:00:00 2001 From: Matt Jarjoura Date: Wed, 3 Apr 2013 13:48:08 -0700 Subject: [PATCH 0007/1412] sqlserver_adapter: fix azure again --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a1437eb10..c7e728bed 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -205,7 +205,8 @@ def initialize(connection, logger, pool, config) @database_year = begin if @database_version =~ /Azure/i @sqlserver_azure = true - @database_version.match(/\s(\d{4})\s/)[1].to_i + @database_version.match(/\s-\s([0-9.]+)/)[1] + year = 2012 else year = DATABASE_VERSION_REGEXP.match(@database_version)[1] year == "Denali" ? 2011 : year.to_i @@ -218,7 +219,7 @@ def initialize(connection, logger, pool, config) @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' initialize_dateformatter use_database - unless SUPPORTED_VERSIONS.include?(@database_year) + unless (@sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year)) raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." end end From b5e974f17635f7a3c5db4e7dec68c8498e802086 Mon Sep 17 00:00:00 2001 From: Matt Jarjoura Date: Sun, 21 Apr 2013 19:29:27 -0700 Subject: [PATCH 0008/1412] https://github.com/noel/activerecord-sqlserver-adapter/commit/c3b517178429296a329c6bfd4ddad855b998af2d --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c7e728bed..7b38c369e 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -434,6 +434,7 @@ def connect client.execute("SET ANSI_PADDING ON").do client.execute("SET QUOTED_IDENTIFIER ON") client.execute("SET ANSI_WARNINGS ON").do + client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do else client.execute("SET ANSI_DEFAULTS ON").do client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do From ef7400897f5b3022d5628137967f72a83f2b79cc Mon Sep 17 00:00:00 2001 From: Matt Jarjoura Date: Wed, 1 May 2013 14:56:42 -0700 Subject: [PATCH 0009/1412] sqlserver_adapter: Let's attempt to reconnect immediately, the 2 second timeout is actually a sore spot here --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 7b38c369e..497671e15 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -522,9 +522,10 @@ def auto_reconnected? @auto_connecting = true count = 0 while count <= (auto_connect_duration / 2) - sleep 2** count + result = reconnect! ActiveRecord::Base.did_retry_sqlserver_connection(self,count) - return true if reconnect! + return true if result + sleep 2** count count += 1 end ActiveRecord::Base.did_lose_sqlserver_connection(self) From 99535ace84151791669887f2ce19e6294dc3e63e Mon Sep 17 00:00:00 2001 From: raydog153 Date: Sun, 23 Jun 2013 14:43:44 -0400 Subject: [PATCH 0010/1412] ActiveRecord::ConnectionAdaptors::ConnectionSpecification::Resolver.spec now rejects any blank config options --- test/cases/resolver_test_sqlserver.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index 8437394cd..4e0b7faad 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -3,11 +3,11 @@ module ActiveRecord class Base class ConnectionSpecification - + class ResolverTest < ActiveRecord::TestCase - + include SqlserverCoercedTest - + COERCED_TESTS = [ :test_url_host_no_db, :test_url_host_db, @@ -18,7 +18,6 @@ def test_coerced_test_url_host_no_db spec = resolve 'sqlserver://foo?encoding=utf8' assert_equal({ :adapter => "sqlserver", - :database => "", :host => "foo", :encoding => "utf8" }, spec) end @@ -36,13 +35,12 @@ def test_coerced_test_url_port spec = resolve 'sqlserver://foo:123?encoding=utf8' assert_equal({ :adapter => "sqlserver", - :database => "", :port => 123, :host => "foo", :encoding => "utf8" }, spec) end end - + end end end From 15b753a369121a3b49bfe30becbf8b9111d25393 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Tue, 25 Jun 2013 23:15:32 -0400 Subject: [PATCH 0011/1412] Fixing typo and mocha warning when running tests --- test/cases/sqlserver_helper.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index f5e089c08..89ce7b9ec 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -13,7 +13,7 @@ require 'bundler' Bundler.setup require 'shoulda' -require 'mocha' +require 'mocha/setup' require 'active_support/dependencies' require 'active_record' require 'active_record/version' @@ -27,7 +27,7 @@ ActiveRecord::Base.logger = Logger.new(File.expand_path(File.join(SQLSERVER_TEST_ROOT,'debug.log'))) ActiveRecord::Base.logger.level = 0 -# Defining our classes in one place as well as soem core tests that need coercing date/time types. +# Defining our classes in one place as well as some core tests that need coercing date/time types. class UpperTestDefault < ActiveRecord::Base ; self.table_name = 'UPPER_TESTS' ; end class UpperTestLowered < ActiveRecord::Base ; self.table_name = 'upper_tests' ; end @@ -105,14 +105,14 @@ def method_added(method) module ActiveRecord class SQLCounter - self.ignored_sql.concat([ + self.ignored_sql.concat([ %r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)}, %r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION} ]) end end -module ActiveRecord +module ActiveRecord class TestCase < ActiveSupport::TestCase class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end From e949bea4daf208db536471294e2d6d1af26ceef9 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 07:27:51 -0400 Subject: [PATCH 0012/1412] Fixing change_column logic that was causing two unit tests to fail --- .../sqlserver/schema_statements.rb | 75 +++++++++++-------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index df7e78235..8cbf97b21 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -2,7 +2,7 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module SchemaStatements - + def native_database_types @native_database_types ||= initialize_native_database_types.freeze end @@ -43,11 +43,11 @@ def columns(table_name, name = nil) SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options end end - + def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" end - + def remove_column(table_name, *column_names) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? ActiveSupport::Deprecation.warn 'Passing array to remove_columns is deprecated, please use multiple arguments, like: `remove_columns(:posts, :foo, :bar)`', caller if column_names.flatten! @@ -61,16 +61,25 @@ def remove_column(table_name, *column_names) def change_column(table_name, column_name, type, options = {}) sql_commands = [] + indexes = [] column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s } - change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - change_column_sql << " NOT NULL" if options[:null] == false - sql_commands << change_column_sql + if options_include_default?(options) || (column_object && column_object.type != type.to_sym) - remove_default_constraint(table_name,column_name) + remove_default_constraint(table_name,column_name) + indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) } + remove_indexes(table_name, column_name) end + sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(options[:default])} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + sql_commands[-1] << " NOT NULL" if !options[:null].nil? && options[:null] == false if options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" end + + #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.collect {|c|quote_column_name(c)}.join(', ')})" + end sql_commands.each { |c| do_execute(c) } end @@ -83,7 +92,7 @@ def rename_column(table_name, column_name, new_column_name) detect_column_for! table_name, column_name do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" end - + def remove_index!(table_name, index_name) do_execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end @@ -104,27 +113,27 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) end end - def change_column_null(table_name, column_name, null, default = nil) + def change_column_null(table_name, column_name, allow_null, default = nil) column = detect_column_for! table_name, column_name - unless null || default.nil? + if !allow_null.nil? && allow_null == false && !default.nil? do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}" - sql << " NOT NULL" unless null + sql << " NOT NULL" if !allow_null.nil? && allow_null == false do_execute sql end - + # === SQLServer Specific ======================================== # - + def views tables('VIEW') end - - + + protected - + # === SQLServer Specific ======================================== # - + def initialize_native_database_types { :primary_key => "int NOT NULL IDENTITY(1,1) PRIMARY KEY", @@ -156,7 +165,7 @@ def column_definitions(table_name) table_schema = Utils.unqualify_table_schema(table_name) table_name = Utils.unqualify_table_name(table_name) sql = %{ - SELECT DISTINCT + SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, #{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name, columns.DATA_TYPE AS type, @@ -172,7 +181,7 @@ def column_definitions(table_name) WHEN columns.IS_NULLABLE = 'YES' THEN 1 ELSE NULL END AS [is_nullable], - CASE + CASE WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1 ELSE NULL END AS [is_primary], @@ -240,7 +249,7 @@ def column_definitions(table_name) ci end end - + def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA' constraints.each do |constraint| @@ -262,9 +271,9 @@ def remove_indexes(table_name, column_name) remove_index(table_name, {:name => index.name}) end end - + # === SQLServer Specific (Misc Helpers) ========================= # - + def get_table_name(sql) if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i $2 || $3 @@ -274,29 +283,29 @@ def get_table_name(sql) nil end end - + def default_constraint_name(table_name, column_name) "DF_#{table_name}_#{column_name}" end - + def detect_column_for!(table_name, column_name) unless column = schema_cache.columns[table_name].detect { |c| c.name == column_name.to_s } raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" end column end - + def lowercase_schema_reflection_sql(node) lowercase_schema_reflection ? "LOWER(#{node})" : node end - + # === SQLServer Specific (View Reflection) ====================== # - + def view_table_name(table_name) view_info = schema_cache.view_information(table_name) view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name end - + def view_information(table_name) table_name = Utils.unqualify_table_name(table_name) view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'", 'SCHEMA' @@ -313,18 +322,18 @@ def view_information(table_name) end view_info end - + def table_name_or_views_table_name(table_name) unquoted_table_name = Utils.unqualify_table_name(table_name) schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name end - + def views_real_column_name(table_name,column_name) view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name end - + # === SQLServer Specific (Identity Inserts) ===================== # def query_requires_identity_insert?(sql) @@ -336,11 +345,11 @@ def query_requires_identity_insert?(sql) false end end - + def insert_sql?(sql) !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? end - + def with_identity_insert_enabled(table_name) table_name = quote_table_name(table_name_or_views_table_name(table_name)) set_identity_insert(table_name, true) From 457af60efad53303cd7f26df09ea5e7487c07e6c Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 08:18:32 -0400 Subject: [PATCH 0013/1412] Fixing binary_to_string conversion for non UTF-8 strings --- .../connection_adapters/sqlserver_adapter.rb | 181 +++++++++--------- 1 file changed, 92 insertions(+), 89 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index fe48a8c64..990e4158d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -19,9 +19,9 @@ require 'active_record/connection_adapters/sqlserver/utils' module ActiveRecord - + class Base - + def self.sqlserver_connection(config) #:nodoc: config = config.symbolize_keys config.reverse_merge! :mode => :dblib @@ -38,21 +38,21 @@ def self.sqlserver_connection(config) #:nodoc: end ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(:mode=>mode)) end - + protected - + def self.did_retry_sqlserver_connection(connection,count) logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}." end - + def self.did_lose_sqlserver_connection(connection) logger.info "CONNECTION LOST: #{connection.class.name}" end - + end - + module ConnectionAdapters - + class SQLServerColumn < Column def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {}) @@ -60,39 +60,42 @@ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = { super(name, default, sql_type, null) @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary] end - + class << self - + def string_to_binary(value) "0x#{value.unpack("H*")[0]}" end - + def binary_to_string(value) + if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT + value = value.force_encoding(Encoding::ASCII_8BIT) + end value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*') end - + end - + def is_identity? @sqlserver_options[:is_identity] end - + def is_primary? @sqlserver_options[:is_primary] end - + def is_utf8? !!(@sql_type =~ /nvarchar|ntext|nchar/i) end - + def is_integer? !!(@sql_type =~ /int/i) end - + def is_real? !!(@sql_type =~ /real/i) end - + def sql_type_for_statement if is_integer? || is_real? sql_type.sub(/\((\d+)?\)/,'') @@ -100,15 +103,15 @@ def sql_type_for_statement sql_type end end - + def default_function @sqlserver_options[:default_function] end - + def table_name @sqlserver_options[:table_name] end - + def table_klass @table_klass ||= begin table_name.classify.constantize @@ -117,14 +120,14 @@ def table_klass end (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil end - + def database_year @sqlserver_options[:database_year] end - - + + private - + def extract_limit(sql_type) case sql_type when /^smallint/i @@ -139,7 +142,7 @@ def extract_limit(sql_type) super end end - + def simplified_type(field_type) case field_type when /real/i then :float @@ -153,7 +156,7 @@ def simplified_type(field_type) else super end end - + def simplified_datetime if database_year >= 2008 :datetime @@ -165,33 +168,33 @@ def simplified_datetime :datetime end end - + end #class SQLServerColumn - + class SQLServerAdapter < AbstractAdapter - + include Sqlserver::Quoting include Sqlserver::DatabaseStatements include Sqlserver::Showplan include Sqlserver::SchemaStatements include Sqlserver::DatabaseLimits include Sqlserver::Errors - + VERSION = File.read(File.expand_path("../../../../VERSION",__FILE__)).strip ADAPTER_NAME = 'SQLServer'.freeze DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ SUPPORTED_VERSIONS = [2005,2008,2010,2011,2012] - + attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition - + cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, :enable_default_unicode_types, :auto_connect, :retry_deadlock_victim, :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration, :showplan_option - + self.enable_default_unicode_types = true - - + + def initialize(connection, logger, pool, config) super(connection, logger, pool) # AbstractAdapter Responsibility @@ -222,54 +225,54 @@ def initialize(connection, logger, pool, config) raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." end end - + # === Abstract Adapter ========================================== # - + def adapter_name ADAPTER_NAME end - + def supports_migrations? true end - + def supports_primary_key? true end - + def supports_count_distinct? true end - + def supports_ddl_transactions? true end - + def supports_bulk_alter? false end - + def supports_savepoints? true end - + def supports_index_sort_order? true end - + def supports_explain? true end - + def disable_referential_integrity do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" yield ensure do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" end - + # === Abstract Adapter (Connection Management) ================== # - + def active? case @connection_options[:mode] when :dblib @@ -296,13 +299,13 @@ def disconnect! @connection.disconnect rescue nil end end - + def reset! remove_database_connections_and_rollback { } end - + # === Abstract Adapter (Misc Support) =========================== # - + def pk_and_sequence_for(table_name) idcol = identity_column(table_name) idcol ? [idcol.name,nil] : nil @@ -311,82 +314,82 @@ def pk_and_sequence_for(table_name) def primary_key(table_name) identity_column(table_name).try(:name) || schema_cache.columns[table_name].detect(&:is_primary?).try(:name) end - + # === SQLServer Specific (DB Reflection) ======================== # - + def sqlserver? true end - + def sqlserver_2005? @database_year == 2005 end - + def sqlserver_2008? @database_year == 2008 end - + def sqlserver_2011? @database_year == 2011 end - + def sqlserver_2012? @database_year == 2012 end - + def sqlserver_azure? @sqlserver_azure end - + def version self.class::VERSION end - + def inspect "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" end - + def auto_connect @@auto_connect.is_a?(FalseClass) ? false : true end - + def auto_connect_duration @@auto_connect_duration ||= 10 end - + def retry_deadlock_victim @@retry_deadlock_victim.is_a?(FalseClass) ? false : true end alias :retry_deadlock_victim? :retry_deadlock_victim - + def native_string_database_type - @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') + @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') end - + def native_text_database_type @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' end - + def native_time_database_type sqlserver_2005? ? 'datetime' : 'time' end - + def native_date_database_type sqlserver_2005? ? 'datetime' : 'date' end - + def native_binary_database_type @@native_binary_database_type || 'varbinary(max)' end - + def cs_equality_operator @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' end - + protected - + # === Abstract Adapter (Misc Support) =========================== # - + def translate_exception(e, message) case message when /cannot insert duplicate key .* with unique index/i @@ -401,9 +404,9 @@ def translate_exception(e, message) super end end - + # === SQLServer Specific (Connection Management) ================ # - + def connect config = @connection_options @connection = case config[:mode] @@ -412,7 +415,7 @@ def connect login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil timeout = config[:timeout].present? ? config[:timeout].to_i/1000 : nil encoding = config[:encoding].present? ? config[:encoding] : nil - TinyTds::Client.new({ + TinyTds::Client.new({ :dataserver => config[:dataserver], :host => config[:host], :port => config[:port], @@ -449,7 +452,7 @@ def connect ODBC::Database.new.drvconnect(driver) else ODBC.connect config[:dsn], config[:username], config[:password] - end.tap do |c| + end.tap do |c| begin c.use_time = true c.use_utc = ActiveRecord::Base.default_timezone == :utc @@ -463,20 +466,20 @@ def connect rescue raise unless @auto_connecting end - + # Override this method so every connection can be configured to your needs. - # For example: + # For example: # raw_connection_do "SET TEXTSIZE #{64.megabytes}" # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" def configure_connection end - + # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. # For example: # "myapp_#{$$}_#{Thread.current.object_id}".to(29) def configure_application_name end - + def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a @@ -485,7 +488,7 @@ def initialize_dateformatter ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat end - + def remove_database_connections_and_rollback(database=nil) database ||= current_database do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" @@ -495,7 +498,7 @@ def remove_database_connections_and_rollback(database=nil) do_execute "ALTER DATABASE #{quote_table_name(database)} SET MULTI_USER" end if block_given? end - + def with_sqlserver_error_handling begin yield @@ -507,14 +510,14 @@ def with_sqlserver_error_handling raise end end - + def disable_auto_reconnect old_auto_connect, self.class.auto_connect = self.class.auto_connect, false yield ensure self.class.auto_connect = old_auto_connect end - + def auto_reconnected? return false unless auto_connect @auto_connecting = true @@ -530,10 +533,10 @@ def auto_reconnected? ensure @auto_connecting = false end - + end #class SQLServerAdapter < AbstractAdapter - + end #module ConnectionAdapters - + end #module ActiveRecord From 9df9d82ccf1f254f9021201e877981eabfbd68d8 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 08:28:41 -0400 Subject: [PATCH 0014/1412] Adding in missing development gem ruby-prof, updating mocha, updating version information --- Gemfile | 6 ++++-- RUNNING_UNIT_TESTS.md | 2 +- VERSION | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 20be78a73..31024aee8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,11 +1,12 @@ -source :rubygems +source 'https://rubygems.org' if ENV['RAILS_SOURCE'] gemspec :path => ENV['RAILS_SOURCE'] else version = ENV['RAILS_VERSION'] || begin require 'net/http' + require 'yaml' spec = eval(File.read('activerecord-sqlserver-adapter.gemspec')) version = spec.dependencies.detect{ |d|d.name == 'activerecord' }.requirement.requirements.first.last.version major, minor, tiny = version.split('.') @@ -38,9 +39,10 @@ group :development do gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'm' - gem 'mocha', '0.9.8' + gem 'mocha', '0.14.0' gem 'nokogiri' gem 'rake', '~> 0.9.2' gem 'shoulda', '2.10.3' + gem 'ruby-prof' end diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index edba6e96a..b9d87191e 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -39,7 +39,7 @@ $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git Optionally, you an just let bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. ``` -$ export RAILS_VERSION='3.1.1' +$ export RAILS_VERSION='3.2.13' $ bundle install ``` diff --git a/VERSION b/VERSION index d6bb32f36..d883a1005 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.10 \ No newline at end of file +3.2.13 From 9329a3a3b1a928b0a1ad97598e915001bf7c7322 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 08:31:34 -0400 Subject: [PATCH 0015/1412] Fixing generating profile report to create output dir if needed, and code change for printing report --- test/profile/helper.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/profile/helper.rb b/test/profile/helper.rb index 58fd5584f..b0a2371cc 100644 --- a/test/profile/helper.rb +++ b/test/profile/helper.rb @@ -2,17 +2,17 @@ require 'ruby-prof' class ActiveRecord::TestCase - - + + protected - + def ruby_profile(name) result = RubyProf.profile { yield } [:flat,:graph,:html].each do |printer| save_ruby_prof_report(result, name, printer) end end - + def save_ruby_prof_report(result, name, printer) ptr = case printer when :flat then RubyProf::FlatPrinter @@ -21,9 +21,10 @@ def save_ruby_prof_report(result, name, printer) end file_name = printer == :html ? "#{name}_graph.html" : "#{name}_#{printer}.txt" file_path = File.join(SQLSERVER_TEST_ROOT, 'profile', 'output', file_name) + Dir.mkdir(File.join(SQLSERVER_TEST_ROOT, 'profile', 'output')) unless File.exists?(File.join(SQLSERVER_TEST_ROOT, 'profile', 'output')) File.open(file_path,'w') do |file| - printer == :html ? ptr.new(result).print(file) : ptr.new(result).print(file,0) + printer == :html ? ptr.new(result).print(file) : ptr.new(result).print(file,{}) end end - + end From efeffccbece81852ce63a2b70883c12c91a1f262 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 08:33:09 -0400 Subject: [PATCH 0016/1412] Fixing explain not capturing queries for explain support, and updating change log. --- CHANGELOG | 151 ++++++++++-------- .../sqlserver/core_ext/explain_subscriber.rb | 26 +++ .../connection_adapters/sqlserver_adapter.rb | 1 + 3 files changed, 109 insertions(+), 69 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb diff --git a/CHANGELOG b/CHANGELOG index 45f7cfcf8..a50b43beb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,17 @@ +* 3.2.13 * + +* Fixed teo unit tests due to changes in ActiveRecord that removes blank config values. +* Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex +* Fixed change_column to update existing table column rows with new default value if there are any NULL values + and the column does not accept nulls +* Fixed change_column to drop and add indexes if the colun type is changes +* Fixed string_to_binary and binary_to_string in some cases where the binary data is not UTF-8 +* Fixing generating profile report to create output dir if needed, and code change for printing report +* Adding ruby-prof to Gemfile, needed when running profile test cases +* Updating mocha to work with newer ActiveRecord test cases + + * 3.2.10 * * Remove connection defaults for host/username/password. Since we want to suppoert Windows Authentication @@ -67,7 +80,7 @@ * 3.2.0 * -* ActiveRecord explain (SHOWPLAN) support. +* ActiveRecord explain (SHOWPLAN) support. http://youtu.be/ckb3YYZZZ2Q * Remove our log_info_schema_queries config since we are not hooking properly into AR's 'SCHEMA' names. @@ -86,7 +99,7 @@ * 3.1.4 * -* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed. +* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed. Fixes #125. [Wüthrich Hannes @hwuethrich] * New deadlock victim retry using the #retry_deadlock_victim config. [Jason Frey, Joe Rafaniello] @@ -98,10 +111,10 @@ * Add methods for sqlserver's #product_version, #product_level, #edition and include them in inspect. Fixes #145 [Jason Frey, Joe Rafaniello] - -* Handle statements that cannot be retried on a new database connection by not reconnecting. + +* Handle statements that cannot be retried on a new database connection by not reconnecting. Fixes #147 [Jason Frey, Joe Rafaniello] - + * Added connection#spid for debugging. Fixes #144 [Jason Frey, Joe Rafaniello] * Add ENV['TEST_FILES'] to Rakefile for easy single case tests. [Jason Frey, Joe Rafaniello] @@ -117,7 +130,7 @@ * 3.1.3 * -* Distinguish between identity and primary key key columns during schema reflection. Allows us +* Distinguish between identity and primary key key columns during schema reflection. Allows us us to only do identity inserts when technically needed. Fixes #139 [chadcf] & [joncanady] @@ -138,7 +151,7 @@ * Make #rollback_db_transaction smarter. -* Provide a method to override for the quoted string prefix. Not a config because trumping this method will +* Provide a method to override for the quoted string prefix. Not a config because trumping this method will have drastically bad results. Fixes #124 * Allow :limit/:offset to be used with fully qualified table and column in :select. @@ -151,12 +164,12 @@ * Make auto reconnect duration configurable. Fixes #109 [David Chelimsky] * Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is - automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time + automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time ActiveSupport formatter that is used per the language settings of the connection. * Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833 - + * Allow drop_database to be called even when DB does not exist. * Remove totally broken ADONET connection mode. Want it back, submit a patch. @@ -218,7 +231,7 @@ * Support for ActiveRecord v3.0.3 and ARel v2.0.7 -* 3.0.7 * +* 3.0.7 * * Properly quote table names when reflecting on views. @@ -258,7 +271,7 @@ * 3.0.1 -* Support DSN'less connections. Resolves ticket 38. +* Support DSN'less connections. Resolves ticket 38. * Support upcoming ruby odbc 0.99992 @@ -289,14 +302,14 @@ * 2.3.5 -* Initial IronRuby ADONET connection mode support baked right in. Removed most &block - parameters, no handle/request object yielded anymore. Better abstraction and compliance - per the ActiveRecord abstract adapter to not yielding handles for #execute and only for - low level #select. Better wrapping of all queries at lowest level in #log so exceptions - at anytime can be handled correctly by core AR. Critical for System::Data's command - readers. Better abstraction for introspecting on #connection_mode. Added support for +* Initial IronRuby ADONET connection mode support baked right in. Removed most &block + parameters, no handle/request object yielded anymore. Better abstraction and compliance + per the ActiveRecord abstract adapter to not yielding handles for #execute and only for + low level #select. Better wrapping of all queries at lowest level in #log so exceptions + at anytime can be handled correctly by core AR. Critical for System::Data's command + readers. Better abstraction for introspecting on #connection_mode. Added support for running singular test cases via TextMate's Command-R. [Ken Collins] - + * Force a binary encoding on values coming in and out of those columns for ruby 1.9. Fixes ticket #33 [Jeroen Zwartepoorte] @@ -315,22 +328,22 @@ * For tables that named with schema(ex. rails.users), they could not get length of column. column of varchar(40) gets length => nil. Ticket #27 & #15 [Ken Tachiya] -* Altered limited_update_conditions regex conditions, the .* would greedily fail +* Altered limited_update_conditions regex conditions, the .* would greedily fail if the where_sql had WHERE in a table or field, etc. [Ransom Briggs] -* Changing test to allow ENV['ARUNIT_DB_NAME'] as the database name for the test units. +* Changing test to allow ENV['ARUNIT_DB_NAME'] as the database name for the test units. Matches up with AR conventions. [Ransom Briggs] 2.3.3 - -* Revert #ad83df82 and again cache column information at the connection's instance. The + +* Revert #ad83df82 and again cache column information at the connection's instance. The previous commit was causing all sorts of view and schema reflection problems. [Ken Collins] 2.3.2 -* Insert queries that include the word "insert" as a partial column name with the word +* Insert queries that include the word "insert" as a partial column name with the word "id" as a value were falsely being matched as identity inserts. [Sean Caffery/bfabry] * Delegate all low level #raw_connection calls to #raw_connection_run and #raw_connection_do @@ -348,10 +361,10 @@ * Coerce a few tests that were failing in 2.3.x [Ken Collins] * Change column/view cache to happen at class level. Allows connection pool to share same - caches as well as the ability to expire the caches when needed. Also fix change_column so + caches as well as the ability to expire the caches when needed. Also fix change_column so that exceptions are not raised when the column contains an existing default. [Ken Collins] -* Allow query_requires_identity_insert? method to return quoted table name in situations where the +* Allow query_requires_identity_insert? method to return quoted table name in situations where the INSERT parts are not quoted themselves. [Gary/iawgens, Richard Penwell, Ken Collins] * Fixed namespace in calling test_sqlserver_odbc within test_unicode_types. [Gary/iawgens] @@ -363,7 +376,7 @@ * Support Identity-key-column judgement on multiple schema environment [Ken Tachiya] -* Add support for tinyint data types. In MySQL all these types would be boolean, however in +* Add support for tinyint data types. In MySQL all these types would be boolean, however in our adapter, they will use the full 1 => 255 Fixnum value as you would expect. [Ken Collins] @@ -381,10 +394,10 @@ * Implement a new remove_default_constraint method that uses sp_helpconstraint [Ken Collins] -* Use a lazy match in add_order_by_for_association_limiting! to allow sub selects to be used. Resolves +* Use a lazy match in add_order_by_for_association_limiting! to allow sub selects to be used. Resolves ticket #11. -* Add default rake task back for testing. Runs the namespaced sqlserver:test_sqlserver_odbc. +* Add default rake task back for testing. Runs the namespaced sqlserver:test_sqlserver_odbc. Resolves ticket #10 [Ken Collins] * Default value detection in column_definitions is kinder to badly formatted, or long winded user @@ -397,7 +410,7 @@ * Leave quoted column names as is. Resolves ticket #36 [Vince Puzzella] -* Changing add_limit! in ActiveRecord::Base for SQLServer so that it passes through any scoped :order +* Changing add_limit! in ActiveRecord::Base for SQLServer so that it passes through any scoped :order parameters. Resolves ticket #35 [Murray Steele] @@ -410,45 +423,45 @@ * 2.2.17 * (May 14th, 2009) -* Add simplified type recognition for varchar(max) and nvarchar(max) under SQL Server 2005 to be a +* Add simplified type recognition for varchar(max) and nvarchar(max) under SQL Server 2005 to be a :text type. This ensures schema dumper does the right thing. Fixes ticket #30. [Ken Collins] -* Tested ruby 1.9, ruby-odbc 0.9996, and DBI 0.4.1. Also added correct support for UTF-8 character - encoding going in and out of the DB. See before gist http://gist.github.com/111709 and after gist +* Tested ruby 1.9, ruby-odbc 0.9996, and DBI 0.4.1. Also added correct support for UTF-8 character + encoding going in and out of the DB. See before gist http://gist.github.com/111709 and after gist http://gist.github.com/111719 [Ken Collins] * 2.2.16 * (April 21st, 2009) -* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests - to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs +* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests + to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs for deep sub selects in paginated results. [Ken Collins] -* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through - the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success - or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins] +* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through + the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success + or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins] -* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This +* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This resolves ticket #26 [Ken Collins] -* Provide support for running queries at different isolation levels using #run_with_isolation_level method - that can take a block or not. Also implement a #user_options method that reflects on the current user +* Provide support for running queries at different isolation levels using #run_with_isolation_level method + that can take a block or not. Also implement a #user_options method that reflects on the current user session values. Resolves #20 [Murray Steele] * 2.2.15 * (March 23rd, 2009) -* Better add_lock! method that can add the lock to just about all the elements in the statement. This - could be eager loaded associations, joins, etc. Done so that paginated results can easily add lock - options for performance. Note, the tally count in add_limit_offset! use "WITH (NOLOCK)" explicitly +* Better add_lock! method that can add the lock to just about all the elements in the statement. This + could be eager loaded associations, joins, etc. Done so that paginated results can easily add lock + options for performance. Note, the tally count in add_limit_offset! use "WITH (NOLOCK)" explicitly as it can not hurt and is needed. [Ken Collins] * 2.2.14 * (March 17th, 2009) -* Rails2.3 - Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecord - version strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSqlserver use Ship vs - Bird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) Previous +* Rails2.3 - Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecord + version strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSqlserver use Ship vs + Bird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) Previous JoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] * Rails2.3 - Implement new savepoint support [Ken Collins] @@ -460,24 +473,24 @@ * Rails2.3 - Implement a custom ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation#aliased_table_name_for method that uses a Regexp.escape so that table/column quoting does not get ignored. [Ken Collins] -* Rails2.3 - Implement #outside_transaction? and a new transaction test case to test some SQL Server +* Rails2.3 - Implement #outside_transaction? and a new transaction test case to test some SQL Server basic support while implementing this method. Future home of some savepoint tests too. [Ken Collins] -* Rails2.3 - Coerced tests that ensure hash conditions on referenced tables are considered when eager +* Rails2.3 - Coerced tests that ensure hash conditions on referenced tables are considered when eager loading with limit/offset. Information on these changes and the ticket in rails are. http://github.com/rails/rails/commit/9a4d557713acb0fc8e80f61af18094034aca029a http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1404-conditions_tables-doesnt-understand-condition-hashes * Rails2.3 - Add coerced tests for true/false attributes in selects use SQL Server case statement. [Ken Collins] -* Making sure that smalldatetime types are OK to use. Also fixed a bug in the #view_information method that - checks to see if a view definition is equal to 4000 chars, meaning that it is most likely truncated and +* Making sure that smalldatetime types are OK to use. Also fixed a bug in the #view_information method that + checks to see if a view definition is equal to 4000 chars, meaning that it is most likely truncated and needs to use the backup method of sp_helptext to get it's view definition. [Ken Collins] * 2.2.13 * (February 10th, 2009) -* Update #indexes to use unqualified table name. Fixes cases where users may decide to use table +* Update #indexes to use unqualified table name. Fixes cases where users may decide to use table name prefixes like 'dbo.'. [Ken Collins] @@ -493,11 +506,11 @@ * 2.2.9 * (January 22nd, 2009) -* Fixing a small bug in the deprecated DBI::Timestamp conversion so it correctly converts nanosecond whole - numbers to back to pre type cast SQL Server milliseconds, ultimately allow ruby's Time#usec which is +* Fixing a small bug in the deprecated DBI::Timestamp conversion so it correctly converts nanosecond whole + numbers to back to pre type cast SQL Server milliseconds, ultimately allow ruby's Time#usec which is microseconds to be correct. [Ken Collins] - -* Sometimes views are more than 4000 chars long and will return NULL for the VIEW_DEFINITION. If so, use + +* Sometimes views are more than 4000 chars long and will return NULL for the VIEW_DEFINITION. If so, use sp_helptext procedure as a backup method. [Ken Collins] @@ -508,13 +521,13 @@ * 2.2.7 (January 9th, 2009) -* Created a connection#execute_procedure method that takes can take any number of ruby objects as variables - and quotes them according to the connection's rules. Also added an ActiveRecord::Base class level core - extension that hooks into this. It also checks if the connection responds to #execute_procedure and if +* Created a connection#execute_procedure method that takes can take any number of ruby objects as variables + and quotes them according to the connection's rules. Also added an ActiveRecord::Base class level core + extension that hooks into this. It also checks if the connection responds to #execute_procedure and if not returns an empty array. [Ken Collins] -* Added a #enable_default_unicode_types class attribute access to make all new added or changed string types - like :string/:text default to unicode/national data types. See the README for full details. Added a rake +* Added a #enable_default_unicode_types class attribute access to make all new added or changed string types + like :string/:text default to unicode/national data types. See the README for full details. Added a rake task that assists setting this to true when running tests. [Ken Collins] @@ -525,15 +538,15 @@ * 2.2.5 (January 4th, 2009) -* Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent by +* Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent by default. [Ken Collins] * Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server milliseconds - and applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SqlserverTimestamp - class to parse the before type cast value appropriately. Also update the adapters #quoted_date method + and applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SqlserverTimestamp + class to parse the before type cast value appropriately. Also update the adapters #quoted_date method to work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] -* Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Now +* Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Now this adapter is compatible for using with other adapters. [Ken Collins] @@ -544,9 +557,9 @@ * 2.2.3 (December 5th, 2008) -* Changing back to using real table name in column_definitions. Makes sure views get back only the columns - that are defined for them with correct names, etc. Now supporting views by looking for NULL default and - then if table name is a view, perform a targeted with sub select to the real table name and column name +* Changing back to using real table name in column_definitions. Makes sure views get back only the columns + that are defined for them with correct names, etc. Now supporting views by looking for NULL default and + then if table name is a view, perform a targeted with sub select to the real table name and column name to find true default. [Ken Collins] * Ensure that add_limit_offset! does not alter sub queries. [Erik Bryn] @@ -561,10 +574,10 @@ 2.2.1 (November 25th, 2008) -* Add identity insert support for views. Cache #views so that identity #table_name_or_views_table_name +* Add identity insert support for views. Cache #views so that identity #table_name_or_views_table_name will run quickly. [Ken Collins] -* Add views support. ActiveRecord classes can use views. The connection now has a #views method and +* Add views support. ActiveRecord classes can use views. The connection now has a #views method and #table_exists? will now fall back to checking views too. [Ken Collins] diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb new file mode 100644 index 000000000..2e3db70b3 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -0,0 +1,26 @@ +module ActiveRecord + module ConnectionAdapters + module Sqlserver + module CoreExt + class ExplainSubscriber + def call(*args) + if queries = Thread.current[:available_queries_for_explain] + payload = args.last + queries << payload.values_at(:sql, :binds) unless ignore_sqlserver_payload?(payload) + end + end + + IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) + SQLSERVER_EXPLAINED_SQLS = /(select|update|delete|insert)/i + + # Need to modify the regex for the TSQL generated by this adapter so we can explain the proper sql statements + def ignore_sqlserver_payload?(payload) + payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ SQLSERVER_EXPLAINED_SQLS + end + + ActiveSupport::Notifications.subscribe("sql.active_record", new) + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 990e4158d..e2f3a672a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -8,6 +8,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/database_statements' require 'active_record/connection_adapters/sqlserver/core_ext/explain' +require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/relation' require 'active_record/connection_adapters/sqlserver/database_limits' require 'active_record/connection_adapters/sqlserver/database_statements' From de8347c1b975ff9e4ccb42cfb7ab3267cee1fee8 Mon Sep 17 00:00:00 2001 From: raydog153 Date: Wed, 26 Jun 2013 13:19:21 -0400 Subject: [PATCH 0017/1412] Fixing typo --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index a50b43beb..c138dd9ad 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,7 +1,7 @@ * 3.2.13 * -* Fixed teo unit tests due to changes in ActiveRecord that removes blank config values. +* Fixed the unit tests due to changes in ActiveRecord that removes blank config values. * Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex * Fixed change_column to update existing table column rows with new default value if there are any NULL values and the column does not accept nulls From 2fe65537fded6317df75a4269a8a19d78c76f8d9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 11:17:47 -0400 Subject: [PATCH 0018/1412] Test TinyTDS 0.6.0. --- CHANGELOG | 3 ++- Gemfile | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c138dd9ad..5b0967957 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,11 @@ * 3.2.13 * +* Test TinyTDS 0.6.0. * Fixed the unit tests due to changes in ActiveRecord that removes blank config values. * Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex * Fixed change_column to update existing table column rows with new default value if there are any NULL values - and the column does not accept nulls + and the column does not accept nulls * Fixed change_column to drop and add indexes if the colun type is changes * Fixed string_to_binary and binary_to_string in some cases where the binary data is not UTF-8 * Fixing generating profile report to create output dir if needed, and code change for printing report diff --git a/Gemfile b/Gemfile index 31024aee8..0c809c118 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ group :tinytds do if ENV['TINYTDS_SOURCE'] gem 'tiny_tds', :path => ENV['TINYTDS_SOURCE'] else - gem 'tiny_tds', '0.5.1' + gem 'tiny_tds', '~> 0.6.0' end end @@ -38,7 +38,6 @@ end group :development do gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' - gem 'm' gem 'mocha', '0.14.0' gem 'nokogiri' gem 'rake', '~> 0.9.2' From 10caf8ead89b8766b199390246f5f45b0412558c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 11:27:26 -0400 Subject: [PATCH 0019/1412] Remove tests where Rails is not doing what the said with Arel. --- test/cases/offset_and_limit_test_sqlserver.rb | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 2570d4cc5..88f71e55a 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -44,22 +44,6 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase books = Book.all :select => 'books.id, books.name', :limit => 3, :offset => 5 assert_equal Book.all[5,3].map(&:id), books.map(&:id) end - - # ActiveRecord Regression 3.2.3? - # https://github.com/rails/rails/commit/a2c2f406612a1855fbc6fe816cf3e15b4ef531d3#commitcomment-1208811 - should_eventually 'allow sql literal for offset' do - assert_sql(/WHERE \[__rnt\]\.\[__rn\] > \(3-2\)/) { Book.limit(10).offset(Arel::Nodes::Ascending.new('3-2')).all } - assert_sql(/WHERE \[__rnt\]\.\[__rn\] > \(SELECT 8 AS \[count\]\)/) do - books = Book.all :limit => 3, :offset => Arel.sql('SELECT 8 AS [count]') - assert_equal 2, books.size, 'remember there are only 10 books and offset is 8' - end - end - - # ActiveRecord Regression 3.2.3? - # https://github.com/rails/rails/commit/a2c2f406612a1855fbc6fe816cf3e15b4ef531d3#commitcomment-1208811 - should_eventually 'not convert strings which look like integers to integers' do - assert_sql(/WHERE \[__rnt\]\.\[__rn\] > \(N''5''\)/) { Book.limit(10).offset('5').all } - end should 'alter SQL to limit number of records returned offset by specified amount' do sql = %|EXEC sp_executesql N'SELECT TOP (3) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [books].[id] ASC) AS [__rn], [books].* FROM [books] ) AS [__rnt] WHERE [__rnt].[__rn] > (5) ORDER BY [__rnt].[__rn] ASC'| From b1516db613159c6c2c72e25c1311fd68a1563163 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 12:28:17 -0400 Subject: [PATCH 0020/1412] Drop shoulda and use minitest-spec-rails!!! This greatly reduces the barrier to upgrading to Rails 4.0. --- CHANGELOG | 1 + Gemfile | 4 ++-- RUNNING_UNIT_TESTS.md | 1 + test/cases/adapter_test_sqlserver.rb | 10 +++++----- test/cases/column_test_sqlserver.rb | 2 +- test/cases/connection_test_sqlserver.rb | 2 +- test/cases/execute_procedure_test_sqlserver.rb | 2 +- test/cases/migration_test_sqlserver.rb | 6 +++--- test/cases/pessimistic_locking_test_sqlserver.rb | 2 +- test/cases/schema_test_sqlserver.rb | 2 +- test/cases/sqlserver_helper.rb | 5 +++-- test/cases/table_name_test_sqlserver.rb | 2 +- test/profile/connection_profile_case.rb | 2 +- test/profile/gc_profile_case.rb | 2 +- 14 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 5b0967957..485eb2a7b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ * 3.2.13 * +* Drop shoulda and use minitest-spec-rails!!! * Test TinyTDS 0.6.0. * Fixed the unit tests due to changes in ActiveRecord that removes blank config values. * Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex diff --git a/Gemfile b/Gemfile index 0c809c118..a60adccdc 100644 --- a/Gemfile +++ b/Gemfile @@ -38,10 +38,10 @@ end group :development do gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' - gem 'mocha', '0.14.0' + gem 'mocha' + gem 'minitest-spec-rails' gem 'nokogiri' gem 'rake', '~> 0.9.2' - gem 'shoulda', '2.10.3' gem 'ruby-prof' end diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index b9d87191e..d35d68f30 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -98,4 +98,5 @@ By default, Bundler will download the Rails git repo and use the git tag that ma * Misc Date/Time erros when using ODBC mode. * Misc Date/Time erros when testing SQL Server 2005. +The `test_update_column_changing_id(PersistencesTest)` fails with `ActiveRecord::StatementInvalid: TinyTds::Error: Cannot update identity column 'id'.: EXEC sp_executesql N'UPDATE [topics] SET [id] = 123 WHERE [topics].[id] = 1; SELECT @@ROWCOUNT AS AffectedRows'` even though if you run the test in isolation using `$ rake test TEST_FILES="test/cases/aaaa_create_tables_test_sqlserver.rb,test/cases/persistence_test_sqlserver.rb"` it will pass. So ignorning it for now. diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index c307798bb..80bc429fd 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -10,7 +10,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase fixtures :tasks, :posts - def setup + setup do @connection = ActiveRecord::Base.connection @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" @@ -82,7 +82,7 @@ def setup setup do @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP - @supported_version = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS + @supported_versions = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)" @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" @sqlserver_2011_string1 = %|Microsoft SQL Server "Denali" (CTP1) - 11.0.1103.9 (Intel X86) Sep 24 2010 22:02:43 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition on Windows NT 6.0 (Build 6002: Service Pack 2)| @@ -94,7 +94,7 @@ def setup should 'return a 4 digit year fixnum for #database_year' do assert_instance_of Fixnum, @connection.database_year - assert_contains @supported_version, @connection.database_year + @supported_versions.must_include @connection.database_year end should 'return a code name if year not available' do @@ -567,7 +567,7 @@ def setup end should 'return block value using #run_with_isolation_level' do - assert_same_elements Task.find(:all), @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all) } + assert_equal Task.find(:all).sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all).sort } end should 'pass a read uncommitted isolation level test' do @@ -659,7 +659,7 @@ def setup end should 'find CustomersView table name' do - assert_contains @connection.views, 'customers_view' + @connection.views.must_include 'customers_view' end should 'work with dynamic finders' do diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 44d8fa7b5..6c062d265 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -3,7 +3,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase - def setup + setup do @connection = ActiveRecord::Base.connection @column_klass = ActiveRecord::ConnectionAdapters::SQLServerColumn end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index c5cff71ac..9d00796f0 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -7,7 +7,7 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase fixtures :topics, :accounts - def setup + setup do @connection = ActiveRecord::Base.connection end diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 46525c027..b949aa764 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -2,7 +2,7 @@ class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase - def setup + setup do @klass = ActiveRecord::Base end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index eca1f3364..07f942416 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -3,7 +3,7 @@ class MigrationTestSqlserver < ActiveRecord::TestCase - def setup + setup do @connection = ActiveRecord::Base.connection end @@ -27,8 +27,8 @@ def setup rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message end - assert_does_not_contain @trans_test_table1, @connection.tables - assert_does_not_contain @trans_test_table2, @connection.tables + @connection.tables.wont_include @trans_test_table1 + @connection.tables.wont_include @trans_test_table2 end end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index fb720f2c4..0f1ad11e2 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -7,7 +7,7 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase self.use_transactional_fixtures = false fixtures :people, :readers - def setup + setup do Person.columns; Reader.columns # Avoid introspection queries during tests. end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 1f1b0c5b2..40e309113 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -2,7 +2,7 @@ class SchemaTestSqlserver < ActiveRecord::TestCase - def setup + setup do @connection = ActiveRecord::Base.connection end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 89ce7b9ec..8a0167140 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -12,12 +12,13 @@ require 'rubygems' require 'bundler' Bundler.setup -require 'shoulda' -require 'mocha/setup' +require 'mocha/api' require 'active_support/dependencies' require 'active_record' require 'active_record/version' require 'active_record/connection_adapters/abstract_adapter' +require 'minitest-spec-rails' +require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' require 'models/topic' diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb index e8b33a1f5..3c10eb019 100644 --- a/test/cases/table_name_test_sqlserver.rb +++ b/test/cases/table_name_test_sqlserver.rb @@ -9,7 +9,7 @@ class TableNameTestSqlserver < ActiveRecord::TestCase self.use_transactional_fixtures = false - def setup + setup do Order.table_name = '[orders]' Order.reset_column_information end diff --git a/test/profile/connection_profile_case.rb b/test/profile/connection_profile_case.rb index 7797dff8b..78ee41f18 100644 --- a/test/profile/connection_profile_case.rb +++ b/test/profile/connection_profile_case.rb @@ -6,7 +6,7 @@ class ConnectionProfileCase < ActiveRecord::TestCase fixtures :topics - def setup + setup do @connection = ActiveRecord::Base.connection end diff --git a/test/profile/gc_profile_case.rb b/test/profile/gc_profile_case.rb index 83834f351..d3eb2605b 100644 --- a/test/profile/gc_profile_case.rb +++ b/test/profile/gc_profile_case.rb @@ -9,7 +9,7 @@ class GcProfileCase < ActiveRecord::TestCase fixtures :topics - def setup + setup do create_mass_topics unless @created_mass_topics @connection = ActiveRecord::Base.connection @select_statement = "SELECT [topics].* FROM [topics]" From 1aaae16d951a916408900e5208611770d32289a3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 12:41:22 -0400 Subject: [PATCH 0021/1412] Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. --- CHANGELOG | 2 ++ lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 485eb2a7b..fe8648d5e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,8 @@ * 3.2.13 * +* Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. +* Fix for Azure SQL. Fixed #263. Thanks @eklipse2k8. * Drop shoulda and use minitest-spec-rails!!! * Test TinyTDS 0.6.0. * Fixed the unit tests due to changes in ActiveRecord that removes blank config values. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 80e3e928b..c21cebd99 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -438,13 +438,13 @@ def connect client.execute("SET ANSI_PADDING ON").do client.execute("SET QUOTED_IDENTIFIER ON") client.execute("SET ANSI_WARNINGS ON").do - client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do else client.execute("SET ANSI_DEFAULTS ON").do client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do client.execute("SET IMPLICIT_TRANSACTIONS OFF").do end client.execute("SET TEXTSIZE 2147483647").do + client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do end when :odbc if config[:dsn].include?(';') From d385506aeb673cd3c861d2b1491055d0f9124ffb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 12:51:12 -0400 Subject: [PATCH 0022/1412] Change notes. Also, new version will be 3.2.11, not 3.2.13. --- CHANGELOG | 3 ++- VERSION | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fe8648d5e..408ded6bf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ -* 3.2.13 * +* 3.2.11 * +* Pass tds_version to TinyTDS. Fixes #233. Thanks @denispeplin. * Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. * Fix for Azure SQL. Fixed #263. Thanks @eklipse2k8. * Drop shoulda and use minitest-spec-rails!!! diff --git a/VERSION b/VERSION index d883a1005..17ce91803 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.13 +3.2.11 From e2360a8b2b6bebbd4fc9ed3f34a7ff8edaf0fad5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 13:00:58 -0400 Subject: [PATCH 0023/1412] Update CHANGELOG. --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 408ded6bf..819fe17d8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ * 3.2.11 * +* Update regex for `RecordNotUnique` exception. Fixes #257. Thanks @pbatorre. * Pass tds_version to TinyTDS. Fixes #233. Thanks @denispeplin. * Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. * Fix for Azure SQL. Fixed #263. Thanks @eklipse2k8. From cdc570c198c7381d52119386e1c4fb404a93577d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Jul 2013 13:07:37 -0400 Subject: [PATCH 0024/1412] Update CHANGELOG. --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 819fe17d8..4b02e1c88 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ * 3.2.11 * +* Handle "No such column" when renaming some columns in the migrations. Fixes #237. Thanks @michelgrootjans. * Update regex for `RecordNotUnique` exception. Fixes #257. Thanks @pbatorre. * Pass tds_version to TinyTDS. Fixes #233. Thanks @denispeplin. * Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. From d6f9243f858b096e0259618068126f965827a590 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 14 Jul 2013 18:10:25 -0400 Subject: [PATCH 0025/1412] Revert string_to_binary changes in 457af60e. Fixes #273. --- CHANGELOG | 5 +++++ lib/active_record/connection_adapters/sqlserver_adapter.rb | 3 --- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 4b02e1c88..e3e1f3577 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,4 +1,9 @@ +* 3.2.12 * + +* Revert string_to_binary changes in 457af60e. Fixes #273. + + * 3.2.11 * * Handle "No such column" when renaming some columns in the migrations. Fixes #237. Thanks @michelgrootjans. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 27e7a3a8e..8eed5e13c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -69,9 +69,6 @@ def string_to_binary(value) end def binary_to_string(value) - if value.respond_to?(:force_encoding) && value.encoding != Encoding::ASCII_8BIT - value = value.force_encoding(Encoding::ASCII_8BIT) - end value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*') end From e95d1f4a4a62932a37d0eefbb3ff4e8a26dfb03b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 14 Jul 2013 18:13:01 -0400 Subject: [PATCH 0026/1412] Release v3.2.12 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 17ce91803..275e51e5e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.11 +3.2.12 From ac180b6b25cd2a50090fc95cc673ec050b26c364 Mon Sep 17 00:00:00 2001 From: "J. Francisco Raposeiras" Date: Mon, 5 Aug 2013 16:33:58 -0300 Subject: [PATCH 0027/1412] Works under Rails v4.0.0 --- activerecord-sqlserver-adapter.gemspec | 2 +- .../sqlserver/core_ext/database_statements.rb | 6 ++-- .../sqlserver/database_statements.rb | 14 +++++--- .../sqlserver/schema_statements.rb | 35 +++++++------------ 4 files changed, 25 insertions(+), 32 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 463165cce..56542b42d 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - s.add_dependency('activerecord', '~> 3.2.0') + s.add_dependency('activerecord', '4') end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb index a6cf73fb1..57c48f2d8 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb @@ -44,7 +44,7 @@ def transaction_with_retry_deadlock_victim(options = {}) if open_transactions == 0 if database_transaction_rollback.is_a?(::ActiveRecord::DeadlockVictim) # SQL Server has already rolled back, so rollback activerecord's history - rollback_transaction_records(true) + rollback_transaction retry else rollback_db_transaction @@ -52,7 +52,7 @@ def transaction_with_retry_deadlock_victim(options = {}) end else rollback_to_savepoint - rollback_transaction_records(false) + rollback_transaction end end raise unless database_transaction_rollback.is_a?(::ActiveRecord::Rollback) @@ -67,7 +67,7 @@ def transaction_with_retry_deadlock_victim(options = {}) begin if open_transactions == 0 commit_db_transaction - commit_transaction_records + # commit_transaction_records else release_savepoint save_point_records = @_current_transaction_records.pop diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6185e5f1d..b68aa1914 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -18,6 +18,8 @@ def execute(sql, name = nil) end def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) + # This is so no id is tried to be updated + sql.gsub! /, \[id\] = @[0-9]*/, '' if sql =~ /UPDATE/ && sql =~ /, \[id\] = / if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) } else @@ -25,7 +27,7 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) end end - def exec_insert(sql, name, binds) + def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query sql, name, binds, :insert => true end @@ -135,9 +137,11 @@ def use_database(database=nil) def user_options return {} if sqlserver_azure? - select_rows("dbcc useroptions",'SCHEMA').inject(HashWithIndifferentAccess.new) do |values,row| - set_option = row[0].gsub(/\s+/,'_') - user_value = row[1] + select_rows("dbcc useroptions",'SCHEMA').inject(HashWithIndifferentAccess.new) do |values,row| + set_option = row.values[0].gsub(/\s+/,'_') if row.instance_of? Hash + set_option = row[0].gsub(/\s+/,'_') if row.instance_of? Array + user_value = row.values[1] if row.instance_of? Hash + user_value = row[1] if row.instance_of? Array values[set_option] = user_value values end @@ -294,7 +298,7 @@ def charset protected def select(sql, name = nil, binds = []) - exec_query(sql, name, binds).to_a + exec_query(sql, name, binds) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 47de5fd36..74af7ac3a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -47,39 +47,28 @@ def columns(table_name, name = nil) def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" end - - def remove_column(table_name, *column_names) - raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_names.empty? - ActiveSupport::Deprecation.warn 'Passing array to remove_columns is deprecated, please use multiple arguments, like: `remove_columns(:posts, :foo, :bar)`', caller if column_names.flatten! - column_names.flatten.each do |column_name| - remove_check_constraints(table_name, column_name) - remove_default_constraint(table_name, column_name) - remove_indexes(table_name, column_name) - do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}" - end + + def remove_column(table_name, column_name, type = nil) + raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if (column_name.is_a? Array) + ActiveSupport::Deprecation.warn 'Passing multiple arguments to remove_columns is deprecated, please use just one column name, like: `remove_columns(:posts, :column_name, :type)`', caller if column_name + remove_check_constraints(table_name, column_name) + remove_default_constraint(table_name, column_name) + remove_indexes(table_name, column_name) + do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}" end def change_column(table_name, column_name, type, options = {}) sql_commands = [] - indexes = [] column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s } - + change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + change_column_sql << " NOT NULL" if options[:null] == false + sql_commands << change_column_sql if options_include_default?(options) || (column_object && column_object.type != type.to_sym) - remove_default_constraint(table_name,column_name) - indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) } - remove_indexes(table_name, column_name) + remove_default_constraint(table_name,column_name) end - sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(options[:default])} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - sql_commands[-1] << " NOT NULL" if !options[:null].nil? && options[:null] == false if options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" end - - #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.collect {|c|quote_column_name(c)}.join(', ')})" - end sql_commands.each { |c| do_execute(c) } end From b606c5c3a3dd8954805d5ab74af8a443400927d6 Mon Sep 17 00:00:00 2001 From: "J. Francisco Raposerias" Date: Thu, 22 Aug 2013 12:47:35 -0300 Subject: [PATCH 0028/1412] Commented some lines: rollback_transaction_records --- .../sqlserver/core_ext/database_statements.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb index 57c48f2d8..1c2928c9d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb @@ -48,7 +48,7 @@ def transaction_with_retry_deadlock_victim(options = {}) retry else rollback_db_transaction - rollback_transaction_records(true) + #rollback_transaction_records(true) end else rollback_to_savepoint @@ -79,10 +79,10 @@ def transaction_with_retry_deadlock_victim(options = {}) rescue Exception => database_transaction_rollback if open_transactions == 0 rollback_db_transaction - rollback_transaction_records(true) + #rollback_transaction_records(true) else rollback_to_savepoint - rollback_transaction_records(false) + #rollback_transaction_records(false) end raise end From 141373c1c602c67894c97a49f33d8b8cccb0d720 Mon Sep 17 00:00:00 2001 From: Louis Galipeau Date: Wed, 30 Oct 2013 19:17:58 -0300 Subject: [PATCH 0029/1412] create_database has an additional parameter for collation When creating a database you can now optionally specify the collation used instead of having to rely on the server's default collation. The latter is not always ideal when dealing with legacy systems. --- .../sqlserver/database_statements.rb | 8 ++- .../database_statements_test_sqlserver.rb | 49 +++++++++++++++++++ test/config.yml | 1 + 3 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 test/cases/database_statements_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6185e5f1d..c5ebb342d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -278,8 +278,12 @@ def drop_database(database) end end - def create_database(database) - do_execute "CREATE DATABASE #{quote_table_name(database)}" + def create_database(database, collation=@connection_options[:collation]) + if collation + do_execute "CREATE DATABASE #{quote_table_name(database)} COLLATE #{collation}" + else + do_execute "CREATE DATABASE #{quote_table_name(database)}" + end end def current_database diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb new file mode 100644 index 000000000..1bacabf0e --- /dev/null +++ b/test/cases/database_statements_test_sqlserver.rb @@ -0,0 +1,49 @@ +require 'cases/sqlserver_helper' + +class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase + + self.use_transactional_fixtures = false + + setup do + @connection = ActiveRecord::Base.connection + end + + should 'create database' do + @connection.create_database 'activerecord_unittest3' #, 'SQL_Latin1_General_CP1_CI_AS' + database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" + puts @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + assert_equal 'activerecord_unittest3', database_name + end + + should 'drop database' do + @connection.drop_database 'activerecord_unittest3' + database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" + assert_equal nil, database_name + end + + context 'with collation' do + teardown do + @connection.drop_database 'activerecord_unittest3' + end + + should 'create database with default collation for the server' do + @connection.create_database 'activerecord_unittest3' + default_collation = @connection.select_value "SELECT SERVERPROPERTY('Collation')" + database_collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + assert_equal default_collation, database_collation + end + + should 'create database with collation set by the method' do + @connection.create_database 'activerecord_unittest3', 'SQL_Latin1_General_CP1_CI_AS' + collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation + end + + should 'create database with collation set by the config' do + @connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS' + @connection.create_database 'activerecord_unittest3' + collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation + end + end +end \ No newline at end of file diff --git a/test/config.yml b/test/config.yml index 553903d79..bd20e22f6 100644 --- a/test/config.yml +++ b/test/config.yml @@ -9,6 +9,7 @@ default_connection_info: &default_connection_info username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> + collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> connections: From 5b3e2a934415e5649689d241c71f06c40d685120 Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 23 Nov 2013 22:58:18 -0500 Subject: [PATCH 0030/1412] increment/decrement_open_transactions deprecated https://github.com/rails/rails/commit/9296e6939bcc786149a07dac334267c403 5b623a --- .../sqlserver/core_ext/database_statements.rb | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb index 1c2928c9d..1b45f5545 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb @@ -3,11 +3,11 @@ module ConnectionAdapters module Sqlserver module CoreExt module DatabaseStatements - + # This is a copy of the current (3.1.3) ActiveRecord's transaction method. We should propose - # a patch to the default transaction method to make it more callback for adapters that want to + # a patch to the default transaction method to make it more callback for adapters that want to # do deadlock retry logic. Because this is a copy, we really need to keep an eye out on this when - # upgradding the adapter. + # upgradding the adapter. def transaction_with_retry_deadlock_victim(options = {}) options.assert_valid_keys :requires_new, :joinable @@ -30,7 +30,6 @@ def transaction_with_retry_deadlock_victim(options = {}) elsif requires_new create_savepoint end - increment_open_transactions transaction_open = true @_current_transaction_records.push([]) end @@ -39,7 +38,6 @@ def transaction_with_retry_deadlock_victim(options = {}) rescue Exception => database_transaction_rollback if transaction_open && !outside_transaction? transaction_open = false - decrement_open_transactions # handle deadlock victim retries at the outermost transaction if open_transactions == 0 if database_transaction_rollback.is_a?(::ActiveRecord::DeadlockVictim) @@ -63,7 +61,6 @@ def transaction_with_retry_deadlock_victim(options = {}) if outside_transaction? @open_transactions = 0 elsif transaction_open - decrement_open_transactions begin if open_transactions == 0 commit_db_transaction @@ -88,7 +85,7 @@ def transaction_with_retry_deadlock_victim(options = {}) end end end - + end end end From c0ba24b685775bb0a470cc00144e3bf0b65508ff Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 23 Nov 2013 23:08:01 -0500 Subject: [PATCH 0031/1412] Fix Deprecation warning - call columns with a table name! --- .../connection_adapters/sqlserver/schema_statements.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 74af7ac3a..58ffd19c1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -47,7 +47,7 @@ def columns(table_name, name = nil) def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" end - + def remove_column(table_name, column_name, type = nil) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if (column_name.is_a? Array) ActiveSupport::Deprecation.warn 'Passing multiple arguments to remove_columns is deprecated, please use just one column name, like: `remove_columns(:posts, :column_name, :type)`', caller if column_name @@ -59,7 +59,7 @@ def remove_column(table_name, column_name, type = nil) def change_column(table_name, column_name, type, options = {}) sql_commands = [] - column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s } + column_object = schema_cache .columns(table_name).detect { |c| c.name.to_s == column_name.to_s } change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" change_column_sql << " NOT NULL" if options[:null] == false sql_commands << change_column_sql @@ -279,7 +279,7 @@ def default_constraint_name(table_name, column_name) end def detect_column_for!(table_name, column_name) - unless column = schema_cache.columns[table_name].detect { |c| c.name == column_name.to_s } + unless column = schema_cache .columns(table_name).detect { |c| c.name == column_name.to_s } raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" end column @@ -354,9 +354,9 @@ def set_identity_insert(table_name, enable = true) rescue Exception => e raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end - + #TODO: DEPRECATION WARNING: call columns with a table name!. (called from identity_column at /Users/acarey/code/source/4/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:359) def identity_column(table_name) - schema_cache.columns[table_name].detect(&:is_identity?) + schema_cache.columns(table_name).detect(&:is_identity?) end end From 6065bee5ed4bb7c8279acae562070eda3d87b423 Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 23 Nov 2013 23:08:45 -0500 Subject: [PATCH 0032/1412] Fix deprication warning call columns with a table name! --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 8eed5e13c..f1fbfacc6 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -311,7 +311,7 @@ def pk_and_sequence_for(table_name) end def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache.columns[table_name].detect(&:is_primary?).try(:name) + identity_column(table_name).try(:name) || schema_cache .columns(table_name).detect(&:is_primary?).try(:name) end # === SQLServer Specific (DB Reflection) ======================== # From ed0238207a94389bcd081bca014ff40be7e8bd7a Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 23 Nov 2013 23:09:10 -0500 Subject: [PATCH 0033/1412] Fix tiny_tds caused segfault --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index a60adccdc..9b2b3cd3d 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,8 @@ group :tinytds do if ENV['TINYTDS_SOURCE'] gem 'tiny_tds', :path => ENV['TINYTDS_SOURCE'] else - gem 'tiny_tds', '~> 0.6.0' + #segfault caused by tiny_tds 0.6.1 + gem 'tiny_tds', :git =>"https://github.com/rails-sqlserver/tiny_tds.git" end end From 899e5aca1afb15d77aea3a9e15ba9e9f4aef538f Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Nov 2013 08:01:25 -0500 Subject: [PATCH 0034/1412] require arel 4.0.0 --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 9b2b3cd3d..2eac5cbd5 100644 --- a/Gemfile +++ b/Gemfile @@ -21,6 +21,8 @@ end if ENV['AREL'] gem 'arel', :path => ENV['AREL'] + else + gem 'arel', '4.0.0' end group :tinytds do From 042ffe22bcdc1eed83ec0226159157a4e56818cb Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 24 Nov 2013 08:03:44 -0500 Subject: [PATCH 0035/1412] .find(:first) deprecated --- .../cases/attribute_methods_test_sqlserver.rb | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index ea3bd78ad..6b8f03750 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -6,35 +6,35 @@ class AttributeMethodsTestSqlserver < ActiveRecord::TestCase end class AttributeMethodsTest < ActiveRecord::TestCase - + COERCED_TESTS = [ :test_read_attributes_before_type_cast_on_datetime, :test_typecast_attribute_from_select_to_false, :test_typecast_attribute_from_select_to_true ] - + include SqlserverCoercedTest - + fixtures :developers - + def test_coerced_read_attributes_before_type_cast_on_datetime developer = Developer.find(:first) if developer.created_at_before_type_cast.is_a?(String) - assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"] + assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"] end end - + def test_coerced_typecast_attribute_from_select_to_false topic = Topic.create(:title => 'Budget') - topic = Topic.find(:first, :select => "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test") + topic = Topic.first(select => "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test") assert !topic.is_test? end - + def test_coerced_typecast_attribute_from_select_to_true topic = Topic.create(:title => 'Budget') - topic = Topic.find(:first, :select => "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test") + topic = Topic.first(:select => "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test") assert topic.is_test? end - - + + end From 6e4ccd66947cc1807ab32311efb251f6a8963834 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 26 Nov 2013 11:56:47 -0500 Subject: [PATCH 0036/1412] allow only some tests to be run bundle exec rake test SQLSERVER_ONLY=true bundle exec rake test TEST_FILES="~/code/source/rails40/activerecord/test/cases/scoping/named_ scoping_test.rb" --- Rakefile | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 4c13a44c3..8f6d0f43d 100644 --- a/Rakefile +++ b/Rakefile @@ -10,14 +10,24 @@ def test_libs(mode='dblib') "#{File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')}"] end +# bundle exec rake test SQLSERVER_ONLY=true +# +# If you have trouble running single tests (errors about requirements): +# http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files - return ENV['TEST_FILES'].split(',').sort if ENV['TEST_FILES'] - files = Dir.glob("test/cases/**/*_test_sqlserver.rb").sort + test_setup = "test/cases/aaaa_create_tables_test_sqlserver.rb" + return (ENV['TEST_FILES']+","+test_setup).split(',').sort if ENV['TEST_FILES'] + sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb").sort ar_path = Gem.loaded_specs['activerecord'].full_gem_path ar_cases = Dir.glob("#{ar_path}/test/cases/**/*_test.rb") adapter_cases = Dir.glob("#{ar_path}/test/cases/adapters/**/*_test.rb") - files += (ar_cases-adapter_cases).sort - files + if ENV['SQLSERVER_ONLY'] + sqlserver_cases + elsif ENV['ACTIVERECORD_ONLY'] + test_setup + (ar_cases - adapter_cases).sort + else + sqlserver_cases + (ar_cases - adapter_cases).sort + end end task :test => ['test:dblib'] From 664965e9f4bbda21488dea3ed2aa935bf969f370 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 26 Nov 2013 11:57:38 -0500 Subject: [PATCH 0037/1412] Disable Timeout --- test/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/config.yml b/test/config.yml index 553903d79..4294f9c06 100644 --- a/test/config.yml +++ b/test/config.yml @@ -9,7 +9,7 @@ default_connection_info: &default_connection_info username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> - + timeout: 0 connections: dblib: From eef4ed0b10f223f55292ecdc3535ab604053ca17 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 26 Nov 2013 11:59:33 -0500 Subject: [PATCH 0038/1412] .find(:first) deprecated --- test/cases/attribute_methods_test_sqlserver.rb | 2 +- test/cases/method_scoping_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 6b8f03750..3b3c2b481 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -18,7 +18,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase fixtures :developers def test_coerced_read_attributes_before_type_cast_on_datetime - developer = Developer.find(:first) + developer = Developer.first if developer.created_at_before_type_cast.is_a?(String) assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"] end diff --git a/test/cases/method_scoping_test_sqlserver.rb b/test/cases/method_scoping_test_sqlserver.rb index 921b46133..e2e024eb5 100644 --- a/test/cases/method_scoping_test_sqlserver.rb +++ b/test/cases/method_scoping_test_sqlserver.rb @@ -18,7 +18,7 @@ def test_coerced_test_merged_scoped_find Developer.send(:with_scope, :find => { :conditions => "salary < 100000" }) do Developer.send(:with_scope, :find => { :offset => 1, :order => 'id asc' }) do assert_sql /ORDER BY id asc/i do - assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc')) + assert_equal(poor_jamis, Developer.first(:order => 'id asc')) end end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index c3537af8b..5f753dfd2 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -133,9 +133,9 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'can find by biginit' do assert_equal @bi5k, @edge_class.find_by_bigint(@b5k) - assert_equal @b5k, @edge_class.find(:first, :select => 'bigint', :conditions => {:bigint => @b5k}).bigint + assert_equal @b5k, @edge_class.first(:select => 'bigint', :conditions => {:bigint => @b5k}).bigint assert_equal @bimjr, @edge_class.find_by_bigint(@bnum) - assert_equal @bnum, @edge_class.find(:first, :select => 'bigint', :conditions => {:bigint => @bnum}).bigint + assert_equal @bnum, @edge_class.first(:select => 'bigint', :conditions => {:bigint => @bnum}).bigint end end From 10ee8112deabdcf94d6fee93788230b9cf129315 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 26 Nov 2013 12:00:07 -0500 Subject: [PATCH 0039/1412] active_support/core_ext/exception no longer valid --- test/cases/persistence_test_sqlserver.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 2ece8a14e..2c76821d2 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -14,7 +14,6 @@ require 'models/minivan' require 'models/person' require 'rexml/document' -require 'active_support/core_ext/exception' class PersistencesTestSqlserver < ActiveRecord::TestCase end From 8fadc7a8e53896ef474f8090479d6453e56dfc32 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 26 Nov 2013 12:09:28 -0500 Subject: [PATCH 0040/1412] TODO's --- Gemfile | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 5 ++++- .../connection_adapters/sqlserver_adapter.rb | 10 ++++++++++ lib/arel/visitors/sqlserver.rb | 2 +- test/cases/session_test_sqlserver.rb | 4 ++-- 5 files changed, 18 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 2eac5cbd5..67e9f216a 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,7 @@ source 'https://rubygems.org' if ENV['RAILS_SOURCE'] gemspec :path => ENV['RAILS_SOURCE'] else + # Need to get rails source beacause the gem doesn't include tests version = ENV['RAILS_VERSION'] || begin require 'net/http' require 'yaml' diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 58ffd19c1..97201c7fe 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -50,7 +50,8 @@ def rename_table(table_name, new_name) def remove_column(table_name, column_name, type = nil) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if (column_name.is_a? Array) - ActiveSupport::Deprecation.warn 'Passing multiple arguments to remove_columns is deprecated, please use just one column name, like: `remove_columns(:posts, :column_name, :type)`', caller if column_name + # TODO: this deprecation warning should be fixed or removed + # ActiveSupport::Deprecation.warn 'Passing multiple arguments to remove_columns is deprecated, please use just one column name, like: `remove_columns(:posts, :column_name, :type)`', caller if column_name remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) @@ -270,6 +271,8 @@ def get_table_name(sql) elsif sql =~ /FROM\s+([^\(\s]+)\s*/i $1 else + # TODO: + puts sql nil end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f1fbfacc6..e7eaf39ae 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -195,6 +195,9 @@ class SQLServerAdapter < AbstractAdapter def initialize(connection, logger, pool, config) super(connection, logger, pool) + # TODO: Is ActiveSupport::Notifications.notifier nil here + # If it is not, does it become nil between here and 'SELECT @@version below' + # If it is, why is it nil here, do we need to change our call to the super impl # AbstractAdapter Responsibility @schema_cache = Sqlserver::SchemaCache.new self @visitor = Arel::Visitors::SQLServer.new self @@ -203,6 +206,13 @@ def initialize(connection, logger, pool, config) @connection_options = config connect @database_version = select_value 'SELECT @@version', 'SCHEMA' + +# db_ver= "Microsoft SQL Server 2012 - 11.0.2100.60 (X64) +# Feb 10 2012 19:39:15 +# Copyright (c) Microsoft Corporation +# Express Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor) +# " + #TODO @database_year = begin if @database_version =~ /Azure/i @sqlserver_azure = true diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f7a1951b6..cc645edbc 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -89,7 +89,7 @@ class SQLServer < Arel::Visitors::ToSql private # SQLServer ToSql/Visitor (Overides) - + # TODO: Change for arel 4.0.1 def visit_Arel_Nodes_SelectStatement(o) if complex_count_sql?(o) visit_Arel_Nodes_SelectStatementForComplexCount(o) diff --git a/test/cases/session_test_sqlserver.rb b/test/cases/session_test_sqlserver.rb index e19c96c98..19b4ee702 100644 --- a/test/cases/session_test_sqlserver.rb +++ b/test/cases/session_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/sqlserver_helper' require 'action_dispatch' -require 'active_record/session_store' - +# TODO: require 'active_record/session_store' +# Active Record's Session Store extracted from Rails module ActiveRecord class SessionStore class SessionTest < ActiveRecord::TestCase From c95ffc4bf1ac8a145b1334d7c39750f10835cc8b Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 2 Dec 2013 16:48:48 -0500 Subject: [PATCH 0041/1412] added "turn" gem for better test output --- Gemfile | 1 + test/cases/sqlserver_helper.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/Gemfile b/Gemfile index 67e9f216a..f28521e6c 100644 --- a/Gemfile +++ b/Gemfile @@ -40,6 +40,7 @@ group :odbc do end group :development do + gem "turn" gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'mocha' diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 8a0167140..33b6d0aeb 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -1,3 +1,13 @@ +begin + require 'turn' + + Turn.config do |c| + c.format = :pretty + c.trace = 1 + # c.natural = true + end +rescue LoadError +end SQLSERVER_TEST_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..')) SQLSERVER_ASSETS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'assets')) From 5445135a777426b81699fe56202f35684a2861e4 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 2 Dec 2013 16:49:22 -0500 Subject: [PATCH 0042/1412] specified activerecord 4.0.0 --- Gemfile | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index f28521e6c..4e1815690 100644 --- a/Gemfile +++ b/Gemfile @@ -14,7 +14,7 @@ else uri = URI.parse "http://rubygems.org/api/v1/versions/activerecord.yaml" YAML.load(Net::HTTP.get(uri)).select do |data| a, b, c = data['number'].split('.') - !data['prerelease'] && major == a && minor == b + !data['prerelease'] && major == a && (minor.nil? || minor == b) end.first['number'] end gem 'rails', :git => "git://github.com/rails/rails.git", :tag => "v#{version}" diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 56542b42d..59d7189ac 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - s.add_dependency('activerecord', '4') + s.add_dependency('activerecord', '4.0.0') end From 16925cd6197219265ee5dcd5a46ffcd582079db3 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 2 Dec 2013 16:50:10 -0500 Subject: [PATCH 0043/1412] load setup files when running selected tests --- Rakefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 8f6d0f43d..f1daf07bb 100644 --- a/Rakefile +++ b/Rakefile @@ -15,8 +15,8 @@ end # If you have trouble running single tests (errors about requirements): # http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files - test_setup = "test/cases/aaaa_create_tables_test_sqlserver.rb" - return (ENV['TEST_FILES']+","+test_setup).split(',').sort if ENV['TEST_FILES'] + test_setup = ["test/cases/sqlserver_helper.rb", "test/cases/aaaa_create_tables_test_sqlserver.rb"] + return test_setup+(ENV['TEST_FILES']).split(',').sort if ENV['TEST_FILES'] sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb").sort ar_path = Gem.loaded_specs['activerecord'].full_gem_path ar_cases = Dir.glob("#{ar_path}/test/cases/**/*_test.rb") From 9fea5968319db8ef2e1983a39caade5a7af8adad Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 2 Dec 2013 17:08:47 -0500 Subject: [PATCH 0044/1412] deadlock test skipped for now. It deadlocks. --- test/cases/connection_test_sqlserver.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 9d00796f0..bb5dd5850 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -232,6 +232,7 @@ def execute_with_deadlock_exception(sql, *args) end should 'retry by default' do + skip "takes too long" assert_nothing_raised do ActiveRecord::Base.transaction do assert_equal @expected, @connection.execute(@query) @@ -240,6 +241,7 @@ def execute_with_deadlock_exception(sql, *args) end should 'raise ActiveRecord::DeadlockVictim if retry disabled' do + skip "takes too long" @connection.class.retry_deadlock_victim = false assert_raise(ActiveRecord::DeadlockVictim) do ActiveRecord::Base.transaction do From c5559320f9a2bb6e52a61ace1951ee0890fdafc9 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 3 Dec 2013 13:08:47 -0500 Subject: [PATCH 0045/1412] make clear that Undefined coerced test is not an error message --- test/cases/sqlserver_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 33b6d0aeb..24a147c2f 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -105,7 +105,7 @@ def coerced_tests def method_added(method) if coerced_tests && coerced_tests.include?(method) undef_method(method) rescue nil - STDOUT.puts("Undefined coerced test: #{self.name}##{method}") + STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") end end end From e200bcfeb7de54e25262130ec2a1e0ee88b0c762 Mon Sep 17 00:00:00 2001 From: Louis Galipeau Date: Fri, 6 Dec 2013 00:26:32 -0400 Subject: [PATCH 0046/1412] Removed left over puts. Thanks to @annaswims for spotting it. --- test/cases/database_statements_test_sqlserver.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index 1bacabf0e..3928b6c00 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -11,7 +11,6 @@ class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase should 'create database' do @connection.create_database 'activerecord_unittest3' #, 'SQL_Latin1_General_CP1_CI_AS' database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" - puts @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal 'activerecord_unittest3', database_name end @@ -46,4 +45,4 @@ class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation end end -end \ No newline at end of file +end From e466e811a297df49c1b810915e3ed66118f8bc02 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 6 Dec 2013 16:45:13 -0500 Subject: [PATCH 0047/1412] changed to arel 4.0.1 --- Gemfile | 2 +- lib/arel/visitors/sqlserver.rb | 38 +++++++++++++++++----------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Gemfile b/Gemfile index 4e1815690..c7d298832 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,7 @@ end if ENV['AREL'] gem 'arel', :path => ENV['AREL'] else - gem 'arel', '4.0.0' + gem 'arel', '~> 4.0.1' end group :tinytds do diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index cc645edbc..c8a668127 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -4,7 +4,7 @@ module Arel module Nodes - # Extending the Ordering class to be comparrison friendly which allows us to call #uniq on a + # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a # collection of them. See SelectManager#order for more details. class Ordering < Arel::Nodes::Unary def hash @@ -89,51 +89,51 @@ class SQLServer < Arel::Visitors::ToSql private # SQLServer ToSql/Visitor (Overides) - # TODO: Change for arel 4.0.1 - def visit_Arel_Nodes_SelectStatement(o) + + def visit_Arel_Nodes_SelectStatement(o, a) if complex_count_sql?(o) - visit_Arel_Nodes_SelectStatementForComplexCount(o) + visit_Arel_Nodes_SelectStatementForComplexCount(o, a) elsif o.offset - visit_Arel_Nodes_SelectStatementWithOffset(o) + visit_Arel_Nodes_SelectStatementWithOffset(o, a) else - visit_Arel_Nodes_SelectStatementWithOutOffset(o) + visit_Arel_Nodes_SelectStatementWithOutOffset(o, a) end end - - def visit_Arel_Nodes_UpdateStatement(o) + + def visit_Arel_Nodes_UpdateStatement(o, a) if o.orders.any? && o.limit.nil? o.limit = Nodes::Limit.new(9223372036854775807) end super end - def visit_Arel_Nodes_Offset(o) + def visit_Arel_Nodes_Offset(o, a) "WHERE [__rnt].[__rn] > (#{visit o.expr})" end - def visit_Arel_Nodes_Limit(o) + def visit_Arel_Nodes_Limit(o, a) "TOP (#{visit o.expr})" end - def visit_Arel_Nodes_Lock(o) + def visit_Arel_Nodes_Lock(o, a) visit o.expr end - - def visit_Arel_Nodes_Ordering(o) + + def visit_Arel_Nodes_Ordering(o, a) if o.respond_to?(:direction) "#{visit o.expr} #{o.ascending? ? 'ASC' : 'DESC'}" else visit o.expr end end - - def visit_Arel_Nodes_Bin(o) + + def visit_Arel_Nodes_Bin(o, a) "#{visit o.expr} #{@connection.cs_equality_operator}" end # SQLServer ToSql/Visitor (Additions) - def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false) + def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) find_and_fix_uncorrelated_joins_in_select_statement(o) core = o.cores.first projections = core.projections @@ -165,7 +165,7 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, windowed=false) ].compact.join ' ' end - def visit_Arel_Nodes_SelectStatementWithOffset(o) + def visit_Arel_Nodes_SelectStatementWithOffset(o, a) core = o.cores.first o.limit ||= Arel::Nodes::Limit.new(9223372036854775807) orders = rowtable_orders(o) @@ -174,14 +174,14 @@ def visit_Arel_Nodes_SelectStatementWithOffset(o) (rowtable_projections(o).map{ |x| visit(x) }.join(', ')), "FROM (", "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],", - visit_Arel_Nodes_SelectStatementWithOutOffset(o,true), + visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, true), ") AS [__rnt]", (visit(o.offset) if o.offset), "ORDER BY [__rnt].[__rn] ASC" ].compact.join ' ' end - def visit_Arel_Nodes_SelectStatementForComplexCount(o) + def visit_Arel_Nodes_SelectStatementForComplexCount(o, a) core = o.cores.first o.limit.expr = Arel.sql("#{o.limit.expr} + #{o.offset ? o.offset.expr : 0}") if o.limit orders = rowtable_orders(o) From f1b815daa1b28e9668e6e65132b5a3f8bcae156f Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 6 Dec 2013 16:46:12 -0500 Subject: [PATCH 0048/1412] fixed spelling --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d81f8eca8..a119290ad 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ You can configure a few options to your needs. First is the max column width for ActiveRecord::ConnectionAdapters::Sqlserver::Showplan::PrinterTable.max_column_width = 500 ``` -Another configuration is the showplan option. Some might find the XML format more useful. If you have Nokogiri installed, we will format the XML string. I will gladly accept pathces that make the XML printer more useful! +Another configuration is the showplan option. Some might find the XML format more useful. If you have Nokogiri installed, we will format the XML string. I will gladly accept pathches that make the XML printer more useful! ```ruby ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_XML' From bee31ed5da7d4fc43adc718f510fce2eb52e8965 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 11 Dec 2013 15:09:15 -0500 Subject: [PATCH 0049/1412] updated tests to be consistent with rails 4 --- test/cases/attribute_methods_test_sqlserver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 3b3c2b481..2e49e5ba0 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -26,13 +26,13 @@ def test_coerced_read_attributes_before_type_cast_on_datetime def test_coerced_typecast_attribute_from_select_to_false topic = Topic.create(:title => 'Budget') - topic = Topic.first(select => "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test") + topic = Topic.all.merge!(select: "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test").first assert !topic.is_test? end def test_coerced_typecast_attribute_from_select_to_true topic = Topic.create(:title => 'Budget') - topic = Topic.first(:select => "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test") + topic = Topic.all.merge!(select: "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test").first assert topic.is_test? end From bb5ba8e7b8f6bd4e42a6acc3d464b9f17d003d24 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 11:08:08 -0500 Subject: [PATCH 0050/1412] turn format changed --- test/cases/sqlserver_helper.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 24a147c2f..fc7108c17 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -3,8 +3,7 @@ Turn.config do |c| c.format = :pretty - c.trace = 1 - # c.natural = true + c.verbose = true end rescue LoadError end From ff6134012b757e3f6bb577e263563d38dafa563b Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:11:14 -0500 Subject: [PATCH 0051/1412] changed derpecated find syntax --- test/cases/adapter_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 80bc429fd..20985ca9a 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -567,7 +567,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end should 'return block value using #run_with_isolation_level' do - assert_equal Task.find(:all).sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(:all).sort } + assert_equal Task.all.sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } end should 'pass a read uncommitted isolation level test' do From a733956eb0e760fe1b955b86df939311f072b40c Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:11:56 -0500 Subject: [PATCH 0052/1412] arel in gemsepc --- activerecord-sqlserver-adapter.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 59d7189ac..1cc893783 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,7 @@ Gem::Specification.new do |s| s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - s.add_dependency('activerecord', '4.0.0') + s.add_dependency('activerecord', '~> 4.0.0') + s.add_dependency('arel', '~> 4.0.1') end From 4a2e199d5cf2e448f752c5353da1565af0bba354 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:13:25 -0500 Subject: [PATCH 0053/1412] removed extra spaces --- .../connection_adapters/sqlserver_adapter.rb | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index e7eaf39ae..f054ef32c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -63,7 +63,7 @@ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = { end class << self - + # TODO: I think this has problems with ruby 2.0 def string_to_binary(value) "0x#{value.unpack("H*")[0]}" end @@ -206,13 +206,6 @@ def initialize(connection, logger, pool, config) @connection_options = config connect @database_version = select_value 'SELECT @@version', 'SCHEMA' - -# db_ver= "Microsoft SQL Server 2012 - 11.0.2100.60 (X64) -# Feb 10 2012 19:39:15 -# Copyright (c) Microsoft Corporation -# Express Edition (64-bit) on Windows NT 6.1 (Build 7601: Service Pack 1) (Hypervisor) -# " - #TODO @database_year = begin if @database_version =~ /Azure/i @sqlserver_azure = true @@ -321,7 +314,7 @@ def pk_and_sequence_for(table_name) end def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache .columns(table_name).detect(&:is_primary?).try(:name) + identity_column(table_name).try(:name) || schema_cache.columns(table_name).detect(&:is_primary?).try(:name) end # === SQLServer Specific (DB Reflection) ======================== # From 66baca533130c73e28daa3345793a626235c01f8 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:14:00 -0500 Subject: [PATCH 0054/1412] arel in gemsepc --- Gemfile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index c7d298832..992d3aed1 100644 --- a/Gemfile +++ b/Gemfile @@ -22,15 +22,13 @@ end if ENV['AREL'] gem 'arel', :path => ENV['AREL'] - else - gem 'arel', '~> 4.0.1' end group :tinytds do if ENV['TINYTDS_SOURCE'] gem 'tiny_tds', :path => ENV['TINYTDS_SOURCE'] else - #segfault caused by tiny_tds 0.6.1 + # TODO: [Rails4] Change back... segfault caused by tiny_tds 0.6.1 gem 'tiny_tds', :git =>"https://github.com/rails-sqlserver/tiny_tds.git" end end From b3ee75bde073c083648eda4c297e39031e34b55d Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:14:34 -0500 Subject: [PATCH 0055/1412] removed extra spaces --- .../connection_adapters/sqlserver/schema_statements.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 97201c7fe..acf1b243b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -60,7 +60,7 @@ def remove_column(table_name, column_name, type = nil) def change_column(table_name, column_name, type, options = {}) sql_commands = [] - column_object = schema_cache .columns(table_name).detect { |c| c.name.to_s == column_name.to_s } + column_object = schema_cache.columns(table_name).detect { |c| c.name.to_s == column_name.to_s } change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" change_column_sql << " NOT NULL" if options[:null] == false sql_commands << change_column_sql @@ -282,7 +282,7 @@ def default_constraint_name(table_name, column_name) end def detect_column_for!(table_name, column_name) - unless column = schema_cache .columns(table_name).detect { |c| c.name == column_name.to_s } + unless column = schema_cache.columns(table_name).detect { |c| c.name == column_name.to_s } raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" end column @@ -357,7 +357,7 @@ def set_identity_insert(table_name, enable = true) rescue Exception => e raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end - #TODO: DEPRECATION WARNING: call columns with a table name!. (called from identity_column at /Users/acarey/code/source/4/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:359) + def identity_column(table_name) schema_cache.columns(table_name).detect(&:is_identity?) end From 7939322056ce8e176745350a37092e949005e5ac Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:15:29 -0500 Subject: [PATCH 0056/1412] dropping support for ruby <1.9.3, removing with_kcode --- .../uniqueness_validation_test_sqlserver.rb | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/uniqueness_validation_test_sqlserver.rb index e68d9db05..14c113ed6 100644 --- a/test/cases/uniqueness_validation_test_sqlserver.rb +++ b/test/cases/uniqueness_validation_test_sqlserver.rb @@ -29,15 +29,13 @@ class UniquenessValidationTest < ActiveRecord::TestCase # "一二三四五六七八".mb_chars.to(4).to_s # => "一二三四五" def test_coerced_validate_uniqueness_with_limit_and_utf8 - with_kcode('UTF8') do - Event.connection.change_column :events, :title, :nvarchar, :limit => 30 - Event.reset_column_information - # Now the actual test copied from core. - e1 = Event.create(:title => "一二三四五") - assert e1.valid?, "Could not create an event with a unique, 5 character title" - e2 = Event.create(:title => "一二三四五六七八") - assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" - end + Event.connection.change_column :events, :title, :nvarchar, :limit => 30 + Event.reset_column_information + # Now the actual test copied from core. + e1 = Event.create(:title => "一二三四五") + assert e1.valid?, "Could not create an event with a unique, 5 character title" + e2 = Event.create(:title => "一二三四五六七八") + assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" end end From 5ead08c1aa5d621f5b4a4095f85902c5beec8a07 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:28:16 -0500 Subject: [PATCH 0057/1412] Updated for Rails 4 prerelease --- README.md | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a119290ad..bd6619570 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ +# Rails 4 - By no means ready for production +Current test status: +3866 tests, 3412 passed, 85 failures, 369 errors, 29 skips, 10084 assertions + + # ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. @@ -5,16 +10,7 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server ## What's New -* Rails 3.2 support. With explain (SHOWPLAN) support. -* Deadlock victim retry logic using the #retry_deadlock_victim config. -* Proper interface to configure the connection and TinyTDS app name reported to SQL Server. -* Rails 3.1 prepared statement support leverages cached query plans. - If you use DBLIB/TinyTDS, you must use FreeTDS 0.91 !!!!! - https://github.com/rails-sqlserver/tiny_tds/issues/41 -* We now support your native language date/time formats automatically! -* Default unicode datatypes! Disable with #enable_default_unicode_types to false. -* New #lowercase_schema_reflection configuration option for legacy DBs. -* New dblib connection mode using TinyTDS! Default mode too! +* Rails 4 support #### Testing Rake Tasks Support From 97cbb09a38dfbaab08dfef6998e575baa4006ae7 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 13:29:27 -0500 Subject: [PATCH 0058/1412] I suspect the DSN should be ACTIVERECORD_UNITTEST_DSN --- test/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/config.yml b/test/config.yml index 4294f9c06..fe6d5fdbe 100644 --- a/test/config.yml +++ b/test/config.yml @@ -26,7 +26,7 @@ connections: odbc: arunit: <<: *default_connection_info - dsn: <%= ENV['ACTIVERECORD_UNITTEST2_DSN'] || 'activerecord_unittest' %> + dsn: <%= ENV['ACTIVERECORD_UNITTEST_DSN'] || 'activerecord_unittest' %> arunit2: <<: *default_connection_info database: activerecord_unittest2 From 6b6f88d23a21cf8c0c49df07bf76b23bd1be61ba Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 19:31:16 -0500 Subject: [PATCH 0059/1412] Removed Retry Deadlock Victim --- CHANGELOG | 3 + README.md | 9 -- .../sqlserver/core_ext/database_statements.rb | 94 ------------------- .../sqlserver/database_statements.rb | 10 -- .../connection_adapters/sqlserver_adapter.rb | 12 +-- test/cases/connection_test_sqlserver.rb | 25 +---- 6 files changed, 6 insertions(+), 147 deletions(-) delete mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb diff --git a/CHANGELOG b/CHANGELOG index e3e1f3577..d57fbdbb1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +* 4.0.0 * + +*Removed deadlock victim retry in favor of Isolation Level * 3.2.12 * diff --git a/README.md b/README.md index bd6619570..23889daf8 100644 --- a/README.md +++ b/README.md @@ -110,15 +110,6 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = It is important to remember that unicode types in SQL Server have approximately half the storage capacity as their counter parts. So where a normal string would max out at (8000) a unicode string will top off at (4000). -#### Deadlock Victim Retry - -In a config initializer, you can configure the adapter to retry deadlock victims' SQL. Note, this relies on us copying ActiveRecord's `#transaction` method and can be brittle when upgrading. If you think that our version of `#transaction` is out of sync with the version of rails in our gemspec, please open a ticket and let us know. Our custom transaction method can be found in `activerecord/connection_adapters/sqlserver/core_ext/database_statements.rb`. - -```ruby -ActiveRecord::ConnectionAdapters::SQLServerAdapter.retry_deadlock_victim = true -``` - - #### Force Schema To Lowercase Although it is not necessary, the Ruby convention is to use lowercase method names. If your database schema is in upper or mixed case, we can force all table and column names during the schema reflection process to be lowercase. Add this to your config/initializers file for the adapter. diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb deleted file mode 100644 index 1b45f5545..000000000 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +++ /dev/null @@ -1,94 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module Sqlserver - module CoreExt - module DatabaseStatements - - # This is a copy of the current (3.1.3) ActiveRecord's transaction method. We should propose - # a patch to the default transaction method to make it more callback for adapters that want to - # do deadlock retry logic. Because this is a copy, we really need to keep an eye out on this when - # upgradding the adapter. - def transaction_with_retry_deadlock_victim(options = {}) - options.assert_valid_keys :requires_new, :joinable - - last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil - if options.has_key?(:joinable) - @transaction_joinable = options[:joinable] - else - @transaction_joinable = true - end - requires_new = options[:requires_new] || !last_transaction_joinable - - transaction_open = false - @_current_transaction_records ||= [] - - begin - if block_given? - if requires_new || open_transactions == 0 - if open_transactions == 0 - begin_db_transaction - elsif requires_new - create_savepoint - end - transaction_open = true - @_current_transaction_records.push([]) - end - yield - end - rescue Exception => database_transaction_rollback - if transaction_open && !outside_transaction? - transaction_open = false - # handle deadlock victim retries at the outermost transaction - if open_transactions == 0 - if database_transaction_rollback.is_a?(::ActiveRecord::DeadlockVictim) - # SQL Server has already rolled back, so rollback activerecord's history - rollback_transaction - retry - else - rollback_db_transaction - #rollback_transaction_records(true) - end - else - rollback_to_savepoint - rollback_transaction - end - end - raise unless database_transaction_rollback.is_a?(::ActiveRecord::Rollback) - end - ensure - @transaction_joinable = last_transaction_joinable - - if outside_transaction? - @open_transactions = 0 - elsif transaction_open - begin - if open_transactions == 0 - commit_db_transaction - # commit_transaction_records - else - release_savepoint - save_point_records = @_current_transaction_records.pop - unless save_point_records.blank? - @_current_transaction_records.push([]) if @_current_transaction_records.empty? - @_current_transaction_records.last.concat(save_point_records) - end - end - rescue Exception => database_transaction_rollback - if open_transactions == 0 - rollback_db_transaction - #rollback_transaction_records(true) - else - rollback_to_savepoint - #rollback_transaction_records(false) - end - raise - end - end - end - - end - end - end - end -end - diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b68aa1914..96e3de63e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -3,8 +3,6 @@ module ConnectionAdapters module Sqlserver module DatabaseStatements - include CoreExt::DatabaseStatements - def select_rows(sql, name = nil) raw_select sql, name, [], :fetch => :rows end @@ -49,14 +47,6 @@ def supports_statement_cache? true end - def transaction(options = {}) - if retry_deadlock_victim? - block_given? ? transaction_with_retry_deadlock_victim(options) { yield } : transaction_with_retry_deadlock_victim(options) - else - block_given? ? super(options) { yield } : super(options) - end - end - def begin_db_transaction do_execute "BEGIN TRANSACTION" end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f054ef32c..942367d29 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -6,7 +6,6 @@ require 'active_support/core_ext/string' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' -require 'active_record/connection_adapters/sqlserver/core_ext/database_statements' require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/relation' @@ -186,9 +185,8 @@ class SQLServerAdapter < AbstractAdapter attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, - :enable_default_unicode_types, :auto_connect, :retry_deadlock_victim, - :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration, - :showplan_option + :enable_default_unicode_types, :auto_connect, :cs_equality_operator, + :lowercase_schema_reflection, :auto_connect_duration, :showplan_option self.enable_default_unicode_types = true @@ -359,11 +357,6 @@ def auto_connect_duration @@auto_connect_duration ||= 10 end - def retry_deadlock_victim - @@retry_deadlock_victim.is_a?(FalseClass) ? false : true - end - alias :retry_deadlock_victim? :retry_deadlock_victim - def native_string_database_type @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') end @@ -509,7 +502,6 @@ def with_sqlserver_error_handling rescue Exception => e case translate_exception(e,e.message) when LostConnection; retry if auto_reconnected? - when DeadlockVictim; retry if retry_deadlock_victim? && open_transactions == 0 end raise end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index bb5dd5850..de6c64d27 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -180,18 +180,7 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase raw_conn.stubs(:execute).raises(deadlock_victim_exception(@query)).then.returns(stubbed_handle) end - teardown do - @connection.class.retry_deadlock_victim = nil - end - - should 'retry by default' do - assert_nothing_raised do - assert_equal @expected, @connection.execute(@query) - end - end - - should 'raise ActiveRecord::DeadlockVictim if retry is disabled' do - @connection.class.retry_deadlock_victim = false + should 'raise ActiveRecord::DeadlockVictim' do assert_raise(ActiveRecord::DeadlockVictim) do assert_equal @expected, @connection.execute(@query) end @@ -228,21 +217,9 @@ def execute_with_deadlock_exception(sql, *args) remove_method :execute_without_deadlock_exception end @connection.send(:remove_instance_variable, :@raised_deadlock_exception) - @connection.class.retry_deadlock_victim = nil - end - - should 'retry by default' do - skip "takes too long" - assert_nothing_raised do - ActiveRecord::Base.transaction do - assert_equal @expected, @connection.execute(@query) - end - end end should 'raise ActiveRecord::DeadlockVictim if retry disabled' do - skip "takes too long" - @connection.class.retry_deadlock_victim = false assert_raise(ActiveRecord::DeadlockVictim) do ActiveRecord::Base.transaction do assert_equal @expected, @connection.execute(@query) From 24c084cbe3fe0fc5b2b6e7149b0ed68f28776e8b Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 12 Dec 2013 19:33:09 -0500 Subject: [PATCH 0060/1412] Include bind_visitor Fix 'undefined method `accept' for nil:NilClass' --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 942367d29..b26500d2f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,5 +1,6 @@ require 'base64' require 'arel/visitors/sqlserver' +require 'arel/visitors/bind_visitor' require 'active_record' require 'active_record/base' require 'active_support/concern' @@ -190,6 +191,9 @@ class SQLServerAdapter < AbstractAdapter self.enable_default_unicode_types = true + class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: + include Arel::Visitors::BindVisitor + end def initialize(connection, logger, pool, config) super(connection, logger, pool) From 5af764a5fcb10589352183f1236bd251cacc26b6 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 16 Dec 2013 13:08:33 -0500 Subject: [PATCH 0061/1412] Added simplecov for test coverage, removed turn --- Gemfile | 2 +- test/cases/sqlserver_helper.rb | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 992d3aed1..177e6f4c0 100644 --- a/Gemfile +++ b/Gemfile @@ -38,7 +38,6 @@ group :odbc do end group :development do - gem "turn" gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'mocha' @@ -46,5 +45,6 @@ group :development do gem 'nokogiri' gem 'rake', '~> 0.9.2' gem 'ruby-prof' + gem 'simplecov' end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index fc7108c17..21c4fd1e5 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -1,13 +1,3 @@ -begin - require 'turn' - - Turn.config do |c| - c.format = :pretty - c.verbose = true - end -rescue LoadError -end - SQLSERVER_TEST_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..')) SQLSERVER_ASSETS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'assets')) SQLSERVER_FIXTURES_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'fixtures')) @@ -21,6 +11,9 @@ require 'rubygems' require 'bundler' Bundler.setup +require 'simplecov' +SimpleCov.start + require 'mocha/api' require 'active_support/dependencies' require 'active_record' @@ -30,7 +23,6 @@ require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' require 'models/topic' - GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly?) ActiveRecord::Migration.verbose = false From 20a117ac92104dd2bddc63cbae65b0494408fb0d Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 16 Dec 2013 13:39:50 -0500 Subject: [PATCH 0062/1412] remove auto_explain_threshold_in_seconds --- CHANGELOG | 3 ++- README.md | 7 ++----- test/cases/showplan_test_sqlserver.rb | 8 -------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index d57fbdbb1..a4718d28c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ * 4.0.0 * -*Removed deadlock victim retry in favor of Isolation Level +* Removed deadlock victim retry in favor of Isolation Level +* Removed auto_explain_threshold_in_seconds (not used in rails 4) * 3.2.12 * diff --git a/README.md b/README.md index 23889daf8..aa1730583 100644 --- a/README.md +++ b/README.md @@ -189,12 +189,9 @@ Another configuration is the showplan option. Some might find the XML format mor ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_XML' ``` -**NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. To disable explain, just set the threshold to nil. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. Change this setting in your ```config/environments/development.rb```: - -```ruby -config.active_record.auto_explain_threshold_in_seconds = nil -``` +# TODO auto_explain_threshold_in_seconds has been removed from rails 4 +**NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. ## Versions diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 6c5a22ac5..25e0b3e5e 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -80,13 +80,6 @@ def with_showplan_option(option) ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = old_option end - def with_threshold(threshold) - current_threshold = base.auto_explain_threshold_in_seconds - base.auto_explain_threshold_in_seconds = threshold - yield - ensure - base.auto_explain_threshold_in_seconds = current_threshold - end def capture_logger original_logger = base.logger @@ -100,7 +93,6 @@ def capture_logger end def capture_queries - base.auto_explain_threshold_in_seconds = nil queries = Thread.current[:available_queries_for_explain] = [] with_threshold(0) do yield From 98a6cff47470f4a99fbcd5eed5805c3dba3ee0d8 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 16 Dec 2013 13:42:15 -0500 Subject: [PATCH 0063/1412] removed deprecated artiverecord syntax --- .../sqlserver/core_ext/explain_subscriber.rb | 2 +- .../sqlserver/database_statements.rb | 13 +++++++++---- .../sqlserver/schema_statements.rb | 4 ---- test/cases/method_scoping_test_sqlserver.rb | 4 ++-- test/cases/offset_and_limit_test_sqlserver.rb | 8 ++++---- test/cases/session_test_sqlserver.rb | 3 +-- test/cases/specific_schema_test_sqlserver.rb | 12 ++++++------ test/config.yml | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 2e3db70b3..6d787d763 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -13,7 +13,7 @@ def call(*args) IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) SQLSERVER_EXPLAINED_SQLS = /(select|update|delete|insert)/i - # Need to modify the regex for the TSQL generated by this adapter so we can explain the proper sql statements + # TODO Need to modify the regex for the TSQL generated by this adapter so we can explain the proper sql statements def ignore_sqlserver_payload?(payload) payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ SQLSERVER_EXPLAINED_SQLS end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 96e3de63e..932069ee1 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -25,6 +25,7 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) end end + # TODO do something with added params. Rails 4 added 2 parameters and we're not using them. def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query sql, name, binds, :insert => true end @@ -128,15 +129,19 @@ def use_database(database=nil) def user_options return {} if sqlserver_azure? select_rows("dbcc useroptions",'SCHEMA').inject(HashWithIndifferentAccess.new) do |values,row| - set_option = row.values[0].gsub(/\s+/,'_') if row.instance_of? Hash - set_option = row[0].gsub(/\s+/,'_') if row.instance_of? Array - user_value = row.values[1] if row.instance_of? Hash - user_value = row[1] if row.instance_of? Array + if row.instance_of? Hash + set_option = row.values[0].gsub(/\s+/,'_') + user_value = row.values[1] + elsif row.instance_of? Array + set_option = row[0].gsub(/\s+/,'_') + user_value = row[1] + end values[set_option] = user_value values end end + # TODO Rails 4 now supports isolation levels def user_options_dateformat if sqlserver_azure? select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA' diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index acf1b243b..f5266b132 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -50,8 +50,6 @@ def rename_table(table_name, new_name) def remove_column(table_name, column_name, type = nil) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if (column_name.is_a? Array) - # TODO: this deprecation warning should be fixed or removed - # ActiveSupport::Deprecation.warn 'Passing multiple arguments to remove_columns is deprecated, please use just one column name, like: `remove_columns(:posts, :column_name, :type)`', caller if column_name remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) @@ -271,8 +269,6 @@ def get_table_name(sql) elsif sql =~ /FROM\s+([^\(\s]+)\s*/i $1 else - # TODO: - puts sql nil end end diff --git a/test/cases/method_scoping_test_sqlserver.rb b/test/cases/method_scoping_test_sqlserver.rb index e2e024eb5..447431bc6 100644 --- a/test/cases/method_scoping_test_sqlserver.rb +++ b/test/cases/method_scoping_test_sqlserver.rb @@ -12,13 +12,13 @@ class NestedScopingTest < ActiveRecord::TestCase fixtures :developers - + # TODO update test for rails 4 def test_coerced_test_merged_scoped_find poor_jamis = developers(:poor_jamis) Developer.send(:with_scope, :find => { :conditions => "salary < 100000" }) do Developer.send(:with_scope, :find => { :offset => 1, :order => 'id asc' }) do assert_sql /ORDER BY id asc/i do - assert_equal(poor_jamis, Developer.first(:order => 'id asc')) + assert_equal(poor_jamis, Developer.order('id asc').first) end end end diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 88f71e55a..8b918c12b 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -21,7 +21,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase context 'When selecting with limit' do should 'alter sql to limit number of records returned' do - assert_sql(/SELECT TOP \(10\)/) { Book.limit(10).all } + assert_sql(/SELECT TOP \(10\)/) { Book.limit(10) } end end @@ -29,7 +29,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase context 'When selecting with offset' do should 'have limit (top) of 9223372036854775807 if only offset is passed' do - assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.all(:offset=>1) } + assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.offset(1) } end should 'support calling exists?' do @@ -47,12 +47,12 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase should 'alter SQL to limit number of records returned offset by specified amount' do sql = %|EXEC sp_executesql N'SELECT TOP (3) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [books].[id] ASC) AS [__rn], [books].* FROM [books] ) AS [__rnt] WHERE [__rnt].[__rn] > (5) ORDER BY [__rnt].[__rn] ASC'| - assert_sql(sql) { Book.limit(3).offset(5).all } + assert_sql(sql) { Book.limit(3).offset(5) } end should 'add locks to deepest sub select' do pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ - assert_sql(pattern) { Book.all :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } + assert_sql(pattern) { Book.load :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } assert_sql(pattern) { Book.count :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } end diff --git a/test/cases/session_test_sqlserver.rb b/test/cases/session_test_sqlserver.rb index 19b4ee702..02f540213 100644 --- a/test/cases/session_test_sqlserver.rb +++ b/test/cases/session_test_sqlserver.rb @@ -1,7 +1,6 @@ require 'cases/sqlserver_helper' require 'action_dispatch' -# TODO: require 'active_record/session_store' -# Active Record's Session Store extracted from Rails + module ActiveRecord class SessionStore class SessionTest < ActiveRecord::TestCase diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 5f753dfd2..470d9180f 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -76,7 +76,7 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'use primary key for row table order in pagination sql' do sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ - assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5).all } + assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5) } end end @@ -113,11 +113,11 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase teardown { @edge_class.delete_all } should 'allow all sorts of ordering without adapter munging it up' do - assert_equal ['A','B','C'], @edge_class.all(:order => 'description').map(&:description) - assert_equal ['A','B','C'], @edge_class.all(:order => 'description asc').map(&:description) - assert_equal ['A','B','C'], @edge_class.all(:order => 'description ASC').map(&:description) - assert_equal ['C','B','A'], @edge_class.all(:order => 'description desc').map(&:description) - assert_equal ['C','B','A'], @edge_class.all(:order => 'description DESC').map(&:description) + assert_equal ['A','B','C'], @edge_class.load.order('description').map(&:description) + assert_equal ['A','B','C'], @edge_class.load.order('description asc').map(&:description) + assert_equal ['A','B','C'], @edge_class.load.order('description ASC').map(&:description) + assert_equal ['C','B','A'], @edge_class.load.order('description desc').map(&:description) + assert_equal ['C','B','A'], @edge_class.load.order('description DESC').map(&:description) end end diff --git a/test/config.yml b/test/config.yml index fe6d5fdbe..312c7b4d9 100644 --- a/test/config.yml +++ b/test/config.yml @@ -9,7 +9,7 @@ default_connection_info: &default_connection_info username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> - timeout: 0 + connections: dblib: From b85ecec928622f93b5f422efb9d76c836052ca81 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 16 Dec 2013 13:44:42 -0500 Subject: [PATCH 0064/1412] ignore simplecov results --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0619b84bf..43f3deb65 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ Gemfile.lock test/profile/output/* .rvmrc .rbenv-version -.idea \ No newline at end of file +.idea +coverage/* \ No newline at end of file From 0e8ff0e077686914fd9665b7945766c613803293 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 19 Dec 2013 16:14:10 -0500 Subject: [PATCH 0065/1412] outside_transaction? deprecated --- .../sqlserver/database_statements.rb | 8 ++----- test/cases/transaction_test_sqlserver.rb | 21 ++----------------- 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 932069ee1..f29b47796 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -25,7 +25,7 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) end end - # TODO do something with added params. Rails 4 added 2 parameters and we're not using them. + #The abstract adapter ignores the last two parameters also def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query sql, name, binds, :insert => true end @@ -39,10 +39,6 @@ def exec_update(sql, name, binds) sql << "; SELECT @@ROWCOUNT AS AffectedRows" super.rows.first.first end - - def outside_transaction? - select_value('SELECT @@TRANCOUNT', 'SCHEMA') == 0 - end def supports_statement_cache? true @@ -61,7 +57,7 @@ def rollback_db_transaction end def create_savepoint - disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" } + # disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" } end def release_savepoint diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 48c2e9998..8f5bfafc4 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -35,25 +35,8 @@ class TransactionTestSqlserver < ActiveRecord::TestCase end context 'Testing #outside_transaction?' do - - should 'work in simple usage' do - assert Ship.connection.outside_transaction? - Ship.connection.begin_db_transaction - assert !Ship.connection.outside_transaction? - Ship.connection.rollback_db_transaction - assert Ship.connection.outside_transaction? - end - - should 'work inside nested transactions' do - assert Ship.connection.outside_transaction? - Ship.transaction do - assert !Ship.connection.outside_transaction? - Ship.transaction do - assert !Ship.connection.outside_transaction? - end - end - assert Ship.connection.outside_transaction? - end + + should 'not call rollback if no transaction is active' do assert_raise RuntimeError do From cb585ec6054aff42e3b4d7734ade12c64a809b38 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 19 Dec 2013 16:44:09 -0500 Subject: [PATCH 0066/1412] readded turn --- Gemfile | 1 + test/cases/sqlserver_helper.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Gemfile b/Gemfile index 177e6f4c0..14bdef02a 100644 --- a/Gemfile +++ b/Gemfile @@ -46,5 +46,6 @@ group :development do gem 'rake', '~> 0.9.2' gem 'ruby-prof' gem 'simplecov' + gem 'turn' end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 21c4fd1e5..8cad55379 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -13,6 +13,13 @@ Bundler.setup require 'simplecov' SimpleCov.start +require 'turn' +Turn.config do |c| + c.format = :dot + #c.trace = 10 + # c.natural = true +c.verbose = true +end require 'mocha/api' require 'active_support/dependencies' From fbfffcaf89d4566556c0ddf95de87824710f58fd Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 11:10:28 -0500 Subject: [PATCH 0067/1412] enable savepoint --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f29b47796..1bb7deb34 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -57,7 +57,7 @@ def rollback_db_transaction end def create_savepoint - # disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" } + disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" } end def release_savepoint From 41ea3b76f2907a38f052d883352820f4f0dfbe48 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 11:11:40 -0500 Subject: [PATCH 0068/1412] regex should allow insert tabl_name without 'into' --- .../connection_adapters/sqlserver/schema_statements.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f5266b132..34415cde8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -264,8 +264,8 @@ def remove_indexes(table_name, column_name) # === SQLServer Specific (Misc Helpers) ========================= # def get_table_name(sql) - if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i - $2 || $3 + if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i + $3 || $4 elsif sql =~ /FROM\s+([^\(\s]+)\s*/i $1 else @@ -326,7 +326,8 @@ def views_real_column_name(table_name,column_name) # === SQLServer Specific (Identity Inserts) ===================== # def query_requires_identity_insert?(sql) - if insert_sql?(sql) + + if insert_sql?(sql) table_name = get_table_name(sql) id_column = identity_column(table_name) id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false From 70f474ee8767a24c8c455db479e2112688b0e784 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 11:12:21 -0500 Subject: [PATCH 0069/1412] removed tests because they're now tested by core --- test/cases/transaction_test_sqlserver.rb | 28 ------------------------ 1 file changed, 28 deletions(-) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 8f5bfafc4..0ab64a163 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -34,34 +34,6 @@ class TransactionTestSqlserver < ActiveRecord::TestCase end - context 'Testing #outside_transaction?' do - - - - should 'not call rollback if no transaction is active' do - assert_raise RuntimeError do - Ship.transaction do - Ship.connection.rollback_db_transaction - Ship.connection.expects(:rollback_db_transaction).never - raise "Rails doesn't scale!" - end - end - end - - should 'test_open_transactions_count_is_reset_to_zero_if_no_transaction_active' do - Ship.transaction do - Ship.transaction do - Ship.connection.rollback_db_transaction - end - assert_equal 0, Ship.connection.open_transactions - end - assert_equal 0, Ship.connection.open_transactions - end - - end - - - protected def delete_ships From 09dd71d445ee36bfa38823003cafd070fe5cf3a9 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 11:27:35 -0500 Subject: [PATCH 0070/1412] updated scoping syntax --- test/cases/method_scoping_test_sqlserver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/method_scoping_test_sqlserver.rb b/test/cases/method_scoping_test_sqlserver.rb index 447431bc6..fc3b84d13 100644 --- a/test/cases/method_scoping_test_sqlserver.rb +++ b/test/cases/method_scoping_test_sqlserver.rb @@ -15,8 +15,8 @@ class NestedScopingTest < ActiveRecord::TestCase # TODO update test for rails 4 def test_coerced_test_merged_scoped_find poor_jamis = developers(:poor_jamis) - Developer.send(:with_scope, :find => { :conditions => "salary < 100000" }) do - Developer.send(:with_scope, :find => { :offset => 1, :order => 'id asc' }) do + Developer.where("salary < 100000").scoping do + Developer.offset(1).order('id asc').scoping do assert_sql /ORDER BY id asc/i do assert_equal(poor_jamis, Developer.order('id asc').first) end From ae7b78a9c72776b46f9323a218fa3c253b9a3bc6 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 11:28:05 -0500 Subject: [PATCH 0071/1412] updated deprecated find syntax --- test/cases/inheritance_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index 9b4bc021f..df0998eed 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -27,7 +27,7 @@ def test_coerced_a_bad_type_column def test_coerced_eager_load_belongs_to_primary_key_quoting con = Account.connection assert_sql(/\[companies\]\.\[id\] IN \(N''1''\)/) do - Account.find(1, :include => :firm) + Account.find(1).includes(:firm) end end From cac085ed26c4050f66b2ddb7dda45e70a1c53a77 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 14:30:02 -0500 Subject: [PATCH 0072/1412] updated deprecated syntax --- test/cases/inheritance_test_sqlserver.rb | 3 ++- test/cases/offset_and_limit_test_sqlserver.rb | 10 ++++----- test/cases/specific_schema_test_sqlserver.rb | 22 +++++++++---------- test/cases/sqlserver_helper.rb | 2 +- test/cases/table_name_test_sqlserver.rb | 2 +- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index df0998eed..bb6be922d 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -24,10 +24,11 @@ def test_coerced_a_bad_type_column assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } end + def test_coerced_eager_load_belongs_to_primary_key_quoting con = Account.connection assert_sql(/\[companies\]\.\[id\] IN \(N''1''\)/) do - Account.find(1).includes(:firm) + Account.includes(:firm).find(1) end end diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 8b918c12b..b13a0499c 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -21,7 +21,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase context 'When selecting with limit' do should 'alter sql to limit number of records returned' do - assert_sql(/SELECT TOP \(10\)/) { Book.limit(10) } + assert_sql(/SELECT TOP \(10\)/) { Book.limit(10).load } end end @@ -29,7 +29,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase context 'When selecting with offset' do should 'have limit (top) of 9223372036854775807 if only offset is passed' do - assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.offset(1) } + assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.offset(1).load } end should 'support calling exists?' do @@ -41,18 +41,18 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase context 'When selecting with limit and offset' do should 'work with fully qualified table and columns in select' do - books = Book.all :select => 'books.id, books.name', :limit => 3, :offset => 5 + books = Book.select('books.id, books.name').limit(3).offset(5) assert_equal Book.all[5,3].map(&:id), books.map(&:id) end should 'alter SQL to limit number of records returned offset by specified amount' do sql = %|EXEC sp_executesql N'SELECT TOP (3) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [books].[id] ASC) AS [__rn], [books].* FROM [books] ) AS [__rnt] WHERE [__rnt].[__rn] > (5) ORDER BY [__rnt].[__rn] ASC'| - assert_sql(sql) { Book.limit(3).offset(5) } + assert_sql(sql) { Book.limit(3).offset(5).load } end should 'add locks to deepest sub select' do pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ - assert_sql(pattern) { Book.load :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } + assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).load } assert_sql(pattern) { Book.count :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 470d9180f..f670e03b6 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -75,8 +75,8 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase end should 'use primary key for row table order in pagination sql' do - sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ - assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5) } + sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ + assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5).load } end end @@ -87,7 +87,7 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase @edge_class.delete_all r = @edge_class.create! 'crazy]]quote' => 'crazyqoute' assert @edge_class.columns_hash['crazy]]quote'] - assert_equal r, @edge_class.first(:conditions => {'crazy]]quote' => 'crazyqoute'}) + assert_equal r, @edge_class.where('crazy]]quote' => 'crazyqoute').first end end @@ -113,11 +113,11 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase teardown { @edge_class.delete_all } should 'allow all sorts of ordering without adapter munging it up' do - assert_equal ['A','B','C'], @edge_class.load.order('description').map(&:description) - assert_equal ['A','B','C'], @edge_class.load.order('description asc').map(&:description) - assert_equal ['A','B','C'], @edge_class.load.order('description ASC').map(&:description) - assert_equal ['C','B','A'], @edge_class.load.order('description desc').map(&:description) - assert_equal ['C','B','A'], @edge_class.load.order('description DESC').map(&:description) + assert_equal ['A','B','C'], @edge_class.order('description').map(&:description) + assert_equal ['A','B','C'], @edge_class.order('description asc').map(&:description) + assert_equal ['A','B','C'], @edge_class.order('description ASC').map(&:description) + assert_equal ['C','B','A'], @edge_class.order('description desc').map(&:description) + assert_equal ['C','B','A'], @edge_class.order('description DESC').map(&:description) end end @@ -133,9 +133,9 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'can find by biginit' do assert_equal @bi5k, @edge_class.find_by_bigint(@b5k) - assert_equal @b5k, @edge_class.first(:select => 'bigint', :conditions => {:bigint => @b5k}).bigint + assert_equal @b5k, @edge_class.select('bigint').where(bigint: @b5k).first.bigint assert_equal @bimjr, @edge_class.find_by_bigint(@bnum) - assert_equal @bnum, @edge_class.first(:select => 'bigint', :conditions => {:bigint => @bnum}).bigint + assert_equal @bnum, @edge_class.select('bigint').where(bigint: @bnum).first.bigint end end @@ -194,7 +194,7 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'handle dollar symbols' do SqlServerDollarTableName.new.save - SqlServerDollarTableName.limit(20).offset(1).all + SqlServerDollarTableName.limit(20).offset(1) end end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 8cad55379..130ba7425 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -71,7 +71,7 @@ def with_spaces=(value) end protected def set_new_id - self[:guid_newid] ||= connection.newid_function if new_id_setting + self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting end end class SqlServerDollarTableName < ActiveRecord::Base diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb index 3c10eb019..ad3628674 100644 --- a/test/cases/table_name_test_sqlserver.rb +++ b/test/cases/table_name_test_sqlserver.rb @@ -19,7 +19,7 @@ class TableNameTestSqlserver < ActiveRecord::TestCase end should 'not re-escape table name if it is escaped already for SQL queries' do - assert_sql(/SELECT \[orders\]\.\* FROM \[orders\]/) { Order.all } + assert_sql(/SELECT \[orders\]\.\* FROM \[orders\]/) { Order.all.load } end context 'Table scoped to user.table_name' do From df364ec64d4814ae943b478d627c6e25e4642f60 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 20 Dec 2013 16:56:32 -0500 Subject: [PATCH 0073/1412] attempted to update explain syntax. More to do, holidays --- README.md | 3 --- test/cases/showplan_test_sqlserver.rb | 12 ++---------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index aa1730583..2fe1ae809 100644 --- a/README.md +++ b/README.md @@ -188,9 +188,6 @@ Another configuration is the showplan option. Some might find the XML format mor ```ruby ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_XML' ``` - -# TODO auto_explain_threshold_in_seconds has been removed from rails 4 - **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. ## Versions diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 25e0b3e5e..194e420e6 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -8,7 +8,7 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase context 'Unprepare previously prepared SQL' do should 'from simple statement' do - plan = Car.where(:id => 1).explain + plan = Car.where(id: 1).explain assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end @@ -19,17 +19,9 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end - should 'from prepared statement' do - plan = capture_logger do - with_threshold(0) { Car.find(1) } - end - assert plan.include?('EXPLAIN for: SELECT TOP (1) [cars].* FROM [cars] WHERE [cars].[id] = @0 [["id", 1]]') - assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' - end - should 'from prepared statement ...' do plan = capture_logger do - with_threshold(0) { Car.where(:name => ',').first } + Car.where(:name => ',').limit(1).explain end assert plan.include?("SELECT TOP (1) [cars].* FROM [cars] WHERE [cars].[name] = N','") assert plan.include?("TOP EXPRESSION"), 'make sure we do not showplan the sp_executesql' From b2cb614bfb72b5f3d12d7fc48c47c0649db75f2a Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 25 Dec 2013 23:10:23 -0500 Subject: [PATCH 0074/1412] ConnectionSpecification is now in the ConnectionAdapters module --- test/cases/resolver_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index 4e0b7faad..12968e1d7 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -1,7 +1,7 @@ require "cases/helper" module ActiveRecord - class Base + module ConnectionAdapters class ConnectionSpecification class ResolverTest < ActiveRecord::TestCase From 15101d613c74f7da51371e14e5916767fd12fde1 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 25 Dec 2013 23:28:25 -0500 Subject: [PATCH 0075/1412] ActiveRecord::Result now has an empty? method --- test/cases/connection_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index de6c64d27..23b151345 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -237,7 +237,7 @@ def execute_with_deadlock_exception(sql, *args) should 'testing #activity_stats' do stats = @connection.activity_stats - assert stats.length > 0 + assert !stats.empty? assert stats.all? { |s| s.has_key?("session_id") } assert stats.all? { |s| s["database"] == @connection.current_database } end From 30d3945e0223dad259c2c8df2ce4101f635b29f5 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 26 Dec 2013 00:31:14 -0500 Subject: [PATCH 0076/1412] switched to rails 1.9 hash syntax --- test/cases/schema_dumper_test_sqlserver.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 0db3f9ba5..4eaaaa9f5 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -11,7 +11,8 @@ class SchemaDumperTestSqlserver < ActiveRecord::TestCase table_dump('movies') do |output| match = output.match(%r{create_table "movies"(.*)do}) assert_not_nil(match, "nonstandardpk table not found") - assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved" + puts "**#{output}" + assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" end end @@ -21,16 +22,16 @@ class SchemaDumperTestSqlserver < ActiveRecord::TestCase should 'include limit constraint that match logic for smallint and bigint in #extract_limit' do table_dump('integer_limits') do |output| - assert_match %r{c_int_1.*:limit => 2}, output - assert_match %r{c_int_2.*:limit => 2}, output + assert_match %r{c_int_1.*limit: 2}, output + assert_match %r{c_int_2.*limit: 2}, output assert_match %r{c_int_3.*}, output assert_match %r{c_int_4.*}, output assert_no_match %r{c_int_3.*:limit}, output assert_no_match %r{c_int_4.*:limit}, output - assert_match %r{c_int_5.*:limit => 8}, output - assert_match %r{c_int_6.*:limit => 8}, output - assert_match %r{c_int_7.*:limit => 8}, output - assert_match %r{c_int_8.*:limit => 8}, output + assert_match %r{c_int_5.*limit: 8}, output + assert_match %r{c_int_6.*limit: 8}, output + assert_match %r{c_int_7.*limit: 8}, output + assert_match %r{c_int_8.*limit: 8}, output end end @@ -79,7 +80,7 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal output = standard_dump - assert_match %r{t.decimal\s+"atoms_in_universe",\s+:precision => 38,\s+:scale => 0}, output + assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output end private From d2cc375f71f00bb8e2f276061d5c0ec229fd35b3 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 26 Dec 2013 01:12:00 -0500 Subject: [PATCH 0077/1412] updated AciveRecord syntax --- test/cases/pessimistic_locking_test_sqlserver.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 0f1ad11e2..726cd3637 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -16,25 +16,25 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase should 'lock with simple find' do assert_nothing_raised do Person.transaction do - Person.find 1, :lock => true + Person.lock(true).find(1) end end end - +#DEPRECATION WARNING: ActiveRecord::Base#with_scope and #with_exclusive_scope are deprecated. Please use ActiveRecord::Relation#scoping instead. (You can use #merge to merge multiple scopes together.). (called from with_scope at /Users/acarey/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-deprecated_finders-1.0.3/lib/active_record/deprecated_finders/base.rb:75) should 'lock with scoped find' do assert_nothing_raised do Person.transaction do - Person.send(:with_scope, :find => { :lock => true }) do - Person.find 1 + Person.lock(true).scoping do + Person.find(1) end end end end should 'lock with eager find' do - assert_nothing_raised do + assert_nothing_raised do Person.transaction do - Person.find 1, :include => :readers, :lock => true + Person.lock(true).includes(:readers).find(1) end end end @@ -52,7 +52,7 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase should 'simply add lock to find all' do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH \(NOLOCK\)| do - Person.all(:lock => 'WITH (NOLOCK)') + Person.lock('WITH (NOLOCK)').load end end @@ -68,7 +68,7 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase eager_ids_sql = /SELECT TOP \(5\).*FROM \[people\] WITH \(NOLOCK\)/ loader_sql = /FROM \[people\] WITH \(NOLOCK\).*WHERE \[people\]\.\[id\] IN/ assert_sql(eager_ids_sql,loader_sql) do - Person.all(:include => :readers, :lock => 'WITH (NOLOCK)', :limit => 5, :offset => 10) + Person.lock('WITH (NOLOCK)').limit(5).offset(10).includes(:readers).references(:readers).load end end From 043b0f1765dabcaecb0f8182b741dcac5c281e2b Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 26 Dec 2013 01:40:33 -0500 Subject: [PATCH 0078/1412] updating deprecated AR syntax --- test/cases/eager_test_sqlserver.rb | 2 +- test/cases/offset_and_limit_test_sqlserver.rb | 11 +++++------ test/cases/schema_dumper_test_sqlserver.rb | 3 +-- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index c80988998..3bcf464e9 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -15,7 +15,7 @@ class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors def test_coerced_count_with_include - assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15") + assert_equal 3, authors(:david).posts_with_comments.where("len(comments.body) > 15").count end diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index b13a0499c..8d905e689 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -53,16 +53,15 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase should 'add locks to deepest sub select' do pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).load } - assert_sql(pattern) { Book.count :limit => 3, :offset => 5, :lock => 'WITH (NOLOCK)' } end should 'have valid sort order' do - order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").all.map(&:c) + order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").load.map(&:c) assert_equal [2, 1, 0], order_row_numbers end should 'work with through associations' do - assert_equal people(:david), jobs(:unicyclist).people.limit(1).offset(1).all.first + assert_equal people(:david), jobs(:unicyclist).people.limit(1).offset(1).first end should 'work with through uniq associations' do @@ -70,8 +69,8 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase mary = authors(:mary) thinking = posts(:thinking) # Mary has duplicate categorizations to the thinking post. - assert_equal [thinking, thinking], mary.categorized_posts.all - assert_equal [thinking], mary.unique_categorized_posts.limit(2).offset(0) + assert_equal [thinking, thinking], mary.categorized_posts.load + assert_equal [thinking], mary.unique_categorized_posts.limit(2).offset(0).load # Paging thru David's uniq ordered comments, with count too. assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], david.ordered_uniq_comments.map(&:id) assert_equal [3, 5], david.ordered_uniq_comments.limit(2).offset(2).map(&:id) @@ -83,7 +82,7 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase should 'remove [__rnt] table names from relation reflection and hence do not eager loading' do create_10_books create_10_books - assert_queries(1) { Book.limit(10).offset(10).includes(:subscriptions).all } + assert_queries(1) { Book.includes(:subscriptions).limit(10).offset(10).references(:subscriptions).load } end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 4eaaaa9f5..5663269b3 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -10,8 +10,7 @@ class SchemaDumperTestSqlserver < ActiveRecord::TestCase should 'honor nonstandards' do table_dump('movies') do |output| match = output.match(%r{create_table "movies"(.*)do}) - assert_not_nil(match, "nonstandardpk table not found") - puts "**#{output}" + assert_not_nil(match, "nonstandardpk table not found") assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" end end From 01e69545c6736423b98355132ac3a04f0683eb80 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 26 Dec 2013 02:06:33 -0500 Subject: [PATCH 0079/1412] minor cleanup --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 3 --- test/cases/offset_and_limit_test_sqlserver.rb | 4 +++- test/cases/pessimistic_locking_test_sqlserver.rb | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b26500d2f..cfb556ef9 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -197,9 +197,6 @@ class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: def initialize(connection, logger, pool, config) super(connection, logger, pool) - # TODO: Is ActiveSupport::Notifications.notifier nil here - # If it is not, does it become nil between here and 'SELECT @@version below' - # If it is, why is it nil here, do we need to change our call to the super impl # AbstractAdapter Responsibility @schema_cache = Sqlserver::SchemaCache.new self @visitor = Arel::Visitors::SQLServer.new self diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 8d905e689..77b0b82f9 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -52,11 +52,13 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase should 'add locks to deepest sub select' do pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ + assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).count } assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).load } + end should 'have valid sort order' do - order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").load.map(&:c) + order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").map(&:c) assert_equal [2, 1, 0], order_row_numbers end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 726cd3637..1f386bb09 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -20,7 +20,7 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase end end end -#DEPRECATION WARNING: ActiveRecord::Base#with_scope and #with_exclusive_scope are deprecated. Please use ActiveRecord::Relation#scoping instead. (You can use #merge to merge multiple scopes together.). (called from with_scope at /Users/acarey/.rvm/gems/ruby-1.9.3-p448/gems/activerecord-deprecated_finders-1.0.3/lib/active_record/deprecated_finders/base.rb:75) + should 'lock with scoped find' do assert_nothing_raised do Person.transaction do From 17769dab3a9d1f0c24138031eb7bb04f9d8311fe Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Mon, 30 Dec 2013 13:51:29 -0500 Subject: [PATCH 0080/1412] Removed outdated explain subscriber code. --- .../sqlserver/core_ext/explain_subscriber.rb | 27 +------- test/cases/showplan_test_sqlserver.rb | 61 ++++++------------- 2 files changed, 19 insertions(+), 69 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 6d787d763..7dbfbf251 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1,26 +1 @@ -module ActiveRecord - module ConnectionAdapters - module Sqlserver - module CoreExt - class ExplainSubscriber - def call(*args) - if queries = Thread.current[:available_queries_for_explain] - payload = args.last - queries << payload.values_at(:sql, :binds) unless ignore_sqlserver_payload?(payload) - end - end - - IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) - SQLSERVER_EXPLAINED_SQLS = /(select|update|delete|insert)/i - - # TODO Need to modify the regex for the TSQL generated by this adapter so we can explain the proper sql statements - def ignore_sqlserver_payload?(payload) - payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ SQLSERVER_EXPLAINED_SQLS - end - - ActiveSupport::Notifications.subscribe("sql.active_record", new) - end - end - end - end -end +ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)/i diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 194e420e6..3a4c1e4f9 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -2,13 +2,13 @@ require 'models/car' class ShowplanTestSqlserver < ActiveRecord::TestCase - + fixtures :cars - + context 'Unprepare previously prepared SQL' do - + should 'from simple statement' do - plan = Car.where(id: 1).explain + plan = Car.where(id: 1).explain assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end @@ -18,44 +18,42 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)") assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end - + should 'from prepared statement ...' do - plan = capture_logger do - Car.where(:name => ',').limit(1).explain - end + plan = Car.where(:name => ',').limit(1).explain assert plan.include?("SELECT TOP (1) [cars].* FROM [cars] WHERE [cars].[name] = N','") assert plan.include?("TOP EXPRESSION"), 'make sure we do not showplan the sp_executesql' assert plan.include?("Clustered Index Scan"), 'make sure we do not showplan the sp_executesql' end - + end - + context 'With SHOWPLAN_TEXT option' do - + should 'use simple table printer' do with_showplan_option('SHOWPLAN_TEXT') do plan = Car.where(:id => 1).explain - assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") + assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end end - + end - + context 'With SHOWPLAN_XML option' do - + should 'show formatted xml' do with_showplan_option('SHOWPLAN_XML') do plan = Car.where(:id => 1).explain assert plan.include?('ShowPlanXML') end end - + end - - + + private - + def base ActiveRecord::Base end @@ -63,7 +61,7 @@ def base def connection base.connection end - + def with_showplan_option(option) old_option = ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = option @@ -71,28 +69,5 @@ def with_showplan_option(option) ensure ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = old_option end - - - def capture_logger - original_logger = base.logger - log = StringIO.new - base.logger = Logger.new(log) - base.logger.level = Logger::WARN - yield - log.string - ensure - base.logger = original_logger - end - def capture_queries - queries = Thread.current[:available_queries_for_explain] = [] - with_threshold(0) do - yield - end - queries - ensure - Thread.current[:available_queries_for_explain] = nil - end - - end From b120c04e4f3da19ef454dce87c7d2d80bbea1583 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Mon, 30 Dec 2013 15:27:54 -0500 Subject: [PATCH 0081/1412] Tightened explain regex. --- .../sqlserver/core_ext/explain_subscriber.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 7dbfbf251..349a40bd8 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1 +1 @@ -ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)/i +ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i From 6265ca866fdb273c76584a0a09c379247732bab8 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Mon, 30 Dec 2013 16:22:03 -0500 Subject: [PATCH 0082/1412] Reset transactions on reconnects and disconnects. --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index cfb556ef9..6d6cf05d8 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -186,7 +186,7 @@ class SQLServerAdapter < AbstractAdapter attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, - :enable_default_unicode_types, :auto_connect, :cs_equality_operator, + :enable_default_unicode_types, :auto_connect, :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration, :showplan_option self.enable_default_unicode_types = true @@ -286,12 +286,14 @@ def active? end def reconnect! + reset_transaction disconnect! connect active? end def disconnect! + reset_transaction @spid = nil case @connection_options[:mode] when :dblib From 8d255174635ed2e664ecc5b9f52455fb96cbf546 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 31 Dec 2013 10:11:27 -0500 Subject: [PATCH 0083/1412] Remoted innacurate expected failure --- RUNNING_UNIT_TESTS.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index d35d68f30..6f6e8bd1d 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -97,6 +97,3 @@ By default, Bundler will download the Rails git repo and use the git tag that ma * Misc Date/Time erros when using ODBC mode. * Misc Date/Time erros when testing SQL Server 2005. - -The `test_update_column_changing_id(PersistencesTest)` fails with `ActiveRecord::StatementInvalid: TinyTds::Error: Cannot update identity column 'id'.: EXEC sp_executesql N'UPDATE [topics] SET [id] = 123 WHERE [topics].[id] = 1; SELECT @@ROWCOUNT AS AffectedRows'` even though if you run the test in isolation using `$ rake test TEST_FILES="test/cases/aaaa_create_tables_test_sqlserver.rb,test/cases/persistence_test_sqlserver.rb"` it will pass. So ignorning it for now. - From f0fa339aa12974a42c8021d3233739629a6e7023 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 31 Dec 2013 13:28:53 -0500 Subject: [PATCH 0084/1412] update regex to strip comma before or after --- .../sqlserver/database_statements.rb | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1bb7deb34..ff2246c02 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -16,8 +16,16 @@ def execute(sql, name = nil) end def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - # This is so no id is tried to be updated - sql.gsub! /, \[id\] = @[0-9]*/, '' if sql =~ /UPDATE/ && sql =~ /, \[id\] = / + # We can't update Identiy columns in sqlserver. So, strip out the id from the update. + if sql =~ /UPDATE/ + # take off a comma before or after. This could probably be done better + if sql =~ /, \[id\] = @?[0-9]*/ + sql.gsub! /, \[id\] = @?[0-9]*/, '' + elsif sql =~ /\s\[id\] = @?[0-9]*,/ + sql.gsub! /\s\[id\] = @?[0-9]*,/, '' + end + end + if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) } else @@ -25,7 +33,7 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) end end - #The abstract adapter ignores the last two parameters also + #The abstract adapter ignores the last two parameters also def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) exec_query sql, name, binds, :insert => true end From 7a6d736cd8f3b439231048b59c558b3bc9b02c8a Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 31 Dec 2013 13:29:15 -0500 Subject: [PATCH 0085/1412] exclude test folder from coverage --- test/cases/sqlserver_helper.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 130ba7425..0af3473d9 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -12,7 +12,9 @@ require 'bundler' Bundler.setup require 'simplecov' -SimpleCov.start +SimpleCov.start do + add_filter "/test/" +end require 'turn' Turn.config do |c| c.format = :dot From b8e908c22fd28aed182559caabb65ef617b01706 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 31 Dec 2013 14:00:49 -0500 Subject: [PATCH 0086/1412] updated persistence test because we can't update an identity column --- test/cases/persistence_test_sqlserver.rb | 25 ++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 2c76821d2..5a56029ba 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -22,7 +22,7 @@ class PersistencesTest < ActiveRecord::TestCase fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans - COERCED_TESTS = [:test_update_all_doesnt_ignore_order] + COERCED_TESTS = [:test_update_all_doesnt_ignore_order, :test_update_columns_changing_id, :test_update_attributes] include SqlserverCoercedTest @@ -43,7 +43,28 @@ def test_coerced_update_all_doesnt_ignore_order end end end - + + def test_coerced_update_attributes + topic = Topic.find(1) + assert !topic.approved? + assert_equal "The First Topic", topic.title + + topic.update_attributes("approved" => true, "title" => "The First Topic Updated") + topic.reload + assert topic.approved? + assert_equal "The First Topic Updated", topic.title + + topic.update_attributes(approved: false, title: "The First Topic") + topic.reload + assert !topic.approved? + assert_equal "The First Topic", topic.title + + assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do + Topic.create!(id: 3, title: "Hm is it possible?") + end + assert_not_equal "Hm is it possible?", Topic.find(3).title + end + end From 3dfff1aa8df7b8db187b81ed28052b750d5c24cf Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 2 Jan 2014 13:34:04 -0500 Subject: [PATCH 0087/1412] added ignores --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 43f3deb65..511d7d68d 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,6 @@ test/profile/output/* .rvmrc .rbenv-version .idea -coverage/* \ No newline at end of file +coverage/* +.flooignore +.floo \ No newline at end of file From f452a0272830d5ddcb8c9cf65fcb10df895aff55 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Fri, 3 Jan 2014 11:01:08 -0500 Subject: [PATCH 0088/1412] Use SQL Service LIMIT syntax. --- test/cases/finder_test_sqlserver.rb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index aca251ad3..1898760fa 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -5,15 +5,16 @@ class FinderTestSqlserver < ActiveRecord::TestCase end class FinderTest < ActiveRecord::TestCase - + COERCED_TESTS = [ :test_exists_does_not_select_columns_without_alias, :test_string_sanitation, - :test_first_and_last_with_integer_should_use_sql_limit + :test_first_and_last_with_integer_should_use_sql_limit, + :test_take_and_first_and_last_with_integer_should_use_sql_limit ] - + include SqlserverCoercedTest - + def test_coerced_exists_does_not_select_columns_without_alias assert_sql(/SELECT TOP \(1\) 1 AS one FROM \[topics\]/i) do Topic.exists? @@ -24,11 +25,17 @@ def test_coerced_string_sanitation assert_not_equal "N'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") end - + def test_coerced_first_and_last_with_integer_should_use_sql_limit assert_sql(/TOP \(2\)/) { Topic.first(2).entries } assert_sql(/TOP \(5\)/) { Topic.last(5).entries } end - + + def test_coerced_take_and_first_and_last_with_integer_should_use_sql_limit + assert_sql(/TOP \(3\)/) { Topic.take(3).entries } + assert_sql(/TOP \(2\)/) { Topic.first(2).entries } + assert_sql(/TOP \(5\)/) { Topic.last(5).entries } + end + end From fc41976a90346b00178e1edf3dbe73f7a3de0f44 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Fri, 3 Jan 2014 11:46:04 -0500 Subject: [PATCH 0089/1412] Rename column and associated index gets renamed too. --- .../sqlserver/schema_statements.rb | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 34415cde8..1a63a77d1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -70,7 +70,6 @@ def change_column(table_name, column_name, type, options = {}) end sql_commands.each { |c| do_execute(c) } end - def change_column_default(table_name, column_name, default) remove_default_constraint(table_name, column_name) do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}" @@ -80,6 +79,11 @@ def rename_column(table_name, column_name, new_column_name) schema_cache.clear_table_cache!(table_name) detect_column_for! table_name, column_name do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" + rename_column_indexes(table_name, column_name, new_column_name) + end + + def rename_index(table_name, old_name, new_name) + execute "EXEC sp_rename N'#{table_name}.#{old_name}', N'#{new_name}', N'INDEX'" end def remove_index!(table_name, index_name) @@ -326,8 +330,8 @@ def views_real_column_name(table_name,column_name) # === SQLServer Specific (Identity Inserts) ===================== # def query_requires_identity_insert?(sql) - - if insert_sql?(sql) + + if insert_sql?(sql) table_name = get_table_name(sql) id_column = identity_column(table_name) id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false @@ -354,7 +358,7 @@ def set_identity_insert(table_name, enable = true) rescue Exception => e raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end - + def identity_column(table_name) schema_cache.columns(table_name).detect(&:is_identity?) end From 8dfd4eb6428635c945e36cda45ba126891182d91 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Fri, 3 Jan 2014 15:33:23 -0500 Subject: [PATCH 0090/1412] fixed index names and constraints --- .../sqlserver/schema_statements.rb | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 1a63a77d1..a6f514a40 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -46,6 +46,7 @@ def columns(table_name, name = nil) def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" + rename_table_indexes(table_name, new_name) end def remove_column(table_name, column_name, type = nil) @@ -58,18 +59,28 @@ def remove_column(table_name, column_name, type = nil) def change_column(table_name, column_name, type, options = {}) sql_commands = [] + indexes = [] column_object = schema_cache.columns(table_name).detect { |c| c.name.to_s == column_name.to_s } - change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - change_column_sql << " NOT NULL" if options[:null] == false - sql_commands << change_column_sql + if options_include_default?(options) || (column_object && column_object.type != type.to_sym) - remove_default_constraint(table_name,column_name) + remove_default_constraint(table_name,column_name) + indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) } + remove_indexes(table_name, column_name) end + sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(options[:default])} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + sql_commands[-1] << " NOT NULL" if !options[:null].nil? && options[:null] == false if options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" end + + #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.collect {|c|quote_column_name(c)}.join(', ')})" + end sql_commands.each { |c| do_execute(c) } end + def change_column_default(table_name, column_name, default) remove_default_constraint(table_name, column_name) do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}" @@ -80,6 +91,7 @@ def rename_column(table_name, column_name, new_column_name) detect_column_for! table_name, column_name do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" rename_column_indexes(table_name, column_name, new_column_name) + schema_cache.clear_table_cache!(table_name) end def rename_index(table_name, old_name, new_name) From 09578c2d319e0ea446ed0255a6d367636fa09503 Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 4 Jan 2014 00:50:20 -0500 Subject: [PATCH 0091/1412] no need to check for ruby 1.8.7. It's not supported --- lib/active_record/connection_adapters/sqlserver/quoting.rb | 4 ---- test/cases/sqlserver_helper.rb | 2 -- test/cases/unicode_test_sqlserver.rb | 2 +- test/profile/gc_profile_case.rb | 2 -- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 02776b81e..5a41ac5ce 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -71,10 +71,6 @@ def quoted_datetime(value) if value.is_a?(Date) time_zone_qualified_value.to_time.xmlschema.to(18) else - # CHANGED [Ruby 1.8] Not needed when 1.8 is dropped. - if value.is_a?(ActiveSupport::TimeWithZone) && RUBY_VERSION < '1.9' - time_zone_qualified_value = time_zone_qualified_value.to_time - end time_zone_qualified_value.iso8601(3).to(22) end else diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 0af3473d9..df2855f64 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -131,14 +131,12 @@ def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get( def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end def sqlserver_2008? ; ActiveRecord::Base.connection.sqlserver_2008? ; end def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end - def ruby_19? ; RUBY_VERSION >= '1.9' ; end end def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_2005? ; self.class.sqlserver_2005? ; end def sqlserver_2008? ; self.class.sqlserver_2008? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end - def ruby_19? ; self.class.ruby_19? ; end def with_enable_default_unicode_types? ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types.is_a?(TrueClass) end diff --git a/test/cases/unicode_test_sqlserver.rb b/test/cases/unicode_test_sqlserver.rb index e96ea86e5..18852ae05 100644 --- a/test/cases/unicode_test_sqlserver.rb +++ b/test/cases/unicode_test_sqlserver.rb @@ -38,7 +38,7 @@ class UnicodeTestSqlserver < ActiveRecord::TestCase else raise 'need to add a case for this' end - assert_equal Encoding.find('UTF-8'), data.nvarchar.encoding if ruby_19? + assert_equal Encoding.find('UTF-8'), data.nvarchar.encoding end end diff --git a/test/profile/gc_profile_case.rb b/test/profile/gc_profile_case.rb index d3eb2605b..b75ce4511 100644 --- a/test/profile/gc_profile_case.rb +++ b/test/profile/gc_profile_case.rb @@ -3,8 +3,6 @@ require 'models/topic' require 'models/reply' -raise "GC allocation benchmarks only supported on Ruby 1.9!" unless RUBY_VERSION >= '1.9' - class GcProfileCase < ActiveRecord::TestCase fixtures :topics From 5413c675172e246626a461e30dec8cc243a1406d Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 4 Jan 2014 00:51:59 -0500 Subject: [PATCH 0092/1412] added columns_for_distinct (copied from postgres) --- .../sqlserver/schema_statements.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a6f514a40..6b5eb394f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -44,6 +44,20 @@ def columns(table_name, name = nil) end end + # like postgres, sqlserver requires the ORDER BY columns in the select list for distinct queries, and + # requires that the ORDER BY include the distinct column. + # this method is idental to the postgres method + def columns_for_distinct(columns, orders) #:nodoc: + order_columns = orders.map{ |s| + # Convert Arel node to string + s = s.to_sql unless s.is_a?(String) + # Remove any ASC/DESC modifiers + s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') + }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + + [super, *order_columns].join(', ') + end + def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" rename_table_indexes(table_name, new_name) From 60a358c5185bc3951f304efc7c462d1480dd12ca Mon Sep 17 00:00:00 2001 From: Anna Date: Sat, 4 Jan 2014 01:19:27 -0500 Subject: [PATCH 0093/1412] force ASCII_8BIT encoding --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6d6cf05d8..b63d753ef 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -63,13 +63,16 @@ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = { end class << self - # TODO: I think this has problems with ruby 2.0 + def string_to_binary(value) "0x#{value.unpack("H*")[0]}" end def binary_to_string(value) - value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*') + if value.encoding != Encoding::ASCII_8BIT + value = value.force_encoding(Encoding::ASCII_8BIT) + end + value end end From 265e8c74d36b8c233d38e6bdda9628f46aa13f1c Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 5 Jan 2014 20:12:36 -0500 Subject: [PATCH 0094/1412] Binary test doesn't need to be coerced anymore --- CHANGELOG | 2 +- test/cases/binary_test_sqlserver.rb | 14 -------------- test/cases/method_scoping_test_sqlserver.rb | 1 - 3 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 test/cases/binary_test_sqlserver.rb diff --git a/CHANGELOG b/CHANGELOG index a4718d28c..bc07b3d4c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,5 @@ * 4.0.0 * - +* Dropped support for ruby 1.8.7 * Removed deadlock victim retry in favor of Isolation Level * Removed auto_explain_threshold_in_seconds (not used in rails 4) diff --git a/test/cases/binary_test_sqlserver.rb b/test/cases/binary_test_sqlserver.rb deleted file mode 100644 index d2d323ff0..000000000 --- a/test/cases/binary_test_sqlserver.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'cases/sqlserver_helper' - -class BinaryTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_mixed_encoding] - - include SqlserverCoercedTest - - def test_coerced_mixed_encoding - assert true # We do encodings right. - end - -end - diff --git a/test/cases/method_scoping_test_sqlserver.rb b/test/cases/method_scoping_test_sqlserver.rb index fc3b84d13..89c93edbe 100644 --- a/test/cases/method_scoping_test_sqlserver.rb +++ b/test/cases/method_scoping_test_sqlserver.rb @@ -12,7 +12,6 @@ class NestedScopingTest < ActiveRecord::TestCase fixtures :developers - # TODO update test for rails 4 def test_coerced_test_merged_scoped_find poor_jamis = developers(:poor_jamis) Developer.where("salary < 100000").scoping do From 17499e72e27416274b3aa89919f3213e624a5efb Mon Sep 17 00:00:00 2001 From: Anna Date: Sun, 5 Jan 2014 20:17:25 -0500 Subject: [PATCH 0095/1412] temp fix. adding mysql --- Gemfile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gemfile b/Gemfile index 14bdef02a..4dc274649 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,10 @@ group :odbc do end group :development do + # TODO: activerecord/test/cases/invalid_connection_test.rb:12 requires + # mysql. Take this out and override that + gem 'mysql' + gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'mocha' From 94b977d60a26c014c3d8fa12ab9c283777355c55 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 6 Jan 2014 00:07:25 -0500 Subject: [PATCH 0096/1412] override test because sqlserver autoconnects --- test/cases/connection_test_sqlserver.rb | 9 ------- test/cases/disconnected_test_sqlserver.rb | 32 +++++++++++++++++++++++ test/cases/migration_test_sqlserver.rb | 12 --------- test/cases/sqlserver_helper.rb | 8 ++++++ 4 files changed, 40 insertions(+), 21 deletions(-) create mode 100644 test/cases/disconnected_test_sqlserver.rb diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 23b151345..3c546a19c 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -272,13 +272,4 @@ def deadlock_victim_exception(sql) error end - - def with_auto_connect(boolean) - existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect - ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean - yield - ensure - ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing - end - end diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb new file mode 100644 index 000000000..5592267ba --- /dev/null +++ b/test/cases/disconnected_test_sqlserver.rb @@ -0,0 +1,32 @@ +require 'cases/sqlserver_helper' + +class TestDisconnectedAdapter < ActiveRecord::TestCase + def setup + skip "TestDisconnectedAdapterSqlserver instead " + end +end + +class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase + self.use_transactional_fixtures = false + + def setup + skip "in-memory database mustn't disconnect" if in_memory_db? + @connection = ActiveRecord::Base.connection + end + + def teardown + return if in_memory_db? + spec = ActiveRecord::Base.connection_config + ActiveRecord::Base.establish_connection(spec) + end + + test "can't execute statements while disconnected" do + with_auto_connect(false) do + @connection.execute "SELECT count(*) from products" + @connection.disconnect! + assert_raises(ActiveRecord::LostConnection) do + @connection.execute "SELECT count(*) from products" + end + end + end +end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 07f942416..f90d7c278 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -70,15 +70,3 @@ class MigrationTest < ActiveRecord::TestCase def test_coerced_test_migrator_db_has_no_schema_migrations_table ; assert true ; end end end - -class ChangeTableMigrationsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_string_creates_string_column] - include SqlserverCoercedTest - def test_coerced_string_creates_string_column - with_change_table do |t| - @connection.expects(:add_column).with(:delete_me, :foo, 'nvarchar(255)', {}) - @connection.expects(:add_column).with(:delete_me, :bar, 'nvarchar(255)', {}) - t.string :foo, :bar - end - end -end \ No newline at end of file diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index df2855f64..32920e101 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -153,6 +153,14 @@ def with_enable_default_unicode_types(setting) ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = old_text ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = old_string end + + def with_auto_connect(boolean) + existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect + ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean + yield + ensure + ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing + end end end From e9c52900da3ffb11b01246c269ae1ca613d2ac6e Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 6 Jan 2014 14:39:40 -0500 Subject: [PATCH 0097/1412] fixed connection tests --- test/cases/disconnected_test_sqlserver.rb | 1 + test/cases/invalid_connection_test_sqlserver.rb | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 test/cases/invalid_connection_test_sqlserver.rb diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 5592267ba..e6072be02 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -1,4 +1,5 @@ require 'cases/sqlserver_helper' +require 'cases/disconnected_test' class TestDisconnectedAdapter < ActiveRecord::TestCase def setup diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb new file mode 100644 index 000000000..4be328746 --- /dev/null +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -0,0 +1,10 @@ +require 'cases/sqlserver_helper' +require 'cases/invalid_connection_test' + +class TestAdapterWithInvalidConnection < ActiveRecord::TestCase + def setup + #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). + #It makes much more sense for use to use sqlserver + Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' + end +end From 8fb83a84fe3301ed6e8c6e86a424ad2909852226 Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Mon, 6 Jan 2014 22:05:31 -0500 Subject: [PATCH 0098/1412] Fix context "with a DateTime" should "return an ISO 8601 datetime string" test failure. --- .../connection_adapters/sqlserver/quoting.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 5a41ac5ce..c7c5ab432 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -2,10 +2,10 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module Quoting - + QUOTED_TRUE, QUOTED_FALSE = '1', '0' QUOTED_STRING_PREFIX = 'N' - + def quote(value, column = nil) case value when String, ActiveSupport::Multibyte::Chars @@ -32,11 +32,11 @@ def quote(value, column = nil) super end end - + def quoted_string_prefix QUOTED_STRING_PREFIX end - + def quote_string(string) string.to_s.gsub(/\'/, "''") end @@ -48,7 +48,7 @@ def quote_column_name(name) def quote_table_name(name) quote_column_name(name) end - + def substitute_at(column, index) if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' nil @@ -69,7 +69,7 @@ def quoted_datetime(value) if value.acts_like?(:time) time_zone_qualified_value = quoted_value_acts_like_time_filter(value) if value.is_a?(Date) - time_zone_qualified_value.to_time.xmlschema.to(18) + time_zone_qualified_value.iso8601(3).to(18) else time_zone_qualified_value.iso8601(3).to(22) end @@ -77,7 +77,7 @@ def quoted_datetime(value) quoted_date(value) end end - + def quoted_full_iso8601(value) if value.acts_like?(:time) value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22) @@ -95,14 +95,14 @@ def quoted_date(value) super end end - + protected - + def quoted_value_acts_like_time_filter(value) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value end - + end end end From b097ce348a5d6de291eaa4a233e8b93a8b838a5b Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 7 Jan 2014 11:40:16 -0500 Subject: [PATCH 0099/1412] removed mysql --- Gemfile | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 4dc274649..9a1d322e2 100644 --- a/Gemfile +++ b/Gemfile @@ -38,10 +38,6 @@ group :odbc do end group :development do - # TODO: activerecord/test/cases/invalid_connection_test.rb:12 requires - # mysql. Take this out and override that - gem 'mysql' - gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'mocha' @@ -51,5 +47,6 @@ group :development do gem 'ruby-prof' gem 'simplecov' gem 'turn' + gem 'rubocop' end From 20f41902fb0145eafd260d1f7ea408613ec2bbf7 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 7 Jan 2014 23:38:44 -0500 Subject: [PATCH 0100/1412] =?UTF-8?q?moved=20schema=20setup=20to=20the=20h?= =?UTF-8?q?elper=20because=20it=E2=80=99s=20not=20a=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aaaa_create_tables_test_sqlserver.rb | 19 ------------------- test/cases/sqlserver_helper.rb | 7 +++++++ 2 files changed, 7 insertions(+), 19 deletions(-) delete mode 100644 test/cases/aaaa_create_tables_test_sqlserver.rb diff --git a/test/cases/aaaa_create_tables_test_sqlserver.rb b/test/cases/aaaa_create_tables_test_sqlserver.rb deleted file mode 100644 index 6aa788455..000000000 --- a/test/cases/aaaa_create_tables_test_sqlserver.rb +++ /dev/null @@ -1,19 +0,0 @@ -# The filename begins with "aaaa" to ensure this is the first test. -require 'cases/sqlserver_helper' - -class AAAACreateTablesTestSqlserver < ActiveRecord::TestCase - - self.use_transactional_fixtures = false - - should 'load activerecord schema then sqlserver specific schema' do - # Core AR. - schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" - eval(File.read(schema_file)) - assert true - # SQL Server. - sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" - eval(File.read(sqlserver_specific_schema_file)) - assert true - end - -end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 32920e101..ae2c5ec25 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -164,4 +164,11 @@ def with_auto_connect(boolean) end end + # Core AR. + schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" + eval(File.read(schema_file)) + + # SQL Server. + sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" + eval(File.read(sqlserver_specific_schema_file)) From 8a6efaa6fc44d4a8c91dedb0356bd60f6c58b7c3 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 7 Jan 2014 23:39:30 -0500 Subject: [PATCH 0101/1412] fix coerced tests when files are run individually --- test/cases/sqlserver_helper.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index ae2c5ec25..c50d28eb4 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -98,10 +98,22 @@ module SqlserverCoercedTest def self.included(base) base.extend ClassMethods end + module ClassMethods + + def self.extended(base) + base.class_eval do + Array(coerced_tests).each do |method_name| + undef_method(method_name) rescue nil + STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method_name}") + end + end + end + def coerced_tests self.const_get(:COERCED_TESTS) rescue nil end + def method_added(method) if coerced_tests && coerced_tests.include?(method) undef_method(method) rescue nil From 97df2b6dfdb0dab3f8a1ab2e0950caaa2b40d27a Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 7 Jan 2014 23:41:32 -0500 Subject: [PATCH 0102/1412] =?UTF-8?q?clean=20up=20sqlserver=5Fhelper=20-?= =?UTF-8?q?=20Move=20models=20to=20their=20own=20directory=20and=20don?= =?UTF-8?q?=E2=80=99t=20require=20all=20models=20for=20every=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cases/adapter_test_sqlserver.rb | 8 +++ .../cases/attribute_methods_test_sqlserver.rb | 1 + test/cases/column_test_sqlserver.rb | 7 +++ test/cases/connection_test_sqlserver.rb | 1 + test/cases/finder_test_sqlserver.rb | 1 + test/cases/migration_test_sqlserver.rb | 1 + test/cases/offset_and_limit_test_sqlserver.rb | 1 + test/cases/persistence_test_sqlserver.rb | 1 + .../pessimistic_locking_test_sqlserver.rb | 1 + test/cases/schema_test_sqlserver.rb | 2 + test/cases/specific_schema_test_sqlserver.rb | 10 +++- test/cases/sqlserver_helper.rb | 53 ------------------- 12 files changed, 33 insertions(+), 54 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 20985ca9a..fee5abeaf 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -5,6 +5,14 @@ require 'models/subscriber' require 'models/minimalistic' require 'models/post' +require 'models_sqlserver/fk_test_has_pk' +require 'models_sqlserver/fk_test_has_fk' +require 'models_sqlserver/customers_view' +require 'models_sqlserver/sql_server_chronic' +require 'models_sqlserver/string_defaults_big_view' +require 'models_sqlserver/string_defaults_view' +require 'models_sqlserver/topic' +require 'models_sqlserver/upper_test_default' class AdapterTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 2e49e5ba0..c2168bf49 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -1,6 +1,7 @@ require 'cases/sqlserver_helper' require 'models/developer' require 'models/topic' +require 'models_sqlserver/topic' class AttributeMethodsTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 6c062d265..81e79a3e0 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,5 +1,12 @@ require 'cases/sqlserver_helper' require 'models/binary' +require 'models_sqlserver/float_data' +require 'models_sqlserver/numeric_data' +require 'models_sqlserver/sql_server_chronic' +require 'models_sqlserver/sql_server_string' +require 'models_sqlserver/sql_server_unicode' +require 'models_sqlserver/table_with_real_column' +require 'models_sqlserver/topic' class ColumnTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 3c546a19c..ac9633467 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/reply' +require 'models_sqlserver/topic' class ConnectionTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 1898760fa..8c96fc739 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/event' +require 'models_sqlserver/topic' class FinderTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index f90d7c278..4fa963815 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/person' +require 'models_sqlserver/person' class MigrationTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 77b0b82f9..3b0b63346 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -8,6 +8,7 @@ require 'models/post' require 'models/comment' require 'models/categorization' +require 'models_sqlserver/sql_server_order_row_number' class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 5a56029ba..1034900d8 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -13,6 +13,7 @@ require 'models/parrot' require 'models/minivan' require 'models/person' +require 'models_sqlserver/topic' require 'rexml/document' class PersistencesTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 1f386bb09..b02c5f474 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,6 +1,7 @@ require 'cases/sqlserver_helper' require 'models/person' require 'models/reader' +require 'models_sqlserver/person' class PessimisticLockingTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 40e309113..40557d6dc 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,4 +1,6 @@ require 'cases/sqlserver_helper' +require 'models_sqlserver/sql_server_natural_pk_data' +require 'models_sqlserver/sql_server_natural_pk_data_schema' class SchemaTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index f670e03b6..249571c20 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,5 +1,13 @@ require 'cases/sqlserver_helper' - +require 'models_sqlserver/no_pk_data' +require 'models_sqlserver/sql_server_dollar_table_name' +require 'models_sqlserver/sql_server_edge_schema' +require 'models_sqlserver/sql_server_natural_pk_int_data' +require 'models_sqlserver/sql_server_quoted_table' +require 'models_sqlserver/sql_server_quoted_view_1' +require 'models_sqlserver/sql_server_quoted_view_2' +require 'models_sqlserver/sql_server_tinyint_pk' +require 'models_sqlserver/string_default' class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'be able to complex count tables with no primary key' do diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index c50d28eb4..a6858e5e5 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -38,59 +38,6 @@ ActiveRecord::Base.logger = Logger.new(File.expand_path(File.join(SQLSERVER_TEST_ROOT,'debug.log'))) ActiveRecord::Base.logger.level = 0 -# Defining our classes in one place as well as some core tests that need coercing date/time types. - -class UpperTestDefault < ActiveRecord::Base ; self.table_name = 'UPPER_TESTS' ; end -class UpperTestLowered < ActiveRecord::Base ; self.table_name = 'upper_tests' ; end -class TableWithRealColumn < ActiveRecord::Base; end -class FkTestHasFk < ActiveRecord::Base ; end -class FkTestHasPk < ActiveRecord::Base ; end -class NumericData < ActiveRecord::Base ; self.table_name = 'numeric_data' ; end -class FloatData < ActiveRecord::Base ; self.table_name = 'float_data' ; end -class CustomersView < ActiveRecord::Base ; self.table_name = 'customers_view' ; end -class StringDefaultsView < ActiveRecord::Base ; self.table_name = 'string_defaults_view' ; end -class StringDefaultsBigView < ActiveRecord::Base ; self.table_name = 'string_defaults_big_view' ; end -class SqlServerNaturalPkData < ActiveRecord::Base ; self.table_name = 'natural_pk_data' ; self.primary_key = 'legacy_id' ; end -class SqlServerTinyintPk < ActiveRecord::Base ; self.table_name = 'tinyint_pk_table' ; end -class SqlServerNaturalPkIntData < ActiveRecord::Base ; self.table_name = 'natural_pk_int_data' ; end -class SqlServerOrderRowNumber < ActiveRecord::Base ; self.table_name = 'order_row_number' ; end -class SqlServerNaturalPkDataSchema < ActiveRecord::Base ; self.table_name = 'test.sql_server_schema_natural_id' ; end -class SqlServerQuotedTable < ActiveRecord::Base ; self.table_name = 'quoted-table' ; end -class SqlServerQuotedView1 < ActiveRecord::Base ; self.table_name = 'quoted-view1' ; end -class SqlServerQuotedView2 < ActiveRecord::Base ; self.table_name = 'quoted-view2' ; end -class SqlServerUnicode < ActiveRecord::Base ; end -class SqlServerString < ActiveRecord::Base ; end -class NoPkData < ActiveRecord::Base ; self.table_name = 'no_pk_data' ; end -class StringDefault < ActiveRecord::Base; end -class SqlServerEdgeSchema < ActiveRecord::Base - attr_accessor :new_id_setting - before_create :set_new_id - def with_spaces - read_attribute :'with spaces' - end - def with_spaces=(value) - write_attribute :'with spaces', value - end - protected - def set_new_id - self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting - end -end -class SqlServerDollarTableName < ActiveRecord::Base - self.table_name = 'my$strange_table' -end -class SqlServerChronic < ActiveRecord::Base - coerce_sqlserver_date :date - coerce_sqlserver_time :time - default_timezone = :utc -end -class Topic < ActiveRecord::Base - coerce_sqlserver_date :last_read - coerce_sqlserver_time :bonus_time -end -class Person < ActiveRecord::Base - coerce_sqlserver_date :favorite_day -end # A module that we can include in classes where we want to override an active record test. From 77c58b1e8792553f8c96a4b0141c01f0c5887ffd Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 7 Jan 2014 23:41:32 -0500 Subject: [PATCH 0103/1412] =?UTF-8?q?clean=20up=20sqlserver=5Fhelper=20-?= =?UTF-8?q?=20Move=20models=20to=20their=20own=20directory=20and=20don?= =?UTF-8?q?=E2=80=99t=20require=20all=20models=20for=20every=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cases/adapter_test_sqlserver.rb | 8 +++ .../cases/attribute_methods_test_sqlserver.rb | 1 + test/cases/column_test_sqlserver.rb | 7 +++ test/cases/connection_test_sqlserver.rb | 1 + test/cases/finder_test_sqlserver.rb | 1 + test/cases/migration_test_sqlserver.rb | 1 + test/cases/offset_and_limit_test_sqlserver.rb | 1 + test/cases/persistence_test_sqlserver.rb | 1 + .../pessimistic_locking_test_sqlserver.rb | 1 + test/cases/schema_test_sqlserver.rb | 2 + test/cases/specific_schema_test_sqlserver.rb | 10 +++- test/cases/sqlserver_helper.rb | 53 ------------------- test/models_sqlserver/customers_view.rb | 3 ++ test/models_sqlserver/fk_test_has_fk.rb | 2 + test/models_sqlserver/fk_test_has_pk.rb | 2 + test/models_sqlserver/float_data.rb | 3 ++ test/models_sqlserver/no_pk_data.rb | 3 ++ test/models_sqlserver/numeric_data.rb | 3 ++ test/models_sqlserver/person.rb | 3 ++ test/models_sqlserver/sql_server_chronic.rb | 5 ++ .../sql_server_dollar_table_name.rb | 3 ++ .../sql_server_edge_schema.rb | 17 ++++++ .../sql_server_natural_pk_data.rb | 4 ++ .../sql_server_natural_pk_data_schema.rb | 3 ++ .../sql_server_natural_pk_int_data.rb | 3 ++ .../sql_server_order_row_number.rb | 3 ++ .../sql_server_quoted_table.rb | 3 ++ .../sql_server_quoted_view_1.rb | 3 ++ .../sql_server_quoted_view_2.rb | 3 ++ test/models_sqlserver/sql_server_string.rb | 2 + .../models_sqlserver/sql_server_tinyint_pk.rb | 3 ++ test/models_sqlserver/sql_server_unicode.rb | 2 + test/models_sqlserver/string_default.rb | 2 + .../string_defaults_big_view.rb | 3 ++ test/models_sqlserver/string_defaults_view.rb | 3 ++ .../table_with_real_column.rb | 2 + test/models_sqlserver/topic.rb | 4 ++ test/models_sqlserver/upper_test_default.rb | 3 ++ test/models_sqlserver/upper_test_lowered.rb | 3 ++ 39 files changed, 126 insertions(+), 54 deletions(-) create mode 100644 test/models_sqlserver/customers_view.rb create mode 100644 test/models_sqlserver/fk_test_has_fk.rb create mode 100644 test/models_sqlserver/fk_test_has_pk.rb create mode 100644 test/models_sqlserver/float_data.rb create mode 100644 test/models_sqlserver/no_pk_data.rb create mode 100644 test/models_sqlserver/numeric_data.rb create mode 100644 test/models_sqlserver/person.rb create mode 100644 test/models_sqlserver/sql_server_chronic.rb create mode 100644 test/models_sqlserver/sql_server_dollar_table_name.rb create mode 100644 test/models_sqlserver/sql_server_edge_schema.rb create mode 100644 test/models_sqlserver/sql_server_natural_pk_data.rb create mode 100644 test/models_sqlserver/sql_server_natural_pk_data_schema.rb create mode 100644 test/models_sqlserver/sql_server_natural_pk_int_data.rb create mode 100644 test/models_sqlserver/sql_server_order_row_number.rb create mode 100644 test/models_sqlserver/sql_server_quoted_table.rb create mode 100644 test/models_sqlserver/sql_server_quoted_view_1.rb create mode 100644 test/models_sqlserver/sql_server_quoted_view_2.rb create mode 100644 test/models_sqlserver/sql_server_string.rb create mode 100644 test/models_sqlserver/sql_server_tinyint_pk.rb create mode 100644 test/models_sqlserver/sql_server_unicode.rb create mode 100644 test/models_sqlserver/string_default.rb create mode 100644 test/models_sqlserver/string_defaults_big_view.rb create mode 100644 test/models_sqlserver/string_defaults_view.rb create mode 100644 test/models_sqlserver/table_with_real_column.rb create mode 100644 test/models_sqlserver/topic.rb create mode 100644 test/models_sqlserver/upper_test_default.rb create mode 100644 test/models_sqlserver/upper_test_lowered.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 20985ca9a..fee5abeaf 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -5,6 +5,14 @@ require 'models/subscriber' require 'models/minimalistic' require 'models/post' +require 'models_sqlserver/fk_test_has_pk' +require 'models_sqlserver/fk_test_has_fk' +require 'models_sqlserver/customers_view' +require 'models_sqlserver/sql_server_chronic' +require 'models_sqlserver/string_defaults_big_view' +require 'models_sqlserver/string_defaults_view' +require 'models_sqlserver/topic' +require 'models_sqlserver/upper_test_default' class AdapterTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 2e49e5ba0..c2168bf49 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -1,6 +1,7 @@ require 'cases/sqlserver_helper' require 'models/developer' require 'models/topic' +require 'models_sqlserver/topic' class AttributeMethodsTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 6c062d265..81e79a3e0 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,5 +1,12 @@ require 'cases/sqlserver_helper' require 'models/binary' +require 'models_sqlserver/float_data' +require 'models_sqlserver/numeric_data' +require 'models_sqlserver/sql_server_chronic' +require 'models_sqlserver/sql_server_string' +require 'models_sqlserver/sql_server_unicode' +require 'models_sqlserver/table_with_real_column' +require 'models_sqlserver/topic' class ColumnTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 3c546a19c..ac9633467 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/reply' +require 'models_sqlserver/topic' class ConnectionTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 1898760fa..8c96fc739 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/event' +require 'models_sqlserver/topic' class FinderTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index f90d7c278..4fa963815 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,5 +1,6 @@ require 'cases/sqlserver_helper' require 'models/person' +require 'models_sqlserver/person' class MigrationTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 77b0b82f9..3b0b63346 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -8,6 +8,7 @@ require 'models/post' require 'models/comment' require 'models/categorization' +require 'models_sqlserver/sql_server_order_row_number' class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 5a56029ba..1034900d8 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -13,6 +13,7 @@ require 'models/parrot' require 'models/minivan' require 'models/person' +require 'models_sqlserver/topic' require 'rexml/document' class PersistencesTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 1f386bb09..b02c5f474 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,6 +1,7 @@ require 'cases/sqlserver_helper' require 'models/person' require 'models/reader' +require 'models_sqlserver/person' class PessimisticLockingTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 40e309113..40557d6dc 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,4 +1,6 @@ require 'cases/sqlserver_helper' +require 'models_sqlserver/sql_server_natural_pk_data' +require 'models_sqlserver/sql_server_natural_pk_data_schema' class SchemaTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index f670e03b6..249571c20 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,5 +1,13 @@ require 'cases/sqlserver_helper' - +require 'models_sqlserver/no_pk_data' +require 'models_sqlserver/sql_server_dollar_table_name' +require 'models_sqlserver/sql_server_edge_schema' +require 'models_sqlserver/sql_server_natural_pk_int_data' +require 'models_sqlserver/sql_server_quoted_table' +require 'models_sqlserver/sql_server_quoted_view_1' +require 'models_sqlserver/sql_server_quoted_view_2' +require 'models_sqlserver/sql_server_tinyint_pk' +require 'models_sqlserver/string_default' class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'be able to complex count tables with no primary key' do diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index c50d28eb4..a6858e5e5 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -38,59 +38,6 @@ ActiveRecord::Base.logger = Logger.new(File.expand_path(File.join(SQLSERVER_TEST_ROOT,'debug.log'))) ActiveRecord::Base.logger.level = 0 -# Defining our classes in one place as well as some core tests that need coercing date/time types. - -class UpperTestDefault < ActiveRecord::Base ; self.table_name = 'UPPER_TESTS' ; end -class UpperTestLowered < ActiveRecord::Base ; self.table_name = 'upper_tests' ; end -class TableWithRealColumn < ActiveRecord::Base; end -class FkTestHasFk < ActiveRecord::Base ; end -class FkTestHasPk < ActiveRecord::Base ; end -class NumericData < ActiveRecord::Base ; self.table_name = 'numeric_data' ; end -class FloatData < ActiveRecord::Base ; self.table_name = 'float_data' ; end -class CustomersView < ActiveRecord::Base ; self.table_name = 'customers_view' ; end -class StringDefaultsView < ActiveRecord::Base ; self.table_name = 'string_defaults_view' ; end -class StringDefaultsBigView < ActiveRecord::Base ; self.table_name = 'string_defaults_big_view' ; end -class SqlServerNaturalPkData < ActiveRecord::Base ; self.table_name = 'natural_pk_data' ; self.primary_key = 'legacy_id' ; end -class SqlServerTinyintPk < ActiveRecord::Base ; self.table_name = 'tinyint_pk_table' ; end -class SqlServerNaturalPkIntData < ActiveRecord::Base ; self.table_name = 'natural_pk_int_data' ; end -class SqlServerOrderRowNumber < ActiveRecord::Base ; self.table_name = 'order_row_number' ; end -class SqlServerNaturalPkDataSchema < ActiveRecord::Base ; self.table_name = 'test.sql_server_schema_natural_id' ; end -class SqlServerQuotedTable < ActiveRecord::Base ; self.table_name = 'quoted-table' ; end -class SqlServerQuotedView1 < ActiveRecord::Base ; self.table_name = 'quoted-view1' ; end -class SqlServerQuotedView2 < ActiveRecord::Base ; self.table_name = 'quoted-view2' ; end -class SqlServerUnicode < ActiveRecord::Base ; end -class SqlServerString < ActiveRecord::Base ; end -class NoPkData < ActiveRecord::Base ; self.table_name = 'no_pk_data' ; end -class StringDefault < ActiveRecord::Base; end -class SqlServerEdgeSchema < ActiveRecord::Base - attr_accessor :new_id_setting - before_create :set_new_id - def with_spaces - read_attribute :'with spaces' - end - def with_spaces=(value) - write_attribute :'with spaces', value - end - protected - def set_new_id - self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting - end -end -class SqlServerDollarTableName < ActiveRecord::Base - self.table_name = 'my$strange_table' -end -class SqlServerChronic < ActiveRecord::Base - coerce_sqlserver_date :date - coerce_sqlserver_time :time - default_timezone = :utc -end -class Topic < ActiveRecord::Base - coerce_sqlserver_date :last_read - coerce_sqlserver_time :bonus_time -end -class Person < ActiveRecord::Base - coerce_sqlserver_date :favorite_day -end # A module that we can include in classes where we want to override an active record test. diff --git a/test/models_sqlserver/customers_view.rb b/test/models_sqlserver/customers_view.rb new file mode 100644 index 000000000..3e571271a --- /dev/null +++ b/test/models_sqlserver/customers_view.rb @@ -0,0 +1,3 @@ +class CustomersView < ActiveRecord::Base + self.table_name = 'customers_view' +end \ No newline at end of file diff --git a/test/models_sqlserver/fk_test_has_fk.rb b/test/models_sqlserver/fk_test_has_fk.rb new file mode 100644 index 000000000..23de5cf95 --- /dev/null +++ b/test/models_sqlserver/fk_test_has_fk.rb @@ -0,0 +1,2 @@ +class FkTestHasFk < ActiveRecord::Base +end diff --git a/test/models_sqlserver/fk_test_has_pk.rb b/test/models_sqlserver/fk_test_has_pk.rb new file mode 100644 index 000000000..c76e294ce --- /dev/null +++ b/test/models_sqlserver/fk_test_has_pk.rb @@ -0,0 +1,2 @@ +class FkTestHasPk < ActiveRecord::Base +end diff --git a/test/models_sqlserver/float_data.rb b/test/models_sqlserver/float_data.rb new file mode 100644 index 000000000..b7ff8ccdb --- /dev/null +++ b/test/models_sqlserver/float_data.rb @@ -0,0 +1,3 @@ +class FloatData < ActiveRecord::Base + self.table_name = 'float_data' +end \ No newline at end of file diff --git a/test/models_sqlserver/no_pk_data.rb b/test/models_sqlserver/no_pk_data.rb new file mode 100644 index 000000000..f19cb75ea --- /dev/null +++ b/test/models_sqlserver/no_pk_data.rb @@ -0,0 +1,3 @@ +class NoPkData < ActiveRecord::Base + self.table_name = 'no_pk_data' +end \ No newline at end of file diff --git a/test/models_sqlserver/numeric_data.rb b/test/models_sqlserver/numeric_data.rb new file mode 100644 index 000000000..129f265a0 --- /dev/null +++ b/test/models_sqlserver/numeric_data.rb @@ -0,0 +1,3 @@ +class NumericData < ActiveRecord::Base + self.table_name = 'numeric_data' +end diff --git a/test/models_sqlserver/person.rb b/test/models_sqlserver/person.rb new file mode 100644 index 000000000..04aa163ed --- /dev/null +++ b/test/models_sqlserver/person.rb @@ -0,0 +1,3 @@ +class Person < ActiveRecord::Base + coerce_sqlserver_date :favorite_day +end diff --git a/test/models_sqlserver/sql_server_chronic.rb b/test/models_sqlserver/sql_server_chronic.rb new file mode 100644 index 000000000..45b1cdfd1 --- /dev/null +++ b/test/models_sqlserver/sql_server_chronic.rb @@ -0,0 +1,5 @@ +class SqlServerChronic < ActiveRecord::Base + coerce_sqlserver_date :date + coerce_sqlserver_time :time + default_timezone = :utc +end diff --git a/test/models_sqlserver/sql_server_dollar_table_name.rb b/test/models_sqlserver/sql_server_dollar_table_name.rb new file mode 100644 index 000000000..9247abe7b --- /dev/null +++ b/test/models_sqlserver/sql_server_dollar_table_name.rb @@ -0,0 +1,3 @@ +class SqlServerDollarTableName < ActiveRecord::Base + self.table_name = 'my$strange_table' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_edge_schema.rb b/test/models_sqlserver/sql_server_edge_schema.rb new file mode 100644 index 000000000..eeaa27ddc --- /dev/null +++ b/test/models_sqlserver/sql_server_edge_schema.rb @@ -0,0 +1,17 @@ +class SqlServerEdgeSchema < ActiveRecord::Base + attr_accessor :new_id_setting + before_create :set_new_id + def with_spaces + read_attribute :'with spaces' + end + + def with_spaces=(value) + write_attribute :'with spaces', value + end + + protected + def set_new_id + self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting + end + +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_natural_pk_data.rb b/test/models_sqlserver/sql_server_natural_pk_data.rb new file mode 100644 index 000000000..10caedbb3 --- /dev/null +++ b/test/models_sqlserver/sql_server_natural_pk_data.rb @@ -0,0 +1,4 @@ +class SqlServerNaturalPkData < ActiveRecord::Base + self.table_name = 'natural_pk_data' +self.primary_key = 'legacy_id' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_natural_pk_data_schema.rb b/test/models_sqlserver/sql_server_natural_pk_data_schema.rb new file mode 100644 index 000000000..0ff4cbb20 --- /dev/null +++ b/test/models_sqlserver/sql_server_natural_pk_data_schema.rb @@ -0,0 +1,3 @@ +class SqlServerNaturalPkDataSchema < ActiveRecord::Base + self.table_name = 'test.sql_server_schema_natural_id' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_natural_pk_int_data.rb b/test/models_sqlserver/sql_server_natural_pk_int_data.rb new file mode 100644 index 000000000..47cd20609 --- /dev/null +++ b/test/models_sqlserver/sql_server_natural_pk_int_data.rb @@ -0,0 +1,3 @@ +class SqlServerNaturalPkIntData < ActiveRecord::Base + self.table_name = 'natural_pk_int_data' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_order_row_number.rb b/test/models_sqlserver/sql_server_order_row_number.rb new file mode 100644 index 000000000..c7f7c4cee --- /dev/null +++ b/test/models_sqlserver/sql_server_order_row_number.rb @@ -0,0 +1,3 @@ +class SqlServerOrderRowNumber < ActiveRecord::Base + self.table_name = 'order_row_number' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_quoted_table.rb b/test/models_sqlserver/sql_server_quoted_table.rb new file mode 100644 index 000000000..9218ddaae --- /dev/null +++ b/test/models_sqlserver/sql_server_quoted_table.rb @@ -0,0 +1,3 @@ +class SqlServerQuotedTable < ActiveRecord::Base + self.table_name = 'quoted-table' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_quoted_view_1.rb b/test/models_sqlserver/sql_server_quoted_view_1.rb new file mode 100644 index 000000000..96c81dfb7 --- /dev/null +++ b/test/models_sqlserver/sql_server_quoted_view_1.rb @@ -0,0 +1,3 @@ +class SqlServerQuotedView1 < ActiveRecord::Base + self.table_name = 'quoted-view1' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_quoted_view_2.rb b/test/models_sqlserver/sql_server_quoted_view_2.rb new file mode 100644 index 000000000..2ca207c4d --- /dev/null +++ b/test/models_sqlserver/sql_server_quoted_view_2.rb @@ -0,0 +1,3 @@ +class SqlServerQuotedView2 < ActiveRecord::Base + self.table_name = 'quoted-view2' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_string.rb b/test/models_sqlserver/sql_server_string.rb new file mode 100644 index 000000000..b206ed7de --- /dev/null +++ b/test/models_sqlserver/sql_server_string.rb @@ -0,0 +1,2 @@ +class SqlServerString < ActiveRecord::Base +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_tinyint_pk.rb b/test/models_sqlserver/sql_server_tinyint_pk.rb new file mode 100644 index 000000000..4fbcb6b4d --- /dev/null +++ b/test/models_sqlserver/sql_server_tinyint_pk.rb @@ -0,0 +1,3 @@ +class SqlServerTinyintPk < ActiveRecord::Base + self.table_name = 'tinyint_pk_table' +end \ No newline at end of file diff --git a/test/models_sqlserver/sql_server_unicode.rb b/test/models_sqlserver/sql_server_unicode.rb new file mode 100644 index 000000000..c712f3ffa --- /dev/null +++ b/test/models_sqlserver/sql_server_unicode.rb @@ -0,0 +1,2 @@ +class SqlServerUnicode < ActiveRecord::Base +end \ No newline at end of file diff --git a/test/models_sqlserver/string_default.rb b/test/models_sqlserver/string_default.rb new file mode 100644 index 000000000..31c8eaea3 --- /dev/null +++ b/test/models_sqlserver/string_default.rb @@ -0,0 +1,2 @@ +class StringDefault < ActiveRecord::Base +end \ No newline at end of file diff --git a/test/models_sqlserver/string_defaults_big_view.rb b/test/models_sqlserver/string_defaults_big_view.rb new file mode 100644 index 000000000..30a2da5c1 --- /dev/null +++ b/test/models_sqlserver/string_defaults_big_view.rb @@ -0,0 +1,3 @@ +class StringDefaultsBigView < ActiveRecord::Base + self.table_name = 'string_defaults_big_view' +end \ No newline at end of file diff --git a/test/models_sqlserver/string_defaults_view.rb b/test/models_sqlserver/string_defaults_view.rb new file mode 100644 index 000000000..d8f702f00 --- /dev/null +++ b/test/models_sqlserver/string_defaults_view.rb @@ -0,0 +1,3 @@ +class StringDefaultsView < ActiveRecord::Base + self.table_name = 'string_defaults_view' +end \ No newline at end of file diff --git a/test/models_sqlserver/table_with_real_column.rb b/test/models_sqlserver/table_with_real_column.rb new file mode 100644 index 000000000..ceddb5047 --- /dev/null +++ b/test/models_sqlserver/table_with_real_column.rb @@ -0,0 +1,2 @@ +class TableWithRealColumn < ActiveRecord::Base +end \ No newline at end of file diff --git a/test/models_sqlserver/topic.rb b/test/models_sqlserver/topic.rb new file mode 100644 index 000000000..3e1099907 --- /dev/null +++ b/test/models_sqlserver/topic.rb @@ -0,0 +1,4 @@ +class Topic < ActiveRecord::Base + coerce_sqlserver_date :last_read + coerce_sqlserver_time :bonus_time +end \ No newline at end of file diff --git a/test/models_sqlserver/upper_test_default.rb b/test/models_sqlserver/upper_test_default.rb new file mode 100644 index 000000000..fae5dfe8d --- /dev/null +++ b/test/models_sqlserver/upper_test_default.rb @@ -0,0 +1,3 @@ +class UpperTestDefault < ActiveRecord::Base + self.table_name = 'UPPER_TESTS' +end \ No newline at end of file diff --git a/test/models_sqlserver/upper_test_lowered.rb b/test/models_sqlserver/upper_test_lowered.rb new file mode 100644 index 000000000..01ceca5b3 --- /dev/null +++ b/test/models_sqlserver/upper_test_lowered.rb @@ -0,0 +1,3 @@ +class UpperTestLowered < ActiveRecord::Base + self.table_name = 'upper_tests' +end From a1e126e4e5281afb94d772dd7aa2d566d488ce2d Mon Sep 17 00:00:00 2001 From: Garrett Hart Date: Wed, 8 Jan 2014 12:43:49 -0500 Subject: [PATCH 0104/1412] Fixes "uninitialized constant AdapterTestSqlserver::UpperTestLowered" error. --- test/cases/adapter_test_sqlserver.rb | 319 ++++++++++++++------------- 1 file changed, 160 insertions(+), 159 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index fee5abeaf..3d8a44b33 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -13,68 +13,69 @@ require 'models_sqlserver/string_defaults_view' require 'models_sqlserver/topic' require 'models_sqlserver/upper_test_default' +require 'models_sqlserver/upper_test_lowered' class AdapterTestSqlserver < ActiveRecord::TestCase - + fixtures :tasks, :posts - + setup do @connection = ActiveRecord::Base.connection @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" end - + context 'For abstract behavior' do - + should 'have a 128 max #table_alias_length' do assert @connection.table_alias_length <= 128 end - + should 'raise invalid statement error' do assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") } end - + should 'be our adapter_name' do assert_equal 'SQLServer', @connection.adapter_name end - + should 'include version in inspect' do assert_match(/version\: \d.\d/,@connection.inspect) end - + should 'include database product level in inspect' do assert_match(/product_level\: "\w+/, @connection.inspect) end - + should 'include database product version in inspect' do assert_match(/product_version\: "\d+/, @connection.inspect) end - + should 'include database edition in inspect' do assert_match(/edition\: "\w+/, @connection.inspect) end - + should 'set database product level' do assert_match(/\w+/, @connection.product_level) end - + should 'set database product version' do assert_match(/\d+/, @connection.product_version) end - + should 'set database edition' do assert_match(/\w+/, @connection.edition) end - + should 'support migrations' do assert @connection.supports_migrations? end - + should 'support DDL in transactions' do assert @connection.supports_ddl_transactions? end - + should 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do begin assert_equal 'tasks', Task.table_name @@ -85,9 +86,9 @@ class AdapterTestSqlserver < ActiveRecord::TestCase Task.table_name = 'tasks' end end - + context 'for database version' do - + setup do @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP @supported_versions = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS @@ -95,24 +96,24 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" @sqlserver_2011_string1 = %|Microsoft SQL Server "Denali" (CTP1) - 11.0.1103.9 (Intel X86) Sep 24 2010 22:02:43 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition on Windows NT 6.0 (Build 6002: Service Pack 2)| end - + should 'return a string from #database_version that matches class regexp' do assert_match @version_regexp, @connection.database_version end unless sqlserver_azure? - + should 'return a 4 digit year fixnum for #database_year' do assert_instance_of Fixnum, @connection.database_year @supported_versions.must_include @connection.database_year end - + should 'return a code name if year not available' do assert_equal "Denali", @version_regexp.match(@sqlserver_2011_string1)[1] end - + end - + context 'for Utils.unqualify_table_name and Utils.unqualify_db_name' do - + setup do @expected_table_name = 'baz' @expected_db_name = 'foo' @@ -120,71 +121,71 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @third_table_names = ['[foo].[bar].[baz]','foo.bar.baz'] @qualifed_table_names = @first_second_table_names + @third_table_names end - + should 'return clean table_name from Utils.unqualify_table_name' do @qualifed_table_names.each do |qtn| - assert_equal @expected_table_name, + assert_equal @expected_table_name, ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_table_name(qtn), "This qualifed_table_name #{qtn} did not unqualify correctly." end end - + should 'return nil from Utils.unqualify_db_name when table_name is less than 2 qualified' do @first_second_table_names.each do |qtn| assert_equal nil, ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_db_name(qtn), "This qualifed_table_name #{qtn} did not return nil." end end - + should 'return clean db_name from Utils.unqualify_db_name when table is thrid level qualified' do @third_table_names.each do |qtn| - assert_equal @expected_db_name, + assert_equal @expected_db_name, ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_db_name(qtn), "This qualifed_table_name #{qtn} did not unqualify the db_name correctly." end end - + end - + should 'return true to #insert_sql? for inserts only' do assert @connection.send(:insert_sql?,'INSERT...') assert @connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") assert !@connection.send(:insert_sql?,'UPDATE...') assert !@connection.send(:insert_sql?,'SELECT...') end - + context 'for #get_table_name' do - + should 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do assert_equal '[funny_jokes]', @connection.send(:get_table_name,@basic_insert_sql) assert_equal '[customers]', @connection.send(:get_table_name,@basic_update_sql) assert_equal '[customers]', @connection.send(:get_table_name,@basic_select_sql) end - + end - + context 'with different language' do - + setup do @default_language = @connection.user_options_language end - + teardown do @connection.execute("SET LANGUAGE #{@default_language}") rescue nil @connection.send :initialize_dateformatter end - + should 'memoize users dateformat' do @connection.execute("SET LANGUAGE us_english") rescue nil dateformat = @connection.instance_variable_get(:@database_dateformat) assert_equal 'mdy', dateformat end - + should 'have a dateformatter' do assert Date::DATE_FORMATS[:_sqlserver_dateformat] assert Time::DATE_FORMATS[:_sqlserver_dateformat] end - + should 'do a date insertion when language is german' do @connection.execute("SET LANGUAGE deutsch") @connection.send :initialize_dateformatter @@ -192,27 +193,27 @@ class AdapterTestSqlserver < ActiveRecord::TestCase Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31)) end end - + end - + context 'testing #enable_default_unicode_types configuration' do - + should 'use non-unicode types when set to false' do with_enable_default_unicode_types(false) do assert_equal 'varchar', @connection.native_string_database_type assert_equal 'varchar(max)', @connection.native_text_database_type end end - + should 'use unicode types when set to true' do with_enable_default_unicode_types(true) do assert_equal 'nvarchar', @connection.native_string_database_type assert_equal 'nvarchar(max)', @connection.native_text_database_type end end - + end - + context 'testing #lowercase_schema_reflection' do setup do @@ -220,7 +221,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase UpperTestDefault.create :COLUMN1 => 'Got a minute?', :COLUMN2 => 419 UpperTestDefault.create :COLUMN1 => 'Favorite number?', :COLUMN2 => 69 end - + teardown do @connection.lowercase_schema_reflection = false end @@ -231,7 +232,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase assert_equal 'Favorite number?', UpperTestDefault.last.COLUMN1 assert UpperTestDefault.columns_hash['COLUMN2'] end - + should 'lowercase schema reflection when set' do @connection.lowercase_schema_reflection = true UpperTestLowered.reset_column_information @@ -242,13 +243,13 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end end - + end - + context 'For chronic data types' do - + context 'with a usec' do - + setup do @time = Time.now @db_datetime_003 = '2012-11-08 10:24:36.003' @@ -258,31 +259,31 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @connection.execute("INSERT INTO [sql_server_chronics] ([datetime]) VALUES('#{datetime}')") end end - + teardown do @all_datetimes.each do |datetime| @connection.execute("DELETE FROM [sql_server_chronics] WHERE [datetime] = '#{datetime}'") end end - + context 'finding existing DB objects' do - + should 'find 003 millisecond in the DB with before and after casting' do existing_003 = SqlServerChronic.find_by_datetime!(@db_datetime_003) assert_equal @db_datetime_003, existing_003.datetime_before_type_cast if existing_003.datetime_before_type_cast.is_a?(String) assert_equal 3000, existing_003.datetime.usec, 'A 003 millisecond in SQL Server is 3000 microseconds' end - + should 'find 123 millisecond in the DB with before and after casting' do existing_123 = SqlServerChronic.find_by_datetime!(@db_datetime_123) assert_equal @db_datetime_123, existing_123.datetime_before_type_cast if existing_123.datetime_before_type_cast.is_a?(String) assert_equal 123000, existing_123.datetime.usec, 'A 123 millisecond in SQL Server is 123000 microseconds' end - + end - + context 'saving new datetime objects' do - + should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do Time.any_instance.stubs :iso8601 => "2011-07-26T12:29:01.123-04:00" saved = SqlServerChronic.create!(:datetime => @time).reload @@ -290,15 +291,15 @@ class AdapterTestSqlserver < ActiveRecord::TestCase assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String) assert_equal 123000, saved.datetime.usec end - + end - + end - + end - + context 'For identity inserts' do - + setup do @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')" @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')" @@ -307,7 +308,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'" @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" end - + should '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) @@ -316,23 +317,23 @@ class AdapterTestSqlserver < ActiveRecord::TestCase 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) end - + should 'return false to #query_requires_identity_insert? for normal SQL' do [@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql| assert !@connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" end end - + should 'find identity column using #identity_column' do joke_id_column = Joke.columns.detect { |c| c.name == 'id' } assert_equal joke_id_column.name, @connection.send(:identity_column,Joke.table_name).name assert_equal joke_id_column.sql_type, @connection.send(:identity_column,Joke.table_name).sql_type end - + should 'return nil when calling #identity_column for a table_name with no identity' do assert_nil @connection.send(:identity_column,Subscriber.table_name) end unless sqlserver_azure? - + should 'be able to disable referential integrity' do Minimalistic.delete_all @connection.send :set_identity_insert, Minimalistic.table_name, false @@ -341,35 +342,35 @@ class AdapterTestSqlserver < ActiveRecord::TestCase o.id = 420 o.save! end - + end - + context 'For Quoting' do should 'return 1 for #quoted_true' do assert_equal '1', @connection.quoted_true end - + should 'return 0 for #quoted_false' do assert_equal '0', @connection.quoted_false end - + should 'not escape backslash characters like abstract adapter' do string_with_backslashs = "\\n" assert_equal string_with_backslashs, @connection.quote_string(string_with_backslashs) end - + should 'quote column names with brackets' do assert_equal '[foo]', @connection.quote_column_name(:foo) assert_equal '[foo]', @connection.quote_column_name('foo') assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar') end - + should 'not quote already quoted column names with brackets' do assert_equal '[foo]', @connection.quote_column_name('[foo]') assert_equal '[foo].[bar]', @connection.quote_column_name('[foo].[bar]') end - + should 'quote table names like columns' do assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar') assert_equal '[foo].[bar].[baz]', @connection.quote_column_name('foo.bar.baz') @@ -425,14 +426,14 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end context "#quoted_datetime" do - + setup do @iso_string = '2001-02-03T04:05:06-0700' @date = Date.parse @iso_string @time = Time.parse @iso_string @datetime = DateTime.parse @iso_string end - + context "with a Date" do should "return a dd-mm-yyyy date string" do @@ -496,39 +497,39 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end end - + end - + context 'When disabling referential integrity' do - + setup do @connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } @parent = FkTestHasPk.create! @member = FkTestHasFk.create!(:fk_id => @parent.id) end - + should 'NOT ALLOW by default the deletion of a referenced parent' do FkTestHasPk.connection.disable_referential_integrity { } assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end - + should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do FkTestHasPk.connection.disable_referential_integrity { @parent.destroy } end - + should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do assert_raise(ActiveRecord::StatementInvalid) do FkTestHasPk.connection.disable_referential_integrity { } @parent.destroy end end - + end - + context 'For DatabaseStatements' do - + context "finding out what user_options are available" do - + should "run the database consistency checker useroptions command" do keys = [:textsize, :language, :isolation_level, :dateformat] user_options = @connection.user_options @@ -537,25 +538,25 @@ class AdapterTestSqlserver < ActiveRecord::TestCase assert user_options.key?(key), msg end end - + should "return a underscored key hash with indifferent access of the results" do user_options = @connection.user_options assert_equal 'read committed', user_options['isolation_level'] assert_equal 'read committed', user_options[:isolation_level] end - + end unless sqlserver_azure? - + context "altering isolation levels" do - + should "barf if the requested isolation level is not valid" do assert_raise(ArgumentError) do @connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end end end - + context "with a valid isolation level" do - + setup do @t1 = tasks(:first_task) @t2 = tasks(:another_task) @@ -564,7 +565,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase good_isolation_level = @connection.user_options_isolation_level.blank? || @connection.user_options_isolation_level =~ /read committed/i assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options_isolation_level.inspect}" end - + should 'allow #run_with_isolation_level to not take a block to set it' do begin @connection.run_with_isolation_level 'READ UNCOMMITTED' @@ -573,11 +574,11 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @connection.run_with_isolation_level 'READ COMMITTED' end end - + should 'return block value using #run_with_isolation_level' do assert_equal Task.all.sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } end - + should 'pass a read uncommitted isolation level test' do assert_nil @t2.starting, 'Fixture should have this empty.' begin @@ -594,41 +595,41 @@ class AdapterTestSqlserver < ActiveRecord::TestCase assert @dirty_t2.starting, 'Should have a dirty date.' assert_nil Task.find(@t2.id).starting, 'Should be nil again from botched transaction above.' end - + end unless sqlserver_azure? - + end - + end - + context 'For SchemaStatements' do - + context 'returning from #type_to_sql' do - + should 'create integers when no limit supplied' do assert_equal 'integer', @connection.type_to_sql(:integer) end - + should 'create integers when limit is 4' do assert_equal 'integer', @connection.type_to_sql(:integer, 4) end - + should 'create integers when limit is 3' do assert_equal 'integer', @connection.type_to_sql(:integer, 3) end - + should 'create smallints when limit is less than 3' do assert_equal 'smallint', @connection.type_to_sql(:integer, 2) assert_equal 'smallint', @connection.type_to_sql(:integer, 1) end - + should 'create bigints when limit is greateer than 4' do assert_equal 'bigint', @connection.type_to_sql(:integer, 5) assert_equal 'bigint', @connection.type_to_sql(:integer, 6) assert_equal 'bigint', @connection.type_to_sql(:integer, 7) assert_equal 'bigint', @connection.type_to_sql(:integer, 8) end - + should 'create floats when no limit supplied' do assert_equal 'float(8)', @connection.type_to_sql(:float) end @@ -636,165 +637,165 @@ class AdapterTestSqlserver < ActiveRecord::TestCase should 'create floats when limit is supplied' do assert_equal 'float(27)', @connection.type_to_sql(:float, 27) end - + end - + end - + context 'For indexes' do - + setup do @desc_index_name = 'idx_credit_limit_test_desc' @connection.execute "CREATE INDEX [#{@desc_index_name}] ON [accounts] (credit_limit DESC)" end - + teardown do @connection.execute "DROP INDEX [#{@desc_index_name}] ON [accounts]" end - + should 'have indexes with descending order' do assert @connection.indexes('accounts').detect { |i| i.name == @desc_index_name } end - + end - + context 'For views' do - + context 'using @connection.views' do - + should 'return an array' do assert_instance_of Array, @connection.views end - + should 'find CustomersView table name' do @connection.views.must_include 'customers_view' end - + should 'work with dynamic finders' do name = 'MetaSkills' customer = CustomersView.create! :name => name assert_equal customer, CustomersView.find_by_name(name) end - + should 'not contain system views' do systables = ['sysconstraints','syssegments'] systables.each do |systable| assert !@connection.views.include?(systable), "This systable #{systable} should not be in the views array." end end - + should 'allow the connection#view_information method to return meta data on the view' do view_info = @connection.send(:view_information,'customers_view') assert_equal('customers_view', view_info['TABLE_NAME']) assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION']) end - + should 'allow the connection#view_table_name method to return true table_name for the view' do assert_equal 'customers', @connection.send(:view_table_name,'customers_view') assert_equal 'topics', @connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' end - + end - + context 'used by a class for table_name' do - + context 'with same column names' do - + should 'have matching column objects' do columns = ['id','name','balance'] assert !CustomersView.columns.blank? assert_equal columns.size, CustomersView.columns.size columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - CustomersView.columns_hash[colname], + assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + CustomersView.columns_hash[colname], "Column name #{colname.inspect} was not found in these columns #{CustomersView.columns.map(&:name).inspect}" end end - + should 'find identity column' do assert CustomersView.columns_hash['id'].primary end - + should 'find default values' do assert_equal 0, CustomersView.new.balance end - + should 'respond true to table_exists?' do assert CustomersView.table_exists? end - + should 'have correct table name for all column objects' do - assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' }, + assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' }, CustomersView.columns.map(&:table_name).inspect end - + end - + context 'with aliased column names' do - + should 'have matching column objects' do columns = ['id','pretend_null'] assert !StringDefaultsView.columns.blank? assert_equal columns.size, StringDefaultsView.columns.size columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - StringDefaultsView.columns_hash[colname], + assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + StringDefaultsView.columns_hash[colname], "Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}" end end - + should 'find identity column' do assert StringDefaultsView.columns_hash['id'].primary end - + should 'find default values' do - assert_equal 'null', StringDefaultsView.new.pretend_null, + assert_equal 'null', StringDefaultsView.new.pretend_null, StringDefaultsView.columns_hash['pretend_null'].inspect end - + should 'respond true to table_exists?' do assert StringDefaultsView.table_exists? end - + should 'have correct table name for all column objects' do - assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, + assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, StringDefaultsView.columns.map(&:table_name).inspect end - + end - + end - + context 'doing identity inserts' do - + setup do @view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" end - + should 'respond true/tablename to #query_requires_identity_insert?' do assert_equal '[customers_view]', @connection.send(:query_requires_identity_insert?,@view_insert_sql) end - + should 'be able to do an identity insert' do assert_nothing_raised { @connection.execute(@view_insert_sql) } assert CustomersView.find(420) end - + end - + context 'that have more than 4000 chars for their defintion' do - + should 'cope with null returned for the defintion' do assert_nothing_raised() { StringDefaultsBigView.columns } end - + should 'using alternate view defintion still be able to find real default' do - assert_equal 'null', StringDefaultsBigView.new.pretend_null, + assert_equal 'null', StringDefaultsBigView.new.pretend_null, StringDefaultsBigView.columns_hash['pretend_null'].inspect end - + end - + end - + end From e61fe86b12121637e729a5df227c5efce4e6de18 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 8 Jan 2014 16:15:26 -0500 Subject: [PATCH 0105/1412] added todos --- test/cases/bind_parameter_test_sqlserver.rb | 1 + .../has_and_belongs_to_many_associations_test_sqlserver.rb | 2 ++ test/cases/migration_test_sqlserver.rb | 7 ++++++- test/cases/scratch_test_sqlserver.rb | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index fdd30ab6e..b9e0b9b0a 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -14,6 +14,7 @@ class ActiveRecord::BindParameterTest < ActiveRecord::TestCase include SqlserverCoercedTest + # TODO: put a real test here def test_coerced_binds_are_logged assert true, 'they are!' end diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb index 568e22e30..26c916234 100644 --- a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb +++ b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb @@ -12,10 +12,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase include SqlserverCoercedTest + # TODO: put a real test here def test_coerced_count_with_finder_sql assert true end + # TODO: put a real test here def test_coerced_caching_of_columns assert true end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 4fa963815..3420f85f6 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -68,6 +68,11 @@ class MigrationTestSqlserver < ActiveRecord::TestCase class MigrationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] include SqlserverCoercedTest - def test_coerced_test_migrator_db_has_no_schema_migrations_table ; assert true ; end + + # TODO: put a real test here + def test_coerced_test_migrator_db_has_no_schema_migrations_table + assert true + end + end end diff --git a/test/cases/scratch_test_sqlserver.rb b/test/cases/scratch_test_sqlserver.rb index 8a8b1d9d8..513bfbae2 100644 --- a/test/cases/scratch_test_sqlserver.rb +++ b/test/cases/scratch_test_sqlserver.rb @@ -2,6 +2,7 @@ class ScratchTestSqlserver < ActiveRecord::TestCase + # TODO: put a real test here should 'pass' do assert true end From 02e3c86da7f517ca38b06ba73b2611811dceff38 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 8 Jan 2014 16:18:38 -0500 Subject: [PATCH 0106/1412] Run Arel tests --- Rakefile | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/Rakefile b/Rakefile index f1daf07bb..cf13bea78 100644 --- a/Rakefile +++ b/Rakefile @@ -1,13 +1,17 @@ require 'rake' require 'rake/testtask' +AR_PATH = Gem.loaded_specs['activerecord'].full_gem_path +AREL_PATH = Gem.loaded_specs['arel'].full_gem_path # Notes for cross compile: # $ gcla ; bundle install ; rake compile ; rake cross compile ; rake cross native gem def test_libs(mode='dblib') ['lib', 'test', - "#{File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')}"] + "#{File.join(AR_PATH,'test')}", + "#{File.join(AREL_PATH,'test')}", + ] end # bundle exec rake test SQLSERVER_ONLY=true @@ -15,18 +19,24 @@ end # If you have trouble running single tests (errors about requirements): # http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files - test_setup = ["test/cases/sqlserver_helper.rb", "test/cases/aaaa_create_tables_test_sqlserver.rb"] - return test_setup+(ENV['TEST_FILES']).split(',').sort if ENV['TEST_FILES'] - sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb").sort - ar_path = Gem.loaded_specs['activerecord'].full_gem_path - ar_cases = Dir.glob("#{ar_path}/test/cases/**/*_test.rb") - adapter_cases = Dir.glob("#{ar_path}/test/cases/adapters/**/*_test.rb") + test_setup = ["test/cases/sqlserver_helper.rb"] + return test_setup+(ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] + + sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb") + + ar_cases = Dir.glob("#{AR_PATH}/test/cases/**/*_test.rb") + adapter_cases = Dir.glob("#{AR_PATH}/test/cases/adapters/**/*_test.rb") + + arel_cases = Dir.glob("#{AREL_PATH}/test/**/test_*.rb") + if ENV['SQLSERVER_ONLY'] sqlserver_cases elsif ENV['ACTIVERECORD_ONLY'] - test_setup + (ar_cases - adapter_cases).sort + test_setup + (ar_cases - adapter_cases) + elsif ENV['AREL_ONLY'] + arel_cases else - sqlserver_cases + (ar_cases - adapter_cases).sort + arel_cases + sqlserver_cases + (ar_cases - adapter_cases) end end From 34885703867f0927b101ada7db8962fd3ba38fb9 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 10 Jan 2014 09:04:37 -0500 Subject: [PATCH 0107/1412] remove requirement --- Rakefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index f1daf07bb..78caa93b4 100644 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,7 @@ end # If you have trouble running single tests (errors about requirements): # http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files - test_setup = ["test/cases/sqlserver_helper.rb", "test/cases/aaaa_create_tables_test_sqlserver.rb"] + test_setup = ["test/cases/sqlserver_helper.rb"] return test_setup+(ENV['TEST_FILES']).split(',').sort if ENV['TEST_FILES'] sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb").sort ar_path = Gem.loaded_specs['activerecord'].full_gem_path From 9c13c1b9cd426182ef2d15dbc6cd29b0eacfb135 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 10 Jan 2014 10:40:12 -0500 Subject: [PATCH 0108/1412] run arel tests --- Rakefile | 3 ++- test/cases/sqlserver_helper.rb | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index cf13bea78..c4f7963ec 100644 --- a/Rakefile +++ b/Rakefile @@ -36,8 +36,9 @@ def test_files elsif ENV['AREL_ONLY'] arel_cases else - arel_cases + sqlserver_cases + (ar_cases - adapter_cases) + test_setup + arel_cases + sqlserver_cases + (ar_cases - adapter_cases) end + end task :test => ['test:dblib'] diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index a6858e5e5..42fd20a52 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -4,9 +4,12 @@ SQLSERVER_MIGRATIONS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'migrations')) SQLSERVER_SCHEMA_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'schema')) ACTIVERECORD_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')) +AREL_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['arel'].full_gem_path,'test')) + ENV['ARCONFIG'] = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'config.yml')) $:.unshift ACTIVERECORD_TEST_ROOT +$LOAD_PATH.unshift AREL_TEST_ROOT require 'rubygems' require 'bundler' From d0801d5137cee694190f2c7eef5ef035bf4a67e7 Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 10 Jan 2014 10:41:40 -0500 Subject: [PATCH 0109/1412] =?UTF-8?q?Coerce=20test=20to=20allow=20for=20N?= =?UTF-8?q?=E2=80=99varchar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/cases/where_chain_test_sqlserver.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 test/cases/where_chain_test_sqlserver.rb diff --git a/test/cases/where_chain_test_sqlserver.rb b/test/cases/where_chain_test_sqlserver.rb new file mode 100644 index 000000000..66ec2f180 --- /dev/null +++ b/test/cases/where_chain_test_sqlserver.rb @@ -0,0 +1,17 @@ +require 'cases/sqlserver_helper' +require 'models/post' +require 'models/comment' + +module ActiveRecord + class WhereChainTest < ActiveRecord::TestCase + include SqlserverCoercedTest + + COERCED_TESTS = [:test_not_eq_with_array_parameter] + + def test_coerced_not_eq_with_array_parameter + expected = Arel::Nodes::Not.new("title = N'hello'") + relation = Post.where.not(['title = ?', 'hello']) + assert_equal([expected], relation.where_values) + end + end +end \ No newline at end of file From 4520133ea2cb34c2ea15445409478da85ace415d Mon Sep 17 00:00:00 2001 From: Anna Date: Fri, 10 Jan 2014 16:54:46 -0500 Subject: [PATCH 0110/1412] coerce tests in sqlserver gem rather than rails --- Rakefile | 2 +- test/cases/adapter_test_sqlserver.rb | 9 ++++++ test/cases/associations_test_sqlserver.rb | 16 ++++++++++ test/cases/base_test_sqlserver.rb | 6 +++- test/cases/column_test_sqlserver.rb | 39 +++++++++++++++++++++-- 5 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 test/cases/associations_test_sqlserver.rb diff --git a/Rakefile b/Rakefile index a8f10a82d..e9fecfe1b 100644 --- a/Rakefile +++ b/Rakefile @@ -21,7 +21,7 @@ end def test_files test_setup = ["test/cases/sqlserver_helper.rb"] - return test_setup+(ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] + return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb") diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 3d8a44b33..ba8b28c97 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -799,3 +799,12 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end end + + +class AdapterTest < ActiveRecord::TestCase + COERCED_TESTS = [:test_update_prepared_statement] + # Like PostgreSQL, SQL Server does not support null bytes in strings. + # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server + # Until that patch is made to rails we are preventing this test from running in this gem. + include SqlserverCoercedTest +end \ No newline at end of file diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb new file mode 100644 index 000000000..255c21d26 --- /dev/null +++ b/test/cases/associations_test_sqlserver.rb @@ -0,0 +1,16 @@ +require 'cases/sqlserver_helper' +require 'models/owner' + +class HasManyThroughAssociationsTest < ActiveRecord::TestCase + COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] + # Rails does not do a case-insensive comparison + # Until that patch is made to rails we are preventing this test from running in this gem. + + include SqlserverCoercedTest + def test_coerced_has_many_through_obeys_order_on_through_association + owner = owners(:blackbeard) + # assert owner.toys.to_sql.include?("pets.name desc") # What's currently in rails + assert owner.toys.to_sql.downcase.include?("pets.name desc") + assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name } + end +end diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index 2dc5498dc..7f57cdf21 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -4,7 +4,11 @@ class BasicsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_column_names_are_escaped] + COERCED_TESTS = [:test_column_names_are_escaped, + :test_respect_internal_encoding] + # test_respect_internal_encoding is not run for PostgreSQL at the rails level and the same should happen for SQL Server + # Until that patch is made to rails we are preventing this test from running in this gem. + include SqlserverCoercedTest diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 81e79a3e0..3906e0bd8 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -3,10 +3,13 @@ require 'models_sqlserver/float_data' require 'models_sqlserver/numeric_data' require 'models_sqlserver/sql_server_chronic' +require 'models_sqlserver/sql_server_edge_schema' require 'models_sqlserver/sql_server_string' require 'models_sqlserver/sql_server_unicode' require 'models_sqlserver/table_with_real_column' + require 'models_sqlserver/topic' +require "cases/migration/helper" class ColumnTestSqlserver < ActiveRecord::TestCase @@ -339,7 +342,37 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal 'tinyint(1)', @tinyint.sql_type end - end - - + end end + +module ActiveRecord + class Migration + class ColumnsTest < ActiveRecord::TestCase + include ActiveRecord::Migration::TestHelper + + COERCED_TESTS = [:test_remove_column_with_multi_column_index] + # The if current_adapter? conditional below should also contain :SQLServerAdapter. + # Until that patch is made to rails we are preventing this test from running in this gem. + + include SqlserverCoercedTest + + #the only thing we changed in this method was to add :SQLServerAdapter + def test_coerced_remove_column_with_multi_column_index + 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 + + assert_equal 1, connection.indexes('test_models').size + remove_column("test_models", "hat_size") + + # Every database and/or database adapter has their own behavior + # if it drops the multi-column index when any of the indexed columns dropped by remove_column. + if current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter) + assert_equal [], connection.indexes('test_models').map(&:name) + else + assert_equal ['index_test_models_on_hat_style_and_hat_size'], connection.indexes('test_models').map(&:name) + end + end + end + end +end \ No newline at end of file From e0688b68f0d784c38bdea3dc6b7a6baa4dc875cb Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 13 Jan 2014 14:15:35 -0500 Subject: [PATCH 0111/1412] added sqlserver ignore regexes to rails instead --- test/cases/sqlserver_helper.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 42fd20a52..9d33f512c 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -73,17 +73,17 @@ def method_added(method) end end - -# Our changes/additions to ActiveRecord test helpers specific for SQL Server. - -module ActiveRecord - class SQLCounter - self.ignored_sql.concat([ - %r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)}, - %r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION} - ]) - end -end +# #Our changes/additions to ActiveRecord test helpers specific for SQL Server. +# module ActiveRecord +# class SQLCounter +# sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] +# ignored_sql.concat sqlserver_ignored +# end + +# puts ActiveSupport::Notifications.subscribed('sql.active_record') +# # ActiveSupport::Notifications.unsubscribe('sql.active_record') +# ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) +# end module ActiveRecord class TestCase < ActiveSupport::TestCase From 7f378ae6c25ce47a54f1b7acb3be36455c61ac12 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 13 Jan 2014 22:10:46 -0500 Subject: [PATCH 0112/1412] indentation --- test/cases/associations_test_sqlserver.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb index 255c21d26..5b0841100 100644 --- a/test/cases/associations_test_sqlserver.rb +++ b/test/cases/associations_test_sqlserver.rb @@ -2,9 +2,9 @@ require 'models/owner' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] - # Rails does not do a case-insensive comparison - # Until that patch is made to rails we are preventing this test from running in this gem. + COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] + # Rails does not do a case-insensive comparison + # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest def test_coerced_has_many_through_obeys_order_on_through_association From 059d633f6d0b0af52725cb9f5ce9d2225176fca4 Mon Sep 17 00:00:00 2001 From: Anna Date: Mon, 13 Jan 2014 22:11:21 -0500 Subject: [PATCH 0113/1412] overcome IO.pipe error with binmode --- .../connection_management_test_sqlserver.rb | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 test/cases/connection_management_test_sqlserver.rb diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/connection_management_test_sqlserver.rb new file mode 100644 index 000000000..0a8a52f20 --- /dev/null +++ b/test/cases/connection_management_test_sqlserver.rb @@ -0,0 +1,58 @@ +require "cases/sqlserver_helper" +require "rack" + +module ActiveRecord + module ConnectionAdapters + class ConnectionManagementTest < ActiveRecord::TestCase + + COERCED_TESTS = [:test_connection_pool_per_pid] + # Until that patch is made to rails we are preventing this test from running in this gem. + + include SqlserverCoercedTest + + #https://www.ruby-forum.com/topic/4221299 + def test_coerced_connection_pool_per_pid + return skip('must support fork') unless Process.respond_to?(:fork) + + object_id = ActiveRecord::Base.connection.object_id + + rd, wr = IO.pipe + + # #https://www.ruby-forum.com/topic/4221299 + # puts "rd.internal_encoding:#{rd.internal_encoding}\nrd.external_encoding:#{rd.external_encoding}" + # puts "wr.internal_encoding:#{wr.internal_encoding}\nwr.external_encoding:#{wr.external_encoding}" + # # rd.internal_encoding: + # # rd.external_encoding:UTF-8 + # # wr.internal_encoding: + # # wr.external_encoding:UTF-8 + + + rd.binmode + wr.binmode + + # ERROR + # marshal data too short + # test/cases/connection_management_test_sqlserver.rb:43:in `load' + # test/cases/connection_management_test_sqlserver.rb:43:in `test_coerced_connection_pool_per_pid' + + + # /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:41:in `write': "\x98" from ASCII-8BIT to UTF-8 (Encoding::UndefinedConversionError) + # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:41:in `block in test_coerced_connection_pool_per_pid' + # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:38:in `fork' + # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:38:in `test_coerced_connection_pool_per_pid' + pid = fork { + rd.close + wr.write Marshal.dump ActiveRecord::Base.connection.object_id + wr.close + exit! + } + + wr.close + + Process.waitpid pid + assert_not_equal object_id, Marshal.load(rd.read) + rd.close + end + end + end +end From 97e2b71eb7848e480a8f8966ea9d2a6e47669dbe Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 11:46:03 -0500 Subject: [PATCH 0114/1412] add module name --- test/cases/adapter_test_sqlserver.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index ba8b28c97..8244abb65 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -801,10 +801,16 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end -class AdapterTest < ActiveRecord::TestCase +module ActiveRecord + class AdapterTest < ActiveRecord::TestCase + COERCED_TESTS = [:test_update_prepared_statement] # Like PostgreSQL, SQL Server does not support null bytes in strings. + # DECLARE @mybin1 binary(5), @mybin2 binary(5) + # SET @mybin1 = 0x00 + # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include SqlserverCoercedTest + end end \ No newline at end of file From c28bb50923f86231f1bec696fe0e32b658920faf Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 15:33:31 -0500 Subject: [PATCH 0115/1412] add references --- test/cases/eager_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index 3bcf464e9..470f14ff7 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -15,7 +15,7 @@ class EagerAssociationTest < ActiveRecord::TestCase fixtures :posts, :comments, :authors def test_coerced_count_with_include - assert_equal 3, authors(:david).posts_with_comments.where("len(comments.body) > 15").count + assert_equal 3, authors(:david).posts_with_comments.where("len(comments.body) > 15").references(:comments).count end From 05d5dc9ff098b9c5b70f3c5d66089d8b70018940 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 17:17:12 -0500 Subject: [PATCH 0116/1412] more coerced tests added and deleted --- test/cases/finder_test_sqlserver.rb | 7 ------ test/cases/method_scoping_test_sqlserver.rb | 28 --------------------- test/cases/relations_test_sqlserver.rb | 28 +++++++++++++++++++++ 3 files changed, 28 insertions(+), 35 deletions(-) delete mode 100644 test/cases/method_scoping_test_sqlserver.rb create mode 100644 test/cases/relations_test_sqlserver.rb diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 8c96fc739..9b2769c26 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -10,7 +10,6 @@ class FinderTest < ActiveRecord::TestCase COERCED_TESTS = [ :test_exists_does_not_select_columns_without_alias, :test_string_sanitation, - :test_first_and_last_with_integer_should_use_sql_limit, :test_take_and_first_and_last_with_integer_should_use_sql_limit ] @@ -27,16 +26,10 @@ def test_coerced_string_sanitation assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") end - def test_coerced_first_and_last_with_integer_should_use_sql_limit - assert_sql(/TOP \(2\)/) { Topic.first(2).entries } - assert_sql(/TOP \(5\)/) { Topic.last(5).entries } - end - def test_coerced_take_and_first_and_last_with_integer_should_use_sql_limit assert_sql(/TOP \(3\)/) { Topic.take(3).entries } assert_sql(/TOP \(2\)/) { Topic.first(2).entries } assert_sql(/TOP \(5\)/) { Topic.last(5).entries } end - end diff --git a/test/cases/method_scoping_test_sqlserver.rb b/test/cases/method_scoping_test_sqlserver.rb deleted file mode 100644 index 89c93edbe..000000000 --- a/test/cases/method_scoping_test_sqlserver.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'cases/sqlserver_helper' -require 'models/developer' - -class MethodScopingTestSqlServer < ActiveRecord::TestCase -end - -class NestedScopingTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_merged_scoped_find] - - include SqlserverCoercedTest - - fixtures :developers - - def test_coerced_test_merged_scoped_find - poor_jamis = developers(:poor_jamis) - Developer.where("salary < 100000").scoping do - Developer.offset(1).order('id asc').scoping do - assert_sql /ORDER BY id asc/i do - assert_equal(poor_jamis, Developer.order('id asc').first) - end - end - end - end - -end - - diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb new file mode 100644 index 000000000..503685369 --- /dev/null +++ b/test/cases/relations_test_sqlserver.rb @@ -0,0 +1,28 @@ +require "cases/sqlserver_helper" +require 'models/post' + + +class RelationTest < ActiveRecord::TestCase + COERCED_TESTS = [:test_merging_reorders_bind_params] + # Until that patch is made to rails we are preventing this test from running in this gem. + include SqlserverCoercedTest + fixtures :posts + + def test_coerced_merging_reorders_bind_params + puts "RUNS" + post = Post.first + id_column = Post.columns_hash['id'] + title_column = Post.columns_hash['title'] + + bvr = Post.connection.substitute_at id_column, 1 + right = Post.where(id: bvr) + right.bind_values += [[id_column, post.id]] + + bvl = Post.connection.substitute_at title_column, 0 + left = Post.where(title: bvl) + left.bind_values += [[title_column, post.title]] + + merged = left.merge(right) + assert_equal post, merged.first + end +end \ No newline at end of file From a09a19c97b5185e1f5dcae49cc5a88ed3b3e9267 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 17:20:08 -0500 Subject: [PATCH 0117/1412] confirm that method is actually undefined --- test/cases/sqlserver_helper.rb | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 9d33f512c..afc67e545 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -54,8 +54,7 @@ module ClassMethods def self.extended(base) base.class_eval do Array(coerced_tests).each do |method_name| - undef_method(method_name) rescue nil - STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method_name}") + undefine_and_puts(method_name) end end end @@ -66,10 +65,15 @@ def coerced_tests def method_added(method) if coerced_tests && coerced_tests.include?(method) - undef_method(method) rescue nil - STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") + undefine_and_puts(method) end end + + def undefine_and_puts(method) + result = undef_method(method) rescue nil + STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? + end + end end @@ -132,5 +136,4 @@ def with_auto_connect(boolean) # SQL Server. sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" - eval(File.read(sqlserver_specific_schema_file)) - + eval(File.read(sqlserver_specific_schema_file)) \ No newline at end of file From 15385ca9b5108f024862413574a3a3fd7a7d56fa Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 17:56:54 -0500 Subject: [PATCH 0118/1412] more documentation --- README.md | 14 +++++++++++--- test/cases/relations_test_sqlserver.rb | 3 +-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 2fe1ae809..aa7dc71ba 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,11 @@ -# Rails 4 - By no means ready for production -Current test status: -3866 tests, 3412 passed, 85 failures, 369 errors, 29 skips, 10084 assertions +# Rails 4 +One failing test. Until this is fixex when you use both :includes and :limit you will get fewer results than expected. +```ruby +FAIL + Expected: 2 + Actual: 1 + /Users/acarey/code/source/rails/rails40/activerecord/test/cases/finder_test.rb:822:in `test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct' +``` # ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. @@ -228,6 +233,8 @@ Many many people have contributed. If you do not see your name here and it shoul Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors * metaskills (Ken Collins) +* Annaswims (Annaswims) +* Thirdshift (Garrett Hart) * h-lame (Murray Steele) * vegantech * cjheath (Clifford Heath) @@ -242,6 +249,7 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- * jeremydurham (Jeremy Durham) + ## Donators I am trying to save up for a Happy Hacking pro keyboard. Help me out via GitTip! https://www.gittip.com/metaskills/ diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index 503685369..c1f283b38 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -7,9 +7,8 @@ class RelationTest < ActiveRecord::TestCase # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest fixtures :posts - + def test_coerced_merging_reorders_bind_params - puts "RUNS" post = Post.first id_column = Post.columns_hash['id'] title_column = Post.columns_hash['title'] From 4501c15feebdacf4604984828e551158f92d5b92 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 22:58:17 -0500 Subject: [PATCH 0119/1412] Arel::Nodes::Ordering is now comparison friendly --- lib/arel/visitors/sqlserver.rb | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index c8a668127..d3c460442 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,25 +1,7 @@ require 'arel' module Arel - - module Nodes - - # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a - # collection of them. See SelectManager#order for more details. - class Ordering < Arel::Nodes::Unary - def hash - expr.hash - end - def ==(other) - other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr - end - def eql?(other) - self == other - end - end - - end - + class SelectManager < Arel::TreeManager AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::SQLServerAdapter'.freeze From 70dee3c2babf57117ce8232b6cd0c43d4b55159d Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 23:08:42 -0500 Subject: [PATCH 0120/1412] moved SelectManager class to its own file --- lib/arel/select_manager.rb | 64 ++++++++++++++++++++++++++++++++++ lib/arel/visitors/sqlserver.rb | 64 ---------------------------------- 2 files changed, 64 insertions(+), 64 deletions(-) create mode 100644 lib/arel/select_manager.rb diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb new file mode 100644 index 000000000..4d82594f3 --- /dev/null +++ b/lib/arel/select_manager.rb @@ -0,0 +1,64 @@ +module Arel + class SelectManager < Arel::TreeManager + + AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::SQLServerAdapter'.freeze + + # Getting real Ordering objects is very important for us. We need to be able to call #uniq on + # a colleciton of them reliably as well as using their true object attributes to mutate them + # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this + # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects. + alias :order_without_sqlserver :order + def order(*expr) + return order_without_sqlserver(*expr) unless engine_activerecord_sqlserver_adapter? + @ast.orders.concat(expr.map{ |x| + case x + when Arel::Attributes::Attribute + table = Arel::Table.new(x.relation.table_alias || x.relation.name) + e = table[x.name] + Arel::Nodes::Ascending.new e + when Arel::Nodes::Ordering + x + when String + x.split(',').map do |s| + s = x if x.strip =~ /\A\b\w+\b\(.*,.*\)(\s+(ASC|DESC))?\Z/i # Allow functions with comma(s) to pass thru. + s.strip! + d = s =~ /(ASC|DESC)\Z/i ? $1.upcase : nil + e = d.nil? ? s : s.mb_chars[0...-d.length].strip + e = Arel.sql(e) + d && d == "DESC" ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e) + end + else + e = Arel.sql(x.to_s) + Arel::Nodes::Ascending.new e + end + }.flatten) + self + end + + # A friendly over ride that allows us to put a special lock object that can have a default or pass + # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method. + alias :lock_without_sqlserver :lock + def lock(locking=true) + if engine_activerecord_sqlserver_adapter? + case locking + when true + locking = Arel.sql('WITH(HOLDLOCK, ROWLOCK)') + when Arel::Nodes::SqlLiteral + when String + locking = Arel.sql locking + end + @ast.lock = Arel::Nodes::Lock.new(locking) + self + else + lock_without_sqlserver(locking) + end + end + + private + + def engine_activerecord_sqlserver_adapter? + @engine.connection && @engine.connection.class.name == AR_CA_SQLSA_NAME + end + + end +end \ No newline at end of file diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index d3c460442..f92340354 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,70 +1,6 @@ require 'arel' module Arel - - class SelectManager < Arel::TreeManager - - AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::SQLServerAdapter'.freeze - - # Getting real Ordering objects is very important for us. We need to be able to call #uniq on - # a colleciton of them reliably as well as using their true object attributes to mutate them - # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this - # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects. - alias :order_without_sqlserver :order - def order(*expr) - return order_without_sqlserver(*expr) unless engine_activerecord_sqlserver_adapter? - @ast.orders.concat(expr.map{ |x| - case x - when Arel::Attributes::Attribute - table = Arel::Table.new(x.relation.table_alias || x.relation.name) - e = table[x.name] - Arel::Nodes::Ascending.new e - when Arel::Nodes::Ordering - x - when String - x.split(',').map do |s| - s = x if x.strip =~ /\A\b\w+\b\(.*,.*\)(\s+(ASC|DESC))?\Z/i # Allow functions with comma(s) to pass thru. - s.strip! - d = s =~ /(ASC|DESC)\Z/i ? $1.upcase : nil - e = d.nil? ? s : s.mb_chars[0...-d.length].strip - e = Arel.sql(e) - d && d == "DESC" ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e) - end - else - e = Arel.sql(x.to_s) - Arel::Nodes::Ascending.new e - end - }.flatten) - self - end - - # A friendly over ride that allows us to put a special lock object that can have a default or pass - # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method. - alias :lock_without_sqlserver :lock - def lock(locking=true) - if engine_activerecord_sqlserver_adapter? - case locking - when true - locking = Arel.sql('WITH(HOLDLOCK, ROWLOCK)') - when Arel::Nodes::SqlLiteral - when String - locking = Arel.sql locking - end - @ast.lock = Arel::Nodes::Lock.new(locking) - self - else - lock_without_sqlserver(locking) - end - end - - private - - def engine_activerecord_sqlserver_adapter? - @engine.connection && @engine.connection.class.name == AR_CA_SQLSA_NAME - end - - end - module Visitors class SQLServer < Arel::Visitors::ToSql From 188f9bb20cc8302804a91d9fda0f27b1f45463b8 Mon Sep 17 00:00:00 2001 From: Anna Date: Tue, 14 Jan 2014 23:09:47 -0500 Subject: [PATCH 0121/1412] added arel->png generator for debugging arel --- Gemfile | 1 + test/cases/sqlserver_helper.rb | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/Gemfile b/Gemfile index 9a1d322e2..69ddbd721 100644 --- a/Gemfile +++ b/Gemfile @@ -48,5 +48,6 @@ group :development do gem 'simplecov' gem 'turn' gem 'rubocop' + gem 'ruby-graphviz' end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index afc67e545..a074d7a06 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -26,6 +26,8 @@ c.verbose = true end +require 'graphviz' + require 'mocha/api' require 'active_support/dependencies' require 'active_record' @@ -130,6 +132,13 @@ def with_auto_connect(boolean) end end +#useful for debugging Arel. +# You can call it like arel_to_png(User.where(name: "foo").arel) +def arel_to_png(arel) + graph = GraphViz.parse_string(arel.to_dot) + graph.output(:png => "query.png") +end + # Core AR. schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" eval(File.read(schema_file)) From d95d776af35c98bf173b3fdecbd04cc874b49cdc Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 16:32:34 -0500 Subject: [PATCH 0122/1412] moved select_manager to it's own file --- ...manager.rb => select_manager_sqlserver.rb} | 0 lib/arel/visitors/sqlserver.rb | 22 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) rename lib/arel/{select_manager.rb => select_manager_sqlserver.rb} (100%) diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager_sqlserver.rb similarity index 100% rename from lib/arel/select_manager.rb rename to lib/arel/select_manager_sqlserver.rb diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f92340354..162147d22 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,6 +1,25 @@ require 'arel' - +require 'arel/select_manager_sqlserver' module Arel + + module Nodes + + # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a + # collection of them. See SelectManager#order for more details. + class Ordering < Arel::Nodes::Unary + def hash + expr.hash + end + def ==(other) + other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr + end + def eql?(other) + self == other + end + end + + end + module Visitors class SQLServer < Arel::Visitors::ToSql @@ -9,6 +28,7 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) def visit_Arel_Nodes_SelectStatement(o, a) + puts "o.cores.size#{o.cores.size}" unless o.cores.size < 2 if complex_count_sql?(o) visit_Arel_Nodes_SelectStatementForComplexCount(o, a) elsif o.offset From 4c2be6b6200aed164fec165477940acbfb1899d0 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 16:33:31 -0500 Subject: [PATCH 0123/1412] moved sqlserver_ignore regex to it's own file --- .../connection_adapters/sqlserver_adapter.rb | 1 + lib/active_record/sqlserver_test_case.rb | 17 +++++++++++++++++ test/cases/sqlserver_helper.rb | 12 ------------ 3 files changed, 18 insertions(+), 12 deletions(-) create mode 100644 lib/active_record/sqlserver_test_case.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b63d753ef..85fe80b7f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -18,6 +18,7 @@ require 'active_record/connection_adapters/sqlserver/showplan' require 'active_record/connection_adapters/sqlserver/quoting' require 'active_record/connection_adapters/sqlserver/utils' +require 'active_record/sqlserver_test_case' module ActiveRecord diff --git a/lib/active_record/sqlserver_test_case.rb b/lib/active_record/sqlserver_test_case.rb new file mode 100644 index 000000000..8e3200199 --- /dev/null +++ b/lib/active_record/sqlserver_test_case.rb @@ -0,0 +1,17 @@ +# I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' +# This is a temporary hack until we can just get the sqlserver_ignored regex in rails +puts "loaded" + +ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| + if listener.inspect =~ /ActiveRecord::SQLCounter/ + ActiveSupport::Notifications.unsubscribe(listener) + end +end + +module ActiveRecord + class SQLCounter + sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] + ignored_sql.concat sqlserver_ignored + end + ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) +end \ No newline at end of file diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index a074d7a06..ccbdbeeeb 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -79,18 +79,6 @@ def undefine_and_puts(method) end end -# #Our changes/additions to ActiveRecord test helpers specific for SQL Server. -# module ActiveRecord -# class SQLCounter -# sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] -# ignored_sql.concat sqlserver_ignored -# end - -# puts ActiveSupport::Notifications.subscribed('sql.active_record') -# # ActiveSupport::Notifications.unsubscribe('sql.active_record') -# ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) -# end - module ActiveRecord class TestCase < ActiveSupport::TestCase class << self From fb1740d38ad88cdcaa8de06744026e6eed5adf50 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 17:42:16 -0500 Subject: [PATCH 0124/1412] moved arel test helpers --- test/cases/arel_helper.rb | 20 +++++++++++++++++ test/cases/sqlserver_helper.rb | 41 +++++++++++----------------------- 2 files changed, 33 insertions(+), 28 deletions(-) create mode 100644 test/cases/arel_helper.rb diff --git a/test/cases/arel_helper.rb b/test/cases/arel_helper.rb new file mode 100644 index 000000000..995864c36 --- /dev/null +++ b/test/cases/arel_helper.rb @@ -0,0 +1,20 @@ +AREL_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['arel'].full_gem_path,'test')) +$LOAD_PATH.unshift AREL_TEST_ROOT + +# TODO: Find A better way to run Arel tests without failing on +# SQL Server brackets instead of quotes + class Object + def must_be_like other + actual = gsub(/\s+/, ' ').gsub(/\[|\]/,'"').gsub(/N\'/,'\'').strip + expected = other.gsub(/\s+/, ' ').strip + actual.must_equal expected + end + end + + +# Useful for debugging Arel. +# You can call it like arel_to_png(User.where(name: "foo").arel) +def arel_to_png(arel) + graph = GraphViz.parse_string(arel.to_dot) + graph.output(:png => "query.png") +end \ No newline at end of file diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index ccbdbeeeb..0a12ac8b4 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -4,30 +4,16 @@ SQLSERVER_MIGRATIONS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'migrations')) SQLSERVER_SCHEMA_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'schema')) ACTIVERECORD_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')) -AREL_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['arel'].full_gem_path,'test')) ENV['ARCONFIG'] = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'config.yml')) -$:.unshift ACTIVERECORD_TEST_ROOT -$LOAD_PATH.unshift AREL_TEST_ROOT +$LOAD_PATH.unshift ACTIVERECORD_TEST_ROOT require 'rubygems' require 'bundler' Bundler.setup require 'simplecov' -SimpleCov.start do - add_filter "/test/" -end -require 'turn' -Turn.config do |c| - c.format = :dot - #c.trace = 10 - # c.natural = true -c.verbose = true -end - require 'graphviz' - require 'mocha/api' require 'active_support/dependencies' require 'active_record' @@ -37,6 +23,12 @@ require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' require 'models/topic' +require 'cases/arel_helper' + +SimpleCov.start do + add_filter "/test/" +end + GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly?) ActiveRecord::Migration.verbose = false @@ -120,17 +112,10 @@ def with_auto_connect(boolean) end end -#useful for debugging Arel. -# You can call it like arel_to_png(User.where(name: "foo").arel) -def arel_to_png(arel) - graph = GraphViz.parse_string(arel.to_dot) - graph.output(:png => "query.png") -end +# Core AR. +schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" +eval(File.read(schema_file)) - # Core AR. - schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" - eval(File.read(schema_file)) - - # SQL Server. - sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" - eval(File.read(sqlserver_specific_schema_file)) \ No newline at end of file +# SQL Server. +sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" +eval(File.read(sqlserver_specific_schema_file)) \ No newline at end of file From 9bb1724f63e2638624d5da2972295d3be6f15fef Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 17:44:09 -0500 Subject: [PATCH 0125/1412] tidying --- Gemfile | 2 -- .../sqlserver/database_statements.rb | 1 + .../connection_adapters/sqlserver_adapter.rb | 2 +- lib/arel/visitors/sqlserver.rb | 1 - test/cases/column_test_sqlserver.rb | 1 - test/cases/finder_test_sqlserver.rb | 22 +++++++++++++++++-- test/cases/inheritance_test_sqlserver.rb | 1 - .../sql_server_natural_pk_data.rb | 2 +- 8 files changed, 23 insertions(+), 9 deletions(-) diff --git a/Gemfile b/Gemfile index 69ddbd721..e3c078ac5 100644 --- a/Gemfile +++ b/Gemfile @@ -46,8 +46,6 @@ group :development do gem 'rake', '~> 0.9.2' gem 'ruby-prof' gem 'simplecov' - gem 'turn' - gem 'rubocop' gem 'ruby-graphviz' end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index ff2246c02..01feef9a5 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -15,6 +15,7 @@ def execute(sql, name = nil) end end + # TODO I bet there's a better way than a regex to take care of this def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) # We can't update Identiy columns in sqlserver. So, strip out the id from the update. if sql =~ /UPDATE/ diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 85fe80b7f..c8e4d8811 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -66,7 +66,7 @@ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = { class << self def string_to_binary(value) - "0x#{value.unpack("H*")[0]}" + "0x#{value.unpack("H*")[0]}" end def binary_to_string(value) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 162147d22..eed6b2be6 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -28,7 +28,6 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) def visit_Arel_Nodes_SelectStatement(o, a) - puts "o.cores.size#{o.cores.size}" unless o.cores.size < 2 if complex_count_sql?(o) visit_Arel_Nodes_SelectStatementForComplexCount(o, a) elsif o.offset diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 3906e0bd8..374426cea 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -7,7 +7,6 @@ require 'models_sqlserver/sql_server_string' require 'models_sqlserver/sql_server_unicode' require 'models_sqlserver/table_with_real_column' - require 'models_sqlserver/topic' require "cases/migration/helper" diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 9b2769c26..77e854e1f 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -1,20 +1,38 @@ require 'cases/sqlserver_helper' require 'models/event' +require 'models/author' +require 'models/post' require 'models_sqlserver/topic' class FinderTestSqlserver < ActiveRecord::TestCase end class FinderTest < ActiveRecord::TestCase - + fixtures :authors, :author_addresses, :posts COERCED_TESTS = [ :test_exists_does_not_select_columns_without_alias, :test_string_sanitation, - :test_take_and_first_and_last_with_integer_should_use_sql_limit + :test_take_and_first_and_last_with_integer_should_use_sql_limit, + :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct + ] include SqlserverCoercedTest + + def test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct + p = Post.all.merge!(:includes => { :authors => :author_address }, + :order => 'author_addresses.id DESC ', + :limit => 2) + arel_to_png(p) + assert_equal 2, p.to_a.size + puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" + + assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, + :order => 'author_addresses_authors.id DESC ', :limit => 3).to_a.size + end + + def test_coerced_exists_does_not_select_columns_without_alias assert_sql(/SELECT TOP \(1\) 1 AS one FROM \[topics\]/i) do Topic.exists? diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index bb6be922d..fc8106908 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -24,7 +24,6 @@ def test_coerced_a_bad_type_column assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } end - def test_coerced_eager_load_belongs_to_primary_key_quoting con = Account.connection assert_sql(/\[companies\]\.\[id\] IN \(N''1''\)/) do diff --git a/test/models_sqlserver/sql_server_natural_pk_data.rb b/test/models_sqlserver/sql_server_natural_pk_data.rb index 10caedbb3..ba4d381db 100644 --- a/test/models_sqlserver/sql_server_natural_pk_data.rb +++ b/test/models_sqlserver/sql_server_natural_pk_data.rb @@ -1,4 +1,4 @@ class SqlServerNaturalPkData < ActiveRecord::Base self.table_name = 'natural_pk_data' -self.primary_key = 'legacy_id' + self.primary_key = 'legacy_id' end \ No newline at end of file From cc4fd3d3bdbdfb1dbee009d281eab38c7b351778 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 17:45:19 -0500 Subject: [PATCH 0126/1412] documentation --- README.md | 3 +-- RUNNING_UNIT_TESTS.md | 5 +++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa7dc71ba..c0a8a0760 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Rails 4 -One failing test. Until this is fixex when you use both :includes and :limit you will get fewer results than expected. +One failing test. Until this is fixex when you use includes, limit, and order you will get fewer results than expected. ```ruby FAIL Expected: 2 @@ -249,7 +249,6 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- * jeremydurham (Jeremy Durham) - ## Donators I am trying to save up for a Happy Hacking pro keyboard. Help me out via GitTip! https://www.gittip.com/metaskills/ diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 6f6e8bd1d..b86a87341 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -97,3 +97,8 @@ By default, Bundler will download the Rails git repo and use the git tag that ma * Misc Date/Time erros when using ODBC mode. * Misc Date/Time erros when testing SQL Server 2005. +* A find with Limit, Order, and Includes may return fewer results than the limit specifies + +FinderTest#test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct [/Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/finder_test_sqlserver.rb:28]: +Expected: 2 + Actual: 1 \ No newline at end of file From 739102bf107f61c0fe311fa220fe6d735c1adec6 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 23:25:02 -0500 Subject: [PATCH 0127/1412] Conditinally coerce tests that are new in rails 4.0.1 --- test/cases/disconnected_test_sqlserver.rb | 48 ++++++++++--------- .../invalid_connection_test_sqlserver.rb | 17 ++++--- 2 files changed, 35 insertions(+), 30 deletions(-) diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index e6072be02..5dbd3c666 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -1,33 +1,35 @@ -require 'cases/sqlserver_helper' -require 'cases/disconnected_test' +if Pathname.new('cases/disconnected_test').exist? + require 'cases/sqlserver_helper' + require 'cases/disconnected_test' -class TestDisconnectedAdapter < ActiveRecord::TestCase - def setup - skip "TestDisconnectedAdapterSqlserver instead " + class TestDisconnectedAdapter < ActiveRecord::TestCase + def setup + skip "TestDisconnectedAdapterSqlserver instead " + end end -end -class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase - self.use_transactional_fixtures = false + class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase + self.use_transactional_fixtures = false - def setup - skip "in-memory database mustn't disconnect" if in_memory_db? - @connection = ActiveRecord::Base.connection - end + def setup + skip "in-memory database mustn't disconnect" if in_memory_db? + @connection = ActiveRecord::Base.connection + end - def teardown - return if in_memory_db? - spec = ActiveRecord::Base.connection_config - ActiveRecord::Base.establish_connection(spec) - end + def teardown + return if in_memory_db? + spec = ActiveRecord::Base.connection_config + ActiveRecord::Base.establish_connection(spec) + end - test "can't execute statements while disconnected" do - with_auto_connect(false) do - @connection.execute "SELECT count(*) from products" - @connection.disconnect! - assert_raises(ActiveRecord::LostConnection) do + test "can't execute statements while disconnected" do + with_auto_connect(false) do @connection.execute "SELECT count(*) from products" + @connection.disconnect! + assert_raises(ActiveRecord::LostConnection) do + @connection.execute "SELECT count(*) from products" + end end end end -end +end \ No newline at end of file diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb index 4be328746..5e9cf8a68 100644 --- a/test/cases/invalid_connection_test_sqlserver.rb +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -1,10 +1,13 @@ -require 'cases/sqlserver_helper' -require 'cases/invalid_connection_test' +#cases/invalid_connection_test was added in rails 4.0.1 +if Pathname.new('cases/invalid_connection_test').exist? + require 'cases/sqlserver_helper' + require 'cases/invalid_connection_test' -class TestAdapterWithInvalidConnection < ActiveRecord::TestCase - def setup - #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). - #It makes much more sense for use to use sqlserver - Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' + class TestAdapterWithInvalidConnection < ActiveRecord::TestCase + def setup + #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). + #It makes much more sense for us to use sqlserver + Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' + end end end From 09587b984d6e4faaae8225a6f7e7c59fd6103255 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 15 Jan 2014 23:25:26 -0500 Subject: [PATCH 0128/1412] bump version to 4.0.0 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 275e51e5e..fcdb2e109 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.2.12 +4.0.0 From 79b0aee35e349d42aaa90fca9eb21bd3add7199c Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 16 Jan 2014 16:17:13 -0500 Subject: [PATCH 0129/1412] move simplecov back up --- test/cases/arel_helper.rb | 4 ++-- test/cases/sqlserver_helper.rb | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/test/cases/arel_helper.rb b/test/cases/arel_helper.rb index 995864c36..0d44ba0f1 100644 --- a/test/cases/arel_helper.rb +++ b/test/cases/arel_helper.rb @@ -14,7 +14,7 @@ def must_be_like other # Useful for debugging Arel. # You can call it like arel_to_png(User.where(name: "foo").arel) -def arel_to_png(arel) +def arel_to_png(arel, file_name = "query") graph = GraphViz.parse_string(arel.to_dot) - graph.output(:png => "query.png") + graph.output(:png => "#{file_name}.png") end \ No newline at end of file diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 0a12ac8b4..9b859b22c 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -13,6 +13,9 @@ require 'bundler' Bundler.setup require 'simplecov' +SimpleCov.start do + add_filter "/test/" +end require 'graphviz' require 'mocha/api' require 'active_support/dependencies' @@ -25,10 +28,6 @@ require 'models/topic' require 'cases/arel_helper' -SimpleCov.start do - add_filter "/test/" -end - GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly?) ActiveRecord::Migration.verbose = false From 560ec10111b64a71cbf238f2a2bf146a41742638 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 16 Jan 2014 16:35:40 -0500 Subject: [PATCH 0130/1412] troubleshooting failing test --- test/cases/finder_test_sqlserver.rb | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 77e854e1f..ea86f49d0 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -2,7 +2,9 @@ require 'models/event' require 'models/author' require 'models/post' +require 'models/categorization' require 'models_sqlserver/topic' +# require 'cases/finder_test.rb' class FinderTestSqlserver < ActiveRecord::TestCase end @@ -20,15 +22,18 @@ class FinderTest < ActiveRecord::TestCase include SqlserverCoercedTest + # TODO This test passes in rails 4.0.0 but not 4.0.1-2 def test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct p = Post.all.merge!(:includes => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2) - arel_to_png(p) - assert_equal 2, p.to_a.size - puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" + # ar_version = Gem.loaded_specs['activerecord'].version.version + # arel_to_png(p, "#{ar_version}") + count = p.to_a.size + #puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" + assert_equal 2, count - assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, + assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, :order => 'author_addresses_authors.id DESC ', :limit => 3).to_a.size end From dba58fc77af4e8585e1d8dd8b52aa55b49c05a5d Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 16 Jan 2014 16:35:40 -0500 Subject: [PATCH 0131/1412] troubleshooting failing test --- .../connection_adapters/sqlserver_adapter.rb | 1 - test/cases/disconnected_test_sqlserver.rb | 5 +++-- test/cases/finder_test_sqlserver.rb | 13 +++++++++---- test/cases/invalid_connection_test_sqlserver.rb | 2 +- test/cases/sqlserver_helper.rb | 1 + .../cases}/sqlserver_test_case.rb | 6 +++--- 6 files changed, 17 insertions(+), 11 deletions(-) rename {lib/active_record => test/cases}/sqlserver_test_case.rb (83%) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c8e4d8811..1c2d0849b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -18,7 +18,6 @@ require 'active_record/connection_adapters/sqlserver/showplan' require 'active_record/connection_adapters/sqlserver/quoting' require 'active_record/connection_adapters/sqlserver/utils' -require 'active_record/sqlserver_test_case' module ActiveRecord diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 5dbd3c666..621aff949 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -1,4 +1,5 @@ -if Pathname.new('cases/disconnected_test').exist? +if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/disconnected_test.rb").exist? + #cases/disconnected_test was added in rails 4.0.1 so this errors in 4.0.0 require 'cases/sqlserver_helper' require 'cases/disconnected_test' @@ -32,4 +33,4 @@ def teardown end end end -end \ No newline at end of file +end diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 77e854e1f..ea86f49d0 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -2,7 +2,9 @@ require 'models/event' require 'models/author' require 'models/post' +require 'models/categorization' require 'models_sqlserver/topic' +# require 'cases/finder_test.rb' class FinderTestSqlserver < ActiveRecord::TestCase end @@ -20,15 +22,18 @@ class FinderTest < ActiveRecord::TestCase include SqlserverCoercedTest + # TODO This test passes in rails 4.0.0 but not 4.0.1-2 def test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct p = Post.all.merge!(:includes => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2) - arel_to_png(p) - assert_equal 2, p.to_a.size - puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" + # ar_version = Gem.loaded_specs['activerecord'].version.version + # arel_to_png(p, "#{ar_version}") + count = p.to_a.size + #puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" + assert_equal 2, count - assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, + assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, :order => 'author_addresses_authors.id DESC ', :limit => 3).to_a.size end diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb index 5e9cf8a68..84a9e28fd 100644 --- a/test/cases/invalid_connection_test_sqlserver.rb +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -1,5 +1,5 @@ #cases/invalid_connection_test was added in rails 4.0.1 -if Pathname.new('cases/invalid_connection_test').exist? +if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/invalid_connection_test.rb").exist? require 'cases/sqlserver_helper' require 'cases/invalid_connection_test' diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 9b859b22c..a5aed27b5 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -27,6 +27,7 @@ require 'cases/helper' require 'models/topic' require 'cases/arel_helper' +require 'cases/sqlserver_test_case' GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly?) diff --git a/lib/active_record/sqlserver_test_case.rb b/test/cases/sqlserver_test_case.rb similarity index 83% rename from lib/active_record/sqlserver_test_case.rb rename to test/cases/sqlserver_test_case.rb index 8e3200199..489a41034 100644 --- a/lib/active_record/sqlserver_test_case.rb +++ b/test/cases/sqlserver_test_case.rb @@ -1,7 +1,7 @@ -# I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' -# This is a temporary hack until we can just get the sqlserver_ignored regex in rails -puts "loaded" +require 'active_record/test_case.rb' +# TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' +# This is a temporary hack until we can just get the sqlserver_ignored regex in rails ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| if listener.inspect =~ /ActiveRecord::SQLCounter/ ActiveSupport::Notifications.unsubscribe(listener) From a8d6f26483609349c37cacca6a47dc923c1008f1 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 16 Jan 2014 18:22:25 -0500 Subject: [PATCH 0132/1412] moved --- lib/active_record/sqlserver_test_case.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 lib/active_record/sqlserver_test_case.rb diff --git a/lib/active_record/sqlserver_test_case.rb b/lib/active_record/sqlserver_test_case.rb new file mode 100644 index 000000000..489a41034 --- /dev/null +++ b/lib/active_record/sqlserver_test_case.rb @@ -0,0 +1,17 @@ +require 'active_record/test_case.rb' + +# TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' +# This is a temporary hack until we can just get the sqlserver_ignored regex in rails +ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| + if listener.inspect =~ /ActiveRecord::SQLCounter/ + ActiveSupport::Notifications.unsubscribe(listener) + end +end + +module ActiveRecord + class SQLCounter + sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] + ignored_sql.concat sqlserver_ignored + end + ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) +end \ No newline at end of file From 95a4e1fcd44f453a494af20d311a5280ec454a51 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 16 Jan 2014 18:27:06 -0500 Subject: [PATCH 0133/1412] documentation --- README.md | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index c0a8a0760..258d26983 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,3 @@ -# Rails 4 -One failing test. Until this is fixex when you use includes, limit, and order you will get fewer results than expected. -```ruby -FAIL - Expected: 2 - Actual: 1 - /Users/acarey/code/source/rails/rails40/activerecord/test/cases/finder_test.rb:822:in `test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct' -``` - - # ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. @@ -16,7 +6,7 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server ## What's New * Rails 4 support - +* Ruby 2.0.0 and 2.1.0.prerelease support #### Testing Rake Tasks Support @@ -206,7 +196,7 @@ The adapter has no strict gem dependencies outside of ActiveRecord. You will hav ```ruby gem 'tiny_tds' -gem 'activerecord-sqlserver-adapter', '~> 3.1.0' +gem 'activerecord-sqlserver-adapter', '~> 4.0.0' ``` If you want to use ruby ODBC, please use at least version 0.99992 since that contains fixes for both native types as well as fixes for proper encoding support under 1.9. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too! From 595339bccdcd9ed980c372ca65ed9b4a50222569 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Tue, 21 Jan 2014 16:18:32 -0500 Subject: [PATCH 0134/1412] moved arel classes/modules to separate files, no functional difference --- .../connection_adapters/sqlserver_adapter.rb | 2 +- lib/arel/arel_sqlserver.rb | 5 +++++ lib/arel/nodes_sqlserver.rb | 19 +++++++++++++++++ lib/arel/visitors/sqlserver.rb | 21 ------------------- 4 files changed, 25 insertions(+), 22 deletions(-) create mode 100644 lib/arel/arel_sqlserver.rb create mode 100644 lib/arel/nodes_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1c2d0849b..993972d07 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,5 +1,5 @@ require 'base64' -require 'arel/visitors/sqlserver' +require 'arel/arel_sqlserver' require 'arel/visitors/bind_visitor' require 'active_record' require 'active_record/base' diff --git a/lib/arel/arel_sqlserver.rb b/lib/arel/arel_sqlserver.rb new file mode 100644 index 000000000..ff6363460 --- /dev/null +++ b/lib/arel/arel_sqlserver.rb @@ -0,0 +1,5 @@ +require 'arel' +require 'arel/select_manager_sqlserver' +require 'arel/nodes_sqlserver' +require 'arel/visitors/sqlserver' +require 'arel/visitors/bind_visitor' \ No newline at end of file diff --git a/lib/arel/nodes_sqlserver.rb b/lib/arel/nodes_sqlserver.rb new file mode 100644 index 000000000..cfc21cf65 --- /dev/null +++ b/lib/arel/nodes_sqlserver.rb @@ -0,0 +1,19 @@ +module Arel + module Nodes + + # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a + # collection of them. See SelectManager#order for more details. + class Ordering < Arel::Nodes::Unary + def hash + expr.hash + end + def ==(other) + other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr + end + def eql?(other) + self == other + end + end + + end +end \ No newline at end of file diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index eed6b2be6..87849aef1 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,25 +1,4 @@ -require 'arel' -require 'arel/select_manager_sqlserver' module Arel - - module Nodes - - # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a - # collection of them. See SelectManager#order for more details. - class Ordering < Arel::Nodes::Unary - def hash - expr.hash - end - def ==(other) - other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr - end - def eql?(other) - self == other - end - end - - end - module Visitors class SQLServer < Arel::Visitors::ToSql From a52a8df876f2e8b32266efce77a3b78cb2e1165d Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Tue, 21 Jan 2014 16:19:53 -0500 Subject: [PATCH 0135/1412] no longer necessary to specify encoding --- activerecord-sqlserver-adapter.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 1cc893783..20d6bd82a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,4 +1,3 @@ -# -*- encoding: utf-8 -*- $:.push File.expand_path("../lib", __FILE__) Gem::Specification.new do |s| From 376d0364bdef59c39fefcb997c41ceaae3c47064 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Wed, 22 Jan 2014 09:11:15 -0500 Subject: [PATCH 0136/1412] arel now supports comparisons, but we still need to override the eql? method for Ordering --- lib/arel/nodes_sqlserver.rb | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/lib/arel/nodes_sqlserver.rb b/lib/arel/nodes_sqlserver.rb index cfc21cf65..ce40beaa7 100644 --- a/lib/arel/nodes_sqlserver.rb +++ b/lib/arel/nodes_sqlserver.rb @@ -1,19 +1,14 @@ module Arel module Nodes - # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a # collection of them. See SelectManager#order for more details. class Ordering < Arel::Nodes::Unary - def hash - expr.hash - end - def ==(other) - other.is_a?(Arel::Nodes::Ordering) && self.expr == other.expr - end def eql?(other) - self == other + #Arel::Nodes::Ascending or Arel::Nodes::Desecnding + other.is_a?(Arel::Nodes::Ordering) && + self.expr == other.expr end + alias :== :eql? end - end end \ No newline at end of file From 111d029c275cf089175e7b6ab06228d9ebe26e77 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Wed, 22 Jan 2014 12:58:29 -0500 Subject: [PATCH 0137/1412] pass a to all visitor calls --- lib/arel/visitors/sqlserver.rb | 111 +++++++++++++++++---------------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 87849aef1..d2fbe7007 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -24,27 +24,27 @@ def visit_Arel_Nodes_UpdateStatement(o, a) end def visit_Arel_Nodes_Offset(o, a) - "WHERE [__rnt].[__rn] > (#{visit o.expr})" + "WHERE [__rnt].[__rn] > (#{visit o.expr, a})" end def visit_Arel_Nodes_Limit(o, a) - "TOP (#{visit o.expr})" + "TOP (#{visit o.expr, a})" end def visit_Arel_Nodes_Lock(o, a) - visit o.expr + visit o.expr, a end def visit_Arel_Nodes_Ordering(o, a) if o.respond_to?(:direction) - "#{visit o.expr} #{o.ascending? ? 'ASC' : 'DESC'}" + "#{visit o.expr, a} #{o.ascending? ? 'ASC' : 'DESC'}" else - visit o.expr + visit o.expr, a end end def visit_Arel_Nodes_Bin(o, a) - "#{visit o.expr} #{@connection.cs_equality_operator}" + "#{visit o.expr, a} #{@connection.cs_equality_operator}" end # SQLServer ToSql/Visitor (Additions) @@ -56,28 +56,28 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) groups = core.groups orders = o.orders.uniq if windowed - projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x) } - groups = projections.map { |x| projection_without_expression(x) } if windowed_single_distinct_select_statement?(o) && groups.empty? + projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x, a) } + groups = projections.map { |x| projection_without_expression(x, a) } if windowed_single_distinct_select_statement?(o) && groups.empty? groups += orders.map { |x| Arel.sql(x.expr) } if windowed_single_distinct_select_statement?(o) - elsif eager_limiting_select_statement?(o) - projections = projections.map { |x| projection_without_expression(x) } - groups = projections.map { |x| projection_without_expression(x) } + elsif eager_limiting_select_statement?(o, a) + projections = projections.map { |x| projection_without_expression(x, a) } + groups = projections.map { |x| projection_without_expression(x, a) } orders = orders.map do |x| - expr = Arel.sql projection_without_expression(x.expr) + expr = Arel.sql projection_without_expression(x.expr, a) x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr]) end - elsif top_one_everything_for_through_join?(o) - projections = projections.map { |x| projection_without_expression(x) } + elsif top_one_everything_for_through_join?(o, a) + projections = projections.map { |x| projection_without_expression(x, a) } end [ ("SELECT" if !windowed), - (visit(core.set_quantifier) if core.set_quantifier && !windowed), - (visit(o.limit) if o.limit && !windowed), - (projections.map{ |x| v = visit(x); v == "1" ? "1 AS [__wrp]" : v }.join(', ')), - (source_with_lock_for_select_statement(o)), - ("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{groups.map { |x| visit x }.join ', ' }" unless groups.empty?), - (visit(core.having) if core.having), - ("ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}" if !orders.empty? && !windowed) + (visit(core.set_quantifier, a) if core.set_quantifier && !windowed), + (visit(o.limit, a) if o.limit && !windowed), + (projections.map{ |x| v = visit(x, a); v == "1" ? "1 AS [__wrp]" : v }.join(', ')), + (source_with_lock_for_select_statement(o, a)), + ("WHERE #{core.wheres.map{ |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), + ("GROUP BY #{groups.map { |x| visit(x, a) }.join ', ' }" unless groups.empty?), + (visit(core.having, a) if core.having), + ("ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}" if !orders.empty? && !windowed) ].compact.join ' ' end @@ -86,13 +86,13 @@ def visit_Arel_Nodes_SelectStatementWithOffset(o, a) o.limit ||= Arel::Nodes::Limit.new(9223372036854775807) orders = rowtable_orders(o) [ "SELECT", - (visit(o.limit) if o.limit && !windowed_single_distinct_select_statement?(o)), - (rowtable_projections(o).map{ |x| visit(x) }.join(', ')), + (visit(o.limit, a) if o.limit && !windowed_single_distinct_select_statement?(o)), + (rowtable_projections(o, a).map{ |x| visit(x, a) }.join(', ')), "FROM (", - "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],", + "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}) AS [__rn],", visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, true), ") AS [__rnt]", - (visit(o.offset) if o.offset), + (visit(o.offset, a) if o.offset), "ORDER BY [__rnt].[__rn] ASC" ].compact.join ' ' end @@ -104,27 +104,27 @@ def visit_Arel_Nodes_SelectStatementForComplexCount(o, a) [ "SELECT COUNT([count]) AS [count_id]", "FROM (", "SELECT", - (visit(o.limit) if o.limit), - "ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}) AS [__rn],", + (visit(o.limit, a) if o.limit), + "ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}) AS [__rn],", "1 AS [count]", - (source_with_lock_for_select_statement(o)), - ("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{core.groups.map { |x| visit x }.join ', ' }" unless core.groups.empty?), - (visit(core.having) if core.having), - ("ORDER BY #{o.orders.map{ |x| visit(x) }.join(', ')}" if !o.orders.empty?), + (source_with_lock_for_select_statement(o, a)), + ("WHERE #{core.wheres.map{ |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), + ("GROUP BY #{core.groups.map { |x| visit(x, a) }.join ', ' }" unless core.groups.empty?), + (visit(core.having, a) if core.having), + ("ORDER BY #{o.orders.map{ |x| visit(x, a) }.join(', ')}" if !o.orders.empty?), ") AS [__rnt]", - (visit(o.offset) if o.offset) + (visit(o.offset, a) if o.offset) ].compact.join ' ' end # SQLServer Helpers - def source_with_lock_for_select_statement(o) + def source_with_lock_for_select_statement(o, a) core = o.cores.first - source = "FROM #{visit(core.source).strip}" if core.source + source = "FROM #{visit(core.source, a).strip}" if core.source if source && o.lock - lock = visit o.lock + lock = visit o.lock, a index = source.match(/FROM [\w\[\]\.]+/)[0].mb_chars.length source.insert index, " #{lock}" else @@ -164,23 +164,27 @@ def single_distinct_select_statement?(o) end def windowed_single_distinct_select_statement?(o) - o.limit && o.offset && single_distinct_select_statement?(o) + + o.limit && + o.offset && + single_distinct_select_statement?(o) end - def single_distinct_select_everything_statement?(o) - single_distinct_select_statement?(o) && visit(o.cores.first.projections.first).ends_with?(".*") + def single_distinct_select_everything_statement?(o, a) + single_distinct_select_statement?(o) && + visit(o.cores.first.projections.first, a).ends_with?(".*") end - def top_one_everything_for_through_join?(o) - single_distinct_select_everything_statement?(o) && + def top_one_everything_for_through_join?(o, a) + single_distinct_select_everything_statement?(o, a) && (o.limit && !o.offset) && join_in_select_statement?(o) end - def all_projections_aliased_in_select_statement?(o) + def all_projections_aliased_in_select_statement?(o, a) projections = o.cores.first.projections projections.all? do |x| - visit(x).split(',').all? { |y| y.include?(' AS ') } + visit(x, a).split(',').all? { |y| y.include?(' AS ') } end end @@ -189,12 +193,12 @@ def function_select_statement?(o) core.projections.any? { |x| Arel::Nodes::Function === x } end - def eager_limiting_select_statement?(o) + def eager_limiting_select_statement?(o, a) core = o.cores.first single_distinct_select_statement?(o) && (o.limit && !o.offset) && core.groups.empty? && - !single_distinct_select_everything_statement?(o) + !single_distinct_select_everything_statement?(o, a) end def join_in_select_statement?(o) @@ -243,14 +247,15 @@ def find_and_fix_uncorrelated_joins_in_select_statement(o) j2.sub! "[#{j2_tn}].", "[#{j2_tn}_crltd]." end - def rowtable_projections(o) + def rowtable_projections(o, a) core = o.cores.first + puts "windowed_single_distinct_select_statement?(o)#{windowed_single_distinct_select_statement?(o)} core.groups.blank?:#{core.groups.blank?}" if windowed_single_distinct_select_statement?(o) && core.groups.blank? tn = table_from_select_statement(o).name core.projections.map do |x| x.dup.tap do |p| p.sub! 'DISTINCT', '' - p.insert 0, visit(o.limit) if o.limit + p.insert 0, visit(o.limit, a) if o.limit p.gsub! /\[?#{tn}\]?\./, '[__rnt].' p.strip! end @@ -259,14 +264,14 @@ def rowtable_projections(o) tn = table_from_select_statement(o).name core.projections.map do |x| x.dup.tap do |p| - p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit)}".strip if o.limit + p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit, a)}".strip if o.limit p.gsub! /\[?#{tn}\]?\./, '[__rnt].' p.strip! end end - elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o) + elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o, a) core.projections.map do |x| - Arel.sql visit(x).split(',').map{ |y| y.split(' AS ').last.strip }.join(', ') + Arel.sql visit(x, a).split(',').map{ |y| y.split(' AS ').last.strip }.join(', ') end elsif select_primary_key_sql?(o) [Arel.sql("[__rnt].#{quote_column_name(core.projections.first.name)}")] @@ -287,8 +292,8 @@ def rowtable_orders(o) end # TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral. - def projection_without_expression(projection) - Arel.sql(visit(projection).split(',').map do |x| + def projection_without_expression(projection, a) + Arel.sql(visit(projection, a).split(',').map do |x| x.strip! x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/,'\3') x.sub!(/^DISTINCT\s*/,'') From 1bb0baa47fe9778088654b24e6b476bd6bf5cd69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Ko=CC=88lbl?= Date: Mon, 27 Jan 2014 13:20:37 +0200 Subject: [PATCH 0138/1412] Fix warning about already defined EXPLAINED_SQLS --- .../sqlserver/core_ext/explain_subscriber.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 349a40bd8..d96f8ac2c 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1 +1,4 @@ -ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i +silence_warnings do + # Already defined in Rails + ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i +end From c80efeed8d24d705e3270d96c7c0fbcd1d978c62 Mon Sep 17 00:00:00 2001 From: Nikhil Sarwate Date: Wed, 5 Feb 2014 09:53:35 -0800 Subject: [PATCH 0139/1412] missing parens --- lib/arel/visitors/sqlserver.rb | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index eed6b2be6..ed750b1bb 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -96,7 +96,7 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) (projections.map{ |x| v = visit(x); v == "1" ? "1 AS [__wrp]" : v }.join(', ')), (source_with_lock_for_select_statement(o)), ("WHERE #{core.wheres.map{ |x| visit(x) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{groups.map { |x| visit x }.join ', ' }" unless groups.empty?), + ("GROUP BY #{groups.map { |x| visit(x) }.join ', ' }" unless groups.empty?), (visit(core.having) if core.having), ("ORDER BY #{orders.map{ |x| visit(x) }.join(', ')}" if !orders.empty? && !windowed) ].compact.join ' ' @@ -183,18 +183,18 @@ def single_distinct_select_statement?(o) ((p1.respond_to?(:distinct) && p1.distinct) || p1.respond_to?(:include?) && p1.include?('DISTINCT')) end - + def windowed_single_distinct_select_statement?(o) o.limit && o.offset && single_distinct_select_statement?(o) end - + def single_distinct_select_everything_statement?(o) single_distinct_select_statement?(o) && visit(o.cores.first.projections.first).ends_with?(".*") end - + def top_one_everything_for_through_join?(o) - single_distinct_select_everything_statement?(o) && - (o.limit && !o.offset) && + single_distinct_select_everything_statement?(o) && + (o.limit && !o.offset) && join_in_select_statement?(o) end @@ -212,9 +212,9 @@ def function_select_statement?(o) def eager_limiting_select_statement?(o) core = o.cores.first - single_distinct_select_statement?(o) && - (o.limit && !o.offset) && - core.groups.empty? && + single_distinct_select_statement?(o) && + (o.limit && !o.offset) && + core.groups.empty? && !single_distinct_select_everything_statement?(o) end @@ -230,7 +230,7 @@ def complex_count_sql?(o) o.limit && !join_in_select_statement?(o) end - + def select_primary_key_sql?(o) core = o.cores.first return false if core.projections.size != 1 From e3e5304b34d42464c5bc5bae27f0f165ff4b0ef9 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Fri, 7 Feb 2014 01:06:57 -0500 Subject: [PATCH 0140/1412] noted rails pull request --- lib/arel/visitors/sqlserver.rb | 1 - .../connection_management_test_sqlserver.rb | 26 +++---------------- 2 files changed, 3 insertions(+), 24 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 944219dc6..ff4a25868 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -249,7 +249,6 @@ def find_and_fix_uncorrelated_joins_in_select_statement(o) def rowtable_projections(o, a) core = o.cores.first - puts "windowed_single_distinct_select_statement?(o)#{windowed_single_distinct_select_statement?(o)} core.groups.blank?:#{core.groups.blank?}" if windowed_single_distinct_select_statement?(o) && core.groups.blank? tn = table_from_select_statement(o).name core.projections.map do |x| diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/connection_management_test_sqlserver.rb index 0a8a52f20..af30a2269 100644 --- a/test/cases/connection_management_test_sqlserver.rb +++ b/test/cases/connection_management_test_sqlserver.rb @@ -1,3 +1,5 @@ +#This rails pull request will make this code unnecessary https://github.com/rails/rails/pull/13745 + require "cases/sqlserver_helper" require "rack" @@ -6,7 +8,6 @@ module ConnectionAdapters class ConnectionManagementTest < ActiveRecord::TestCase COERCED_TESTS = [:test_connection_pool_per_pid] - # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest @@ -17,30 +18,9 @@ def test_coerced_connection_pool_per_pid object_id = ActiveRecord::Base.connection.object_id rd, wr = IO.pipe - - # #https://www.ruby-forum.com/topic/4221299 - # puts "rd.internal_encoding:#{rd.internal_encoding}\nrd.external_encoding:#{rd.external_encoding}" - # puts "wr.internal_encoding:#{wr.internal_encoding}\nwr.external_encoding:#{wr.external_encoding}" - # # rd.internal_encoding: - # # rd.external_encoding:UTF-8 - # # wr.internal_encoding: - # # wr.external_encoding:UTF-8 - - rd.binmode wr.binmode - - # ERROR - # marshal data too short - # test/cases/connection_management_test_sqlserver.rb:43:in `load' - # test/cases/connection_management_test_sqlserver.rb:43:in `test_coerced_connection_pool_per_pid' - - - # /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:41:in `write': "\x98" from ASCII-8BIT to UTF-8 (Encoding::UndefinedConversionError) - # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:41:in `block in test_coerced_connection_pool_per_pid' - # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:38:in `fork' - # from /Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/connection_management_test_sqlserver.rb:38:in `test_coerced_connection_pool_per_pid' - pid = fork { + pid = fork { rd.close wr.write Marshal.dump ActiveRecord::Base.connection.object_id wr.close From e3108b6782167639528bea1201ca6867ee333268 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Fri, 14 Feb 2014 11:12:34 -0500 Subject: [PATCH 0141/1412] noting that there is no 4.0 release yet --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 258d26983..685bb3194 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server ## What's New -* Rails 4 support +* Rails 4 support (not yet released, look at outstanding issues) * Ruby 2.0.0 and 2.1.0.prerelease support #### Testing Rake Tasks Support From 64f63e2ff96e1852175c38ea497823063077630d Mon Sep 17 00:00:00 2001 From: Eric Cohen Date: Thu, 13 Mar 2014 17:10:09 +0200 Subject: [PATCH 0142/1412] Fix bug where database names with dots would get improperly quoted. A sample DB name of part1.part2 would be quoted as [part1].[part2] instead of [part1.part2] --- .../sqlserver/database_statements.rb | 8 ++++---- .../connection_adapters/sqlserver/quoting.rb | 4 ++++ .../sqlserver/schema_cache.rb | 18 +++++++++++++----- .../connection_adapters/sqlserver_adapter.rb | 4 ++-- .../database_statements_test_sqlserver.rb | 9 +++++++++ 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1ee10faaf..5f415b467 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -128,7 +128,7 @@ def execute_procedure(proc_name, *variables) def use_database(database=nil) return if sqlserver_azure? database ||= @connection_options[:database] - do_execute "USE #{quote_table_name(database)}" unless database.blank? + do_execute "USE #{quote_database_name(database)}" unless database.blank? end def user_options @@ -267,7 +267,7 @@ def drop_database(database) retry_count = 0 max_retries = 1 begin - do_execute "DROP DATABASE #{quote_table_name(database)}" + do_execute "DROP DATABASE #{quote_database_name(database)}" rescue ActiveRecord::StatementInvalid => err if err.message =~ /because it is currently in use/i raise if retry_count >= max_retries @@ -284,9 +284,9 @@ def drop_database(database) def create_database(database, collation=@connection_options[:collation]) if collation - do_execute "CREATE DATABASE #{quote_table_name(database)} COLLATE #{collation}" + do_execute "CREATE DATABASE #{quote_database_name(database)} COLLATE #{collation}" else - do_execute "CREATE DATABASE #{quote_table_name(database)}" + do_execute "CREATE DATABASE #{quote_database_name(database)}" end end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index c7c5ab432..28820b139 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -49,6 +49,10 @@ def quote_table_name(name) quote_column_name(name) end + def quote_database_name(name) + schema_cache.quote_name(name, false) + end + def substitute_at(column, index) if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' nil diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index 280048e77..ec9ece0e3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -65,14 +65,22 @@ def view_information(table_name) return @view_information[key] if @view_information.key? key @view_information[key] = connection.send(:view_information, table_name) end - - def quote_name(name) + + def quote_name(name, split_on_dots = true) return @quoted_names[name] if @quoted_names.key? name - @quoted_names[name] = name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.') + + @quoted_names[name] = if split_on_dots + name.to_s.split('.').map{ |n| quote_name_part(n) }.join('.') + else + quote_name_part(name.to_s) + end end - - + private + + def quote_name_part(part) + part =~ /^\[.*\]$/ ? part : "[#{part.to_s.gsub(']', ']]')}]" + end def table_name_key(table_name) Utils.unqualify_table_name(table_name) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1c2d0849b..108c9f439 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -494,11 +494,11 @@ def initialize_dateformatter def remove_database_connections_and_rollback(database=nil) database ||= current_database - do_execute "ALTER DATABASE #{quote_table_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" + do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" begin yield ensure - do_execute "ALTER DATABASE #{quote_table_name(database)} SET MULTI_USER" + do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" end if block_given? end diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index 3928b6c00..9df07d7e1 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -19,6 +19,15 @@ class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" assert_equal nil, database_name end + + should 'create/use/drop database with name with dots' do + @connection.create_database 'activerecord.unittest' + database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'" + assert_equal 'activerecord.unittest', database_name + @connection.use_database 'activerecord.unittest' + @connection.use_database 'master' + @connection.drop_database 'activerecord.unittest' + end context 'with collation' do teardown do From d15d388cb835908525b6782085148b0c2f9924fb Mon Sep 17 00:00:00 2001 From: "Erik-B. Ernst" Date: Wed, 19 Mar 2014 16:10:26 +0100 Subject: [PATCH 0143/1412] fixed 'wrong number of arguments (3 for 2) (ArgumentError)' in database_statements.rb --- .../connection_adapters/sqlserver/database_statements.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1ee10faaf..121102056 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -2,9 +2,9 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module DatabaseStatements - - def select_rows(sql, name = nil) - raw_select sql, name, [], :fetch => :rows + + def select_rows(sql, name = nil, binds = []) + raw_select sql, name, binds, :fetch => :rows end def execute(sql, name = nil) From 4ba12c3655196fd0ae5b7b79409fee1803f43d88 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Tue, 25 Mar 2014 23:29:32 -0400 Subject: [PATCH 0144/1412] use default database after test --- .../database_statements_test_sqlserver.rb | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index 9df07d7e1..16406f3ac 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -1,19 +1,19 @@ require 'cases/sqlserver_helper' - + class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase - + self.use_transactional_fixtures = false - + setup do @connection = ActiveRecord::Base.connection end - + should 'create database' do @connection.create_database 'activerecord_unittest3' #, 'SQL_Latin1_General_CP1_CI_AS' database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" assert_equal 'activerecord_unittest3', database_name end - + should 'drop database' do @connection.drop_database 'activerecord_unittest3' database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" @@ -25,28 +25,28 @@ class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'" assert_equal 'activerecord.unittest', database_name @connection.use_database 'activerecord.unittest' - @connection.use_database 'master' + @connection.use_database @connection.drop_database 'activerecord.unittest' end - + context 'with collation' do teardown do @connection.drop_database 'activerecord_unittest3' end - + should 'create database with default collation for the server' do @connection.create_database 'activerecord_unittest3' default_collation = @connection.select_value "SELECT SERVERPROPERTY('Collation')" database_collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal default_collation, database_collation end - + should 'create database with collation set by the method' do @connection.create_database 'activerecord_unittest3', 'SQL_Latin1_General_CP1_CI_AS' collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation end - + should 'create database with collation set by the config' do @connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS' @connection.create_database 'activerecord_unittest3' From 58dea084bb457936dc1bfb2097f392fd40891c76 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Sun, 2 Mar 2014 23:57:54 -0500 Subject: [PATCH 0145/1412] Added rubocop gem and fixed style, no functional changes removed trailing whitespace and fixed other whitespace issues convert 1.8 hash syntax change double to single quotes where possible style changes such as preferring map over collect --- .rubocop.yml | 20 ++ Gemfile | 12 +- README.md | 10 +- Rakefile | 61 +++-- activerecord-sqlserver-adapter.gemspec | 25 +-- .../sqlserver/core_ext/active_record.rb | 9 +- .../sqlserver/core_ext/explain.rb | 15 +- .../sqlserver/core_ext/odbc.rb | 18 +- .../sqlserver/core_ext/relation.rb | 4 +- .../sqlserver/database_limits.rb | 6 +- .../sqlserver/database_statements.rb | 208 +++++++++--------- .../connection_adapters/sqlserver/errors.rb | 24 +- .../connection_adapters/sqlserver/quoting.rb | 4 +- .../sqlserver/schema_cache.rb | 26 +-- .../sqlserver/schema_statements.rb | 147 ++++++------- .../connection_adapters/sqlserver/showplan.rb | 23 +- .../sqlserver/showplan/printer_table.rb | 19 +- .../sqlserver/showplan/printer_xml.rb | 7 +- .../connection_adapters/sqlserver/utils.rb | 14 +- .../connection_adapters/sqlserver_adapter.rb | 166 +++++++------- lib/active_record/sqlserver_test_case.rb | 10 +- lib/arel/arel_sqlserver.rb | 2 +- lib/arel/nodes_sqlserver.rb | 8 +- lib/arel/select_manager_sqlserver.rb | 26 +-- lib/arel/visitors/sqlserver.rb | 83 ++++--- test/cases/adapter_test_sqlserver.rb | 24 +- test/cases/arel_helper.rb | 6 +- test/cases/associations_test_sqlserver.rb | 2 +- .../cases/attribute_methods_test_sqlserver.rb | 4 +- test/cases/base_test_sqlserver.rb | 10 +- test/cases/batches_test_sqlserver.rb | 14 +- .../belongs_to_associations_test_sqlserver.rb | 10 +- test/cases/bind_parameter_test_sqlserver.rb | 10 +- test/cases/calculations_test_sqlserver.rb | 22 +- test/cases/column_test_sqlserver.rb | 110 ++++----- test/cases/connection_test_sqlserver.rb | 76 +++---- test/cases/eager_test_sqlserver.rb | 12 +- .../cases/execute_procedure_test_sqlserver.rb | 14 +- test/cases/finder_test_sqlserver.rb | 14 +- ...ngs_to_many_associations_test_sqlserver.rb | 12 +- test/cases/inheritance_test_sqlserver.rb | 12 +- .../invalid_connection_test_sqlserver.rb | 2 +- test/cases/migration_test_sqlserver.rb | 34 +-- test/cases/offset_and_limit_test_sqlserver.rb | 44 ++-- test/cases/order_test_sqlserver.rb | 102 ++++----- test/cases/persistence_test_sqlserver.rb | 8 +- .../pessimistic_locking_test_sqlserver.rb | 22 +- test/cases/query_cache_test_sqlserver.rb | 12 +- test/cases/relations_test_sqlserver.rb | 2 +- test/cases/resolver_test_sqlserver.rb | 22 +- test/cases/schema_dumper_test_sqlserver.rb | 40 ++-- test/cases/schema_test_sqlserver.rb | 52 ++--- test/cases/scratch_test_sqlserver.rb | 4 +- test/cases/session_test_sqlserver.rb | 6 +- test/cases/showplan_test_sqlserver.rb | 6 +- test/cases/specific_schema_test_sqlserver.rb | 92 ++++---- test/cases/sqlserver_helper.rb | 8 +- test/cases/sqlserver_test_case.rb | 2 +- test/cases/table_name_test_sqlserver.rb | 14 +- test/cases/transaction_test_sqlserver.rb | 24 +- test/cases/unicode_test_sqlserver.rb | 22 +- .../uniqueness_validation_test_sqlserver.rb | 26 +-- .../1_table_will_never_be_created.rb | 6 +- .../sql_server_edge_schema.rb | 6 +- test/profile/connection_profile_case.rb | 12 +- test/profile/finder_profile_case.rb | 8 +- test/profile/gc_profile_case.rb | 26 +-- test/profile/query_plan_complex.rb | 40 ++-- test/profile/query_plan_simple.rb | 9 +- test/schema/sqlserver_specific_schema.rb | 170 +++++++------- 70 files changed, 1031 insertions(+), 1089 deletions(-) create mode 100644 .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..732301fc9 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,20 @@ +AllCops: + Exclude: + - doc/**/* + - test/**/* +LineLength: + Max: 200 +Documentation: + Enabled: false +MethodLength: + Enabled: false + +# TODO: enable after the more important stuff is fixed +LineLength: + Enabled: false +CyclomaticComplexity: + Enabled: false +SignalException: + Enabled: false +MethodName: + Enabled: false diff --git a/Gemfile b/Gemfile index e3c078ac5..b9eda3832 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' if ENV['RAILS_SOURCE'] - gemspec :path => ENV['RAILS_SOURCE'] + gemspec path: ENV['RAILS_SOURCE'] else # Need to get rails source beacause the gem doesn't include tests version = ENV['RAILS_VERSION'] || begin @@ -17,19 +17,19 @@ else !data['prerelease'] && major == a && (minor.nil? || minor == b) end.first['number'] end - gem 'rails', :git => "git://github.com/rails/rails.git", :tag => "v#{version}" + gem 'rails', git: "git://github.com/rails/rails.git", tag: "v#{version}" end if ENV['AREL'] - gem 'arel', :path => ENV['AREL'] + gem 'arel', path: ENV['AREL'] end group :tinytds do if ENV['TINYTDS_SOURCE'] - gem 'tiny_tds', :path => ENV['TINYTDS_SOURCE'] + gem 'tiny_tds', path: ENV['TINYTDS_SOURCE'] else # TODO: [Rails4] Change back... segfault caused by tiny_tds 0.6.1 - gem 'tiny_tds', :git =>"https://github.com/rails-sqlserver/tiny_tds.git" + gem 'tiny_tds', git:"https://github.com/rails-sqlserver/tiny_tds.git" end end @@ -44,8 +44,8 @@ group :development do gem 'minitest-spec-rails' gem 'nokogiri' gem 'rake', '~> 0.9.2' + gem 'rubocop' gem 'ruby-prof' gem 'simplecov' gem 'ruby-graphviz' end - diff --git a/README.md b/README.md index 685bb3194..0a3088f14 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Every class that sub classes ActiveRecord::Base will now have an execute_procedu ```ruby Account.execute_procedure :update_totals, 'admin', nil, true # Or with named parameters. -Account.execute_procedure :update_totals, :named => 'params' +Account.execute_procedure :update_totals, named: 'params' ``` #### Native Data Type Support @@ -53,9 +53,9 @@ Currently the following custom data types have been tested for schema definition For example: ```ruby -create_table :sql_server_custom_types, :force => true do |t| - t.column :ten_code, :char, :limit => 10 - t.column :ten_code_utf8, :nchar, :limit => 10 +create_table :sql_server_custom_types, force: true do |t| + t.column :ten_code, :char, limit: 10 + t.column :ten_code_utf8, :nchar, limit: 10 t.column :title_utf8, :nvarchar t.column :body, :varchar_max # Creates varchar(max) t.column :body_utf8, :ntext @@ -159,7 +159,7 @@ end The 3.2 version of the adapter support ActiveRecord's explain features. In SQL Server, this is called the showplan. By default we use the `SHOWPLAN_ALL` option and format it using a simple table printer. So the following ruby would log the plan table below it. ```ruby -Car.where(:id => 1).explain +Car.where(id: 1).explain ``` ``` diff --git a/Rakefile b/Rakefile index e9fecfe1b..d304633ef 100644 --- a/Rakefile +++ b/Rakefile @@ -6,25 +6,25 @@ AREL_PATH = Gem.loaded_specs['arel'].full_gem_path # Notes for cross compile: # $ gcla ; bundle install ; rake compile ; rake cross compile ; rake cross native gem -def test_libs(mode='dblib') +def test_libs ['lib', 'test', - "#{File.join(AR_PATH,'test')}", - "#{File.join(AREL_PATH,'test')}", + "#{File.join(AR_PATH, 'test')}", + "#{File.join(AREL_PATH, 'test')}" ] end # bundle exec rake test SQLSERVER_ONLY=true -# +# # If you have trouble running single tests (errors about requirements): # http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files - test_setup = ["test/cases/sqlserver_helper.rb"] + test_setup = ['test/cases/sqlserver_helper.rb'] return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] - - sqlserver_cases = Dir.glob("test/cases/**/*_test_sqlserver.rb") - + + sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') + ar_cases = Dir.glob("#{AR_PATH}/test/cases/**/*_test.rb") adapter_cases = Dir.glob("#{AR_PATH}/test/cases/adapters/**/*_test.rb") @@ -39,33 +39,31 @@ def test_files else test_setup + arel_cases + sqlserver_cases + (ar_cases - adapter_cases) end - end -task :test => ['test:dblib'] -task :default => [:test] - +task test: ['test:dblib'] +task default: [:test] namespace :test do - - ['dblib','odbc'].each do |mode| - + + %w(dblib odbc).each do |mode| + Rake::TestTask.new(mode) do |t| - t.libs = test_libs(mode) + t.libs = test_libs t.test_files = test_files t.verbose = true end - + end - + task 'dblib:env' do ENV['ARCONN'] = 'dblib' end - - task 'odbc:env' do + + task 'odbc:env' do ENV['ARCONN'] = 'odbc' end - + end task 'test:dblib' => 'test:dblib:env' @@ -73,23 +71,22 @@ task 'test:odbc' => 'test:odbc:env' namespace :profile do - - ['dblib','odbc'].each do |mode| + %w(dblib odbc).each do |mode| namespace mode.to_sym do - - Dir.glob("test/profile/*_profile_case.rb").sort.each do |test_file| - - profile_case = File.basename(test_file).sub('_profile_case.rb','') - + + Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| + + profile_case = File.basename(test_file).sub('_profile_case.rb', '') + Rake::TestTask.new(profile_case) do |t| - t.libs = test_libs(mode) + t.libs = test_libs t.test_files = [test_file] t.verbose = true end - + end - + end end - + end diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 20d6bd82a..203eabe0f 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,21 +1,20 @@ -$:.push File.expand_path("../lib", __FILE__) +$LOAD_PATH.push File.expand_path('../lib', __FILE__) Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY - s.name = "activerecord-sqlserver-adapter" - s.version = File.read(File.expand_path("../VERSION",__FILE__)).strip - s.summary = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." - s.description = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." - - s.authors = ['Ken Collins', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] - s.email = "ken@metaskills.net" - s.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter" - - s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*' ] + s.name = 'activerecord-sqlserver-adapter' + s.version = File.read(File.expand_path('../VERSION', __FILE__)).strip + s.summary = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' + s.description = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' + + s.authors = ['Ken Collins', 'Anna Carey', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] + s.email = 'ken@metaskills.net' + s.homepage = 'http://github.com/rails-sqlserver/activerecord-sqlserver-adapter' + + s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*'] s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - + s.add_dependency('activerecord', '~> 4.0.0') s.add_dependency('arel', '~> 4.0.1') end - diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index a264105f1..a20530a70 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -3,9 +3,8 @@ module ConnectionAdapters module Sqlserver module CoreExt module ActiveRecord - extend ActiveSupport::Concern - + included do class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns self.coerced_sqlserver_date_columns = Set.new @@ -13,10 +12,9 @@ module ActiveRecord end module ClassMethods - def execute_procedure(proc_name, *variables) if connection.respond_to?(:execute_procedure) - connection.execute_procedure(proc_name,*variables) + connection.execute_procedure(proc_name, *variables) else [] end @@ -29,14 +27,11 @@ def coerce_sqlserver_date(*attributes) def coerce_sqlserver_time(*attributes) self.coerced_sqlserver_time_columns += attributes.map(&:to_s) end - end - end end end end end - ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord 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 3d00e10ed..974a62e4a 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -3,19 +3,18 @@ module ConnectionAdapters module Sqlserver module CoreExt module Explain - - SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql " + SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' SQLSERVER_PARAM_MATCHER = /@\d+ =/ - + def exec_explain(queries) unprepared_queries = queries.map { |sql, bind| [unprepare_sqlserver_statement(sql), bind] } super(unprepared_queries) end - + private - - # This is somewhat hacky, but it should reliably reformat our prepared sql statment - # which uses sp_executesql to just the first argument, then unquote it. Likewise our + + # This is somewhat hacky, but it should reliably reformat our prepared sql statment + # which uses sp_executesql to just the first argument, then unquote it. Likewise our # do_exec_query method should substitude the @n args withe the quoted values. def unprepare_sqlserver_statement(sql) if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) @@ -29,8 +28,6 @@ def unprepare_sqlserver_statement(sql) sql end end - - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb index c81cd8f6d..f0c940bc8 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb @@ -3,36 +3,26 @@ module ConnectionAdapters module Sqlserver module CoreExt module ODBC - module Statement - def finished? - begin - connected? - false - rescue ::ODBC::Error - true - end + connected? + false + rescue ::ODBC::Error + true end - end module Database - def run_block(*args) yield sth = run(*args) sth.drop end - end - end end end end end - ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database - diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb index a84bf081a..0cecfdc6a 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb @@ -3,13 +3,11 @@ module ConnectionAdapters module Sqlserver module CoreExt module Relation - private - + def tables_in_string(string) super - ['__rnt'] end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 3a7f33858..ac6212901 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -2,7 +2,6 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module DatabaseLimits - def table_alias_length 128 end @@ -32,17 +31,16 @@ def columns_per_multicolumn_index end def in_clause_length - 65536 + 65_536 end def sql_query_length - 65536 * 4096 + 65_536 * 4_096 end def joins_per_query 256 end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 25516f010..43d278e50 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -4,26 +4,26 @@ module Sqlserver module DatabaseStatements def select_rows(sql, name = nil, binds = []) - raw_select sql, name, binds, :fetch => :rows + raw_select sql, name, binds, fetch: :rows end def execute(sql, name = nil) if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name) { do_execute(sql,name) } + with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } else - do_execute(sql,name) + do_execute(sql, name) end end - - # TODO I bet there's a better way than a regex to take care of this + + # TODO: I bet there's a better way than a regex to take care of this def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - # We can't update Identiy columns in sqlserver. So, strip out the id from the update. - if sql =~ /UPDATE/ - # take off a comma before or after. This could probably be done better + # We can't update Identiy columns in sqlserver. So, strip out the id from the update. + if sql =~ /UPDATE/ + # take off a comma before or after. This could probably be done better if sql =~ /, \[id\] = @?[0-9]*/ - sql.gsub! /, \[id\] = @?[0-9]*/, '' + sql.gsub!(/, \[id\] = @?[0-9]*/, '') elsif sql =~ /\s\[id\] = @?[0-9]*,/ - sql.gsub! /\s\[id\] = @?[0-9]*,/, '' + sql.gsub!(/\s\[id\] = @?[0-9]*,/, '') end end @@ -33,36 +33,36 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) do_exec_query(sql, name, binds) end end - - #The abstract adapter ignores the last two parameters also - def exec_insert(sql, name, binds, pk = nil, sequence_name = nil) - exec_query sql, name, binds, :insert => true + + # The abstract adapter ignores the last two parameters also + def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil) + exec_query sql, name, binds, insert: true end - + def exec_delete(sql, name, binds) - sql << "; SELECT @@ROWCOUNT AS AffectedRows" + sql << '; SELECT @@ROWCOUNT AS AffectedRows' super.rows.first.first end def exec_update(sql, name, binds) - sql << "; SELECT @@ROWCOUNT AS AffectedRows" + sql << '; SELECT @@ROWCOUNT AS AffectedRows' super.rows.first.first end - + def supports_statement_cache? true end def begin_db_transaction - do_execute "BEGIN TRANSACTION" + do_execute 'BEGIN TRANSACTION' end def commit_db_transaction - disable_auto_reconnect { do_execute "COMMIT TRANSACTION" } + disable_auto_reconnect { do_execute 'COMMIT TRANSACTION' } end def rollback_db_transaction - do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" + do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end def create_savepoint @@ -76,23 +76,23 @@ def rollback_to_savepoint disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" } end - def add_limit_offset!(sql, options) + def add_limit_offset!(_sql, _options) raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.' end def empty_insert_statement_value - "DEFAULT VALUES" + 'DEFAULT VALUES' end def case_sensitive_modifier(node) node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node end - + # === SQLServer Specific ======================================== # - + def execute_procedure(proc_name, *variables) vars = if variables.any? && variables.first.is_a?(Hash) - variables.first.map { |k,v| "@#{k} = #{quote(v)}" } + variables.first.map { |k, v| "@#{k} = #{quote(v)}" } else variables.map { |v| quote(v) } end.join(', ') @@ -102,51 +102,49 @@ def execute_procedure(proc_name, *variables) case @connection_options[:mode] when :dblib result = @connection.execute(sql) - result.each(:as => :hash, :cache_rows => true) do |row| + result.each(as: :hash, cache_rows: true) do |row| r = row.with_indifferent_access yield(r) if block_given? end - result.each.map{ |row| row.is_a?(Hash) ? row.with_indifferent_access : row } + result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row } when :odbc results = [] raw_connection_run(sql) do |handle| - get_rows = lambda { - rows = handle_to_names_and_values handle, :fetch => :all - rows.each_with_index { |r,i| rows[i] = r.with_indifferent_access } + get_rows = lambda do + rows = handle_to_names_and_values handle, fetch: :all + rows.each_with_index { |r, i| rows[i] = r.with_indifferent_access } results << rows - } - get_rows.call - while handle_more_results?(handle) - get_rows.call end + get_rows.call + get_rows.call while handle_more_results?(handle) end results.many? ? results : results.first end end end - - def use_database(database=nil) + + def use_database(database = nil) return if sqlserver_azure? database ||= @connection_options[:database] do_execute "USE #{quote_database_name(database)}" unless database.blank? end - + def user_options return {} if sqlserver_azure? - select_rows("dbcc useroptions",'SCHEMA').inject(HashWithIndifferentAccess.new) do |values,row| + select_rows('dbcc useroptions', 'SCHEMA').reduce(HashWithIndifferentAccess.new) do |values, row| if row.instance_of? Hash - set_option = row.values[0].gsub(/\s+/,'_') - user_value = row.values[1] - elsif row.instance_of? Array - set_option = row[0].gsub(/\s+/,'_') - user_value = row[1] - end + set_option = row.values[0].gsub(/\s+/, '_') + user_value = row.values[1] + elsif row.instance_of? Array + set_option = row[0].gsub(/\s+/, '_') + user_value = row[1] + end values[set_option] = user_value values end end - - # TODO Rails 4 now supports isolation levels + + # TODO: Rails 4 now supports isolation levels def user_options_dateformat if sqlserver_azure? select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA' @@ -154,24 +152,24 @@ def user_options_dateformat user_options['dateformat'] end end - + def user_options_isolation_level if sqlserver_azure? - sql = %|SELECT CASE [transaction_isolation_level] + sql = %(SELECT CASE [transaction_isolation_level] WHEN 0 THEN NULL - WHEN 1 THEN 'READ UNCOMITTED' - WHEN 2 THEN 'READ COMITTED' - WHEN 3 THEN 'REPEATABLE READ' - WHEN 4 THEN 'SERIALIZABLE' - WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level] - FROM [sys].[dm_exec_sessions] - WHERE [session_id] = @@SPID|.squish + WHEN 1 THEN 'READ UNCOMITTED' + WHEN 2 THEN 'READ COMITTED' + WHEN 3 THEN 'REPEATABLE READ' + WHEN 4 THEN 'SERIALIZABLE' + WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level] + FROM [sys].[dm_exec_sessions] + WHERE [session_id] = @@SPID).squish select_value sql, 'SCHEMA' else user_options['isolation_level'] end end - + def user_options_language if sqlserver_azure? select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA' @@ -181,24 +179,26 @@ def user_options_language end def run_with_isolation_level(isolation_level) - raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." if !valid_isolation_levels.include?(isolation_level.upcase) - initial_isolation_level = user_options_isolation_level || "READ COMMITTED" + unless valid_isolation_levels.include?(isolation_level.upcase) + raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." + end + initial_isolation_level = user_options_isolation_level || 'READ COMMITTED' do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}" begin - yield + yield ensure do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}" end if block_given? end - + def newid_function - select_value "SELECT NEWID()" + select_value 'SELECT NEWID()' end - + def newsequentialid_function - select_value "SELECT NEWSEQUENTIALID()" + select_value 'SELECT NEWSEQUENTIALID()' end - + def activity_stats select_all %| SELECT @@ -243,16 +243,16 @@ def activity_stats WHERE db_name(r.database_id) = '#{current_database}' ORDER BY s.session_id| end - + # === SQLServer Specific (Rake/Test Helpers) ==================== # - + def recreate_database remove_database_connections_and_rollback do do_execute "EXEC sp_MSforeachtable 'DROP TABLE ?'" end end - def recreate_database!(database=nil) + def recreate_database!(database = nil) current_db = current_database database ||= current_db this_db = database.to_s == current_db @@ -282,7 +282,7 @@ def drop_database(database) end end - def create_database(database, collation=@connection_options[:collation]) + def create_database(database, collation = @connection_options[:collation]) if collation do_execute "CREATE DATABASE #{quote_database_name(database)} COLLATE #{collation}" else @@ -293,33 +293,32 @@ def create_database(database, collation=@connection_options[:collation]) def current_database select_value 'SELECT DB_NAME()' end - + def charset select_value "SELECT SERVERPROPERTY('SqlCharSetName')" end - - + protected - + def select(sql, name = nil, binds = []) exec_query(sql, name, binds) end - + def sql_for_insert(sql, pk, id_value, sequence_name, binds) sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"# unless binds.empty? super end def last_inserted_id(result) - super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident") + super || select_value('SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident') end - + # === SQLServer Specific ======================================== # - + def valid_isolation_levels - ["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"] + ['READ COMMITTED', 'READ UNCOMMITTED', 'REPEATABLE READ', 'SERIALIZABLE', 'SNAPSHOT'] end - + # === SQLServer Specific (Executing) ============================ # def do_execute(sql, name = 'SQL') @@ -327,12 +326,12 @@ def do_execute(sql, name = 'SQL') with_sqlserver_error_handling { raw_connection_do(sql) } end end - + def do_exec_query(sql, name, binds) explaining = name == 'EXPLAIN' names_and_types = [] params = [] - binds.each_with_index do |(column,value),index| + binds.each_with_index do |(column, value), index| ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column) next if ar_column && column.sql_type == 'timestamp' v = value @@ -345,9 +344,9 @@ def do_exec_query(sql, name, binds) v = value.to_i "@#{index} int" else - raise "Unknown bind columns. We can account for this." + raise 'Unknown bind columns. We can account for this.' end - quoted_value = ar_column ? quote(v,column) : quote(v,nil) + quoted_value = ar_column ? quote(v, column) : quote(v, nil) params << (explaining ? quoted_value : "@#{index} = #{quoted_value}") end if explaining @@ -359,9 +358,9 @@ def do_exec_query(sql, name, binds) sql = "EXEC sp_executesql #{quote(sql)}" sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty? end - raw_select sql, name, binds, :ar_result => true + raw_select sql, name, binds, ar_result: true end - + def raw_connection_do(sql) case @connection_options[:mode] when :dblib @@ -372,22 +371,20 @@ def raw_connection_do(sql) ensure @update_sql = false end - + # === SQLServer Specific (Selecting) ============================ # - def raw_select(sql, name='SQL', binds=[], options={}) - log(sql,name,binds) { _raw_select(sql, options) } + def raw_select(sql, name = 'SQL', binds = [], options = {}) + log(sql, name, binds) { _raw_select(sql, options) } end - - def _raw_select(sql, options={}) - begin - handle = raw_connection_run(sql) - handle_to_names_and_values(handle, options) - ensure - finish_statement_handle(handle) - end + + def _raw_select(sql, options = {}) + handle = raw_connection_run(sql) + handle_to_names_and_values(handle, options) + ensure + finish_statement_handle(handle) end - + def raw_connection_run(sql) with_sqlserver_error_handling do case @connection_options[:mode] @@ -398,7 +395,7 @@ def raw_connection_run(sql) end end end - + def handle_more_results?(handle) case @connection_options[:mode] when :dblib @@ -406,8 +403,8 @@ def handle_more_results?(handle) handle.more_results end end - - def handle_to_names_and_values(handle, options={}) + + def handle_to_names_and_values(handle, options = {}) case @connection_options[:mode] when :dblib handle_to_names_and_values_dblib(handle, options) @@ -415,8 +412,8 @@ def handle_to_names_and_values(handle, options={}) handle_to_names_and_values_odbc(handle, options) end end - - def handle_to_names_and_values_dblib(handle, options={}) + + def handle_to_names_and_values_dblib(handle, options = {}) query_options = {}.tap do |qo| qo[:timezone] = ActiveRecord::Base.default_timezone || :utc qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash @@ -425,8 +422,8 @@ def handle_to_names_and_values_dblib(handle, options={}) columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results end - - def handle_to_names_and_values_odbc(handle, options={}) + + def handle_to_names_and_values_odbc(handle, options = {}) @connection.use_utc = ActiveRecord::Base.default_timezone == :utc if options[:ar_result] columns = lowercase_schema_reflection ? handle.columns(true).map { |c| c.name.downcase } : handle.columns(true).map { |c| c.name } @@ -441,7 +438,7 @@ def handle_to_names_and_values_odbc(handle, options={}) end end end - + def finish_statement_handle(handle) case @connection_options[:mode] when :dblib @@ -451,7 +448,6 @@ def finish_statement_handle(handle) end handle end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 9de1f8e19..083ccfa11 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -1,35 +1,31 @@ module ActiveRecord - class LostConnection < WrappedDatabaseException end - + class DeadlockVictim < WrappedDatabaseException end - + module ConnectionAdapters module Sqlserver module Errors - LOST_CONNECTION_EXCEPTIONS = { - :dblib => ['TinyTds::Error'], - :odbc => ['ODBC::Error'] + dblib: ['TinyTds::Error'], + odbc: ['ODBC::Error'] }.freeze - + LOST_CONNECTION_MESSAGES = { - :dblib => [/closed connection/, /dead or not enabled/, /server failed/i], - :odbc => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i] + dblib: [/closed connection/, /dead or not enabled/, /server failed/i], + odbc: [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i] }.freeze - - + def lost_connection_exceptions exceptions = LOST_CONNECTION_EXCEPTIONS[@connection_options[:mode]] - @lost_connection_exceptions ||= exceptions ? exceptions.map{ |e| e.constantize rescue nil }.compact : [] + @lost_connection_exceptions ||= exceptions ? exceptions.map { |e| e.constantize rescue nil }.compact : [] end - + def lost_connection_messages LOST_CONNECTION_MESSAGES[@connection_options[:mode]] end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 28820b139..1aa1bd12a 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -2,7 +2,6 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module Quoting - QUOTED_TRUE, QUOTED_FALSE = '1', '0' QUOTED_STRING_PREFIX = 'N' @@ -92,7 +91,7 @@ def quoted_full_iso8601(value) def quoted_date(value) if value.acts_like?(:time) && value.respond_to?(:usec) - "#{super}.#{sprintf("%03d",value.usec/1000)}" + "#{super}.#{sprintf('%03d', value.usec / 1000)}" elsif value.acts_like?(:date) value.to_s(:_sqlserver_dateformat) else @@ -106,7 +105,6 @@ def quoted_value_acts_like_time_filter(value) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index ec9ece0e3..9accc616d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -2,9 +2,8 @@ module ActiveRecord module ConnectionAdapters module Sqlserver class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache - attr_reader :view_information - + def initialize(conn) super @table_names = nil @@ -12,16 +11,16 @@ def initialize(conn) @view_information = {} @quoted_names = {} end - + # Superclass Overrides - + def table_exists?(table_name) return false if table_name.blank? key = table_name_key(table_name) return @tables[key] if @tables.key? key @tables[key] = connection.table_exists?(table_name) end - + def clear! super @table_names = nil @@ -29,7 +28,7 @@ def clear! @view_information.clear @quoted_names.clear end - + def clear_table_cache!(table_name) key = table_name_key(table_name) super(key) @@ -45,27 +44,28 @@ def clear_table_cache!(table_name) end @view_information.delete key end - + # SQL Server Specific - + def table_names @table_names ||= connection.tables end - + def view_names @view_names ||= connection.views end - + def view_exists?(table_name) table_exists?(table_name) end - + def view_information(table_name) key = table_name_key(table_name) return @view_information[key] if @view_information.key? key @view_information[key] = connection.send(:view_information, table_name) end + def quote_name(name, split_on_dots = true) return @quoted_names[name] if @quoted_names.key? name @@ -81,13 +81,11 @@ def quote_name(name, split_on_dots = true) def quote_name_part(part) part =~ /^\[.*\]$/ ? part : "[#{part.to_s.gsub(']', ']]')}]" end - + def table_name_key(table_name) Utils.unqualify_table_name(table_name) end - end end end end - diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6b5eb394f..c3216ebd1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -2,7 +2,6 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module SchemaStatements - def native_database_types @native_database_types ||= initialize_native_database_types.freeze end @@ -18,8 +17,8 @@ def table_exists?(table_name) end def indexes(table_name, name = nil) - data = select("EXEC sp_helpindex #{quote(table_name)}",name) rescue [] - data.inject([]) do |indexes,index| + data = select("EXEC sp_helpindex #{quote(table_name)}", name) rescue [] + data.reduce([]) do |indexes, index| index = index.with_indifferent_access if index[:index_description] =~ /primary key/ indexes @@ -36,10 +35,10 @@ def indexes(table_name, name = nil) end end - def columns(table_name, name = nil) + def columns(table_name, _name = nil) return [] if table_name.blank? - column_definitions(table_name).collect do |ci| - sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year) + column_definitions(table_name).map do |ci| + sqlserver_options = ci.except(:name, :default_value, :type, :null).merge(database_year: database_year) SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options end end @@ -48,12 +47,12 @@ def columns(table_name, name = nil) # requires that the ORDER BY include the distinct column. # this method is idental to the postgres method def columns_for_distinct(columns, orders) #:nodoc: - order_columns = orders.map{ |s| - # Convert Arel node to string - s = s.to_sql unless s.is_a?(String) - # Remove any ASC/DESC modifiers - s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') - }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + order_columns = orders.map do |s| + # Convert Arel node to string + s = s.to_sql unless s.is_a?(String) + # Remove any ASC/DESC modifiers + s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') + end.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } [super, *order_columns].join(', ') end @@ -63,8 +62,8 @@ def rename_table(table_name, new_name) rename_table_indexes(table_name, new_name) end - def remove_column(table_name, column_name, type = nil) - raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if (column_name.is_a? Array) + def remove_column(table_name, column_name, _type = nil) + raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) @@ -74,23 +73,23 @@ def remove_column(table_name, column_name, type = nil) def change_column(table_name, column_name, type, options = {}) sql_commands = [] indexes = [] - column_object = schema_cache.columns(table_name).detect { |c| c.name.to_s == column_name.to_s } + column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } if options_include_default?(options) || (column_object && column_object.type != type.to_sym) - remove_default_constraint(table_name,column_name) - indexes = indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) } + remove_default_constraint(table_name, column_name) + indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } remove_indexes(table_name, column_name) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(options[:default])} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" - sql_commands[-1] << " NOT NULL" if !options[:null].nil? && options[:null] == false + sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false if options_include_default?(options) - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" end - #Add any removed indexes back + # 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.collect {|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| do_execute(c) } end @@ -117,15 +116,15 @@ def remove_index!(table_name, index_name) end def type_to_sql(type, limit = nil, precision = nil, scale = nil) - type_limitable = ['string','integer','float','char','nchar','varchar','nvarchar'].include?(type.to_s) + type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable case type.to_s when 'integer' case limit - when 1..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.") + when 1..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 else super @@ -138,7 +137,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") end sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}" - sql << " NOT NULL" if !allow_null.nil? && allow_null == false + sql << ' NOT NULL' if !allow_null.nil? && allow_null == false do_execute sql end @@ -148,33 +147,32 @@ def views tables('VIEW') end - protected # === SQLServer Specific ======================================== # def initialize_native_database_types { - :primary_key => "int NOT NULL IDENTITY(1,1) PRIMARY KEY", - :string => { :name => native_string_database_type, :limit => 255 }, - :text => { :name => native_text_database_type }, - :integer => { :name => "int", :limit => 4 }, - :float => { :name => "float", :limit => 8 }, - :decimal => { :name => "decimal" }, - :datetime => { :name => "datetime" }, - :timestamp => { :name => "datetime" }, - :time => { :name => native_time_database_type }, - :date => { :name => native_date_database_type }, - :binary => { :name => native_binary_database_type }, - :boolean => { :name => "bit"}, + primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', + string: { name: native_string_database_type, limit: 255 }, + text: { name: native_text_database_type }, + integer: { name: 'int', limit: 4 }, + float: { name: 'float', limit: 8 }, + decimal: { name: 'decimal' }, + datetime: { name: 'datetime' }, + timestamp: { name: 'datetime' }, + time: { name: native_time_database_type }, + date: { name: native_date_database_type }, + binary: { name: native_binary_database_type }, + boolean: { name: 'bit' }, # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them. - :char => { :name => 'char' }, - :varchar_max => { :name => 'varchar(max)' }, - :nchar => { :name => "nchar" }, - :nvarchar => { :name => "nvarchar", :limit => 255 }, - :nvarchar_max => { :name => "nvarchar(max)" }, - :ntext => { :name => "ntext" }, - :ss_timestamp => { :name => 'timestamp' } + char: { name: 'char' }, + varchar_max: { name: 'varchar(max)' }, + nchar: { name: 'nchar' }, + nvarchar: { name: 'nvarchar', limit: 255 }, + nvarchar_max: { name: 'nvarchar(max)' }, + ntext: { name: 'ntext' }, + ss_timestamp: { name: 'timestamp' } } end @@ -226,29 +224,29 @@ def column_definitions(table_name) ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME WHERE columns.TABLE_NAME = @0 - AND columns.TABLE_SCHEMA = #{table_schema.blank? ? "schema_name()" : "@1"} + AND columns.TABLE_SCHEMA = #{table_schema.blank? ? 'schema_name()' : '@1'} ORDER BY columns.ordinal_position - }.gsub(/[ \t\r\n]+/,' ') + }.gsub(/[ \t\r\n]+/, ' ') binds = [['table_name', table_name]] - binds << ['table_schema',table_schema] unless table_schema.blank? + binds << ['table_schema', table_schema] unless table_schema.blank? results = do_exec_query(sql, 'SCHEMA', binds) - results.collect do |ci| + results.map do |ci| ci = ci.symbolize_keys ci[:type] = case ci[:type] - when /^bit|image|text|ntext|datetime$/ - ci[:type] - when /^numeric|decimal$/i - "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})" - when /^float|real$/i - "#{ci[:type]}(#{ci[:numeric_precision]})" - when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/ - ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})" - else - ci[:type] - end + when /^bit|image|text|ntext|datetime$/ + ci[:type] + when /^numeric|decimal$/i + "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})" + when /^float|real$/i + "#{ci[:type]}(#{ci[:numeric_precision]})" + when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/ + ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})" + else + ci[:type] + end if ci[:default_value].nil? && schema_cache.view_names.include?(table_name) real_table_name = table_name_or_views_table_name(table_name) - real_column_name = views_real_column_name(table_name,ci[:name]) + real_column_name = views_real_column_name(table_name, ci[:name]) col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'" ci[:default_value] = select_value col_default_sql, 'SCHEMA' end @@ -256,13 +254,14 @@ def column_definitions(table_name) when nil, '(null)', '(NULL)' nil when /\A\((\w+\(\))\)\Z/ - ci[:default_function] = $1 + ci[:default_function] = Regexp.last_match[1] nil else match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m) match_data ? match_data[1] : nil end - ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable) + ci[:null] = ci[:is_nullable].to_i == 1 + ci.delete(:is_nullable) ci[:is_primary] = ci[:is_primary].to_i == 1 ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class) ci @@ -286,8 +285,8 @@ def remove_default_constraint(table_name, column_name) end def remove_indexes(table_name, column_name) - indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }.each do |index| - remove_index(table_name, {:name => index.name}) + indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }.each do |index| + remove_index(table_name, name: index.name) end end @@ -295,9 +294,9 @@ def remove_indexes(table_name, column_name) def get_table_name(sql) if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i - $3 || $4 + Regexp.last_match[3] || Regexp.last_match[4] elsif sql =~ /FROM\s+([^\(\s]+)\s*/i - $1 + Regexp.last_match[1] else nil end @@ -308,7 +307,7 @@ def default_constraint_name(table_name, column_name) end def detect_column_for!(table_name, column_name) - unless column = schema_cache.columns(table_name).detect { |c| c.name == column_name.to_s } + unless column = schema_cache.columns(table_name).find { |c| c.name == column_name.to_s } raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" end column @@ -347,7 +346,7 @@ def table_name_or_views_table_name(table_name) schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name end - def views_real_column_name(table_name,column_name) + def views_real_column_name(table_name, column_name) view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name @@ -356,7 +355,6 @@ def views_real_column_name(table_name,column_name) # === SQLServer Specific (Identity Inserts) ===================== # def query_requires_identity_insert?(sql) - if insert_sql?(sql) table_name = get_table_name(sql) id_column = identity_column(table_name) @@ -381,14 +379,13 @@ def with_identity_insert_enabled(table_name) def set_identity_insert(table_name, enable = true) sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" do_execute sql, 'SCHEMA' - rescue Exception => e + rescue Exception raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end def identity_column(table_name) - schema_cache.columns(table_name).detect(&:is_identity?) + schema_cache.columns(table_name).find(&:is_identity?) end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index adba2a0a0..af2e32a89 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -5,54 +5,52 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module Showplan - OPTION_ALL = 'SHOWPLAN_ALL' OPTION_TEXT = 'SHOWPLAN_TEXT' OPTION_XML = 'SHOWPLAN_XML' OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML] - + def explain(arel, binds = []) sql = to_sql(arel) result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) } printer = showplan_printer.new(result) printer.pp end - - + protected - + def with_showplan_on set_showplan_option(true) yield ensure set_showplan_option(false) end - + def set_showplan_option(enable = true) sql = "SET #{option} #{enable ? 'ON' : 'OFF'}" raw_connection_do(sql) - rescue Exception => e + rescue Exception raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" end - + def option (SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt| raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt) end end - + def showplan_all? option == OPTION_ALL end - + def showplan_text? option == OPTION_TEXT end - + def showplan_xml? option == OPTION_XML end - + def showplan_printer case option when OPTION_XML then PrinterXml @@ -60,7 +58,6 @@ def showplan_printer else PrinterTable end end - end end end 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 f6b28d4f8..9af28d23a 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb @@ -3,17 +3,16 @@ module ConnectionAdapters module Sqlserver module Showplan class PrinterTable - cattr_accessor :max_column_width, :cell_padding self.max_column_width = 50 self.cell_padding = 1 - + attr_reader :result - + def initialize(result) @result = result end - + def pp @widths = compute_column_widths @separator = build_separator @@ -27,7 +26,7 @@ def pp pp << @separator pp.join("\n") + "\n" end - + private def compute_column_widths @@ -40,11 +39,11 @@ def compute_column_widths end end end - + def build_separator - '+' + @widths.map {|w| '-' * (w + (cell_padding*2))}.join('+') + '+' + '+' + @widths.map { |w| '-' * (w + (cell_padding * 2)) }.join('+') + '+' end - + def build_cells(items) cells = [] items.each_with_index do |item, i| @@ -52,7 +51,7 @@ def build_cells(items) end "| #{cells.join(' | ')} |" end - + def cast_item(item) case item when NilClass then 'NULL' @@ -60,9 +59,7 @@ def cast_item(item) else item.to_s.truncate(max_column_width) end end - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb index e90f0ba73..dbedd7448 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb @@ -3,22 +3,19 @@ module ConnectionAdapters module Sqlserver module Showplan class PrinterXml - def initialize(result) @result = result end - + def pp xml = @result.rows.first.first if defined?(Nokogiri) - Nokogiri::XML(xml).to_xml :indent => 2, :encoding => 'UTF-8' + Nokogiri::XML(xml).to_xml indent: 2, encoding: 'UTF-8' else xml end end - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index bab688063..1022b870a 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -2,31 +2,25 @@ module ActiveRecord module ConnectionAdapters module Sqlserver class Utils - class << self - def unquote_string(string) string.to_s.gsub(/\'\'/, "'") end - + def unqualify_table_name(table_name) - table_name.to_s.split('.').last.tr('[]','') + table_name.to_s.split('.').last.tr('[]', '') end def unqualify_table_schema(table_name) - table_name.to_s.split('.')[-2].gsub(/[\[\]]/,'') rescue nil + table_name.to_s.split('.')[-2].gsub(/[\[\]]/, '') rescue nil end def unqualify_db_name(table_name) table_names = table_name.to_s.split('.') - table_names.length == 3 ? table_names.first.tr('[]','') : nil + table_names.length == 3 ? table_names.first.tr('[]', '') : nil end - end - end end end end - - diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a2df134af..4e4edaa8f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -20,42 +20,35 @@ require 'active_record/connection_adapters/sqlserver/utils' module ActiveRecord - class Base - def self.sqlserver_connection(config) #:nodoc: config = config.symbolize_keys - config.reverse_merge! :mode => :dblib + config.reverse_merge! mode: :dblib mode = config[:mode].to_s.downcase.underscore.to_sym case mode when :dblib require 'tiny_tds' when :odbc - raise ArgumentError, 'Missing :dsn configuration.' unless config.has_key?(:dsn) + raise ArgumentError, 'Missing :dsn configuration.' unless config.key?(:dsn) require 'odbc' require 'active_record/connection_adapters/sqlserver/core_ext/odbc' else raise ArgumentError, "Unknown connection mode in #{config.inspect}." end - ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(:mode=>mode)) + ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) end - protected - - def self.did_retry_sqlserver_connection(connection,count) + def self.did_retry_sqlserver_connection(connection, count) logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}." end def self.did_lose_sqlserver_connection(connection) logger.info "CONNECTION LOST: #{connection.class.name}" end - end module ConnectionAdapters - class SQLServerColumn < Column - def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {}) @sqlserver_options = sqlserver_options.symbolize_keys super(name, default, sql_type, null) @@ -63,18 +56,16 @@ def initialize(name, default, sql_type = nil, null = true, sqlserver_options = { end class << self - def string_to_binary(value) "0x#{value.unpack("H*")[0]}" end def binary_to_string(value) if value.encoding != Encoding::ASCII_8BIT - value = value.force_encoding(Encoding::ASCII_8BIT) + value = value.force_encoding(Encoding::ASCII_8BIT) end value end - end def is_identity? @@ -86,20 +77,20 @@ def is_primary? end def is_utf8? - !!(@sql_type =~ /nvarchar|ntext|nchar/i) + @sql_type =~ /nvarchar|ntext|nchar/i end def is_integer? - !!(@sql_type =~ /int/i) + @sql_type =~ /int/i end def is_real? - !!(@sql_type =~ /real/i) + @sql_type =~ /real/i end def sql_type_for_statement if is_integer? || is_real? - sql_type.sub(/\((\d+)?\)/,'') + sql_type.sub(/\((\d+)?\)/, '') else sql_type end @@ -126,7 +117,6 @@ def database_year @sqlserver_options[:database_year] end - private def extract_limit(sql_type) @@ -146,15 +136,15 @@ def extract_limit(sql_type) def simplified_type(field_type) case field_type - when /real/i then :float - when /money/i then :decimal - when /image/i then :binary - when /bit/i then :boolean - when /uniqueidentifier/i then :string - when /datetime/i then simplified_datetime - when /varchar\(max\)/ then :text - when /timestamp/ then :binary - else super + when /real/i then :float + when /money/i then :decimal + when /image/i then :binary + when /bit/i then :boolean + when /uniqueidentifier/i then :string + when /datetime/i then simplified_datetime + when /varchar\(max\)/ then :text + when /timestamp/ then :binary + else super end end @@ -169,11 +159,9 @@ def simplified_datetime :datetime end end - - end #class SQLServerColumn + end # class SQLServerColumn class SQLServerAdapter < AbstractAdapter - include Sqlserver::Quoting include Sqlserver::DatabaseStatements include Sqlserver::Showplan @@ -181,10 +169,10 @@ class SQLServerAdapter < AbstractAdapter include Sqlserver::DatabaseLimits include Sqlserver::Errors - VERSION = File.read(File.expand_path("../../../../VERSION",__FILE__)).strip + VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip ADAPTER_NAME = 'SQLServer'.freeze DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - SUPPORTED_VERSIONS = [2005,2008,2010,2011,2012] + SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition @@ -215,7 +203,7 @@ def initialize(connection, logger, pool, config) year = 2012 else year = DATABASE_VERSION_REGEXP.match(@database_version)[1] - year == "Denali" ? 2011 : year.to_i + year == 'Denali' ? 2011 : year.to_i end rescue 0 @@ -225,7 +213,7 @@ def initialize(connection, logger, pool, config) @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' initialize_dateformatter use_database - unless (@sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year)) + unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." end end @@ -282,7 +270,7 @@ def active? when :dblib return @connection.active? end - raw_connection_do("SELECT 1") + raw_connection_do('SELECT 1') true rescue *lost_connection_exceptions false @@ -307,18 +295,18 @@ def disconnect! end def reset! - remove_database_connections_and_rollback { } + remove_database_connections_and_rollback {} end # === Abstract Adapter (Misc Support) =========================== # def pk_and_sequence_for(table_name) idcol = identity_column(table_name) - idcol ? [idcol.name,nil] : nil + idcol ? [idcol.name, nil] : nil end def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache.columns(table_name).detect(&:is_primary?).try(:name) + identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) end # === SQLServer Specific (DB Reflection) ======================== # @@ -394,13 +382,13 @@ def cs_equality_operator def translate_exception(e, message) case message when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i - RecordNotUnique.new(message,e) + RecordNotUnique.new(message, e) when /conflicted with the foreign key constraint/i - InvalidForeignKey.new(message,e) + InvalidForeignKey.new(message, e) when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message,e) + DeadlockVictim.new(message, e) when *lost_connection_messages - LostConnection.new(message,e) + LostConnection.new(message, e) else super end @@ -414,43 +402,43 @@ def connect when :dblib appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil - timeout = config[:timeout].present? ? config[:timeout].to_i/1000 : nil + timeout = config[:timeout].present? ? config[:timeout].to_i / 1000 : nil encoding = config[:encoding].present? ? config[:encoding] : nil - TinyTds::Client.new({ - :dataserver => config[:dataserver], - :host => config[:host], - :port => config[:port], - :username => config[:username], - :password => config[:password], - :database => config[:database], - :tds_version => config[:tds_version], - :appname => appname, - :login_timeout => login_timeout, - :timeout => timeout, - :encoding => encoding, - :azure => config[:azure] - }).tap do |client| + TinyTds::Client.new( + dataserver: config[:dataserver], + host: config[:host], + port: config[:port], + username: config[:username], + password: config[:password], + database: config[:database], + tds_version: config[:tds_version], + appname: appname, + login_timeout: login_timeout, + timeout: timeout, + encoding: encoding, + azure: config[:azure] + ).tap do |client| if config[:azure] - client.execute("SET ANSI_NULLS ON").do - client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do - client.execute("SET ANSI_NULL_DFLT_ON ON").do - client.execute("SET IMPLICIT_TRANSACTIONS OFF").do - client.execute("SET ANSI_PADDING ON").do - client.execute("SET QUOTED_IDENTIFIER ON") - client.execute("SET ANSI_WARNINGS ON").do + client.execute('SET ANSI_NULLS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET ANSI_NULL_DFLT_ON ON').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + client.execute('SET ANSI_PADDING ON').do + client.execute('SET QUOTED_IDENTIFIER ON') + client.execute('SET ANSI_WARNINGS ON').do else - client.execute("SET ANSI_DEFAULTS ON").do - client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do - client.execute("SET IMPLICIT_TRANSACTIONS OFF").do + client.execute('SET ANSI_DEFAULTS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do end - client.execute("SET TEXTSIZE 2147483647").do - client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do + client.execute('SET TEXTSIZE 2147483647').do + client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do end when :odbc if config[:dsn].include?(';') driver = ODBC::Driver.new.tap do |d| d.name = config[:dsn_name] || 'Driver1' - d.attrs = config[:dsn].split(';').map{ |atr| atr.split('=') }.reject{ |kv| kv.size != 2 }.inject({}){ |h,kv| k,v = kv ; h[k] = v ; h } + d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } end ODBC::Database.new.drvconnect(driver) else @@ -459,12 +447,12 @@ def connect begin c.use_time = true c.use_utc = ActiveRecord::Base.default_timezone == :utc - rescue Exception => e - warn "Ruby ODBC v0.99992 or higher is required." + rescue Exception + warn 'Ruby ODBC v0.99992 or higher is required.' end end end - @spid = _raw_select("SELECT @@SPID", :fetch => :rows).first.first + @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first configure_connection rescue raise unless @auto_connecting @@ -486,13 +474,13 @@ def configure_application_name def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a - [a,b,c].each { |f| f.upcase! if f == 'y' } + [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 end - def remove_database_connections_and_rollback(database=nil) + def remove_database_connections_and_rollback(database = nil) database ||= current_database do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" begin @@ -503,14 +491,12 @@ def remove_database_connections_and_rollback(database=nil) end def with_sqlserver_error_handling - begin - yield - rescue Exception => e - case translate_exception(e,e.message) - when LostConnection; retry if auto_reconnected? - end - raise + yield + rescue Exception => e + case translate_exception(e, e.message) + when LostConnection then retry if auto_reconnected? end + raise end def disable_auto_reconnect @@ -526,9 +512,9 @@ def auto_reconnected? count = 0 while count <= (auto_connect_duration / 2) result = reconnect! - ActiveRecord::Base.did_retry_sqlserver_connection(self,count) + ActiveRecord::Base.did_retry_sqlserver_connection(self, count) return true if result - sleep 2** count + sleep 2**count count += 1 end ActiveRecord::Base.did_lose_sqlserver_connection(self) @@ -536,10 +522,6 @@ def auto_reconnected? ensure @auto_connecting = false end - - end #class SQLServerAdapter < AbstractAdapter - - end #module ConnectionAdapters - -end #module ActiveRecord - + end # class SQLServerAdapter < AbstractAdapter + end # module ConnectionAdapters +end # module ActiveRecord diff --git a/lib/active_record/sqlserver_test_case.rb b/lib/active_record/sqlserver_test_case.rb index 489a41034..f221e1788 100644 --- a/lib/active_record/sqlserver_test_case.rb +++ b/lib/active_record/sqlserver_test_case.rb @@ -3,15 +3,15 @@ # TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' # This is a temporary hack until we can just get the sqlserver_ignored regex in rails ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| - if listener.inspect =~ /ActiveRecord::SQLCounter/ - ActiveSupport::Notifications.unsubscribe(listener) + if listener.inspect =~ /ActiveRecord::SQLCounter/ + ActiveSupport::Notifications.unsubscribe(listener) end end module ActiveRecord class SQLCounter - sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] - ignored_sql.concat sqlserver_ignored + sqlserver_ignored = [/SELECT SCOPE_IDENTITY/, /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/, /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/] + ignored_sql.concat sqlserver_ignored end ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) -end \ No newline at end of file +end diff --git a/lib/arel/arel_sqlserver.rb b/lib/arel/arel_sqlserver.rb index ff6363460..30bb311d5 100644 --- a/lib/arel/arel_sqlserver.rb +++ b/lib/arel/arel_sqlserver.rb @@ -2,4 +2,4 @@ require 'arel/select_manager_sqlserver' require 'arel/nodes_sqlserver' require 'arel/visitors/sqlserver' -require 'arel/visitors/bind_visitor' \ No newline at end of file +require 'arel/visitors/bind_visitor' diff --git a/lib/arel/nodes_sqlserver.rb b/lib/arel/nodes_sqlserver.rb index ce40beaa7..b8ce99968 100644 --- a/lib/arel/nodes_sqlserver.rb +++ b/lib/arel/nodes_sqlserver.rb @@ -4,11 +4,11 @@ module Nodes # collection of them. See SelectManager#order for more details. class Ordering < Arel::Nodes::Unary def eql?(other) - #Arel::Nodes::Ascending or Arel::Nodes::Desecnding + # Arel::Nodes::Ascending or Arel::Nodes::Desecnding other.is_a?(Arel::Nodes::Ordering) && - self.expr == other.expr + expr == other.expr end - alias :== :eql? + alias_method :==, :eql? end end -end \ No newline at end of file +end diff --git a/lib/arel/select_manager_sqlserver.rb b/lib/arel/select_manager_sqlserver.rb index 4d82594f3..d72c88bde 100644 --- a/lib/arel/select_manager_sqlserver.rb +++ b/lib/arel/select_manager_sqlserver.rb @@ -1,16 +1,15 @@ module Arel - class SelectManager < Arel::TreeManager - + class SelectManager < Arel::TreeManager AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::SQLServerAdapter'.freeze - + # Getting real Ordering objects is very important for us. We need to be able to call #uniq on # a colleciton of them reliably as well as using their true object attributes to mutate them # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects. - alias :order_without_sqlserver :order + alias_method :order_without_sqlserver, :order def order(*expr) return order_without_sqlserver(*expr) unless engine_activerecord_sqlserver_adapter? - @ast.orders.concat(expr.map{ |x| + @ast.orders.concat(expr.map do |x| case x when Arel::Attributes::Attribute table = Arel::Table.new(x.relation.table_alias || x.relation.name) @@ -22,23 +21,23 @@ def order(*expr) x.split(',').map do |s| s = x if x.strip =~ /\A\b\w+\b\(.*,.*\)(\s+(ASC|DESC))?\Z/i # Allow functions with comma(s) to pass thru. s.strip! - d = s =~ /(ASC|DESC)\Z/i ? $1.upcase : nil + d = s =~ /(ASC|DESC)\Z/i ? Regexp.last_match[1].upcase : nil e = d.nil? ? s : s.mb_chars[0...-d.length].strip e = Arel.sql(e) - d && d == "DESC" ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e) + d && d == 'DESC' ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e) end else e = Arel.sql(x.to_s) Arel::Nodes::Ascending.new e end - }.flatten) + end.flatten) self end # A friendly over ride that allows us to put a special lock object that can have a default or pass # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method. - alias :lock_without_sqlserver :lock - def lock(locking=true) + alias_method :lock_without_sqlserver, :lock + def lock(locking = true) if engine_activerecord_sqlserver_adapter? case locking when true @@ -53,12 +52,11 @@ def lock(locking=true) lock_without_sqlserver(locking) end end - + private - + def engine_activerecord_sqlserver_adapter? @engine.connection && @engine.connection.class.name == AR_CA_SQLSA_NAME end - end -end \ No newline at end of file +end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index ff4a25868..b75a5549b 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,11 +1,9 @@ module Arel module Visitors class SQLServer < Arel::Visitors::ToSql - private # SQLServer ToSql/Visitor (Overides) - def visit_Arel_Nodes_SelectStatement(o, a) if complex_count_sql?(o) visit_Arel_Nodes_SelectStatementForComplexCount(o, a) @@ -18,7 +16,7 @@ def visit_Arel_Nodes_SelectStatement(o, a) def visit_Arel_Nodes_UpdateStatement(o, a) if o.orders.any? && o.limit.nil? - o.limit = Nodes::Limit.new(9223372036854775807) + o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) end super end @@ -69,31 +67,36 @@ def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) elsif top_one_everything_for_through_join?(o, a) projections = projections.map { |x| projection_without_expression(x, a) } end - [ ("SELECT" if !windowed), + [ + ('SELECT' unless windowed), (visit(core.set_quantifier, a) if core.set_quantifier && !windowed), (visit(o.limit, a) if o.limit && !windowed), - (projections.map{ |x| v = visit(x, a); v == "1" ? "1 AS [__wrp]" : v }.join(', ')), + (projections.map do |x| + v = visit(x, a) + v == '1' ? '1 AS [__wrp]' : v + end.join(', ')), (source_with_lock_for_select_statement(o, a)), - ("WHERE #{core.wheres.map{ |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), + ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), ("GROUP BY #{groups.map { |x| visit(x, a) }.join ', ' }" unless groups.empty?), (visit(core.having, a) if core.having), - ("ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}" if !orders.empty? && !windowed) + ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty? && !windowed) ].compact.join ' ' end def visit_Arel_Nodes_SelectStatementWithOffset(o, a) core = o.cores.first - o.limit ||= Arel::Nodes::Limit.new(9223372036854775807) + o.limit ||= Arel::Nodes::Limit.new(9_223_372_036_854_775_807) orders = rowtable_orders(o) - [ "SELECT", + [ + 'SELECT', (visit(o.limit, a) if o.limit && !windowed_single_distinct_select_statement?(o)), - (rowtable_projections(o, a).map{ |x| visit(x, a) }.join(', ')), - "FROM (", - "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}) AS [__rn],", - visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, true), - ") AS [__rnt]", + (rowtable_projections(o, a).map { |x| visit(x, a) }.join(', ')), + 'FROM (', + "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}) AS [__rn],", + visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, true), + ') AS [__rnt]', (visit(o.offset, a) if o.offset), - "ORDER BY [__rnt].[__rn] ASC" + 'ORDER BY [__rnt].[__rn] ASC' ].compact.join ' ' end @@ -101,23 +104,23 @@ def visit_Arel_Nodes_SelectStatementForComplexCount(o, a) core = o.cores.first o.limit.expr = Arel.sql("#{o.limit.expr} + #{o.offset ? o.offset.expr : 0}") if o.limit orders = rowtable_orders(o) - [ "SELECT COUNT([count]) AS [count_id]", - "FROM (", - "SELECT", - (visit(o.limit, a) if o.limit), - "ROW_NUMBER() OVER (ORDER BY #{orders.map{ |x| visit(x, a) }.join(', ')}) AS [__rn],", - "1 AS [count]", - (source_with_lock_for_select_statement(o, a)), - ("WHERE #{core.wheres.map{ |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{core.groups.map { |x| visit(x, a) }.join ', ' }" unless core.groups.empty?), - (visit(core.having, a) if core.having), - ("ORDER BY #{o.orders.map{ |x| visit(x, a) }.join(', ')}" if !o.orders.empty?), - ") AS [__rnt]", + [ + 'SELECT COUNT([count]) AS [count_id]', + 'FROM (', + 'SELECT', + (visit(o.limit, a) if o.limit), + "ROW_NUMBER() OVER (ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}) AS [__rn],", + '1 AS [count]', + (source_with_lock_for_select_statement(o, a)), + ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), + ("GROUP BY #{core.groups.map { |x| visit(x, a) }.join ', ' }" unless core.groups.empty?), + (visit(core.having, a) if core.having), + ("ORDER BY #{o.orders.map { |x| visit(x, a) }.join(', ')}" unless o.orders.empty?), + ') AS [__rnt]', (visit(o.offset, a) if o.offset) ].compact.join ' ' end - # SQLServer Helpers def source_with_lock_for_select_statement(o, a) @@ -142,7 +145,7 @@ def table_from_select_statement(o) # elsif Arel::Nodes::JoinSource === core.source # Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left # end - table_finder = lambda { |x| + table_finder = lambda do |x| case x when Arel::Table x @@ -151,7 +154,7 @@ def table_from_select_statement(o) when Arel::Nodes::Join table_finder.call(x.left) end - } + end table_finder.call(core.froms) end @@ -164,7 +167,6 @@ def single_distinct_select_statement?(o) end def windowed_single_distinct_select_statement?(o) - o.limit && o.offset && single_distinct_select_statement?(o) @@ -172,7 +174,7 @@ def windowed_single_distinct_select_statement?(o) def single_distinct_select_everything_statement?(o, a) single_distinct_select_statement?(o) && - visit(o.cores.first.projections.first, a).ends_with?(".*") + visit(o.cores.first.projections.first, a).ends_with?('.*') end def top_one_everything_for_through_join?(o, a) @@ -240,7 +242,7 @@ def find_and_fix_uncorrelated_joins_in_select_statement(o) j2 = core.froms.right return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::SqlLiteral === j2 && j2.include?('JOIN ') j1_tn = j1.right.name - j2_tn = j2.match(/JOIN \[(.*)\].*ON/).try(:[],1) + j2_tn = j2.match(/JOIN \[(.*)\].*ON/).try(:[], 1) return unless j1_tn == j2_tn on_index = j2.index(' ON ') j2.insert on_index, " AS [#{j2_tn}_crltd]" @@ -255,7 +257,7 @@ def rowtable_projections(o, a) x.dup.tap do |p| p.sub! 'DISTINCT', '' p.insert 0, visit(o.limit, a) if o.limit - p.gsub! /\[?#{tn}\]?\./, '[__rnt].' + p.gsub!(/\[?#{tn}\]?\./, '[__rnt].') p.strip! end end @@ -264,13 +266,13 @@ def rowtable_projections(o, a) core.projections.map do |x| x.dup.tap do |p| p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit, a)}".strip if o.limit - p.gsub! /\[?#{tn}\]?\./, '[__rnt].' + p.gsub!(/\[?#{tn}\]?\./, '[__rnt].') p.strip! end end elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o, a) core.projections.map do |x| - Arel.sql visit(x, a).split(',').map{ |y| y.split(' AS ').last.strip }.join(', ') + Arel.sql visit(x, a).split(',').map { |y| y.split(' AS ').last.strip }.join(', ') end elsif select_primary_key_sql?(o) [Arel.sql("[__rnt].#{quote_column_name(core.projections.first.name)}")] @@ -280,7 +282,6 @@ def rowtable_projections(o, a) end def rowtable_orders(o) - core = o.cores.first if !o.orders.empty? o.orders else @@ -294,16 +295,14 @@ def rowtable_orders(o) def projection_without_expression(projection, a) Arel.sql(visit(projection, a).split(',').map do |x| x.strip! - x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/,'\3') - x.sub!(/^DISTINCT\s*/,'') - x.sub!(/TOP\s*\(\d+\)\s*/i,'') + x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/, '\3') + x.sub!(/^DISTINCT\s*/, '') + x.sub!(/TOP\s*\(\d+\)\s*/i, '') x.strip end.join(', ')) end - end end - end Arel::Visitors::VISITORS['sqlserver'] = Arel::Visitors::SQLServer diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 8244abb65..3a109343d 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -190,7 +190,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase @connection.execute("SET LANGUAGE deutsch") @connection.send :initialize_dateformatter assert_nothing_raised do - Task.create(:starting => Time.utc(2000, 1, 31, 5, 42, 0), :ending => Date.new(2006, 12, 31)) + Task.create(starting: Time.utc(2000, 1, 31, 5, 42, 0), ending: Date.new(2006, 12, 31)) end end @@ -218,8 +218,8 @@ class AdapterTestSqlserver < ActiveRecord::TestCase setup do UpperTestDefault.delete_all - UpperTestDefault.create :COLUMN1 => 'Got a minute?', :COLUMN2 => 419 - UpperTestDefault.create :COLUMN1 => 'Favorite number?', :COLUMN2 => 69 + UpperTestDefault.create COLUMN1: 'Got a minute?', COLUMN2: 419 + UpperTestDefault.create COLUMN1: 'Favorite number?', COLUMN2: 69 end teardown do @@ -285,8 +285,8 @@ class AdapterTestSqlserver < ActiveRecord::TestCase context 'saving new datetime objects' do should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do - Time.any_instance.stubs :iso8601 => "2011-07-26T12:29:01.123-04:00" - saved = SqlServerChronic.create!(:datetime => @time).reload + Time.any_instance.stubs iso8601: "2011-07-26T12:29:01.123-04:00" + saved = SqlServerChronic.create!(datetime: @time).reload saved.reload assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String) assert_equal 123000, saved.datetime.usec @@ -325,7 +325,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end should 'find identity column using #identity_column' do - joke_id_column = Joke.columns.detect { |c| c.name == 'id' } + joke_id_column = Joke.columns.find { |c| c.name == 'id' } assert_equal joke_id_column.name, @connection.send(:identity_column,Joke.table_name).name assert_equal joke_id_column.sql_type, @connection.send(:identity_column,Joke.table_name).sql_type end @@ -505,7 +505,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase setup do @connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } @parent = FkTestHasPk.create! - @member = FkTestHasFk.create!(:fk_id => @parent.id) + @member = FkTestHasFk.create!(fk_id: @parent.id) end should 'NOT ALLOW by default the deletion of a referenced parent' do @@ -654,7 +654,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end should 'have indexes with descending order' do - assert @connection.indexes('accounts').detect { |i| i.name == @desc_index_name } + assert @connection.indexes('accounts').find { |i| i.name == @desc_index_name } end end @@ -673,7 +673,7 @@ class AdapterTestSqlserver < ActiveRecord::TestCase should 'work with dynamic finders' do name = 'MetaSkills' - customer = CustomersView.create! :name => name + customer = CustomersView.create! name: name assert_equal customer, CustomersView.find_by_name(name) end @@ -804,13 +804,13 @@ class AdapterTestSqlserver < ActiveRecord::TestCase module ActiveRecord class AdapterTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_update_prepared_statement] + COERCED_TESTS = [:test_update_prepared_statement] # Like PostgreSQL, SQL Server does not support null bytes in strings. # DECLARE @mybin1 binary(5), @mybin2 binary(5) # SET @mybin1 = 0x00 - # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' + # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest end -end \ No newline at end of file +end diff --git a/test/cases/arel_helper.rb b/test/cases/arel_helper.rb index 0d44ba0f1..9bbc328aa 100644 --- a/test/cases/arel_helper.rb +++ b/test/cases/arel_helper.rb @@ -1,7 +1,7 @@ AREL_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['arel'].full_gem_path,'test')) $LOAD_PATH.unshift AREL_TEST_ROOT -# TODO: Find A better way to run Arel tests without failing on +# TODO: Find A better way to run Arel tests without failing on # SQL Server brackets instead of quotes class Object def must_be_like other @@ -12,9 +12,9 @@ def must_be_like other end -# Useful for debugging Arel. +# Useful for debugging Arel. # You can call it like arel_to_png(User.where(name: "foo").arel) def arel_to_png(arel, file_name = "query") graph = GraphViz.parse_string(arel.to_dot) - graph.output(:png => "#{file_name}.png") + graph.output(png: "#{file_name}.png") end \ No newline at end of file diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb index 5b0841100..eb2fbee61 100644 --- a/test/cases/associations_test_sqlserver.rb +++ b/test/cases/associations_test_sqlserver.rb @@ -2,7 +2,7 @@ require 'models/owner' class HasManyThroughAssociationsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] + COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] # Rails does not do a case-insensive comparison # Until that patch is made to rails we are preventing this test from running in this gem. diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index c2168bf49..1c7bb6a83 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -26,13 +26,13 @@ def test_coerced_read_attributes_before_type_cast_on_datetime end def test_coerced_typecast_attribute_from_select_to_false - topic = Topic.create(:title => 'Budget') + topic = Topic.create(title: 'Budget') topic = Topic.all.merge!(select: "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test").first assert !topic.is_test? end def test_coerced_typecast_attribute_from_select_to_true - topic = Topic.create(:title => 'Budget') + topic = Topic.create(title: 'Budget') topic = Topic.all.merge!(select: "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test").first assert topic.is_test? end diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index 7f57cdf21..21ba4206d 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -3,13 +3,13 @@ require 'models/auto_id' class BasicsTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_column_names_are_escaped, + + COERCED_TESTS = [:test_column_names_are_escaped, :test_respect_internal_encoding] # test_respect_internal_encoding is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. - + include SqlserverCoercedTest should 'operate as other database adapters when finding primary keys, standards are postgresql adapter' do @@ -18,10 +18,10 @@ class BasicsTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Post.find('') } assert_raise(ActiveRecord::RecordNotFound) { Post.find(nil) } end - + def test_coerced_column_names_are_escaped assert_equal "[foo]]bar]", ActiveRecord::Base.connection.quote_column_name("foo]bar") end - + end diff --git a/test/cases/batches_test_sqlserver.rb b/test/cases/batches_test_sqlserver.rb index 60c486ed1..b69c90f74 100644 --- a/test/cases/batches_test_sqlserver.rb +++ b/test/cases/batches_test_sqlserver.rb @@ -5,24 +5,24 @@ class BatchesTestSqlserver < ActiveRecord::TestCase end class EachTest < ActiveRecord::TestCase - + COERCED_TESTS = [ :test_find_in_batches_should_quote_batch_order ] - + include SqlserverCoercedTest - + fixtures :posts - + def test_coerced_find_in_batches_should_quote_batch_order c = Post.connection assert_sql(/ORDER BY \[posts\]\.\[id\]/) do - Post.find_in_batches(:batch_size => 1) do |batch| + Post.find_in_batches(batch_size: 1) do |batch| assert_kind_of Array, batch assert_kind_of Post, batch.first end end end - - + + end diff --git a/test/cases/belongs_to_associations_test_sqlserver.rb b/test/cases/belongs_to_associations_test_sqlserver.rb index e6d02020b..c31a2a905 100644 --- a/test/cases/belongs_to_associations_test_sqlserver.rb +++ b/test/cases/belongs_to_associations_test_sqlserver.rb @@ -4,16 +4,16 @@ class BelongsToAssociationsTestSqlserver < ActiveRecord::TestCase end class BelongsToAssociationsTest < ActiveRecord::TestCase - + COERCED_TESTS = [:test_belongs_to_with_primary_key_joins_on_correct_column] - + include SqlserverCoercedTest - + def test_coerced_belongs_to_with_primary_key_joins_on_correct_column sql = Client.joins(:firm_with_primary_key).to_sql assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql) assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql) end - - + + end diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index b9e0b9b0a..08fad1641 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -5,21 +5,21 @@ class BindParameterTestSqlserver < ActiveRecord::TestCase end class ActiveRecord::BindParameterTest < ActiveRecord::TestCase - + fixtures :topics - + COERCED_TESTS = [ :test_binds_are_logged ] - + include SqlserverCoercedTest - + # TODO: put a real test here def test_coerced_binds_are_logged assert true, 'they are!' end - + end diff --git a/test/cases/calculations_test_sqlserver.rb b/test/cases/calculations_test_sqlserver.rb index 7a993c465..9b5c8173f 100644 --- a/test/cases/calculations_test_sqlserver.rb +++ b/test/cases/calculations_test_sqlserver.rb @@ -9,19 +9,19 @@ class CalculationsTestSqlserver < ActiveRecord::TestCase end class CalculationsTest < ActiveRecord::TestCase - + COERCED_TESTS = [ - :test_should_return_decimal_average_of_integer_field, + :test_should_return_decimal_average_of_integer_field, :test_should_sum_expression, :test_limit_is_kept, :test_limit_with_offset_is_kept, :test_offset_is_kept ] - + include SqlserverCoercedTest - + fixtures :accounts - + def test_coerced_should_return_decimal_average_of_integer_field # Other DBs return 3.5 like this. # Account.all.map(&:id).inspect # => [1, 2, 3, 4, 5, 6] @@ -31,29 +31,29 @@ def test_coerced_should_return_decimal_average_of_integer_field value = Account.average(:id) assert_equal 3, value end - + def test_coerced_should_sum_expression assert_equal 636, Account.sum("2 * credit_limit") end - + def test_coerced_limit_is_kept queries = assert_sql { Account.limit(1).count } assert_equal 1, queries.length assert_match(/TOP \(1\)/, queries.first) end - + def test_coerced_limit_with_offset_is_kept queries = assert_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length assert_match(/TOP \(1\)/, queries.first) assert_match(/\[__rn\] > \(1\)/, queries.first) end - + def test_coerced_offset_is_kept queries = assert_sql { Account.offset(1).count } assert_equal 1, queries.length assert_match(/\[__rn\] > \(1\)/, queries.first) end - - + + end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 374426cea..0acae76ea 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -11,23 +11,23 @@ require "cases/migration/helper" class ColumnTestSqlserver < ActiveRecord::TestCase - + setup do @connection = ActiveRecord::Base.connection @column_klass = ActiveRecord::ConnectionAdapters::SQLServerColumn end - + should 'return real_number as float' do assert_equal :float, TableWithRealColumn.columns_hash["real_number"].type end - + should 'know its #table_name and #table_klass' do Topic.columns.each do |column| assert_equal 'topics', column.table_name, "This column #{column.inspect} did not know it's #table_name" assert_equal Topic, column.table_klass, "This column #{column.inspect} did not know it's #table_klass" end end - + should 'return correct null, limit, and default for Topic' do tch = Topic.columns_hash assert_equal false, tch['id'].null @@ -36,33 +36,33 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal true, tch['approved'].default assert_equal 0, tch['replies_count'].default end - + context 'For binary columns' do setup do @binary_string = "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000\000!\371\004\000\000\000\000\000,\000\000\000\000\001\000\001\000\000\002\002D\001\000;" - @saved_bdata = Binary.create!(:data => @binary_string) + @saved_bdata = Binary.create!(data: @binary_string) end - + should 'read and write binary data equally' do assert_equal @binary_string, Binary.find(@saved_bdata).data end - + should 'have correct attributes' do column = Binary.columns_hash['data'] assert_equal :binary, column.type assert_equal @connection.native_binary_database_type, column.sql_type assert_equal nil, column.limit end - + should 'quote data for sqlserver with literal 0x prefix' do # See the output of the stored procedure: 'exec sp_datatype_info' sqlserver_encoded_bdata = "0x47494638396101000100800000ffffff00000021f90400000000002c00000000010001000002024401003b" - assert_equal sqlserver_encoded_bdata, @column_klass.string_to_binary(@binary_string) + assert_equal sqlserver_encoded_bdata, @column_klass.string_to_binary(@binary_string) end end - + context 'For string columns' do setup do @@ -78,14 +78,14 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal :text, @varcharmax.type, @varcharmax.inspect assert_equal :text, @varcharmax10.type, @varcharmax10.inspect end - + should 'have correct #sql_type per schema definition' do assert_equal 'char(1)', @char.sql_type, 'Specifing a char type with no limit is 1 by SQL Server standards.' assert_equal 'char(10)', @char10.sql_type, @char10.inspect assert_equal 'varchar(max)', @varcharmax.sql_type, 'A -1 limit should be converted to max (max) type.' assert_equal 'varchar(max)', @varcharmax10.sql_type, 'A -1 limit should be converted to max (max) type.' end - + should 'have correct #limit per schema definition' do assert_equal 1, @char.limit assert_equal 10, @char10.limit @@ -94,9 +94,9 @@ class ColumnTestSqlserver < ActiveRecord::TestCase end end - + context 'For all national/unicode columns' do - + setup do @nchar = SqlServerUnicode.columns_hash['nchar'] @nvarchar = SqlServerUnicode.columns_hash['nvarchar'] @@ -107,13 +107,13 @@ class ColumnTestSqlserver < ActiveRecord::TestCase @nvarcharmax = SqlServerUnicode.columns_hash['nvarchar_max'] @nvarcharmax10 = SqlServerUnicode.columns_hash['nvarchar_max_10'] end - + should 'all respond true to #is_utf8?' do SqlServerUnicode.columns_hash.except('id').values.each do |column| assert column.is_utf8?, "This column #{column.inspect} should have been a unicode column." end end - + should 'have correct simplified types' do assert_equal :string, @nchar.type assert_equal :string, @nvarchar.type @@ -124,7 +124,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal :text, @nvarcharmax.type, @nvarcharmax.inspect assert_equal :text, @nvarcharmax10.type, @nvarcharmax10.inspect end - + should 'have correct #sql_type per schema definition' do assert_equal 'nchar(1)', @nchar.sql_type, 'Specifing a nchar type with no limit is 1 by SQL Server standards.' assert_equal 'nvarchar(255)', @nvarchar.sql_type, 'Default nvarchar limit is 255.' @@ -135,7 +135,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal 'nvarchar(max)', @nvarcharmax.sql_type, 'A -1 limit should be converted to max (max) type.' assert_equal 'nvarchar(max)', @nvarcharmax10.sql_type, 'A -1 limit should be converted to max (max) type.' end - + should 'have correct #limit per schema definition' do assert_equal 1, @nchar.limit assert_equal 255, @nvarchar.limit @@ -145,9 +145,9 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal nil, @nvarcharmax.limit, 'Limits on max types are moot and we should let rails know that.' assert_equal nil, @nvarcharmax10.limit, 'Limits on max types are moot and we should let rails know that.' end - + end - + context 'For datetime columns' do setup do @@ -162,7 +162,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase should 'have correct simplified type for uncast datetime' do assert_equal :datetime, @datetime.type end - + should 'use correct #sql_type for different sql server versions' do assert_equal 'datetime', @datetime.sql_type if sqlserver_2005? @@ -173,115 +173,115 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal 'time', @time.sql_type end end - + should 'all be have nil #limit' do assert_equal nil, @date.limit assert_equal nil, @time.limit assert_equal nil, @datetime.limit end - + context 'with timestamps' do should 'use datetime sql type when using :timestamp in schema statements' do assert_equal :datetime, @timestamp.type assert_equal 'datetime', @timestamp.sql_type end - + should 'be able to use real sql server timestamp if you really want to' do assert_equal :binary, @ss_timestamp.type assert_equal 'timestamp', @ss_timestamp.sql_type end unless sqlserver_azure? - + should 'return :timestamp as a binaryish string' do chronic = SqlServerChronic.create!.reload assert_match %r|\000|, chronic.ss_timestamp end unless sqlserver_azure? end - + context 'For smalldatetime types' do - + should 'have created that type using rails migrations' do assert_equal 'smalldatetime', @smalldatetime.sql_type end - + should 'be able to insert column without truncation warnings or the like' do - SqlServerChronic.create! :smalldatetime => Time.now + SqlServerChronic.create! smalldatetime: Time.now end - + should 'be able to update column without truncation warnings or the like' do - ssc = SqlServerChronic.create! :smalldatetime => 2.days.ago - ssc.update_attributes! :smalldatetime => Time.now + ssc = SqlServerChronic.create! smalldatetime: 2.days.ago + ssc.update_attributes! smalldatetime: Time.now end end - + context 'which have coerced types' do - + setup do christmas_08 = "2008-12-25".to_time christmas_08_afternoon = "2008-12-25 12:00".to_time - @chronic_date = SqlServerChronic.create!(:date => christmas_08).reload - @chronic_time = SqlServerChronic.create!(:time => christmas_08_afternoon).reload + @chronic_date = SqlServerChronic.create!(date: christmas_08).reload + @chronic_time = SqlServerChronic.create!(time: christmas_08_afternoon).reload end - + should 'have an inheritable attribute ' do assert SqlServerChronic.coerced_sqlserver_date_columns.include?('date') unless sqlserver_2008? end - + should 'have column and objects cast to date' do assert_equal :date, @date.type, "This column: \n#{@date.inspect}" assert_instance_of Date, @chronic_date.date end - + should 'have column objects cast to time' do assert_equal :time, @time.type, "This column: \n#{@time.inspect}" assert_instance_of Time, @chronic_time.time end - + end end - + context 'For decimal and numeric columns' do - + setup do @bank_balance = NumericData.columns_hash['bank_balance'] @big_bank_balance = NumericData.columns_hash['big_bank_balance'] @world_population = NumericData.columns_hash['world_population'] @my_house_population = NumericData.columns_hash['my_house_population'] end - + should 'have correct simplified types' do assert_equal :decimal, @bank_balance.type assert_equal :decimal, @big_bank_balance.type assert_equal :integer, @world_population.type, 'Since #extract_scale == 0' assert_equal :integer, @my_house_population.type, 'Since #extract_scale == 0' end - + should 'have correct #sql_type' do assert_equal 'decimal(10,2)', @bank_balance.sql_type assert_equal 'decimal(15,2)', @big_bank_balance.sql_type assert_equal 'decimal(10,0)', @world_population.sql_type assert_equal 'decimal(2,0)', @my_house_population.sql_type end - + should 'have correct #limit' do assert_equal nil, @bank_balance.limit assert_equal nil, @big_bank_balance.limit assert_equal nil, @world_population.limit assert_equal nil, @my_house_population.limit end - + should 'return correct precisions and scales' do assert_equal [10,2], [@bank_balance.precision, @bank_balance.scale] assert_equal [15,2], [@big_bank_balance.precision, @big_bank_balance.scale] assert_equal [10,0], [@world_population.precision, @world_population.scale] assert_equal [2,0], [@my_house_population.precision, @my_house_population.scale] end - + end - + context 'For float columns' do # NOTE: float limits are adjusted to 24 or 53 by the database as per # http://msdn.microsoft.com/en-us/library/ms173773.aspx @@ -328,7 +328,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase end end - + context 'For tinyint columns' do setup do @@ -341,7 +341,7 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal 'tinyint(1)', @tinyint.sql_type end - end + end end module ActiveRecord @@ -349,7 +349,7 @@ class Migration class ColumnsTest < ActiveRecord::TestCase include ActiveRecord::Migration::TestHelper - COERCED_TESTS = [:test_remove_column_with_multi_column_index] + COERCED_TESTS = [:test_remove_column_with_multi_column_index] # The if current_adapter? conditional below should also contain :SQLServerAdapter. # Until that patch is made to rails we are preventing this test from running in this gem. @@ -358,8 +358,8 @@ class ColumnsTest < ActiveRecord::TestCase #the only thing we changed in this method was to add :SQLServerAdapter def test_coerced_remove_column_with_multi_column_index 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") @@ -374,4 +374,4 @@ def test_coerced_remove_column_with_multi_column_index end end end -end \ No newline at end of file +end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index ac9633467..34d5aafd7 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,26 +1,26 @@ require 'cases/sqlserver_helper' -require 'models/reply' +require 'models/reply' require 'models_sqlserver/topic' class ConnectionTestSqlserver < ActiveRecord::TestCase - + self.use_transactional_fixtures = false - + fixtures :topics, :accounts - + setup do @connection = ActiveRecord::Base.connection end - + should 'affect rows' do 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 assert_equal "2 updated", Topic.find(2).content - assert_equal 2, Topic.delete([1, 2]) + assert_equal 2, Topic.delete([1, 2]) end - + should 'allow usage of :database connection option to remove setting from dsn' do assert_equal 'activerecord_unittest', @connection.current_database begin @@ -31,7 +31,7 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase assert_equal 'activerecord_unittest', @connection.current_database, 'Would default back to connection options' end end unless sqlserver_azure? - + context 'ODBC connection management' do should 'return finished ODBC statement handle from #execute without block' do @@ -85,30 +85,30 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase end end if connection_mode_odbc? - - + + context 'Connection management' do - + setup do assert @connection.active? end - + should 'set spid on connect' do assert_instance_of Fixnum, @connection.spid end - + should 'reset spid on disconnect!' do @connection.disconnect! assert @connection.spid.nil? end - + should 'be able to disconnect and reconnect at will' do @connection.disconnect! assert !@connection.active? @connection.reconnect! assert @connection.active? end - + should 'auto reconnect when setting is on' do with_auto_connect(true) do @connection.disconnect! @@ -116,36 +116,36 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase assert @connection.active? end end - + should 'not auto reconnect when setting is off' do with_auto_connect(false) do @connection.disconnect! assert_raise(ActiveRecord::LostConnection) { Topic.count } end end - + should 'not auto reconnect on commit transaction' do @connection.disconnect! assert_raise(ActiveRecord::LostConnection) { @connection.commit_db_transaction } end - + should 'gracefully ignore lost connections on rollback transaction' do @connection.disconnect! assert_nothing_raised { @connection.rollback_db_transaction } end - + should 'not auto reconnect on create savepoint' do @connection.disconnect! assert_raise(ActiveRecord::LostConnection) { @connection.create_savepoint } end - + should 'not auto reconnect on rollback to savepoint ' do @connection.disconnect! assert_raise(ActiveRecord::LostConnection) { @connection.rollback_to_savepoint } end - + context 'testing #disable_auto_reconnect' do - + should 'when auto reconnect setting is on' do with_auto_connect(true) do @connection.send(:disable_auto_reconnect) do @@ -154,7 +154,7 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase assert @connection.class.auto_connect end end - + should 'when auto reconnect setting is off' do with_auto_connect(false) do @connection.send(:disable_auto_reconnect) do @@ -163,9 +163,9 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase assert !@connection.class.auto_connect end end - + end - + context 'with a deadlock victim exception (1205)' do context 'outside a transaction' do @@ -173,7 +173,7 @@ class ConnectionTestSqlserver < ActiveRecord::TestCase setup do @query = "SELECT 1 as [one]" @expected = @connection.execute(@query) - # Execute the query to get a handle of the expected result, which + # Execute the query to get a handle of the expected result, which # will be returned after a simulated deadlock victim (1205). raw_conn = @connection.instance_variable_get(:@connection) stubbed_handle = raw_conn.execute(@query) @@ -231,31 +231,31 @@ def execute_with_deadlock_exception(sql, *args) end end if connection_mode_dblib? # Since it is easier to test, but feature should work in ODBC too. - + end - + context 'Diagnostics' do - + should 'testing #activity_stats' do stats = @connection.activity_stats - assert !stats.empty? + assert !stats.empty? assert stats.all? { |s| s.has_key?("session_id") } assert stats.all? { |s| s["database"] == @connection.current_database } end - + end - - - + + + private - + def assert_all_odbc_statements_used_are_closed(&block) odbc = @connection.raw_connection.class.parent existing_handles = [] ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h } existing_handle_ids = existing_handles.map(&:object_id) assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed" - GC.disable + GC.disable yield used_handles = [] ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) } @@ -264,7 +264,7 @@ def assert_all_odbc_statements_used_are_closed(&block) ensure GC.enable end - + def deadlock_victim_exception(sql) require 'tiny_tds/error' error = TinyTds::Error.new("Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}") @@ -272,5 +272,5 @@ def deadlock_victim_exception(sql) error.db_error_number = 1205 error end - + end diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index 470f14ff7..2c71b851b 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -7,16 +7,16 @@ class EagerAssociationTestSqlserver < ActiveRecord::TestCase end class EagerAssociationTest < ActiveRecord::TestCase - + COERCED_TESTS = [:test_count_with_include] - + include SqlserverCoercedTest - + fixtures :posts, :comments, :authors - + def test_coerced_count_with_include assert_equal 3, authors(:david).posts_with_comments.where("len(comments.body) > 15").references(:comments).count end - - + + end diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index b949aa764..ff536d1ab 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -1,17 +1,17 @@ require 'cases/sqlserver_helper' class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase - + setup do @klass = ActiveRecord::Base end - + should 'execute a simple procedure' do tables = @klass.execute_procedure :sp_tables assert_instance_of Array, tables assert tables.first.respond_to?(:keys) end - + should 'take parameter arguments' do tables = @klass.execute_procedure :sp_tables, 'sql_server_chronics' table_info = tables.first @@ -19,7 +19,7 @@ class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" end - + should 'allow multiple result sets to be returned' do results1, results2 = @klass.execute_procedure('sp_helpconstraint','accounts') assert_instance_of Array, results1 @@ -32,12 +32,12 @@ class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase end should 'take named parameter arguments' do - tables = @klass.execute_procedure :sp_tables, :table_name => 'tables', :table_owner => 'sys' + tables = @klass.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys' table_info = tables.first assert_equal 1, tables.size assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" end - - + + end diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index ea86f49d0..8606e24e1 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -16,7 +16,7 @@ class FinderTest < ActiveRecord::TestCase :test_string_sanitation, :test_take_and_first_and_last_with_integer_should_use_sql_limit, :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - + ] include SqlserverCoercedTest @@ -24,17 +24,17 @@ class FinderTest < ActiveRecord::TestCase # TODO This test passes in rails 4.0.0 but not 4.0.1-2 def test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - p = Post.all.merge!(:includes => { :authors => :author_address }, - :order => 'author_addresses.id DESC ', - :limit => 2) + p = Post.all.merge!(includes: { authors: :author_address }, + order: 'author_addresses.id DESC ', + limit: 2) # ar_version = Gem.loaded_specs['activerecord'].version.version # arel_to_png(p, "#{ar_version}") count = p.to_a.size #puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" assert_equal 2, count - - assert_equal 3, Post.all.merge!(:includes => { :author => :author_address, :authors => :author_address}, - :order => 'author_addresses_authors.id DESC ', :limit => 3).to_a.size + + assert_equal 3, Post.all.merge!(includes: { author: :author_address, authors: :author_address}, + order: 'author_addresses_authors.id DESC ', limit: 3).to_a.size end diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb index 26c916234..5e37c8816 100644 --- a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb +++ b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb @@ -4,23 +4,23 @@ class HasAndBelongsToManyAssociationsTestSqlserver < ActiveRecord::TestCase end class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase - + COERCED_TESTS = [ :test_count_with_finder_sql, :test_caching_of_columns ] - + include SqlserverCoercedTest - + # TODO: put a real test here def test_coerced_count_with_finder_sql assert true end - + # TODO: put a real test here def test_coerced_caching_of_columns assert true end - - + + end diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index fc8106908..a283c6a50 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -7,23 +7,23 @@ class InheritanceTestSqlserver < ActiveRecord::TestCase end class InheritanceTest < ActiveRecord::TestCase - + fixtures :companies, :projects, :subscribers, :accounts - + COERCED_TESTS = [ :test_a_bad_type_column, :test_eager_load_belongs_to_primary_key_quoting ] - + include SqlserverCoercedTest - + def test_coerced_a_bad_type_column Company.connection.execute "SET IDENTITY_INSERT [companies] ON" Company.connection.insert "INSERT INTO companies ([id], [type], [name]) VALUES(100, N'bad_class!', N'Not happening')" Company.connection.execute "SET IDENTITY_INSERT [companies] OFF" assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } end - + def test_coerced_eager_load_belongs_to_primary_key_quoting con = Account.connection assert_sql(/\[companies\]\.\[id\] IN \(N''1''\)/) do @@ -31,7 +31,7 @@ def test_coerced_eager_load_belongs_to_primary_key_quoting end end - + end diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb index 84a9e28fd..e7a98dcda 100644 --- a/test/cases/invalid_connection_test_sqlserver.rb +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'cases/sqlserver_helper' require 'cases/invalid_connection_test' - class TestAdapterWithInvalidConnection < ActiveRecord::TestCase + class TestAdapterWithInvalidConnection < ActiveRecord::TestCase def setup #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). #It makes much more sense for us to use sqlserver diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 3420f85f6..22c127cb8 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -3,25 +3,25 @@ require 'models_sqlserver/person' class MigrationTestSqlserver < ActiveRecord::TestCase - + setup do @connection = ActiveRecord::Base.connection end - + context 'For transactions' do - + setup do @trans_test_table1 = 'sqlserver_trans_table1' @trans_test_table2 = 'sqlserver_trans_table2' @trans_tables = [@trans_test_table1,@trans_test_table2] end - + teardown do @trans_tables.each do |table_name| ActiveRecord::Migration.drop_table(table_name) if @connection.tables.include?(table_name) end end - + should 'not create a tables if error in migrations' do begin ActiveRecord::Migrator.up(SQLSERVER_MIGRATIONS_ROOT+'/transaction_table') @@ -31,11 +31,11 @@ class MigrationTestSqlserver < ActiveRecord::TestCase @connection.tables.wont_include @trans_test_table1 @connection.tables.wont_include @trans_test_table2 end - + end - + context 'For changing column' do - + should 'not raise exception when column contains default constraint' do lock_version_column = Person.columns_hash['lock_version'] assert_equal :integer, lock_version_column.type @@ -46,33 +46,33 @@ class MigrationTestSqlserver < ActiveRecord::TestCase assert_equal :string, lock_version_column.type assert lock_version_column.default.nil? end - + should 'not drop the default contraint if just renaming' do - find_default = lambda do - @connection.execute_procedure(:sp_helpconstraint, 'defaults', 'nomsg').select do |row| + find_default = lambda do + @connection.execute_procedure(:sp_helpconstraint, 'defaults', 'nomsg').select do |row| row['constraint_type'] == "DEFAULT on column decimal_number" end.last end default_before = find_default.call - @connection.change_column :defaults, :decimal_number, :decimal, :precision => 4 + @connection.change_column :defaults, :decimal_number, :decimal, precision: 4 default_after = find_default.call assert default_after assert_equal default_before['constraint_keys'], default_after['constraint_keys'] end - + end - + end if ActiveRecord::TestCase.sqlserver_azure? class MigrationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] include SqlserverCoercedTest - + # TODO: put a real test here def test_coerced_test_migrator_db_has_no_schema_migrations_table assert true - end - + end + end end diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 3b0b63346..abb437a23 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -11,37 +11,37 @@ require 'models_sqlserver/sql_server_order_row_number' class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase - + fixtures :jobs, :people, :references, :subscriptions, :authors, :posts, :comments, :categorizations - + setup :create_10_books teardown :destroy_all_books - - + + context 'When selecting with limit' do - + should 'alter sql to limit number of records returned' do assert_sql(/SELECT TOP \(10\)/) { Book.limit(10).load } end - + end - + context 'When selecting with offset' do should 'have limit (top) of 9223372036854775807 if only offset is passed' do assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.offset(1).load } end - + should 'support calling exists?' do assert Book.offset(3).exists? end - + end - + context 'When selecting with limit and offset' do - - should 'work with fully qualified table and columns in select' do + + should 'work with fully qualified table and columns in select' do books = Book.select('books.id, books.name').limit(3).offset(5) assert_equal Book.all[5,3].map(&:id), books.map(&:id) end @@ -50,14 +50,14 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase sql = %|EXEC sp_executesql N'SELECT TOP (3) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [books].[id] ASC) AS [__rn], [books].* FROM [books] ) AS [__rnt] WHERE [__rnt].[__rn] > (5) ORDER BY [__rnt].[__rn] ASC'| assert_sql(sql) { Book.limit(3).offset(5).load } end - + should 'add locks to deepest sub select' do pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).count } assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).load } end - + should 'have valid sort order' do order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").map(&:c) assert_equal [2, 1, 0], order_row_numbers @@ -87,8 +87,8 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase create_10_books assert_queries(1) { Book.includes(:subscriptions).limit(10).offset(10).references(:subscriptions).load } end - - + + context 'with count' do should 'pass a gauntlet of window tests' do @@ -108,20 +108,20 @@ class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase end end - + end - - + + protected - + def create_10_books Book.delete_all @books = (1..10).map {|i| Book.create!} end - + def destroy_all_books @books.each { |b| b.destroy } end - + end diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index 0ba71d995..a184ded64 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -2,145 +2,145 @@ require 'models/post' class OrderTestSqlserver < ActiveRecord::TestCase - + fixtures :posts - + context 'Order by' do - + should 'not mangel complex order clauses' do xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END" - xyz_post = Post.create :title => 'XYZ Post', :body => 'Test cased orders.' + xyz_post = Post.create title: 'XYZ Post', body: 'Test cased orders.' assert_equal xyz_post, Post.order(Arel::Nodes::Ordering.new(Arel.sql(xyz_order))).first end - + should 'support column' do order = "title" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support column ASC' do order = "title ASC" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support column DESC' do order = "title DESC" - post1 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support column as symbol' do order = :title - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support table and column' do order = "posts.title" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support quoted column' do order = "[title]" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support quoted table and column' do order = "[posts].[title]" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support primary: column, secondary: column' do order = "title DESC, body" - post1 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' - post2 = Post.create :title => 'ZZZ Post', :body => 'ZZZ Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: table and column, secondary: column' do order = "posts.title DESC, body" - post1 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' - post2 = Post.create :title => 'ZZZ Post', :body => 'ZZZ Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: case expression, secondary: column' do order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body" - post1 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' - post2 = Post.create :title => 'ZZZ Post', :body => 'ZZZ Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: quoted table and column, secondary: case expresion' do order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC" - post1 = Post.create :title => 'ZZZ Post', :body => 'ZZZ Test cased orders.' - post2 = Post.create :title => 'ZZY Post', :body => 'ZZZ Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support inline function' do order = "LEN(title)" - post1 = Post.create :title => 'A', :body => 'AAA Test cased orders.' + post1 = Post.create title: 'A', body: 'AAA Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support inline function with parameters' do order = "SUBSTRING(title, 1, 3)" - post1 = Post.create :title => 'AAA Post', :body => 'Test cased orders.' + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support inline function with parameters DESC' do order = "SUBSTRING(title, 1, 3) DESC" - post1 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first end - + should 'support primary: inline function, secondary: column' do order = "LEN(title), body" - post1 = Post.create :title => 'A', :body => 'AAA Test cased orders.' - post2 = Post.create :title => 'A', :body => 'Test cased orders.' + post1 = Post.create title: 'A', body: 'AAA Test cased orders.' + post2 = Post.create title: 'A', body: 'Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: inline function, secondary: column with direction' do order = "LEN(title) ASC, body DESC" - post1 = Post.create :title => 'A', :body => 'ZZZ Test cased orders.' - post2 = Post.create :title => 'A', :body => 'Test cased orders.' + post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'A', body: 'Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: column, secondary: inline function' do order = "body DESC, LEN(title)" - post1 = Post.create :title => 'Post', :body => 'ZZZ Test cased orders.' - post2 = Post.create :title => 'Longer Post', :body => 'ZZZ Test cased orders.' + post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: case expression, secondary: inline function' do order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC" - post1 = Post.create :title => 'ZZZ Post', :body => 'Z' - post2 = Post.create :title => 'ZZZ Post', :body => 'Test cased orders.' + post1 = Post.create title: 'ZZZ Post', body: 'Z' + post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - + should 'support primary: inline function, secondary: case expression' do order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC" - post1 = Post.create :title => 'ZZZ Post', :body => 'Z' - post2 = Post.create :title => 'Post', :body => 'Z' + post1 = Post.create title: 'ZZZ Post', body: 'Z' + post2 = Post.create title: 'Post', body: 'Z' assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 1034900d8..510c9949b 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -20,13 +20,13 @@ class PersistencesTestSqlserver < ActiveRecord::TestCase end class PersistencesTest < ActiveRecord::TestCase - + fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans - + COERCED_TESTS = [:test_update_all_doesnt_ignore_order, :test_update_columns_changing_id, :test_update_attributes] - + include SqlserverCoercedTest - + def test_coerced_update_all_doesnt_ignore_order assert_equal authors(:david).id + 1, authors(:mary).id test_update_with_order_succeeds = lambda do |order| diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index b02c5f474..88403e1b7 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -4,14 +4,14 @@ require 'models_sqlserver/person' class PessimisticLockingTestSqlserver < ActiveRecord::TestCase - + self.use_transactional_fixtures = false fixtures :people, :readers - + setup do Person.columns; Reader.columns # Avoid introspection queries during tests. end - + context 'For simple finds with default lock option' do should 'lock with simple find' do @@ -50,7 +50,7 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase end end end - + should 'simply add lock to find all' do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH \(NOLOCK\)| do Person.lock('WITH (NOLOCK)').load @@ -58,13 +58,13 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase end end - + context 'For paginated finds' do - + setup do - 20.times { |n| Person.create!(:first_name => "Thing_#{n}") } + 20.times { |n| Person.create!(first_name: "Thing_#{n}") } end - + should 'cope with eager loading un-locked paginated' do eager_ids_sql = /SELECT TOP \(5\).*FROM \[people\] WITH \(NOLOCK\)/ loader_sql = /FROM \[people\] WITH \(NOLOCK\).*WHERE \[people\]\.\[id\] IN/ @@ -72,8 +72,8 @@ class PessimisticLockingTestSqlserver < ActiveRecord::TestCase Person.lock('WITH (NOLOCK)').limit(5).offset(10).includes(:readers).references(:readers).load end end - + end - - + + end diff --git a/test/cases/query_cache_test_sqlserver.rb b/test/cases/query_cache_test_sqlserver.rb index b16babc71..857973c9b 100644 --- a/test/cases/query_cache_test_sqlserver.rb +++ b/test/cases/query_cache_test_sqlserver.rb @@ -5,18 +5,18 @@ class QueryCacheTestSqlserver < ActiveRecord::TestCase end class QueryCacheTest < ActiveRecord::TestCase - + COERCED_TESTS = [:test_cache_does_not_wrap_string_results_in_arrays] - + include SqlserverCoercedTest - + fixtures :tasks - + def test_coerced_cache_does_not_wrap_string_results_in_arrays Task.cache do assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end end - - + + end diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index c1f283b38..fe6c4558a 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -3,7 +3,7 @@ class RelationTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_merging_reorders_bind_params] + COERCED_TESTS = [:test_merging_reorders_bind_params] # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest fixtures :posts diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index 12968e1d7..2f9eabb55 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -17,27 +17,27 @@ class ResolverTest < ActiveRecord::TestCase def test_coerced_test_url_host_no_db spec = resolve 'sqlserver://foo?encoding=utf8' assert_equal({ - :adapter => "sqlserver", - :host => "foo", - :encoding => "utf8" }, spec) + adapter: "sqlserver", + host: "foo", + encoding: "utf8" }, spec) end def test_coerced_test_url_host_db spec = resolve 'sqlserver://foo/bar?encoding=utf8' assert_equal({ - :adapter => "sqlserver", - :database => "bar", - :host => "foo", - :encoding => "utf8" }, spec) + adapter: "sqlserver", + database: "bar", + host: "foo", + encoding: "utf8" }, spec) end def test_coerced_test_url_port spec = resolve 'sqlserver://foo:123?encoding=utf8' assert_equal({ - :adapter => "sqlserver", - :port => 123, - :host => "foo", - :encoding => "utf8" }, spec) + adapter: "sqlserver", + port: 123, + host: "foo", + encoding: "utf8" }, spec) end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 5663269b3..02cb13027 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -2,23 +2,23 @@ require 'stringio' class SchemaDumperTestSqlserver < ActiveRecord::TestCase - + setup :find_all_tables - + context 'For primary keys' do should 'honor nonstandards' do table_dump('movies') do |output| match = output.match(%r{create_table "movies"(.*)do}) - assert_not_nil(match, "nonstandardpk table not found") + assert_not_nil(match, "nonstandardpk table not found") assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" end end - + end - + context 'For integers' do - + should 'include limit constraint that match logic for smallint and bigint in #extract_limit' do table_dump('integer_limits') do |output| assert_match %r{c_int_1.*limit: 2}, output @@ -33,9 +33,9 @@ class SchemaDumperTestSqlserver < ActiveRecord::TestCase assert_match %r{c_int_8.*limit: 8}, output end end - + end - + context 'For strings' do should 'have varchar(max) dumped as text' do @@ -45,21 +45,21 @@ class SchemaDumperTestSqlserver < ActiveRecord::TestCase end end - - + + private - + def find_all_tables @all_tables ||= ActiveRecord::Base.connection.tables end - + def standard_dump(ignore_tables = []) stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = [*ignore_tables] ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) stream.string end - + def table_dump(*table_names) stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = @all_tables-table_names @@ -67,30 +67,30 @@ def table_dump(*table_names) yield stream.string stream.string end - + end class SchemaDumperTest < ActiveRecord::TestCase - + COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal] - + include SqlserverCoercedTest - + def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal output = standard_dump assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output end - + private - + def standard_dump stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = [] ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) stream.string end - + end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 40557d6dc..5d82804c4 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -3,71 +3,71 @@ require 'models_sqlserver/sql_server_natural_pk_data_schema' class SchemaTestSqlserver < ActiveRecord::TestCase - + setup do @connection = ActiveRecord::Base.connection end - + def read_schema_name(table_name) ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_table_schema(table_name) - end - + end + context 'When table is dbo schema' do - + should 'find primary key for tables with odd schema' do assert_equal 'legacy_id', @connection.primary_key('natural_pk_data') assert SqlServerNaturalPkData.columns_hash['legacy_id'].primary end - + end - + context 'When table is in non-dbo schema' do - + should 'work with #table_exists?' do assert @connection.table_exists?('test.sql_server_schema_natural_id') end - + should 'find primary key for tables with odd schema' do assert_equal 'legacy_id', @connection.primary_key('test.sql_server_schema_natural_id') assert SqlServerNaturalPkDataSchema.columns_hash['legacy_id'].primary end - + should "have only one identity column" do columns = @connection.columns("test.sql_server_schema_identity") - assert_equal 2, columns.size + assert_equal 2, columns.size assert_equal 1, columns.select{ |c| c.primary }.size - end - + end + should "read only column properties for table in specific schema" do test_columns = @connection.columns("test.sql_server_schema_columns") dbo_columns = @connection.columns("dbo.sql_server_schema_columns") columns = @connection.columns("sql_server_schema_columns") # This returns table from dbo schema - assert_equal 7, test_columns.size + 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.primary }.size assert_equal 1, dbo_columns.select{ |c| c.primary }.size assert_equal 1, columns.select{ |c| c.primary }.size - end - - should "return schema name in all cases" do + end + + should "return schema name in all cases" do assert_nil read_schema_name("table") assert_equal "schema1", read_schema_name("schema1.table") assert_equal "schema2", read_schema_name("database.schema2.table") assert_equal "schema3", read_schema_name("server.database.schema3.table") assert_equal "schema3", read_schema_name("[server].[database].[schema3].[table]") - end - + end + should "return correct varchar and nvarchar column limit (length) when table is in non dbo schema" do columns = @connection.columns("test.sql_server_schema_columns") - assert_equal 255, columns.find{|c| c.name == 'name'}.limit - assert_equal 1000, columns.find{|c| c.name == 'description'}.limit - assert_equal 255, columns.find{|c| c.name == 'n_name'}.limit - assert_equal 1000, columns.find{|c| c.name == 'n_description'}.limit + assert_equal 255, columns.find {|c| c.name == 'name'}.limit + assert_equal 1000, columns.find {|c| c.name == 'description'}.limit + assert_equal 255, columns.find {|c| c.name == 'n_name'}.limit + assert_equal 1000, columns.find {|c| c.name == 'n_description'}.limit end - + end - - + + end diff --git a/test/cases/scratch_test_sqlserver.rb b/test/cases/scratch_test_sqlserver.rb index 513bfbae2..0da1d9d57 100644 --- a/test/cases/scratch_test_sqlserver.rb +++ b/test/cases/scratch_test_sqlserver.rb @@ -6,7 +6,7 @@ class ScratchTestSqlserver < ActiveRecord::TestCase should 'pass' do assert true end - - + + end diff --git a/test/cases/session_test_sqlserver.rb b/test/cases/session_test_sqlserver.rb index 02f540213..5450e335b 100644 --- a/test/cases/session_test_sqlserver.rb +++ b/test/cases/session_test_sqlserver.rb @@ -4,15 +4,15 @@ module ActiveRecord class SessionStore class SessionTest < ActiveRecord::TestCase - + setup :reset_column_information_for_each_test protected - + def reset_column_information_for_each_test Session.reset_column_information end - + end end end diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 3a4c1e4f9..7c26ce6d8 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -20,7 +20,7 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase end should 'from prepared statement ...' do - plan = Car.where(:name => ',').limit(1).explain + plan = Car.where(name: ',').limit(1).explain assert plan.include?("SELECT TOP (1) [cars].* FROM [cars] WHERE [cars].[name] = N','") assert plan.include?("TOP EXPRESSION"), 'make sure we do not showplan the sp_executesql' assert plan.include?("Clustered Index Scan"), 'make sure we do not showplan the sp_executesql' @@ -32,7 +32,7 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase should 'use simple table printer' do with_showplan_option('SHOWPLAN_TEXT') do - plan = Car.where(:id => 1).explain + plan = Car.where(id: 1).explain assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' end @@ -44,7 +44,7 @@ class ShowplanTestSqlserver < ActiveRecord::TestCase should 'show formatted xml' do with_showplan_option('SHOWPLAN_XML') do - plan = Car.where(:id => 1).explain + plan = Car.where(id: 1).explain assert plan.include?('ShowPlanXML') end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 249571c20..92f711d77 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -9,13 +9,13 @@ require 'models_sqlserver/sql_server_tinyint_pk' require 'models_sqlserver/string_default' class SpecificSchemaTestSqlserver < ActiveRecord::TestCase - + should 'be able to complex count tables with no primary key' do NoPkData.delete_all - 10.times { |n| NoPkData.create! :name => "Test#{n}" } - assert_equal 1, NoPkData.where(:name => 'Test5').count + 10.times { |n| NoPkData.create! name: "Test#{n}" } + assert_equal 1, NoPkData.where(name: 'Test5').count end - + should 'quote table names properly even when they are views' do obj = SqlServerQuotedTable.create! assert_nothing_raised { SqlServerQuotedTable.first } @@ -29,7 +29,7 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase default = StringDefault.new assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default end - + should 'default strings before save' do default = StringDefault.new assert_equal nil, default.string_with_null_default @@ -48,47 +48,47 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase assert_equal 'NULL', default.string_with_pretend_null_three assert_equal '(NULL)', default.string_with_pretend_null_four end - + context 'Testing edge case schemas' do - + setup do @edge_class = SqlServerEdgeSchema end - + context 'with tinyint primary key' do - + should 'work with identity inserts and finders' do - record = SqlServerTinyintPk.new :name => '1' + record = SqlServerTinyintPk.new name: '1' record.id = 1 record.save! assert_equal record, SqlServerTinyintPk.find(1) end - + end - + context 'with natural primary keys' do should 'work with identity inserts' do - record = SqlServerNaturalPkData.new :name => 'Test', :description => 'Natural identity inserts.' + record = SqlServerNaturalPkData.new name: 'Test', description: 'Natural identity inserts.' record.id = '12345ABCDE' assert record.save assert_equal '12345ABCDE', record.reload.id end - + should 'work with identity inserts when the key is an int' do - record = SqlServerNaturalPkIntData.new :name => 'Test', :description => 'Natural identity inserts.' + record = SqlServerNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.' record.id = 12 assert record.save assert_equal 12, record.reload.id end - + should 'use primary key for row table order in pagination sql' do - sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ + sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5).load } end end - + context 'with special quoted column' do should 'work as normal' do @@ -104,20 +104,20 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'create record using a custom attribute reader and be able to load it back in' do value = 'Saved value into a column that has a space in the name.' - record = @edge_class.create! :with_spaces => value + record = @edge_class.create! with_spaces: value assert_equal value, @edge_class.find(record.id).with_spaces end - + end - + context 'with description column' do setup do - @da = @edge_class.create! :description => 'A' - @db = @edge_class.create! :description => 'B' - @dc = @edge_class.create! :description => 'C' + @da = @edge_class.create! description: 'A' + @db = @edge_class.create! description: 'B' + @dc = @edge_class.create! description: 'C' end - + teardown { @edge_class.delete_all } should 'allow all sorts of ordering without adapter munging it up' do @@ -129,14 +129,14 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase end end - + context 'with bigint column' do setup do @b5k = 5000 - @bi5k = @edge_class.create! :bigint => @b5k, :description => 'Five Thousand' + @bi5k = @edge_class.create! bigint: @b5k, description: 'Five Thousand' @bnum = 9_000_000_000_000_000_000 - @bimjr = @edge_class.create! :bigint => @bnum, :description => 'Close to max bignum' + @bimjr = @edge_class.create! bigint: @bnum, description: 'Close to max bignum' end should 'can find by biginit' do @@ -147,25 +147,25 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase end end - + context 'with tinyint column' do setup do - @tiny1 = @edge_class.create! :tinyint => 1 - @tiny255 = @edge_class.create! :tinyint => 255 + @tiny1 = @edge_class.create! tinyint: 1 + @tiny255 = @edge_class.create! tinyint: 255 end should 'not treat tinyint like boolean as mysql does' do assert_equal 1, @edge_class.find_by_tinyint(1).tinyint assert_equal 255, @edge_class.find_by_tinyint(255).tinyint end - + should 'throw an error when going out of our tiny int bounds' do - assert_raise(ActiveRecord::StatementInvalid) { @edge_class.create! :tinyint => 256 } + assert_raise(ActiveRecord::StatementInvalid) { @edge_class.create! tinyint: 256 } end - + end - + context 'with uniqueidentifier column' do setup do @@ -174,10 +174,10 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase end should 'allow a simple insert and read of a column without a default function' do - obj = @edge_class.create! :guid => @newid + obj = @edge_class.create! guid: @newid assert_equal @newid, @edge_class.find(obj.id).guid end - + should 'record the default function name in the column definition but still show a nil real default, will use one day for insert/update' do newid_column = @edge_class.columns_hash['guid_newid'] assert newid_column.default_function.present? @@ -188,7 +188,7 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase assert_nil newseqid_column.default assert_equal 'newsequentialid()', newseqid_column.default_function end - + should 'use model callback to set get a new guid' do obj = @edge_class.new obj.new_id_setting = true @@ -197,23 +197,23 @@ class SpecificSchemaTestSqlserver < ActiveRecord::TestCase end end - + context 'with strange table names' do - + should 'handle dollar symbols' do SqlServerDollarTableName.new.save SqlServerDollarTableName.limit(20).offset(1) end - + end - + end - - + + protected - + def assert_guid(guid) assert_match %r|\w{8}-\w{4}-\w{4}-\w{4}-\w{12}|, guid end - + end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index a5aed27b5..026efb196 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -42,7 +42,7 @@ module SqlserverCoercedTest def self.included(base) base.extend ClassMethods end - + module ClassMethods def self.extended(base) @@ -52,11 +52,11 @@ def self.extended(base) end end end - + def coerced_tests self.const_get(:COERCED_TESTS) rescue nil end - + def method_added(method) if coerced_tests && coerced_tests.include?(method) undefine_and_puts(method) @@ -67,7 +67,7 @@ def undefine_and_puts(method) result = undef_method(method) rescue nil STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? end - + end end diff --git a/test/cases/sqlserver_test_case.rb b/test/cases/sqlserver_test_case.rb index 489a41034..726da4c3e 100644 --- a/test/cases/sqlserver_test_case.rb +++ b/test/cases/sqlserver_test_case.rb @@ -4,7 +4,7 @@ # This is a temporary hack until we can just get the sqlserver_ignored regex in rails ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| if listener.inspect =~ /ActiveRecord::SQLCounter/ - ActiveSupport::Notifications.unsubscribe(listener) + ActiveSupport::Notifications.unsubscribe(listener) end end diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb index ad3628674..4dcaeac66 100644 --- a/test/cases/table_name_test_sqlserver.rb +++ b/test/cases/table_name_test_sqlserver.rb @@ -6,22 +6,22 @@ class SqlServerRailsOrders < ActiveRecord::Base end class TableNameTestSqlserver < ActiveRecord::TestCase - + self.use_transactional_fixtures = false - + setup do Order.table_name = '[orders]' Order.reset_column_information end - + should 'load columns with escaped table name for model' do assert_equal 4, Order.columns.length end - + should 'not re-escape table name if it is escaped already for SQL queries' do assert_sql(/SELECT \[orders\]\.\* FROM \[orders\]/) { Order.all.load } end - + context 'Table scoped to user.table_name' do setup do @@ -33,6 +33,6 @@ class TableNameTestSqlserver < ActiveRecord::TestCase end end - - + + end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 0ab64a163..36976b287 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -3,27 +3,27 @@ require 'models/developer' class TransactionTestSqlserver < ActiveRecord::TestCase - + self.use_transactional_fixtures = false - + setup :delete_ships - + context 'Testing transaction basics' do - + should 'allow ActiveRecord::Rollback to work in 1 transaction block' do Ship.transaction do - Ship.create! :name => 'Black Pearl' + Ship.create! name: 'Black Pearl' raise ActiveRecord::Rollback end assert_no_ships end - + should 'allow nested transactions to totally rollback' do begin Ship.transaction do - Ship.create! :name => 'Black Pearl' + Ship.create! name: 'Black Pearl' Ship.transaction do - Ship.create! :name => 'Flying Dutchman' + Ship.create! name: 'Flying Dutchman' raise 'HELL' end end @@ -33,16 +33,16 @@ class TransactionTestSqlserver < ActiveRecord::TestCase end end - + protected - + def delete_ships Ship.delete_all end - + def assert_no_ships assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}" end - + end diff --git a/test/cases/unicode_test_sqlserver.rb b/test/cases/unicode_test_sqlserver.rb index 18852ae05..368019f80 100644 --- a/test/cases/unicode_test_sqlserver.rb +++ b/test/cases/unicode_test_sqlserver.rb @@ -2,27 +2,27 @@ require 'cases/sqlserver_helper' class UnicodeTestSqlserver < ActiveRecord::TestCase - - + + context 'Testing basic saves and unicode limits' do should 'save and reload simple nchar string' do - assert nchar_data = SqlServerUnicode.create!(:nchar => 'A') + assert nchar_data = SqlServerUnicode.create!(nchar: 'A') assert_equal 'A', SqlServerUnicode.find(nchar_data.id).nchar end - + should 'save and reload simple nvarchar(max) string' do test_string = 'Ken Collins' - assert nvarcharmax_data = SqlServerUnicode.create!(:nvarchar_max => test_string) + assert nvarcharmax_data = SqlServerUnicode.create!(nvarchar_max: test_string) assert_equal test_string, SqlServerUnicode.find(nvarcharmax_data.id).nvarchar_max end should 'not work with ANSI_WARNINGS for string truncation' do - SqlServerUnicode.create!(:nchar_10 => '01234567891') + SqlServerUnicode.create!(nchar_10: '01234567891') end end - + context 'Testing unicode data' do setup do @@ -30,7 +30,7 @@ class UnicodeTestSqlserver < ActiveRecord::TestCase end should 'insert and retrieve unicode data' do - assert data = SqlServerUnicode.create!(:nvarchar => @unicode_data) + assert data = SqlServerUnicode.create!(nvarchar: @unicode_data) if connection_mode_dblib? assert_equal "一二34五六", data.reload.nvarchar elsif connection_mode_odbc? @@ -42,7 +42,7 @@ class UnicodeTestSqlserver < ActiveRecord::TestCase end end - - - + + + end diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/uniqueness_validation_test_sqlserver.rb index 14c113ed6..de7429ed2 100644 --- a/test/cases/uniqueness_validation_test_sqlserver.rb +++ b/test/cases/uniqueness_validation_test_sqlserver.rb @@ -14,29 +14,29 @@ class UniquenessValidationTestSqlserver < ActiveRecord::TestCase end class UniquenessValidationTest < ActiveRecord::TestCase - + COERCED_TESTS = [:test_validate_uniqueness_with_limit_and_utf8] - + include SqlserverCoercedTest - + # I guess most databases just truncate a string when inserting. To pass this test we do a few things. - # First, we make sure the type is unicode safe, second we extend the limit to well beyond what is - # needed. At the top we make sure to auto truncate the :title string like other databases would do + # First, we make sure the type is unicode safe, second we extend the limit to well beyond what is + # needed. At the top we make sure to auto truncate the :title string like other databases would do # automatically. - # - # "一二三四五".mb_chars.size # => 5 - # "一二三四五六七八".mb_chars.size # => 8 + # + # "一二三四五".mb_chars.size # => 5 + # "一二三四五六七八".mb_chars.size # => 8 # "一二三四五六七八".mb_chars.to(4).to_s # => "一二三四五" - + def test_coerced_validate_uniqueness_with_limit_and_utf8 - Event.connection.change_column :events, :title, :nvarchar, :limit => 30 + Event.connection.change_column :events, :title, :nvarchar, limit: 30 Event.reset_column_information # Now the actual test copied from core. - e1 = Event.create(:title => "一二三四五") + e1 = Event.create(title: "一二三四五") assert e1.valid?, "Could not create an event with a unique, 5 character title" - e2 = Event.create(:title => "一二三四五六七八") + e2 = Event.create(title: "一二三四五六七八") assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" end - + end diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index eeab6d807..45d0e21fc 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -1,11 +1,11 @@ class TableWillNeverBeCreated < ActiveRecord::Migration - + def self.up create_table(:sqlserver_trans_table1) { } create_table(:sqlserver_trans_table2) { raise ActiveRecord::StatementInvalid } end - + def self.down end - + end diff --git a/test/models_sqlserver/sql_server_edge_schema.rb b/test/models_sqlserver/sql_server_edge_schema.rb index eeaa27ddc..1d4131344 100644 --- a/test/models_sqlserver/sql_server_edge_schema.rb +++ b/test/models_sqlserver/sql_server_edge_schema.rb @@ -4,14 +4,14 @@ class SqlServerEdgeSchema < ActiveRecord::Base def with_spaces read_attribute :'with spaces' end - + def with_spaces=(value) write_attribute :'with spaces', value end - + protected def set_new_id self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting end - + end \ No newline at end of file diff --git a/test/profile/connection_profile_case.rb b/test/profile/connection_profile_case.rb index 78ee41f18..90a0a4de1 100644 --- a/test/profile/connection_profile_case.rb +++ b/test/profile/connection_profile_case.rb @@ -3,28 +3,28 @@ require 'models/reply' class ConnectionProfileCase < ActiveRecord::TestCase - + fixtures :topics - + setup do @connection = ActiveRecord::Base.connection end - + def test_select select_statement = "SELECT [topics].* FROM [topics]" ruby_profile :connection_select do 1000.times { @connection.send :select, select_statement } end end - + def test_select_one select_statement = "SELECT [topics].* FROM [topics]" ruby_profile :connection_select_one do 1000.times { @connection.select_one(select_statement) } end end - - + + end diff --git a/test/profile/finder_profile_case.rb b/test/profile/finder_profile_case.rb index 7fb0cc9ea..a8eadfc50 100644 --- a/test/profile/finder_profile_case.rb +++ b/test/profile/finder_profile_case.rb @@ -3,16 +3,16 @@ require 'models/reply' class FinderProfileCase < ActiveRecord::TestCase - + fixtures :topics - + def test_find_all ruby_profile :finder_find_all do 1000.times { Topic.all } end end - - + + end diff --git a/test/profile/gc_profile_case.rb b/test/profile/gc_profile_case.rb index b75ce4511..08416c520 100644 --- a/test/profile/gc_profile_case.rb +++ b/test/profile/gc_profile_case.rb @@ -4,49 +4,49 @@ require 'models/reply' class GcProfileCase < ActiveRecord::TestCase - + fixtures :topics - + setup do create_mass_topics unless @created_mass_topics @connection = ActiveRecord::Base.connection @select_statement = "SELECT [topics].* FROM [topics]" end - + def test_coercion bench_allocations('coercion') do - Topic.all(:limit => 100).each do |t| + Topic.all(limit: 100).each do |t| t.attributes.keys.each do |k| t.send(k.to_sym) end end end end - + def test_select bench_allocations('select') do @connection.send :select, @select_statement end end - + def test_select_one bench_allocations('select_one') do 100.times { @connection.select_one(@select_statement) } end end - + def test_columns bench_allocations('columns') do - 100.times do + 100.times do Topic.reset_column_information Topic.columns end end end - - + + protected - + def create_mass_topics GC::Profiler.clear GC::Profiler.disable @@ -57,7 +57,7 @@ def create_mass_topics GC::Profiler.enable GC::Profiler.clear end - + def bench_allocations(feature, iterations=10, &blk) puts "\nGC overhead for #{feature}" GC::Profiler.clear @@ -66,7 +66,7 @@ def bench_allocations(feature, iterations=10, &blk) GC::Profiler.report(STDOUT) GC::Profiler.disable end - + end diff --git a/test/profile/query_plan_complex.rb b/test/profile/query_plan_complex.rb index 80a46a35a..05d8a3641 100755 --- a/test/profile/query_plan_complex.rb +++ b/test/profile/query_plan_complex.rb @@ -2,9 +2,9 @@ Query Plan Complex ================== -Author: Ken Collins -Date: May 22, 2011 -Summary: Benchmark complex cached query plan reuse in SQL Server. +Author: Ken Collins +Date: May 22, 2011 +Summary: Benchmark complex cached query plan reuse in SQL Server. System Information ------------------ @@ -34,18 +34,18 @@ summary 'Benchmark complex cached query plan reuse in SQL Server.' reps 500 -@client = TinyTds::Client.new :host => 'mc2008', :username => 'rails' +@client = TinyTds::Client.new host: 'mc2008', username: 'rails' measure "Simple - Dynamic SQL" do sql = " - SELECT TOP (1) [companies].id - FROM [companies] - LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] - AND [clients_using_primary_keys_companies].[type] IN (N'Client', N'SpecialClient', N'VerySpecialClient') - WHERE [companies].[type] IN (N'Firm') - AND [companies].[id] = #{rand(1000000)} - GROUP BY [companies].id + SELECT TOP (1) [companies].id + FROM [companies] + LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] + AND [clients_using_primary_keys_companies].[type] IN (N'Client', N'SpecialClient', N'VerySpecialClient') + WHERE [companies].[type] IN (N'Firm') + AND [companies].[id] = #{rand(1000000)} + GROUP BY [companies].id ORDER BY MIN(clients_using_primary_keys_companies.name)" @client.execute(sql).do end @@ -53,15 +53,15 @@ measure "Simple - Query Plan Reuse" do sql = " EXEC sp_executesql N' - SELECT TOP (1) [companies].id - FROM [companies] - LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] - AND [clients_using_primary_keys_companies].[type] IN (N''Client'', N''SpecialClient'', N''VerySpecialClient'') - WHERE [companies].[type] IN (N''Firm'') - AND [companies].[id] = @0 - GROUP BY [companies].id - ORDER BY MIN(clients_using_primary_keys_companies.name)', - N'@0 int', + SELECT TOP (1) [companies].id + FROM [companies] + LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] + AND [clients_using_primary_keys_companies].[type] IN (N''Client'', N''SpecialClient'', N''VerySpecialClient'') + WHERE [companies].[type] IN (N''Firm'') + AND [companies].[id] = @0 + GROUP BY [companies].id + ORDER BY MIN(clients_using_primary_keys_companies.name)', + N'@0 int', @0 = #{rand(1000000)}" @client.execute(sql).do end diff --git a/test/profile/query_plan_simple.rb b/test/profile/query_plan_simple.rb index a944ae8a4..dbf6146a9 100755 --- a/test/profile/query_plan_simple.rb +++ b/test/profile/query_plan_simple.rb @@ -2,9 +2,9 @@ Query Plan Simple ================= -Author: Ken Collins -Date: May 22, 2011 -Summary: Benchmark simple cached query plan reuse in SQL Server. +Author: Ken Collins +Date: May 22, 2011 +Summary: Benchmark simple cached query plan reuse in SQL Server. System Information ------------------ @@ -34,7 +34,7 @@ summary 'Benchmark simple cached query plan reuse in SQL Server.' reps 500 -@client = TinyTds::Client.new :host => 'mc2008', :username => 'rails' +@client = TinyTds::Client.new host: 'mc2008', username: 'rails' measure "Simple - Dynamic SQL" do @@ -44,4 +44,3 @@ measure "Simple - Query Plan Reuse" do @client.execute("EXEC sp_executesql N'SELECT TOP(1) * FROM [posts] WHERE [id] = @0', N'@0 int', @0 = #{rand(1000000)}").do end - diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 37e6150cc..131001f90 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,39 +1,39 @@ ActiveRecord::Schema.define do - - create_table :UPPER_TESTS, :force => true do |t| + + create_table :UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string t.column :COLUMN2, :integer end - - create_table :float_data, :force => true do |t| + + create_table :float_data, force: true do |t| t.float :temperature - t.float :temperature_8, :limit => 8 - t.float :temperature_24, :limit => 24 - t.float :temperature_32, :limit => 32 - t.float :temperature_53, :limit => 53 + t.float :temperature_8, limit: 8 + t.float :temperature_24, limit: 24 + t.float :temperature_32, limit: 32 + t.float :temperature_53, limit: 53 end - - create_table :table_with_real_columns, :force => true do |t| + + create_table :table_with_real_columns, force: true do |t| t.column :real_number, :real end - - create_table :defaults, :force => true do |t| - t.column :positive_integer, :integer, :default => 1 - t.column :negative_integer, :integer, :default => -1 - t.column :decimal_number, :decimal, :precision => 3, :scale => 2, :default => 2.78 + + create_table :defaults, force: true do |t| + t.column :positive_integer, :integer, default: 1 + t.column :negative_integer, :integer, default: -1 + t.column :decimal_number, :decimal, precision: 3, scale: 2, default: 2.78 end - - create_table :string_defaults, :force => true do |t| - t.column :string_with_null_default, :string, :default => nil - t.column :string_with_pretend_null_one, :string, :default => 'null' - t.column :string_with_pretend_null_two, :string, :default => '(null)' - t.column :string_with_pretend_null_three, :string, :default => 'NULL' - t.column :string_with_pretend_null_four, :string, :default => '(NULL)' - t.column :string_with_pretend_paren_three, :string, :default => '(3)' - t.column :string_with_multiline_default, :string, :default => "Some long default with a\nnew line." + + create_table :string_defaults, force: true do |t| + t.column :string_with_null_default, :string, default: nil + t.column :string_with_pretend_null_one, :string, default: 'null' + t.column :string_with_pretend_null_two, :string, default: '(null)' + t.column :string_with_pretend_null_three, :string, default: 'NULL' + t.column :string_with_pretend_null_four, :string, default: '(NULL)' + t.column :string_with_pretend_paren_three, :string, default: '(3)' + t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line." end - - create_table :sql_server_chronics, :force => true do |t| + + create_table :sql_server_chronics, force: true do |t| t.column :date, :date t.column :time, :time t.column :datetime, :datetime @@ -41,43 +41,43 @@ t.column :ss_timestamp, :ss_timestamp unless sqlserver_azure? t.column :smalldatetime, :smalldatetime end - - create_table(:fk_test_has_fks, :force => true) { |t| t.column(:fk_id, :integer, :null => false) } - create_table(:fk_test_has_pks, :force => true) { } + + create_table(:fk_test_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } + create_table(:fk_test_has_pks, force: true) { } execute <<-ADDFKSQL - ALTER TABLE fk_test_has_fks + ALTER TABLE fk_test_has_fks ADD CONSTRAINT FK__fk_test_has_fk_fk_id - FOREIGN KEY (#{quote_column_name('fk_id')}) + FOREIGN KEY (#{quote_column_name('fk_id')}) REFERENCES #{quote_table_name('fk_test_has_pks')} (#{quote_column_name('id')}) ADDFKSQL - - create_table :sql_server_unicodes, :force => true do |t| + + create_table :sql_server_unicodes, force: true do |t| t.column :nchar, :nchar t.column :nvarchar, :nvarchar t.column :ntext, :ntext - t.column :ntext_10, :ntext, :limit => 10 - t.column :nchar_10, :nchar, :limit => 10 - t.column :nvarchar_100, :nvarchar, :limit => 100 - t.column :nvarchar_max, :nvarchar_max - t.column :nvarchar_max_10, :nvarchar_max, :limit => 10 + t.column :ntext_10, :ntext, limit: 10 + t.column :nchar_10, :nchar, limit: 10 + t.column :nvarchar_100, :nvarchar, limit: 100 + t.column :nvarchar_max, :nvarchar_max + t.column :nvarchar_max_10, :nvarchar_max, limit: 10 end - - create_table :sql_server_strings, :force => true do |t| + + create_table :sql_server_strings, force: true do |t| t.column :char, :char - t.column :char_10, :char, :limit => 10 - t.column :varchar_max, :varchar_max - t.column :varchar_max_10, :varchar_max, :limit => 10 + t.column :char_10, :char, limit: 10 + t.column :varchar_max, :varchar_max + t.column :varchar_max_10, :varchar_max, limit: 10 end - - create_table :sql_server_binary_types, :force => true do |t| + + create_table :sql_server_binary_types, force: true do |t| # TODO: Add some different native binary types and test. end - create_table 'my$strange_table', :force => true do |t| + create_table 'my$strange_table', force: true do |t| t.column :number, :real end - - create_table :sql_server_edge_schemas, :force => true do |t| + + create_table :sql_server_edge_schemas, force: true do |t| t.string :description t.column :bigint, :bigint t.column :tinyint, :tinyint @@ -87,11 +87,11 @@ end execute %|ALTER TABLE [sql_server_edge_schemas] ADD [guid_newid] uniqueidentifier DEFAULT NEWID();| execute %|ALTER TABLE [sql_server_edge_schemas] ADD [guid_newseqid] uniqueidentifier DEFAULT NEWSEQUENTIALID();| unless sqlserver_azure? - - create_table :no_pk_data, :force => true, :id => false do |t| + + create_table :no_pk_data, force: true, id: false do |t| t.string :name end - + # http://blogs.msdn.com/b/craigfr/archive/2008/03/19/ranking-functions-row-number.aspx execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'order_row_number') DROP TABLE order_row_number" execute <<-ORDERROWNUMBERSQL @@ -108,17 +108,17 @@ INSERT [order_row_number] VALUES (1, 6, 3) INSERT [order_row_number] VALUES (1, 8, 1) ORDERROWNUMBERSQL - + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'natural_pk_data') DROP TABLE natural_pk_data" execute <<-NATURALPKTABLESQL CREATE TABLE natural_pk_data( - parent_id int, - name nvarchar(255), - description nvarchar(1000), - legacy_id nvarchar(10) NOT NULL PRIMARY KEY + parent_id int, + name nvarchar(255), + description nvarchar(1000), + legacy_id nvarchar(10) NOT NULL PRIMARY KEY ) NATURALPKTABLESQL - + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'natural_pk_int_data') DROP TABLE natural_pk_int_data" execute <<-NATURALPKINTTABLESQL CREATE TABLE natural_pk_int_data( @@ -128,7 +128,7 @@ description nvarchar(1000) ) NATURALPKINTTABLESQL - + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'tinyint_pk_table') DROP TABLE tinyint_pk_table" execute <<-TINYITPKTABLE CREATE TABLE tinyint_pk_table( @@ -136,14 +136,14 @@ name nvarchar(255) ) TINYITPKTABLE - - create_table 'quoted-table', :force => true do |t| + + create_table 'quoted-table', force: true do |t| end execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'quoted-view1') DROP VIEW [quoted-view1]" execute "CREATE VIEW [quoted-view1] AS SELECT * FROM [quoted-table]" execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'quoted-view2') DROP VIEW [quoted-view2]" execute "CREATE VIEW [quoted-view2] AS \n /*#{'x'*4000}}*/ \n SELECT * FROM [quoted-table]" - + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'customers_view') DROP VIEW customers_view" execute <<-CUSTOMERSVIEW CREATE VIEW customers_view AS @@ -157,7 +157,7 @@ SELECT id, string_with_pretend_null_one as pretend_null FROM string_defaults STRINGDEFAULTSVIEW - + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_big_view') DROP VIEW string_defaults_big_view" execute <<-STRINGDEFAULTSBIGVIEW CREATE VIEW string_defaults_big_view AS @@ -168,44 +168,44 @@ # Another schema. - - create_table :sql_server_schema_columns, :force => true do |t| + + create_table :sql_server_schema_columns, force: true do |t| t.column :field1 , :integer end - + execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test') EXEC sp_executesql N'CREATE SCHEMA test'" execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_columns' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_columns" execute <<-SIMILIARTABLEINOTHERSCHEMA CREATE TABLE test.sql_server_schema_columns( - id int IDENTITY NOT NULL primary key, - filed_1 int, - field_2 int, - name varchar(255), - description varchar(1000), - n_name nvarchar(255), - n_description nvarchar(1000) + id int IDENTITY NOT NULL primary key, + filed_1 int, + field_2 int, + name varchar(255), + description varchar(1000), + n_name nvarchar(255), + n_description nvarchar(1000) ) SIMILIARTABLEINOTHERSCHEMA - + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_identity' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_identity" execute <<-SIMILIARTABLEINOTHERSCHEMA CREATE TABLE test.sql_server_schema_identity( - id int IDENTITY NOT NULL primary key, - filed_1 int + id int IDENTITY NOT NULL primary key, + filed_1 int ) SIMILIARTABLEINOTHERSCHEMA - + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_natural_id' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_natural_id" execute <<-NATURALPKTABLESQLINOTHERSCHEMA CREATE TABLE test.sql_server_schema_natural_id( - parent_id int, - name nvarchar(255), - description nvarchar(1000), - legacy_id nvarchar(10) NOT NULL PRIMARY KEY, + parent_id int, + name nvarchar(255), + description nvarchar(1000), + legacy_id nvarchar(10) NOT NULL PRIMARY KEY, ) NATURALPKTABLESQLINOTHERSCHEMA - - + + # Azure needs clustered indexes if sqlserver_azure? execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_schema_migrations_version') CREATE CLUSTERED INDEX [idx_schema_migrations_version] ON [schema_migrations] ([version])" @@ -220,11 +220,11 @@ execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_lessons_students_lid_sid') CREATE CLUSTERED INDEX [idx_lessons_students_lid_sid] ON [lessons_students] ([lesson_id],[student_id])" execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_mateys_pid_tid') CREATE CLUSTERED INDEX [idx_mateys_pid_tid] ON [mateys] ([pirate_id],[target_id])" execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_minivans_minivan_id') CREATE CLUSTERED INDEX [idx_minivans_minivan_id] ON [minivans] ([minivan_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_pirates_paid_pid') CREATE CLUSTERED INDEX [idx_parrots_pirates_paid_pid] ON [parrots_pirates] ([parrot_id],[pirate_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_treasures_pid_tid') CREATE CLUSTERED INDEX [idx_parrots_treasures_pid_tid] ON [parrots_treasures] ([parrot_id],[treasure_id])" + execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_pirates_paid_pid') CREATE CLUSTERED INDEX [idx_parrots_pirates_paid_pid] ON [parrots_pirates] ([parrot_id],[pirate_id])" + execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_treasures_pid_tid') CREATE CLUSTERED INDEX [idx_parrots_treasures_pid_tid] ON [parrots_treasures] ([parrot_id],[treasure_id])" execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_speedometers_speedometer_id') CREATE CLUSTERED INDEX [idx_speedometers_speedometer_id] ON [speedometers] ([speedometer_id])" execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_subscribers_nick') CREATE CLUSTERED INDEX [idx_subscribers_nick] ON [subscribers] ([nick])" execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_countries_treaties_cid_tid') CREATE CLUSTERED INDEX [idx_countries_treaties_cid_tid] ON [countries_treaties] ([country_id],[treaty_id])" end - + end From f16b45b0dcdc51f835407a7ef613c046a6b684a2 Mon Sep 17 00:00:00 2001 From: wbond Date: Mon, 5 May 2014 16:59:00 -0400 Subject: [PATCH 0146/1412] Fixed select_rows() to actual support binds via do_exec_query() --- .../sqlserver/database_statements.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 43d278e50..35a74ae3c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -4,7 +4,7 @@ module Sqlserver module DatabaseStatements def select_rows(sql, name = nil, binds = []) - raw_select sql, name, binds, fetch: :rows + do_exec_query sql, name, binds, fetch: :rows end def execute(sql, name = nil) @@ -327,7 +327,13 @@ def do_execute(sql, name = 'SQL') end end - def do_exec_query(sql, name, binds) + def do_exec_query(sql, name, binds, options = {}) + # This allows non-AR code to utilize the binds + # handling code, e.g. select_rows() + if options[:fetch] != :rows + options[:ar_result] = true + end + explaining = name == 'EXPLAIN' names_and_types = [] params = [] @@ -358,7 +364,7 @@ def do_exec_query(sql, name, binds) sql = "EXEC sp_executesql #{quote(sql)}" sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty? end - raw_select sql, name, binds, ar_result: true + raw_select sql, name, binds, options end def raw_connection_do(sql) From ad5431a5b64fb142b64d87a96bfa914390f478b5 Mon Sep 17 00:00:00 2001 From: "Romulo A. Ceccon" Date: Tue, 6 May 2014 17:56:22 -0300 Subject: [PATCH 0147/1412] support for partial/filtered indices on SQL Server 2008 --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 4e4edaa8f..f63319fca 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -252,6 +252,10 @@ def supports_index_sort_order? true end + def supports_partial_index? + @database_year >= 2008 + end + def supports_explain? true end From 6e227013a6625388ad8b7c42546939ed39e08f1e Mon Sep 17 00:00:00 2001 From: Alexandros Giouzenis Date: Fri, 9 May 2014 19:46:46 +0300 Subject: [PATCH 0148/1412] Rails 4 compatible UUID Support --- .../sqlserver/database_statements.rb | 11 +- .../connection_adapters/sqlserver/quoting.rb | 11 ++ .../sqlserver/schema_creation.rb | 29 ++++ .../sqlserver/schema_statements.rb | 15 ++- .../sqlserver/table_definition.rb | 25 ++++ .../connection_adapters/sqlserver_adapter.rb | 13 +- test/cases/uuid_test_sqlserver.rb | 127 ++++++++++++++++++ 7 files changed, 220 insertions(+), 11 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/schema_creation.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/table_definition.rb create mode 100644 test/cases/uuid_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 35a74ae3c..359e5e445 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -305,14 +305,15 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"# unless binds.empty? + sql = + if pk + sql.insert(sql.index(/ (DEFAULT )?VALUES/), " OUTPUT inserted.#{pk}") + else + "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" + end super end - def last_inserted_id(result) - super || select_value('SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident') - end - # === SQLServer Specific ======================================== # def valid_isolation_levels diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 1aa1bd12a..bac830173 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -12,6 +12,8 @@ def quote(value, column = nil) value.to_i.to_s elsif column && column.type == :binary column.class.string_to_binary(value) + elsif column && [:uuid, :uniqueidentifier].include?(column.type) + "'#{quote_string(value)}'" elsif value.is_utf8? || (column && column.type == :string) "#{quoted_string_prefix}'#{quote_string(value)}'" else @@ -52,6 +54,15 @@ def quote_database_name(name) schema_cache.quote_name(name, false) end + # Does not quote function default values for UUID columns + def quote_default_value(value, column) + if column.type == :uuid && value =~ /\(\)/ + value + else + quote(value) + end + end + def substitute_at(column, index) if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' nil diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb new file mode 100644 index 000000000..920d8585e --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -0,0 +1,29 @@ +module ActiveRecord + module ConnectionAdapters + module Sqlserver + class SchemaCreation < AbstractAdapter::SchemaCreation + private + + def visit_ColumnDefinition(o) + sql = super + if o.primary_key? && o.type == :uuid + sql << " PRIMARY KEY " + add_column_options!(sql, column_options(o)) + end + sql + end + + def add_column_options!(sql, options) + column = options.fetch(:column) { return super } + if [:uniqueidentifier, :uuid].include?(column.type) && options[:default] =~ /\(\)/ + sql << " DEFAULT #{options.delete(:default)}" + super + else + super + end + end + end + end + end +end + diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c3216ebd1..66a4b18f8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -80,11 +80,11 @@ def change_column(table_name, column_name, type, options = {}) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } remove_indexes(table_name, column_name) end - sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(options[:default])} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? + sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false if options_include_default?(options) - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}" + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(options[:default], column_object)} FOR #{quote_column_name(column_name)}" end # Add any removed indexes back @@ -96,7 +96,9 @@ def change_column(table_name, column_name, type, options = {}) def change_column_default(table_name, column_name, default) remove_default_constraint(table_name, column_name) - do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}" + column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } + do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(default, column_object)} FOR #{quote_column_name(column_name)}" + schema_cache.clear_table_cache!(table_name) end def rename_column(table_name, column_name, new_column_name) @@ -165,6 +167,7 @@ def initialize_native_database_types date: { name: native_date_database_type }, binary: { name: native_binary_database_type }, boolean: { name: 'bit' }, + uuid: { name: 'uniqueidentifier'}, # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them. char: { name: 'char' }, varchar_max: { name: 'varchar(max)' }, @@ -386,6 +389,12 @@ def set_identity_insert(table_name, enable = true) def identity_column(table_name) schema_cache.columns(table_name).find(&:is_identity?) end + + private + + def create_table_definition(name, temporary, options) + TableDefinition.new native_database_types, name, temporary, options + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb new file mode 100644 index 000000000..18d119c7e --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -0,0 +1,25 @@ +module ActiveRecord + module ConnectionAdapters + module Sqlserver + class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + def uuid(name, options = {}) + column(name, 'uniqueidentifier', options) + end + + def primary_key(name, type = :primary_key, options = {}) + return super unless type == :uuid + options[:default] = options.fetch(:default, 'NEWID()') + options[:primary_key] = true + column name, type, options + end + + def column(name, type = nil, options = {}) + super + self + end + end + end + end +end + + diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 4e4edaa8f..0bc59cfe7 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -14,8 +14,10 @@ require 'active_record/connection_adapters/sqlserver/database_statements' require 'active_record/connection_adapters/sqlserver/errors' require 'active_record/connection_adapters/sqlserver/schema_cache' +require 'active_record/connection_adapters/sqlserver/schema_creation' require 'active_record/connection_adapters/sqlserver/schema_statements' require 'active_record/connection_adapters/sqlserver/showplan' +require 'active_record/connection_adapters/sqlserver/table_definition' require 'active_record/connection_adapters/sqlserver/quoting' require 'active_record/connection_adapters/sqlserver/utils' @@ -140,7 +142,7 @@ def simplified_type(field_type) when /money/i then :decimal when /image/i then :binary when /bit/i then :boolean - when /uniqueidentifier/i then :string + when /uniqueidentifier/i then :uuid when /datetime/i then simplified_datetime when /varchar\(max\)/ then :text when /timestamp/ then :binary @@ -301,14 +303,18 @@ def reset! # === Abstract Adapter (Misc Support) =========================== # def pk_and_sequence_for(table_name) - idcol = identity_column(table_name) - idcol ? [idcol.name, nil] : nil + pk = primary_key(table_name) + pk ? [pk, nil] : nil end def primary_key(table_name) identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) end + def schema_creation + Sqlserver::SchemaCreation.new self + end + # === SQLServer Specific (DB Reflection) ======================== # def sqlserver? @@ -525,3 +531,4 @@ def auto_reconnected? end # class SQLServerAdapter < AbstractAdapter end # module ConnectionAdapters end # module ActiveRecord + diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb new file mode 100644 index 000000000..f59abfc48 --- /dev/null +++ b/test/cases/uuid_test_sqlserver.rb @@ -0,0 +1,127 @@ +require 'cases/sqlserver_helper' + +class SQLServerUUIDTest < ActiveRecord::TestCase + class UUID < ActiveRecord::Base + self.table_name = 'sql_server_uuids' + end + + def setup + @connection = ActiveRecord::Base.connection + + @connection.reconnect! + + @connection.transaction do + @connection.create_table('sql_server_uuids', id: :uuid, default: 'NEWSEQUENTIALID()') do |t| + t.string 'name' + t.uuid 'other_uuid', default: 'NEWID()' + end + end + end + + def teardown + @connection.execute "IF OBJECT_ID('sql_server_uuids', 'U') IS NOT NULL DROP TABLE sql_server_uuids" + end + + def test_id_is_uuid + assert_equal :uuid, UUID.columns_hash['id'].type + assert UUID.primary_key + end + + def test_id_has_a_default + u = UUID.create + assert_not_nil u.id + end + + def test_auto_create_uuid + u = UUID.create + u.reload + assert_not_nil u.other_uuid + end + + def test_pk_and_sequence_for_uuid_primary_key + pk, seq = @connection.pk_and_sequence_for('sql_server_uuids') + assert_equal 'id', pk + assert_equal nil, seq + end + + def primary_key_for_uuid_primary_key + assert_equal 'id', @connection.primary_key('sql_server_uuids') + end + + def test_change_column_default + @connection.add_column :sql_server_uuids, :thingy, :uuid, null: false, default: "NEWSEQUENTIALID()" + UUID.reset_column_information + column = UUID.columns.find { |c| c.name == 'thingy' } + assert_equal "newsequentialid()", column.default_function + + @connection.change_column :sql_server_uuids, :thingy, :uuid, null: false, default: "NEWID()" + + UUID.reset_column_information + column = UUID.columns.find { |c| c.name == 'thingy' } + assert_equal "newid()", column.default_function + end +end + +class SQLServerUUIDTestNilDefault < ActiveRecord::TestCase + class UUID < ActiveRecord::Base + self.table_name = 'sql_server_uuids' + end + + def setup + @connection = ActiveRecord::Base.connection + + @connection.reconnect! + + @connection.transaction do + @connection.create_table('sql_server_uuids', id: false) do |t| + t.primary_key :id, :uuid, default: nil + t.string 'name' + end + end + end + + def teardown + @connection.execute "IF OBJECT_ID('sql_server_uuids', 'U') IS NOT NULL DROP TABLE sql_server_uuids" + end + +end + +class SQLServerUUIDTestInverseOf < ActiveRecord::TestCase + class UuidPost < ActiveRecord::Base + self.table_name = 'sql_server_uuid_posts' + has_many :uuid_comments, inverse_of: :uuid_post + end + + class UuidComment < ActiveRecord::Base + self.table_name = 'sql_server_uuid_comments' + belongs_to :uuid_post + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.reconnect! + + @connection.transaction do + @connection.create_table('sql_server_uuid_posts', id: :uuid) do |t| + t.string 'title' + end + @connection.create_table('sql_server_uuid_comments', id: :uuid) do |t| + t.uuid :uuid_post_id, default: 'NEWID()' + t.string 'content' + end + end + end + + def teardown + @connection.transaction do + @connection.execute "IF OBJECT_ID('sql_server_uuid_comments', 'U') IS NOT NULL DROP TABLE sql_server_uuid_comments" + @connection.execute "IF OBJECT_ID('sql_server_uuid_posts', 'U') IS NOT NULL DROP TABLE sql_server_uuid_posts" + end + end + + def test_collection_association_with_uuid + post = UuidPost.create! + comment = post.uuid_comments.create! + assert post.uuid_comments.find(comment.id) + end +end From 3b65e3884b552ea754b67f492fc1b0696f8aeea7 Mon Sep 17 00:00:00 2001 From: wbond Date: Wed, 14 May 2014 15:46:27 -0400 Subject: [PATCH 0149/1412] Fixed selecting DISTINCT rows from a table when ordering by a column that is not part of the SELECT list where the join creates at least one extra row. While previously the test suite combined with Rails 4.0.0 looked like it worked, the constructed SQL would return incorrect values for FinderTest#test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct. By using a GROUP BY and MAX()/MIN() in the ORDER BY with no DISTINCT call, the order of the returned rows was not necessarily correct. With Rails 4.0.1, a related bug was fixed that introduces a new method, columns_for_distinct. Unfortunately by adding the columns from the ORDER BY clause to the SELECT list, the DISTINCT nature of the queries was altered. This combined with a test limiting the results to only 2 records, exposed the bug. From testing various SQL constructs to solve the issue, I ended up determining that to properly return a single value from the related table used in the ORDER BY, a query with a subquery utilizing ROW_NUMBER() and DENSE_RANK() was needed. The ROW_NUMBER() was partitioned over the originally requested columns, and then the outer query would filter the results to just the first row of each partition. DENSE_RANK() was used to generate a proper ordering of the partitions in relation to each other. In the process I also determined that extra code, plus an extra test, was necessary to ensure that when such a construct was generated using both a limit and offset, that the results would be correct also. See https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/306 for further discussion, plus example queries and results. --- .../sqlserver/schema_statements.rb | 15 +- lib/arel/visitors/sqlserver.rb | 146 ++++++++++++++++++ test/cases/finder_test_sqlserver.rb | 32 ++-- 3 files changed, 165 insertions(+), 28 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c3216ebd1..cab53c5c4 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -44,17 +44,12 @@ def columns(table_name, _name = nil) end # like postgres, sqlserver requires the ORDER BY columns in the select list for distinct queries, and - # requires that the ORDER BY include the distinct column. - # this method is idental to the postgres method + # requires that the ORDER BY include the distinct column. Unfortunately, sqlserver does not support + # DISTINCT ON () like Posgres, or FIRST_VALUE() like Oracle (at least before SQL Server 2012). Because + # of these facts, we don't actually add any extra columns for distinct, but instead have to create + # a subquery with ROW_NUMBER() and DENSE_RANK() in our monkey-patches to Arel. def columns_for_distinct(columns, orders) #:nodoc: - order_columns = orders.map do |s| - # Convert Arel node to string - s = s.to_sql unless s.is_a?(String) - # Remove any ASC/DESC modifiers - s.gsub(/\s+(ASC|DESC)\s*(NULLS\s+(FIRST|LAST)\s*)?/i, '') - end.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } - - [super, *order_columns].join(', ') + columns end def rename_table(table_name, new_name) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index b75a5549b..f800ab0a1 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -7,6 +7,8 @@ class SQLServer < Arel::Visitors::ToSql def visit_Arel_Nodes_SelectStatement(o, a) if complex_count_sql?(o) visit_Arel_Nodes_SelectStatementForComplexCount(o, a) + elsif distinct_non_present_orders?(o, a) + visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) elsif o.offset visit_Arel_Nodes_SelectStatementWithOffset(o, a) else @@ -47,6 +49,76 @@ def visit_Arel_Nodes_Bin(o, a) # SQLServer ToSql/Visitor (Additions) + # This constructs a query using DENSE_RANK() and ROW_NUMBER() to allow + # ordering a DISTINCT set of data by columns in another table that are + # not part of what we actually want to be DISTINCT. Without this, it is + # possible for the DISTINCT qualifier combined with TOP to return fewer + # rows than were requested. + def visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) + core = o.cores.first + projections = core.projections + groups = core.groups + orders = o.orders.uniq + + select_frags = projections.map do |x| + frag = projection_to_sql_remove_distinct(x, core, a) + # Remove the table specifier + frag.gsub!(/^[^\.]*\./, '') + # If there is an alias, remove everything but + frag.gsub(/^.*\sAS\s+/i, '') + end + + if o.offset + select_frags << 'ROW_NUMBER() OVER (ORDER BY __order) AS __offset' + else + select_frags << '__order' + end + + projection_list = projections.map { |x| projection_to_sql_remove_distinct(x, core, a) }.join(', ') + + sql = [ + ('SELECT'), + (visit(core.set_quantifier, a) if core.set_quantifier and !o.offset), + (visit(o.limit, a) if o.limit and !o.offset), + (select_frags.join(', ')), + ('FROM ('), + ('SELECT'), + ( + [ + (projection_list), + (', DENSE_RANK() OVER ('), + ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty?), + (') AS __order'), + (', ROW_NUMBER() OVER ('), + ("PARTITION BY #{projection_list}" if !orders.empty?), + (" ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty?), + (') AS __joined_row_num') + ].join('') + ), + (source_with_lock_for_select_statement(o, a)), + ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), + ("GROUP BY #{groups.map { |x| visit(x, a) }.join ', ' }" unless groups.empty?), + (visit(core.having, a) if core.having), + (') AS __sq'), + ('WHERE __joined_row_num = 1'), + ('ORDER BY __order' unless o.offset) + ].compact.join(' ') + + if o.offset + sql = [ + ('SELECT'), + (visit(core.set_quantifier, a) if core.set_quantifier), + (visit(o.limit, a) if o.limit), + ('*'), + ('FROM (' + sql + ') AS __osq'), + ("WHERE __offset > #{visit(o.offset.expr, a)}"), + ('ORDER BY __offset') + ].join(' ') + end + + sql + end + def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) find_and_fix_uncorrelated_joins_in_select_statement(o) core = o.cores.first @@ -123,6 +195,18 @@ def visit_Arel_Nodes_SelectStatementForComplexCount(o, a) # SQLServer Helpers + def projection_to_sql_remove_distinct(x, core, a) + frag = Arel.sql(visit(x, a)) + # In Rails 4.0.0, DISTINCT was in a projection, whereas with 4.0.1 + # it is now stored in the set_quantifier. This moves it to the correct + # place so the code works on both 4.0.0 and 4.0.1. + if frag =~ /^\s*DISTINCT\s+/i + core.set_quantifier = Arel::Nodes::Distinct.new() + frag.gsub!(/\s*DISTINCT\s+/, '') + end + frag + end + def source_with_lock_for_select_statement(o, a) core = o.cores.first source = "FROM #{visit(core.source, a).strip}" if core.source @@ -166,6 +250,68 @@ def single_distinct_select_statement?(o) p1.respond_to?(:include?) && p1.include?('DISTINCT')) end + # Determine if the SELECT statement is asking for DISTINCT results, + # but is using columns not part of the SELECT list in the ORDER BY. + # This is necessary because SQL Server requires all ORDER BY entries + # be in the SELECT list with DISTINCT. However, these ordering columns + # can cause duplicate rows, which affect when using a limit. + def distinct_non_present_orders?(o, a) + projections = o.cores.first.projections + + sq = o.cores.first.set_quantifier + p1 = projections.first + + found_distinct = sq and sq.class.to_s =~ /Distinct/ + if (p1.respond_to?(:distinct) && p1.distinct) || (p1.respond_to?(:include?) && p1.include?('DISTINCT')) + found_distinct = true + end + + return false if !found_distinct or o.orders.uniq.empty? + + tables_all_columns = [] + expressions = projections.map do |p| + visit(p, a).split(',').map do |x| + x.strip! + # Rails 4.0.0 included DISTINCT in the first projection + x.gsub!(/\s*DISTINCT\s+/, '') + # Aliased column names + x.gsub!(/\s+AS\s+\w+/i, '') + # Identifier quoting + x.gsub!(/\[|\]/, '') + star_match = /^(\w+)\.\*$/.match(x) + if star_match + tables_all_columns << star_match[1] + end + x.strip.downcase + end.join(', ') + end + + # Make sure each order by is in the select list, otherwise there needs + # to be a subquery with row_numbe() + o.orders.uniq.each do |order| + order = visit(order, a) + order.strip! + + order.gsub!(/\s+(asc|desc)/i, '') + # Identifier quoting + order.gsub!(/\[|\]/, '') + + order.strip! + order.downcase! + + # If we selected all columns from a table, the order is ok + table_match = /^(\w+)\.\w+$/.match(order) + next if table_match and tables_all_columns.include?(table_match[1]) + + next if expressions.include?(order) + + return true + end + + # We didn't find anything in the order by no being selected + false + end + def windowed_single_distinct_select_statement?(o) o.limit && o.offset && diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 8606e24e1..beb0162be 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -10,34 +10,30 @@ class FinderTestSqlserver < ActiveRecord::TestCase end class FinderTest < ActiveRecord::TestCase - fixtures :authors, :author_addresses, :posts + fixtures :authors, :author_addresses, :posts, :categorizations COERCED_TESTS = [ :test_exists_does_not_select_columns_without_alias, :test_string_sanitation, - :test_take_and_first_and_last_with_integer_should_use_sql_limit, - :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - + :test_take_and_first_and_last_with_integer_should_use_sql_limit ] include SqlserverCoercedTest - # TODO This test passes in rails 4.0.0 but not 4.0.1-2 - def test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - p = Post.all.merge!(includes: { authors: :author_address }, - order: 'author_addresses.id DESC ', - limit: 2) - # ar_version = Gem.loaded_specs['activerecord'].version.version - # arel_to_png(p, "#{ar_version}") - count = p.to_a.size - #puts "*****#{ActiveRecord::SQLCounter.log_all.join("\n\n")}" - assert_equal 2, count - - assert_equal 3, Post.all.merge!(includes: { author: :author_address, authors: :author_address}, - order: 'author_addresses_authors.id DESC ', limit: 3).to_a.size + def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct_and_offset + # Based on test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct + # and issue https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/306 + assert_equal( + posts(:welcome, :thinking), + Post.all.merge!( + :includes => { authors: :author_address }, + :order => ['authors.name DESC'], + :limit => 2, + :offset => 1 + ).to_a + ) end - def test_coerced_exists_does_not_select_columns_without_alias assert_sql(/SELECT TOP \(1\) 1 AS one FROM \[topics\]/i) do Topic.exists? From 9192928ef3833725fa8fb5ea60c88b9aa75d48c8 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 13:56:49 -0400 Subject: [PATCH 0150/1412] fix schema_dumper test for UUID --- test/cases/schema_dumper_test_sqlserver.rb | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 02cb13027..602a8654b 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -73,7 +73,7 @@ def table_dump(*table_names) class SchemaDumperTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal] + COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] include SqlserverCoercedTest @@ -82,6 +82,20 @@ def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output end + def test_coerced_types_line_up + column_definition_lines.each do |column_set| + next if column_set.empty? + + lengths = column_set.map do |column| + if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) + match[0].length + end + end + + assert_equal 1, lengths.uniq.length + end + end + private def standard_dump From 1df901f33fdae8e63c380e576002f040551245e6 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 13:57:24 -0400 Subject: [PATCH 0151/1412] only style changes --- Rakefile | 1 - .../sqlserver/database_statements.rb | 1 - .../sqlserver/schema_cache.rb | 3 +-- .../sqlserver/schema_creation.rb | 3 +-- .../sqlserver/schema_statements.rb | 4 ++-- .../sqlserver/table_definition.rb | 2 -- .../connection_adapters/sqlserver_adapter.rb | 1 - lib/arel/visitors/sqlserver.rb | 20 +++++++++---------- 8 files changed, 13 insertions(+), 22 deletions(-) diff --git a/Rakefile b/Rakefile index d304633ef..7edb4253c 100644 --- a/Rakefile +++ b/Rakefile @@ -69,7 +69,6 @@ end task 'test:dblib' => 'test:dblib:env' task 'test:odbc' => 'test:odbc:env' - namespace :profile do %w(dblib odbc).each do |mode| namespace mode.to_sym do diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 359e5e445..12fcbc8b9 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -2,7 +2,6 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module DatabaseStatements - def select_rows(sql, name = nil, binds = []) do_exec_query sql, name, binds, fetch: :rows end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index 9accc616d..c3917ce5d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -65,12 +65,11 @@ def view_information(table_name) @view_information[key] = connection.send(:view_information, table_name) end - def quote_name(name, split_on_dots = true) return @quoted_names[name] if @quoted_names.key? name @quoted_names[name] = if split_on_dots - name.to_s.split('.').map{ |n| quote_name_part(n) }.join('.') + name.to_s.split('.').map { |n| quote_name_part(n) }.join('.') else quote_name_part(name.to_s) end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 920d8585e..c30ff063b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -7,7 +7,7 @@ class SchemaCreation < AbstractAdapter::SchemaCreation def visit_ColumnDefinition(o) sql = super if o.primary_key? && o.type == :uuid - sql << " PRIMARY KEY " + sql << ' PRIMARY KEY ' add_column_options!(sql, column_options(o)) end sql @@ -26,4 +26,3 @@ def add_column_options!(sql, options) end end end - diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 948748469..5b07fea9c 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -48,7 +48,7 @@ def columns(table_name, _name = nil) # DISTINCT ON () like Posgres, or FIRST_VALUE() like Oracle (at least before SQL Server 2012). Because # of these facts, we don't actually add any extra columns for distinct, but instead have to create # a subquery with ROW_NUMBER() and DENSE_RANK() in our monkey-patches to Arel. - def columns_for_distinct(columns, orders) #:nodoc: + def columns_for_distinct(columns, _orders) #:nodoc: columns end @@ -162,7 +162,7 @@ def initialize_native_database_types date: { name: native_date_database_type }, binary: { name: native_binary_database_type }, boolean: { name: 'bit' }, - uuid: { name: 'uniqueidentifier'}, + uuid: { name: 'uniqueidentifier' }, # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them. char: { name: 'char' }, varchar_max: { name: 'varchar(max)' }, diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 18d119c7e..c8d2284cc 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -21,5 +21,3 @@ def column(name, type = nil, options = {}) end end end - - diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f58f98073..142beafd4 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -535,4 +535,3 @@ def auto_reconnected? end # class SQLServerAdapter < AbstractAdapter end # module ConnectionAdapters end # module ActiveRecord - diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f800ab0a1..d230684e7 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -78,8 +78,8 @@ def visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) sql = [ ('SELECT'), - (visit(core.set_quantifier, a) if core.set_quantifier and !o.offset), - (visit(o.limit, a) if o.limit and !o.offset), + (visit(core.set_quantifier, a) if core.set_quantifier && !o.offset), + (visit(o.limit, a) if o.limit && !o.offset), (select_frags.join(', ')), ('FROM ('), ('SELECT'), @@ -87,11 +87,11 @@ def visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) [ (projection_list), (', DENSE_RANK() OVER ('), - ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty?), + ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" unless orders.empty?), (') AS __order'), (', ROW_NUMBER() OVER ('), ("PARTITION BY #{projection_list}" if !orders.empty?), - (" ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty?), + (" ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" unless orders.empty?), (') AS __joined_row_num') ].join('') ), @@ -201,7 +201,7 @@ def projection_to_sql_remove_distinct(x, core, a) # it is now stored in the set_quantifier. This moves it to the correct # place so the code works on both 4.0.0 and 4.0.1. if frag =~ /^\s*DISTINCT\s+/i - core.set_quantifier = Arel::Nodes::Distinct.new() + core.set_quantifier = Arel::Nodes::Distinct.new frag.gsub!(/\s*DISTINCT\s+/, '') end frag @@ -261,12 +261,12 @@ def distinct_non_present_orders?(o, a) sq = o.cores.first.set_quantifier p1 = projections.first - found_distinct = sq and sq.class.to_s =~ /Distinct/ + found_distinct = sq && sq.class.to_s =~ /Distinct/ if (p1.respond_to?(:distinct) && p1.distinct) || (p1.respond_to?(:include?) && p1.include?('DISTINCT')) found_distinct = true end - return false if !found_distinct or o.orders.uniq.empty? + return false if !found_distinct || o.orders.uniq.empty? tables_all_columns = [] expressions = projections.map do |p| @@ -279,9 +279,7 @@ def distinct_non_present_orders?(o, a) # Identifier quoting x.gsub!(/\[|\]/, '') star_match = /^(\w+)\.\*$/.match(x) - if star_match - tables_all_columns << star_match[1] - end + tables_all_columns << star_match[1] if star_match x.strip.downcase end.join(', ') end @@ -301,7 +299,7 @@ def distinct_non_present_orders?(o, a) # If we selected all columns from a table, the order is ok table_match = /^(\w+)\.\w+$/.match(order) - next if table_match and tables_all_columns.include?(table_match[1]) + next if table_match && tables_all_columns.include?(table_match[1]) next if expressions.include?(order) From 64efc8967a7be2d66f9917a336b53843715ac5e2 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 14:26:55 -0400 Subject: [PATCH 0152/1412] moved SQLServerColumn and Activerecord::Base to their own files --- .../connection_adapters/sqlserver_adapter.rb | 516 +----------------- .../connection_adapters/sqlserver_column.rb | 487 +++++++++++++++++ lib/active_record/sqlserver_base.rb | 28 + 3 files changed, 517 insertions(+), 514 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver_column.rb create mode 100644 lib/active_record/sqlserver_base.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 142beafd4..cc9c526bb 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -21,517 +21,5 @@ require 'active_record/connection_adapters/sqlserver/quoting' require 'active_record/connection_adapters/sqlserver/utils' -module ActiveRecord - class Base - def self.sqlserver_connection(config) #:nodoc: - config = config.symbolize_keys - config.reverse_merge! mode: :dblib - mode = config[:mode].to_s.downcase.underscore.to_sym - case mode - when :dblib - require 'tiny_tds' - when :odbc - raise ArgumentError, 'Missing :dsn configuration.' unless config.key?(:dsn) - require 'odbc' - require 'active_record/connection_adapters/sqlserver/core_ext/odbc' - else - raise ArgumentError, "Unknown connection mode in #{config.inspect}." - end - ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) - end - - def self.did_retry_sqlserver_connection(connection, count) - logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}." - end - - def self.did_lose_sqlserver_connection(connection) - logger.info "CONNECTION LOST: #{connection.class.name}" - end - end - - module ConnectionAdapters - class SQLServerColumn < Column - def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {}) - @sqlserver_options = sqlserver_options.symbolize_keys - super(name, default, sql_type, null) - @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary] - end - - class << self - def string_to_binary(value) - "0x#{value.unpack("H*")[0]}" - end - - def binary_to_string(value) - if value.encoding != Encoding::ASCII_8BIT - value = value.force_encoding(Encoding::ASCII_8BIT) - end - value - end - end - - def is_identity? - @sqlserver_options[:is_identity] - end - - def is_primary? - @sqlserver_options[:is_primary] - end - - def is_utf8? - @sql_type =~ /nvarchar|ntext|nchar/i - end - - def is_integer? - @sql_type =~ /int/i - end - - def is_real? - @sql_type =~ /real/i - end - - def sql_type_for_statement - if is_integer? || is_real? - sql_type.sub(/\((\d+)?\)/, '') - else - sql_type - end - end - - def default_function - @sqlserver_options[:default_function] - end - - def table_name - @sqlserver_options[:table_name] - end - - def table_klass - @table_klass ||= begin - table_name.classify.constantize - rescue StandardError, NameError, LoadError - nil - end - (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil - end - - def database_year - @sqlserver_options[:database_year] - end - - private - - def extract_limit(sql_type) - case sql_type - when /^smallint/i - 2 - when /^int/i - 4 - when /^bigint/i - 8 - when /\(max\)/, /decimal/, /numeric/ - nil - else - super - end - end - - def simplified_type(field_type) - case field_type - when /real/i then :float - when /money/i then :decimal - when /image/i then :binary - when /bit/i then :boolean - when /uniqueidentifier/i then :uuid - when /datetime/i then simplified_datetime - when /varchar\(max\)/ then :text - when /timestamp/ then :binary - else super - end - end - - def simplified_datetime - if database_year >= 2008 - :datetime - elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name) - :date - elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name) - :time - else - :datetime - end - end - end # class SQLServerColumn - - class SQLServerAdapter < AbstractAdapter - include Sqlserver::Quoting - include Sqlserver::DatabaseStatements - include Sqlserver::Showplan - include Sqlserver::SchemaStatements - include Sqlserver::DatabaseLimits - include Sqlserver::Errors - - VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip - ADAPTER_NAME = 'SQLServer'.freeze - DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] - - attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition - - cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, - :enable_default_unicode_types, :auto_connect, :cs_equality_operator, - :lowercase_schema_reflection, :auto_connect_duration, :showplan_option - - self.enable_default_unicode_types = true - - class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: - include Arel::Visitors::BindVisitor - end - - def initialize(connection, logger, pool, config) - super(connection, logger, pool) - # AbstractAdapter Responsibility - @schema_cache = Sqlserver::SchemaCache.new self - @visitor = Arel::Visitors::SQLServer.new self - # Our Responsibility - @config = config - @connection_options = config - connect - @database_version = select_value 'SELECT @@version', 'SCHEMA' - @database_year = begin - if @database_version =~ /Azure/i - @sqlserver_azure = true - @database_version.match(/\s-\s([0-9.]+)/)[1] - year = 2012 - else - year = DATABASE_VERSION_REGEXP.match(@database_version)[1] - year == 'Denali' ? 2011 : year.to_i - end - rescue - 0 - end - @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA' - @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA' - @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' - initialize_dateformatter - use_database - unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) - raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." - end - end - - # === Abstract Adapter ========================================== # - - def adapter_name - ADAPTER_NAME - end - - def supports_migrations? - true - end - - def supports_primary_key? - true - end - - def supports_count_distinct? - true - end - - def supports_ddl_transactions? - true - end - - def supports_bulk_alter? - false - end - - def supports_savepoints? - true - end - - def supports_index_sort_order? - true - end - - def supports_partial_index? - @database_year >= 2008 - end - - def supports_explain? - true - end - - def disable_referential_integrity - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" - yield - ensure - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" - end - - # === Abstract Adapter (Connection Management) ================== # - - def active? - case @connection_options[:mode] - when :dblib - return @connection.active? - end - raw_connection_do('SELECT 1') - true - rescue *lost_connection_exceptions - false - end - - def reconnect! - reset_transaction - disconnect! - connect - active? - end - - def disconnect! - reset_transaction - @spid = nil - case @connection_options[:mode] - when :dblib - @connection.close rescue nil - when :odbc - @connection.disconnect rescue nil - end - end - - def reset! - remove_database_connections_and_rollback {} - end - - # === Abstract Adapter (Misc Support) =========================== # - - def pk_and_sequence_for(table_name) - pk = primary_key(table_name) - pk ? [pk, nil] : nil - end - - def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) - end - - def schema_creation - Sqlserver::SchemaCreation.new self - end - - # === SQLServer Specific (DB Reflection) ======================== # - - def sqlserver? - true - end - - def sqlserver_2005? - @database_year == 2005 - end - - def sqlserver_2008? - @database_year == 2008 - end - - def sqlserver_2011? - @database_year == 2011 - end - - def sqlserver_2012? - @database_year == 2012 - end - - def sqlserver_azure? - @sqlserver_azure - end - - def version - self.class::VERSION - end - - def inspect - "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" - end - - def auto_connect - @@auto_connect.is_a?(FalseClass) ? false : true - end - - def auto_connect_duration - @@auto_connect_duration ||= 10 - end - - def native_string_database_type - @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') - end - - def native_text_database_type - @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' - end - - def native_time_database_type - sqlserver_2005? ? 'datetime' : 'time' - end - - def native_date_database_type - sqlserver_2005? ? 'datetime' : 'date' - end - - def native_binary_database_type - @@native_binary_database_type || 'varbinary(max)' - end - - def cs_equality_operator - @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' - end - - protected - - # === Abstract Adapter (Misc Support) =========================== # - - def translate_exception(e, message) - case message - when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i - RecordNotUnique.new(message, e) - when /conflicted with the foreign key constraint/i - InvalidForeignKey.new(message, e) - when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message, e) - when *lost_connection_messages - LostConnection.new(message, e) - else - super - end - end - - # === SQLServer Specific (Connection Management) ================ # - - def connect - config = @connection_options - @connection = case config[:mode] - when :dblib - appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil - login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil - timeout = config[:timeout].present? ? config[:timeout].to_i / 1000 : nil - encoding = config[:encoding].present? ? config[:encoding] : nil - TinyTds::Client.new( - dataserver: config[:dataserver], - host: config[:host], - port: config[:port], - username: config[:username], - password: config[:password], - database: config[:database], - tds_version: config[:tds_version], - appname: appname, - login_timeout: login_timeout, - timeout: timeout, - encoding: encoding, - azure: config[:azure] - ).tap do |client| - if config[:azure] - client.execute('SET ANSI_NULLS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET ANSI_NULL_DFLT_ON ON').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - client.execute('SET ANSI_PADDING ON').do - client.execute('SET QUOTED_IDENTIFIER ON') - client.execute('SET ANSI_WARNINGS ON').do - else - client.execute('SET ANSI_DEFAULTS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - end - client.execute('SET TEXTSIZE 2147483647').do - client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do - end - when :odbc - if config[:dsn].include?(';') - driver = ODBC::Driver.new.tap do |d| - d.name = config[:dsn_name] || 'Driver1' - d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } - end - ODBC::Database.new.drvconnect(driver) - else - ODBC.connect config[:dsn], config[:username], config[:password] - end.tap do |c| - begin - c.use_time = true - c.use_utc = ActiveRecord::Base.default_timezone == :utc - rescue Exception - warn 'Ruby ODBC v0.99992 or higher is required.' - end - end - end - @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first - configure_connection - rescue - raise unless @auto_connecting - end - - # Override this method so every connection can be configured to your needs. - # For example: - # raw_connection_do "SET TEXTSIZE #{64.megabytes}" - # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" - def configure_connection - end - - # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. - # For example: - # "myapp_#{$$}_#{Thread.current.object_id}".to(29) - def configure_application_name - end - - def initialize_dateformatter - @database_dateformat = user_options_dateformat - a, b, c = @database_dateformat.each_char.to_a - [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 - end - - def remove_database_connections_and_rollback(database = nil) - database ||= current_database - do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" - begin - yield - ensure - do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" - end if block_given? - end - - def with_sqlserver_error_handling - yield - rescue Exception => e - case translate_exception(e, e.message) - when LostConnection then retry if auto_reconnected? - end - raise - end - - def disable_auto_reconnect - old_auto_connect, self.class.auto_connect = self.class.auto_connect, false - yield - ensure - self.class.auto_connect = old_auto_connect - end - - def auto_reconnected? - return false unless auto_connect - @auto_connecting = true - count = 0 - while count <= (auto_connect_duration / 2) - result = reconnect! - ActiveRecord::Base.did_retry_sqlserver_connection(self, count) - return true if result - sleep 2**count - count += 1 - end - ActiveRecord::Base.did_lose_sqlserver_connection(self) - false - ensure - @auto_connecting = false - end - end # class SQLServerAdapter < AbstractAdapter - end # module ConnectionAdapters -end # module ActiveRecord +require 'active_record/sqlserver_base' +require 'active_record/connection_adapters/sqlserver_column' diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb new file mode 100644 index 000000000..baff3aeb2 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -0,0 +1,487 @@ +module ActiveRecord + module ConnectionAdapters + class SQLServerColumn < Column + def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {}) + @sqlserver_options = sqlserver_options.symbolize_keys + super(name, default, sql_type, null) + @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary] + end + + class << self + def string_to_binary(value) + "0x#{value.unpack("H*")[0]}" + end + + def binary_to_string(value) + if value.encoding != Encoding::ASCII_8BIT + value = value.force_encoding(Encoding::ASCII_8BIT) + end + value + end + end + + def is_identity? + @sqlserver_options[:is_identity] + end + + def is_primary? + @sqlserver_options[:is_primary] + end + + def is_utf8? + @sql_type =~ /nvarchar|ntext|nchar/i + end + + def is_integer? + @sql_type =~ /int/i + end + + def is_real? + @sql_type =~ /real/i + end + + def sql_type_for_statement + if is_integer? || is_real? + sql_type.sub(/\((\d+)?\)/, '') + else + sql_type + end + end + + def default_function + @sqlserver_options[:default_function] + end + + def table_name + @sqlserver_options[:table_name] + end + + def table_klass + @table_klass ||= begin + table_name.classify.constantize + rescue StandardError, NameError, LoadError + nil + end + (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil + end + + def database_year + @sqlserver_options[:database_year] + end + + private + + def extract_limit(sql_type) + case sql_type + when /^smallint/i + 2 + when /^int/i + 4 + when /^bigint/i + 8 + when /\(max\)/, /decimal/, /numeric/ + nil + else + super + end + end + + def simplified_type(field_type) + case field_type + when /real/i then :float + when /money/i then :decimal + when /image/i then :binary + when /bit/i then :boolean + when /uniqueidentifier/i then :uuid + when /datetime/i then simplified_datetime + when /varchar\(max\)/ then :text + when /timestamp/ then :binary + else super + end + end + + def simplified_datetime + if database_year >= 2008 + :datetime + elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name) + :date + elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name) + :time + else + :datetime + end + end + end # class SQLServerColumn + + class SQLServerAdapter < AbstractAdapter + include Sqlserver::Quoting + include Sqlserver::DatabaseStatements + include Sqlserver::Showplan + include Sqlserver::SchemaStatements + include Sqlserver::DatabaseLimits + include Sqlserver::Errors + + VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip + ADAPTER_NAME = 'SQLServer'.freeze + DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ + SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] + + attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition + + cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, + :enable_default_unicode_types, :auto_connect, :cs_equality_operator, + :lowercase_schema_reflection, :auto_connect_duration, :showplan_option + + self.enable_default_unicode_types = true + + class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: + include Arel::Visitors::BindVisitor + end + + def initialize(connection, logger, pool, config) + super(connection, logger, pool) + # AbstractAdapter Responsibility + @schema_cache = Sqlserver::SchemaCache.new self + @visitor = Arel::Visitors::SQLServer.new self + # Our Responsibility + @config = config + @connection_options = config + connect + @database_version = select_value 'SELECT @@version', 'SCHEMA' + @database_year = begin + if @database_version =~ /Azure/i + @sqlserver_azure = true + @database_version.match(/\s-\s([0-9.]+)/)[1] + year = 2012 + else + year = DATABASE_VERSION_REGEXP.match(@database_version)[1] + year == 'Denali' ? 2011 : year.to_i + end + rescue + 0 + end + @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA' + @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA' + @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' + initialize_dateformatter + use_database + unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) + raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." + end + end + + # === Abstract Adapter ========================================== # + + def adapter_name + ADAPTER_NAME + end + + def supports_migrations? + true + end + + def supports_primary_key? + true + end + + def supports_count_distinct? + true + end + + def supports_ddl_transactions? + true + end + + def supports_bulk_alter? + false + end + + def supports_savepoints? + true + end + + def supports_index_sort_order? + true + end + + def supports_partial_index? + @database_year >= 2008 + end + + def supports_explain? + true + end + + def disable_referential_integrity + do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" + yield + ensure + do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" + end + + # === Abstract Adapter (Connection Management) ================== # + + def active? + case @connection_options[:mode] + when :dblib + return @connection.active? + end + raw_connection_do('SELECT 1') + true + rescue *lost_connection_exceptions + false + end + + def reconnect! + reset_transaction + disconnect! + connect + active? + end + + def disconnect! + reset_transaction + @spid = nil + case @connection_options[:mode] + when :dblib + @connection.close rescue nil + when :odbc + @connection.disconnect rescue nil + end + end + + def reset! + remove_database_connections_and_rollback {} + end + + # === Abstract Adapter (Misc Support) =========================== # + + def pk_and_sequence_for(table_name) + pk = primary_key(table_name) + pk ? [pk, nil] : nil + end + + def primary_key(table_name) + identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) + end + + def schema_creation + Sqlserver::SchemaCreation.new self + end + + # === SQLServer Specific (DB Reflection) ======================== # + + def sqlserver? + true + end + + def sqlserver_2005? + @database_year == 2005 + end + + def sqlserver_2008? + @database_year == 2008 + end + + def sqlserver_2011? + @database_year == 2011 + end + + def sqlserver_2012? + @database_year == 2012 + end + + def sqlserver_azure? + @sqlserver_azure + end + + def version + self.class::VERSION + end + + def inspect + "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" + end + + def auto_connect + @@auto_connect.is_a?(FalseClass) ? false : true + end + + def auto_connect_duration + @@auto_connect_duration ||= 10 + end + + def native_string_database_type + @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') + end + + def native_text_database_type + @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' + end + + def native_time_database_type + sqlserver_2005? ? 'datetime' : 'time' + end + + def native_date_database_type + sqlserver_2005? ? 'datetime' : 'date' + end + + def native_binary_database_type + @@native_binary_database_type || 'varbinary(max)' + end + + def cs_equality_operator + @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' + end + + protected + + # === Abstract Adapter (Misc Support) =========================== # + + def translate_exception(e, message) + case message + when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i + RecordNotUnique.new(message, e) + when /conflicted with the foreign key constraint/i + InvalidForeignKey.new(message, e) + when /has been chosen as the deadlock victim/i + DeadlockVictim.new(message, e) + when *lost_connection_messages + LostConnection.new(message, e) + else + super + end + end + + # === SQLServer Specific (Connection Management) ================ # + + def connect + config = @connection_options + @connection = case config[:mode] + when :dblib + appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil + login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil + timeout = config[:timeout].present? ? config[:timeout].to_i / 1000 : nil + encoding = config[:encoding].present? ? config[:encoding] : nil + TinyTds::Client.new( + dataserver: config[:dataserver], + host: config[:host], + port: config[:port], + username: config[:username], + password: config[:password], + database: config[:database], + tds_version: config[:tds_version], + appname: appname, + login_timeout: login_timeout, + timeout: timeout, + encoding: encoding, + azure: config[:azure] + ).tap do |client| + if config[:azure] + client.execute('SET ANSI_NULLS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET ANSI_NULL_DFLT_ON ON').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + client.execute('SET ANSI_PADDING ON').do + client.execute('SET QUOTED_IDENTIFIER ON') + client.execute('SET ANSI_WARNINGS ON').do + else + client.execute('SET ANSI_DEFAULTS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + end + client.execute('SET TEXTSIZE 2147483647').do + client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do + end + when :odbc + if config[:dsn].include?(';') + driver = ODBC::Driver.new.tap do |d| + d.name = config[:dsn_name] || 'Driver1' + d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } + end + ODBC::Database.new.drvconnect(driver) + else + ODBC.connect config[:dsn], config[:username], config[:password] + end.tap do |c| + begin + c.use_time = true + c.use_utc = ActiveRecord::Base.default_timezone == :utc + rescue Exception + warn 'Ruby ODBC v0.99992 or higher is required.' + end + end + end + @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first + configure_connection + rescue + raise unless @auto_connecting + end + + # Override this method so every connection can be configured to your needs. + # For example: + # raw_connection_do "SET TEXTSIZE #{64.megabytes}" + # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" + def configure_connection + end + + # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. + # For example: + # "myapp_#{$$}_#{Thread.current.object_id}".to(29) + def configure_application_name + end + + def initialize_dateformatter + @database_dateformat = user_options_dateformat + a, b, c = @database_dateformat.each_char.to_a + [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 + end + + def remove_database_connections_and_rollback(database = nil) + database ||= current_database + do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" + begin + yield + ensure + do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" + end if block_given? + end + + def with_sqlserver_error_handling + yield + rescue Exception => e + case translate_exception(e, e.message) + when LostConnection then retry if auto_reconnected? + end + raise + end + + def disable_auto_reconnect + old_auto_connect, self.class.auto_connect = self.class.auto_connect, false + yield + ensure + self.class.auto_connect = old_auto_connect + end + + def auto_reconnected? + return false unless auto_connect + @auto_connecting = true + count = 0 + while count <= (auto_connect_duration / 2) + result = reconnect! + ActiveRecord::Base.did_retry_sqlserver_connection(self, count) + return true if result + sleep 2**count + count += 1 + end + ActiveRecord::Base.did_lose_sqlserver_connection(self) + false + ensure + @auto_connecting = false + end + end # class SQLServerAdapter < AbstractAdapter + end # module ConnectionAdapters +end # module ActiveRecord diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb new file mode 100644 index 000000000..e1b8f673f --- /dev/null +++ b/lib/active_record/sqlserver_base.rb @@ -0,0 +1,28 @@ +module ActiveRecord + class Base + def self.sqlserver_connection(config) #:nodoc: + config = config.symbolize_keys + config.reverse_merge! mode: :dblib + mode = config[:mode].to_s.downcase.underscore.to_sym + case mode + when :dblib + require 'tiny_tds' + when :odbc + raise ArgumentError, 'Missing :dsn configuration.' unless config.key?(:dsn) + require 'odbc' + require 'active_record/connection_adapters/sqlserver/core_ext/odbc' + else + raise ArgumentError, "Unknown connection mode in #{config.inspect}." + end + ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) + end + + def self.did_retry_sqlserver_connection(connection, count) + logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}." + end + + def self.did_lose_sqlserver_connection(connection) + logger.info "CONNECTION LOST: #{connection.class.name}" + end + end +end From 297e4cf0845dc2fb897bed52288037fb02612bc5 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 15:02:02 -0400 Subject: [PATCH 0153/1412] moved SQLServerAdapter back and split up the connect method --- .../connection_adapters/sqlserver_adapter.rb | 395 ++++++++++++++++++ .../connection_adapters/sqlserver_column.rb | 373 +---------------- 2 files changed, 396 insertions(+), 372 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index cc9c526bb..11e6ca96d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -23,3 +23,398 @@ require 'active_record/sqlserver_base' require 'active_record/connection_adapters/sqlserver_column' + +module ActiveRecord + module ConnectionAdapters + class SQLServerAdapter < AbstractAdapter + include Sqlserver::Quoting + include Sqlserver::DatabaseStatements + include Sqlserver::Showplan + include Sqlserver::SchemaStatements + include Sqlserver::DatabaseLimits + include Sqlserver::Errors + + VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip + ADAPTER_NAME = 'SQLServer'.freeze + DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ + SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] + + attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition + + cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, + :enable_default_unicode_types, :auto_connect, :cs_equality_operator, + :lowercase_schema_reflection, :auto_connect_duration, :showplan_option + + self.enable_default_unicode_types = true + + class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: + include Arel::Visitors::BindVisitor + end + + def initialize(connection, logger, pool, config) + super(connection, logger, pool) + # AbstractAdapter Responsibility + @schema_cache = Sqlserver::SchemaCache.new self + @visitor = Arel::Visitors::SQLServer.new self + # Our Responsibility + @config = config + @connection_options = config + connect + @database_version = select_value 'SELECT @@version', 'SCHEMA' + @database_year = begin + if @database_version =~ /Azure/i + @sqlserver_azure = true + @database_version.match(/\s-\s([0-9.]+)/)[1] + year = 2012 + else + year = DATABASE_VERSION_REGEXP.match(@database_version)[1] + year == 'Denali' ? 2011 : year.to_i + end + rescue + 0 + end + @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA' + @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA' + @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' + initialize_dateformatter + use_database + unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) + raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." + end + end + + # === Abstract Adapter ========================================== # + + def adapter_name + ADAPTER_NAME + end + + def supports_migrations? + true + end + + def supports_primary_key? + true + end + + def supports_count_distinct? + true + end + + def supports_ddl_transactions? + true + end + + def supports_bulk_alter? + false + end + + def supports_savepoints? + true + end + + def supports_index_sort_order? + true + end + + def supports_partial_index? + @database_year >= 2008 + end + + def supports_explain? + true + end + + def disable_referential_integrity + do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" + yield + ensure + do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" + end + + # === Abstract Adapter (Connection Management) ================== # + + def active? + case @connection_options[:mode] + when :dblib + return @connection.active? + end + raw_connection_do('SELECT 1') + true + rescue *lost_connection_exceptions + false + end + + def reconnect! + reset_transaction + disconnect! + connect + active? + end + + def disconnect! + reset_transaction + @spid = nil + case @connection_options[:mode] + when :dblib + @connection.close rescue nil + when :odbc + @connection.disconnect rescue nil + end + end + + def reset! + remove_database_connections_and_rollback {} + end + + # === Abstract Adapter (Misc Support) =========================== # + + def pk_and_sequence_for(table_name) + pk = primary_key(table_name) + pk ? [pk, nil] : nil + end + + def primary_key(table_name) + identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) + end + + def schema_creation + Sqlserver::SchemaCreation.new self + end + + # === SQLServer Specific (DB Reflection) ======================== # + + def sqlserver? + true + end + + def sqlserver_2005? + @database_year == 2005 + end + + def sqlserver_2008? + @database_year == 2008 + end + + def sqlserver_2011? + @database_year == 2011 + end + + def sqlserver_2012? + @database_year == 2012 + end + + def sqlserver_azure? + @sqlserver_azure + end + + def version + self.class::VERSION + end + + def inspect + "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" + end + + def auto_connect + @@auto_connect.is_a?(FalseClass) ? false : true + end + + def auto_connect_duration + @@auto_connect_duration ||= 10 + end + + def native_string_database_type + @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') + end + + def native_text_database_type + @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' + end + + def native_time_database_type + sqlserver_2005? ? 'datetime' : 'time' + end + + def native_date_database_type + sqlserver_2005? ? 'datetime' : 'date' + end + + def native_binary_database_type + @@native_binary_database_type || 'varbinary(max)' + end + + def cs_equality_operator + @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' + end + + protected + + # === Abstract Adapter (Misc Support) =========================== # + + def translate_exception(e, message) + case message + when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i + RecordNotUnique.new(message, e) + when /conflicted with the foreign key constraint/i + InvalidForeignKey.new(message, e) + when /has been chosen as the deadlock victim/i + DeadlockVictim.new(message, e) + when *lost_connection_messages + LostConnection.new(message, e) + else + super + end + end + + # === SQLServer Specific (Connection Management) ================ # + + def connect + config = @connection_options + @connection = case config[:mode] + when :dblib + dblib_connect(config) + when :odbc + odbc_connect(config) + end + @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first + configure_connection + rescue + raise unless @auto_connecting + end + + def dblib_connect(config) + TinyTds::Client.new( + dataserver: config[:dataserver], + host: config[:host], + port: config[:port], + username: config[:username], + password: config[:password], + database: config[:database], + tds_version: config[:tds_version], + appname: appname(config), + login_timeout: login_timeout(config), + timeout: timeout(config), + encoding: encoding(config), + azure: config[:azure] + ).tap do |client| + if config[:azure] + client.execute('SET ANSI_NULLS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET ANSI_NULL_DFLT_ON ON').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + client.execute('SET ANSI_PADDING ON').do + client.execute('SET QUOTED_IDENTIFIER ON') + client.execute('SET ANSI_WARNINGS ON').do + else + client.execute('SET ANSI_DEFAULTS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + end + client.execute('SET TEXTSIZE 2147483647').do + client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do + end + end + + def appname(config) + config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil + end + + def login_timeout(config) + config[:login_timeout].present? ? config[:login_timeout].to_i : nil + end + + def timeout(config) + config[:timeout].present? ? config[:timeout].to_i / 1000 : nil + end + + def encoding(config) + config[:encoding].present? ? config[:encoding] : nil + end + + def odbc_connect(config) + if config[:dsn].include?(';') + driver = ODBC::Driver.new.tap do |d| + d.name = config[:dsn_name] || 'Driver1' + d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } + end + ODBC::Database.new.drvconnect(driver) + else + ODBC.connect config[:dsn], config[:username], config[:password] + end.tap do |c| + begin + c.use_time = true + c.use_utc = ActiveRecord::Base.default_timezone == :utc + rescue Exception + warn 'Ruby ODBC v0.99992 or higher is required.' + end + end + end + + # Override this method so every connection can be configured to your needs. + # For example: + # raw_connection_do "SET TEXTSIZE #{64.megabytes}" + # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" + def configure_connection + end + + # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. + # For example: + # "myapp_#{$$}_#{Thread.current.object_id}".to(29) + def configure_application_name + end + + def initialize_dateformatter + @database_dateformat = user_options_dateformat + a, b, c = @database_dateformat.each_char.to_a + [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 + end + + def remove_database_connections_and_rollback(database = nil) + database ||= current_database + do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" + begin + yield + ensure + do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" + end if block_given? + end + + def with_sqlserver_error_handling + yield + rescue Exception => e + case translate_exception(e, e.message) + when LostConnection then retry if auto_reconnected? + end + raise + end + + def disable_auto_reconnect + old_auto_connect, self.class.auto_connect = self.class.auto_connect, false + yield + ensure + self.class.auto_connect = old_auto_connect + end + + def auto_reconnected? + return false unless auto_connect + @auto_connecting = true + count = 0 + while count <= (auto_connect_duration / 2) + result = reconnect! + ActiveRecord::Base.did_retry_sqlserver_connection(self, count) + return true if result + sleep 2**count + count += 1 + end + ActiveRecord::Base.did_lose_sqlserver_connection(self) + false + ensure + @auto_connecting = false + end + end # class SQLServerAdapter < AbstractAdapter + end # module ConnectionAdapters +end # module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index baff3aeb2..caf3e819e 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -112,376 +112,5 @@ def simplified_datetime end end end # class SQLServerColumn - - class SQLServerAdapter < AbstractAdapter - include Sqlserver::Quoting - include Sqlserver::DatabaseStatements - include Sqlserver::Showplan - include Sqlserver::SchemaStatements - include Sqlserver::DatabaseLimits - include Sqlserver::Errors - - VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip - ADAPTER_NAME = 'SQLServer'.freeze - DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] - - attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition - - cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, - :enable_default_unicode_types, :auto_connect, :cs_equality_operator, - :lowercase_schema_reflection, :auto_connect_duration, :showplan_option - - self.enable_default_unicode_types = true - - class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: - include Arel::Visitors::BindVisitor - end - - def initialize(connection, logger, pool, config) - super(connection, logger, pool) - # AbstractAdapter Responsibility - @schema_cache = Sqlserver::SchemaCache.new self - @visitor = Arel::Visitors::SQLServer.new self - # Our Responsibility - @config = config - @connection_options = config - connect - @database_version = select_value 'SELECT @@version', 'SCHEMA' - @database_year = begin - if @database_version =~ /Azure/i - @sqlserver_azure = true - @database_version.match(/\s-\s([0-9.]+)/)[1] - year = 2012 - else - year = DATABASE_VERSION_REGEXP.match(@database_version)[1] - year == 'Denali' ? 2011 : year.to_i - end - rescue - 0 - end - @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA' - @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA' - @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' - initialize_dateformatter - use_database - unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) - raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." - end - end - - # === Abstract Adapter ========================================== # - - def adapter_name - ADAPTER_NAME - end - - def supports_migrations? - true - end - - def supports_primary_key? - true - end - - def supports_count_distinct? - true - end - - def supports_ddl_transactions? - true - end - - def supports_bulk_alter? - false - end - - def supports_savepoints? - true - end - - def supports_index_sort_order? - true - end - - def supports_partial_index? - @database_year >= 2008 - end - - def supports_explain? - true - end - - def disable_referential_integrity - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" - yield - ensure - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" - end - - # === Abstract Adapter (Connection Management) ================== # - - def active? - case @connection_options[:mode] - when :dblib - return @connection.active? - end - raw_connection_do('SELECT 1') - true - rescue *lost_connection_exceptions - false - end - - def reconnect! - reset_transaction - disconnect! - connect - active? - end - - def disconnect! - reset_transaction - @spid = nil - case @connection_options[:mode] - when :dblib - @connection.close rescue nil - when :odbc - @connection.disconnect rescue nil - end - end - - def reset! - remove_database_connections_and_rollback {} - end - - # === Abstract Adapter (Misc Support) =========================== # - - def pk_and_sequence_for(table_name) - pk = primary_key(table_name) - pk ? [pk, nil] : nil - end - - def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) - end - - def schema_creation - Sqlserver::SchemaCreation.new self - end - - # === SQLServer Specific (DB Reflection) ======================== # - - def sqlserver? - true - end - - def sqlserver_2005? - @database_year == 2005 - end - - def sqlserver_2008? - @database_year == 2008 - end - - def sqlserver_2011? - @database_year == 2011 - end - - def sqlserver_2012? - @database_year == 2012 - end - - def sqlserver_azure? - @sqlserver_azure - end - - def version - self.class::VERSION - end - - def inspect - "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" - end - - def auto_connect - @@auto_connect.is_a?(FalseClass) ? false : true - end - - def auto_connect_duration - @@auto_connect_duration ||= 10 - end - - def native_string_database_type - @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') - end - - def native_text_database_type - @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' - end - - def native_time_database_type - sqlserver_2005? ? 'datetime' : 'time' - end - - def native_date_database_type - sqlserver_2005? ? 'datetime' : 'date' - end - - def native_binary_database_type - @@native_binary_database_type || 'varbinary(max)' - end - - def cs_equality_operator - @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' - end - - protected - - # === Abstract Adapter (Misc Support) =========================== # - - def translate_exception(e, message) - case message - when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i - RecordNotUnique.new(message, e) - when /conflicted with the foreign key constraint/i - InvalidForeignKey.new(message, e) - when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message, e) - when *lost_connection_messages - LostConnection.new(message, e) - else - super - end - end - - # === SQLServer Specific (Connection Management) ================ # - - def connect - config = @connection_options - @connection = case config[:mode] - when :dblib - appname = config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil - login_timeout = config[:login_timeout].present? ? config[:login_timeout].to_i : nil - timeout = config[:timeout].present? ? config[:timeout].to_i / 1000 : nil - encoding = config[:encoding].present? ? config[:encoding] : nil - TinyTds::Client.new( - dataserver: config[:dataserver], - host: config[:host], - port: config[:port], - username: config[:username], - password: config[:password], - database: config[:database], - tds_version: config[:tds_version], - appname: appname, - login_timeout: login_timeout, - timeout: timeout, - encoding: encoding, - azure: config[:azure] - ).tap do |client| - if config[:azure] - client.execute('SET ANSI_NULLS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET ANSI_NULL_DFLT_ON ON').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - client.execute('SET ANSI_PADDING ON').do - client.execute('SET QUOTED_IDENTIFIER ON') - client.execute('SET ANSI_WARNINGS ON').do - else - client.execute('SET ANSI_DEFAULTS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - end - client.execute('SET TEXTSIZE 2147483647').do - client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do - end - when :odbc - if config[:dsn].include?(';') - driver = ODBC::Driver.new.tap do |d| - d.name = config[:dsn_name] || 'Driver1' - d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } - end - ODBC::Database.new.drvconnect(driver) - else - ODBC.connect config[:dsn], config[:username], config[:password] - end.tap do |c| - begin - c.use_time = true - c.use_utc = ActiveRecord::Base.default_timezone == :utc - rescue Exception - warn 'Ruby ODBC v0.99992 or higher is required.' - end - end - end - @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first - configure_connection - rescue - raise unless @auto_connecting - end - - # Override this method so every connection can be configured to your needs. - # For example: - # raw_connection_do "SET TEXTSIZE #{64.megabytes}" - # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" - def configure_connection - end - - # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. - # For example: - # "myapp_#{$$}_#{Thread.current.object_id}".to(29) - def configure_application_name - end - - def initialize_dateformatter - @database_dateformat = user_options_dateformat - a, b, c = @database_dateformat.each_char.to_a - [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 - end - - def remove_database_connections_and_rollback(database = nil) - database ||= current_database - do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" - begin - yield - ensure - do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" - end if block_given? - end - - def with_sqlserver_error_handling - yield - rescue Exception => e - case translate_exception(e, e.message) - when LostConnection then retry if auto_reconnected? - end - raise - end - - def disable_auto_reconnect - old_auto_connect, self.class.auto_connect = self.class.auto_connect, false - yield - ensure - self.class.auto_connect = old_auto_connect - end - - def auto_reconnected? - return false unless auto_connect - @auto_connecting = true - count = 0 - while count <= (auto_connect_duration / 2) - result = reconnect! - ActiveRecord::Base.did_retry_sqlserver_connection(self, count) - return true if result - sleep 2**count - count += 1 - end - ActiveRecord::Base.did_lose_sqlserver_connection(self) - false - ensure - @auto_connecting = false - end - end # class SQLServerAdapter < AbstractAdapter - end # module ConnectionAdapters + end # module ConnectionAdapters end # module ActiveRecord From 87188cb066df2d559ae524b8b99bcaac8dc5cf63 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 16:53:57 -0400 Subject: [PATCH 0154/1412] added pry --- Gemfile | 1 + test/cases/sqlserver_helper.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index b9eda3832..a09e59a13 100644 --- a/Gemfile +++ b/Gemfile @@ -48,4 +48,5 @@ group :development do gem 'ruby-prof' gem 'simplecov' gem 'ruby-graphviz' + gem 'pry' end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index 026efb196..bfcc1f8c0 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -16,6 +16,7 @@ SimpleCov.start do add_filter "/test/" end +require 'pry' require 'graphviz' require 'mocha/api' require 'active_support/dependencies' @@ -118,4 +119,4 @@ def with_auto_connect(boolean) # SQL Server. sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" -eval(File.read(sqlserver_specific_schema_file)) \ No newline at end of file +eval(File.read(sqlserver_specific_schema_file)) From e3bcabffd5b3f37c4bd8a56ede2f295b43b86008 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Thu, 15 May 2014 17:09:32 -0400 Subject: [PATCH 0155/1412] better handling of updates with identiy column. Room for improvement. --- .../sqlserver/database_statements.rb | 14 +++---------- .../sqlserver/schema_statements.rb | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 12fcbc8b9..bfa9ddbdf 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,20 +14,12 @@ def execute(sql, name = nil) end end - # TODO: I bet there's a better way than a regex to take care of this def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - # We can't update Identiy columns in sqlserver. So, strip out the id from the update. - if sql =~ /UPDATE/ - # take off a comma before or after. This could probably be done better - if sql =~ /, \[id\] = @?[0-9]*/ - sql.gsub!(/, \[id\] = @?[0-9]*/, '') - elsif sql =~ /\s\[id\] = @?[0-9]*,/ - sql.gsub!(/\s\[id\] = @?[0-9]*,/, '') - end - end - if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) } + elsif update_sql?(sql) + sql = strip_ident_from_update(sql) + do_exec_query(sql, name, binds) else do_exec_query(sql, name, binds) end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5b07fea9c..baad33152 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -366,6 +366,27 @@ def insert_sql?(sql) !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? end + def strip_ident_from_update(sql) + # We can't update Identiy columns in sqlserver. So, strip out the id from the update. + # There has to be a better way to handle this, but this'll do for now. + table_name = get_table_name(sql) + id_column = identity_column(table_name) + + if id_column + regex_col_name = Regexp.quote(quote_column_name(id_column.name)) + if sql =~ /, #{regex_col_name} = @?[0-9]*/ + sql = sql.gsub(/, #{regex_col_name} = @?[0-9]*/, '') + elsif sql =~ /\s#{regex_col_name} = @?[0-9]*,/ + sql = sql.gsub(/\s#{regex_col_name} = @?[0-9]*,/, '') + end + end + sql + end + + def update_sql?(sql) + !(sql =~ /^\s*(UPDATE|EXEC sp_executesql N'UPDATE)/i).nil? + end + def with_identity_insert_enabled(table_name) table_name = quote_table_name(table_name_or_views_table_name(table_name)) set_identity_insert(table_name, true) From 9ff57a91a8ef1a25162d6f04352176003e58f72b Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Fri, 17 Jan 2014 00:05:57 -0500 Subject: [PATCH 0156/1412] The tests now run with rails 4.1.0 removing duplicate standard_dump --- Gemfile | 3 ++- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 29 +++++++++++----------- test/cases/schema_dumper_test_sqlserver.rb | 11 ++------ test/cases/sqlserver_helper.rb | 1 + test/cases/sqlserver_test_case.rb | 2 +- 6 files changed, 22 insertions(+), 26 deletions(-) diff --git a/Gemfile b/Gemfile index a09e59a13..5b68644a0 100644 --- a/Gemfile +++ b/Gemfile @@ -41,7 +41,8 @@ group :development do gem 'bcrypt-ruby', '~> 3.0.0' gem 'bench_press' gem 'mocha' - gem 'minitest-spec-rails' + # TODO: Change back when it's ready + gem 'minitest-spec-rails', git: "https://github.com/metaskills/minitest-spec-rails.git" gem 'nokogiri' gem 'rake', '~> 0.9.2' gem 'rubocop' diff --git a/VERSION b/VERSION index fcdb2e109..c721eebf0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.0.0 +4.1.0.pre diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 203eabe0f..4f39d3c21 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,20 +1,21 @@ -$LOAD_PATH.push File.expand_path('../lib', __FILE__) +$:.push File.expand_path("../lib", __FILE__) Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY - s.name = 'activerecord-sqlserver-adapter' - s.version = File.read(File.expand_path('../VERSION', __FILE__)).strip - s.summary = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' - s.description = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' - - s.authors = ['Ken Collins', 'Anna Carey', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] - s.email = 'ken@metaskills.net' - s.homepage = 'http://github.com/rails-sqlserver/activerecord-sqlserver-adapter' - - s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*'] + s.name = "activerecord-sqlserver-adapter" + s.version = File.read(File.expand_path("../VERSION",__FILE__)).strip + s.summary = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." + s.description = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." + + s.authors = ['Ken Collins', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] + s.email = "ken@metaskills.net" + s.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter" + + s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*' ] s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - - s.add_dependency('activerecord', '~> 4.0.0') - s.add_dependency('arel', '~> 4.0.1') + + s.add_dependency('activerecord', '~> 4.1.0') + s.add_dependency('arel') end + diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 602a8654b..892a45d96 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,6 +1,8 @@ require 'cases/sqlserver_helper' +require 'cases/schema_dumper_test' require 'stringio' + class SchemaDumperTestSqlserver < ActiveRecord::TestCase setup :find_all_tables @@ -96,15 +98,6 @@ def test_coerced_types_line_up end end - private - - def standard_dump - stream = StringIO.new - ActiveRecord::SchemaDumper.ignore_tables = [] - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) - stream.string - end - end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index bfcc1f8c0..a46fa0cfd 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -24,6 +24,7 @@ require 'active_record/version' require 'active_record/connection_adapters/abstract_adapter' require 'minitest-spec-rails' +require 'minitest-spec-rails/init/active_support' require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' require 'models/topic' diff --git a/test/cases/sqlserver_test_case.rb b/test/cases/sqlserver_test_case.rb index 726da4c3e..02d6f1c24 100644 --- a/test/cases/sqlserver_test_case.rb +++ b/test/cases/sqlserver_test_case.rb @@ -1,4 +1,4 @@ -require 'active_record/test_case.rb' +require 'cases/test_case.rb' # TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' # This is a temporary hack until we can just get the sqlserver_ignored regex in rails From 444b0f9ec928b088ce7c1ed4db607a3e7063315f Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 09:22:13 -0400 Subject: [PATCH 0157/1412] Fixed bind type casting to re-set the casted value back to the bind, fixed bind parameter tests to work with SQL server --- .../sqlserver/database_statements.rb | 6 +- test/cases/bind_parameter_test_sqlserver.rb | 57 +++++++++++++++---- 2 files changed, 52 insertions(+), 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index bfa9ddbdf..142174c5d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -334,7 +334,11 @@ def do_exec_query(sql, name, binds, options = {}) next if ar_column && column.sql_type == 'timestamp' v = value names_and_types << if ar_column - v = value.to_i if column.is_integer? && value.present? + if column.is_integer? && value.present? + v = value.to_i + # Reset the casted value to the bind as required by Rails 4.1 + binds[index] = [column, v] + end "@#{index} #{column.sql_type_for_statement}" elsif column.acts_like?(:string) "@#{index} nvarchar(max)" diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index 08fad1641..4a3c9b0e1 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -1,25 +1,62 @@ require 'cases/sqlserver_helper' require 'models/topic' +require 'models_sqlserver/topic' -class BindParameterTestSqlserver < ActiveRecord::TestCase -end - -class ActiveRecord::BindParameterTest < ActiveRecord::TestCase - - fixtures :topics +class BindParameterTestSqlServer < ActiveRecord::TestCase COERCED_TESTS = [ - :test_binds_are_logged + :test_binds_are_logged, + :test_binds_are_logged_after_type_cast ] include SqlserverCoercedTest - # TODO: put a real test here + fixtures :topics + + class LogListener + attr_accessor :calls + + def initialize + @calls = [] + end + + def call(*args) + calls << args + end + end + + def setup + super + @connection = ActiveRecord::Base.connection + @listener = LogListener.new + @pk = Topic.columns.find { |c| c.primary } + ActiveSupport::Notifications.subscribe('sql.active_record', @listener) + end + + def teardown + ActiveSupport::Notifications.unsubscribe(@listener) + end + def test_coerced_binds_are_logged - assert true, 'they are!' + sub = @connection.substitute_at(@pk, 0) + binds = [[@pk, 1]] + sql = "select * from topics where id = #{sub}" + + @connection.exec_query(sql, 'SQL', binds) + + message = @listener.calls.find { |args| args[4][:sql].include? sql } + assert_equal binds, message[4][:binds] end + def test_coerced_binds_are_logged_after_type_cast + sub = @connection.substitute_at(@pk, 0) + binds = [[@pk, "3"]] + sql = "select * from topics where id = #{sub}" -end + @connection.exec_query(sql, 'SQL', binds) + message = @listener.calls.find { |args| args[4][:sql].include? sql } + assert_equal [[@pk, 3]], message[4][:binds] + end +end From 01011a62c34dd768012959daeec9d623537bc69e Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 11:23:26 -0400 Subject: [PATCH 0158/1412] Added the :authors fixture to AdapterTest to fix a failure in test_select_methods_passing_a_association_relation --- test/cases/adapter_test_sqlserver.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 3a109343d..ba85a141c 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -812,5 +812,7 @@ class AdapterTest < ActiveRecord::TestCase # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest + + fixtures :authors end end From c1469b0a87fef36af6deca990f9fd9b7013ce73f Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 11:55:48 -0400 Subject: [PATCH 0159/1412] Type casting is now happening, so the test regex needs to be updated --- test/cases/inheritance_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index a283c6a50..219af0f17 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -26,7 +26,7 @@ def test_coerced_a_bad_type_column def test_coerced_eager_load_belongs_to_primary_key_quoting con = Account.connection - assert_sql(/\[companies\]\.\[id\] IN \(N''1''\)/) do + assert_sql(/\[companies\]\.\[id\] IN \(1\)/) do Account.includes(:firm).find(1) end end From dbf384b88bf3a383e4cb04a510a810d8ea265218 Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 12:49:14 -0400 Subject: [PATCH 0160/1412] The test test_to_sql_on_eager_join was seeing slightly different result in that the SQL was embedded in a call to sp_executesql, which seems ok to me --- test/cases/relations_test_sqlserver.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index fe6c4558a..33c305bfc 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -3,7 +3,10 @@ class RelationTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_merging_reorders_bind_params] + COERCED_TESTS = [ + :test_merging_reorders_bind_params, + :test_to_sql_on_eager_join + ] # Until that patch is made to rails we are preventing this test from running in this gem. include SqlserverCoercedTest fixtures :posts @@ -24,4 +27,12 @@ def test_coerced_merging_reorders_bind_params merged = left.merge(right) assert_equal post, merged.first end -end \ No newline at end of file + + def test_coerced_to_sql_on_eager_join + expected = assert_sql { + Post.eager_load(:last_comment).order('comments.id DESC').to_a + }.first + actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql + assert_equal expected.include?(actual), true + end +end From 8035c190a1b16963e6cb602c5b5737db0a8712ad Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 12:54:18 -0400 Subject: [PATCH 0161/1412] Rails 4.1 seems to use strings for database config keys nows --- test/cases/resolver_test_sqlserver.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index 2f9eabb55..5718a3f11 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -17,27 +17,27 @@ class ResolverTest < ActiveRecord::TestCase def test_coerced_test_url_host_no_db spec = resolve 'sqlserver://foo?encoding=utf8' assert_equal({ - adapter: "sqlserver", - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "sqlserver", + "host" => "foo", + "encoding" => "utf8" }, spec) end def test_coerced_test_url_host_db spec = resolve 'sqlserver://foo/bar?encoding=utf8' assert_equal({ - adapter: "sqlserver", - database: "bar", - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "sqlserver", + "database" => "bar", + "host" => "foo", + "encoding" => "utf8" }, spec) end def test_coerced_test_url_port spec = resolve 'sqlserver://foo:123?encoding=utf8' assert_equal({ - adapter: "sqlserver", - port: 123, - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "sqlserver", + "port" => 123, + "host" => "foo", + "encoding" => "utf8" }, spec) end end From e439d8385e8421bd9570fc9d47b41a8b9ee2f8d2 Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 13:41:42 -0400 Subject: [PATCH 0162/1412] Fix how we are checking bind tests, can't use coerce since the test methods are defined in an if block --- test/cases/bind_parameter_test_sqlserver.rb | 77 +++++++-------------- 1 file changed, 24 insertions(+), 53 deletions(-) diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index 4a3c9b0e1..4a033e715 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -1,62 +1,33 @@ require 'cases/sqlserver_helper' require 'models/topic' require 'models_sqlserver/topic' - -class BindParameterTestSqlServer < ActiveRecord::TestCase - - COERCED_TESTS = [ - :test_binds_are_logged, - :test_binds_are_logged_after_type_cast - ] - - include SqlserverCoercedTest - - fixtures :topics - - class LogListener - attr_accessor :calls - - def initialize - @calls = [] +require 'cases/bind_parameter_test' + +# We don't coerce here because these tests are located inside of an if block +# and don't seem to be able to be properly overriden with the coerce +# functionality +module ActiveRecord + class BindParameterTest + def test_binds_are_logged + sub = @connection.substitute_at(@pk, 0) + binds = [[@pk, 1]] + sql = "select * from topics where id = #{sub}" + + @connection.exec_query(sql, 'SQL', binds) + + message = @listener.calls.find { |args| args[4][:sql].include? sql } + assert_equal binds, message[4][:binds] end - def call(*args) - calls << args - end - end + def test_binds_are_logged_after_type_cast + sub = @connection.substitute_at(@pk, 0) + binds = [[@pk, "3"]] + sql = "select * from topics where id = #{sub}" - def setup - super - @connection = ActiveRecord::Base.connection - @listener = LogListener.new - @pk = Topic.columns.find { |c| c.primary } - ActiveSupport::Notifications.subscribe('sql.active_record', @listener) - end + @connection.exec_query(sql, 'SQL', binds) - def teardown - ActiveSupport::Notifications.unsubscribe(@listener) - end - - def test_coerced_binds_are_logged - sub = @connection.substitute_at(@pk, 0) - binds = [[@pk, 1]] - sql = "select * from topics where id = #{sub}" - - @connection.exec_query(sql, 'SQL', binds) - - message = @listener.calls.find { |args| args[4][:sql].include? sql } - assert_equal binds, message[4][:binds] - end - - def test_coerced_binds_are_logged_after_type_cast - sub = @connection.substitute_at(@pk, 0) - binds = [[@pk, "3"]] - sql = "select * from topics where id = #{sub}" - - @connection.exec_query(sql, 'SQL', binds) - - message = @listener.calls.find { |args| args[4][:sql].include? sql } - assert_equal [[@pk, 3]], message[4][:binds] + message = @listener.calls.find { |args| args[4][:sql].include? sql } + assert_equal [[@pk, 3]], message[4][:binds] + end end - end From a451b28bc1c2ded9a780254e9827e222e0df5524 Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 14:18:39 -0400 Subject: [PATCH 0163/1412] Fixed tests in rails/activerecord/test/cases/connection_adapters/connection_handler_test.rb by setting the RAILS_ENV to 'default_env' --- Rakefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Rakefile b/Rakefile index 7edb4253c..c750dec89 100644 --- a/Rakefile +++ b/Rakefile @@ -6,6 +6,11 @@ AREL_PATH = Gem.loaded_specs['arel'].full_gem_path # Notes for cross compile: # $ gcla ; bundle install ; rake compile ; rake cross compile ; rake cross native gem +# Since the Gemfile for this project requires, rails, it ends up causing +# Rails.env to be defined, which affects some of the unit tests. We fix this +# by setting the RAILS_ENV to "default_env" +ENV['RAILS_ENV'] = 'default_env' + def test_libs ['lib', 'test', From efbead12684e8bca119858b09813649c31a086a3 Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 14:37:58 -0400 Subject: [PATCH 0164/1412] Unescape quotes from default values --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index baad33152..b0a19f95b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -256,7 +256,7 @@ def column_definitions(table_name) nil else match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m) - match_data ? match_data[1] : nil + match_data ? match_data[1].gsub("''", "'") : nil end ci[:null] = ci[:is_nullable].to_i == 1 ci.delete(:is_nullable) From be71de1908592f8f189497258225e1870a2797bc Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 16:11:19 -0400 Subject: [PATCH 0165/1412] Added support for create_table :as, which fixes tests MigrationTest#test_create_table_with_query and MigrationTest#test_create_table_with_query_from_relation --- .../sqlserver/schema_creation.rb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index c30ff063b..1e97cf69e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -22,6 +22,34 @@ def add_column_options!(sql, options) super end end + + def visit_TableDefinition(o) + quoted_name = "#{quote_table_name((o.temporary ? '#' : '') + o.name.to_s)} " + + if o.as + if o.as.is_a?(ActiveRecord::Relation) + select = o.as.to_sql + elsif o.as.is_a?(String) + select = o.as + else + raise "Only able to generate a table from a SELECT statement passed as a String or ActiveRecord::Relation" + end + + create_sql = 'SELECT * INTO ' + create_sql << quoted_name + create_sql << 'FROM (' + create_sql << select + create_sql << ') AS __sq' + + else + create_sql = "CREATE TABLE " + create_sql << quoted_name + create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " + create_sql << "#{o.options}" + end + + create_sql + end end end end From c1e1db14d6458db3f98f1bedf522c38ba551ed96 Mon Sep 17 00:00:00 2001 From: wbond Date: Thu, 15 May 2014 16:27:59 -0400 Subject: [PATCH 0166/1412] Clear the schema cache for a table after creating it to prevent a cached negative response - fixes test ActiveRecord::Migration::ChangeSchemaTest#test_change_column_null --- .../connection_adapters/sqlserver/schema_statements.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b0a19f95b..6a8863bfc 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -16,6 +16,12 @@ def table_exists?(table_name) super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name) end + def create_table(table_name, options = {}) + res = super + schema_cache.clear_table_cache!(table_name) + res + end + def indexes(table_name, name = nil) data = select("EXEC sp_helpindex #{quote(table_name)}", name) rescue [] data.reduce([]) do |indexes, index| From a9b715a16792bdf053e821bfdb8682bbb430e4c7 Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 10:58:09 -0400 Subject: [PATCH 0167/1412] PersistencesTest was renamed to PersistenceTest in rails 4.1, so we have to do it in our codebase also --- test/cases/persistence_test_sqlserver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 510c9949b..42b5d7aef 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -16,10 +16,10 @@ require 'models_sqlserver/topic' require 'rexml/document' -class PersistencesTestSqlserver < ActiveRecord::TestCase +class PersistenceTestSqlserver < ActiveRecord::TestCase end -class PersistencesTest < ActiveRecord::TestCase +class PersistenceTest < ActiveRecord::TestCase fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans From 2f509a6715302d9b0b1f604b95427519ea139e9f Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 11:38:54 -0400 Subject: [PATCH 0168/1412] Use Arel::Nodes::BindParam instead of Arel.sql for substitute_at so that ActiveRecord::Relation merging works properly - tests RelationMergingTest#test_merging_reorders_bind_params and RelationMergingTest#test_relation_to_sql --- lib/active_record/connection_adapters/sqlserver/quoting.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index bac830173..609a71b95 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -67,7 +67,7 @@ def substitute_at(column, index) if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' nil else - Arel.sql "@#{index}" + Arel::Nodes::BindParam.new "@#{index}" end end From fa2443ef7ef0e2abec0a577d69b1fcfd00ae208b Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 12:03:32 -0400 Subject: [PATCH 0169/1412] Coerced test for ActiveRecord::PredicateBuilderTest#test_registering_new_handlers since the regex doesn't work for T-SQL --- .../cases/predicate_builder_test_sqlserver.rb | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/cases/predicate_builder_test_sqlserver.rb diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/predicate_builder_test_sqlserver.rb new file mode 100644 index 000000000..1524f6aa8 --- /dev/null +++ b/test/cases/predicate_builder_test_sqlserver.rb @@ -0,0 +1,20 @@ +require 'cases/sqlserver_helper' +require 'models/topic' +require 'models_sqlserver/topic' + +module ActiveRecord + class PredicateBuilderTest < ActiveRecord::TestCase + + COERCED_TESTS = [:test_registering_new_handlers] + + include SqlserverCoercedTest + + def test_coerced_registering_new_handlers + ActiveRecord::PredicateBuilder.register_handler(Regexp, proc do |column, value| + Arel::Nodes::InfixOperation.new('~', column, value.source) + end) + + assert_match %r{\[topics\].\[title\] ~ N'rails'}i, Topic.where(title: /rails/).to_sql + end + end +end From f41742103c3705ad4d340b3f90fe53dc66679fc0 Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 13:26:17 -0400 Subject: [PATCH 0170/1412] Fixed test NamedScopingTest#test_scopes_honor_current_scopes_from_when_defined by adding a second column to the ORDER BY condition of the :ranked_by_comments scope of the Post model --- test/cases/named_scoping_test_sqlserver.rb | 6 ++++++ test/models_sqlserver/post.rb | 3 +++ 2 files changed, 9 insertions(+) create mode 100644 test/cases/named_scoping_test_sqlserver.rb create mode 100644 test/models_sqlserver/post.rb diff --git a/test/cases/named_scoping_test_sqlserver.rb b/test/cases/named_scoping_test_sqlserver.rb new file mode 100644 index 000000000..398c51109 --- /dev/null +++ b/test/cases/named_scoping_test_sqlserver.rb @@ -0,0 +1,6 @@ +require 'cases/sqlserver_helper' +require 'models/post' +require 'models_sqlserver/post' + +# This file is really just to ensure we fix the order by on the +# :ranked_by_comments scope of Post diff --git a/test/models_sqlserver/post.rb b/test/models_sqlserver/post.rb new file mode 100644 index 000000000..ba846ea4a --- /dev/null +++ b/test/models_sqlserver/post.rb @@ -0,0 +1,3 @@ +class Post < ActiveRecord::Base + scope :ranked_by_comments, -> { order("comments_count DESC, id ASC") } +end From 0b638b7d843857e824ce09068a85fbb2393154e3 Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 13:58:48 -0400 Subject: [PATCH 0171/1412] Added savepoint name param to create_savepoint, release_savepoint and rollback_to_savepoint --- .../sqlserver/database_statements.rb | 10 +++++----- test/cases/transaction_test_sqlserver.rb | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 142174c5d..4bd36bd20 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -56,15 +56,15 @@ def rollback_db_transaction do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end - def create_savepoint - disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" } + def create_savepoint(name = current_savepoint_name) + disable_auto_reconnect { do_execute "SAVE TRANSACTION #{name}" } end - def release_savepoint + def release_savepoint(name = current_savepoint_name) end - def rollback_to_savepoint - disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" } + def rollback_to_savepoint(name = current_savepoint_name) + disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{name}" } end def add_limit_offset!(_sql, _options) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 36976b287..a78bfd2ce 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -46,3 +46,19 @@ def assert_no_ships end +class TransactionTest < ActiveRecord::TestCase + include SqlserverCoercedTest + + COERCED_TESTS = [:test_releasing_named_savepoints] + + def test_coerced_releasing_named_savepoints + Topic.transaction do + Topic.connection.create_savepoint("another") + Topic.connection.release_savepoint("another") + + # The origin rails test tries to re-release the savepoint, but + # since sqlserver doesn't have the concept of releasing, it doesn't + # fail, so we just omit that part here + end + end +end From d1760460f8b7f0932b674243cecef907d0252847 Mon Sep 17 00:00:00 2001 From: wbond Date: Fri, 16 May 2014 14:59:16 -0400 Subject: [PATCH 0172/1412] Updated UUID enhancements to work with rails 4.1 API --- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6a8863bfc..fd2c7bc21 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -414,8 +414,8 @@ def identity_column(table_name) private - def create_table_definition(name, temporary, options) - TableDefinition.new native_database_types, name, temporary, options + def create_table_definition(name, temporary, options, as = nil) + TableDefinition.new native_database_types, name, temporary, options, as end end end From 4e9e7dd5028221adaf832437feebcc88d0a0e1c2 Mon Sep 17 00:00:00 2001 From: coldnebo Date: Mon, 19 May 2014 09:32:53 -0400 Subject: [PATCH 0173/1412] fix for #330 --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index fd2c7bc21..32d036d56 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -352,7 +352,7 @@ def table_name_or_views_table_name(table_name) def views_real_column_name(table_name, column_name) view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] - match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) + match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) rescue nil match_data ? match_data[1] : column_name end From e75bd0b0ce81edccf209d18e353c67bfb0d4238b Mon Sep 17 00:00:00 2001 From: wbond Date: Mon, 19 May 2014 16:20:21 -0400 Subject: [PATCH 0174/1412] Updated readme with up-to-date Rails and Ruby version support --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0a3088f14..80d91f847 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,9 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server ## What's New -* Rails 4 support (not yet released, look at outstanding issues) -* Ruby 2.0.0 and 2.1.0.prerelease support +* Rails 4.0 and 4.1 support +* Ruby 2.0 and 2.1 support + #### Testing Rake Tasks Support From a5ff7112efeedfd5bbd50c5e86bda7fb301a2980 Mon Sep 17 00:00:00 2001 From: wbond Date: Mon, 19 May 2014 16:20:33 -0400 Subject: [PATCH 0175/1412] Changed line endings to unix --- RUNNING_UNIT_TESTS.md | 206 +++++++++++++++++++++--------------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index b86a87341..acc9fb695 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -1,104 +1,104 @@ - -# How To Run The Test! - -This process is much easier than it has been before! - - -## TL;DR - -Default testing uses DBLIB with TinyTDS. - -* Setup two databases in SQL Server, [activerecord_unittest] and [activerecord_unittest2] -* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. - - http://twitpic.com/9bsiyp/full - - http://twitpic.com/9bsj7z/full - - http://twitpic.com/9bsjdx/full - - http://twitpic.com/9bsjl7/full -* $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git -* $ bundle install -* $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' - - -## Creating the test databases - -The default names for the test databases are `activerecord_unittest` and `activerecord_unittest2`. If you want to use another database name then be sure to update the connection file that matches your connection method in test/connections/native_sqlserver_#{connection_method}/connection.rb. Define a user named 'rails' in SQL Server with all privileges granted for the test databases. Use an empty password for said user. - -The connection files make certain assumptions. For instance, the ODBC connection assumes you have a DSN setup that matches the name of the default database names. Remember too you have to set an environment variable for the DSN of the adapter, see the connection.rb file that matches your connection mode for details. - - -## Cloning The Repos - -Clone adapter git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git. The master branch is the one under development for Rails 3, track the repos 2-3-stable branch for 2.x development. - -The tests of this adapter depend on the existence of the Rails which under the 3.1 version and above is automatically cloned for you with bundler. However you can clone Rails from git://github.com/rails/rails.git and set the `RAILS_SOURCE` environment variable so bundler will use another local path instead. - -``` -$ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git -``` - -Optionally, you an just let bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. - -``` -$ export RAILS_VERSION='3.2.13' -$ bundle install -``` - - -## Configure DB Connection - -Please consult the `test/config.yml` file which is used to parse the configuration options for the DB connections when running tests. This file has overrides for any connection mode that you can set using simple environment variables. Assuming you are using FreeTDS 0.91 and above - -``` -$ export ACTIVERECORD_UNITTEST_HOST='my.db.net' # Defaults to localhost -$ export ACTIVERECORD_UNITTEST_PORT='1533' # Defaults to 1433 -``` - -If you have FreeTDS installed and/or want to use a named dataserver in your freetds.conf file - -``` -$ export ACTIVERECORD_UNITTEST_DATASERVER='mydbname' -``` - -These can be passed down to rake too. - -``` -$ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' -``` - - -## Bundling - -Now with that out of the way you can run "bundle install" to hook everything up. Our tests use bundler to setup the load paths correctly. The default mode is DBLIB using TinyTDS. It is important to use bundle exec so we can wire up the ActiveRecord test libs correctly. - -``` -$ bundle exec rake test -``` - - -## Testing Options - -The Gemfile contains groups for `:tinytds` and `:odbc`. By default it will install both gems which allows you to run the full test suite in either connection mode. If for some reason any one of these is problematic or of no concern, you could always opt out of bundling either gem with something like this. - -``` -$ bundle install --without odbc -``` - -You can run different connection modes using the following rake commands. Again, the DBLIB connection mode using TinyTDS is the default test task. - -``` -$ bundle exec rake test:dblib -$ bundle exec rake test:odbc -``` - -By default, Bundler will download the Rails git repo and use the git tag that matches the dependency version in our gemspec. If you want to test another version of Rails, you can either temporarily change the :tag for Rails in the Gemfile. Likewise, you can clone the Rails repo your self to another directory and use the `RAILS_SOURCE` environment variable. - - -## Current Expected Failures - -* Misc Date/Time erros when using ODBC mode. -* Misc Date/Time erros when testing SQL Server 2005. -* A find with Limit, Order, and Includes may return fewer results than the limit specifies - -FinderTest#test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct [/Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/finder_test_sqlserver.rb:28]: -Expected: 2 + +# How To Run The Test! + +This process is much easier than it has been before! + + +## TL;DR + +Default testing uses DBLIB with TinyTDS. + +* Setup two databases in SQL Server, [activerecord_unittest] and [activerecord_unittest2] +* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. + - http://twitpic.com/9bsiyp/full + - http://twitpic.com/9bsj7z/full + - http://twitpic.com/9bsjdx/full + - http://twitpic.com/9bsjl7/full +* $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git +* $ bundle install +* $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' + + +## Creating the test databases + +The default names for the test databases are `activerecord_unittest` and `activerecord_unittest2`. If you want to use another database name then be sure to update the connection file that matches your connection method in test/connections/native_sqlserver_#{connection_method}/connection.rb. Define a user named 'rails' in SQL Server with all privileges granted for the test databases. Use an empty password for said user. + +The connection files make certain assumptions. For instance, the ODBC connection assumes you have a DSN setup that matches the name of the default database names. Remember too you have to set an environment variable for the DSN of the adapter, see the connection.rb file that matches your connection mode for details. + + +## Cloning The Repos + +Clone adapter git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git. The master branch is the one under development for Rails 3, track the repos 2-3-stable branch for 2.x development. + +The tests of this adapter depend on the existence of the Rails which under the 3.1 version and above is automatically cloned for you with bundler. However you can clone Rails from git://github.com/rails/rails.git and set the `RAILS_SOURCE` environment variable so bundler will use another local path instead. + +``` +$ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git +``` + +Optionally, you an just let bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. + +``` +$ export RAILS_VERSION='3.2.13' +$ bundle install +``` + + +## Configure DB Connection + +Please consult the `test/config.yml` file which is used to parse the configuration options for the DB connections when running tests. This file has overrides for any connection mode that you can set using simple environment variables. Assuming you are using FreeTDS 0.91 and above + +``` +$ export ACTIVERECORD_UNITTEST_HOST='my.db.net' # Defaults to localhost +$ export ACTIVERECORD_UNITTEST_PORT='1533' # Defaults to 1433 +``` + +If you have FreeTDS installed and/or want to use a named dataserver in your freetds.conf file + +``` +$ export ACTIVERECORD_UNITTEST_DATASERVER='mydbname' +``` + +These can be passed down to rake too. + +``` +$ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' +``` + + +## Bundling + +Now with that out of the way you can run "bundle install" to hook everything up. Our tests use bundler to setup the load paths correctly. The default mode is DBLIB using TinyTDS. It is important to use bundle exec so we can wire up the ActiveRecord test libs correctly. + +``` +$ bundle exec rake test +``` + + +## Testing Options + +The Gemfile contains groups for `:tinytds` and `:odbc`. By default it will install both gems which allows you to run the full test suite in either connection mode. If for some reason any one of these is problematic or of no concern, you could always opt out of bundling either gem with something like this. + +``` +$ bundle install --without odbc +``` + +You can run different connection modes using the following rake commands. Again, the DBLIB connection mode using TinyTDS is the default test task. + +``` +$ bundle exec rake test:dblib +$ bundle exec rake test:odbc +``` + +By default, Bundler will download the Rails git repo and use the git tag that matches the dependency version in our gemspec. If you want to test another version of Rails, you can either temporarily change the :tag for Rails in the Gemfile. Likewise, you can clone the Rails repo your self to another directory and use the `RAILS_SOURCE` environment variable. + + +## Current Expected Failures + +* Misc Date/Time erros when using ODBC mode. +* Misc Date/Time erros when testing SQL Server 2005. +* A find with Limit, Order, and Includes may return fewer results than the limit specifies + +FinderTest#test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct [/Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/finder_test_sqlserver.rb:28]: +Expected: 2 Actual: 1 \ No newline at end of file From 679b46c2286cc997314bde740616dfd3ece3bd9b Mon Sep 17 00:00:00 2001 From: wbond Date: Mon, 19 May 2014 16:20:58 -0400 Subject: [PATCH 0176/1412] Removed out-dated reference to a failing test --- RUNNING_UNIT_TESTS.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index acc9fb695..8ebe4bc40 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -97,8 +97,3 @@ By default, Bundler will download the Rails git repo and use the git tag that ma * Misc Date/Time erros when using ODBC mode. * Misc Date/Time erros when testing SQL Server 2005. -* A find with Limit, Order, and Includes may return fewer results than the limit specifies - -FinderTest#test_coerced_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct [/Users/acarey/code/nextgear/sqlserver/annaswims/activerecord-sqlserver-adapter/test/cases/finder_test_sqlserver.rb:28]: -Expected: 2 - Actual: 1 \ No newline at end of file From eaced2841bd91301fecec1f7f0b7e2ab5d1b4327 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Fri, 23 May 2014 00:32:44 -0400 Subject: [PATCH 0177/1412] Fixed some spacing and single quote issues --- activerecord-sqlserver-adapter.gemspec | 24 ++++++++-------- .../sqlserver/database_statements.rb | 4 +-- .../sqlserver/schema_creation.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 28 +++++++++---------- .../connection_adapters/sqlserver_column.rb | 2 +- 5 files changed, 30 insertions(+), 30 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 4f39d3c21..99f206fed 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,20 +1,20 @@ -$:.push File.expand_path("../lib", __FILE__) +$LOAD_PATH.push File.expand_path('../lib', __FILE__) Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY - s.name = "activerecord-sqlserver-adapter" - s.version = File.read(File.expand_path("../VERSION",__FILE__)).strip - s.summary = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." - s.description = "ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher." - - s.authors = ['Ken Collins', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] - s.email = "ken@metaskills.net" - s.homepage = "http://github.com/rails-sqlserver/activerecord-sqlserver-adapter" - - s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*' ] + s.name = 'activerecord-sqlserver-adapter' + s.version = File.read(File.expand_path('../VERSION', __FILE__)).strip + s.summary = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' + s.description = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' + + s.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] + s.email = 'ken@metaskills.net' + s.homepage = 'http://github.com/rails-sqlserver/activerecord-sqlserver-adapter' + + s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*'] s.require_path = 'lib' s.rubyforge_project = 'activerecord-sqlserver-adapter' - + s.add_dependency('activerecord', '~> 4.1.0') s.add_dependency('arel') end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 4bd36bd20..a3a8e6481 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -18,8 +18,8 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) } elsif update_sql?(sql) - sql = strip_ident_from_update(sql) - do_exec_query(sql, name, binds) + sql = strip_ident_from_update(sql) + do_exec_query(sql, name, binds) else do_exec_query(sql, name, binds) end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 1e97cf69e..41114aad3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -32,7 +32,7 @@ def visit_TableDefinition(o) elsif o.as.is_a?(String) select = o.as else - raise "Only able to generate a table from a SELECT statement passed as a String or ActiveRecord::Relation" + raise 'Only able to generate a table from a SELECT statement passed as a String or ActiveRecord::Relation' end create_sql = 'SELECT * INTO ' diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 11e6ca96d..82f6d4a56 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -298,26 +298,26 @@ def dblib_connect(config) encoding: encoding(config), azure: config[:azure] ).tap do |client| - if config[:azure] - client.execute('SET ANSI_NULLS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET ANSI_NULL_DFLT_ON ON').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - client.execute('SET ANSI_PADDING ON').do - client.execute('SET QUOTED_IDENTIFIER ON') - client.execute('SET ANSI_WARNINGS ON').do - else - client.execute('SET ANSI_DEFAULTS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - end + if config[:azure] + client.execute('SET ANSI_NULLS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET ANSI_NULL_DFLT_ON ON').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + client.execute('SET ANSI_PADDING ON').do + client.execute('SET QUOTED_IDENTIFIER ON') + client.execute('SET ANSI_WARNINGS ON').do + else + client.execute('SET ANSI_DEFAULTS ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do + end client.execute('SET TEXTSIZE 2147483647').do client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do end end def appname(config) - config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil + config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil end def login_timeout(config) diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index caf3e819e..dc5e00e62 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -112,5 +112,5 @@ def simplified_datetime end end end # class SQLServerColumn - end # module ConnectionAdapters + end # module ConnectionAdapters end # module ActiveRecord From 34d5c731059b5f61bceb91b35cd1c16950639bf6 Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Fri, 23 May 2014 00:42:21 -0400 Subject: [PATCH 0178/1412] Relese 4.1.0 version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c721eebf0..ee74734aa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.1.0.pre +4.1.0 From d7a2181f2741aba898aae6ef5c4692ce857bdafe Mon Sep 17 00:00:00 2001 From: Bryant Ung Date: Sat, 5 Jul 2014 18:26:03 -0700 Subject: [PATCH 0179/1412] added support for sql server 2014 --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 82f6d4a56..20718a6e3 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -37,7 +37,7 @@ class SQLServerAdapter < AbstractAdapter VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip ADAPTER_NAME = 'SQLServer'.freeze DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012] + SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012, 2014] attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition @@ -204,6 +204,10 @@ def sqlserver_2012? @database_year == 2012 end + def sqlserver_2014? + @database_year == 2014 + end + def sqlserver_azure? @sqlserver_azure end From fda9e759e664b27da28e80289cc815f299d185be Mon Sep 17 00:00:00 2001 From: Anna Carey Date: Tue, 26 Aug 2014 20:47:38 -0400 Subject: [PATCH 0180/1412] Revert "prevent downstream nil exception when view_definition is nil." --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 32d036d56..fd2c7bc21 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -352,7 +352,7 @@ def table_name_or_views_table_name(table_name) def views_real_column_name(table_name, column_name) view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] - match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) rescue nil + match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name end From f311e4d84ab51aba41ac4b1f67be47402ebca6d6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 21 Nov 2014 09:46:09 -0500 Subject: [PATCH 0181/1412] Note about maintainers. --- README.md | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 80d91f847..b86c75ce7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. +**This project is looking for a new maintainers** + The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. @@ -89,7 +91,7 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_binary_database_type = #### Setting Unicode Types As Default -By default the adapter will use unicode safe data types for `:string` and `:text` types when defining/changing the schema! This was changed in version 3.1 since it is about time we push better unicode support and since we default to TinyTDS (DBLIB) which supports unicode queries and data. If you choose, you can set the following class attribute in a config/initializers file that will disable this behavior. +By default the adapter will use unicode safe data types for `:string` and `:text` types when defining/changing the schema! This was changed in version 3.1 since it is about time we push better unicode support and since we default to TinyTDS (DBLIB) which supports unicode queries and data. If you choose, you can set the following class attribute in a config/initializers file that will disable this behavior. ```ruby # Default @@ -240,12 +242,7 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- * jeremydurham (Jeremy Durham) -## Donators - -I am trying to save up for a Happy Hacking pro keyboard. Help me out via GitTip! https://www.gittip.com/metaskills/ - - ## License -Copyright © 2008-2011. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. +Copyright © 2008-2014. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. From f8e56f2cceda5d83e39bdab96297622b47082d7d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 21 Nov 2014 09:47:47 -0500 Subject: [PATCH 0182/1412] Maintainer link. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b86c75ce7..c6d79d2ae 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. -**This project is looking for a new maintainers** +**This project is looking for a new maintainers. Join the [discusion here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/364)** The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. From 89cc8d41bdd58e3513d7bf76fa7a51835e794747 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 30 Dec 2014 08:02:15 -0500 Subject: [PATCH 0183/1412] Master is now 4.2.x --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ee74734aa..6aba2b245 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.1.0 +4.2.0 From 215a964f442e03f03a6627d9865883989b2ee766 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 30 Dec 2014 12:50:37 -0500 Subject: [PATCH 0184/1412] Refactor gemspec and clean up project test deps a bit. --- .rubocop.yml | 20 --------- CHANGELOG | 13 ++++++ Gemfile | 16 +------ Rakefile | 35 +++------------- VERSION | 1 - activerecord-sqlserver-adapter.gemspec | 42 ++++++++++--------- .../connection_adapters/sqlserver/version.rb | 14 +++++++ .../connection_adapters/sqlserver_adapter.rb | 23 +++++----- test/cases/arel_helper.rb | 20 --------- test/cases/sqlserver_helper.rb | 20 +++------ test/config.yml | 4 +- 11 files changed, 75 insertions(+), 133 deletions(-) delete mode 100644 .rubocop.yml delete mode 100644 VERSION create mode 100644 lib/active_record/connection_adapters/sqlserver/version.rb delete mode 100644 test/cases/arel_helper.rb diff --git a/.rubocop.yml b/.rubocop.yml deleted file mode 100644 index 732301fc9..000000000 --- a/.rubocop.yml +++ /dev/null @@ -1,20 +0,0 @@ -AllCops: - Exclude: - - doc/**/* - - test/**/* -LineLength: - Max: 200 -Documentation: - Enabled: false -MethodLength: - Enabled: false - -# TODO: enable after the more important stuff is fixed -LineLength: - Enabled: false -CyclomaticComplexity: - Enabled: false -SignalException: - Enabled: false -MethodName: - Enabled: false diff --git a/CHANGELOG b/CHANGELOG index bc07b3d4c..4bf84b83d 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,21 @@ + +* 4.2.0 * + +* ... + + +* 4.1.0 * + +* Not sure if this even happened. Just got to 4.2.0 :) + + * 4.0.0 * + * Dropped support for ruby 1.8.7 * Removed deadlock victim retry in favor of Isolation Level * Removed auto_explain_threshold_in_seconds (not used in rails 4) + * 3.2.12 * * Revert string_to_binary changes in 457af60e. Fixes #273. diff --git a/Gemfile b/Gemfile index 5b68644a0..d2fd26047 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ - source 'https://rubygems.org' +gemspec if ENV['RAILS_SOURCE'] gemspec path: ENV['RAILS_SOURCE'] @@ -37,17 +37,3 @@ group :odbc do gem 'ruby-odbc' end -group :development do - gem 'bcrypt-ruby', '~> 3.0.0' - gem 'bench_press' - gem 'mocha' - # TODO: Change back when it's ready - gem 'minitest-spec-rails', git: "https://github.com/metaskills/minitest-spec-rails.git" - gem 'nokogiri' - gem 'rake', '~> 0.9.2' - gem 'rubocop' - gem 'ruby-prof' - gem 'simplecov' - gem 'ruby-graphviz' - gem 'pry' -end diff --git a/Rakefile b/Rakefile index c750dec89..4230f2fcb 100644 --- a/Rakefile +++ b/Rakefile @@ -1,10 +1,7 @@ require 'rake' require 'rake/testtask' -AR_PATH = Gem.loaded_specs['activerecord'].full_gem_path -AREL_PATH = Gem.loaded_specs['arel'].full_gem_path -# Notes for cross compile: -# $ gcla ; bundle install ; rake compile ; rake cross compile ; rake cross native gem +AR_PATH = Gem.loaded_specs['activerecord'].full_gem_path # Since the Gemfile for this project requires, rails, it ends up causing # Rails.env to be defined, which affects some of the unit tests. We fix this @@ -12,37 +9,23 @@ AREL_PATH = Gem.loaded_specs['arel'].full_gem_path ENV['RAILS_ENV'] = 'default_env' def test_libs - ['lib', - 'test', - "#{File.join(AR_PATH, 'test')}", - "#{File.join(AREL_PATH, 'test')}" - ] + ar_lib = File.join AR_PATH, 'lib' + ar_test = File.join AR_PATH, 'test' + ['lib', 'test', ar_lib, ar_test] end -# bundle exec rake test SQLSERVER_ONLY=true -# -# If you have trouble running single tests (errors about requirements): -# http://veganswithtypewriters.net/blog/2013/06/29/weirdness-with-rake-solved/ def test_files test_setup = ['test/cases/sqlserver_helper.rb'] - return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] - sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') - ar_cases = Dir.glob("#{AR_PATH}/test/cases/**/*_test.rb") adapter_cases = Dir.glob("#{AR_PATH}/test/cases/adapters/**/*_test.rb") - - arel_cases = Dir.glob("#{AREL_PATH}/test/**/test_*.rb") - if ENV['SQLSERVER_ONLY'] sqlserver_cases elsif ENV['ACTIVERECORD_ONLY'] test_setup + (ar_cases - adapter_cases) - elsif ENV['AREL_ONLY'] - arel_cases else - test_setup + arel_cases + sqlserver_cases + (ar_cases - adapter_cases) + test_setup + sqlserver_cases + (ar_cases - adapter_cases) end end @@ -75,22 +58,16 @@ task 'test:dblib' => 'test:dblib:env' task 'test:odbc' => 'test:odbc:env' namespace :profile do - %w(dblib odbc).each do |mode| + ['dblib', 'odbc'].each do |mode| namespace mode.to_sym do - Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| - profile_case = File.basename(test_file).sub('_profile_case.rb', '') - Rake::TestTask.new(profile_case) do |t| t.libs = test_libs t.test_files = [test_file] t.verbose = true end - end - end end - end diff --git a/VERSION b/VERSION deleted file mode 100644 index 6aba2b245..000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -4.2.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 99f206fed..261c144f7 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,21 +1,25 @@ -$LOAD_PATH.push File.expand_path('../lib', __FILE__) +# -*- encoding: utf-8 -*- +$:.push File.expand_path("../lib", __FILE__) +require "active_record/connection_adapters/sqlserver/version" -Gem::Specification.new do |s| - s.platform = Gem::Platform::RUBY - s.name = 'activerecord-sqlserver-adapter' - s.version = File.read(File.expand_path('../VERSION', __FILE__)).strip - s.summary = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' - s.description = 'ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher.' - - s.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] - s.email = 'ken@metaskills.net' - s.homepage = 'http://github.com/rails-sqlserver/activerecord-sqlserver-adapter' - - s.files = Dir['CHANGELOG', 'MIT-LICENSE', 'README.rdoc', 'VERSION', 'lib/**/*'] - s.require_path = 'lib' - s.rubyforge_project = 'activerecord-sqlserver-adapter' - - s.add_dependency('activerecord', '~> 4.1.0') - s.add_dependency('arel') +Gem::Specification.new do |spec| + spec.name = 'activerecord-sqlserver-adapter' + spec.version = ActiveRecord::ConnectionAdapters::Sqlserver::Version::VERSION + spec.platform = Gem::Platform::RUBY + spec.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] + 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 = spec.summary + 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.require_paths = ['lib'] + spec.add_dependency 'activerecord', '~> 4.2.0' + spec.add_development_dependency 'bundler' + spec.add_development_dependency 'rake' + spec.add_development_dependency 'minitest-spec-rails' + spec.add_development_dependency 'mocha' + spec.add_development_dependency 'nokogiri' + spec.add_development_dependency 'pry' end - diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb new file mode 100644 index 000000000..1fd345ee0 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -0,0 +1,14 @@ +module ActiveRecord + module ConnectionAdapters + module Sqlserver + module Version + + VERSION = '4.2.0' + + SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012, 2014] + DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ + + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 20718a6e3..bb1a2f647 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -10,6 +10,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/relation' +require 'active_record/connection_adapters/sqlserver/version' require 'active_record/connection_adapters/sqlserver/database_limits' require 'active_record/connection_adapters/sqlserver/database_statements' require 'active_record/connection_adapters/sqlserver/errors' @@ -20,24 +21,22 @@ require 'active_record/connection_adapters/sqlserver/table_definition' require 'active_record/connection_adapters/sqlserver/quoting' require 'active_record/connection_adapters/sqlserver/utils' - require 'active_record/sqlserver_base' require 'active_record/connection_adapters/sqlserver_column' module ActiveRecord module ConnectionAdapters class SQLServerAdapter < AbstractAdapter - include Sqlserver::Quoting - include Sqlserver::DatabaseStatements - include Sqlserver::Showplan - include Sqlserver::SchemaStatements - include Sqlserver::DatabaseLimits - include Sqlserver::Errors - - VERSION = File.read(File.expand_path('../../../../VERSION', __FILE__)).strip - ADAPTER_NAME = 'SQLServer'.freeze - DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012, 2014] + + include Sqlserver::Version, + Sqlserver::Quoting, + Sqlserver::DatabaseStatements, + Sqlserver::Showplan, + Sqlserver::SchemaStatements, + Sqlserver::DatabaseLimits, + Sqlserver::Errors + + ADAPTER_NAME = 'SQLServer'.freeze attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition diff --git a/test/cases/arel_helper.rb b/test/cases/arel_helper.rb deleted file mode 100644 index 9bbc328aa..000000000 --- a/test/cases/arel_helper.rb +++ /dev/null @@ -1,20 +0,0 @@ -AREL_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['arel'].full_gem_path,'test')) -$LOAD_PATH.unshift AREL_TEST_ROOT - -# TODO: Find A better way to run Arel tests without failing on -# SQL Server brackets instead of quotes - class Object - def must_be_like other - actual = gsub(/\s+/, ' ').gsub(/\[|\]/,'"').gsub(/N\'/,'\'').strip - expected = other.gsub(/\s+/, ' ').strip - actual.must_equal expected - end - end - - -# Useful for debugging Arel. -# You can call it like arel_to_png(User.where(name: "foo").arel) -def arel_to_png(arel, file_name = "query") - graph = GraphViz.parse_string(arel.to_dot) - graph.output(png: "#{file_name}.png") -end \ No newline at end of file diff --git a/test/cases/sqlserver_helper.rb b/test/cases/sqlserver_helper.rb index a46fa0cfd..7bf7511dc 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/sqlserver_helper.rb @@ -9,16 +9,7 @@ $LOAD_PATH.unshift ACTIVERECORD_TEST_ROOT -require 'rubygems' -require 'bundler' -Bundler.setup -require 'simplecov' -SimpleCov.start do - add_filter "/test/" -end -require 'pry' -require 'graphviz' -require 'mocha/api' +require 'bundler' ; Bundler.require :development, :test require 'active_support/dependencies' require 'active_record' require 'active_record/version' @@ -28,19 +19,16 @@ require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' require 'models/topic' -require 'cases/arel_helper' require 'cases/sqlserver_test_case' -GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly?) - ActiveRecord::Migration.verbose = false ActiveRecord::Base.logger = Logger.new(File.expand_path(File.join(SQLSERVER_TEST_ROOT,'debug.log'))) ActiveRecord::Base.logger.level = 0 - # A module that we can include in classes where we want to override an active record test. - +# module SqlserverCoercedTest + def self.included(base) base.extend ClassMethods end @@ -114,6 +102,8 @@ def with_auto_connect(boolean) end end +require 'mocha/mini_test' + # Core AR. schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" eval(File.read(schema_file)) diff --git a/test/config.yml b/test/config.yml index c0047a88f..bc9c142b7 100644 --- a/test/config.yml +++ b/test/config.yml @@ -12,7 +12,7 @@ default_connection_info: &default_connection_info collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> connections: - + dblib: arunit: <<: *default_connection_info @@ -32,4 +32,4 @@ connections: <<: *default_connection_info database: activerecord_unittest2 dsn: <%= ENV['ACTIVERECORD_UNITTEST2_DSN'] || 'activerecord_unittest2' %> - + From 9361188cc9bab5f82c0044177b6e6293b8d7d15c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 30 Dec 2014 13:44:41 -0500 Subject: [PATCH 0185/1412] New CHANGELOG format. --- CHANGELOG => CHANGELOG.md | 410 ++++++++++++-------------------------- 1 file changed, 122 insertions(+), 288 deletions(-) rename CHANGELOG => CHANGELOG.md (56%) diff --git a/CHANGELOG b/CHANGELOG.md similarity index 56% rename from CHANGELOG rename to CHANGELOG.md index 4bf84b83d..812efe948 100644 --- a/CHANGELOG +++ b/CHANGELOG.md @@ -1,27 +1,27 @@ -* 4.2.0 * +## v4.2.0 * ... -* 4.1.0 * +## v4.1.0 * Not sure if this even happened. Just got to 4.2.0 :) -* 4.0.0 * +## v4.0.0 * Dropped support for ruby 1.8.7 * Removed deadlock victim retry in favor of Isolation Level * Removed auto_explain_threshold_in_seconds (not used in rails 4) -* 3.2.12 * +## v3.2.12 * Revert string_to_binary changes in 457af60e. Fixes #273. -* 3.2.11 * +## v3.2.11 * Handle "No such column" when renaming some columns in the migrations. Fixes #237. Thanks @michelgrootjans. * Update regex for `RecordNotUnique` exception. Fixes #257. Thanks @pbatorre. @@ -32,8 +32,7 @@ * Test TinyTDS 0.6.0. * Fixed the unit tests due to changes in ActiveRecord that removes blank config values. * Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex -* Fixed change_column to update existing table column rows with new default value if there are any NULL values - and the column does not accept nulls +* Fixed change_column to update existing table column rows with new default value if there are any NULL values and the column does not accept nulls * Fixed change_column to drop and add indexes if the colun type is changes * Fixed string_to_binary and binary_to_string in some cases where the binary data is not UTF-8 * Fixing generating profile report to create output dir if needed, and code change for printing report @@ -41,14 +40,13 @@ * Updating mocha to work with newer ActiveRecord test cases -* 3.2.10 * +## v3.2.10 -* Remove connection defaults for host/username/password. Since we want to suppoert Windows Authentication - and there are just to many possibilities. So we now have to be explicit. +* Remove connection defaults for host/username/password. Since we want to suppoert Windows Authentication and there are just to many possibilities. So we now have to be explicit. * Remove really old TinyTDS warning. -* 3.2.9 * +## v3.2.9 * The #remove_default_constraint uses #execute_procedure now. Fixes #223. Thanks @gicappa and @clintmiller. * Mimic other adapters quoting for empty strings passed to integer columns. Fixes #164. @@ -56,561 +54,397 @@ * Make sure exclude [__rnt] table names form relation reflection. Fixes #219 and #221. Thanks @sphogan. -* 3.2.8 * +## v3.2.8 * Include VERSION in gemspec's files. -* 3.2.7 * +## v3.2.7 * Find VERSION in base file out of module namespace. Fixes #208 * Better support for explain without sp_execute args. FIxes #207 -* 3.2.6 * +## v3.2.6 * Unique has_many associations with pagination now work. Fixes #209 -* 3.2.5 * +## v3.2.5 * Fix a few test from ActiveRecord 3.2.6 upgrade. - * Fix db_name usage bug in #column_definitions [Altonymous] -* 3.2.4 * +## v3.2.4 * Fixed schema reflection for identity columns using ODBC. Fixes #193. -* 3.2.3 * +## v3.2.3 * Fixed datetime quoting for ActiveSupport::TimeWithZone objects. Fixes #187 and #189. -* 3.2.2 * +## v3.2.2 * Fixes all known issues with cross database schema reflection. Fixes #185 [Chris Altman] - * Fix exists? with offset by patching visitor. Fixes #171 and Fixes #167 - * Set default text size to 2147483647 for TinyTDS connections. Fixes #181 - * Set @config ivar for 3rd party libs. Fixes #177 - * Make #sql_type_for_statement work for integers that may have empty parens or none at all. Fixes #175 -* 3.2.1 * +## v3.2.1 * Add explicit order-by clause for windowed results. Fixes #161. -* 3.2.0 * - -* ActiveRecord explain (SHOWPLAN) support. - http://youtu.be/ckb3YYZZZ2Q +## v3.2.0 +* ActiveRecord explain (SHOWPLAN) support. http://youtu.be/ckb3YYZZZ2Q * Remove our log_info_schema_queries config since we are not hooking properly into AR's 'SCHEMA' names. - * Properly use 'SCHEMA' name arguement in DB statements to comply with ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS. - * Make use of the new ConnectionAdapters::SchemaCache for our needs. - * New Sqlserver::Utils class for out helpers. Moved table name unquotes there. -* 3.1.5 * +## v3.1.5 * Better support for orders with an expression. Fixes #155. [Jason Frey, Joe Rafaniello] -* 3.1.4 * - -* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed. - Fixes #125. [Wüthrich Hannes @hwuethrich] +## v3.1.4 +* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed. Fixes #125. [Wüthrich Hannes @hwuethrich] * New deadlock victim retry using the #retry_deadlock_victim config. [Jason Frey, Joe Rafaniello] - -* Renamed #with_auto_reconnect to #with_sqlserver_error_handling now that it handles both dropped - connections and deadlock victim errors. Fixes #150 [Jason Frey, Joe Rafaniello] - +* Renamed #with_auto_reconnect to #with_sqlserver_error_handling now that it handles both dropped connections and deadlock victim errors. Fixes #150 [Jason Frey, Joe Rafaniello] * Add activity_stats method that mimics the SQL Server Activity Monitor. Fixes #146 [Jason Frey, Joe Rafaniello] - -* Add methods for sqlserver's #product_version, #product_level, #edition and include them in inspect. - Fixes #145 [Jason Frey, Joe Rafaniello] - -* Handle statements that cannot be retried on a new database connection by not reconnecting. - Fixes #147 [Jason Frey, Joe Rafaniello] - +* Add methods for sqlserver's #product_version, #product_level, #edition and include them in inspect. Fixes #145 [Jason Frey, Joe Rafaniello] +* Handle statements that cannot be retried on a new database connection by not reconnecting. Fixes #147 [Jason Frey, Joe Rafaniello] * Added connection#spid for debugging. Fixes #144 [Jason Frey, Joe Rafaniello] - * Add ENV['TEST_FILES'] to Rakefile for easy single case tests. [Jason Frey, Joe Rafaniello] - * Pass ActiveRecord tests. Made windowed distinct pass all orders to groups. - test_limited_eager_with_multiple_order_columns - test_limited_eager_with_order - * Pass AR tests by moving DISTINCT to GROUP BY in windowed SQL. - test_count_eager_with_has_many_and_limit_and_high_offset - test_eager_with_has_many_and_limit_and_high_offset -* 3.1.3 * +## v3.1.3 -* Distinguish between identity and primary key key columns during schema reflection. Allows us - us to only do identity inserts when technically needed. Fixes #139 [chadcf] & [joncanady] +* Distinguish between identity and primary key key columns during schema reflection. Allows us to only do identity inserts when technically needed. Fixes #139 [chadcf] & [joncanady] -* 3.1.2 * +## v3.1.2 * Fix SQL Azure conflicts with DBCC useroptions. Use new #user_options_xyz methods. [kazamachi] - * Fix identity inserts for tables with natural PKs. [Gian Carlo Pace] - * Create a #configure_connection method that can be overridden. Think "SET TEXTSIZE...". - * Create a #configure_application_name method that can be overridden for unique TinyTDS app names - * Fixed the #finish_statement_handle to cancel the TinyTDS connection if needed. -* 3.1.1 * +## v3.1.1 * Make #rollback_db_transaction smarter. - -* Provide a method to override for the quoted string prefix. Not a config because trumping this method will - have drastically bad results. Fixes #124 - +* Provide a method to override for the quoted string prefix. Not a config because trumping this method will have drastically bad results. Fixes #124 * Allow :limit/:offset to be used with fully qualified table and column in :select. -* 3.1.0 * +## v3.1.0 * Add support/test around handling of float/real column types [Lucas Maxwell] - * Make auto reconnect duration configurable. Fixes #109 [David Chelimsky] - -* Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is - automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time - ActiveSupport formatter that is used per the language settings of the connection. - -* Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update - that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833 - +* Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time ActiveSupport formatter that is used per the language settings of the connection. +* Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833 * Allow drop_database to be called even when DB does not exist. - * Remove totally broken ADONET connection mode. Want it back, submit a patch. - * Schema reflection now finds primary key for all occasions. Fixed #60 [Boško Ivanišević] - * Allow complex order objects to not be molested by our visitor overrides. Fixes #99 - * Default unicode datatypes! - -* New #lowercase_schema_reflection configuration that allows you to downcase all tables and columns. - Good for legacy databases. Fixes #86. Thanks @dmajkic. - +* New #lowercase_schema_reflection configuration that allows you to downcase all tables and columns. Good for legacy databases. Fixes #86. Thanks @dmajkic. * Rails 3.1 with prepared statement support. Uses "EXEC sp_executesql ..." for just about everything now. -* 3.0.15 * +## v3.0.15 * Way better schema support! Thanks to @ianic! Fixes #61 - * Warn of possible permission problems if "EXEC sp_helptext..." does not work view. Fixes #73. -* 3.0.13/3.0.14 * +## v3.0.13/3.0.14 * Allow TinyTDS/DBLIB mode to pass down :host/:port config options. -* 3.0.12 * +## v3.0.12 * Bug fix for previous TinyTDS lost connections. -* 3.0.11 * +## v3.0.11 * Azure compatibility. - * TinyTDS enhancements for lost connections. Default connection mode. -* 3.0.10 * +## v3.0.10 * Fix #rowtable_orders visitor helper to use first column if no pk column was found. - * Flatten sp_helpconstraint when looking for constraints just in case fks are present. Issue #64. - * Start to support 2011 code named "Denali". - * Limit and Offset can take SqlLiteral objects now. -* 3.0.9 * +## v3.0.9 * Fix array literal parsing bug for ruby 1.9. -* 3.0.8 * +## v3.0.8 * Support for ActiveRecord v3.0.3 and ARel v2.0.7 -* 3.0.7 * +## v3.0.7 * Properly quote table names when reflecting on views. - * Add "dead or not enabled" to :dblib's lost connection messages. -* 3.0.6 * +## v3.0.6 * Maintenance release. Lock down to ActiveRecord 3.0.1 using ARel 1.0.0. -* 3.0.5 * +## v3.0.5 * Fixed native database type memoization, now at connection instance level. Fix #execute_procedure for :dblib mode to return indifferent access rows too. - * Make login timeout and query timeout backward database.yml friendly for :dblib mode. -* 3.0.4 * +## v3.0.4 * Add multiple results set support with #execute_procedure for :dblib mode. [Ken Collins] - * Simplify encoding support. [Ken Collins] - * Add binary timestamp datatype handling. [Erik Bryn] -* 3.0.3 +## v3.0.3 * Add TinyTDS/dblib connection mode. [Ken Collins] -* 3.0.2 +## v3.0.2 * Fix DSN'less code. [Erik Bryn] -* 3.0.1 +## v3.0.1 * Support DSN'less connections. Resolves ticket 38. - * Support upcoming ruby odbc 0.99992 -* 3.0.0 +## v3.0.0 * Release rails 3 version! -* 2.3.8 +## v2.3.8 * Properly quote all database names in rake helper methods. [Ken Collins] -* 2.3.7 +## v2.3.7 * Correctly use :date/:time SQL types in 2008 [Ken Collins] -* 2.3.6 +## v2.3.6 * Allow DNS's to not contain a database and use what is in database.yml [Marco Mastrodonato] - * Rake tasks methods for vanilla rails :db namespace parity. [Ken Collins] - * IronRuby integrated security fixes [Jimmy Schementi] -* 2.3.5 - -* Initial IronRuby ADONET connection mode support baked right in. Removed most &block - parameters, no handle/request object yielded anymore. Better abstraction and compliance - per the ActiveRecord abstract adapter to not yielding handles for #execute and only for - low level #select. Better wrapping of all queries at lowest level in #log so exceptions - at anytime can be handled correctly by core AR. Critical for System::Data's command - readers. Better abstraction for introspecting on #connection_mode. Added support for - running singular test cases via TextMate's Command-R. [Ken Collins] - -* Force a binary encoding on values coming in and out of those columns for ruby 1.9. - Fixes ticket #33 [Jeroen Zwartepoorte] - -* Using change_column will leave default if the type does not change or a new default - is not included. Fixes issue #22. [Ransom Briggs] +## v2.3.5 +* Initial IronRuby ADONET connection mode support baked right in. Removed most &blockparameters, no handle/request object yielded anymore. Better abstraction and compliance er the ActiveRecord abstract adapter to not yielding handles for #execute and only forlow level #select. Better wrapping of all queries at lowest level in #log so exceptionsat anytime can be handled correctly by core AR. Critical for System::Data's commandreaders. Better abstraction for introspecting on #connection_mode. Added support forrunning singular test cases via TextMate's Command-R. [Ken Collins] +* Force a binary encoding on values coming in and out of those columns for ruby 1.9.Fixes ticket #33 [Jeroen Zwartepoorte] +* Using change_column will leave default if the type does not change or a new defaultis not included. Fixes issue #22. [Ransom Briggs] * Use correct SP name for sp_MSforeachtable so any collation can get to it. [7to3] - * Qualify INFORMATION_SCHEMA.COLUMNS with a correct period DB name if present. - * Allow adapter to return multiple results sets, for example from stored procedures. [Chris Hall] -* 2.3.4 - -* For tables that named with schema(ex. rails.users), they could not get length of column. - column of varchar(40) gets length => nil. Ticket #27 & #15 [Ken Tachiya] - -* Altered limited_update_conditions regex conditions, the .* would greedily fail - if the where_sql had WHERE in a table or field, etc. [Ransom Briggs] - -* Changing test to allow ENV['ARUNIT_DB_NAME'] as the database name for the test units. - Matches up with AR conventions. [Ransom Briggs] +## v2.3.4 +* For tables that named with schema(ex. rails.users), they could not get length of column. Column of varchar(40) gets length => nil. Ticket #27 & #15 [Ken Tachiya] +* Altered limited_update_conditions regex conditions, the .* would greedily fail if the where_sql had WHERE in a table or field, etc. [Ransom Briggs] +* Changing test to allow ENV['ARUNIT_DB_NAME'] as the database name for the test units. Matches up with AR conventions. [Ransom Briggs] -2.3.3 -* Revert #ad83df82 and again cache column information at the connection's instance. The - previous commit was causing all sorts of view and schema reflection problems. [Ken Collins] +## v2.3.3 +* Revert #ad83df82 and again cache column information at the connection's instance. The previous commit was causing all sorts of view and schema reflection problems. [Ken Collins] -2.3.2 -* Insert queries that include the word "insert" as a partial column name with the word - "id" as a value were falsely being matched as identity inserts. [Sean Caffery/bfabry] - -* Delegate all low level #raw_connection calls to #raw_connection_run and #raw_connection_do - which abstract out the low level modes in the connection options at that point. [Ken Collins] +## v2.3.2 +* Insert queries that include the word "insert" as a partial column name with the word "id" as a value were falsely being matched as identity inserts. [Sean Caffery/bfabry] +* Delegate all low level #raw_connection calls to #raw_connection_run and #raw_connection_do which abstract out the low level modes in the connection options at that point. [Ken Collins] * Remove DBI dependency and go straight ODBC for speed improvement [Erik Bryn] - * Leave order by alone when same column crosses two tables [Ransom Briggs] -* 2.3 * (December 1st, 2009) +## v2.3.0 (2009-12-01) * Table and column aliases can handle many. Resolves ticket #19 [stonegao] - * Coerce a few tests that were failing in 2.3.x [Ken Collins] - -* Change column/view cache to happen at class level. Allows connection pool to share same - caches as well as the ability to expire the caches when needed. Also fix change_column so - that exceptions are not raised when the column contains an existing default. [Ken Collins] - -* Allow query_requires_identity_insert? method to return quoted table name in situations where the - INSERT parts are not quoted themselves. [Gary/iawgens, Richard Penwell, Ken Collins] - +* Change column/view cache to happen at class level. Allows connection pool to share same caches as well as the ability to expire the caches when needed. Also fix change_column so that exceptions are not raised when the column contains an existing default. [Ken Collins] +* Allow query_requires_identity_insert? method to return quoted table name in situations where the INSERT parts are not quoted themselves. [Gary/iawgens, Richard Penwell, Ken Collins] * Fixed namespace in calling test_sqlserver_odbc within test_unicode_types. [Gary/iawgens] - * Columns with multi-line defaults work correctly. [bfabry] -* 2.2.22 * (October 15th, 2009) +## v2.2.22 (2009-10-15) * Support Identity-key-column judgement on multiple schema environment [Ken Tachiya] - -* Add support for tinyint data types. In MySQL all these types would be boolean, however in - our adapter, they will use the full 1 => 255 Fixnum value as you would expect. [Ken Collins] +* Add support for tinyint data types. In MySQL all these types would be boolean, however in our adapter, they will use the full 1 => 255 Fixnum value as you would expect. [Ken Collins] -* 2.2.21 * (September 10th, 2009) +## v2.2.21 (2009-09-10) -* Changes for gem best practices per http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices - Details of such are as follows: [Ken Collins] +* Changes for gem best practices per http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices. Details of such are as follows: [Ken Collins] - Removed rails-sqlserver-2000-2005-adapter.rb load file for old github usage. - Move the core_ext directory to active_record/connection_adapters/sqlserver_adapter/core_ext - Renamespace SQLServerDBI to ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::DBI - Renamespace ActiveRecord::ConnectionAdapters::SQLServerActiveRecordExtensions to ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ActiveRecord -* 2.2.20 * (September 10th, 2009) +## v2.2.20 (2009-09-10) * Implement a new remove_default_constraint method that uses sp_helpconstraint [Ken Collins] - -* Use a lazy match in add_order_by_for_association_limiting! to allow sub selects to be used. Resolves - ticket #11. - -* Add default rake task back for testing. Runs the namespaced sqlserver:test_sqlserver_odbc. - Resolves ticket #10 [Ken Collins] - -* Default value detection in column_definitions is kinder to badly formatted, or long winded user - defined functions, for default values. Resolves ticket #8 [Ken Collins] - +* Use a lazy match in add_order_by_for_association_limiting! to allow sub selects to be used. Resolves ticket #11. +* Add default rake task back for testing. Runs the namespaced sqlserver:test_sqlserver_odbc. Resolves ticket #10 [Ken Collins] +* Default value detection in column_definitions is kinder to badly formatted, or long winded user defined functions, for default values. Resolves ticket #8 [Ken Collins] * Make sure bigint SQL Server data type can be used and converted back to Bignum as expected. [Ken Collins] -* 2.2.19 * (June 19th, 2009) +## v2.2.19 (2009-06-19) * Leave quoted column names as is. Resolves ticket #36 [Vince Puzzella] - -* Changing add_limit! in ActiveRecord::Base for SQLServer so that it passes through any scoped :order - parameters. Resolves ticket #35 [Murray Steele] +* Changing add_limit! in ActiveRecord::Base for SQLServer so that it passes through any scoped :order parameters. Resolves ticket #35 [Murray Steele] -* 2.2.18 * (June 5th, 2009) +## v2.2.18 (2009-06-05) * Column reflection on table name rescues LoadError and a few others. Resolves tickets #25 & #33 [Ken Collins] - * Added 2008 support. Resolves ticket #32 [Ken Collins] -* 2.2.17 * (May 14th, 2009) - -* Add simplified type recognition for varchar(max) and nvarchar(max) under SQL Server 2005 to be a - :text type. This ensures schema dumper does the right thing. Fixes ticket #30. [Ken Collins] - -* Tested ruby 1.9, ruby-odbc 0.9996, and DBI 0.4.1. Also added correct support for UTF-8 character - encoding going in and out of the DB. See before gist http://gist.github.com/111709 and after gist - http://gist.github.com/111719 [Ken Collins] - - -* 2.2.16 * (April 21st, 2009) - -* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests - to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs - for deep sub selects in paginated results. [Ken Collins] - -* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through - the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success - or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins] - -* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This - resolves ticket #26 [Ken Collins] - -* Provide support for running queries at different isolation levels using #run_with_isolation_level method - that can take a block or not. Also implement a #user_options method that reflects on the current user - session values. Resolves #20 [Murray Steele] +## v2.2.17 (2009-05-14) +* Add simplified type recognition for varchar(max) and nvarchar(max) under SQL Server 2005 to be a :text type. This ensures schema dumper does the right thing. Fixes ticket #30. [Ken Collins] +* Tested ruby 1.9, ruby-odbc 0.9996, and DBI 0.4.1. Also added correct support for UTF-8 character encoding going in and out of the DB. See before gist http://gist.github.com/111709 and after gist http://gist.github.com/111719 [Ken Collins] -* 2.2.15 * (March 23rd, 2009) -* Better add_lock! method that can add the lock to just about all the elements in the statement. This - could be eager loaded associations, joins, etc. Done so that paginated results can easily add lock - options for performance. Note, the tally count in add_limit_offset! use "WITH (NOLOCK)" explicitly - as it can not hurt and is needed. [Ken Collins] +## v2.2.16 (2009-04-21) +* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs for deep sub selects in paginated results. [Ken Collins] +* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins] +* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This resolves ticket #26 [Ken Collins] +* Provide support for running queries at different isolation levels using #run_with_isolation_level method that can take a block or not. Also implement a #user_options method that reflects on the current user session values. Resolves #20 [Murray Steele] -* 2.2.14 * (March 17th, 2009) -* Rails2.3 - Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecord - version strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSqlserver use Ship vs - Bird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) Previous - JoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] +## v2.2.15 (2009-03-23) -* Rails2.3 - Implement new savepoint support [Ken Collins] - http://rails.lighthouseapp.com/projects/8994/tickets/383 - http://www.codeproject.com/KB/database/sqlservertransactions.aspx +* Better add_lock! method that can add the lock to just about all the elements in the statement. This could be eager loaded associations, joins, etc. Done so that paginated results can easily add lock options for performance. Note, the tally count in add_limit_offset! use "WITH (NOLOCK)" explicitly as it can not hurt and is needed. [Ken Collins] -* Rails2.3 - Coerce NestedScopingTest#test_merged_scoped_find to use correct regexp for adapter. [Ken Collins] -* Rails2.3 - Implement a custom ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation#aliased_table_name_for - method that uses a Regexp.escape so that table/column quoting does not get ignored. [Ken Collins] +## v2.2.14 (2009-03-17) -* Rails2.3 - Implement #outside_transaction? and a new transaction test case to test some SQL Server - basic support while implementing this method. Future home of some savepoint tests too. [Ken Collins] +* Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecordversion strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSqlserver use Ship vsBird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) PreviousJoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] +* Implement new savepoint support [Ken Collins]http://rails.lighthouseapp.com/projects/8994/tickets/383http://www.codeproject.com/KB/database/sqlservertransactions.aspx +* Coerce NestedScopingTest#test_merged_scoped_find to use correct regexp for adapter. [Ken Collins] +* Implement a custom ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation#aliased_table_name_formethod that uses a Regexp.escape so that table/column quoting does not get ignored. [Ken Collins] +* Implement #outside_transaction? and a new transaction test case to test some SQL Serverbasic support while implementing this method. Future home of some savepoint tests too. [Ken Collins] +* Rails2.3 - Coerced tests that ensure hash conditions on referenced tables are considered when eagerloading with limit/offset. Information on these changes and the ticket in rails are.http://github.com/rails/rails/commit/9a4d557713acb0fc8e80f61af18094034aca029ahttp://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1404-conditions_tables-doesnt-understand-condition-hashes +* Add coerced tests for true/false attributes in selects use SQL Server case statement. [Ken Collins] +* Making sure that smalldatetime types are OK to use. Also fixed a bug in the #view_information method thatchecks to see if a view definition is equal to 4000 chars, meaning that it is most likely truncated andneeds to use the backup method of sp_helptext to get it's view definition. [Ken Collins] -* Rails2.3 - Coerced tests that ensure hash conditions on referenced tables are considered when eager - loading with limit/offset. Information on these changes and the ticket in rails are. - http://github.com/rails/rails/commit/9a4d557713acb0fc8e80f61af18094034aca029a - http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1404-conditions_tables-doesnt-understand-condition-hashes -* Rails2.3 - Add coerced tests for true/false attributes in selects use SQL Server case statement. [Ken Collins] +## v2.2.13 (2009-02-10) -* Making sure that smalldatetime types are OK to use. Also fixed a bug in the #view_information method that - checks to see if a view definition is equal to 4000 chars, meaning that it is most likely truncated and - needs to use the backup method of sp_helptext to get it's view definition. [Ken Collins] +* Update #indexes to use unqualified table name. Fixes cases where users may decide to use tablename prefixes like 'dbo.'. [Ken Collins] -* 2.2.13 * (February 10th, 2009) - -* Update #indexes to use unqualified table name. Fixes cases where users may decide to use table - name prefixes like 'dbo.'. [Ken Collins] - - -* 2.2.12 * (February 8th, 2009) +## v2.2.12 (2009-02-08) * Update table_exists? to work with qualified table names that may include an user prefix. [Ken Collins] -* 2.2.10/11 * (January 22nd, 2009) +## v2.2.10/11 (2009-01-22) * Add a rails-sqlserver-2000-2005-adapter.rb file so that long :lib option for config.gem is no longer needed. -* 2.2.9 * (January 22nd, 2009) +## v2.2.9 (2009-01-22) -* Fixing a small bug in the deprecated DBI::Timestamp conversion so it correctly converts nanosecond whole - numbers to back to pre type cast SQL Server milliseconds, ultimately allow ruby's Time#usec which is - microseconds to be correct. [Ken Collins] +* Fixing a small bug in the deprecated DBI::Timestamp conversion so it correctly converts nanosecond wholenumbers to back to pre type cast SQL Server milliseconds, ultimately allow ruby's Time#usec which ismicroseconds to be correct. [Ken Collins] +* Sometimes views are more than 4000 chars long and will return NULL for the VIEW_DEFINITION. If so, usesp_helptext procedure as a backup method. [Ken Collins] -* Sometimes views are more than 4000 chars long and will return NULL for the VIEW_DEFINITION. If so, use - sp_helptext procedure as a backup method. [Ken Collins] - -* 2.2.8 (January 9th, 2009) +## v2.2.8 (2009-01-09) * Update execute_procedure method a bit to remove excess code. [Ken Collins] -* 2.2.7 (January 9th, 2009) - -* Created a connection#execute_procedure method that takes can take any number of ruby objects as variables - and quotes them according to the connection's rules. Also added an ActiveRecord::Base class level core - extension that hooks into this. It also checks if the connection responds to #execute_procedure and if - not returns an empty array. [Ken Collins] +## v2.2.7 (2009-01-09) -* Added a #enable_default_unicode_types class attribute access to make all new added or changed string types - like :string/:text default to unicode/national data types. See the README for full details. Added a rake - task that assists setting this to true when running tests. [Ken Collins] +* Created a connection#execute_procedure method that takes can take any number of ruby objects as variablesand quotes them according to the connection's rules. Also added an ActiveRecord::Base class level coreextension that hooks into this. It also checks if the connection responds to #execute_procedure and ifnot returns an empty array. [Ken Collins] +* Added a #enable_default_unicode_types class attribute access to make all new added or changed string typeslike :string/:text default to unicode/national data types. See the README for full details. Added a raketask that assists setting this to true when running tests. [Ken Collins] -* 2.2.6 (January 8th, 2009) +## v2.2.6 (2009-01-08) * Introduced a bug in 2.2.5 in the #add_order! core ext for ActiveRecord. Fixed [Ken Collins] -* 2.2.5 (January 4th, 2009) +## v2.2.5 (2009-01-04) -* Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent by - default. [Ken Collins] +* Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent bydefault. [Ken Collins] +* Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server millisecondsand applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SqlserverTimestampclass to parse the before type cast value appropriately. Also update the adapters #quoted_date methodto work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] +* Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Nowthis adapter is compatible for using with other adapters. [Ken Collins] -* Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server milliseconds - and applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SqlserverTimestamp - class to parse the before type cast value appropriately. Also update the adapters #quoted_date method - to work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] -* Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Now - this adapter is compatible for using with other adapters. [Ken Collins] - - -* 2.2.4 (December 5th, 2008) +## v2.2.4 (2008-12-05) * Fix a type left in #views_real_column_name. Also cache #view_information lookups. [Ken Collins] -* 2.2.3 (December 5th, 2008) - -* Changing back to using real table name in column_definitions. Makes sure views get back only the columns - that are defined for them with correct names, etc. Now supporting views by looking for NULL default and - then if table name is a view, perform a targeted with sub select to the real table name and column name - to find true default. [Ken Collins] +## v2.2.3 (2008-12-05) +* Changing back to using real table name in column_definitions. Makes sure views get back only the columnsthat are defined for them with correct names, etc. Now supporting views by looking for NULL default andthen if table name is a view, perform a targeted with sub select to the real table name and column nameto find true default. [Ken Collins] * Ensure that add_limit_offset! does not alter sub queries. [Erik Bryn] -2.2.2 (December 2nd, 2008) +## v2.2.2 (2008-12-02) * Add support for view defaults by making column_definitions use real table name for schema info. [Ken Collins] - * Include version in connection method and inspection. [Ken Collins] -2.2.1 (November 25th, 2008) - -* Add identity insert support for views. Cache #views so that identity #table_name_or_views_table_name - will run quickly. [Ken Collins] +## v2.2.1 (2008-11-25) -* Add views support. ActiveRecord classes can use views. The connection now has a #views method and - #table_exists? will now fall back to checking views too. [Ken Collins] +* Add identity insert support for views. Cache #views so that identity #table_name_or_views_table_namewill run quickly. [Ken Collins] +* Add views support. ActiveRecord classes can use views. The connection now has a #views method and #table_exists? will now fall back to checking views too. [Ken Collins] -2.2.0 (November 21st, 2008) +## v2.2.0 (2008-11-21) * Release for rails 2.2.2. Many many changes. [Ken Collins], [Murray Steele], [Shawn Balestracci], [Joe Rafaniello] From c590b3b987e7e9b84157ff07e69363cc64f9c9e1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 09:40:53 -0500 Subject: [PATCH 0186/1412] Remove all profiles. Will add these back later in a different way. --- test/profile/connection_profile_case.rb | 30 ---------- test/profile/finder_profile_case.rb | 18 ------ test/profile/gc_profile_case.rb | 75 ------------------------- test/profile/helper.rb | 30 ---------- test/profile/query_plan_complex.rb | 68 ---------------------- test/profile/query_plan_simple.rb | 46 --------------- 6 files changed, 267 deletions(-) delete mode 100644 test/profile/connection_profile_case.rb delete mode 100644 test/profile/finder_profile_case.rb delete mode 100644 test/profile/gc_profile_case.rb delete mode 100644 test/profile/helper.rb delete mode 100755 test/profile/query_plan_complex.rb delete mode 100755 test/profile/query_plan_simple.rb diff --git a/test/profile/connection_profile_case.rb b/test/profile/connection_profile_case.rb deleted file mode 100644 index 90a0a4de1..000000000 --- a/test/profile/connection_profile_case.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'profile/helper' -require 'models/topic' -require 'models/reply' - -class ConnectionProfileCase < ActiveRecord::TestCase - - fixtures :topics - - setup do - @connection = ActiveRecord::Base.connection - end - - def test_select - select_statement = "SELECT [topics].* FROM [topics]" - ruby_profile :connection_select do - 1000.times { @connection.send :select, select_statement } - end - end - - def test_select_one - select_statement = "SELECT [topics].* FROM [topics]" - ruby_profile :connection_select_one do - 1000.times { @connection.select_one(select_statement) } - end - end - - -end - - diff --git a/test/profile/finder_profile_case.rb b/test/profile/finder_profile_case.rb deleted file mode 100644 index a8eadfc50..000000000 --- a/test/profile/finder_profile_case.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'profile/helper' -require 'models/topic' -require 'models/reply' - -class FinderProfileCase < ActiveRecord::TestCase - - fixtures :topics - - def test_find_all - ruby_profile :finder_find_all do - 1000.times { Topic.all } - end - end - - -end - - diff --git a/test/profile/gc_profile_case.rb b/test/profile/gc_profile_case.rb deleted file mode 100644 index 08416c520..000000000 --- a/test/profile/gc_profile_case.rb +++ /dev/null @@ -1,75 +0,0 @@ -require 'benchmark' -require 'cases/sqlserver_helper' -require 'models/topic' -require 'models/reply' - -class GcProfileCase < ActiveRecord::TestCase - - fixtures :topics - - setup do - create_mass_topics unless @created_mass_topics - @connection = ActiveRecord::Base.connection - @select_statement = "SELECT [topics].* FROM [topics]" - end - - def test_coercion - bench_allocations('coercion') do - Topic.all(limit: 100).each do |t| - t.attributes.keys.each do |k| - t.send(k.to_sym) - end - end - end - end - - def test_select - bench_allocations('select') do - @connection.send :select, @select_statement - end - end - - def test_select_one - bench_allocations('select_one') do - 100.times { @connection.select_one(@select_statement) } - end - end - - def test_columns - bench_allocations('columns') do - 100.times do - Topic.reset_column_information - Topic.columns - end - end - end - - - protected - - def create_mass_topics - GC::Profiler.clear - GC::Profiler.disable - all_topics = Topic.all - 100.times { all_topics.each { |t| Topic.create! t.attributes } } - @created_mass_topics = true - GC.start - GC::Profiler.enable - GC::Profiler.clear - end - - def bench_allocations(feature, iterations=10, &blk) - puts "\nGC overhead for #{feature}" - GC::Profiler.clear - GC::Profiler.enable - iterations.times{ blk.call } - GC::Profiler.report(STDOUT) - GC::Profiler.disable - end - -end - - - - - diff --git a/test/profile/helper.rb b/test/profile/helper.rb deleted file mode 100644 index b0a2371cc..000000000 --- a/test/profile/helper.rb +++ /dev/null @@ -1,30 +0,0 @@ -require 'cases/sqlserver_helper' -require 'ruby-prof' - -class ActiveRecord::TestCase - - - protected - - def ruby_profile(name) - result = RubyProf.profile { yield } - [:flat,:graph,:html].each do |printer| - save_ruby_prof_report(result, name, printer) - end - end - - def save_ruby_prof_report(result, name, printer) - ptr = case printer - when :flat then RubyProf::FlatPrinter - when :graph then RubyProf::GraphPrinter - when :html then RubyProf::GraphHtmlPrinter - end - file_name = printer == :html ? "#{name}_graph.html" : "#{name}_#{printer}.txt" - file_path = File.join(SQLSERVER_TEST_ROOT, 'profile', 'output', file_name) - Dir.mkdir(File.join(SQLSERVER_TEST_ROOT, 'profile', 'output')) unless File.exists?(File.join(SQLSERVER_TEST_ROOT, 'profile', 'output')) - File.open(file_path,'w') do |file| - printer == :html ? ptr.new(result).print(file) : ptr.new(result).print(file,{}) - end - end - -end diff --git a/test/profile/query_plan_complex.rb b/test/profile/query_plan_complex.rb deleted file mode 100755 index 05d8a3641..000000000 --- a/test/profile/query_plan_complex.rb +++ /dev/null @@ -1,68 +0,0 @@ -=begin - -Query Plan Complex -================== -Author: Ken Collins -Date: May 22, 2011 -Summary: Benchmark complex cached query plan reuse in SQL Server. - -System Information ------------------- - Operating System: Mac OS X 10.6.7 (10J869) - CPU: Quad-Core Intel Xeon 2.66 GHz - Processor Count: 4 - Memory: 24 GB - ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10.6.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03 - -"Simple - Query Plan Reuse" is up to 88% faster over repetitions ------------------------------------------------------------------ - - Simple - Query Plan Reuse 0.230067014694214 secs Fastest - Simple - Dynamic SQL 1.99195981025696 secs 88% Slower - -=end - -require 'rubygems' -require 'bundler' -Bundler.setup -require 'tiny_tds' -require 'bench_press' - -extend BenchPress - -author 'Ken Collins' -summary 'Benchmark complex cached query plan reuse in SQL Server.' -reps 500 - -@client = TinyTds::Client.new host: 'mc2008', username: 'rails' - - -measure "Simple - Dynamic SQL" do - sql = " - SELECT TOP (1) [companies].id - FROM [companies] - LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] - AND [clients_using_primary_keys_companies].[type] IN (N'Client', N'SpecialClient', N'VerySpecialClient') - WHERE [companies].[type] IN (N'Firm') - AND [companies].[id] = #{rand(1000000)} - GROUP BY [companies].id - ORDER BY MIN(clients_using_primary_keys_companies.name)" - @client.execute(sql).do -end - -measure "Simple - Query Plan Reuse" do - sql = " - EXEC sp_executesql N' - SELECT TOP (1) [companies].id - FROM [companies] - LEFT OUTER JOIN [companies] [clients_using_primary_keys_companies] ON [clients_using_primary_keys_companies].[firm_name] = [companies].[name] - AND [clients_using_primary_keys_companies].[type] IN (N''Client'', N''SpecialClient'', N''VerySpecialClient'') - WHERE [companies].[type] IN (N''Firm'') - AND [companies].[id] = @0 - GROUP BY [companies].id - ORDER BY MIN(clients_using_primary_keys_companies.name)', - N'@0 int', - @0 = #{rand(1000000)}" - @client.execute(sql).do -end - diff --git a/test/profile/query_plan_simple.rb b/test/profile/query_plan_simple.rb deleted file mode 100755 index dbf6146a9..000000000 --- a/test/profile/query_plan_simple.rb +++ /dev/null @@ -1,46 +0,0 @@ -=begin - -Query Plan Simple -================= -Author: Ken Collins -Date: May 22, 2011 -Summary: Benchmark simple cached query plan reuse in SQL Server. - -System Information ------------------- - Operating System: Mac OS X 10.6.7 (10J869) - CPU: Quad-Core Intel Xeon 2.66 GHz - Processor Count: 4 - Memory: 24 GB - ruby 1.8.7 (2011-02-18 patchlevel 334) [i686-darwin10.6.0], MBARI 0x6770, Ruby Enterprise Edition 2011.03 - -"Simple - Query Plan Reuse" is up to 58% faster over repetitions ------------------------------------------------------------------ - - Simple - Query Plan Reuse 0.20799994468689 secs Fastest - Simple - Dynamic SQL 0.49638819694519 secs 58% Slower - -=end - -require 'rubygems' -require 'bundler' -Bundler.setup -require 'tiny_tds' -require 'bench_press' - -extend BenchPress - -author 'Ken Collins' -summary 'Benchmark simple cached query plan reuse in SQL Server.' -reps 500 - -@client = TinyTds::Client.new host: 'mc2008', username: 'rails' - - -measure "Simple - Dynamic SQL" do - @client.execute("SELECT TOP(1) * FROM [posts] WHERE [id] = #{rand(1000000)}").do -end - -measure "Simple - Query Plan Reuse" do - @client.execute("EXEC sp_executesql N'SELECT TOP(1) * FROM [posts] WHERE [id] = @0', N'@0 int', @0 = #{rand(1000000)}").do -end From d60859457550c0f04188b1af986b43f32218e8e2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 09:54:07 -0500 Subject: [PATCH 0187/1412] Rename 'cases/sqlserver_helper' to our suffix convention 'cases/helper_sqlserver'. --- Rakefile | 2 +- test/cases/adapter_test_sqlserver.rb | 2 +- test/cases/associations_test_sqlserver.rb | 2 +- .../cases/attribute_methods_test_sqlserver.rb | 2 +- test/cases/base_test_sqlserver.rb | 2 +- test/cases/batches_test_sqlserver.rb | 2 +- .../belongs_to_associations_test_sqlserver.rb | 2 +- test/cases/bind_parameter_test_sqlserver.rb | 2 +- test/cases/calculations_test_sqlserver.rb | 2 +- test/cases/column_test_sqlserver.rb | 2 +- .../connection_management_test_sqlserver.rb | 2 +- test/cases/connection_test_sqlserver.rb | 2 +- .../database_statements_test_sqlserver.rb | 2 +- test/cases/disconnected_test_sqlserver.rb | 2 +- test/cases/eager_test_sqlserver.rb | 2 +- .../cases/execute_procedure_test_sqlserver.rb | 2 +- test/cases/finder_test_sqlserver.rb | 2 +- ...ngs_to_many_associations_test_sqlserver.rb | 2 +- ...qlserver_helper.rb => helper_sqlserver.rb} | 20 ++++++++----------- test/cases/inheritance_test_sqlserver.rb | 2 +- .../invalid_connection_test_sqlserver.rb | 2 +- test/cases/migration_test_sqlserver.rb | 2 +- test/cases/named_scoping_test_sqlserver.rb | 2 +- test/cases/offset_and_limit_test_sqlserver.rb | 2 +- test/cases/order_test_sqlserver.rb | 2 +- test/cases/persistence_test_sqlserver.rb | 2 +- .../pessimistic_locking_test_sqlserver.rb | 2 +- .../cases/predicate_builder_test_sqlserver.rb | 2 +- test/cases/query_cache_test_sqlserver.rb | 2 +- test/cases/relations_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 2 +- test/cases/schema_test_sqlserver.rb | 2 +- test/cases/scratch_test_sqlserver.rb | 2 +- test/cases/session_test_sqlserver.rb | 2 +- test/cases/showplan_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 2 +- test/cases/table_name_test_sqlserver.rb | 2 +- test/cases/transaction_test_sqlserver.rb | 2 +- test/cases/unicode_test_sqlserver.rb | 2 +- .../uniqueness_validation_test_sqlserver.rb | 2 +- test/cases/uuid_test_sqlserver.rb | 2 +- test/cases/where_chain_test_sqlserver.rb | 4 ++-- 42 files changed, 50 insertions(+), 54 deletions(-) rename test/cases/{sqlserver_helper.rb => helper_sqlserver.rb} (90%) diff --git a/Rakefile b/Rakefile index 4230f2fcb..6acbd548f 100644 --- a/Rakefile +++ b/Rakefile @@ -15,7 +15,7 @@ def test_libs end def test_files - test_setup = ['test/cases/sqlserver_helper.rb'] + test_setup = ['test/cases/helper_sqlserver.rb'] return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') ar_cases = Dir.glob("#{AR_PATH}/test/cases/**/*_test.rb") diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index ba85a141c..e21e08461 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/task' require 'models/reply' require 'models/joke' diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb index eb2fbee61..59f94abeb 100644 --- a/test/cases/associations_test_sqlserver.rb +++ b/test/cases/associations_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/owner' class HasManyThroughAssociationsTest < ActiveRecord::TestCase diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 1c7bb6a83..777066542 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/developer' require 'models/topic' require 'models_sqlserver/topic' diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index 21ba4206d..3155894d0 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' require 'models/auto_id' diff --git a/test/cases/batches_test_sqlserver.rb b/test/cases/batches_test_sqlserver.rb index b69c90f74..ad09d3237 100644 --- a/test/cases/batches_test_sqlserver.rb +++ b/test/cases/batches_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' class BatchesTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/belongs_to_associations_test_sqlserver.rb b/test/cases/belongs_to_associations_test_sqlserver.rb index c31a2a905..813ca6b20 100644 --- a/test/cases/belongs_to_associations_test_sqlserver.rb +++ b/test/cases/belongs_to_associations_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class BelongsToAssociationsTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index 4a033e715..9c89eea54 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/topic' require 'models_sqlserver/topic' require 'cases/bind_parameter_test' diff --git a/test/cases/calculations_test_sqlserver.rb b/test/cases/calculations_test_sqlserver.rb index 9b5c8173f..1ad37b628 100644 --- a/test/cases/calculations_test_sqlserver.rb +++ b/test/cases/calculations_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/company' require 'models/topic' require 'models/edge' diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 0acae76ea..de537e903 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/binary' require 'models_sqlserver/float_data' require 'models_sqlserver/numeric_data' diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/connection_management_test_sqlserver.rb index af30a2269..0114dd490 100644 --- a/test/cases/connection_management_test_sqlserver.rb +++ b/test/cases/connection_management_test_sqlserver.rb @@ -1,6 +1,6 @@ #This rails pull request will make this code unnecessary https://github.com/rails/rails/pull/13745 -require "cases/sqlserver_helper" +require "cases/helper_sqlserver" require "rack" module ActiveRecord diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 34d5aafd7..0d9da8b0c 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/reply' require 'models_sqlserver/topic' diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index 16406f3ac..e542b10a3 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 621aff949..7f6f4a0e9 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -1,6 +1,6 @@ if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/disconnected_test.rb").exist? #cases/disconnected_test was added in rails 4.0.1 so this errors in 4.0.0 - require 'cases/sqlserver_helper' + require 'cases/helper_sqlserver' require 'cases/disconnected_test' class TestDisconnectedAdapter < ActiveRecord::TestCase diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index 2c71b851b..ca26537d9 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' require 'models/comment' require 'models/author' diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index ff536d1ab..4d28ee046 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index beb0162be..d1db6d4eb 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/event' require 'models/author' require 'models/post' diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb index 5e37c8816..4e8d7238d 100644 --- a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb +++ b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class HasAndBelongsToManyAssociationsTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/sqlserver_helper.rb b/test/cases/helper_sqlserver.rb similarity index 90% rename from test/cases/sqlserver_helper.rb rename to test/cases/helper_sqlserver.rb index 7bf7511dc..dc1ef176c 100644 --- a/test/cases/sqlserver_helper.rb +++ b/test/cases/helper_sqlserver.rb @@ -18,13 +18,8 @@ require 'minitest-spec-rails/init/active_support' require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' -require 'models/topic' require 'cases/sqlserver_test_case' -ActiveRecord::Migration.verbose = false -ActiveRecord::Base.logger = Logger.new(File.expand_path(File.join(SQLSERVER_TEST_ROOT,'debug.log'))) -ActiveRecord::Base.logger.level = 0 - # A module that we can include in classes where we want to override an active record test. # module SqlserverCoercedTest @@ -63,6 +58,7 @@ def undefine_and_puts(method) module ActiveRecord class TestCase < ActiveSupport::TestCase + class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end @@ -70,14 +66,17 @@ def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end def sqlserver_2008? ; ActiveRecord::Base.connection.sqlserver_2008? ; end def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end end + def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_2005? ; self.class.sqlserver_2005? ; end def sqlserver_2008? ; self.class.sqlserver_2008? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end + def with_enable_default_unicode_types? ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types.is_a?(TrueClass) end + def with_enable_default_unicode_types(setting) old_setting = ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types old_text = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type @@ -96,18 +95,15 @@ def with_auto_connect(boolean) existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean yield - ensure - ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing + ensure + ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing end + end end require 'mocha/mini_test' -# Core AR. -schema_file = "#{ACTIVERECORD_TEST_ROOT}/schema/schema.rb" -eval(File.read(schema_file)) - # SQL Server. sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" -eval(File.read(sqlserver_specific_schema_file)) +eval(File.read(sqlserver_specific_schema_file)) unless ENV["SKIP_SCHEMA"] diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index 219af0f17..06dd4f65e 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/company' require 'models/project' require 'models/subscriber' diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb index e7a98dcda..b06b9598e 100644 --- a/test/cases/invalid_connection_test_sqlserver.rb +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -1,6 +1,6 @@ #cases/invalid_connection_test was added in rails 4.0.1 if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/invalid_connection_test.rb").exist? - require 'cases/sqlserver_helper' + require 'cases/helper_sqlserver' require 'cases/invalid_connection_test' class TestAdapterWithInvalidConnection < ActiveRecord::TestCase diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 22c127cb8..556d95e98 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/person' require 'models_sqlserver/person' diff --git a/test/cases/named_scoping_test_sqlserver.rb b/test/cases/named_scoping_test_sqlserver.rb index 398c51109..6cfa25b35 100644 --- a/test/cases/named_scoping_test_sqlserver.rb +++ b/test/cases/named_scoping_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' require 'models_sqlserver/post' diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index abb437a23..32ed7656c 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/job' require 'models/person' require 'models/reference' diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index a184ded64..2162af5c7 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' class OrderTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 42b5d7aef..19cfbe32e 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' require 'models/comment' require 'models/author' diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 88403e1b7..192cd0fde 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/person' require 'models/reader' require 'models_sqlserver/person' diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/predicate_builder_test_sqlserver.rb index 1524f6aa8..27c3c055e 100644 --- a/test/cases/predicate_builder_test_sqlserver.rb +++ b/test/cases/predicate_builder_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/topic' require 'models_sqlserver/topic' diff --git a/test/cases/query_cache_test_sqlserver.rb b/test/cases/query_cache_test_sqlserver.rb index 857973c9b..268045fa5 100644 --- a/test/cases/query_cache_test_sqlserver.rb +++ b/test/cases/query_cache_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/task' class QueryCacheTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index 33c305bfc..ae2bc0881 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -1,4 +1,4 @@ -require "cases/sqlserver_helper" +require "cases/helper_sqlserver" require 'models/post' diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 892a45d96..d79c6e33e 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'cases/schema_dumper_test' require 'stringio' diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 5d82804c4..809486db5 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models_sqlserver/sql_server_natural_pk_data' require 'models_sqlserver/sql_server_natural_pk_data_schema' diff --git a/test/cases/scratch_test_sqlserver.rb b/test/cases/scratch_test_sqlserver.rb index 0da1d9d57..825a16549 100644 --- a/test/cases/scratch_test_sqlserver.rb +++ b/test/cases/scratch_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class ScratchTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/session_test_sqlserver.rb b/test/cases/session_test_sqlserver.rb index 5450e335b..f942ac6fd 100644 --- a/test/cases/session_test_sqlserver.rb +++ b/test/cases/session_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'action_dispatch' module ActiveRecord diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 7c26ce6d8..4fb2aa433 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/car' class ShowplanTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 92f711d77..cef0dc7b4 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models_sqlserver/no_pk_data' require 'models_sqlserver/sql_server_dollar_table_name' require 'models_sqlserver/sql_server_edge_schema' diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb index 4dcaeac66..fe4022ff9 100644 --- a/test/cases/table_name_test_sqlserver.rb +++ b/test/cases/table_name_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/order' class SqlServerRailsOrders < ActiveRecord::Base diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index a78bfd2ce..5a7d02f82 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/ship' require 'models/developer' diff --git a/test/cases/unicode_test_sqlserver.rb b/test/cases/unicode_test_sqlserver.rb index 368019f80..71bfa3a6b 100644 --- a/test/cases/unicode_test_sqlserver.rb +++ b/test/cases/unicode_test_sqlserver.rb @@ -1,5 +1,5 @@ # encoding: UTF-8 -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class UnicodeTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/uniqueness_validation_test_sqlserver.rb index de7429ed2..8374b289b 100644 --- a/test/cases/uniqueness_validation_test_sqlserver.rb +++ b/test/cases/uniqueness_validation_test_sqlserver.rb @@ -1,5 +1,5 @@ # encoding: utf-8 -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/event' class Event < ActiveRecord::Base diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index f59abfc48..a340a34b2 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' class SQLServerUUIDTest < ActiveRecord::TestCase class UUID < ActiveRecord::Base diff --git a/test/cases/where_chain_test_sqlserver.rb b/test/cases/where_chain_test_sqlserver.rb index 66ec2f180..11d5fdf2b 100644 --- a/test/cases/where_chain_test_sqlserver.rb +++ b/test/cases/where_chain_test_sqlserver.rb @@ -1,4 +1,4 @@ -require 'cases/sqlserver_helper' +require 'cases/helper_sqlserver' require 'models/post' require 'models/comment' @@ -14,4 +14,4 @@ def test_coerced_not_eq_with_array_parameter assert_equal([expected], relation.where_values) end end -end \ No newline at end of file +end From f1812362592e572802433c72e6b23039c12b1e53 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 10:36:59 -0500 Subject: [PATCH 0188/1412] Load schema root for SQL Server using new test/support dir. * We have a new `ARTest::Sqlserver` namespace. * Rails will be moving all their patches to `ARTest` too vs root. * Previous commit fixed loading AR schema twice. --- test/cases/helper_sqlserver.rb | 6 +----- test/support/load_schema_sqlserver.rb | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 test/support/load_schema_sqlserver.rb diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index dc1ef176c..31f64f966 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -2,7 +2,6 @@ SQLSERVER_ASSETS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'assets')) SQLSERVER_FIXTURES_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'fixtures')) SQLSERVER_MIGRATIONS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'migrations')) -SQLSERVER_SCHEMA_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'schema')) ACTIVERECORD_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')) ENV['ARCONFIG'] = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'config.yml')) @@ -18,6 +17,7 @@ require 'minitest-spec-rails/init/active_support' require 'minitest-spec-rails/init/mini_shoulda' require 'cases/helper' +require 'support/load_schema_sqlserver' require 'cases/sqlserver_test_case' # A module that we can include in classes where we want to override an active record test. @@ -103,7 +103,3 @@ def with_auto_connect(boolean) end require 'mocha/mini_test' - -# SQL Server. -sqlserver_specific_schema_file = "#{SQLSERVER_SCHEMA_ROOT}/sqlserver_specific_schema.rb" -eval(File.read(sqlserver_specific_schema_file)) unless ENV["SKIP_SCHEMA"] diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb new file mode 100644 index 000000000..ef7ba54ff --- /dev/null +++ b/test/support/load_schema_sqlserver.rb @@ -0,0 +1,25 @@ +module ARTest + module Sqlserver + + extend self + + def schema_root + File.join SQLSERVER_TEST_ROOT, 'schema' + end + + def schema_file + File.join schema_root, 'sqlserver_specific_schema.rb' + end + + def load_schema + original_stdout = $stdout + $stdout = StringIO.new + load schema_file + ensure + $stdout = original_stdout + end + + end +end + +ARTest::Sqlserver.load_schema From eb6ef0755b964debfbe6e3a20b32962dddd20fb7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 11:26:08 -0500 Subject: [PATCH 0189/1412] Move path constants to 'support/paths_sqlserver' with `ARTest::Sqlserver` methods. --- test/cases/disconnected_test_sqlserver.rb | 48 +++++++++---------- test/cases/helper_sqlserver.rb | 11 +---- .../invalid_connection_test_sqlserver.rb | 17 +++---- test/cases/migration_test_sqlserver.rb | 3 +- test/support/load_schema_sqlserver.rb | 4 +- test/support/paths_sqlserver.rb | 41 ++++++++++++++++ 6 files changed, 76 insertions(+), 48 deletions(-) create mode 100644 test/support/paths_sqlserver.rb diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 7f6f4a0e9..4c0f36fa0 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -1,36 +1,34 @@ -if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/disconnected_test.rb").exist? - #cases/disconnected_test was added in rails 4.0.1 so this errors in 4.0.0 - require 'cases/helper_sqlserver' - require 'cases/disconnected_test' +require 'cases/helper_sqlserver' +require 'cases/disconnected_test' - class TestDisconnectedAdapter < ActiveRecord::TestCase - def setup - skip "TestDisconnectedAdapterSqlserver instead " - end +class TestDisconnectedAdapter < ActiveRecord::TestCase + def setup + skip "TestDisconnectedAdapterSqlserver instead " end +end - class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase - self.use_transactional_fixtures = false +class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase + self.use_transactional_fixtures = false - def setup - skip "in-memory database mustn't disconnect" if in_memory_db? - @connection = ActiveRecord::Base.connection - end + def setup + skip "in-memory database mustn't disconnect" if in_memory_db? + @connection = ActiveRecord::Base.connection + end - def teardown - return if in_memory_db? - spec = ActiveRecord::Base.connection_config - ActiveRecord::Base.establish_connection(spec) - end + def teardown + return if in_memory_db? + spec = ActiveRecord::Base.connection_config + ActiveRecord::Base.establish_connection(spec) + end - test "can't execute statements while disconnected" do - with_auto_connect(false) do + test "can't execute statements while disconnected" do + with_auto_connect(false) do + @connection.execute "SELECT count(*) from products" + @connection.disconnect! + assert_raises(ActiveRecord::LostConnection) do @connection.execute "SELECT count(*) from products" - @connection.disconnect! - assert_raises(ActiveRecord::LostConnection) do - @connection.execute "SELECT count(*) from products" - end end end end + end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 31f64f966..1678d6019 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,13 +1,4 @@ -SQLSERVER_TEST_ROOT = File.expand_path(File.join(File.dirname(__FILE__),'..')) -SQLSERVER_ASSETS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'assets')) -SQLSERVER_FIXTURES_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'fixtures')) -SQLSERVER_MIGRATIONS_ROOT = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'migrations')) -ACTIVERECORD_TEST_ROOT = File.expand_path(File.join(Gem.loaded_specs['activerecord'].full_gem_path,'test')) - -ENV['ARCONFIG'] = File.expand_path(File.join(SQLSERVER_TEST_ROOT,'config.yml')) - -$LOAD_PATH.unshift ACTIVERECORD_TEST_ROOT - +require 'support/paths_sqlserver' require 'bundler' ; Bundler.require :development, :test require 'active_support/dependencies' require 'active_record' diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/invalid_connection_test_sqlserver.rb index b06b9598e..ffa7b6dbf 100644 --- a/test/cases/invalid_connection_test_sqlserver.rb +++ b/test/cases/invalid_connection_test_sqlserver.rb @@ -1,13 +1,10 @@ -#cases/invalid_connection_test was added in rails 4.0.1 -if Pathname.new("#{ACTIVERECORD_TEST_ROOT}/cases/invalid_connection_test.rb").exist? - require 'cases/helper_sqlserver' - require 'cases/invalid_connection_test' +require 'cases/helper_sqlserver' +require 'cases/invalid_connection_test' - class TestAdapterWithInvalidConnection < ActiveRecord::TestCase - def setup - #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). - #It makes much more sense for us to use sqlserver - Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' - end +class TestAdapterWithInvalidConnection < ActiveRecord::TestCase + def setup + #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). + #It makes much more sense for us to use sqlserver + Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' end end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 556d95e98..7be8a09cf 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -24,7 +24,8 @@ class MigrationTestSqlserver < ActiveRecord::TestCase should 'not create a tables if error in migrations' do begin - ActiveRecord::Migrator.up(SQLSERVER_MIGRATIONS_ROOT+'/transaction_table') + migrations_dir = File.join ARTest::Sqlserver.migrations_root, 'transaction_table' + ActiveRecord::Migrator.up(migrations_dir) rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message end diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index ef7ba54ff..776c69923 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -4,11 +4,11 @@ module Sqlserver extend self def schema_root - File.join SQLSERVER_TEST_ROOT, 'schema' + @schema_root ||= File.join ARTest::Sqlserver.test_root_sqlserver, 'schema' end def schema_file - File.join schema_root, 'sqlserver_specific_schema.rb' + @schema_file ||= File.join schema_root, 'sqlserver_specific_schema.rb' end def load_schema diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb new file mode 100644 index 000000000..fc9b9873b --- /dev/null +++ b/test/support/paths_sqlserver.rb @@ -0,0 +1,41 @@ +module ARTest + module Sqlserver + + extend self + + def test_root_sqlserver + @test_root_sqlserver ||= begin + root = File.join File.dirname(__FILE__), '..' + File.expand_path(root) + end + end + + def test_root_activerecord + @test_root_activerecord ||= begin + gem_root = Gem.loaded_specs['activerecord'].full_gem_path + File.join gem_root, 'test' + end + end + + def test_root_activerecord_add_to_load_path + return if $LOAD_PATH.include? test_root_activerecord + $LOAD_PATH.unshift(test_root_activerecord) + end + + def migrations_root + @migrations_root ||= File.join test_root_sqlserver, 'migrations' + end + + def arconfig_file + @arconfig_file ||= File.join test_root_sqlserver, 'config.yml' + end + + def arconfig_file_env! + ENV['ARCONFIG'] = ARTest::Sqlserver.arconfig_file + end + + end +end + +ARTest::Sqlserver.test_root_activerecord_add_to_load_path +ARTest::Sqlserver.arconfig_file_env! From 8e29cc6b9418b667a10d68dd30fb3c8b6ee524b2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 11:44:59 -0500 Subject: [PATCH 0190/1412] Clean up un-needed requires and test load order. --- .../connection_adapters/sqlserver_adapter.rb | 6 +----- test/cases/helper_sqlserver.rb | 10 ++-------- test/support/minitest_sqlserver.rb | 2 ++ 3 files changed, 5 insertions(+), 13 deletions(-) create mode 100644 test/support/minitest_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index bb1a2f647..2a3540411 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,10 +1,6 @@ require 'base64' -require 'arel/arel_sqlserver' -require 'arel/visitors/bind_visitor' require 'active_record' -require 'active_record/base' -require 'active_support/concern' -require 'active_support/core_ext/string' +require 'arel/arel_sqlserver' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/explain' diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 1678d6019..fd580a9f7 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,12 +1,6 @@ +require 'bundler' ; Bundler.require :default, :development, :test require 'support/paths_sqlserver' -require 'bundler' ; Bundler.require :development, :test -require 'active_support/dependencies' -require 'active_record' -require 'active_record/version' -require 'active_record/connection_adapters/abstract_adapter' -require 'minitest-spec-rails' -require 'minitest-spec-rails/init/active_support' -require 'minitest-spec-rails/init/mini_shoulda' +require 'support/minitest_sqlserver' require 'cases/helper' require 'support/load_schema_sqlserver' require 'cases/sqlserver_test_case' diff --git a/test/support/minitest_sqlserver.rb b/test/support/minitest_sqlserver.rb new file mode 100644 index 000000000..433504dc7 --- /dev/null +++ b/test/support/minitest_sqlserver.rb @@ -0,0 +1,2 @@ +require 'minitest-spec-rails/init/active_support' +require 'minitest-spec-rails/init/mini_shoulda' From 4d2120d79a1e4834656006c0e8c935c867a34a66 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 11:54:44 -0500 Subject: [PATCH 0191/1412] Move `SqlserverCoercedTest` to `ARTest::Sqlserver::CoercedTest`. --- test/cases/adapter_test_sqlserver.rb | 2 +- test/cases/associations_test_sqlserver.rb | 2 +- .../cases/attribute_methods_test_sqlserver.rb | 2 +- test/cases/base_test_sqlserver.rb | 2 +- test/cases/batches_test_sqlserver.rb | 2 +- .../belongs_to_associations_test_sqlserver.rb | 2 +- test/cases/calculations_test_sqlserver.rb | 2 +- test/cases/column_test_sqlserver.rb | 2 +- .../connection_management_test_sqlserver.rb | 2 +- test/cases/eager_test_sqlserver.rb | 2 +- test/cases/finder_test_sqlserver.rb | 2 +- ...ngs_to_many_associations_test_sqlserver.rb | 2 +- test/cases/helper_sqlserver.rb | 37 +------------------ test/cases/inheritance_test_sqlserver.rb | 2 +- test/cases/migration_test_sqlserver.rb | 2 +- test/cases/persistence_test_sqlserver.rb | 2 +- .../cases/predicate_builder_test_sqlserver.rb | 2 +- test/cases/query_cache_test_sqlserver.rb | 2 +- test/cases/relations_test_sqlserver.rb | 3 +- test/cases/resolver_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 2 +- test/cases/transaction_test_sqlserver.rb | 2 +- .../uniqueness_validation_test_sqlserver.rb | 4 +- test/cases/where_chain_test_sqlserver.rb | 4 +- test/support/coerced_test_sqlserver.rb | 37 +++++++++++++++++++ 25 files changed, 64 insertions(+), 61 deletions(-) create mode 100644 test/support/coerced_test_sqlserver.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index e21e08461..8a2d97fd9 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -811,7 +811,7 @@ class AdapterTest < ActiveRecord::TestCase # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :authors end diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb index 59f94abeb..d68cbb2c7 100644 --- a/test/cases/associations_test_sqlserver.rb +++ b/test/cases/associations_test_sqlserver.rb @@ -6,7 +6,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase # Rails does not do a case-insensive comparison # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_has_many_through_obeys_order_on_through_association owner = owners(:blackbeard) # assert owner.toys.to_sql.include?("pets.name desc") # What's currently in rails diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 777066542..73c94a423 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -14,7 +14,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase :test_typecast_attribute_from_select_to_true ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :developers diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index 3155894d0..9f91be94b 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -10,7 +10,7 @@ class BasicsTest < ActiveRecord::TestCase # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest should 'operate as other database adapters when finding primary keys, standards are postgresql adapter' do assert_nil Post.where(id:'').first diff --git a/test/cases/batches_test_sqlserver.rb b/test/cases/batches_test_sqlserver.rb index ad09d3237..269ad8ddd 100644 --- a/test/cases/batches_test_sqlserver.rb +++ b/test/cases/batches_test_sqlserver.rb @@ -10,7 +10,7 @@ class EachTest < ActiveRecord::TestCase :test_find_in_batches_should_quote_batch_order ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :posts diff --git a/test/cases/belongs_to_associations_test_sqlserver.rb b/test/cases/belongs_to_associations_test_sqlserver.rb index 813ca6b20..77d77a8ea 100644 --- a/test/cases/belongs_to_associations_test_sqlserver.rb +++ b/test/cases/belongs_to_associations_test_sqlserver.rb @@ -7,7 +7,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase COERCED_TESTS = [:test_belongs_to_with_primary_key_joins_on_correct_column] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_belongs_to_with_primary_key_joins_on_correct_column sql = Client.joins(:firm_with_primary_key).to_sql diff --git a/test/cases/calculations_test_sqlserver.rb b/test/cases/calculations_test_sqlserver.rb index 1ad37b628..518c1f90a 100644 --- a/test/cases/calculations_test_sqlserver.rb +++ b/test/cases/calculations_test_sqlserver.rb @@ -18,7 +18,7 @@ class CalculationsTest < ActiveRecord::TestCase :test_offset_is_kept ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :accounts diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index de537e903..7aa697b75 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -353,7 +353,7 @@ class ColumnsTest < ActiveRecord::TestCase # The if current_adapter? conditional below should also contain :SQLServerAdapter. # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest #the only thing we changed in this method was to add :SQLServerAdapter def test_coerced_remove_column_with_multi_column_index diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/connection_management_test_sqlserver.rb index 0114dd490..6e4396d06 100644 --- a/test/cases/connection_management_test_sqlserver.rb +++ b/test/cases/connection_management_test_sqlserver.rb @@ -9,7 +9,7 @@ class ConnectionManagementTest < ActiveRecord::TestCase COERCED_TESTS = [:test_connection_pool_per_pid] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest #https://www.ruby-forum.com/topic/4221299 def test_coerced_connection_pool_per_pid diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index ca26537d9..3bd25de83 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -10,7 +10,7 @@ class EagerAssociationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_count_with_include] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :posts, :comments, :authors diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index d1db6d4eb..bbc88f30a 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -17,7 +17,7 @@ class FinderTest < ActiveRecord::TestCase :test_take_and_first_and_last_with_integer_should_use_sql_limit ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct_and_offset diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb index 4e8d7238d..77d9786a5 100644 --- a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb +++ b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb @@ -10,7 +10,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase :test_caching_of_columns ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest # TODO: put a real test here def test_coerced_count_with_finder_sql diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index fd580a9f7..e57ebca9b 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -3,44 +3,9 @@ require 'support/minitest_sqlserver' require 'cases/helper' require 'support/load_schema_sqlserver' +require 'support/coerced_test_sqlserver' require 'cases/sqlserver_test_case' -# A module that we can include in classes where we want to override an active record test. -# -module SqlserverCoercedTest - - def self.included(base) - base.extend ClassMethods - end - - module ClassMethods - - def self.extended(base) - base.class_eval do - Array(coerced_tests).each do |method_name| - undefine_and_puts(method_name) - end - end - end - - def coerced_tests - self.const_get(:COERCED_TESTS) rescue nil - end - - def method_added(method) - if coerced_tests && coerced_tests.include?(method) - undefine_and_puts(method) - end - end - - def undefine_and_puts(method) - result = undef_method(method) rescue nil - STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? - end - - end -end - module ActiveRecord class TestCase < ActiveSupport::TestCase diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index 06dd4f65e..5d9141e81 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -15,7 +15,7 @@ class InheritanceTest < ActiveRecord::TestCase :test_eager_load_belongs_to_primary_key_quoting ] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_a_bad_type_column Company.connection.execute "SET IDENTITY_INSERT [companies] ON" diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 7be8a09cf..6225af84a 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -68,7 +68,7 @@ class MigrationTestSqlserver < ActiveRecord::TestCase if ActiveRecord::TestCase.sqlserver_azure? class MigrationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest # TODO: put a real test here def test_coerced_test_migrator_db_has_no_schema_migrations_table diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 19cfbe32e..68c4fb39a 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -25,7 +25,7 @@ class PersistenceTest < ActiveRecord::TestCase COERCED_TESTS = [:test_update_all_doesnt_ignore_order, :test_update_columns_changing_id, :test_update_attributes] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_update_all_doesnt_ignore_order assert_equal authors(:david).id + 1, authors(:mary).id diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/predicate_builder_test_sqlserver.rb index 27c3c055e..b352091ad 100644 --- a/test/cases/predicate_builder_test_sqlserver.rb +++ b/test/cases/predicate_builder_test_sqlserver.rb @@ -7,7 +7,7 @@ class PredicateBuilderTest < ActiveRecord::TestCase COERCED_TESTS = [:test_registering_new_handlers] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_registering_new_handlers ActiveRecord::PredicateBuilder.register_handler(Regexp, proc do |column, value| diff --git a/test/cases/query_cache_test_sqlserver.rb b/test/cases/query_cache_test_sqlserver.rb index 268045fa5..cf26d2d42 100644 --- a/test/cases/query_cache_test_sqlserver.rb +++ b/test/cases/query_cache_test_sqlserver.rb @@ -8,7 +8,7 @@ class QueryCacheTest < ActiveRecord::TestCase COERCED_TESTS = [:test_cache_does_not_wrap_string_results_in_arrays] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :tasks diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index ae2bc0881..c12b6a3dd 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -1,14 +1,13 @@ require "cases/helper_sqlserver" require 'models/post' - class RelationTest < ActiveRecord::TestCase COERCED_TESTS = [ :test_merging_reorders_bind_params, :test_to_sql_on_eager_join ] # Until that patch is made to rails we are preventing this test from running in this gem. - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest fixtures :posts def test_coerced_merging_reorders_bind_params diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index 5718a3f11..c9512a85b 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -6,7 +6,7 @@ class ConnectionSpecification class ResolverTest < ActiveRecord::TestCase - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest COERCED_TESTS = [ :test_url_host_no_db, diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index d79c6e33e..9f7ffe552 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -77,7 +77,7 @@ class SchemaDumperTest < ActiveRecord::TestCase COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal output = standard_dump diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 5a7d02f82..0a0c65770 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -47,7 +47,7 @@ def assert_no_ships end class TransactionTest < ActiveRecord::TestCase - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest COERCED_TESTS = [:test_releasing_named_savepoints] diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/uniqueness_validation_test_sqlserver.rb index 8374b289b..29b45cbf4 100644 --- a/test/cases/uniqueness_validation_test_sqlserver.rb +++ b/test/cases/uniqueness_validation_test_sqlserver.rb @@ -17,14 +17,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_validate_uniqueness_with_limit_and_utf8] - include SqlserverCoercedTest + include ARTest::Sqlserver::CoercedTest # I guess most databases just truncate a string when inserting. To pass this test we do a few things. # First, we make sure the type is unicode safe, second we extend the limit to well beyond what is # needed. At the top we make sure to auto truncate the :title string like other databases would do # automatically. # - # "一二三四五".mb_chars.size # => 5 + # "一二三四五".mb_chars.size # => 5 # "一二三四五六七八".mb_chars.size # => 8 # "一二三四五六七八".mb_chars.to(4).to_s # => "一二三四五" diff --git a/test/cases/where_chain_test_sqlserver.rb b/test/cases/where_chain_test_sqlserver.rb index 11d5fdf2b..80e26bae2 100644 --- a/test/cases/where_chain_test_sqlserver.rb +++ b/test/cases/where_chain_test_sqlserver.rb @@ -4,7 +4,8 @@ module ActiveRecord class WhereChainTest < ActiveRecord::TestCase - include SqlserverCoercedTest + + include ARTest::Sqlserver::CoercedTest COERCED_TESTS = [:test_not_eq_with_array_parameter] @@ -13,5 +14,6 @@ def test_coerced_not_eq_with_array_parameter relation = Post.where.not(['title = ?', 'hello']) assert_equal([expected], relation.where_values) end + end end diff --git a/test/support/coerced_test_sqlserver.rb b/test/support/coerced_test_sqlserver.rb new file mode 100644 index 000000000..6764a6393 --- /dev/null +++ b/test/support/coerced_test_sqlserver.rb @@ -0,0 +1,37 @@ +module ARTest + module Sqlserver + module CoercedTest + + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + + def self.extended(base) + base.class_eval do + Array(coerced_tests).each do |method_name| + undefine_and_puts(method_name) + end + end + end + + def coerced_tests + self.const_get(:COERCED_TESTS) rescue nil + end + + def method_added(method) + if coerced_tests && coerced_tests.include?(method) + undefine_and_puts(method) + end + end + + def undefine_and_puts(method) + result = undef_method(method) rescue nil + STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? + end + + end + end + end +end From a14ee3ad93adf894715da69efa40fc4656351172 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 12:15:30 -0500 Subject: [PATCH 0192/1412] Move lib/active_record/sqlserver_test_case.rb to test/support/sql_counter_sqlserver.rb --- lib/active_record/sqlserver_test_case.rb | 17 ------------- test/cases/helper_sqlserver.rb | 2 +- test/support/sql_counter_sqlserver.rb | 31 ++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 18 deletions(-) delete mode 100644 lib/active_record/sqlserver_test_case.rb create mode 100644 test/support/sql_counter_sqlserver.rb diff --git a/lib/active_record/sqlserver_test_case.rb b/lib/active_record/sqlserver_test_case.rb deleted file mode 100644 index f221e1788..000000000 --- a/lib/active_record/sqlserver_test_case.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'active_record/test_case.rb' - -# TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' -# This is a temporary hack until we can just get the sqlserver_ignored regex in rails -ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| - if listener.inspect =~ /ActiveRecord::SQLCounter/ - ActiveSupport::Notifications.unsubscribe(listener) - end -end - -module ActiveRecord - class SQLCounter - sqlserver_ignored = [/SELECT SCOPE_IDENTITY/, /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/, /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/] - ignored_sql.concat sqlserver_ignored - end - ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) -end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index e57ebca9b..b230fbb06 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -4,7 +4,7 @@ require 'cases/helper' require 'support/load_schema_sqlserver' require 'support/coerced_test_sqlserver' -require 'cases/sqlserver_test_case' +require 'support/sql_counter_sqlserver' module ActiveRecord class TestCase < ActiveSupport::TestCase diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb new file mode 100644 index 000000000..239f85de2 --- /dev/null +++ b/test/support/sql_counter_sqlserver.rb @@ -0,0 +1,31 @@ +module ARTest + module Sqlserver + + extend self + + attr_accessor :sql_counter_listenter + + def ignored_sql + [ /SELECT SCOPE_IDENTITY/, + /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/, + /SELECT @@version/, + /SELECT @@TRANCOUNT/, + /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/ ] + end + + def sql_counter_listenters + ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').select do |listener| + listener.inspect =~ /ActiveRecord::SQLCounter/ + end + end + + def sql_counter_listenters_unsubscribe + sql_counter_listenters.each { |listener| ActiveSupport::Notifications.unsubscribe(listener) } + end + + end +end + +ActiveRecord::SQLCounter.ignored_sql.concat ARTest::Sqlserver.ignored_sql +ARTest::Sqlserver.sql_counter_listenters_unsubscribe +ARTest::Sqlserver.sql_counter_listenter = ActiveSupport::Notifications.subscribe 'sql.active_record', ActiveRecord::SQLCounter.new From 10aac34f040e1e4a4d8375d5a646e258042b8696 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 15:54:38 -0500 Subject: [PATCH 0193/1412] Adding Guard. Terse and un-memoized path helpers. --- Guardfile | 13 +++++++++++++ Rakefile | 17 +++++------------ activerecord-sqlserver-adapter.gemspec | 3 ++- test/cases/helper_sqlserver.rb | 3 +-- test/support/paths_sqlserver.rb | 24 +++++++++++++----------- 5 files changed, 34 insertions(+), 26 deletions(-) create mode 100644 Guardfile diff --git a/Guardfile b/Guardfile new file mode 100644 index 000000000..91d29fce2 --- /dev/null +++ b/Guardfile @@ -0,0 +1,13 @@ +require_relative 'test/support/paths_sqlserver' + +guard :minitest, { + all_on_start: false, + autorun: false, + include: ['lib', 'test', File.join(ARTest::Sqlserver.root_activerecord, 'lib'), File.join(ARTest::Sqlserver.root_activerecord, 'test')], + test_folders: ['test'], + test_file_patterns: ["*_test_sqlserver.rb"], +} do + watch(%r{^test/cases/\w+_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 diff --git a/Rakefile b/Rakefile index 6acbd548f..1a67e17b0 100644 --- a/Rakefile +++ b/Rakefile @@ -1,16 +1,9 @@ -require 'rake' require 'rake/testtask' - -AR_PATH = Gem.loaded_specs['activerecord'].full_gem_path - -# Since the Gemfile for this project requires, rails, it ends up causing -# Rails.env to be defined, which affects some of the unit tests. We fix this -# by setting the RAILS_ENV to "default_env" -ENV['RAILS_ENV'] = 'default_env' +require_relative 'test/support/paths_sqlserver' def test_libs - ar_lib = File.join AR_PATH, 'lib' - ar_test = File.join AR_PATH, 'test' + ar_lib = File.join ARTest::Sqlserver.root_activerecord, 'lib' + ar_test = File.join ARTest::Sqlserver.root_activerecord, 'test' ['lib', 'test', ar_lib, ar_test] end @@ -18,8 +11,8 @@ def test_files test_setup = ['test/cases/helper_sqlserver.rb'] return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') - ar_cases = Dir.glob("#{AR_PATH}/test/cases/**/*_test.rb") - adapter_cases = Dir.glob("#{AR_PATH}/test/cases/adapters/**/*_test.rb") + ar_cases = Dir.glob("#{ARTest::Sqlserver.root_activerecord}/test/cases/**/*_test.rb") + adapter_cases = Dir.glob("#{ARTest::Sqlserver.root_activerecord}/test/cases/adapters/**/*_test.rb") if ENV['SQLSERVER_ONLY'] sqlserver_cases elsif ENV['ACTIVERECORD_ONLY'] diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 261c144f7..dbdc227da 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,9 +17,10 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.0' spec.add_development_dependency 'bundler' - spec.add_development_dependency 'rake' + spec.add_development_dependency 'guard-minitest' spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' spec.add_development_dependency 'nokogiri' spec.add_development_dependency 'pry' + spec.add_development_dependency 'rake' end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index b230fbb06..21e69e236 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -5,6 +5,7 @@ require 'support/load_schema_sqlserver' require 'support/coerced_test_sqlserver' require 'support/sql_counter_sqlserver' +require 'mocha/mini_test' module ActiveRecord class TestCase < ActiveSupport::TestCase @@ -51,5 +52,3 @@ def with_auto_connect(boolean) end end - -require 'mocha/mini_test' diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index fc9b9873b..8db1097dd 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -3,18 +3,20 @@ module Sqlserver extend self + def root_sqlserver + File.expand_path File.join(File.dirname(__FILE__), '..', '..') + end + def test_root_sqlserver - @test_root_sqlserver ||= begin - root = File.join File.dirname(__FILE__), '..' - File.expand_path(root) - end + File.join root_sqlserver, 'test' + end + + def root_activerecord + Gem.loaded_specs['activerecord'].full_gem_path end def test_root_activerecord - @test_root_activerecord ||= begin - gem_root = Gem.loaded_specs['activerecord'].full_gem_path - File.join gem_root, 'test' - end + File.join root_activerecord, 'test' end def test_root_activerecord_add_to_load_path @@ -23,15 +25,15 @@ def test_root_activerecord_add_to_load_path end def migrations_root - @migrations_root ||= File.join test_root_sqlserver, 'migrations' + File.join test_root_sqlserver, 'migrations' end def arconfig_file - @arconfig_file ||= File.join test_root_sqlserver, 'config.yml' + File.join test_root_sqlserver, 'config.yml' end def arconfig_file_env! - ENV['ARCONFIG'] = ARTest::Sqlserver.arconfig_file + ENV['ARCONFIG'] = arconfig_file end end From 5b41ff1fe2d18b4d5910041fb7c7a7b991f51593 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 18:14:35 -0500 Subject: [PATCH 0194/1412] Use guard terminal notififier. --- Gemfile | 4 ++++ Guardfile | 2 ++ Rakefile | 10 ++-------- test/support/paths_sqlserver.rb | 13 +++++++++---- 4 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index d2fd26047..4ca2e392e 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,10 @@ source 'https://rubygems.org' gemspec +if RbConfig::CONFIG["host_os"] =~ /darwin/ + gem 'terminal-notifier-guard' +end + if ENV['RAILS_SOURCE'] gemspec path: ENV['RAILS_SOURCE'] else diff --git a/Guardfile b/Guardfile index 91d29fce2..4f43d5d7e 100644 --- a/Guardfile +++ b/Guardfile @@ -1,5 +1,7 @@ require_relative 'test/support/paths_sqlserver' +notification :terminal_notifier if Guard::Notifier::TerminalNotifier.available? + guard :minitest, { all_on_start: false, autorun: false, diff --git a/Rakefile b/Rakefile index 1a67e17b0..efb660abe 100644 --- a/Rakefile +++ b/Rakefile @@ -1,12 +1,6 @@ require 'rake/testtask' require_relative 'test/support/paths_sqlserver' -def test_libs - ar_lib = File.join ARTest::Sqlserver.root_activerecord, 'lib' - ar_test = File.join ARTest::Sqlserver.root_activerecord, 'test' - ['lib', 'test', ar_lib, ar_test] -end - def test_files test_setup = ['test/cases/helper_sqlserver.rb'] return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] @@ -30,7 +24,7 @@ namespace :test do %w(dblib odbc).each do |mode| Rake::TestTask.new(mode) do |t| - t.libs = test_libs + t.libs = ARTest::Sqlserver.test_load_paths t.test_files = test_files t.verbose = true end @@ -56,7 +50,7 @@ namespace :profile do Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| profile_case = File.basename(test_file).sub('_profile_case.rb', '') Rake::TestTask.new(profile_case) do |t| - t.libs = test_libs + t.libs = ARTest::Sqlserver.test_load_paths t.test_files = [test_file] t.verbose = true end diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index 8db1097dd..0fa13b19f 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -19,9 +19,14 @@ def test_root_activerecord File.join root_activerecord, 'test' end - def test_root_activerecord_add_to_load_path - return if $LOAD_PATH.include? test_root_activerecord - $LOAD_PATH.unshift(test_root_activerecord) + def test_load_paths + ar_lib = File.join root_activerecord, 'lib' + ar_test = File.join root_activerecord, 'test' + ['lib', 'test', ar_lib, ar_test] + end + + def add_to_load_paths! + test_load_paths.each { |p| $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p) } end def migrations_root @@ -39,5 +44,5 @@ def arconfig_file_env! end end -ARTest::Sqlserver.test_root_activerecord_add_to_load_path +ARTest::Sqlserver.add_to_load_paths! ARTest::Sqlserver.arconfig_file_env! From c14d949a48d3ef3e396c52a41c6acb1f030eed15 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 1 Jan 2015 19:48:46 -0500 Subject: [PATCH 0195/1412] Last few Rakefile cleanups. --- Rakefile | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Rakefile b/Rakefile index efb660abe..b29f2fb5b 100644 --- a/Rakefile +++ b/Rakefile @@ -2,17 +2,15 @@ require 'rake/testtask' require_relative 'test/support/paths_sqlserver' def test_files - test_setup = ['test/cases/helper_sqlserver.rb'] - return test_setup + (ENV['TEST_FILES']).split(',') if ENV['TEST_FILES'] + return ENV['TEST_FILES'].split(',') if ENV['TEST_FILES'] sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') ar_cases = Dir.glob("#{ARTest::Sqlserver.root_activerecord}/test/cases/**/*_test.rb") - adapter_cases = Dir.glob("#{ARTest::Sqlserver.root_activerecord}/test/cases/adapters/**/*_test.rb") if ENV['SQLSERVER_ONLY'] sqlserver_cases elsif ENV['ACTIVERECORD_ONLY'] - test_setup + (ar_cases - adapter_cases) + ar_cases else - test_setup + sqlserver_cases + (ar_cases - adapter_cases) + sqlserver_cases + ar_cases end end From 3ed20410f4580f6de3efffec7e343fd62357b561 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 2 Jan 2015 12:10:16 -0500 Subject: [PATCH 0196/1412] Single PORO for decomposing and quoting SQL Server names/identifiers. Please read this article for all details: http://technet.microsoft.com/en-us/library/ms187879(v=sql.105).aspx --- .../sqlserver/database_statements.rb | 11 +- .../connection_adapters/sqlserver/quoting.rb | 10 +- .../sqlserver/schema_cache.rb | 19 +-- .../sqlserver/schema_statements.rb | 12 +- .../connection_adapters/sqlserver/utils.rb | 114 ++++++++++++++++-- .../connection_adapters/sqlserver_adapter.rb | 6 +- test/cases/adapter_test_sqlserver.rb | 35 ------ test/cases/schema_test_sqlserver.rb | 12 -- test/cases/utils_test_sqlserver.rb | 88 ++++++++++++++ 9 files changed, 212 insertions(+), 95 deletions(-) create mode 100644 test/cases/utils_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index a3a8e6481..d3b3bcf2c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -116,8 +116,8 @@ def execute_procedure(proc_name, *variables) def use_database(database = nil) return if sqlserver_azure? - database ||= @connection_options[:database] - do_execute "USE #{quote_database_name(database)}" unless database.blank? + name = Sqlserver::Utils.extract_identifiers(database || @connection_options[:database]) + do_execute "USE #{name}" unless database.blank? end def user_options @@ -258,7 +258,7 @@ def drop_database(database) retry_count = 0 max_retries = 1 begin - do_execute "DROP DATABASE #{quote_database_name(database)}" + do_execute "DROP DATABASE #{Sqlserver::Utils.extract_identifiers(database)}" rescue ActiveRecord::StatementInvalid => err if err.message =~ /because it is currently in use/i raise if retry_count >= max_retries @@ -274,10 +274,11 @@ def drop_database(database) end def create_database(database, collation = @connection_options[:collation]) + name = Sqlserver::Utils.extract_identifiers(database) if collation - do_execute "CREATE DATABASE #{quote_database_name(database)} COLLATE #{collation}" + do_execute "CREATE DATABASE #{name} COLLATE #{collation}" else - do_execute "CREATE DATABASE #{quote_database_name(database)}" + do_execute "CREATE DATABASE #{name}" end end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 609a71b95..9e115718c 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -38,22 +38,18 @@ def quoted_string_prefix QUOTED_STRING_PREFIX end - def quote_string(string) - string.to_s.gsub(/\'/, "''") + def quote_string(s) + Sqlserver::Utils.quote_string(s) end def quote_column_name(name) - schema_cache.quote_name(name) + Sqlserver::Utils.extract_identifiers(name).object_quoted end def quote_table_name(name) quote_column_name(name) end - def quote_database_name(name) - schema_cache.quote_name(name, false) - end - # Does not quote function default values for UUID columns def quote_default_value(value, column) if column.type == :uuid && value =~ /\(\)/ diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index c3917ce5d..ede9b0d2a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module Sqlserver class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache + attr_reader :view_information def initialize(conn) @@ -9,7 +10,6 @@ def initialize(conn) @table_names = nil @view_names = nil @view_information = {} - @quoted_names = {} end # Superclass Overrides @@ -26,7 +26,6 @@ def clear! @table_names = nil @view_names = nil @view_information.clear - @quoted_names.clear end def clear_table_cache!(table_name) @@ -65,25 +64,13 @@ def view_information(table_name) @view_information[key] = connection.send(:view_information, table_name) end - def quote_name(name, split_on_dots = true) - return @quoted_names[name] if @quoted_names.key? name - - @quoted_names[name] = if split_on_dots - name.to_s.split('.').map { |n| quote_name_part(n) }.join('.') - else - quote_name_part(name.to_s) - end - end private - def quote_name_part(part) - part =~ /^\[.*\]$/ ? part : "[#{part.to_s.gsub(']', ']]')}]" - end - def table_name_key(table_name) - Utils.unqualify_table_name(table_name) + Sqlserver::Utils.extract_identifiers(table_name).object end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index fd2c7bc21..34c4eb31b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -12,7 +12,7 @@ def tables(table_type = 'BASE TABLE') def table_exists?(table_name) return false if table_name.blank? - unquoted_table_name = Utils.unqualify_table_name(table_name) + unquoted_table_name = Sqlserver::Utils.extract_identifiers(table_name).object super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name) end @@ -181,10 +181,10 @@ def initialize_native_database_types end def column_definitions(table_name) - db_name = Utils.unqualify_db_name(table_name) + db_name = Sqlserver::Utils.extract_identifiers(table_name).database db_name_with_period = "#{db_name}." if db_name - table_schema = Utils.unqualify_table_schema(table_name) - table_name = Utils.unqualify_table_name(table_name) + table_schema = Sqlserver::Utils.extract_identifiers(table_name).schema + table_name = Sqlserver::Utils.extract_identifiers(table_name).object sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -329,7 +329,7 @@ def view_table_name(table_name) end def view_information(table_name) - table_name = Utils.unqualify_table_name(table_name) + table_name = Sqlserver::Utils.extract_identifiers(table_name).object view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'", 'SCHEMA' if view_info view_info = view_info.with_indifferent_access @@ -346,7 +346,7 @@ def view_information(table_name) end def table_name_or_views_table_name(table_name) - unquoted_table_name = Utils.unqualify_table_name(table_name) + unquoted_table_name = Sqlserver::Utils.extract_identifiers(table_name).object schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 1022b870a..2ac70d7de 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -1,25 +1,117 @@ +require 'strscan' + module ActiveRecord module ConnectionAdapters module Sqlserver - class Utils - class << self - def unquote_string(string) - string.to_s.gsub(/\'\'/, "'") + module Utils + + # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL + # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils. + # + class Name + + SEPARATOR = "." + SCANNER = /\]?\./ + + attr_reader :server, :database, :schema, :object + attr_reader :raw_name + + def initialize(name) + @raw_name = name.to_s + parse_raw_name + end + + def object_quoted + quote object + end + + def schema_quoted + schema ? quote(schema) : schema end - def unqualify_table_name(table_name) - table_name.to_s.split('.').last.tr('[]', '') + def database_quoted + database ? quote(database) : database end - def unqualify_table_schema(table_name) - table_name.to_s.split('.')[-2].gsub(/[\[\]]/, '') rescue nil + def server_quoted + server ? quote(server) : server end - def unqualify_db_name(table_name) - table_names = table_name.to_s.split('.') - table_names.length == 3 ? table_names.first.tr('[]', '') : nil + def to_s + quoted end + + def quoted + parts.map{ |p| quote(p) if p }.join SEPARATOR + end + + def ==(o) + o.class == self.class && o.parts == parts + end + alias_method :eql?, :== + + def hash + parts.hash + end + + protected + + def parse_raw_name + @parts = [] + return if raw_name.blank? + scanner = StringScanner.new(raw_name) + matched = scanner.scan_until(SCANNER) + while matched + part = matched[0..-2] + @parts << (part.blank? ? nil : unquote(part)) + matched = scanner.scan_until(SCANNER) + end + case @parts.length + when 3 + @server, @database, @schema = @parts + when 2 + @database, @schema = @parts + when 1 + @schema = @parts.first + end + rest = scanner.rest + rest = rest.starts_with?('.') ? rest[1..-1] : rest[0..-1] + @object = unquote(rest) + @parts << @object + end + + def quote(part) + part =~ /\A\[.*\]\z/ ? part : "[#{part.to_s.gsub(']', ']]')}]" + end + + def unquote(part) + if part && part.start_with?('[') + part[1..-2] + else + part + end + end + + def parts + @parts + end + end + + extend self + + def quote_string(s) + s.gsub /\'/, "''" + end + + def unquote_string(s) + s.to_s.gsub(/\'\'/, "'") + end + + def extract_identifiers(name) + Sqlserver::Utils::Name.new(name) + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 2a3540411..d3f59c30a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -373,12 +373,12 @@ def initialize_dateformatter end def remove_database_connections_and_rollback(database = nil) - database ||= current_database - do_execute "ALTER DATABASE #{quote_database_name(database)} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" + name = Sqlserver::Utils.extract_identifiers(database || current_database) + do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" begin yield ensure - do_execute "ALTER DATABASE #{quote_database_name(database)} SET MULTI_USER" + do_execute "ALTER DATABASE #{name} SET MULTI_USER" end if block_given? end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 8a2d97fd9..2e5521603 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -112,41 +112,6 @@ class AdapterTestSqlserver < ActiveRecord::TestCase end - context 'for Utils.unqualify_table_name and Utils.unqualify_db_name' do - - setup do - @expected_table_name = 'baz' - @expected_db_name = 'foo' - @first_second_table_names = ['[baz]','baz','[bar].[baz]','bar.baz'] - @third_table_names = ['[foo].[bar].[baz]','foo.bar.baz'] - @qualifed_table_names = @first_second_table_names + @third_table_names - end - - should 'return clean table_name from Utils.unqualify_table_name' do - @qualifed_table_names.each do |qtn| - assert_equal @expected_table_name, - ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_table_name(qtn), - "This qualifed_table_name #{qtn} did not unqualify correctly." - end - end - - should 'return nil from Utils.unqualify_db_name when table_name is less than 2 qualified' do - @first_second_table_names.each do |qtn| - assert_equal nil, ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_db_name(qtn), - "This qualifed_table_name #{qtn} did not return nil." - end - end - - should 'return clean db_name from Utils.unqualify_db_name when table is thrid level qualified' do - @third_table_names.each do |qtn| - assert_equal @expected_db_name, - ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_db_name(qtn), - "This qualifed_table_name #{qtn} did not unqualify the db_name correctly." - end - end - - end - should 'return true to #insert_sql? for inserts only' do assert @connection.send(:insert_sql?,'INSERT...') assert @connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 809486db5..bc4d82150 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -8,10 +8,6 @@ class SchemaTestSqlserver < ActiveRecord::TestCase @connection = ActiveRecord::Base.connection end - def read_schema_name(table_name) - ActiveRecord::ConnectionAdapters::Sqlserver::Utils.unqualify_table_schema(table_name) - end - context 'When table is dbo schema' do should 'find primary key for tables with odd schema' do @@ -50,14 +46,6 @@ def read_schema_name(table_name) assert_equal 1, columns.select{ |c| c.primary }.size end - should "return schema name in all cases" do - assert_nil read_schema_name("table") - assert_equal "schema1", read_schema_name("schema1.table") - assert_equal "schema2", read_schema_name("database.schema2.table") - assert_equal "schema3", read_schema_name("server.database.schema3.table") - assert_equal "schema3", read_schema_name("[server].[database].[schema3].[table]") - end - should "return correct varchar and nvarchar column limit (length) when table is in non dbo schema" do columns = @connection.columns("test.sql_server_schema_columns") assert_equal 255, columns.find {|c| c.name == 'name'}.limit diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb new file mode 100644 index 000000000..1b5fd5390 --- /dev/null +++ b/test/cases/utils_test_sqlserver.rb @@ -0,0 +1,88 @@ +require 'cases/helper_sqlserver' + +class UtilsTestSqlserver < ActiveRecord::TestCase + + Utils = ActiveRecord::ConnectionAdapters::Sqlserver::Utils + + it '.quote_string' do + Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users" + end + + it '.unquote_string' do + Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" + end + + describe '.extract_identifiers constructor and thus Sqlserver::Utils::Name value object' do + + let(:valid_names) { valid_names_unquoted + valid_names_quoted } + + let(:valid_names_unquoted) {[ + 'server.database.schema.object', + 'server.database..object', + 'server..schema.object', + 'server...object', + 'database.schema.object', + 'database..object', + 'schema.object', + 'object' + ]} + + let(:valid_names_quoted) {[ + '[server].[database].[schema].[object]', + '[server].[database]..[object]', + '[server]..[schema].[object]', + '[server]...[object]', + '[database].[schema].[object]', + '[database]..[object]', + '[schema].[object]', + '[object]' + ]} + + 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/ } } + + it 'extracts and returns #object identifier unquoted by default or quoted as needed' do + valid_names.each do |n| + name = Utils.extract_identifiers(n) + name.object.must_equal 'object', "With #{n.inspect} for #object" + name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted" + end + end + + [:schema, :database, :server].each do |part| + + it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do + present, blank = send(:"#{part}_names") + present.each do |n| + name = Utils.extract_identifiers(n) + name.send(:"#{part}").must_equal "#{part}", "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| + name = Utils.extract_identifiers(n) + name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method" + name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method" + end + end + + end + + it 'does not blow up on nil or blank string name' do + Utils.extract_identifiers(nil).object.must_be_nil + Utils.extract_identifiers(' ').object.must_be_nil + end + + it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do + Utils.extract_identifiers('object').quoted.must_equal '[object]' + Utils.extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' + Utils.extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' + end + + it 'can take a symbol argument' do + Utils.extract_identifiers(:object).object.must_equal 'object' + end + + end + +end From fee1a2d04b7e4f261b99666252b48b9866f9f912 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 10:53:14 -0500 Subject: [PATCH 0197/1412] Allow Guardfile to focs on a single test due to any change. For Example: $ FOCUS_TEST=test/cases/base_test_sqlserver.rb bundle exec guard --- Guardfile | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Guardfile b/Guardfile index 4f43d5d7e..4ca4580d3 100644 --- a/Guardfile +++ b/Guardfile @@ -1,15 +1,24 @@ require_relative 'test/support/paths_sqlserver' notification :terminal_notifier if Guard::Notifier::TerminalNotifier.available? +ignore %r{debug\.log} + +ar_lib = File.join ARTest::Sqlserver.root_activerecord, 'lib' +ar_test = File.join ARTest::Sqlserver.root_activerecord, 'test' guard :minitest, { all_on_start: false, autorun: false, - include: ['lib', 'test', File.join(ARTest::Sqlserver.root_activerecord, 'lib'), File.join(ARTest::Sqlserver.root_activerecord, 'test')], + include: ['lib', 'test', ar_lib, ar_test], test_folders: ['test'], - test_file_patterns: ["*_test_sqlserver.rb"], + test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb"] } do - watch(%r{^test/cases/\w+_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' } + # Our project watchers. + if ENV['FOCUS_TEST'] + watch(%r{.*}) { ENV['FOCUS_TEST'] } if ENV['FOCUS_TEST'] + else + watch(%r{^test/cases/\w+_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 From e7c257ad777f3b4acba740994c7ae1cbb4c4ac04 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 10:59:32 -0500 Subject: [PATCH 0198/1412] Proper indent for new TinyTDS client args. Rename config helpers with config_ prefix. --- .../connection_adapters/sqlserver_adapter.rb | 74 +++++++++---------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d3f59c30a..6790eac9f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -284,19 +284,19 @@ def connect def dblib_connect(config) TinyTds::Client.new( - dataserver: config[:dataserver], - host: config[:host], - port: config[:port], - username: config[:username], - password: config[:password], - database: config[:database], - tds_version: config[:tds_version], - appname: appname(config), - login_timeout: login_timeout(config), - timeout: timeout(config), - encoding: encoding(config), - azure: config[:azure] - ).tap do |client| + dataserver: config[:dataserver], + host: config[:host], + port: config[:port], + username: config[:username], + password: config[:password], + database: config[:database], + tds_version: config[:tds_version], + appname: config_appname(config), + login_timeout: config_login_timeout(config), + timeout: config_timeout(config), + encoding: config_encoding(config), + azure: config[:azure] + ).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do @@ -315,22 +315,6 @@ def dblib_connect(config) end end - def appname(config) - config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil - end - - def login_timeout(config) - config[:login_timeout].present? ? config[:login_timeout].to_i : nil - end - - def timeout(config) - config[:timeout].present? ? config[:timeout].to_i / 1000 : nil - end - - def encoding(config) - config[:encoding].present? ? config[:encoding] : nil - end - def odbc_connect(config) if config[:dsn].include?(';') driver = ODBC::Driver.new.tap do |d| @@ -350,19 +334,26 @@ def odbc_connect(config) end end - # Override this method so every connection can be configured to your needs. - # For example: - # raw_connection_do "SET TEXTSIZE #{64.megabytes}" - # raw_connection_do "SET CONCAT_NULL_YIELDS_NULL ON" - def configure_connection + def config_appname(config) + config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil + end + + def config_login_timeout(config) + config[:login_timeout].present? ? config[:login_timeout].to_i : nil end - # Override this method so every connection can have a unique name. Max 30 characters. Used by TinyTDS only. - # For example: - # "myapp_#{$$}_#{Thread.current.object_id}".to(29) - def configure_application_name + def config_timeout(config) + config[:timeout].present? ? config[:timeout].to_i / 1000 : nil end + def config_encoding(config) + config[:encoding].present? ? config[:encoding] : nil + end + + def configure_connection ; end + + def configure_application_name ; end + def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a @@ -414,6 +405,7 @@ def auto_reconnected? ensure @auto_connecting = false end - end # class SQLServerAdapter < AbstractAdapter - end # module ConnectionAdapters -end # module ActiveRecord + + end + end +end From caf631b99ef2ec4bf8db1b121a4c41c0d4c430b7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 11:08:17 -0500 Subject: [PATCH 0199/1412] Make Fetch Happen! Used the `base_test_sqlserver.rb` to get basic query support working. * Teardown our Arel visitor!!! Fetch now happens. Will build up form here. * Teardown quoting.rb to basics. Will build from here. * Use abstract adapter `lookup_cast_type` and pass `cast_type` when building column objects now. * TODO: I will be building out new Type objects that allow TinyTDS to win at native casting. * Simplify our column object. Remove 2008 hacks and need for so much out of scope reflection. --- .../connection_adapters/sqlserver/quoting.rb | 76 ++- .../sqlserver/schema_statements.rb | 9 +- .../connection_adapters/sqlserver_adapter.rb | 6 +- .../connection_adapters/sqlserver_column.rb | 40 +- lib/arel/nodes_sqlserver.rb | 14 - lib/arel/select_manager_sqlserver.rb | 62 --- lib/arel/visitors/sqlserver.rb | 471 ++---------------- lib/{arel => }/arel_sqlserver.rb | 4 +- test/cases/base_test_sqlserver.rb | 13 +- test/cases/column_test_sqlserver.rb | 7 - 10 files changed, 106 insertions(+), 596 deletions(-) delete mode 100644 lib/arel/nodes_sqlserver.rb delete mode 100644 lib/arel/select_manager_sqlserver.rb rename lib/{arel => }/arel_sqlserver.rb (54%) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 9e115718c..0cda46611 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -2,41 +2,10 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module Quoting - QUOTED_TRUE, QUOTED_FALSE = '1', '0' - QUOTED_STRING_PREFIX = 'N' - - def quote(value, column = nil) - case value - when String, ActiveSupport::Multibyte::Chars - if column && column.type == :integer && value.blank? - value.to_i.to_s - elsif column && column.type == :binary - column.class.string_to_binary(value) - elsif column && [:uuid, :uniqueidentifier].include?(column.type) - "'#{quote_string(value)}'" - elsif value.is_utf8? || (column && column.type == :string) - "#{quoted_string_prefix}'#{quote_string(value)}'" - else - super - end - when Date, Time - if column && column.sql_type == 'datetime' - "'#{quoted_datetime(value)}'" - elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time') - "'#{quoted_full_iso8601(value)}'" - else - super - end - when nil - column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super - else - super - end - end - def quoted_string_prefix - QUOTED_STRING_PREFIX - end + QUOTED_TRUE = '1' + QUOTED_FALSE = '0' + QUOTED_STRING_PREFIX = 'N' def quote_string(s) Sqlserver::Utils.quote_string(s) @@ -46,11 +15,6 @@ def quote_column_name(name) Sqlserver::Utils.extract_identifiers(name).object_quoted end - def quote_table_name(name) - quote_column_name(name) - end - - # Does not quote function default values for UUID columns def quote_default_value(value, column) if column.type == :uuid && value =~ /\(\)/ value @@ -59,22 +23,27 @@ def quote_default_value(value, column) end end - def substitute_at(column, index) - if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' - nil - else - Arel::Nodes::BindParam.new "@#{index}" - end + def substitute_at(column, _unused = 0) + return nil if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' + super end def quoted_true QUOTED_TRUE end + def unquoted_true + 1 + end + def quoted_false QUOTED_FALSE end + def unquoted_false + 0 + end + def quoted_datetime(value) if value.acts_like?(:time) time_zone_qualified_value = quoted_value_acts_like_time_filter(value) @@ -106,12 +75,27 @@ def quoted_date(value) end end - protected + + private + + def _quote(value) # , column = nil + case value + when String, ActiveSupport::Multibyte::Chars + if value.is_utf8? + "#{QUOTED_STRING_PREFIX}#{super}" + else + super + end + else + super(value) + end + end def quoted_value_acts_like_time_filter(value) zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 34c4eb31b..681106a12 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module Sqlserver module SchemaStatements + def native_database_types @native_database_types ||= initialize_native_database_types.freeze end @@ -45,10 +46,15 @@ def columns(table_name, _name = nil) return [] if table_name.blank? column_definitions(table_name).map do |ci| sqlserver_options = ci.except(:name, :default_value, :type, :null).merge(database_year: database_year) - SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options + cast_type = lookup_cast_type(ci[:type]) + new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options end end + def new_column(name, default, cast_type, sql_type = nil, null = true, sqlserver_options={}) + SQLServerColumn.new name, default, cast_type, sql_type, null, sqlserver_options + end + # like postgres, sqlserver requires the ORDER BY columns in the select list for distinct queries, and # requires that the ORDER BY include the distinct column. Unfortunately, sqlserver does not support # DISTINCT ON () like Posgres, or FIRST_VALUE() like Oracle (at least before SQL Server 2012). Because @@ -417,6 +423,7 @@ def identity_column(table_name) def create_table_definition(name, temporary, options, as = nil) TableDefinition.new native_database_types, name, temporary, options, as end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6790eac9f..92665338f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,6 +1,6 @@ require 'base64' require 'active_record' -require 'arel/arel_sqlserver' +require 'arel_sqlserver' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/explain' @@ -42,10 +42,6 @@ class SQLServerAdapter < AbstractAdapter self.enable_default_unicode_types = true - class BindSubstitution < Arel::Visitors::SQLServer # :nodoc: - include Arel::Visitors::BindVisitor - end - def initialize(connection, logger, pool, config) super(connection, logger, pool) # AbstractAdapter Responsibility diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index dc5e00e62..77ffa3b8c 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -1,9 +1,10 @@ module ActiveRecord module ConnectionAdapters class SQLServerColumn < Column - def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {}) + + def initialize(name, default, cast_type, sql_type = nil, null = true, sqlserver_options = {}) @sqlserver_options = sqlserver_options.symbolize_keys - super(name, default, sql_type, null) + super(name, default, cast_type, sql_type, null) @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary] end @@ -52,22 +53,6 @@ def default_function @sqlserver_options[:default_function] end - def table_name - @sqlserver_options[:table_name] - end - - def table_klass - @table_klass ||= begin - table_name.classify.constantize - rescue StandardError, NameError, LoadError - nil - end - (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil - end - - def database_year - @sqlserver_options[:database_year] - end private @@ -93,24 +78,13 @@ def simplified_type(field_type) when /image/i then :binary when /bit/i then :boolean when /uniqueidentifier/i then :uuid - when /datetime/i then simplified_datetime + when /datetime/i then :datetime when /varchar\(max\)/ then :text when /timestamp/ then :binary else super end end - def simplified_datetime - if database_year >= 2008 - :datetime - elsif table_klass && table_klass.coerced_sqlserver_date_columns.include?(name) - :date - elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name) - :time - else - :datetime - end - end - end # class SQLServerColumn - end # module ConnectionAdapters -end # module ActiveRecord + end + end +end diff --git a/lib/arel/nodes_sqlserver.rb b/lib/arel/nodes_sqlserver.rb deleted file mode 100644 index b8ce99968..000000000 --- a/lib/arel/nodes_sqlserver.rb +++ /dev/null @@ -1,14 +0,0 @@ -module Arel - module Nodes - # Extending the Ordering class to be comparison friendly which allows us to call #uniq on a - # collection of them. See SelectManager#order for more details. - class Ordering < Arel::Nodes::Unary - def eql?(other) - # Arel::Nodes::Ascending or Arel::Nodes::Desecnding - other.is_a?(Arel::Nodes::Ordering) && - expr == other.expr - end - alias_method :==, :eql? - end - end -end diff --git a/lib/arel/select_manager_sqlserver.rb b/lib/arel/select_manager_sqlserver.rb deleted file mode 100644 index d72c88bde..000000000 --- a/lib/arel/select_manager_sqlserver.rb +++ /dev/null @@ -1,62 +0,0 @@ -module Arel - class SelectManager < Arel::TreeManager - AR_CA_SQLSA_NAME = 'ActiveRecord::ConnectionAdapters::SQLServerAdapter'.freeze - - # Getting real Ordering objects is very important for us. We need to be able to call #uniq on - # a colleciton of them reliably as well as using their true object attributes to mutate them - # to grouping objects for the inner sql during a select statment with an offset/rownumber. So this - # is here till ActiveRecord & ARel does this for us instead of using SqlLiteral objects. - alias_method :order_without_sqlserver, :order - def order(*expr) - return order_without_sqlserver(*expr) unless engine_activerecord_sqlserver_adapter? - @ast.orders.concat(expr.map do |x| - case x - when Arel::Attributes::Attribute - table = Arel::Table.new(x.relation.table_alias || x.relation.name) - e = table[x.name] - Arel::Nodes::Ascending.new e - when Arel::Nodes::Ordering - x - when String - x.split(',').map do |s| - s = x if x.strip =~ /\A\b\w+\b\(.*,.*\)(\s+(ASC|DESC))?\Z/i # Allow functions with comma(s) to pass thru. - s.strip! - d = s =~ /(ASC|DESC)\Z/i ? Regexp.last_match[1].upcase : nil - e = d.nil? ? s : s.mb_chars[0...-d.length].strip - e = Arel.sql(e) - d && d == 'DESC' ? Arel::Nodes::Descending.new(e) : Arel::Nodes::Ascending.new(e) - end - else - e = Arel.sql(x.to_s) - Arel::Nodes::Ascending.new e - end - end.flatten) - self - end - - # A friendly over ride that allows us to put a special lock object that can have a default or pass - # custom string hints down. See the visit_Arel_Nodes_LockWithSQLServer delegation method. - alias_method :lock_without_sqlserver, :lock - def lock(locking = true) - if engine_activerecord_sqlserver_adapter? - case locking - when true - locking = Arel.sql('WITH(HOLDLOCK, ROWLOCK)') - when Arel::Nodes::SqlLiteral - when String - locking = Arel.sql locking - end - @ast.lock = Arel::Nodes::Lock.new(locking) - self - else - lock_without_sqlserver(locking) - end - end - - private - - def engine_activerecord_sqlserver_adapter? - @engine.connection && @engine.connection.class.name == AR_CA_SQLSA_NAME - end - end -end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index d230684e7..9c2b4d251 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,450 +1,87 @@ module Arel module Visitors class SQLServer < Arel::Visitors::ToSql + + OFFSET = " OFFSET " + ROWS = " ROWS" + FETCH = " FETCH NEXT " + ROWS_ONLY = " ROWS ONLY" + + private # SQLServer ToSql/Visitor (Overides) - def visit_Arel_Nodes_SelectStatement(o, a) - if complex_count_sql?(o) - visit_Arel_Nodes_SelectStatementForComplexCount(o, a) - elsif distinct_non_present_orders?(o, a) - visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) - elsif o.offset - visit_Arel_Nodes_SelectStatementWithOffset(o, a) - else - visit_Arel_Nodes_SelectStatementWithOutOffset(o, a) - end - end - def visit_Arel_Nodes_UpdateStatement(o, a) - if o.orders.any? && o.limit.nil? - o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) - end - super + def visit_Arel_Nodes_BindParam o, collector + collector.add_bind(o) { |i| "@#{i}" } end - def visit_Arel_Nodes_Offset(o, a) - "WHERE [__rnt].[__rn] > (#{visit o.expr, a})" + def visit_Arel_Nodes_Offset o, collector + collector << OFFSET + visit o.expr, collector + collector << ROWS end - def visit_Arel_Nodes_Limit(o, a) - "TOP (#{visit o.expr, a})" + def visit_Arel_Nodes_Limit o, collector + collector << FETCH + visit o.expr, collector + collector << ROWS_ONLY end - def visit_Arel_Nodes_Lock(o, a) - visit o.expr, a - end - - def visit_Arel_Nodes_Ordering(o, a) - if o.respond_to?(:direction) - "#{visit o.expr, a} #{o.ascending? ? 'ASC' : 'DESC'}" - else - visit o.expr, a + def visit_Arel_Nodes_SelectStatement o, collector + if o.with + collector = visit o.with, collector + collector << SPACE end - end - - def visit_Arel_Nodes_Bin(o, a) - "#{visit o.expr, a} #{@connection.cs_equality_operator}" + collector = o.cores.inject(collector) { |c,x| + visit_Arel_Nodes_SelectCore(x, c) + } + collector = visit_Orders_And_Let_Fetch_Happen o, collector + collector = visit_Make_Fetch_Happen o, collector + collector = visit o.lock, collector if o.lock + collector end # SQLServer ToSql/Visitor (Additions) - # This constructs a query using DENSE_RANK() and ROW_NUMBER() to allow - # ordering a DISTINCT set of data by columns in another table that are - # not part of what we actually want to be DISTINCT. Without this, it is - # possible for the DISTINCT qualifier combined with TOP to return fewer - # rows than were requested. - def visit_Arel_Nodes_SelectStatementDistinctNonPresentOrders(o, a) - core = o.cores.first - projections = core.projections - groups = core.groups - orders = o.orders.uniq - - select_frags = projections.map do |x| - frag = projection_to_sql_remove_distinct(x, core, a) - # Remove the table specifier - frag.gsub!(/^[^\.]*\./, '') - # If there is an alias, remove everything but - frag.gsub(/^.*\sAS\s+/i, '') - end - - if o.offset - select_frags << 'ROW_NUMBER() OVER (ORDER BY __order) AS __offset' - else - select_frags << '__order' + def visit_Orders_And_Let_Fetch_Happen o, collector + if (o.limit || o.offset) && o.orders.empty? + table = table_From_Statement o + column = table.primary_key || table.columns.first + o.orders = [column.asc] end - - projection_list = projections.map { |x| projection_to_sql_remove_distinct(x, core, a) }.join(', ') - - sql = [ - ('SELECT'), - (visit(core.set_quantifier, a) if core.set_quantifier && !o.offset), - (visit(o.limit, a) if o.limit && !o.offset), - (select_frags.join(', ')), - ('FROM ('), - ('SELECT'), - ( - [ - (projection_list), - (', DENSE_RANK() OVER ('), - ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" unless orders.empty?), - (') AS __order'), - (', ROW_NUMBER() OVER ('), - ("PARTITION BY #{projection_list}" if !orders.empty?), - (" ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" unless orders.empty?), - (') AS __joined_row_num') - ].join('') - ), - (source_with_lock_for_select_statement(o, a)), - ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{groups.map { |x| visit(x, a) }.join ', ' }" unless groups.empty?), - (visit(core.having, a) if core.having), - (') AS __sq'), - ('WHERE __joined_row_num = 1'), - ('ORDER BY __order' unless o.offset) - ].compact.join(' ') - - if o.offset - sql = [ - ('SELECT'), - (visit(core.set_quantifier, a) if core.set_quantifier), - (visit(o.limit, a) if o.limit), - ('*'), - ('FROM (' + sql + ') AS __osq'), - ("WHERE __offset > #{visit(o.offset.expr, a)}"), - ('ORDER BY __offset') - ].join(' ') + unless o.orders.empty? + collector << SPACE + collector << ORDER_BY + len = o.orders.length - 1 + o.orders.each_with_index { |x, i| + collector = visit(x, collector) + collector << COMMA unless len == i + } end - - sql + collector end - def visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, windowed = false) - find_and_fix_uncorrelated_joins_in_select_statement(o) - core = o.cores.first - projections = core.projections - groups = core.groups - orders = o.orders.uniq - if windowed - projections = function_select_statement?(o) ? projections : projections.map { |x| projection_without_expression(x, a) } - groups = projections.map { |x| projection_without_expression(x, a) } if windowed_single_distinct_select_statement?(o) && groups.empty? - groups += orders.map { |x| Arel.sql(x.expr) } if windowed_single_distinct_select_statement?(o) - elsif eager_limiting_select_statement?(o, a) - projections = projections.map { |x| projection_without_expression(x, a) } - groups = projections.map { |x| projection_without_expression(x, a) } - orders = orders.map do |x| - expr = Arel.sql projection_without_expression(x.expr, a) - x.descending? ? Arel::Nodes::Max.new([expr]) : Arel::Nodes::Min.new([expr]) - end - elsif top_one_everything_for_through_join?(o, a) - projections = projections.map { |x| projection_without_expression(x, a) } - end - [ - ('SELECT' unless windowed), - (visit(core.set_quantifier, a) if core.set_quantifier && !windowed), - (visit(o.limit, a) if o.limit && !windowed), - (projections.map do |x| - v = visit(x, a) - v == '1' ? '1 AS [__wrp]' : v - end.join(', ')), - (source_with_lock_for_select_statement(o, a)), - ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{groups.map { |x| visit(x, a) }.join ', ' }" unless groups.empty?), - (visit(core.having, a) if core.having), - ("ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}" if !orders.empty? && !windowed) - ].compact.join ' ' - end - - def visit_Arel_Nodes_SelectStatementWithOffset(o, a) - core = o.cores.first - o.limit ||= Arel::Nodes::Limit.new(9_223_372_036_854_775_807) - orders = rowtable_orders(o) - [ - 'SELECT', - (visit(o.limit, a) if o.limit && !windowed_single_distinct_select_statement?(o)), - (rowtable_projections(o, a).map { |x| visit(x, a) }.join(', ')), - 'FROM (', - "SELECT #{core.set_quantifier ? 'DISTINCT DENSE_RANK()' : 'ROW_NUMBER()'} OVER (ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}) AS [__rn],", - visit_Arel_Nodes_SelectStatementWithOutOffset(o, a, true), - ') AS [__rnt]', - (visit(o.offset, a) if o.offset), - 'ORDER BY [__rnt].[__rn] ASC' - ].compact.join ' ' - end - - def visit_Arel_Nodes_SelectStatementForComplexCount(o, a) - core = o.cores.first - o.limit.expr = Arel.sql("#{o.limit.expr} + #{o.offset ? o.offset.expr : 0}") if o.limit - orders = rowtable_orders(o) - [ - 'SELECT COUNT([count]) AS [count_id]', - 'FROM (', - 'SELECT', - (visit(o.limit, a) if o.limit), - "ROW_NUMBER() OVER (ORDER BY #{orders.map { |x| visit(x, a) }.join(', ')}) AS [__rn],", - '1 AS [count]', - (source_with_lock_for_select_statement(o, a)), - ("WHERE #{core.wheres.map { |x| visit(x, a) }.join ' AND ' }" unless core.wheres.empty?), - ("GROUP BY #{core.groups.map { |x| visit(x, a) }.join ', ' }" unless core.groups.empty?), - (visit(core.having, a) if core.having), - ("ORDER BY #{o.orders.map { |x| visit(x, a) }.join(', ')}" unless o.orders.empty?), - ') AS [__rnt]', - (visit(o.offset, a) if o.offset) - ].compact.join ' ' + def visit_Make_Fetch_Happen o, collector + o.offset = Nodes::Offset.new(0) if o.limit && !o.offset + collector = visit o.offset, collector if o.offset + collector = visit o.limit, collector if o.limit + collector end # SQLServer Helpers - def projection_to_sql_remove_distinct(x, core, a) - frag = Arel.sql(visit(x, a)) - # In Rails 4.0.0, DISTINCT was in a projection, whereas with 4.0.1 - # it is now stored in the set_quantifier. This moves it to the correct - # place so the code works on both 4.0.0 and 4.0.1. - if frag =~ /^\s*DISTINCT\s+/i - core.set_quantifier = Arel::Nodes::Distinct.new - frag.gsub!(/\s*DISTINCT\s+/, '') - end - frag - end - - def source_with_lock_for_select_statement(o, a) - core = o.cores.first - source = "FROM #{visit(core.source, a).strip}" if core.source - if source && o.lock - lock = visit o.lock, a - index = source.match(/FROM [\w\[\]\.]+/)[0].mb_chars.length - source.insert index, " #{lock}" - else - source - end - end - - def table_from_select_statement(o) + def table_From_Statement o core = o.cores.first - # TODO: [ARel 2.2] Use #from/#source vs. #froms - # if Arel::Table === core.from - # core.from - # elsif Arel::Nodes::SqlLiteral === core.from - # Arel::Table.new(core.from, @engine) - # elsif Arel::Nodes::JoinSource === core.source - # Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left - # end - table_finder = lambda do |x| - case x - when Arel::Table - x - when Arel::Nodes::SqlLiteral - Arel::Table.new(x, @engine) - when Arel::Nodes::Join - table_finder.call(x.left) - end + if Arel::Table === core.from + core.from + 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 end - table_finder.call(core.froms) end - def single_distinct_select_statement?(o) - projections = o.cores.first.projections - p1 = projections.first - projections.size == 1 && - ((p1.respond_to?(:distinct) && p1.distinct) || - p1.respond_to?(:include?) && p1.include?('DISTINCT')) - end - - # Determine if the SELECT statement is asking for DISTINCT results, - # but is using columns not part of the SELECT list in the ORDER BY. - # This is necessary because SQL Server requires all ORDER BY entries - # be in the SELECT list with DISTINCT. However, these ordering columns - # can cause duplicate rows, which affect when using a limit. - def distinct_non_present_orders?(o, a) - projections = o.cores.first.projections - - sq = o.cores.first.set_quantifier - p1 = projections.first - - found_distinct = sq && sq.class.to_s =~ /Distinct/ - if (p1.respond_to?(:distinct) && p1.distinct) || (p1.respond_to?(:include?) && p1.include?('DISTINCT')) - found_distinct = true - end - - return false if !found_distinct || o.orders.uniq.empty? - - tables_all_columns = [] - expressions = projections.map do |p| - visit(p, a).split(',').map do |x| - x.strip! - # Rails 4.0.0 included DISTINCT in the first projection - x.gsub!(/\s*DISTINCT\s+/, '') - # Aliased column names - x.gsub!(/\s+AS\s+\w+/i, '') - # Identifier quoting - x.gsub!(/\[|\]/, '') - star_match = /^(\w+)\.\*$/.match(x) - tables_all_columns << star_match[1] if star_match - x.strip.downcase - end.join(', ') - end - - # Make sure each order by is in the select list, otherwise there needs - # to be a subquery with row_numbe() - o.orders.uniq.each do |order| - order = visit(order, a) - order.strip! - - order.gsub!(/\s+(asc|desc)/i, '') - # Identifier quoting - order.gsub!(/\[|\]/, '') - - order.strip! - order.downcase! - - # If we selected all columns from a table, the order is ok - table_match = /^(\w+)\.\w+$/.match(order) - next if table_match && tables_all_columns.include?(table_match[1]) - - next if expressions.include?(order) - - return true - end - - # We didn't find anything in the order by no being selected - false - end - - def windowed_single_distinct_select_statement?(o) - o.limit && - o.offset && - single_distinct_select_statement?(o) - end - - def single_distinct_select_everything_statement?(o, a) - single_distinct_select_statement?(o) && - visit(o.cores.first.projections.first, a).ends_with?('.*') - end - - def top_one_everything_for_through_join?(o, a) - single_distinct_select_everything_statement?(o, a) && - (o.limit && !o.offset) && - join_in_select_statement?(o) - end - - def all_projections_aliased_in_select_statement?(o, a) - projections = o.cores.first.projections - projections.all? do |x| - visit(x, a).split(',').all? { |y| y.include?(' AS ') } - end - end - - def function_select_statement?(o) - core = o.cores.first - core.projections.any? { |x| Arel::Nodes::Function === x } - end - - def eager_limiting_select_statement?(o, a) - core = o.cores.first - single_distinct_select_statement?(o) && - (o.limit && !o.offset) && - core.groups.empty? && - !single_distinct_select_everything_statement?(o, a) - end - - def join_in_select_statement?(o) - core = o.cores.first - core.source.right.any? { |x| Arel::Nodes::Join === x } - end - - def complex_count_sql?(o) - core = o.cores.first - core.projections.size == 1 && - Arel::Nodes::Count === core.projections.first && - o.limit && - !join_in_select_statement?(o) - end - - def select_primary_key_sql?(o) - core = o.cores.first - return false if core.projections.size != 1 - p = core.projections.first - t = table_from_select_statement(o) - Arel::Attributes::Attribute === p && t.primary_key && t.primary_key.name == p.name - end - - def find_and_fix_uncorrelated_joins_in_select_statement(o) - core = o.cores.first - # TODO: [ARel 2.2] Use #from/#source vs. #froms - # return if !join_in_select_statement?(o) || core.source.right.size != 2 - # j1 = core.source.right.first - # j2 = core.source.right.second - # return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::StringJoin === j2 - # j1_tn = j1.left.name - # j2_tn = j2.left.match(/JOIN \[(.*)\].*ON/).try(:[],1) - # return unless j1_tn == j2_tn - # crltd_tn = "#{j1_tn}_crltd" - # j1.left.table_alias = crltd_tn - # j1.right.expr.left.relation.table_alias = crltd_tn - return if !join_in_select_statement?(o) || !(Arel::Nodes::StringJoin === core.froms) - j1 = core.froms.left - j2 = core.froms.right - return unless Arel::Nodes::OuterJoin === j1 && Arel::Nodes::SqlLiteral === j2 && j2.include?('JOIN ') - j1_tn = j1.right.name - j2_tn = j2.match(/JOIN \[(.*)\].*ON/).try(:[], 1) - return unless j1_tn == j2_tn - on_index = j2.index(' ON ') - j2.insert on_index, " AS [#{j2_tn}_crltd]" - j2.sub! "[#{j2_tn}].", "[#{j2_tn}_crltd]." - end - - def rowtable_projections(o, a) - core = o.cores.first - if windowed_single_distinct_select_statement?(o) && core.groups.blank? - tn = table_from_select_statement(o).name - core.projections.map do |x| - x.dup.tap do |p| - p.sub! 'DISTINCT', '' - p.insert 0, visit(o.limit, a) if o.limit - p.gsub!(/\[?#{tn}\]?\./, '[__rnt].') - p.strip! - end - end - elsif single_distinct_select_statement?(o) - tn = table_from_select_statement(o).name - core.projections.map do |x| - x.dup.tap do |p| - p.sub! 'DISTINCT', "DISTINCT #{visit(o.limit, a)}".strip if o.limit - p.gsub!(/\[?#{tn}\]?\./, '[__rnt].') - p.strip! - end - end - elsif join_in_select_statement?(o) && all_projections_aliased_in_select_statement?(o, a) - core.projections.map do |x| - Arel.sql visit(x, a).split(',').map { |y| y.split(' AS ').last.strip }.join(', ') - end - elsif select_primary_key_sql?(o) - [Arel.sql("[__rnt].#{quote_column_name(core.projections.first.name)}")] - else - [Arel.sql('[__rnt].*')] - end - end - - def rowtable_orders(o) - if !o.orders.empty? - o.orders - else - t = table_from_select_statement(o) - c = t.primary_key || t.columns.first - [c.asc] - end.uniq - end - - # TODO: We use this for grouping too, maybe make Grouping objects vs SqlLiteral. - def projection_without_expression(projection, a) - Arel.sql(visit(projection, a).split(',').map do |x| - x.strip! - x.sub!(/^(COUNT|SUM|MAX|MIN|AVG)\s*(\((.*)\))?/, '\3') - x.sub!(/^DISTINCT\s*/, '') - x.sub!(/TOP\s*\(\d+\)\s*/i, '') - x.strip - end.join(', ')) - end end end end diff --git a/lib/arel/arel_sqlserver.rb b/lib/arel_sqlserver.rb similarity index 54% rename from lib/arel/arel_sqlserver.rb rename to lib/arel_sqlserver.rb index 30bb311d5..3fa9d949b 100644 --- a/lib/arel/arel_sqlserver.rb +++ b/lib/arel_sqlserver.rb @@ -1,5 +1,3 @@ require 'arel' -require 'arel/select_manager_sqlserver' -require 'arel/nodes_sqlserver' -require 'arel/visitors/sqlserver' require 'arel/visitors/bind_visitor' +require 'arel/visitors/sqlserver' diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index 9f91be94b..f0a490c0b 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -4,15 +4,13 @@ class BasicsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_column_names_are_escaped, - :test_respect_internal_encoding] - # test_respect_internal_encoding is not run for PostgreSQL at the rails level and the same should happen for SQL Server - # Until that patch is made to rails we are preventing this test from running in this gem. - - include ARTest::Sqlserver::CoercedTest - should 'operate as other database adapters when finding primary keys, standards are postgresql adapter' do + COERCED_TESTS = [ + :test_column_names_are_escaped + ] + + it 'operates as other database adapters when finding primary keys, standard is postgresql adapter' do assert_nil Post.where(id:'').first assert_nil Post.where(id:nil).first assert_raise(ActiveRecord::RecordNotFound) { Post.find('') } @@ -24,4 +22,3 @@ def test_coerced_column_names_are_escaped end end - diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 7aa697b75..a9bc76119 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -21,13 +21,6 @@ class ColumnTestSqlserver < ActiveRecord::TestCase assert_equal :float, TableWithRealColumn.columns_hash["real_number"].type end - should 'know its #table_name and #table_klass' do - Topic.columns.each do |column| - assert_equal 'topics', column.table_name, "This column #{column.inspect} did not know it's #table_name" - assert_equal Topic, column.table_klass, "This column #{column.inspect} did not know it's #table_klass" - end - end - should 'return correct null, limit, and default for Topic' do tch = Topic.columns_hash assert_equal false, tch['id'].null From dfa8c58296d663aa1f0f16e5b31bc29b8f7d7f71 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 12:53:09 -0500 Subject: [PATCH 0200/1412] Add minitest-focus dev dep. --- activerecord-sqlserver-adapter.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index dbdc227da..d996640e2 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,6 +18,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activerecord', '~> 4.2.0' spec.add_development_dependency 'bundler' spec.add_development_dependency 'guard-minitest' + spec.add_development_dependency 'minitest-focus' spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' spec.add_development_dependency 'nokogiri' From 8d07d1b772abfc04b47cd6ac24215f617ad1097a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 13:20:54 -0500 Subject: [PATCH 0201/1412] New simple 2012 schema. The idea is that we want a single [datatypes] table to put every type that SQL Sever 2012 understands. This will have a signle test model and we use this models columns to test our new ActiveRecord::Type objects. This current 2012 list might be incomplete. If so, we will add a test. Any newer datatypes for 2014 will be added to a unique 2014.sql file. * Rename test/models_sqlserver to test/models/sqlserver. No files moved. * Deprecate every single model/#{name}.rb. We will only be moving what we need. * New models in test/models/sqlserver MUST use a `SS` prefix for the model name!!! --- test/cases/adapter_test_sqlserver.rb | 18 +++++++++--------- test/cases/attribute_methods_test_sqlserver.rb | 2 +- test/cases/bind_parameter_test_sqlserver.rb | 2 +- test/cases/connection_test_sqlserver.rb | 2 +- test/cases/finder_test_sqlserver.rb | 2 +- test/cases/migration_test_sqlserver.rb | 2 +- test/cases/named_scoping_test_sqlserver.rb | 2 +- test/cases/offset_and_limit_test_sqlserver.rb | 2 +- test/cases/persistence_test_sqlserver.rb | 2 +- .../pessimistic_locking_test_sqlserver.rb | 2 +- test/cases/predicate_builder_test_sqlserver.rb | 2 +- test/cases/schema_test_sqlserver.rb | 4 ++-- test/cases/specific_schema_test_sqlserver.rb | 18 +++++++++--------- .../customers_view.rb | 0 .../fk_test_has_fk.rb | 0 .../fk_test_has_pk.rb | 0 .../{models_sqlserver => models}/float_data.rb | 0 .../{models_sqlserver => models}/no_pk_data.rb | 0 .../numeric_data.rb | 0 test/{models_sqlserver => models}/person.rb | 0 test/{models_sqlserver => models}/post.rb | 0 .../sql_server_chronic.rb | 0 .../sql_server_dollar_table_name.rb | 0 .../sql_server_edge_schema.rb | 0 .../sql_server_natural_pk_data.rb | 0 .../sql_server_natural_pk_data_schema.rb | 0 .../sql_server_natural_pk_int_data.rb | 0 .../sql_server_order_row_number.rb | 0 .../sql_server_quoted_table.rb | 0 .../sql_server_quoted_view_1.rb | 0 .../sql_server_quoted_view_2.rb | 0 .../sql_server_string.rb | 0 .../sql_server_tinyint_pk.rb | 0 .../sql_server_unicode.rb | 0 .../string_default.rb | 0 .../string_defaults_big_view.rb | 0 .../string_defaults_view.rb | 0 .../table_with_real_column.rb | 0 test/{models_sqlserver => models}/topic.rb | 0 .../upper_test_default.rb | 0 .../upper_test_lowered.rb | 0 test/schema/sqlserver_specific_schema.rb | 2 ++ test/support/load_schema_sqlserver.rb | 8 ++++++-- 43 files changed, 38 insertions(+), 32 deletions(-) rename test/{models_sqlserver => models}/customers_view.rb (100%) rename test/{models_sqlserver => models}/fk_test_has_fk.rb (100%) rename test/{models_sqlserver => models}/fk_test_has_pk.rb (100%) rename test/{models_sqlserver => models}/float_data.rb (100%) rename test/{models_sqlserver => models}/no_pk_data.rb (100%) rename test/{models_sqlserver => models}/numeric_data.rb (100%) rename test/{models_sqlserver => models}/person.rb (100%) rename test/{models_sqlserver => models}/post.rb (100%) rename test/{models_sqlserver => models}/sql_server_chronic.rb (100%) rename test/{models_sqlserver => models}/sql_server_dollar_table_name.rb (100%) rename test/{models_sqlserver => models}/sql_server_edge_schema.rb (100%) rename test/{models_sqlserver => models}/sql_server_natural_pk_data.rb (100%) rename test/{models_sqlserver => models}/sql_server_natural_pk_data_schema.rb (100%) rename test/{models_sqlserver => models}/sql_server_natural_pk_int_data.rb (100%) rename test/{models_sqlserver => models}/sql_server_order_row_number.rb (100%) rename test/{models_sqlserver => models}/sql_server_quoted_table.rb (100%) rename test/{models_sqlserver => models}/sql_server_quoted_view_1.rb (100%) rename test/{models_sqlserver => models}/sql_server_quoted_view_2.rb (100%) rename test/{models_sqlserver => models}/sql_server_string.rb (100%) rename test/{models_sqlserver => models}/sql_server_tinyint_pk.rb (100%) rename test/{models_sqlserver => models}/sql_server_unicode.rb (100%) rename test/{models_sqlserver => models}/string_default.rb (100%) rename test/{models_sqlserver => models}/string_defaults_big_view.rb (100%) rename test/{models_sqlserver => models}/string_defaults_view.rb (100%) rename test/{models_sqlserver => models}/table_with_real_column.rb (100%) rename test/{models_sqlserver => models}/topic.rb (100%) rename test/{models_sqlserver => models}/upper_test_default.rb (100%) rename test/{models_sqlserver => models}/upper_test_lowered.rb (100%) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 2e5521603..dbfa55254 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -5,15 +5,15 @@ require 'models/subscriber' require 'models/minimalistic' require 'models/post' -require 'models_sqlserver/fk_test_has_pk' -require 'models_sqlserver/fk_test_has_fk' -require 'models_sqlserver/customers_view' -require 'models_sqlserver/sql_server_chronic' -require 'models_sqlserver/string_defaults_big_view' -require 'models_sqlserver/string_defaults_view' -require 'models_sqlserver/topic' -require 'models_sqlserver/upper_test_default' -require 'models_sqlserver/upper_test_lowered' +require 'models/sqlserver/fk_test_has_pk' +require 'models/sqlserver/fk_test_has_fk' +require 'models/sqlserver/customers_view' +require 'models/sqlserver/sql_server_chronic' +require 'models/sqlserver/string_defaults_big_view' +require 'models/sqlserver/string_defaults_view' +require 'models/sqlserver/topic' +require 'models/sqlserver/upper_test_default' +require 'models/sqlserver/upper_test_lowered' class AdapterTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index 73c94a423..f82eaaa9a 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/developer' require 'models/topic' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' class AttributeMethodsTestSqlserver < ActiveRecord::TestCase end diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/bind_parameter_test_sqlserver.rb index 9c89eea54..dad6c4b61 100644 --- a/test/cases/bind_parameter_test_sqlserver.rb +++ b/test/cases/bind_parameter_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/topic' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' require 'cases/bind_parameter_test' # We don't coerce here because these tests are located inside of an if block diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 0d9da8b0c..fe7390770 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/reply' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' class ConnectionTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index bbc88f30a..2a143a3b7 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'models/author' require 'models/post' require 'models/categorization' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' # require 'cases/finder_test.rb' class FinderTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 6225af84a..6b8123501 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/person' -require 'models_sqlserver/person' +require 'models/sqlserver/person' class MigrationTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/named_scoping_test_sqlserver.rb b/test/cases/named_scoping_test_sqlserver.rb index 6cfa25b35..3aee2d6fa 100644 --- a/test/cases/named_scoping_test_sqlserver.rb +++ b/test/cases/named_scoping_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/post' -require 'models_sqlserver/post' +require 'models/sqlserver/post' # This file is really just to ensure we fix the order by on the # :ranked_by_comments scope of Post diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index 32ed7656c..be3d9ee37 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -8,7 +8,7 @@ require 'models/post' require 'models/comment' require 'models/categorization' -require 'models_sqlserver/sql_server_order_row_number' +require 'models/sqlserver/sql_server_order_row_number' class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index 68c4fb39a..a572a611c 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -13,7 +13,7 @@ require 'models/parrot' require 'models/minivan' require 'models/person' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' require 'rexml/document' class PersistenceTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 192cd0fde..980b2bba7 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/person' require 'models/reader' -require 'models_sqlserver/person' +require 'models/sqlserver/person' class PessimisticLockingTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/predicate_builder_test_sqlserver.rb index b352091ad..81c17c506 100644 --- a/test/cases/predicate_builder_test_sqlserver.rb +++ b/test/cases/predicate_builder_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/topic' -require 'models_sqlserver/topic' +require 'models/sqlserver/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index bc4d82150..7be3c1ee9 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' -require 'models_sqlserver/sql_server_natural_pk_data' -require 'models_sqlserver/sql_server_natural_pk_data_schema' +require 'models/sqlserver/sql_server_natural_pk_data' +require 'models/sqlserver/sql_server_natural_pk_data_schema' class SchemaTestSqlserver < ActiveRecord::TestCase diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index cef0dc7b4..0d41a5ab8 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,13 +1,13 @@ require 'cases/helper_sqlserver' -require 'models_sqlserver/no_pk_data' -require 'models_sqlserver/sql_server_dollar_table_name' -require 'models_sqlserver/sql_server_edge_schema' -require 'models_sqlserver/sql_server_natural_pk_int_data' -require 'models_sqlserver/sql_server_quoted_table' -require 'models_sqlserver/sql_server_quoted_view_1' -require 'models_sqlserver/sql_server_quoted_view_2' -require 'models_sqlserver/sql_server_tinyint_pk' -require 'models_sqlserver/string_default' +require 'models/sqlserver/no_pk_data' +require 'models/sqlserver/sql_server_dollar_table_name' +require 'models/sqlserver/sql_server_edge_schema' +require 'models/sqlserver/sql_server_natural_pk_int_data' +require 'models/sqlserver/sql_server_quoted_table' +require 'models/sqlserver/sql_server_quoted_view_1' +require 'models/sqlserver/sql_server_quoted_view_2' +require 'models/sqlserver/sql_server_tinyint_pk' +require 'models/sqlserver/string_default' class SpecificSchemaTestSqlserver < ActiveRecord::TestCase should 'be able to complex count tables with no primary key' do diff --git a/test/models_sqlserver/customers_view.rb b/test/models/customers_view.rb similarity index 100% rename from test/models_sqlserver/customers_view.rb rename to test/models/customers_view.rb diff --git a/test/models_sqlserver/fk_test_has_fk.rb b/test/models/fk_test_has_fk.rb similarity index 100% rename from test/models_sqlserver/fk_test_has_fk.rb rename to test/models/fk_test_has_fk.rb diff --git a/test/models_sqlserver/fk_test_has_pk.rb b/test/models/fk_test_has_pk.rb similarity index 100% rename from test/models_sqlserver/fk_test_has_pk.rb rename to test/models/fk_test_has_pk.rb diff --git a/test/models_sqlserver/float_data.rb b/test/models/float_data.rb similarity index 100% rename from test/models_sqlserver/float_data.rb rename to test/models/float_data.rb diff --git a/test/models_sqlserver/no_pk_data.rb b/test/models/no_pk_data.rb similarity index 100% rename from test/models_sqlserver/no_pk_data.rb rename to test/models/no_pk_data.rb diff --git a/test/models_sqlserver/numeric_data.rb b/test/models/numeric_data.rb similarity index 100% rename from test/models_sqlserver/numeric_data.rb rename to test/models/numeric_data.rb diff --git a/test/models_sqlserver/person.rb b/test/models/person.rb similarity index 100% rename from test/models_sqlserver/person.rb rename to test/models/person.rb diff --git a/test/models_sqlserver/post.rb b/test/models/post.rb similarity index 100% rename from test/models_sqlserver/post.rb rename to test/models/post.rb diff --git a/test/models_sqlserver/sql_server_chronic.rb b/test/models/sql_server_chronic.rb similarity index 100% rename from test/models_sqlserver/sql_server_chronic.rb rename to test/models/sql_server_chronic.rb diff --git a/test/models_sqlserver/sql_server_dollar_table_name.rb b/test/models/sql_server_dollar_table_name.rb similarity index 100% rename from test/models_sqlserver/sql_server_dollar_table_name.rb rename to test/models/sql_server_dollar_table_name.rb diff --git a/test/models_sqlserver/sql_server_edge_schema.rb b/test/models/sql_server_edge_schema.rb similarity index 100% rename from test/models_sqlserver/sql_server_edge_schema.rb rename to test/models/sql_server_edge_schema.rb diff --git a/test/models_sqlserver/sql_server_natural_pk_data.rb b/test/models/sql_server_natural_pk_data.rb similarity index 100% rename from test/models_sqlserver/sql_server_natural_pk_data.rb rename to test/models/sql_server_natural_pk_data.rb diff --git a/test/models_sqlserver/sql_server_natural_pk_data_schema.rb b/test/models/sql_server_natural_pk_data_schema.rb similarity index 100% rename from test/models_sqlserver/sql_server_natural_pk_data_schema.rb rename to test/models/sql_server_natural_pk_data_schema.rb diff --git a/test/models_sqlserver/sql_server_natural_pk_int_data.rb b/test/models/sql_server_natural_pk_int_data.rb similarity index 100% rename from test/models_sqlserver/sql_server_natural_pk_int_data.rb rename to test/models/sql_server_natural_pk_int_data.rb diff --git a/test/models_sqlserver/sql_server_order_row_number.rb b/test/models/sql_server_order_row_number.rb similarity index 100% rename from test/models_sqlserver/sql_server_order_row_number.rb rename to test/models/sql_server_order_row_number.rb diff --git a/test/models_sqlserver/sql_server_quoted_table.rb b/test/models/sql_server_quoted_table.rb similarity index 100% rename from test/models_sqlserver/sql_server_quoted_table.rb rename to test/models/sql_server_quoted_table.rb diff --git a/test/models_sqlserver/sql_server_quoted_view_1.rb b/test/models/sql_server_quoted_view_1.rb similarity index 100% rename from test/models_sqlserver/sql_server_quoted_view_1.rb rename to test/models/sql_server_quoted_view_1.rb diff --git a/test/models_sqlserver/sql_server_quoted_view_2.rb b/test/models/sql_server_quoted_view_2.rb similarity index 100% rename from test/models_sqlserver/sql_server_quoted_view_2.rb rename to test/models/sql_server_quoted_view_2.rb diff --git a/test/models_sqlserver/sql_server_string.rb b/test/models/sql_server_string.rb similarity index 100% rename from test/models_sqlserver/sql_server_string.rb rename to test/models/sql_server_string.rb diff --git a/test/models_sqlserver/sql_server_tinyint_pk.rb b/test/models/sql_server_tinyint_pk.rb similarity index 100% rename from test/models_sqlserver/sql_server_tinyint_pk.rb rename to test/models/sql_server_tinyint_pk.rb diff --git a/test/models_sqlserver/sql_server_unicode.rb b/test/models/sql_server_unicode.rb similarity index 100% rename from test/models_sqlserver/sql_server_unicode.rb rename to test/models/sql_server_unicode.rb diff --git a/test/models_sqlserver/string_default.rb b/test/models/string_default.rb similarity index 100% rename from test/models_sqlserver/string_default.rb rename to test/models/string_default.rb diff --git a/test/models_sqlserver/string_defaults_big_view.rb b/test/models/string_defaults_big_view.rb similarity index 100% rename from test/models_sqlserver/string_defaults_big_view.rb rename to test/models/string_defaults_big_view.rb diff --git a/test/models_sqlserver/string_defaults_view.rb b/test/models/string_defaults_view.rb similarity index 100% rename from test/models_sqlserver/string_defaults_view.rb rename to test/models/string_defaults_view.rb diff --git a/test/models_sqlserver/table_with_real_column.rb b/test/models/table_with_real_column.rb similarity index 100% rename from test/models_sqlserver/table_with_real_column.rb rename to test/models/table_with_real_column.rb diff --git a/test/models_sqlserver/topic.rb b/test/models/topic.rb similarity index 100% rename from test/models_sqlserver/topic.rb rename to test/models/topic.rb diff --git a/test/models_sqlserver/upper_test_default.rb b/test/models/upper_test_default.rb similarity index 100% rename from test/models_sqlserver/upper_test_default.rb rename to test/models/upper_test_default.rb diff --git a/test/models_sqlserver/upper_test_lowered.rb b/test/models/upper_test_lowered.rb similarity index 100% rename from test/models_sqlserver/upper_test_lowered.rb rename to test/models/upper_test_lowered.rb diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 131001f90..a569d8476 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,5 +1,7 @@ ActiveRecord::Schema.define do + execute File.read(ARTest::Sqlserver.schema_datatypes_2012_file) + create_table :UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string t.column :COLUMN2, :integer diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index 776c69923..ef97df7c2 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -4,11 +4,15 @@ module Sqlserver extend self def schema_root - @schema_root ||= File.join ARTest::Sqlserver.test_root_sqlserver, 'schema' + File.join ARTest::Sqlserver.test_root_sqlserver, 'schema' end def schema_file - @schema_file ||= File.join schema_root, 'sqlserver_specific_schema.rb' + File.join schema_root, 'sqlserver_specific_schema.rb' + end + + def schema_datatypes_2012_file + File.join schema_root, 'datatypes', '2012.sql' end def load_schema From 729db2d248ce55c693e9de8ef6a8ecde91dd162e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 3 Jan 2015 14:52:36 -0500 Subject: [PATCH 0202/1412] Consistent with our NS. Use `SQLServer` vs `Sqlserver`. --- CHANGELOG.md | 6 +++--- Guardfile | 4 ++-- README.md | 2 +- Rakefile | 6 +++--- activerecord-sqlserver-adapter.gemspec | 2 +- .../sqlserver/core_ext/active_record.rb | 4 ++-- .../sqlserver/core_ext/explain.rb | 6 +++--- .../sqlserver/core_ext/odbc.rb | 6 +++--- .../sqlserver/core_ext/relation.rb | 4 ++-- .../sqlserver/database_limits.rb | 2 +- .../sqlserver/database_statements.rb | 8 ++++---- .../connection_adapters/sqlserver/errors.rb | 2 +- .../connection_adapters/sqlserver/quoting.rb | 6 +++--- .../sqlserver/schema_cache.rb | 4 ++-- .../sqlserver/schema_creation.rb | 2 +- .../sqlserver/schema_statements.rb | 14 ++++++------- .../connection_adapters/sqlserver/showplan.rb | 2 +- .../sqlserver/showplan/printer_table.rb | 2 +- .../sqlserver/showplan/printer_xml.rb | 2 +- .../sqlserver/table_definition.rb | 2 +- .../connection_adapters/sqlserver/utils.rb | 4 ++-- .../connection_adapters/sqlserver/version.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 20 +++++++++---------- test/cases/adapter_test_sqlserver.rb | 4 ++-- test/cases/associations_test_sqlserver.rb | 2 +- .../cases/attribute_methods_test_sqlserver.rb | 4 ++-- test/cases/base_test_sqlserver.rb | 2 +- test/cases/batches_test_sqlserver.rb | 4 ++-- .../belongs_to_associations_test_sqlserver.rb | 4 ++-- test/cases/calculations_test_sqlserver.rb | 4 ++-- .../connection_management_test_sqlserver.rb | 2 +- test/cases/connection_test_sqlserver.rb | 2 +- .../database_statements_test_sqlserver.rb | 2 +- test/cases/disconnected_test_sqlserver.rb | 4 ++-- test/cases/eager_test_sqlserver.rb | 4 ++-- .../cases/execute_procedure_test_sqlserver.rb | 2 +- test/cases/finder_test_sqlserver.rb | 4 ++-- ...ngs_to_many_associations_test_sqlserver.rb | 4 ++-- test/cases/inheritance_test_sqlserver.rb | 4 ++-- test/cases/migration_test_sqlserver.rb | 6 +++--- test/cases/offset_and_limit_test_sqlserver.rb | 2 +- test/cases/order_test_sqlserver.rb | 2 +- test/cases/persistence_test_sqlserver.rb | 4 ++-- .../pessimistic_locking_test_sqlserver.rb | 2 +- .../cases/predicate_builder_test_sqlserver.rb | 2 +- test/cases/query_cache_test_sqlserver.rb | 4 ++-- test/cases/relations_test_sqlserver.rb | 2 +- test/cases/resolver_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 4 ++-- test/cases/schema_test_sqlserver.rb | 2 +- test/cases/scratch_test_sqlserver.rb | 2 +- test/cases/showplan_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 2 +- test/cases/table_name_test_sqlserver.rb | 2 +- test/cases/transaction_test_sqlserver.rb | 4 ++-- test/cases/unicode_test_sqlserver.rb | 2 +- .../uniqueness_validation_test_sqlserver.rb | 4 ++-- test/cases/utils_test_sqlserver.rb | 6 +++--- test/cases/where_chain_test_sqlserver.rb | 2 +- test/schema/sqlserver_specific_schema.rb | 2 +- test/support/coerced_test_sqlserver.rb | 2 +- test/support/load_schema_sqlserver.rb | 6 +++--- test/support/paths_sqlserver.rb | 6 +++--- test/support/sql_counter_sqlserver.rb | 8 ++++---- 64 files changed, 123 insertions(+), 123 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 812efe948..b81b4efa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -106,7 +106,7 @@ * Remove our log_info_schema_queries config since we are not hooking properly into AR's 'SCHEMA' names. * Properly use 'SCHEMA' name arguement in DB statements to comply with ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS. * Make use of the new ConnectionAdapters::SchemaCache for our needs. -* New Sqlserver::Utils class for out helpers. Moved table name unquotes there. +* New SQLServer::Utils class for out helpers. Moved table name unquotes there. ## v3.1.5 @@ -367,7 +367,7 @@ ## v2.2.14 (2009-03-17) -* Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecordversion strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSqlserver use Ship vsBird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) PreviousJoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] +* Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecordversion strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSQLServer use Ship vsBird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) PreviousJoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] * Implement new savepoint support [Ken Collins]http://rails.lighthouseapp.com/projects/8994/tickets/383http://www.codeproject.com/KB/database/sqlservertransactions.aspx * Coerce NestedScopingTest#test_merged_scoped_find to use correct regexp for adapter. [Ken Collins] * Implement a custom ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation#aliased_table_name_formethod that uses a Regexp.escape so that table/column quoting does not get ignored. [Ken Collins] @@ -417,7 +417,7 @@ ## v2.2.5 (2009-01-04) * Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent bydefault. [Ken Collins] -* Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server millisecondsand applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SqlserverTimestampclass to parse the before type cast value appropriately. Also update the adapters #quoted_date methodto work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] +* Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server millisecondsand applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SQLServerTimestampclass to parse the before type cast value appropriately. Also update the adapters #quoted_date methodto work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] * Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Nowthis adapter is compatible for using with other adapters. [Ken Collins] diff --git a/Guardfile b/Guardfile index 4ca4580d3..681a07ea9 100644 --- a/Guardfile +++ b/Guardfile @@ -3,8 +3,8 @@ require_relative 'test/support/paths_sqlserver' notification :terminal_notifier if Guard::Notifier::TerminalNotifier.available? ignore %r{debug\.log} -ar_lib = File.join ARTest::Sqlserver.root_activerecord, 'lib' -ar_test = File.join ARTest::Sqlserver.root_activerecord, 'test' +ar_lib = File.join ARTest::SQLServer.root_activerecord, 'lib' +ar_test = File.join ARTest::SQLServer.root_activerecord, 'test' guard :minitest, { all_on_start: false, diff --git a/README.md b/README.md index c6d79d2ae..aa1a70bd5 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1 You can configure a few options to your needs. First is the max column width for the logged table. The default value is 50 characters. You can change it like so. ```ruby -ActiveRecord::ConnectionAdapters::Sqlserver::Showplan::PrinterTable.max_column_width = 500 +ActiveRecord::ConnectionAdapters::SQLServer::Showplan::PrinterTable.max_column_width = 500 ``` Another configuration is the showplan option. Some might find the XML format more useful. If you have Nokogiri installed, we will format the XML string. I will gladly accept pathches that make the XML printer more useful! diff --git a/Rakefile b/Rakefile index b29f2fb5b..b9217b325 100644 --- a/Rakefile +++ b/Rakefile @@ -4,7 +4,7 @@ require_relative 'test/support/paths_sqlserver' def test_files return ENV['TEST_FILES'].split(',') if ENV['TEST_FILES'] sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') - ar_cases = Dir.glob("#{ARTest::Sqlserver.root_activerecord}/test/cases/**/*_test.rb") + ar_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") if ENV['SQLSERVER_ONLY'] sqlserver_cases elsif ENV['ACTIVERECORD_ONLY'] @@ -22,7 +22,7 @@ namespace :test do %w(dblib odbc).each do |mode| Rake::TestTask.new(mode) do |t| - t.libs = ARTest::Sqlserver.test_load_paths + t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files t.verbose = true end @@ -48,7 +48,7 @@ namespace :profile do Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| profile_case = File.basename(test_file).sub('_profile_case.rb', '') Rake::TestTask.new(profile_case) do |t| - t.libs = ARTest::Sqlserver.test_load_paths + t.libs = ARTest::SQLServer.test_load_paths t.test_files = [test_file] t.verbose = true end diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index d996640e2..66821aa92 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -4,7 +4,7 @@ require "active_record/connection_adapters/sqlserver/version" Gem::Specification.new do |spec| spec.name = 'activerecord-sqlserver-adapter' - spec.version = ActiveRecord::ConnectionAdapters::Sqlserver::Version::VERSION + spec.version = ActiveRecord::ConnectionAdapters::SQLServer::Version::VERSION spec.platform = Gem::Platform::RUBY spec.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] spec.email = ['ken@metaskills.net', 'will@wbond.net'] diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index a20530a70..a8eddee5d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module CoreExt module ActiveRecord extend ActiveSupport::Concern @@ -34,4 +34,4 @@ def coerce_sqlserver_time(*attributes) end end -ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord +ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ActiveRecord 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 974a62e4a..1f8e8870e 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module CoreExt module Explain SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' @@ -34,5 +34,5 @@ def unprepare_sqlserver_statement(sql) end end -ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain -ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Explain +ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain +ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb index f0c940bc8..1b7a64d41 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module CoreExt module ODBC module Statement @@ -24,5 +24,5 @@ def run_block(*args) end end -ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement -ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database +ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Statement +ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Database diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb index 0cecfdc6a..df2a65def 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module CoreExt module Relation private @@ -14,4 +14,4 @@ def tables_in_string(string) end end -ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::Relation +ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Relation diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index ac6212901..4c0e56c14 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module DatabaseLimits def table_alias_length 128 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index d3b3bcf2c..fd7ad41fd 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module DatabaseStatements def select_rows(sql, name = nil, binds = []) do_exec_query sql, name, binds, fetch: :rows @@ -116,7 +116,7 @@ def execute_procedure(proc_name, *variables) def use_database(database = nil) return if sqlserver_azure? - name = Sqlserver::Utils.extract_identifiers(database || @connection_options[:database]) + name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]) do_execute "USE #{name}" unless database.blank? end @@ -258,7 +258,7 @@ def drop_database(database) retry_count = 0 max_retries = 1 begin - do_execute "DROP DATABASE #{Sqlserver::Utils.extract_identifiers(database)}" + do_execute "DROP DATABASE #{SQLServer::Utils.extract_identifiers(database)}" rescue ActiveRecord::StatementInvalid => err if err.message =~ /because it is currently in use/i raise if retry_count >= max_retries @@ -274,7 +274,7 @@ def drop_database(database) end def create_database(database, collation = @connection_options[:collation]) - name = Sqlserver::Utils.extract_identifiers(database) + name = SQLServer::Utils.extract_identifiers(database) if collation do_execute "CREATE DATABASE #{name} COLLATE #{collation}" else diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 083ccfa11..c6ec03f27 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -6,7 +6,7 @@ class DeadlockVictim < WrappedDatabaseException end module ConnectionAdapters - module Sqlserver + module SQLServer module Errors LOST_CONNECTION_EXCEPTIONS = { dblib: ['TinyTds::Error'], diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 0cda46611..e9a985da4 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Quoting QUOTED_TRUE = '1' @@ -8,11 +8,11 @@ module Quoting QUOTED_STRING_PREFIX = 'N' def quote_string(s) - Sqlserver::Utils.quote_string(s) + SQLServer::Utils.quote_string(s) end def quote_column_name(name) - Sqlserver::Utils.extract_identifiers(name).object_quoted + SQLServer::Utils.extract_identifiers(name).object_quoted end def quote_default_value(value, column) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index ede9b0d2a..99b92f633 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache attr_reader :view_information @@ -68,7 +68,7 @@ def view_information(table_name) private def table_name_key(table_name) - Sqlserver::Utils.extract_identifiers(table_name).object + SQLServer::Utils.extract_identifiers(table_name).object end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 41114aad3..c6c3fc044 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer class SchemaCreation < AbstractAdapter::SchemaCreation private diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 681106a12..2cb6eb126 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module SchemaStatements def native_database_types @@ -13,7 +13,7 @@ def tables(table_type = 'BASE TABLE') def table_exists?(table_name) return false if table_name.blank? - unquoted_table_name = Sqlserver::Utils.extract_identifiers(table_name).object + unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name) end @@ -187,10 +187,10 @@ def initialize_native_database_types end def column_definitions(table_name) - db_name = Sqlserver::Utils.extract_identifiers(table_name).database + db_name = SQLServer::Utils.extract_identifiers(table_name).database db_name_with_period = "#{db_name}." if db_name - table_schema = Sqlserver::Utils.extract_identifiers(table_name).schema - table_name = Sqlserver::Utils.extract_identifiers(table_name).object + table_schema = SQLServer::Utils.extract_identifiers(table_name).schema + table_name = SQLServer::Utils.extract_identifiers(table_name).object sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -335,7 +335,7 @@ def view_table_name(table_name) end def view_information(table_name) - table_name = Sqlserver::Utils.extract_identifiers(table_name).object + table_name = SQLServer::Utils.extract_identifiers(table_name).object view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'", 'SCHEMA' if view_info view_info = view_info.with_indifferent_access @@ -352,7 +352,7 @@ def view_information(table_name) end def table_name_or_views_table_name(table_name) - unquoted_table_name = Sqlserver::Utils.extract_identifiers(table_name).object + unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index af2e32a89..0f597637e 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -3,7 +3,7 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Showplan OPTION_ALL = 'SHOWPLAN_ALL' OPTION_TEXT = 'SHOWPLAN_TEXT' 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 9af28d23a..70de897db 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Showplan class PrinterTable cattr_accessor :max_column_width, :cell_padding diff --git a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb index dbedd7448..f620a6453 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Showplan class PrinterXml def initialize(result) diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index c8d2284cc..c956eb62f 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition def uuid(name, options = {}) column(name, 'uniqueidentifier', options) diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 2ac70d7de..7b7fe87a1 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -2,7 +2,7 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Utils # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL @@ -109,7 +109,7 @@ def unquote_string(s) end def extract_identifiers(name) - Sqlserver::Utils::Name.new(name) + SQLServer::Utils::Name.new(name) end end diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 1fd345ee0..dcc96149f 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -1,6 +1,6 @@ module ActiveRecord module ConnectionAdapters - module Sqlserver + module SQLServer module Version VERSION = '4.2.0' diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 92665338f..5646f6531 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -24,13 +24,13 @@ module ActiveRecord module ConnectionAdapters class SQLServerAdapter < AbstractAdapter - include Sqlserver::Version, - Sqlserver::Quoting, - Sqlserver::DatabaseStatements, - Sqlserver::Showplan, - Sqlserver::SchemaStatements, - Sqlserver::DatabaseLimits, - Sqlserver::Errors + include SQLServer::Version, + SQLServer::Quoting, + SQLServer::DatabaseStatements, + SQLServer::Showplan, + SQLServer::SchemaStatements, + SQLServer::DatabaseLimits, + SQLServer::Errors ADAPTER_NAME = 'SQLServer'.freeze @@ -45,7 +45,7 @@ class SQLServerAdapter < AbstractAdapter def initialize(connection, logger, pool, config) super(connection, logger, pool) # AbstractAdapter Responsibility - @schema_cache = Sqlserver::SchemaCache.new self + @schema_cache = SQLServer::SchemaCache.new self @visitor = Arel::Visitors::SQLServer.new self # Our Responsibility @config = config @@ -170,7 +170,7 @@ def primary_key(table_name) end def schema_creation - Sqlserver::SchemaCreation.new self + SQLServer::SchemaCreation.new self end # === SQLServer Specific (DB Reflection) ======================== # @@ -360,7 +360,7 @@ def initialize_dateformatter end def remove_database_connections_and_rollback(database = nil) - name = Sqlserver::Utils.extract_identifiers(database || current_database) + name = SQLServer::Utils.extract_identifiers(database || current_database) do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" begin yield diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index dbfa55254..b65b7708e 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -15,7 +15,7 @@ require 'models/sqlserver/upper_test_default' require 'models/sqlserver/upper_test_lowered' -class AdapterTestSqlserver < ActiveRecord::TestCase +class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks, :posts @@ -776,7 +776,7 @@ class AdapterTest < ActiveRecord::TestCase # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server # Until that patch is made to rails we are preventing this test from running in this gem. - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :authors end diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/associations_test_sqlserver.rb index d68cbb2c7..c2e1e9257 100644 --- a/test/cases/associations_test_sqlserver.rb +++ b/test/cases/associations_test_sqlserver.rb @@ -6,7 +6,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase # Rails does not do a case-insensive comparison # Until that patch is made to rails we are preventing this test from running in this gem. - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_has_many_through_obeys_order_on_through_association owner = owners(:blackbeard) # assert owner.toys.to_sql.include?("pets.name desc") # What's currently in rails diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/attribute_methods_test_sqlserver.rb index f82eaaa9a..2f38c77f9 100644 --- a/test/cases/attribute_methods_test_sqlserver.rb +++ b/test/cases/attribute_methods_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'models/topic' require 'models/sqlserver/topic' -class AttributeMethodsTestSqlserver < ActiveRecord::TestCase +class AttributeMethodsTestSQLServer < ActiveRecord::TestCase end class AttributeMethodsTest < ActiveRecord::TestCase @@ -14,7 +14,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase :test_typecast_attribute_from_select_to_true ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :developers diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/base_test_sqlserver.rb index f0a490c0b..c3cfee246 100644 --- a/test/cases/base_test_sqlserver.rb +++ b/test/cases/base_test_sqlserver.rb @@ -4,7 +4,7 @@ class BasicsTest < ActiveRecord::TestCase - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest COERCED_TESTS = [ :test_column_names_are_escaped diff --git a/test/cases/batches_test_sqlserver.rb b/test/cases/batches_test_sqlserver.rb index 269ad8ddd..58f2416c5 100644 --- a/test/cases/batches_test_sqlserver.rb +++ b/test/cases/batches_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/post' -class BatchesTestSqlserver < ActiveRecord::TestCase +class BatchesTestSQLServer < ActiveRecord::TestCase end class EachTest < ActiveRecord::TestCase @@ -10,7 +10,7 @@ class EachTest < ActiveRecord::TestCase :test_find_in_batches_should_quote_batch_order ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :posts diff --git a/test/cases/belongs_to_associations_test_sqlserver.rb b/test/cases/belongs_to_associations_test_sqlserver.rb index 77d77a8ea..4a2d3b5c9 100644 --- a/test/cases/belongs_to_associations_test_sqlserver.rb +++ b/test/cases/belongs_to_associations_test_sqlserver.rb @@ -1,13 +1,13 @@ require 'cases/helper_sqlserver' -class BelongsToAssociationsTestSqlserver < ActiveRecord::TestCase +class BelongsToAssociationsTestSQLServer < ActiveRecord::TestCase end class BelongsToAssociationsTest < ActiveRecord::TestCase COERCED_TESTS = [:test_belongs_to_with_primary_key_joins_on_correct_column] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_belongs_to_with_primary_key_joins_on_correct_column sql = Client.joins(:firm_with_primary_key).to_sql diff --git a/test/cases/calculations_test_sqlserver.rb b/test/cases/calculations_test_sqlserver.rb index 518c1f90a..e75299371 100644 --- a/test/cases/calculations_test_sqlserver.rb +++ b/test/cases/calculations_test_sqlserver.rb @@ -5,7 +5,7 @@ require 'models/club' require 'models/organization' -class CalculationsTestSqlserver < ActiveRecord::TestCase +class CalculationsTestSQLServer < ActiveRecord::TestCase end class CalculationsTest < ActiveRecord::TestCase @@ -18,7 +18,7 @@ class CalculationsTest < ActiveRecord::TestCase :test_offset_is_kept ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :accounts diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/connection_management_test_sqlserver.rb index 6e4396d06..c2b087cb6 100644 --- a/test/cases/connection_management_test_sqlserver.rb +++ b/test/cases/connection_management_test_sqlserver.rb @@ -9,7 +9,7 @@ class ConnectionManagementTest < ActiveRecord::TestCase COERCED_TESTS = [:test_connection_pool_per_pid] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest #https://www.ruby-forum.com/topic/4221299 def test_coerced_connection_pool_per_pid diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index fe7390770..9d17f682e 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -2,7 +2,7 @@ require 'models/reply' require 'models/sqlserver/topic' -class ConnectionTestSqlserver < ActiveRecord::TestCase +class ConnectionTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index e542b10a3..40996dcf1 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' -class DatabaseStatementsTestSqlserver < ActiveRecord::TestCase +class DatabaseStatementsTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 4c0f36fa0..f3da74056 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -3,11 +3,11 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase def setup - skip "TestDisconnectedAdapterSqlserver instead " + skip "TestDisconnectedAdapterSQLServer instead " end end -class TestDisconnectedAdapterSqlserver < ActiveRecord::TestCase +class TestDisconnectedAdapterSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false def setup diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/eager_test_sqlserver.rb index 3bd25de83..8b7f73a0c 100644 --- a/test/cases/eager_test_sqlserver.rb +++ b/test/cases/eager_test_sqlserver.rb @@ -3,14 +3,14 @@ require 'models/comment' require 'models/author' -class EagerAssociationTestSqlserver < ActiveRecord::TestCase +class EagerAssociationTestSQLServer < ActiveRecord::TestCase end class EagerAssociationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_count_with_include] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :posts, :comments, :authors diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 4d28ee046..ea28efa92 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' -class ExecuteProcedureTestSqlserver < ActiveRecord::TestCase +class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase setup do @klass = ActiveRecord::Base diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/finder_test_sqlserver.rb index 2a143a3b7..d6361a2f8 100644 --- a/test/cases/finder_test_sqlserver.rb +++ b/test/cases/finder_test_sqlserver.rb @@ -6,7 +6,7 @@ require 'models/sqlserver/topic' # require 'cases/finder_test.rb' -class FinderTestSqlserver < ActiveRecord::TestCase +class FinderTestSQLServer < ActiveRecord::TestCase end class FinderTest < ActiveRecord::TestCase @@ -17,7 +17,7 @@ class FinderTest < ActiveRecord::TestCase :test_take_and_first_and_last_with_integer_should_use_sql_limit ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct_and_offset diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb index 77d9786a5..60dad5b3d 100644 --- a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb +++ b/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' -class HasAndBelongsToManyAssociationsTestSqlserver < ActiveRecord::TestCase +class HasAndBelongsToManyAssociationsTestSQLServer < ActiveRecord::TestCase end class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase @@ -10,7 +10,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase :test_caching_of_columns ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest # TODO: put a real test here def test_coerced_count_with_finder_sql diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/inheritance_test_sqlserver.rb index 5d9141e81..8be48a882 100644 --- a/test/cases/inheritance_test_sqlserver.rb +++ b/test/cases/inheritance_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'models/project' require 'models/subscriber' -class InheritanceTestSqlserver < ActiveRecord::TestCase +class InheritanceTestSQLServer < ActiveRecord::TestCase end class InheritanceTest < ActiveRecord::TestCase @@ -15,7 +15,7 @@ class InheritanceTest < ActiveRecord::TestCase :test_eager_load_belongs_to_primary_key_quoting ] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_a_bad_type_column Company.connection.execute "SET IDENTITY_INSERT [companies] ON" diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 6b8123501..ac1edc770 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -2,7 +2,7 @@ require 'models/person' require 'models/sqlserver/person' -class MigrationTestSqlserver < ActiveRecord::TestCase +class MigrationTestSQLServer < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.connection @@ -24,7 +24,7 @@ class MigrationTestSqlserver < ActiveRecord::TestCase should 'not create a tables if error in migrations' do begin - migrations_dir = File.join ARTest::Sqlserver.migrations_root, 'transaction_table' + migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table' ActiveRecord::Migrator.up(migrations_dir) rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message @@ -68,7 +68,7 @@ class MigrationTestSqlserver < ActiveRecord::TestCase if ActiveRecord::TestCase.sqlserver_azure? class MigrationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest # TODO: put a real test here def test_coerced_test_migrator_db_has_no_schema_migrations_table diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb index be3d9ee37..f20bfa01f 100644 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ b/test/cases/offset_and_limit_test_sqlserver.rb @@ -10,7 +10,7 @@ require 'models/categorization' require 'models/sqlserver/sql_server_order_row_number' -class OffsetAndLimitTestSqlserver < ActiveRecord::TestCase +class OffsetAndLimitTestSQLServer < ActiveRecord::TestCase fixtures :jobs, :people, :references, :subscriptions, :authors, :posts, :comments, :categorizations diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index 2162af5c7..f538a226e 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/post' -class OrderTestSqlserver < ActiveRecord::TestCase +class OrderTestSQLServer < ActiveRecord::TestCase fixtures :posts diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/persistence_test_sqlserver.rb index a572a611c..430497ba2 100644 --- a/test/cases/persistence_test_sqlserver.rb +++ b/test/cases/persistence_test_sqlserver.rb @@ -16,7 +16,7 @@ require 'models/sqlserver/topic' require 'rexml/document' -class PersistenceTestSqlserver < ActiveRecord::TestCase +class PersistenceTestSQLServer < ActiveRecord::TestCase end class PersistenceTest < ActiveRecord::TestCase @@ -25,7 +25,7 @@ class PersistenceTest < ActiveRecord::TestCase COERCED_TESTS = [:test_update_all_doesnt_ignore_order, :test_update_columns_changing_id, :test_update_attributes] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_update_all_doesnt_ignore_order assert_equal authors(:david).id + 1, authors(:mary).id diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 980b2bba7..dd3133e7c 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'models/reader' require 'models/sqlserver/person' -class PessimisticLockingTestSqlserver < ActiveRecord::TestCase +class PessimisticLockingTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false fixtures :people, :readers diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/predicate_builder_test_sqlserver.rb index 81c17c506..3242ae634 100644 --- a/test/cases/predicate_builder_test_sqlserver.rb +++ b/test/cases/predicate_builder_test_sqlserver.rb @@ -7,7 +7,7 @@ class PredicateBuilderTest < ActiveRecord::TestCase COERCED_TESTS = [:test_registering_new_handlers] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_registering_new_handlers ActiveRecord::PredicateBuilder.register_handler(Regexp, proc do |column, value| diff --git a/test/cases/query_cache_test_sqlserver.rb b/test/cases/query_cache_test_sqlserver.rb index cf26d2d42..8bede2012 100644 --- a/test/cases/query_cache_test_sqlserver.rb +++ b/test/cases/query_cache_test_sqlserver.rb @@ -1,14 +1,14 @@ require 'cases/helper_sqlserver' require 'models/task' -class QueryCacheTestSqlserver < ActiveRecord::TestCase +class QueryCacheTestSQLServer < ActiveRecord::TestCase end class QueryCacheTest < ActiveRecord::TestCase COERCED_TESTS = [:test_cache_does_not_wrap_string_results_in_arrays] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :tasks diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/relations_test_sqlserver.rb index c12b6a3dd..5657b126c 100644 --- a/test/cases/relations_test_sqlserver.rb +++ b/test/cases/relations_test_sqlserver.rb @@ -7,7 +7,7 @@ class RelationTest < ActiveRecord::TestCase :test_to_sql_on_eager_join ] # Until that patch is made to rails we are preventing this test from running in this gem. - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest fixtures :posts def test_coerced_merging_reorders_bind_params diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/resolver_test_sqlserver.rb index c9512a85b..83714db8e 100644 --- a/test/cases/resolver_test_sqlserver.rb +++ b/test/cases/resolver_test_sqlserver.rb @@ -6,7 +6,7 @@ class ConnectionSpecification class ResolverTest < ActiveRecord::TestCase - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest COERCED_TESTS = [ :test_url_host_no_db, diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 9f7ffe552..77f78d533 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -3,7 +3,7 @@ require 'stringio' -class SchemaDumperTestSqlserver < ActiveRecord::TestCase +class SchemaDumperTestSQLServer < ActiveRecord::TestCase setup :find_all_tables @@ -77,7 +77,7 @@ class SchemaDumperTest < ActiveRecord::TestCase COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal output = standard_dump diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 7be3c1ee9..1e741d61f 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -2,7 +2,7 @@ require 'models/sqlserver/sql_server_natural_pk_data' require 'models/sqlserver/sql_server_natural_pk_data_schema' -class SchemaTestSqlserver < ActiveRecord::TestCase +class SchemaTestSQLServer < ActiveRecord::TestCase setup do @connection = ActiveRecord::Base.connection diff --git a/test/cases/scratch_test_sqlserver.rb b/test/cases/scratch_test_sqlserver.rb index 825a16549..e140b9696 100644 --- a/test/cases/scratch_test_sqlserver.rb +++ b/test/cases/scratch_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' -class ScratchTestSqlserver < ActiveRecord::TestCase +class ScratchTestSQLServer < ActiveRecord::TestCase # TODO: put a real test here should 'pass' do diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 4fb2aa433..446fd92da 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/car' -class ShowplanTestSqlserver < ActiveRecord::TestCase +class ShowplanTestSQLServer < ActiveRecord::TestCase fixtures :cars diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 0d41a5ab8..964f9017b 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -8,7 +8,7 @@ require 'models/sqlserver/sql_server_quoted_view_2' require 'models/sqlserver/sql_server_tinyint_pk' require 'models/sqlserver/string_default' -class SpecificSchemaTestSqlserver < ActiveRecord::TestCase +class SpecificSchemaTestSQLServer < ActiveRecord::TestCase should 'be able to complex count tables with no primary key' do NoPkData.delete_all diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb index fe4022ff9..e945acfee 100644 --- a/test/cases/table_name_test_sqlserver.rb +++ b/test/cases/table_name_test_sqlserver.rb @@ -5,7 +5,7 @@ class SqlServerRailsOrders < ActiveRecord::Base self.table_name = 'rails.orders' end -class TableNameTestSqlserver < ActiveRecord::TestCase +class TableNameTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 0a0c65770..3ace7ae18 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -2,7 +2,7 @@ require 'models/ship' require 'models/developer' -class TransactionTestSqlserver < ActiveRecord::TestCase +class TransactionTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false @@ -47,7 +47,7 @@ def assert_no_ships end class TransactionTest < ActiveRecord::TestCase - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest COERCED_TESTS = [:test_releasing_named_savepoints] diff --git a/test/cases/unicode_test_sqlserver.rb b/test/cases/unicode_test_sqlserver.rb index 71bfa3a6b..5f67db4db 100644 --- a/test/cases/unicode_test_sqlserver.rb +++ b/test/cases/unicode_test_sqlserver.rb @@ -1,7 +1,7 @@ # encoding: UTF-8 require 'cases/helper_sqlserver' -class UnicodeTestSqlserver < ActiveRecord::TestCase +class UnicodeTestSQLServer < ActiveRecord::TestCase context 'Testing basic saves and unicode limits' do diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/uniqueness_validation_test_sqlserver.rb index 29b45cbf4..e5f294e0f 100644 --- a/test/cases/uniqueness_validation_test_sqlserver.rb +++ b/test/cases/uniqueness_validation_test_sqlserver.rb @@ -10,14 +10,14 @@ def strip_mb_chars_for_sqlserver end end -class UniquenessValidationTestSqlserver < ActiveRecord::TestCase +class UniquenessValidationTestSQLServer < ActiveRecord::TestCase end class UniquenessValidationTest < ActiveRecord::TestCase COERCED_TESTS = [:test_validate_uniqueness_with_limit_and_utf8] - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest # I guess most databases just truncate a string when inserting. To pass this test we do a few things. # First, we make sure the type is unicode safe, second we extend the limit to well beyond what is diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 1b5fd5390..2fd60d8cf 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -1,8 +1,8 @@ require 'cases/helper_sqlserver' -class UtilsTestSqlserver < ActiveRecord::TestCase +class UtilsTestSQLServer < ActiveRecord::TestCase - Utils = ActiveRecord::ConnectionAdapters::Sqlserver::Utils + Utils = ActiveRecord::ConnectionAdapters::SQLServer::Utils it '.quote_string' do Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users" @@ -12,7 +12,7 @@ class UtilsTestSqlserver < ActiveRecord::TestCase Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" end - describe '.extract_identifiers constructor and thus Sqlserver::Utils::Name value object' do + describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do let(:valid_names) { valid_names_unquoted + valid_names_quoted } diff --git a/test/cases/where_chain_test_sqlserver.rb b/test/cases/where_chain_test_sqlserver.rb index 80e26bae2..f690f61ac 100644 --- a/test/cases/where_chain_test_sqlserver.rb +++ b/test/cases/where_chain_test_sqlserver.rb @@ -5,7 +5,7 @@ module ActiveRecord class WhereChainTest < ActiveRecord::TestCase - include ARTest::Sqlserver::CoercedTest + include ARTest::SQLServer::CoercedTest COERCED_TESTS = [:test_not_eq_with_array_parameter] diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index a569d8476..73d692604 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,6 +1,6 @@ ActiveRecord::Schema.define do - execute File.read(ARTest::Sqlserver.schema_datatypes_2012_file) + execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) create_table :UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string diff --git a/test/support/coerced_test_sqlserver.rb b/test/support/coerced_test_sqlserver.rb index 6764a6393..f87305222 100644 --- a/test/support/coerced_test_sqlserver.rb +++ b/test/support/coerced_test_sqlserver.rb @@ -1,5 +1,5 @@ module ARTest - module Sqlserver + module SQLServer module CoercedTest def self.included(base) diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index ef97df7c2..5e4eb8e1c 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -1,10 +1,10 @@ module ARTest - module Sqlserver + module SQLServer extend self def schema_root - File.join ARTest::Sqlserver.test_root_sqlserver, 'schema' + File.join ARTest::SQLServer.test_root_sqlserver, 'schema' end def schema_file @@ -26,4 +26,4 @@ def load_schema end end -ARTest::Sqlserver.load_schema +ARTest::SQLServer.load_schema diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index 0fa13b19f..acf18c937 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -1,5 +1,5 @@ module ARTest - module Sqlserver + module SQLServer extend self @@ -44,5 +44,5 @@ def arconfig_file_env! end end -ARTest::Sqlserver.add_to_load_paths! -ARTest::Sqlserver.arconfig_file_env! +ARTest::SQLServer.add_to_load_paths! +ARTest::SQLServer.arconfig_file_env! diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 239f85de2..47bf85538 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -1,5 +1,5 @@ module ARTest - module Sqlserver + module SQLServer extend self @@ -26,6 +26,6 @@ def sql_counter_listenters_unsubscribe end end -ActiveRecord::SQLCounter.ignored_sql.concat ARTest::Sqlserver.ignored_sql -ARTest::Sqlserver.sql_counter_listenters_unsubscribe -ARTest::Sqlserver.sql_counter_listenter = ActiveSupport::Notifications.subscribe 'sql.active_record', ActiveRecord::SQLCounter.new +ActiveRecord::SQLCounter.ignored_sql.concat ARTest::SQLServer.ignored_sql +ARTest::SQLServer.sql_counter_listenters_unsubscribe +ARTest::SQLServer.sql_counter_listenter = ActiveSupport::Notifications.subscribe 'sql.active_record', ActiveRecord::SQLCounter.new From fd020b3ec7713c2daea993a5d62120f6b3cefe74 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 12:14:32 -0500 Subject: [PATCH 0203/1412] New ActiveRecord::Type objects for SQLServer. --- README.md | 14 - .../sqlserver/core_ext/active_record.rb | 16 +- .../connection_adapters/sqlserver/quoting.rb | 32 +- .../sqlserver/schema_statements.rb | 7 +- .../connection_adapters/sqlserver/type.rb | 36 + .../sqlserver/type/big_integer.rb | 13 + .../sqlserver/type/binary.rb | 12 + .../sqlserver/type/boolean.rb | 13 + .../sqlserver/type/castable.rb | 15 + .../sqlserver/type/char.rb | 12 + .../sqlserver/type/core_ext/value.rb | 39 + .../sqlserver/type/date.rb | 14 + .../sqlserver/type/datetime.rb | 45 + .../sqlserver/type/decimal.rb | 13 + .../sqlserver/type/float.rb | 13 + .../sqlserver/type/integer.rb | 13 + .../sqlserver/type/money.rb | 21 + .../sqlserver/type/quoter.rb | 32 + .../sqlserver/type/small_integer.rb | 13 + .../sqlserver/type/small_money.rb | 17 + .../sqlserver/type/smalldatetime.rb | 24 + .../sqlserver/type/string.rb | 12 + .../sqlserver/type/text.rb | 12 + .../sqlserver/type/time.rb | 48 + .../sqlserver/type/tiny_integer.rb | 22 + .../sqlserver/type/unicode_char.rb | 12 + .../sqlserver/type/unicode_string.rb | 12 + .../sqlserver/type/unicode_text.rb | 12 + .../sqlserver/type/unicode_varchar.rb | 12 + .../sqlserver/type/unicode_varchar_max.rb | 20 + .../sqlserver/type/varbinary.rb | 12 + .../sqlserver/type/varbinary_max.rb | 16 + .../sqlserver/type/varchar.rb | 12 + .../sqlserver/type/varchar_max.rb | 20 + .../connection_adapters/sqlserver_adapter.rb | 45 + .../connection_adapters/sqlserver_column.rb | 63 +- test/cases/adapter_test_sqlserver.rb | 70 -- test/cases/column_test_sqlserver.rb | 969 +++++++++++------- test/cases/helper_sqlserver.rb | 2 + test/cases/migration_test_sqlserver.rb | 1 - .../pessimistic_locking_test_sqlserver.rb | 1 - test/cases/unicode_test_sqlserver.rb | 48 - test/fixtures/1px.gif | Bin 0 -> 49 bytes test/models/float_data.rb | 3 - test/models/person.rb | 3 - test/models/sql_server_chronic.rb | 5 - test/models/sqlserver/datatype.rb | 3 + test/models/topic.rb | 4 - test/schema/datatypes/2012.sql | 63 ++ test/schema/sqlserver_specific_schema.rb | 11 +- 50 files changed, 1316 insertions(+), 611 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/big_integer.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/binary.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/boolean.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/castable.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/char.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/date.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/datetime.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/decimal.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/float.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/integer.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/money.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/quoter.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/small_integer.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/small_money.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/string.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/text.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/time.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/varbinary.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/varchar.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb delete mode 100644 test/cases/unicode_test_sqlserver.rb create mode 100644 test/fixtures/1px.gif delete mode 100644 test/models/float_data.rb delete mode 100644 test/models/person.rb delete mode 100644 test/models/sql_server_chronic.rb create mode 100644 test/models/sqlserver/datatype.rb delete mode 100644 test/models/topic.rb create mode 100644 test/schema/datatypes/2012.sql diff --git a/README.md b/README.md index aa1a70bd5..ec2047a1c 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,6 @@ This is a long story, but if you are not working with a legacy database and you http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/rails-db-rake-tasks -#### Date/Time Data Type Hinting - -SQL Server 2005 does not include a native data type for just `date` or `time`, it only has `datetime`. To pass the ActiveRecord tests we implemented two simple class methods that can teach your models to coerce column information to be cast correctly. Simply pass a list of symbols to either the `coerce_sqlserver_date` or `coerce_sqlserver_time` methods that correspond to 'datetime' columns that need to be cast correctly. - -```ruby -class Topic < ActiveRecord::Base - coerce_sqlserver_date :last_read - coerce_sqlserver_time :bonus_time -end -``` - -This implementation has some limitations. To date we can only coerce date/time types for models that conform to the expected ActiveRecord class to table naming conventions. So a table of 'foo_bar_widgets' will look for coerced column types in the FooBarWidget class. - - #### Executing Stored Procedures Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index a8eddee5d..b1019e34e 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -3,15 +3,11 @@ module ConnectionAdapters module SQLServer module CoreExt module ActiveRecord - extend ActiveSupport::Concern - included do - class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns - self.coerced_sqlserver_date_columns = Set.new - self.coerced_sqlserver_time_columns = Set.new - end + extend ActiveSupport::Concern module ClassMethods + def execute_procedure(proc_name, *variables) if connection.respond_to?(:execute_procedure) connection.execute_procedure(proc_name, *variables) @@ -20,14 +16,8 @@ def execute_procedure(proc_name, *variables) end end - def coerce_sqlserver_date(*attributes) - self.coerced_sqlserver_date_columns += attributes.map(&:to_s) - end - - def coerce_sqlserver_time(*attributes) - self.coerced_sqlserver_time_columns += attributes.map(&:to_s) - end end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index e9a985da4..3a7c5c90a 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -44,30 +44,10 @@ def unquoted_false 0 end - def quoted_datetime(value) - if value.acts_like?(:time) - time_zone_qualified_value = quoted_value_acts_like_time_filter(value) - if value.is_a?(Date) - time_zone_qualified_value.iso8601(3).to(18) - else - time_zone_qualified_value.iso8601(3).to(22) - end - else - quoted_date(value) - end - end - - def quoted_full_iso8601(value) - if value.acts_like?(:time) - value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22) - else - quoted_date(value) - end - end - def quoted_date(value) if value.acts_like?(:time) && value.respond_to?(:usec) - "#{super}.#{sprintf('%03d', value.usec / 1000)}" + precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last + "#{super}.#{precision}" elsif value.acts_like?(:date) value.to_s(:_sqlserver_dateformat) else @@ -78,8 +58,12 @@ def quoted_date(value) private - def _quote(value) # , column = nil + def _quote(value) case value + when Type::Binary::Data + "0x#{value.hex}" + when SQLServer::Type::Quoter + value.quote_ss_value when String, ActiveSupport::Multibyte::Chars if value.is_utf8? "#{QUOTED_STRING_PREFIX}#{super}" @@ -87,7 +71,7 @@ def _quote(value) # , column = nil super end else - super(value) + super end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2cb6eb126..34216aa95 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -45,7 +45,7 @@ def indexes(table_name, name = nil) def columns(table_name, _name = nil) return [] if table_name.blank? column_definitions(table_name).map do |ci| - sqlserver_options = ci.except(:name, :default_value, :type, :null).merge(database_year: database_year) + sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function cast_type = lookup_cast_type(ci[:type]) new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options end @@ -199,6 +199,7 @@ def column_definitions(table_name) columns.COLUMN_DEFAULT AS default_value, columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, + columns.DATETIME_PRECISION AS datetime_precision, columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH @@ -245,11 +246,13 @@ def column_definitions(table_name) ci[:type] = case ci[:type] when /^bit|image|text|ntext|datetime$/ ci[:type] + when /^time$/i + "#{ci[:type]}(#{ci[:datetime_precision]})" when /^numeric|decimal$/i "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})" when /^float|real$/i "#{ci[:type]}(#{ci[:numeric_precision]})" - when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/ + when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/ ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})" else ci[:type] diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb new file mode 100644 index 000000000..2ce9925a5 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -0,0 +1,36 @@ +require 'active_record/type' +require 'active_record/connection_adapters/sqlserver/type/core_ext/value.rb' +require 'active_record/connection_adapters/sqlserver/type/castable.rb' +require 'active_record/connection_adapters/sqlserver/type/quoter.rb' +# Exact Numerics +require 'active_record/connection_adapters/sqlserver/type/integer.rb' +require 'active_record/connection_adapters/sqlserver/type/big_integer.rb' +require 'active_record/connection_adapters/sqlserver/type/small_integer.rb' +require 'active_record/connection_adapters/sqlserver/type/tiny_integer.rb' +require 'active_record/connection_adapters/sqlserver/type/boolean.rb' +require 'active_record/connection_adapters/sqlserver/type/decimal.rb' +require 'active_record/connection_adapters/sqlserver/type/money.rb' +require 'active_record/connection_adapters/sqlserver/type/small_money.rb' +# Approximate Numerics +require 'active_record/connection_adapters/sqlserver/type/float.rb' +# Date and Time +require 'active_record/connection_adapters/sqlserver/type/date.rb' +require 'active_record/connection_adapters/sqlserver/type/datetime.rb' +require 'active_record/connection_adapters/sqlserver/type/smalldatetime.rb' +require 'active_record/connection_adapters/sqlserver/type/time.rb' +# Character Strings +require 'active_record/connection_adapters/sqlserver/type/string.rb' +require 'active_record/connection_adapters/sqlserver/type/char.rb' +require 'active_record/connection_adapters/sqlserver/type/varchar.rb' +require 'active_record/connection_adapters/sqlserver/type/varchar_max.rb' +require 'active_record/connection_adapters/sqlserver/type/text.rb' +# Unicode Character Strings +require 'active_record/connection_adapters/sqlserver/type/unicode_string.rb' +require 'active_record/connection_adapters/sqlserver/type/unicode_char.rb' +require 'active_record/connection_adapters/sqlserver/type/unicode_varchar.rb' +require 'active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb' +require 'active_record/connection_adapters/sqlserver/type/unicode_text.rb' +# Binary Strings +require 'active_record/connection_adapters/sqlserver/type/binary.rb' +require 'active_record/connection_adapters/sqlserver/type/varbinary.rb' +require 'active_record/connection_adapters/sqlserver/type/varbinary_max.rb' diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb new file mode 100644 index 000000000..13a8c4b01 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class BigInteger < Integer + + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/binary.rb b/lib/active_record/connection_adapters/sqlserver/type/binary.rb new file mode 100644 index 000000000..68d5f7d6b --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/binary.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Binary < ActiveRecord::Type::Binary + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb new file mode 100644 index 000000000..97fbae592 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Boolean < ActiveRecord::Type::Boolean + + include Castable + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/castable.rb b/lib/active_record/connection_adapters/sqlserver/type/castable.rb new file mode 100644 index 000000000..de8444c71 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/castable.rb @@ -0,0 +1,15 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + module Castable + + def type_cast_from_database(value) + type_cast_from_ss_database? ? super : value + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb new file mode 100644 index 000000000..c69c028d6 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Char < String + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb b/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb new file mode 100644 index 000000000..4069b723a --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb @@ -0,0 +1,39 @@ +module ActiveRecord + module Type + class Value + + module SQLServerBehavior + + extend ActiveSupport::Concern + + included do + self.type_cast_from_ss_database = false + end + + module ClassMethods + + def type_cast_from_ss_database + @@type_cast_from_ss_database + end + + def type_cast_from_ss_database=(boolean) + @@type_cast_from_ss_database = !!boolean + end + + end + + def type_cast_from_ss_database? + self.class.type_cast_from_ss_database + end + + def type_cast_from_database(value) + type_cast_from_ss_database? ? super : value + end + + end + + include SQLServerBehavior + + 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 new file mode 100644 index 000000000..866ce8b75 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -0,0 +1,14 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Date < ActiveRecord::Type::Date + + # When FreeTDS/TinyTDS casts this data type natively. + # include Castable + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb new file mode 100644 index 000000000..bc67ba456 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -0,0 +1,45 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DateTime < ActiveRecord::Type::DateTime + + include Castable + + def type_cast_for_database(value) + value = super(value) + return unless value + return value unless value =~ ConnectionAdapters::Column::Format::ISO_DATETIME + "#{value.to_s(:db)}#{cast_usec_for_database(value)}" + end + + + private + + def cast_value(value) + value = value.respond_to?(:usec) ? value : super + return unless value + value.change usec: cast_usec(value) + end + + def cast_usec(value) + return 0 if value.usec.zero? + seconds = value.usec.to_f / 1_000_000.0 + ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3) + (ss_seconds * 1_000_000).to_i + end + + def cast_usec_for_database(value) + usec = cast_usec(value) + '.' + (BigDecimal(usec.to_s) / 1_000_000).round(3).to_s.split('.').last + end + + def second_precision + 0.00333 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb new file mode 100644 index 000000000..c999bec92 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Decimal < ActiveRecord::Type::Decimal + + include Castable + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb new file mode 100644 index 000000000..1629a8198 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Float < ActiveRecord::Type::Float + + include Castable + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/integer.rb b/lib/active_record/connection_adapters/sqlserver/type/integer.rb new file mode 100644 index 000000000..cbaf10f26 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/integer.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Integer < ActiveRecord::Type::Integer + + include Castable + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb new file mode 100644 index 000000000..a03d20b72 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -0,0 +1,21 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Money < Decimal + + def initialize(options = {}) + super + @precision = 19 + @scale = 4 + end + + def type + :money + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/quoter.rb b/lib/active_record/connection_adapters/sqlserver/type/quoter.rb new file mode 100644 index 000000000..b596ab40b --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/quoter.rb @@ -0,0 +1,32 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Quoter + + attr_reader :value, :type + + def initialize(value, type = nil) + @value = value + @type = type + end + + def to_s + @value.to_s + end + alias_method :to_str, :to_s + + def ==(other) + other == to_s || super + end + alias_method :eql?, :== + + def quote_ss_value + type.quote_ss(value) + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb new file mode 100644 index 000000000..f1761e5b4 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb @@ -0,0 +1,13 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class SmallInteger < Integer + + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb new file mode 100644 index 000000000..7d7388bba --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -0,0 +1,17 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class SmallMoney < Money + + def initialize(options = {}) + super + @precision = 10 + @scale = 4 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb new file mode 100644 index 000000000..5c035d57b --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -0,0 +1,24 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class SmallDateTime < DateTime + + include Castable + + + private + + def cast_usec(value) + 0 + end + + def cast_usec_for_database(value) + '.000' + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/string.rb b/lib/active_record/connection_adapters/sqlserver/type/string.rb new file mode 100644 index 000000000..e6b12b3bb --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/string.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class String < ActiveRecord::Type::String + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/text.rb b/lib/active_record/connection_adapters/sqlserver/type/text.rb new file mode 100644 index 000000000..daed3b11d --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/text.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Text < VarcharMax + + + end + end + 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 new file mode 100644 index 000000000..1b3689bf1 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -0,0 +1,48 @@ +Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S' + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Time < ActiveRecord::Type::Time + + # When FreeTDS/TinyTDS casts this data type natively. + # include Castable + + def type_cast_for_database(value) + return if value.nil? + Quoter.new super, self + end + + def quote_ss(value) + date = value.to_s(:_sqlserver_time) + frac = quote_usec(value) + "'#{date}.#{frac}'" + end + + + private + + def cast_value(value) + value = value.respond_to?(:usec) ? value.change(year: 2000, month: 01, day: 01) : super + return if value.blank? + value.change usec: cast_usec(value) + end + + def cast_usec(value) + (usec_to_seconds_frction(value) * 1_000_000).to_i + end + + def usec_to_seconds_frction(value) + (value.usec.to_f / 1_000_000.0).round(precision) + end + + def quote_usec(value) + usec_to_seconds_frction(value).to_s.split('.').last + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb new file mode 100644 index 000000000..2f49a5313 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb @@ -0,0 +1,22 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class TinyInteger < Integer + + + private + + def max_value + 256 + end + + def min_value + 0 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb new file mode 100644 index 000000000..f777b873d --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class UnicodeChar < UnicodeString + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb new file mode 100644 index 000000000..351de8ec2 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class UnicodeString < String + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb new file mode 100644 index 000000000..e61759158 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class UnicodeText < UnicodeVarcharMax + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb new file mode 100644 index 000000000..153d6d3d1 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class UnicodeVarchar < UnicodeChar + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb new file mode 100644 index 000000000..7ba417c9e --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -0,0 +1,20 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class UnicodeVarcharMax < UnicodeVarchar + + def initialize(options = {}) + super + @limit = 2_147_483_647 + end + + def type + :text + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb new file mode 100644 index 000000000..69dd8f4a1 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Varbinary < Binary + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb new file mode 100644 index 000000000..5ae61933a --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -0,0 +1,16 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class VarbinaryMax < Varbinary + + def initialize(options = {}) + super + @limit = 2_147_483_647 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb new file mode 100644 index 000000000..06ab885c0 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -0,0 +1,12 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Varchar < Char + + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb new file mode 100644 index 000000000..933f20527 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -0,0 +1,20 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class VarcharMax < Varchar + + def initialize(options = {}) + super + @limit = 2_147_483_647 + end + + def type + :text + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5646f6531..a58dafd0b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -7,6 +7,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/relation' require 'active_record/connection_adapters/sqlserver/version' +require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' require 'active_record/connection_adapters/sqlserver/database_statements' require 'active_record/connection_adapters/sqlserver/errors' @@ -247,6 +248,50 @@ def cs_equality_operator # === Abstract Adapter (Misc Support) =========================== # + def initialize_type_map(m) + m.register_type %r{.*}, SQLServer::Type::UnicodeString.new + # Exact Numerics + register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger + register_class_with_limit m, 'int(4)', SQLServer::Type::Integer + register_class_with_limit m, 'smallint(2)', SQLServer::Type::SmallInteger + register_class_with_limit m, 'tinyint(1)', SQLServer::Type::TinyInteger + 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) + SQLServer::Type::Decimal.new precision: precision, scale: scale + end + 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 + register_class_with_limit m, %r{\Afloat}, SQLServer::Type::Float + m.alias_type %r{\Areal}i, 'float' + # Date and Time + m.register_type 'date', SQLServer::Type::Date.new + m.register_type 'datetime', SQLServer::Type::DateTime.new + m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new + m.register_type %r{\Atime}i do |sql_type| + scale = extract_scale(sql_type) + precision = extract_precision(sql_type) + 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 + # 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.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 + end + def translate_exception(e, message) case message when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 77ffa3b8c..5c875a40a 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -3,21 +3,16 @@ module ConnectionAdapters class SQLServerColumn < Column def initialize(name, default, cast_type, sql_type = nil, null = true, sqlserver_options = {}) - @sqlserver_options = sqlserver_options.symbolize_keys super(name, default, cast_type, sql_type, null) - @primary = @sqlserver_options[:is_identity] || @sqlserver_options[:is_primary] + @sqlserver_options = sqlserver_options.symbolize_keys + @default_function = @sqlserver_options[:default_function] end - class << self - def string_to_binary(value) - "0x#{value.unpack("H*")[0]}" - end - - def binary_to_string(value) - if value.encoding != Encoding::ASCII_8BIT - value = value.force_encoding(Encoding::ASCII_8BIT) - end - value + def sql_type_for_statement + if is_integer? || is_real? + sql_type.sub(/\((\d+)?\)/, '') + else + sql_type end end @@ -41,50 +36,6 @@ def is_real? @sql_type =~ /real/i end - def sql_type_for_statement - if is_integer? || is_real? - sql_type.sub(/\((\d+)?\)/, '') - else - sql_type - end - end - - def default_function - @sqlserver_options[:default_function] - end - - - private - - def extract_limit(sql_type) - case sql_type - when /^smallint/i - 2 - when /^int/i - 4 - when /^bigint/i - 8 - when /\(max\)/, /decimal/, /numeric/ - nil - else - super - end - end - - def simplified_type(field_type) - case field_type - when /real/i then :float - when /money/i then :decimal - when /image/i then :binary - when /bit/i then :boolean - when /uniqueidentifier/i then :uuid - when /datetime/i then :datetime - when /varchar\(max\)/ then :text - when /timestamp/ then :binary - else super - end - end - end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index b65b7708e..965c89368 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -8,7 +8,6 @@ require 'models/sqlserver/fk_test_has_pk' require 'models/sqlserver/fk_test_has_fk' require 'models/sqlserver/customers_view' -require 'models/sqlserver/sql_server_chronic' require 'models/sqlserver/string_defaults_big_view' require 'models/sqlserver/string_defaults_view' require 'models/sqlserver/topic' @@ -211,58 +210,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'For chronic data types' do - - context 'with a usec' do - - setup do - @time = Time.now - @db_datetime_003 = '2012-11-08 10:24:36.003' - @db_datetime_123 = '2012-11-08 10:24:36.123' - @all_datetimes = [@db_datetime_003, @db_datetime_123] - @all_datetimes.each do |datetime| - @connection.execute("INSERT INTO [sql_server_chronics] ([datetime]) VALUES('#{datetime}')") - end - end - - teardown do - @all_datetimes.each do |datetime| - @connection.execute("DELETE FROM [sql_server_chronics] WHERE [datetime] = '#{datetime}'") - end - end - - context 'finding existing DB objects' do - - should 'find 003 millisecond in the DB with before and after casting' do - existing_003 = SqlServerChronic.find_by_datetime!(@db_datetime_003) - assert_equal @db_datetime_003, existing_003.datetime_before_type_cast if existing_003.datetime_before_type_cast.is_a?(String) - assert_equal 3000, existing_003.datetime.usec, 'A 003 millisecond in SQL Server is 3000 microseconds' - end - - should 'find 123 millisecond in the DB with before and after casting' do - existing_123 = SqlServerChronic.find_by_datetime!(@db_datetime_123) - assert_equal @db_datetime_123, existing_123.datetime_before_type_cast if existing_123.datetime_before_type_cast.is_a?(String) - assert_equal 123000, existing_123.datetime.usec, 'A 123 millisecond in SQL Server is 123000 microseconds' - end - - end - - context 'saving new datetime objects' do - - should 'truncate 123456 usec to just 123 in the DB cast back to 123000' do - Time.any_instance.stubs iso8601: "2011-07-26T12:29:01.123-04:00" - saved = SqlServerChronic.create!(datetime: @time).reload - saved.reload - assert_equal '123', saved.datetime_before_type_cast.split('.')[1] if saved.datetime_before_type_cast.is_a?(String) - assert_equal 123000, saved.datetime.usec - end - - end - - end - - end - context 'For identity inserts' do setup do @@ -371,23 +318,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context "date and time values" do - - setup do - @date = Date.parse '2000-01-01' - @column = SqlServerChronic.columns_hash['datetime'] - end - - context "on a sql datetime column" do - - should "call quoted_datetime and surrounds its result with single quotes" do - assert_equal "'01-01-2000'", @connection.quote(@date, @column) - end - - end - - end - end context "#quoted_datetime" do diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index a9bc76119..d71383352 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,370 +1,613 @@ +# encoding: UTF-8 require 'cases/helper_sqlserver' -require 'models/binary' -require 'models_sqlserver/float_data' -require 'models_sqlserver/numeric_data' -require 'models_sqlserver/sql_server_chronic' -require 'models_sqlserver/sql_server_edge_schema' -require 'models_sqlserver/sql_server_string' -require 'models_sqlserver/sql_server_unicode' -require 'models_sqlserver/table_with_real_column' -require 'models_sqlserver/topic' -require "cases/migration/helper" - -class ColumnTestSqlserver < ActiveRecord::TestCase - - setup do - @connection = ActiveRecord::Base.connection - @column_klass = ActiveRecord::ConnectionAdapters::SQLServerColumn - end - - should 'return real_number as float' do - assert_equal :float, TableWithRealColumn.columns_hash["real_number"].type - end - - should 'return correct null, limit, and default for Topic' do - tch = Topic.columns_hash - assert_equal false, tch['id'].null - assert_equal true, tch['title'].null - assert_equal 255, tch['author_name'].limit - assert_equal true, tch['approved'].default - assert_equal 0, tch['replies_count'].default - end - - context 'For binary columns' do - - setup do - @binary_string = "GIF89a\001\000\001\000\200\000\000\377\377\377\000\000\000!\371\004\000\000\000\000\000,\000\000\000\000\001\000\001\000\000\002\002D\001\000;" - @saved_bdata = Binary.create!(data: @binary_string) - end - - should 'read and write binary data equally' do - assert_equal @binary_string, Binary.find(@saved_bdata).data - end - - should 'have correct attributes' do - column = Binary.columns_hash['data'] - assert_equal :binary, column.type - assert_equal @connection.native_binary_database_type, column.sql_type - assert_equal nil, column.limit - end - - should 'quote data for sqlserver with literal 0x prefix' do - # See the output of the stored procedure: 'exec sp_datatype_info' - sqlserver_encoded_bdata = "0x47494638396101000100800000ffffff00000021f90400000000002c00000000010001000002024401003b" - assert_equal sqlserver_encoded_bdata, @column_klass.string_to_binary(@binary_string) - end - - end - - context 'For string columns' do - - setup do - @char = SqlServerString.columns_hash['char'] - @char10 = SqlServerString.columns_hash['char_10'] - @varcharmax = SqlServerString.columns_hash['varchar_max'] - @varcharmax10 = SqlServerString.columns_hash['varchar_max_10'] - end - - should 'have correct simplified types' do - assert_equal :string, @char.type - assert_equal :string, @char10.type - assert_equal :text, @varcharmax.type, @varcharmax.inspect - assert_equal :text, @varcharmax10.type, @varcharmax10.inspect - end - - should 'have correct #sql_type per schema definition' do - assert_equal 'char(1)', @char.sql_type, 'Specifing a char type with no limit is 1 by SQL Server standards.' - assert_equal 'char(10)', @char10.sql_type, @char10.inspect - assert_equal 'varchar(max)', @varcharmax.sql_type, 'A -1 limit should be converted to max (max) type.' - assert_equal 'varchar(max)', @varcharmax10.sql_type, 'A -1 limit should be converted to max (max) type.' - end - - should 'have correct #limit per schema definition' do - assert_equal 1, @char.limit - assert_equal 10, @char10.limit - assert_equal nil, @varcharmax.limit, 'Limits on max types are moot and we should let rails know that.' - assert_equal nil, @varcharmax10.limit, 'Limits on max types are moot and we should let rails know that.' - end - - end - - context 'For all national/unicode columns' do - - setup do - @nchar = SqlServerUnicode.columns_hash['nchar'] - @nvarchar = SqlServerUnicode.columns_hash['nvarchar'] - @ntext = SqlServerUnicode.columns_hash['ntext'] - @ntext10 = SqlServerUnicode.columns_hash['ntext_10'] - @nchar10 = SqlServerUnicode.columns_hash['nchar_10'] - @nvarchar100 = SqlServerUnicode.columns_hash['nvarchar_100'] - @nvarcharmax = SqlServerUnicode.columns_hash['nvarchar_max'] - @nvarcharmax10 = SqlServerUnicode.columns_hash['nvarchar_max_10'] - end - - should 'all respond true to #is_utf8?' do - SqlServerUnicode.columns_hash.except('id').values.each do |column| - assert column.is_utf8?, "This column #{column.inspect} should have been a unicode column." - end - end - - should 'have correct simplified types' do - assert_equal :string, @nchar.type - assert_equal :string, @nvarchar.type - assert_equal :text, @ntext.type - assert_equal :text, @ntext10.type - assert_equal :string, @nchar10.type - assert_equal :string, @nvarchar100.type - assert_equal :text, @nvarcharmax.type, @nvarcharmax.inspect - assert_equal :text, @nvarcharmax10.type, @nvarcharmax10.inspect - end - - should 'have correct #sql_type per schema definition' do - assert_equal 'nchar(1)', @nchar.sql_type, 'Specifing a nchar type with no limit is 1 by SQL Server standards.' - assert_equal 'nvarchar(255)', @nvarchar.sql_type, 'Default nvarchar limit is 255.' - assert_equal 'ntext', @ntext.sql_type, 'Nice and clean ntext, limit means nothing here.' - assert_equal 'ntext', @ntext10.sql_type, 'Even a next with a limit of 10 specified will mean nothing.' - assert_equal 'nchar(10)', @nchar10.sql_type, 'An nchar with a limit of 10 needs to have it show up here.' - assert_equal 'nvarchar(100)', @nvarchar100.sql_type, 'An nvarchar with a specified limit of 100 needs to show it.' - assert_equal 'nvarchar(max)', @nvarcharmax.sql_type, 'A -1 limit should be converted to max (max) type.' - assert_equal 'nvarchar(max)', @nvarcharmax10.sql_type, 'A -1 limit should be converted to max (max) type.' - end - - should 'have correct #limit per schema definition' do - assert_equal 1, @nchar.limit - assert_equal 255, @nvarchar.limit - assert_equal nil, @ntext.limit, 'An ntext column limit is moot, it is a fixed variable length' - assert_equal 10, @nchar10.limit - assert_equal 100, @nvarchar100.limit - assert_equal nil, @nvarcharmax.limit, 'Limits on max types are moot and we should let rails know that.' - assert_equal nil, @nvarcharmax10.limit, 'Limits on max types are moot and we should let rails know that.' - end - - end - - context 'For datetime columns' do - - setup do - @date = SqlServerChronic.columns_hash['date'] - @time = SqlServerChronic.columns_hash['time'] - @datetime = SqlServerChronic.columns_hash['datetime'] - @smalldatetime = SqlServerChronic.columns_hash['smalldatetime'] - @timestamp = SqlServerChronic.columns_hash['timestamp'] - @ss_timestamp = SqlServerChronic.columns_hash['ss_timestamp'] - end - - should 'have correct simplified type for uncast datetime' do - assert_equal :datetime, @datetime.type - end - - should 'use correct #sql_type for different sql server versions' do - assert_equal 'datetime', @datetime.sql_type - if sqlserver_2005? - assert_equal 'datetime', @date.sql_type - assert_equal 'datetime', @time.sql_type - else - assert_equal 'date', @date.sql_type - assert_equal 'time', @time.sql_type - end - end - - should 'all be have nil #limit' do - assert_equal nil, @date.limit - assert_equal nil, @time.limit - assert_equal nil, @datetime.limit - end - - context 'with timestamps' do - - should 'use datetime sql type when using :timestamp in schema statements' do - assert_equal :datetime, @timestamp.type - assert_equal 'datetime', @timestamp.sql_type - end - should 'be able to use real sql server timestamp if you really want to' do - assert_equal :binary, @ss_timestamp.type - assert_equal 'timestamp', @ss_timestamp.sql_type - end unless sqlserver_azure? - - should 'return :timestamp as a binaryish string' do - chronic = SqlServerChronic.create!.reload - assert_match %r|\000|, chronic.ss_timestamp - end unless sqlserver_azure? - - end - - context 'For smalldatetime types' do - - should 'have created that type using rails migrations' do - assert_equal 'smalldatetime', @smalldatetime.sql_type - end - - should 'be able to insert column without truncation warnings or the like' do - SqlServerChronic.create! smalldatetime: Time.now - end - - should 'be able to update column without truncation warnings or the like' do - ssc = SqlServerChronic.create! smalldatetime: 2.days.ago - ssc.update_attributes! smalldatetime: Time.now - end - - end - - context 'which have coerced types' do - - setup do - christmas_08 = "2008-12-25".to_time - christmas_08_afternoon = "2008-12-25 12:00".to_time - @chronic_date = SqlServerChronic.create!(date: christmas_08).reload - @chronic_time = SqlServerChronic.create!(time: christmas_08_afternoon).reload - end - - should 'have an inheritable attribute ' do - assert SqlServerChronic.coerced_sqlserver_date_columns.include?('date') unless sqlserver_2008? - end - - should 'have column and objects cast to date' do - assert_equal :date, @date.type, "This column: \n#{@date.inspect}" - assert_instance_of Date, @chronic_date.date - end - - should 'have column objects cast to time' do - assert_equal :time, @time.type, "This column: \n#{@time.inspect}" - assert_instance_of Time, @chronic_time.time - end - - end - - end - - context 'For decimal and numeric columns' do - - setup do - @bank_balance = NumericData.columns_hash['bank_balance'] - @big_bank_balance = NumericData.columns_hash['big_bank_balance'] - @world_population = NumericData.columns_hash['world_population'] - @my_house_population = NumericData.columns_hash['my_house_population'] - end - - should 'have correct simplified types' do - assert_equal :decimal, @bank_balance.type - assert_equal :decimal, @big_bank_balance.type - assert_equal :integer, @world_population.type, 'Since #extract_scale == 0' - assert_equal :integer, @my_house_population.type, 'Since #extract_scale == 0' - end - - should 'have correct #sql_type' do - assert_equal 'decimal(10,2)', @bank_balance.sql_type - assert_equal 'decimal(15,2)', @big_bank_balance.sql_type - assert_equal 'decimal(10,0)', @world_population.sql_type - assert_equal 'decimal(2,0)', @my_house_population.sql_type - end - - should 'have correct #limit' do - assert_equal nil, @bank_balance.limit - assert_equal nil, @big_bank_balance.limit - assert_equal nil, @world_population.limit - assert_equal nil, @my_house_population.limit - end - - should 'return correct precisions and scales' do - assert_equal [10,2], [@bank_balance.precision, @bank_balance.scale] - assert_equal [15,2], [@big_bank_balance.precision, @big_bank_balance.scale] - assert_equal [10,0], [@world_population.precision, @world_population.scale] - assert_equal [2,0], [@my_house_population.precision, @my_house_population.scale] +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 column(name) ; SSTestDatatype.columns_hash[name] ; end + def assert_obj_set_and_save(attribute, value) + obj.send :"#{attribute}=", value + obj.send(attribute).must_equal value + obj.save! + obj.reload.send(attribute).must_equal value + end + + # http://msdn.microsoft.com/en-us/library/ms187752.aspx + + # Exact Numerics + + it 'int(4) PRIMARY KEY' do + col = column('id') + 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.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::BigInteger + type.type.must_equal :integer + type.must_be :number? + type.limit.must_equal 8 + assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 + assert_raises(RangeError) { new_obj.bigint = -9_223_372_036_854_775_809 } + assert_obj_set_and_save :bigint, 9_223_372_036_854_775_807 + assert_raises(RangeError) { new_obj.bigint = 9_223_372_036_854_775_808 } + end + + it 'int(4)' do + col = column('int') + col.sql_type.must_equal 'int(4)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Integer + type.type.must_equal :integer + type.must_be :number? + type.limit.must_equal 4 + assert_obj_set_and_save :int, -2_147_483_648 + assert_raises(RangeError) { new_obj.int = -2_147_483_649 } + assert_obj_set_and_save :int, 2_147_483_647 + assert_raises(RangeError) { new_obj.int = 2_147_483_648 } + end + + it 'smallint(2)' do + col = column('smallint') + col.sql_type.must_equal 'smallint(2)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::SmallInteger + type.type.must_equal :integer + type.must_be :number? + type.limit.must_equal 2 + assert_obj_set_and_save :smallint, -32_768 + assert_raises(RangeError) { new_obj.smallint = -32_769 } + assert_obj_set_and_save :smallint, 32_767 + assert_raises(RangeError) { new_obj.smallint = 32_768 } + end + + it 'tinyint(1)' do + col = column('tinyint') + col.sql_type.must_equal 'tinyint(1)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::TinyInteger + type.type.must_equal :integer + type.must_be :number? + type.limit.must_equal 1 + assert_obj_set_and_save :tinyint, 0 + assert_raises(RangeError) { new_obj.tinyint = -1 } + assert_obj_set_and_save :tinyint, 255 + assert_raises(RangeError) { new_obj.tinyint = 256 } + end + + it 'bit' do + col = column('bit') + col.sql_type.must_equal 'bit' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Boolean + type.type.must_equal :boolean + type.wont_be :number? + type.limit.must_equal nil + obj.bit = 0 + obj.bit.must_equal false + obj.save! + obj.reload.bit.must_equal false + obj.bit = '1' + obj.bit.must_equal true + obj.save! + obj.reload.bit.must_equal true + end + + it 'decimal(9,2)' do + col = column('decimal_9_2') + col.sql_type.must_equal 'decimal(9,2)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Decimal + type.type.must_equal :decimal + type.must_be :number? + type.limit.must_equal nil + type.precision.must_equal 9 + type.scale.must_equal 2 + obj.decimal_9_2 = '1234567.8901' + obj.decimal_9_2.must_equal BigDecimal('1234567.8901') + obj.save! + obj.reload.decimal_9_2.must_equal BigDecimal('1234567.89') + end + + it 'decimal(16,4)' do + col = column('decimal_16_4') + col.sql_type.must_equal 'decimal(16,4)' + type = col.cast_type + type.precision.must_equal 16 + type.scale.must_equal 4 + obj.decimal_16_4 = '1234567.8901001' + obj.decimal_16_4.must_equal BigDecimal('1234567.8901001') + obj.save! + obj.reload.decimal_16_4.must_equal BigDecimal('1234567.8901') + end + + it 'numeric(18,0)' do + col = column('numeric_18_0') + col.sql_type.must_equal 'numeric(18,0)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Decimal + type.type.must_equal :decimal + type.must_be :number? + type.limit.must_equal nil + type.precision.must_equal 18 + type.scale.must_equal 0 + obj.numeric_18_0 = '192.1' + obj.numeric_18_0.must_equal BigDecimal('192.1') + obj.save! + obj.reload.numeric_18_0.must_equal BigDecimal('192') + end + + it 'money' do + col = column('money') + col.sql_type.must_equal 'money' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Money + type.type.must_equal :money + type.must_be :number? + type.limit.must_equal nil + type.precision.must_equal 19 + type.scale.must_equal 4 + obj.money = '922337203685477.58061' + obj.money.must_equal BigDecimal('922337203685477.58061') + obj.save! + obj.reload.money.must_equal BigDecimal('922337203685477.5806') + end + + it 'smallmoney' do + col = column('smallmoney') + col.sql_type.must_equal 'smallmoney' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::SmallMoney + type.type.must_equal :money + type.must_be :number? + type.limit.must_equal nil + type.precision.must_equal 10 + type.scale.must_equal 4 + obj.smallmoney = '214748.36461' + obj.smallmoney.must_equal BigDecimal('214748.36461') + obj.save! + obj.reload.smallmoney.must_equal BigDecimal('214748.3646') + end + + # Approximate Numerics + # Float limits are adjusted to 24 or 53 by the database as per http://msdn.microsoft.com/en-us/library/ms173773.aspx + # Floats with a limit of <= 24 are reduced to reals by sqlserver on creation. + + it 'float(53)' do + col = column('float') + col.sql_type.must_equal 'float(53)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Float + type.type.must_equal :float + type.must_be :number? + type.limit.must_equal 53 + type.precision.must_equal nil + type.scale.must_equal nil + obj.float = '214748.36461' + obj.float.must_equal 214748.36461 + obj.save! + obj.reload.float.must_equal 214748.36461 + end + + it 'float(25)' do + col = column('float_25') + col.sql_type.must_equal 'float(53)' + type = col.cast_type + type.must_be_instance_of Type::Float + type.type.must_equal :float + type.limit.must_equal 53 + end + + it 'real(24)' do + col = column('real') + col.sql_type.must_equal 'real(24)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Float + type.type.must_equal :float + type.must_be :number? + type.limit.must_equal 24 + type.precision.must_equal nil + type.scale.must_equal nil + obj.float = '214748.36461' + obj.float.must_equal 214748.36461 + obj.save! + obj.reload.float.must_equal 214748.36461 + end + + # Date and Time + + it 'date' do + col = column('date') + col.sql_type.must_equal 'date' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Date + type.type.must_equal :date + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal nil + type.scale.must_equal nil + # Can cast strings. + obj.date = '0001-01-01' + obj.date.must_equal Date.civil(0001, 1, 1) + obj.save! + obj.reload.date.must_equal Date.civil(0001, 1, 1) + # Can keep and return assigned date. + assert_obj_set_and_save :date, Date.civil(1972, 04, 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.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.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::DateTime + type.type.must_equal :datetime + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal nil + type.scale.must_equal nil + # Can save .003 seconds and return again. + obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000) + obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" + obj.save! + obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + # Will cast to true DB value on attribute write, save and return again. + obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 234567) + obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.save! + obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" + end + + it 'smalldatetime' do + col = column('smalldatetime') + col.sql_type.must_equal 'smalldatetime' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::SmallDateTime + type.type.must_equal :datetime + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal nil + type.scale.must_equal nil + # Will remove fractional seconds and return again. + obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) + obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" + obj.save! + obj.reload.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + end + + it 'time(2)' do + col = column('time_2') + col.sql_type.must_equal 'time(2)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Time + type.type.must_equal :time + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 2 + type.scale.must_equal nil + # Always uses ActiveRecord's 2000-01-01 convention too. + obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0) + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + obj.save! + obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + # Midnight the beggining of the day. + obj.time_2 = Time.utc(2000, 01, 01).midnight.change(usec: 0) + obj.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) + obj.save! + obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) + # The end of day. + obj.time_2 = Time.utc(2000, 01, 01).end_of_day.change(usec: 0) + obj.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) + obj.save! + obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) + # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) + obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000) + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + obj.save! + obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.reload.time_2.usec}> vs <30000>" + # Time's #usec precision (below 2 precision) + obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000) + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + obj.save! + obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.reload.time_2.usec}> vs <0>" + end + + it 'time(7)' do + col = column('time_7') + col.sql_type.must_equal 'time(7)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Time + type.type.must_equal :time + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Time's #usec precision (low) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <300>" + obj.save! + obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.reload.time_7.usec}> vs <300>" + # Time's #usec precision (high) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + obj.save! + obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.reload.time_7.usec}> vs <234567>" + end + + # Character Strings + + it 'char(10)' do + col = column('char_10') + col.sql_type.must_equal 'char(10)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Char + type.type.must_equal :string + type.wont_be :number? + type.limit.must_equal 10 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + obj.char_10 = '012345' + obj.char_10.strip.must_equal '012345' + obj.save! + obj.reload.char_10.strip.must_equal '012345' + end + + it 'varchar(50)' do + col = column('varchar_50') + col.sql_type.must_equal 'varchar(50)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Varchar + type.type.must_equal :string + type.wont_be :number? + type.limit.must_equal 50 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :varchar_50, 'Hello World' + end + + it 'varchar(max)' do + col = column('varchar_max') + col.sql_type.must_equal 'varchar(max)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::VarcharMax + type.type.must_equal :text + type.wont_be :number? + type.limit.must_equal 2_147_483_647 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :varchar_max, 'Hello World' + end + + it 'text' do + col = column('text') + col.sql_type.must_equal 'text' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Text + type.type.must_equal :text + type.wont_be :number? + type.limit.must_equal 2_147_483_647 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :text, 'Hello World' + end + + # Unicode Character Strings + + it 'nchar(10)' do + col = column('nchar_10') + col.sql_type.must_equal 'nchar(10)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::UnicodeChar + type.type.must_equal :string + type.wont_be :number? + type.limit.must_equal 10 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + obj.nchar_10 = "五六" + obj.nchar_10.strip.must_equal "五六" + obj.save! + obj.reload.nchar_10.strip.must_equal "五六" + end + + it 'nvarchar(50)' do + col = column('nvarchar_50') + col.sql_type.must_equal 'nvarchar(50)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::UnicodeVarchar + type.type.must_equal :string + type.wont_be :number? + type.limit.must_equal 50 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :nvarchar_50, "一二34五六" + end + + it 'nvarchar(max)' do + col = column('nvarchar_max') + col.sql_type.must_equal 'nvarchar(max)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::UnicodeVarcharMax + type.type.must_equal :text + type.wont_be :number? + type.limit.must_equal 2_147_483_647 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :nvarchar_max, "一二34五六" + end + + it 'ntext' do + col = column('ntext') + col.sql_type.must_equal 'ntext' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::UnicodeText + type.type.must_equal :text + type.wont_be :number? + type.limit.must_equal 2_147_483_647 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + assert_obj_set_and_save :ntext, "一二34五六" + end + + # 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 } } + + it 'binary(49)' do + col = column('binary_49') + col.sql_type.must_equal 'binary(49)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Binary + type.type.must_equal :binary + type.wont_be :number? + type.limit.must_equal 49 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + binary_data.encoding.must_equal Encoding::BINARY + binary_data.length.must_equal 49 + obj.binary_49 = binary_data + obj.binary_49.must_equal binary_data + obj.save! + obj.reload.binary_49.must_equal binary_data + end + + it 'varbinary(49)' do + col = column('varbinary_49') + col.sql_type.must_equal 'varbinary(49)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Varbinary + type.type.must_equal :binary + type.wont_be :number? + type.limit.must_equal 49 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + binary_data_20 = binary_data.to(20) + binary_data_20.encoding.must_equal Encoding::BINARY + obj.varbinary_49 = binary_data_20 + obj.varbinary_49.must_equal binary_data_20 + obj.save! + obj.reload.varbinary_49.must_equal binary_data_20 + end + + it 'varbinary(max)' do + col = column('varbinary_max') + col.sql_type.must_equal 'varbinary(max)' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::VarbinaryMax + type.type.must_equal :binary + type.wont_be :number? + type.limit.must_equal 2_147_483_647 + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + binary_data.encoding.must_equal Encoding::BINARY + assert_obj_set_and_save :varbinary_max, binary_data end end - context 'For float columns' do - # NOTE: float limits are adjusted to 24 or 53 by the database as per - # http://msdn.microsoft.com/en-us/library/ms173773.aspx - # NOTE: floats with a limit of <= 24 are reduced to reals by sqlserver on creation - - setup do - @temperature = FloatData.columns_hash['temperature'] - @freezing = FloatData.columns_hash['temperature_8'] - @mild = FloatData.columns_hash['temperature_24'] - @beach = FloatData.columns_hash['temperature_32'] - @desert = FloatData.columns_hash['temperature_53'] - end - - should 'have correct simplified types' do - assert_equal :float, @temperature.type - assert_equal :float, @freezing.type - assert_equal :float, @mild.type - assert_equal :float, @beach.type - assert_equal :float, @desert.type - end - - should 'have correct #sql_type' do - assert_equal 'real(24)', @temperature.sql_type - assert_equal 'real(24)', @freezing.sql_type - assert_equal 'real(24)', @mild.sql_type - assert_equal 'float(53)', @beach.sql_type - assert_equal 'float(53)', @desert.sql_type - end - - should 'have correct #limit' do - assert_equal 24, @temperature.limit - assert_equal 24, @freezing.limit - assert_equal 24, @mild.limit - assert_equal 53, @beach.limit - assert_equal 53, @desert.limit - end - - should 'return nil precisions and scales' do - assert_equal [nil,nil], [@temperature.precision, @temperature.scale] - assert_equal [nil,nil], [@freezing.precision, @freezing.scale] - assert_equal [nil,nil], [@mild.precision, @mild.scale] - assert_equal [nil,nil], [@beach.precision, @beach.scale] - assert_equal [nil,nil], [@desert.precision, @desert.scale] - end - - end - - context 'For tinyint columns' do - - setup do - @tinyint = SqlServerEdgeSchema.columns_hash['tinyint'] - end - - should 'be all it should be' do - assert_equal :integer, @tinyint.type - assert_nil @tinyint.scale - assert_equal 'tinyint(1)', @tinyint.sql_type - end - - end -end - -module ActiveRecord - class Migration - class ColumnsTest < ActiveRecord::TestCase - include ActiveRecord::Migration::TestHelper - - COERCED_TESTS = [:test_remove_column_with_multi_column_index] - # The if current_adapter? conditional below should also contain :SQLServerAdapter. - # Until that patch is made to rails we are preventing this test from running in this gem. - - include ARTest::Sqlserver::CoercedTest - - #the only thing we changed in this method was to add :SQLServerAdapter - def test_coerced_remove_column_with_multi_column_index - 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 - - assert_equal 1, connection.indexes('test_models').size - remove_column("test_models", "hat_size") - - # Every database and/or database adapter has their own behavior - # if it drops the multi-column index when any of the indexed columns dropped by remove_column. - if current_adapter?(:PostgreSQLAdapter, :OracleAdapter, :SQLServerAdapter) - assert_equal [], connection.indexes('test_models').map(&:name) - else - assert_equal ['index_test_models_on_hat_style_and_hat_size'], connection.indexes('test_models').map(&:name) - end - end - end - end end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 21e69e236..5c7e5b2b4 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -52,3 +52,5 @@ def with_auto_connect(boolean) end end + +Dir["#{ARTest::SQLServer.test_root_sqlserver}/models/**/*.rb"].each { |f| require f } diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index ac1edc770..a9275ebf1 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,6 +1,5 @@ require 'cases/helper_sqlserver' require 'models/person' -require 'models/sqlserver/person' class MigrationTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index dd3133e7c..fb9f4b756 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,7 +1,6 @@ require 'cases/helper_sqlserver' require 'models/person' require 'models/reader' -require 'models/sqlserver/person' class PessimisticLockingTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/unicode_test_sqlserver.rb b/test/cases/unicode_test_sqlserver.rb deleted file mode 100644 index 5f67db4db..000000000 --- a/test/cases/unicode_test_sqlserver.rb +++ /dev/null @@ -1,48 +0,0 @@ -# encoding: UTF-8 -require 'cases/helper_sqlserver' - -class UnicodeTestSQLServer < ActiveRecord::TestCase - - - context 'Testing basic saves and unicode limits' do - - should 'save and reload simple nchar string' do - assert nchar_data = SqlServerUnicode.create!(nchar: 'A') - assert_equal 'A', SqlServerUnicode.find(nchar_data.id).nchar - end - - should 'save and reload simple nvarchar(max) string' do - test_string = 'Ken Collins' - assert nvarcharmax_data = SqlServerUnicode.create!(nvarchar_max: test_string) - assert_equal test_string, SqlServerUnicode.find(nvarcharmax_data.id).nvarchar_max - end - - should 'not work with ANSI_WARNINGS for string truncation' do - SqlServerUnicode.create!(nchar_10: '01234567891') - end - - end - - context 'Testing unicode data' do - - setup do - @unicode_data = "\344\270\200\344\272\21434\344\272\224\345\205\255" # "一二34五六" - end - - should 'insert and retrieve unicode data' do - assert data = SqlServerUnicode.create!(nvarchar: @unicode_data) - if connection_mode_dblib? - assert_equal "一二34五六", data.reload.nvarchar - elsif connection_mode_odbc? - assert_equal "一二34五六", data.reload.nvarchar, 'perhaps you are not using the utf8 odbc that does this legwork' - else - raise 'need to add a case for this' - end - assert_equal Encoding.find('UTF-8'), data.nvarchar.encoding - end - - end - - - -end diff --git a/test/fixtures/1px.gif b/test/fixtures/1px.gif new file mode 100644 index 0000000000000000000000000000000000000000..f32722af9d01435d7bbc945f2f7283fa0227f007 GIT binary patch literal 49 ycmZ?wbhEHbWMp7un8?8J9}NC6GBGePDE?$&5n=expaWzBWf>V5n3!A`8LR<&whSHs literal 0 HcmV?d00001 diff --git a/test/models/float_data.rb b/test/models/float_data.rb deleted file mode 100644 index b7ff8ccdb..000000000 --- a/test/models/float_data.rb +++ /dev/null @@ -1,3 +0,0 @@ -class FloatData < ActiveRecord::Base - self.table_name = 'float_data' -end \ No newline at end of file diff --git a/test/models/person.rb b/test/models/person.rb deleted file mode 100644 index 04aa163ed..000000000 --- a/test/models/person.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Person < ActiveRecord::Base - coerce_sqlserver_date :favorite_day -end diff --git a/test/models/sql_server_chronic.rb b/test/models/sql_server_chronic.rb deleted file mode 100644 index 45b1cdfd1..000000000 --- a/test/models/sql_server_chronic.rb +++ /dev/null @@ -1,5 +0,0 @@ -class SqlServerChronic < ActiveRecord::Base - coerce_sqlserver_date :date - coerce_sqlserver_time :time - default_timezone = :utc -end diff --git a/test/models/sqlserver/datatype.rb b/test/models/sqlserver/datatype.rb new file mode 100644 index 000000000..5df536c52 --- /dev/null +++ b/test/models/sqlserver/datatype.rb @@ -0,0 +1,3 @@ +class SSTestDatatype < ActiveRecord::Base + self.table_name = :datatypes +end diff --git a/test/models/topic.rb b/test/models/topic.rb deleted file mode 100644 index 3e1099907..000000000 --- a/test/models/topic.rb +++ /dev/null @@ -1,4 +0,0 @@ -class Topic < ActiveRecord::Base - coerce_sqlserver_date :last_read - coerce_sqlserver_time :bonus_time -end \ No newline at end of file diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql new file mode 100644 index 000000000..5b02d425f --- /dev/null +++ b/test/schema/datatypes/2012.sql @@ -0,0 +1,63 @@ + +IF EXISTS ( + SELECT TABLE_NAME + FROM INFORMATION_SCHEMA.TABLES + WHERE TABLE_NAME = N'datatypes' +) DROP TABLE [datatypes] + +CREATE TABLE [datatypes] ( + -- Exact Numerics + [id] [int] NOT NULL IDENTITY(1,1) PRIMARY KEY, + [bigint] [bigint] NULL, + [int] [int] NULL, + [smallint] [smallint] NULL, + [tinyint] [tinyint] NULL, + [bit] [bit] NULL, + [decimal_9_2] [decimal](9, 2) NULL, + [decimal_16_4] [decimal](16, 4) NULL, + [numeric_18_0] [numeric](18, 0) NULL, + [numeric_36_2] [numeric](36, 2) NULL, + [money] [money] NULL, + [smallmoney] [smallmoney] NULL, + -- Approximate Numerics + [float] [float] NULL, + [float_25] [float](25) NULL, + [real] [real] NULL, + -- Date and Time + [date] [date] NULL, + [datetime] [datetime] NULL, + [smalldatetime] [smalldatetime] NULL, + [time_2] [time](2) NULL, + [time_7] [time](7) NULL, + -- Character Strings + [char_10] [char](10) NULL, + [varchar_50] [varchar](50) NULL, + [varchar_max] [varchar](max) NULL, + [text] [text] NULL, + -- Unicode Character Strings + [nchar_10] [nchar](10) NULL, + [nvarchar_50] [nvarchar](50) NULL, + [nvarchar_max] [nvarchar](max) NULL, + [ntext] [ntext] NULL, + -- Binary Strings + [binary_49] [binary](49) NULL, + [varbinary_49] [varbinary](49) NULL, + [varbinary_max] [varbinary](max) NULL, +) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] + + +-- Date and Time (TODO) +-- -------------------- +-- [datetime2_7] [datetime2](7) NULL, +-- [datetimeoffset_2] [datetimeoffset](2) NULL, +-- [datetimeoffset_7] [datetimeoffset](7) NULL, +-- +-- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 71, '0001-01-01T00:00:00.0000000Z' ) +-- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 72, '1984-01-24T04:20:00.0000000-08:00' ) +-- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 73, '9999-12-31T23:59:59.9999999Z' ) +-- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 81, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.00 -08:00 +-- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 82, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.00 +00:00 +-- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 83, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.99 +00:00 +-- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 84, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.0000000 -08:00 +-- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 85, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.0000000 +00:00 +-- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 86, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.9999999 +00:00 diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 73d692604..43111e3e6 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -2,6 +2,13 @@ execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) + create_table :datatypes_migration, force: true do |t| + t.column :real, :real + end + + + + create_table :UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string t.column :COLUMN2, :integer @@ -15,10 +22,6 @@ t.float :temperature_53, limit: 53 end - create_table :table_with_real_columns, force: true do |t| - t.column :real_number, :real - end - create_table :defaults, force: true do |t| t.column :positive_integer, :integer, default: 1 t.column :negative_integer, :integer, default: -1 From c7f6a557723932d58b08b2587db34f8975ffb7d4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 13:53:15 -0500 Subject: [PATCH 0204/1412] Turn on prepared statements again. * Changed bin param integer to start with `@0` so each with index always works. * The `supports_statement_cache?` is still in there for AR's testing reflection now. --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 + lib/arel/visitors/sqlserver.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a58dafd0b..e7ceb6d20 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -48,6 +48,7 @@ def initialize(connection, logger, pool, config) # AbstractAdapter Responsibility @schema_cache = SQLServer::SchemaCache.new self @visitor = Arel::Visitors::SQLServer.new self + @prepared_statements = true # Our Responsibility @config = config @connection_options = config diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 9c2b4d251..02957347f 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -13,7 +13,7 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) def visit_Arel_Nodes_BindParam o, collector - collector.add_bind(o) { |i| "@#{i}" } + collector.add_bind(o) { |i| "@#{i-1}" } end def visit_Arel_Nodes_Offset o, collector From d2f1cc9e7ac98812e90b343698fdb7ffc8100aa3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 14:06:32 -0500 Subject: [PATCH 0205/1412] Update CHANGELOG. --- CHANGELOG.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b81b4efa8..401cedd0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,18 @@ ## v4.2.0 -* ... +#### Added + +* New `ActiveRecord::Type` objects. See `active_record/connection_adapters/sqlserver/type` dir. +* New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. + +#### Changed + +* Complete rewrite of our Arel visitor. Focuing on 2012 and upward so we can make FETCH happen. +* Testing enhancements. + * Guard support, check our Guardfile. + * Use `ARTest` namespace with `SQLServer` module for our helpers/objects. + * Simple 2012 schmea addition and extensive column/type_cast object tests. ## v4.1.0 From 2554ffe15b3faaf6e8255c225230d9122271f6d1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 14:08:04 -0500 Subject: [PATCH 0206/1412] Update a few copyright years. --- MIT-LICENSE | 10 +++++----- README.md | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index e75026d4f..ad08c4535 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,5 +1,5 @@ -Copyright (c) 2008-2011 - +Copyright (c) 2008-2015 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including @@ -7,14 +7,14 @@ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index ec2047a1c..a20cf9346 100644 --- a/README.md +++ b/README.md @@ -230,5 +230,5 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- ## License -Copyright © 2008-2014. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. +Copyright © 2008-2015. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. From 27ab2ca295e4795dd3767b7f114cc6401d4a72db Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 14:48:16 -0500 Subject: [PATCH 0207/1412] Clean up CHANGELOG. Will focus on current minor release only. --- CHANGELOG.md | 427 --------------------------------------------------- 1 file changed, 427 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 401cedd0d..4f6ce87cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,433 +29,6 @@ ## v3.2.12 -* Revert string_to_binary changes in 457af60e. Fixes #273. -## v3.2.11 - -* Handle "No such column" when renaming some columns in the migrations. Fixes #237. Thanks @michelgrootjans. -* Update regex for `RecordNotUnique` exception. Fixes #257. Thanks @pbatorre. -* Pass tds_version to TinyTDS. Fixes #233. Thanks @denispeplin. -* Use CONCAT_NULL_YIELDS_NULL for all TinyTDS connections. Fixes #262. Thanks @noel. -* Fix for Azure SQL. Fixed #263. Thanks @eklipse2k8. -* Drop shoulda and use minitest-spec-rails!!! -* Test TinyTDS 0.6.0. -* Fixed the unit tests due to changes in ActiveRecord that removes blank config values. -* Fixed explain tests that were failing due to changes in ExplainSubscriber, cause was regex -* Fixed change_column to update existing table column rows with new default value if there are any NULL values and the column does not accept nulls -* Fixed change_column to drop and add indexes if the colun type is changes -* Fixed string_to_binary and binary_to_string in some cases where the binary data is not UTF-8 -* Fixing generating profile report to create output dir if needed, and code change for printing report -* Adding ruby-prof to Gemfile, needed when running profile test cases -* Updating mocha to work with newer ActiveRecord test cases - - -## v3.2.10 - -* Remove connection defaults for host/username/password. Since we want to suppoert Windows Authentication and there are just to many possibilities. So we now have to be explicit. -* Remove really old TinyTDS warning. - - -## v3.2.9 - -* The #remove_default_constraint uses #execute_procedure now. Fixes #223. Thanks @gicappa and @clintmiller. -* Mimic other adapters quoting for empty strings passed to integer columns. Fixes #164. -* Allow named parameters in stored procedures. Fixes #216. Thanks @sixfeetover. -* Make sure exclude [__rnt] table names form relation reflection. Fixes #219 and #221. Thanks @sphogan. - - -## v3.2.8 - -* Include VERSION in gemspec's files. - - -## v3.2.7 - -* Find VERSION in base file out of module namespace. Fixes #208 -* Better support for explain without sp_execute args. FIxes #207 - - -## v3.2.6 - -* Unique has_many associations with pagination now work. Fixes #209 - - -## v3.2.5 - -* Fix a few test from ActiveRecord 3.2.6 upgrade. -* Fix db_name usage bug in #column_definitions [Altonymous] - - -## v3.2.4 - -* Fixed schema reflection for identity columns using ODBC. Fixes #193. - - -## v3.2.3 - -* Fixed datetime quoting for ActiveSupport::TimeWithZone objects. Fixes #187 and #189. - - -## v3.2.2 - -* Fixes all known issues with cross database schema reflection. Fixes #185 [Chris Altman] -* Fix exists? with offset by patching visitor. Fixes #171 and Fixes #167 -* Set default text size to 2147483647 for TinyTDS connections. Fixes #181 -* Set @config ivar for 3rd party libs. Fixes #177 -* Make #sql_type_for_statement work for integers that may have empty parens or none at all. Fixes #175 - - -## v3.2.1 - -* Add explicit order-by clause for windowed results. Fixes #161. - - -## v3.2.0 - -* ActiveRecord explain (SHOWPLAN) support. http://youtu.be/ckb3YYZZZ2Q -* Remove our log_info_schema_queries config since we are not hooking properly into AR's 'SCHEMA' names. -* Properly use 'SCHEMA' name arguement in DB statements to comply with ActiveRecord::ExplainSubscriber::IGNORED_PAYLOADS. -* Make use of the new ConnectionAdapters::SchemaCache for our needs. -* New SQLServer::Utils class for out helpers. Moved table name unquotes there. - - -## v3.1.5 - -* Better support for orders with an expression. Fixes #155. [Jason Frey, Joe Rafaniello] - - -## v3.1.4 - -* Use INFORMATION_SCHEMA.KEY_COLUMN_USAGE for schema reflection speed. Fixes #125. [Wüthrich Hannes @hwuethrich] -* New deadlock victim retry using the #retry_deadlock_victim config. [Jason Frey, Joe Rafaniello] -* Renamed #with_auto_reconnect to #with_sqlserver_error_handling now that it handles both dropped connections and deadlock victim errors. Fixes #150 [Jason Frey, Joe Rafaniello] -* Add activity_stats method that mimics the SQL Server Activity Monitor. Fixes #146 [Jason Frey, Joe Rafaniello] -* Add methods for sqlserver's #product_version, #product_level, #edition and include them in inspect. Fixes #145 [Jason Frey, Joe Rafaniello] -* Handle statements that cannot be retried on a new database connection by not reconnecting. Fixes #147 [Jason Frey, Joe Rafaniello] -* Added connection#spid for debugging. Fixes #144 [Jason Frey, Joe Rafaniello] -* Add ENV['TEST_FILES'] to Rakefile for easy single case tests. [Jason Frey, Joe Rafaniello] -* Pass ActiveRecord tests. Made windowed distinct pass all orders to groups. - - test_limited_eager_with_multiple_order_columns - - test_limited_eager_with_order -* Pass AR tests by moving DISTINCT to GROUP BY in windowed SQL. - - test_count_eager_with_has_many_and_limit_and_high_offset - - test_eager_with_has_many_and_limit_and_high_offset - - -## v3.1.3 - -* Distinguish between identity and primary key key columns during schema reflection. Allows us to only do identity inserts when technically needed. Fixes #139 [chadcf] & [joncanady] - - -## v3.1.2 - -* Fix SQL Azure conflicts with DBCC useroptions. Use new #user_options_xyz methods. [kazamachi] -* Fix identity inserts for tables with natural PKs. [Gian Carlo Pace] -* Create a #configure_connection method that can be overridden. Think "SET TEXTSIZE...". -* Create a #configure_application_name method that can be overridden for unique TinyTDS app names -* Fixed the #finish_statement_handle to cancel the TinyTDS connection if needed. - - -## v3.1.1 - -* Make #rollback_db_transaction smarter. -* Provide a method to override for the quoted string prefix. Not a config because trumping this method will have drastically bad results. Fixes #124 -* Allow :limit/:offset to be used with fully qualified table and column in :select. - - -## v3.1.0 - -* Add support/test around handling of float/real column types [Lucas Maxwell] -* Make auto reconnect duration configurable. Fixes #109 [David Chelimsky] -* Quote most time objects to use ISO8601 format to be multi-language dateformat compatible. The [datetime] data type is automatically limited to milliseconds while [time] & [datetimeoffset] have full support. Even included a Date/Time ActiveSupport formatter that is used per the language settings of the connection. -* Include a visit_Arel_Nodes_UpdateStatement method in our Arel visitor to add a limit/top for update that has order and no limit/top. https://github.com/rails/rails/commit/787194ee43ab1fb0a7dc8bfbbfbd5079b047d833 -* Allow drop_database to be called even when DB does not exist. -* Remove totally broken ADONET connection mode. Want it back, submit a patch. -* Schema reflection now finds primary key for all occasions. Fixed #60 [Boško Ivanišević] -* Allow complex order objects to not be molested by our visitor overrides. Fixes #99 -* Default unicode datatypes! -* New #lowercase_schema_reflection configuration that allows you to downcase all tables and columns. Good for legacy databases. Fixes #86. Thanks @dmajkic. -* Rails 3.1 with prepared statement support. Uses "EXEC sp_executesql ..." for just about everything now. - - -## v3.0.15 - -* Way better schema support! Thanks to @ianic! Fixes #61 -* Warn of possible permission problems if "EXEC sp_helptext..." does not work view. Fixes #73. - - -## v3.0.13/3.0.14 - -* Allow TinyTDS/DBLIB mode to pass down :host/:port config options. - - -## v3.0.12 - -* Bug fix for previous TinyTDS lost connections. - - -## v3.0.11 - -* Azure compatibility. -* TinyTDS enhancements for lost connections. Default connection mode. - - -## v3.0.10 - -* Fix #rowtable_orders visitor helper to use first column if no pk column was found. -* Flatten sp_helpconstraint when looking for constraints just in case fks are present. Issue #64. -* Start to support 2011 code named "Denali". -* Limit and Offset can take SqlLiteral objects now. - - -## v3.0.9 - -* Fix array literal parsing bug for ruby 1.9. - - -## v3.0.8 - -* Support for ActiveRecord v3.0.3 and ARel v2.0.7 - - -## v3.0.7 - -* Properly quote table names when reflecting on views. -* Add "dead or not enabled" to :dblib's lost connection messages. - - -## v3.0.6 - -* Maintenance release. Lock down to ActiveRecord 3.0.1 using ARel 1.0.0. - - -## v3.0.5 - -* Fixed native database type memoization, now at connection instance level. Fix #execute_procedure for :dblib mode to return indifferent access rows too. -* Make login timeout and query timeout backward database.yml friendly for :dblib mode. - - -## v3.0.4 - -* Add multiple results set support with #execute_procedure for :dblib mode. [Ken Collins] -* Simplify encoding support. [Ken Collins] -* Add binary timestamp datatype handling. [Erik Bryn] - - -## v3.0.3 - -* Add TinyTDS/dblib connection mode. [Ken Collins] - - -## v3.0.2 - -* Fix DSN'less code. [Erik Bryn] - - -## v3.0.1 - -* Support DSN'less connections. Resolves ticket 38. -* Support upcoming ruby odbc 0.99992 - - -## v3.0.0 - -* Release rails 3 version! - - -## v2.3.8 - -* Properly quote all database names in rake helper methods. [Ken Collins] - - -## v2.3.7 - -* Correctly use :date/:time SQL types in 2008 [Ken Collins] - - -## v2.3.6 - -* Allow DNS's to not contain a database and use what is in database.yml [Marco Mastrodonato] -* Rake tasks methods for vanilla rails :db namespace parity. [Ken Collins] -* IronRuby integrated security fixes [Jimmy Schementi] - - -## v2.3.5 - -* Initial IronRuby ADONET connection mode support baked right in. Removed most &blockparameters, no handle/request object yielded anymore. Better abstraction and compliance er the ActiveRecord abstract adapter to not yielding handles for #execute and only forlow level #select. Better wrapping of all queries at lowest level in #log so exceptionsat anytime can be handled correctly by core AR. Critical for System::Data's commandreaders. Better abstraction for introspecting on #connection_mode. Added support forrunning singular test cases via TextMate's Command-R. [Ken Collins] -* Force a binary encoding on values coming in and out of those columns for ruby 1.9.Fixes ticket #33 [Jeroen Zwartepoorte] -* Using change_column will leave default if the type does not change or a new defaultis not included. Fixes issue #22. [Ransom Briggs] -* Use correct SP name for sp_MSforeachtable so any collation can get to it. [7to3] -* Qualify INFORMATION_SCHEMA.COLUMNS with a correct period DB name if present. -* Allow adapter to return multiple results sets, for example from stored procedures. [Chris Hall] - - -## v2.3.4 - -* For tables that named with schema(ex. rails.users), they could not get length of column. Column of varchar(40) gets length => nil. Ticket #27 & #15 [Ken Tachiya] -* Altered limited_update_conditions regex conditions, the .* would greedily fail if the where_sql had WHERE in a table or field, etc. [Ransom Briggs] -* Changing test to allow ENV['ARUNIT_DB_NAME'] as the database name for the test units. Matches up with AR conventions. [Ransom Briggs] - - -## v2.3.3 - -* Revert #ad83df82 and again cache column information at the connection's instance. The previous commit was causing all sorts of view and schema reflection problems. [Ken Collins] - - -## v2.3.2 - -* Insert queries that include the word "insert" as a partial column name with the word "id" as a value were falsely being matched as identity inserts. [Sean Caffery/bfabry] -* Delegate all low level #raw_connection calls to #raw_connection_run and #raw_connection_do which abstract out the low level modes in the connection options at that point. [Ken Collins] -* Remove DBI dependency and go straight ODBC for speed improvement [Erik Bryn] -* Leave order by alone when same column crosses two tables [Ransom Briggs] - - -## v2.3.0 (2009-12-01) - -* Table and column aliases can handle many. Resolves ticket #19 [stonegao] -* Coerce a few tests that were failing in 2.3.x [Ken Collins] -* Change column/view cache to happen at class level. Allows connection pool to share same caches as well as the ability to expire the caches when needed. Also fix change_column so that exceptions are not raised when the column contains an existing default. [Ken Collins] -* Allow query_requires_identity_insert? method to return quoted table name in situations where the INSERT parts are not quoted themselves. [Gary/iawgens, Richard Penwell, Ken Collins] -* Fixed namespace in calling test_sqlserver_odbc within test_unicode_types. [Gary/iawgens] -* Columns with multi-line defaults work correctly. [bfabry] - - -## v2.2.22 (2009-10-15) - -* Support Identity-key-column judgement on multiple schema environment [Ken Tachiya] -* Add support for tinyint data types. In MySQL all these types would be boolean, however in our adapter, they will use the full 1 => 255 Fixnum value as you would expect. [Ken Collins] - - -## v2.2.21 (2009-09-10) - -* Changes for gem best practices per http://weblog.rubyonrails.org/2009/9/1/gem-packaging-best-practices. Details of such are as follows: [Ken Collins] - - Removed rails-sqlserver-2000-2005-adapter.rb load file for old github usage. - - Move the core_ext directory to active_record/connection_adapters/sqlserver_adapter/core_ext - - Renamespace SQLServerDBI to ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::DBI - - Renamespace ActiveRecord::ConnectionAdapters::SQLServerActiveRecordExtensions to ActiveRecord::ConnectionAdapters::SQLServerCoreExtensions::ActiveRecord - - -## v2.2.20 (2009-09-10) - -* Implement a new remove_default_constraint method that uses sp_helpconstraint [Ken Collins] -* Use a lazy match in add_order_by_for_association_limiting! to allow sub selects to be used. Resolves ticket #11. -* Add default rake task back for testing. Runs the namespaced sqlserver:test_sqlserver_odbc. Resolves ticket #10 [Ken Collins] -* Default value detection in column_definitions is kinder to badly formatted, or long winded user defined functions, for default values. Resolves ticket #8 [Ken Collins] -* Make sure bigint SQL Server data type can be used and converted back to Bignum as expected. [Ken Collins] - - -## v2.2.19 (2009-06-19) - -* Leave quoted column names as is. Resolves ticket #36 [Vince Puzzella] -* Changing add_limit! in ActiveRecord::Base for SQLServer so that it passes through any scoped :order parameters. Resolves ticket #35 [Murray Steele] - - -## v2.2.18 (2009-06-05) - -* Column reflection on table name rescues LoadError and a few others. Resolves tickets #25 & #33 [Ken Collins] -* Added 2008 support. Resolves ticket #32 [Ken Collins] - - -## v2.2.17 (2009-05-14) - -* Add simplified type recognition for varchar(max) and nvarchar(max) under SQL Server 2005 to be a :text type. This ensures schema dumper does the right thing. Fixes ticket #30. [Ken Collins] -* Tested ruby 1.9, ruby-odbc 0.9996, and DBI 0.4.1. Also added correct support for UTF-8 character encoding going in and out of the DB. See before gist http://gist.github.com/111709 and after gist http://gist.github.com/111719 [Ken Collins] - - -## v2.2.16 (2009-04-21) - -* Make add_limit_offset! only add locking hints (for tally) when the :lock option is present. Added tests to make sure tally SQL is augmented correctly and tests to make sure that add_lock! is doing what it needs for deep sub selects in paginated results. [Ken Collins] -* Add auto reconnect support utilizing a new #with_auto_reconnect block. By default each query run through the adapter will automatically reconnect at standard intervals, logging attempts along the way, till success or the original exception bubbles up. See docs for more details. Resolves ticket #18 [Ken Collins] -* Update internal helper method #orders_and_dirs_set to cope with an order clause like "description desc". This resolves ticket #26 [Ken Collins] -* Provide support for running queries at different isolation levels using #run_with_isolation_level method that can take a block or not. Also implement a #user_options method that reflects on the current user session values. Resolves #20 [Murray Steele] - - -## v2.2.15 (2009-03-23) - -* Better add_lock! method that can add the lock to just about all the elements in the statement. This could be eager loaded associations, joins, etc. Done so that paginated results can easily add lock options for performance. Note, the tally count in add_limit_offset! use "WITH (NOLOCK)" explicitly as it can not hurt and is needed. [Ken Collins] - - -## v2.2.14 (2009-03-17) - -* Back passing tests on 2.2 work. Includes: (1) Created new test helpers that check ActiveRecordversion strings so we can conditionally run 2.2 and 2.3 tests. (2) Making TransactionTestSQLServer use Ship vsBird model. Also made it conditional run a few blocks for different versions of ActiveRecord. (3) PreviousJoinDependency#aliased_table_name_for is now only patched in ActiveRecord equal or greater than 2.3. [Ken Collins] -* Implement new savepoint support [Ken Collins]http://rails.lighthouseapp.com/projects/8994/tickets/383http://www.codeproject.com/KB/database/sqlservertransactions.aspx -* Coerce NestedScopingTest#test_merged_scoped_find to use correct regexp for adapter. [Ken Collins] -* Implement a custom ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation#aliased_table_name_formethod that uses a Regexp.escape so that table/column quoting does not get ignored. [Ken Collins] -* Implement #outside_transaction? and a new transaction test case to test some SQL Serverbasic support while implementing this method. Future home of some savepoint tests too. [Ken Collins] -* Rails2.3 - Coerced tests that ensure hash conditions on referenced tables are considered when eagerloading with limit/offset. Information on these changes and the ticket in rails are.http://github.com/rails/rails/commit/9a4d557713acb0fc8e80f61af18094034aca029ahttp://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1404-conditions_tables-doesnt-understand-condition-hashes -* Add coerced tests for true/false attributes in selects use SQL Server case statement. [Ken Collins] -* Making sure that smalldatetime types are OK to use. Also fixed a bug in the #view_information method thatchecks to see if a view definition is equal to 4000 chars, meaning that it is most likely truncated andneeds to use the backup method of sp_helptext to get it's view definition. [Ken Collins] - - -## v2.2.13 (2009-02-10) - -* Update #indexes to use unqualified table name. Fixes cases where users may decide to use tablename prefixes like 'dbo.'. [Ken Collins] - - -## v2.2.12 (2009-02-08) - -* Update table_exists? to work with qualified table names that may include an user prefix. [Ken Collins] - - -## v2.2.10/11 (2009-01-22) - -* Add a rails-sqlserver-2000-2005-adapter.rb file so that long :lib option for config.gem is no longer needed. - - -## v2.2.9 (2009-01-22) - -* Fixing a small bug in the deprecated DBI::Timestamp conversion so it correctly converts nanosecond wholenumbers to back to pre type cast SQL Server milliseconds, ultimately allow ruby's Time#usec which ismicroseconds to be correct. [Ken Collins] -* Sometimes views are more than 4000 chars long and will return NULL for the VIEW_DEFINITION. If so, usesp_helptext procedure as a backup method. [Ken Collins] - - -## v2.2.8 (2009-01-09) - -* Update execute_procedure method a bit to remove excess code. [Ken Collins] - - -## v2.2.7 (2009-01-09) - -* Created a connection#execute_procedure method that takes can take any number of ruby objects as variablesand quotes them according to the connection's rules. Also added an ActiveRecord::Base class level coreextension that hooks into this. It also checks if the connection responds to #execute_procedure and ifnot returns an empty array. [Ken Collins] -* Added a #enable_default_unicode_types class attribute access to make all new added or changed string typeslike :string/:text default to unicode/national data types. See the README for full details. Added a raketask that assists setting this to true when running tests. [Ken Collins] - - -## v2.2.6 (2009-01-08) - -* Introduced a bug in 2.2.5 in the #add_order! core ext for ActiveRecord. Fixed [Ken Collins] - - -## v2.2.5 (2009-01-04) - -* Added a log_info_schema_queries class attribute and make all queries to INFORMATION_SCHEMA silent bydefault. [Ken Collins] -* Fix millisecond support in datetime columns. ODBC::Timestamp incorrectly takes SQL Server millisecondsand applies them as nanoseconds. We cope with this at the DBI layer by using SQLServerDBI::Type::SQLServerTimestampclass to parse the before type cast value appropriately. Also update the adapters #quoted_date methodto work more simply by converting ruby's #usec milliseconds to SQL Server microseconds. [Ken Collins] -* Core extensions for ActiveRecord now reflect on the connection before doing SQL Server things. Nowthis adapter is compatible for using with other adapters. [Ken Collins] - - -## v2.2.4 (2008-12-05) - -* Fix a type left in #views_real_column_name. Also cache #view_information lookups. [Ken Collins] - - -## v2.2.3 (2008-12-05) - -* Changing back to using real table name in column_definitions. Makes sure views get back only the columnsthat are defined for them with correct names, etc. Now supporting views by looking for NULL default andthen if table name is a view, perform a targeted with sub select to the real table name and column nameto find true default. [Ken Collins] -* Ensure that add_limit_offset! does not alter sub queries. [Erik Bryn] - - -## v2.2.2 (2008-12-02) - -* Add support for view defaults by making column_definitions use real table name for schema info. [Ken Collins] -* Include version in connection method and inspection. [Ken Collins] - - -## v2.2.1 (2008-11-25) - -* Add identity insert support for views. Cache #views so that identity #table_name_or_views_table_namewill run quickly. [Ken Collins] -* Add views support. ActiveRecord classes can use views. The connection now has a #views method and #table_exists? will now fall back to checking views too. [Ken Collins] - - -## v2.2.0 (2008-11-21) - -* Release for rails 2.2.2. Many many changes. [Ken Collins], [Murray Steele], [Shawn Balestracci], [Joe Rafaniello] From 3b34effae28f46521f3ac03df053979977437e57 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 14:56:28 -0500 Subject: [PATCH 0208/1412] Remove native string and type configs. These are not needed and there all have easy escape hatches given AR migrations capacity to use pure SQL types. --- CHANGELOG.md | 21 ++-- README.md | 42 +------ .../sqlserver/schema_statements.rb | 10 +- .../connection_adapters/sqlserver_adapter.rb | 25 +---- test/cases/adapter_test_sqlserver.rb | 104 +++++++++--------- test/cases/helper_sqlserver.rb | 22 ---- 6 files changed, 73 insertions(+), 151 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6ce87cc..023cfcb8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,21 +14,26 @@ * Use `ARTest` namespace with `SQLServer` module for our helpers/objects. * Simple 2012 schmea addition and extensive column/type_cast object tests. +#### Deprecated -## v4.1.0 +* n/a -* Not sure if this even happened. Just got to 4.2.0 :) +#### Removed +* SQL Server versions < 2012 which do not support OFFSET and FETCH. http://bit.ly/1B5Bwsd +* The `enable_default_unicode_types` option. Default to national types all the time. Use SQL type name in migrations if needed. +* Native type configs for older DB support. Includes the following with new default value. + * native_string_database_type => `nvarchar` + * native_text_database_type => `nvarchar(max)` + * native_binary_database_type => `varbinary(max)` -## v4.0.0 -* Dropped support for ruby 1.8.7 -* Removed deadlock victim retry in favor of Isolation Level -* Removed auto_explain_threshold_in_seconds (not used in rails 4) +#### Fixed +* n/a -## v3.2.12 - +#### Security +* n/a diff --git a/README.md b/README.md index a20cf9346..e61663baa 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ -# ActiveRecord SQL Server Adapter. For SQL Server 2005 And Higher. + +# ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. **This project is looking for a new maintainers. Join the [discusion here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/364)** @@ -55,45 +56,6 @@ end Manually creating a `varchar(max)` is not necessary since this is the default type created when specifying a `:text` field. As time goes on we will be testing other SQL Server specific data types are handled correctly when created in a migration. -#### Native Text/String/Binary Data Type Accessor - -To pass the ActiveRecord tests we had to implement an class accessor for the native type created for `:text` columns. By default any `:text` column created by migrations will create a `varchar(max)` data type. This type can be queried using the SQL = operator and has plenty of storage space which is why we made it the default. If for some reason you want to change the data type created during migrations you can configure this line to your liking in a config/initializers file. - -```ruby -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'varchar(8000)' -``` - -Also, there is a class attribute setter for the native string database type. This is the same for all SQL Server versions, `varchar`. However it can be used instead of the #enable_default_unicode_types below for finer grain control over which types you want unicode safe when adding or changing the schema. - -```ruby -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = 'nvarchar' -``` - -By default any :binary column created by migrations will create a `varbinary(max)` data type. This too can be set using an initializer. - -```ruby -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_binary_database_type = 'image' -``` - -#### Setting Unicode Types As Default - -By default the adapter will use unicode safe data types for `:string` and `:text` types when defining/changing the schema! This was changed in version 3.1 since it is about time we push better unicode support and since we default to TinyTDS (DBLIB) which supports unicode queries and data. If you choose, you can set the following class attribute in a config/initializers file that will disable this behavior. - -```ruby -# Default -ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = true -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'nvarchar(max)' -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = 'nvarchar' - -# Disabled -ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = false -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = 'varchar(max)' -ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = 'varchar' -``` - -It is important to remember that unicode types in SQL Server have approximately half the storage capacity as their counter parts. So where a normal string would max out at (8000) a unicode string will top off at (4000). - - #### Force Schema To Lowercase Although it is not necessary, the Ruby convention is to use lowercase method names. If your database schema is in upper or mixed case, we can force all table and column names during the schema reflection process to be lowercase. Add this to your config/initializers file for the adapter. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 34216aa95..5d0a0cf0a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -163,16 +163,16 @@ def views def initialize_native_database_types { primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', - string: { name: native_string_database_type, limit: 255 }, - text: { name: native_text_database_type }, + string: { name: 'nvarchar', limit: 255 }, + text: { name: 'nvarchar(max)' }, integer: { name: 'int', limit: 4 }, float: { name: 'float', limit: 8 }, decimal: { name: 'decimal' }, datetime: { name: 'datetime' }, timestamp: { name: 'datetime' }, - time: { name: native_time_database_type }, - date: { name: native_date_database_type }, - binary: { name: native_binary_database_type }, + time: { name: 'time' }, + date: { name: 'date' }, + binary: { name: 'varbinary(max)' }, boolean: { name: 'bit' }, uuid: { name: 'uniqueidentifier' }, # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index e7ceb6d20..43f66478c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -37,12 +37,9 @@ class SQLServerAdapter < AbstractAdapter attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition - cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type, - :enable_default_unicode_types, :auto_connect, :cs_equality_operator, + cattr_accessor :auto_connect, :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration, :showplan_option - self.enable_default_unicode_types = true - def initialize(connection, logger, pool, config) super(connection, logger, pool) # AbstractAdapter Responsibility @@ -221,26 +218,6 @@ def auto_connect_duration @@auto_connect_duration ||= 10 end - def native_string_database_type - @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') - end - - def native_text_database_type - @@native_text_database_type || enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)' - end - - def native_time_database_type - sqlserver_2005? ? 'datetime' : 'time' - end - - def native_date_database_type - sqlserver_2005? ? 'datetime' : 'date' - end - - def native_binary_database_type - @@native_binary_database_type || 'varbinary(max)' - end - def cs_equality_operator @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 965c89368..42189ad74 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -395,7 +395,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'When disabling referential integrity' do + describe 'When disabling referential integrity' do setup do @connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } @@ -403,16 +403,16 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @member = FkTestHasFk.create!(fk_id: @parent.id) end - should 'NOT ALLOW by default the deletion of a referenced parent' do + it 'NOT ALLOW by default the deletion of a referenced parent' do FkTestHasPk.connection.disable_referential_integrity { } assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end - should 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do + it 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do FkTestHasPk.connection.disable_referential_integrity { @parent.destroy } end - should 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do + it 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do assert_raise(ActiveRecord::StatementInvalid) do FkTestHasPk.connection.disable_referential_integrity { } @parent.destroy @@ -421,11 +421,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'For DatabaseStatements' do + describe 'For DatabaseStatements' do - context "finding out what user_options are available" do + describe "finding out what user_options are available" do - should "run the database consistency checker useroptions command" do + it "run the database consistency checker useroptions command" do keys = [:textsize, :language, :isolation_level, :dateformat] user_options = @connection.user_options keys.each do |key| @@ -434,7 +434,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - should "return a underscored key hash with indifferent access of the results" do + it "return a underscored key hash with indifferent access of the results" do user_options = @connection.user_options assert_equal 'read committed', user_options['isolation_level'] assert_equal 'read committed', user_options[:isolation_level] @@ -442,15 +442,15 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end unless sqlserver_azure? - context "altering isolation levels" do + describe "altering isolation levels" do - should "barf if the requested isolation level is not valid" do + it "barf if the requested isolation level is not valid" do assert_raise(ArgumentError) do @connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end end end - context "with a valid isolation level" do + describe "with a valid isolation level" do setup do @t1 = tasks(:first_task) @@ -461,7 +461,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options_isolation_level.inspect}" end - should 'allow #run_with_isolation_level to not take a block to set it' do + it 'allow #run_with_isolation_level to not take a block to set it' do begin @connection.run_with_isolation_level 'READ UNCOMMITTED' assert_match %r|read uncommitted|i, @connection.user_options_isolation_level @@ -470,11 +470,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - should 'return block value using #run_with_isolation_level' do + it 'return block value using #run_with_isolation_level' do assert_equal Task.all.sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } end - should 'pass a read uncommitted isolation level test' do + it 'pass a read uncommitted isolation level test' do assert_nil @t2.starting, 'Fixture should have this empty.' begin Task.transaction do @@ -497,39 +497,39 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'For SchemaStatements' do + describe 'For SchemaStatements' do - context 'returning from #type_to_sql' do + describe 'returning from #type_to_sql' do - should 'create integers when no limit supplied' do + it 'create integers when no limit supplied' do assert_equal 'integer', @connection.type_to_sql(:integer) end - should 'create integers when limit is 4' do + it 'create integers when limit is 4' do assert_equal 'integer', @connection.type_to_sql(:integer, 4) end - should 'create integers when limit is 3' do + it 'create integers when limit is 3' do assert_equal 'integer', @connection.type_to_sql(:integer, 3) end - should 'create smallints when limit is less than 3' do + it 'create smallints when limit is less than 3' do assert_equal 'smallint', @connection.type_to_sql(:integer, 2) assert_equal 'smallint', @connection.type_to_sql(:integer, 1) end - should 'create bigints when limit is greateer than 4' do + it 'create bigints when limit is greateer than 4' do assert_equal 'bigint', @connection.type_to_sql(:integer, 5) assert_equal 'bigint', @connection.type_to_sql(:integer, 6) assert_equal 'bigint', @connection.type_to_sql(:integer, 7) assert_equal 'bigint', @connection.type_to_sql(:integer, 8) end - should 'create floats when no limit supplied' do + it 'create floats when no limit supplied' do assert_equal 'float(8)', @connection.type_to_sql(:float) end - should 'create floats when limit is supplied' do + it 'create floats when limit is supplied' do assert_equal 'float(27)', @connection.type_to_sql(:float, 27) end @@ -537,7 +537,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'For indexes' do + describe 'For indexes' do setup do @desc_index_name = 'idx_credit_limit_test_desc' @@ -548,55 +548,55 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @connection.execute "DROP INDEX [#{@desc_index_name}] ON [accounts]" end - should 'have indexes with descending order' do + it 'have indexes with descending order' do assert @connection.indexes('accounts').find { |i| i.name == @desc_index_name } end end - context 'For views' do + describe 'For views' do - context 'using @connection.views' do + describe 'using @connection.views' do - should 'return an array' do + it 'return an array' do assert_instance_of Array, @connection.views end - should 'find CustomersView table name' do + it 'find CustomersView table name' do @connection.views.must_include 'customers_view' end - should 'work with dynamic finders' do + it 'work with dynamic finders' do name = 'MetaSkills' customer = CustomersView.create! name: name assert_equal customer, CustomersView.find_by_name(name) end - should 'not contain system views' do + it 'not contain system views' do systables = ['sysconstraints','syssegments'] systables.each do |systable| assert !@connection.views.include?(systable), "This systable #{systable} should not be in the views array." end end - should 'allow the connection#view_information method to return meta data on the view' do + it 'allow the connection#view_information method to return meta data on the view' do view_info = @connection.send(:view_information,'customers_view') assert_equal('customers_view', view_info['TABLE_NAME']) assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION']) end - should 'allow the connection#view_table_name method to return true table_name for the view' do + it 'allow the connection#view_table_name method to return true table_name for the view' do assert_equal 'customers', @connection.send(:view_table_name,'customers_view') assert_equal 'topics', @connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' end end - context 'used by a class for table_name' do + describe 'used by a class for table_name' do - context 'with same column names' do + describe 'with same column names' do - should 'have matching column objects' do + it 'have matching column objects' do columns = ['id','name','balance'] assert !CustomersView.columns.blank? assert_equal columns.size, CustomersView.columns.size @@ -607,28 +607,28 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - should 'find identity column' do + it 'find identity column' do assert CustomersView.columns_hash['id'].primary end - should 'find default values' do + it 'find default values' do assert_equal 0, CustomersView.new.balance end - should 'respond true to table_exists?' do + it 'respond true to table_exists?' do assert CustomersView.table_exists? end - should 'have correct table name for all column objects' do + it 'have correct table name for all column objects' do assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' }, CustomersView.columns.map(&:table_name).inspect end end - context 'with aliased column names' do + describe 'with aliased column names' do - should 'have matching column objects' do + it 'have matching column objects' do columns = ['id','pretend_null'] assert !StringDefaultsView.columns.blank? assert_equal columns.size, StringDefaultsView.columns.size @@ -639,20 +639,20 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - should 'find identity column' do + it 'find identity column' do assert StringDefaultsView.columns_hash['id'].primary end - should 'find default values' do + it 'find default values' do assert_equal 'null', StringDefaultsView.new.pretend_null, StringDefaultsView.columns_hash['pretend_null'].inspect end - should 'respond true to table_exists?' do + it 'respond true to table_exists?' do assert StringDefaultsView.table_exists? end - should 'have correct table name for all column objects' do + it 'have correct table name for all column objects' do assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, StringDefaultsView.columns.map(&:table_name).inspect end @@ -661,30 +661,30 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'doing identity inserts' do + describe 'doing identity inserts' do setup do @view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" end - should 'respond true/tablename to #query_requires_identity_insert?' do + it 'respond true/tablename to #query_requires_identity_insert?' do assert_equal '[customers_view]', @connection.send(:query_requires_identity_insert?,@view_insert_sql) end - should 'be able to do an identity insert' do + it 'be able to do an identity insert' do assert_nothing_raised { @connection.execute(@view_insert_sql) } assert CustomersView.find(420) end end - context 'that have more than 4000 chars for their defintion' do + describe 'that have more than 4000 chars for their defintion' do - should 'cope with null returned for the defintion' do + it 'cope with null returned for the defintion' do assert_nothing_raised() { StringDefaultsBigView.columns } end - should 'using alternate view defintion still be able to find real default' do + it 'using alternate view defintion still be able to find real default' do assert_equal 'null', StringDefaultsBigView.new.pretend_null, StringDefaultsBigView.columns_hash['pretend_null'].inspect end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 5c7e5b2b4..93027fb74 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -13,35 +13,13 @@ class TestCase < ActiveSupport::TestCase class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end - def sqlserver_2005? ; ActiveRecord::Base.connection.sqlserver_2005? ; end - def sqlserver_2008? ; ActiveRecord::Base.connection.sqlserver_2008? ; end def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end end def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end - def sqlserver_2005? ; self.class.sqlserver_2005? ; end - def sqlserver_2008? ; self.class.sqlserver_2008? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end - def with_enable_default_unicode_types? - ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types.is_a?(TrueClass) - end - - def with_enable_default_unicode_types(setting) - old_setting = ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types - old_text = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type - old_string = ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type - ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = setting - ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = nil - ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = nil - yield - ensure - ActiveRecord::ConnectionAdapters::SQLServerAdapter.enable_default_unicode_types = old_setting - ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_text_database_type = old_text - ActiveRecord::ConnectionAdapters::SQLServerAdapter.native_string_database_type = old_string - end - def with_auto_connect(boolean) existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean From 8a8e77022162ca57e2fc00e6f73c5637a08ae18e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 15:00:51 -0500 Subject: [PATCH 0209/1412] Start work on abstract test passage. --- test/cases/adapter_test_sqlserver.rb | 511 +++++++++++++-------------- 1 file changed, 246 insertions(+), 265 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 42189ad74..479ee3bc0 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,218 +1,199 @@ require 'cases/helper_sqlserver' -require 'models/task' -require 'models/reply' -require 'models/joke' -require 'models/subscriber' -require 'models/minimalistic' -require 'models/post' -require 'models/sqlserver/fk_test_has_pk' -require 'models/sqlserver/fk_test_has_fk' -require 'models/sqlserver/customers_view' -require 'models/sqlserver/string_defaults_big_view' -require 'models/sqlserver/string_defaults_view' -require 'models/sqlserver/topic' -require 'models/sqlserver/upper_test_default' -require 'models/sqlserver/upper_test_lowered' +require 'models/topic' + +# require 'models/task' +# require 'models/reply' +# require 'models/joke' +# require 'models/subscriber' +# require 'models/minimalistic' +# require 'models/post' +# require 'models/sqlserver/fk_test_has_pk' +# require 'models/sqlserver/fk_test_has_fk' +# require 'models/sqlserver/customers_view' +# require 'models/sqlserver/string_defaults_big_view' +# require 'models/sqlserver/string_defaults_view' +# require 'models/sqlserver/topic' +# require 'models/sqlserver/upper_test_default' +# require 'models/sqlserver/upper_test_lowered' class AdapterTestSQLServer < ActiveRecord::TestCase - fixtures :tasks, :posts + # fixtures :tasks, :posts - setup do - @connection = ActiveRecord::Base.connection + let(:connection) { ActiveRecord::Base.connection } + + before do @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" end - context 'For abstract behavior' do - - should 'have a 128 max #table_alias_length' do - assert @connection.table_alias_length <= 128 - end + it 'has a 128 max #table_alias_length' do + assert connection.table_alias_length <= 128 + end - should 'raise invalid statement error' do - assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") } - end + it 'raises invalid statement error for bad SQL' do + assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") } + end - should 'be our adapter_name' do - assert_equal 'SQLServer', @connection.adapter_name - end + it 'is has our adapter_name' do + assert_equal 'SQLServer', connection.adapter_name + end - should 'include version in inspect' do - assert_match(/version\: \d.\d/,@connection.inspect) - end + it 'include version in inspect' do + assert_match(/version\: \d.\d/, ) + end - should 'include database product level in inspect' do - assert_match(/product_level\: "\w+/, @connection.inspect) - end + it 'include database product level in inspect' do + assert_match(/product_level\: "\w+/, connection.inspect) + end - should 'include database product version in inspect' do - assert_match(/product_version\: "\d+/, @connection.inspect) - end + it 'include database product version in inspect' do + assert_match(/product_version\: "\d+/, connection.inspect) + end - should 'include database edition in inspect' do - assert_match(/edition\: "\w+/, @connection.inspect) - end + it 'include database edition in inspect' do + assert_match(/edition\: "\w+/, connection.inspect) + end - should 'set database product level' do - assert_match(/\w+/, @connection.product_level) - end + it 'set database product level' do + assert_match(/\w+/, connection.product_level) + end - should 'set database product version' do - assert_match(/\d+/, @connection.product_version) - end + it 'set database product version' do + assert_match(/\d+/, connection.product_version) + end - should 'set database edition' do - assert_match(/\w+/, @connection.edition) - end + it 'set database edition' do + assert_match(/\w+/, connection.edition) + end - should 'support migrations' do - assert @connection.supports_migrations? - end + it 'support migrations' do + assert connection.supports_migrations? + end - should 'support DDL in transactions' do - assert @connection.supports_ddl_transactions? - end + it 'support DDL in transactions' do + assert connection.supports_ddl_transactions? + end - should 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do - begin - assert_equal 'tasks', Task.table_name - assert Task.table_exists? - Task.table_name = 'dbo.tasks' - assert Task.table_exists?, 'Tasks table name of dbo.tasks should return true for exists.' - ensure - Task.table_name = 'tasks' - end + it 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do + begin + assert_equal 'tasks', Task.table_name + assert Task.table_exists? + Task.table_name = 'dbo.tasks' + assert Task.table_exists?, 'Tasks table name of dbo.tasks should return true for exists.' + ensure + Task.table_name = 'tasks' end + end - context 'for database version' do - - setup do - @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP - @supported_versions = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS - @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)" - @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" - @sqlserver_2011_string1 = %|Microsoft SQL Server "Denali" (CTP1) - 11.0.1103.9 (Intel X86) Sep 24 2010 22:02:43 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition on Windows NT 6.0 (Build 6002: Service Pack 2)| - end - - should 'return a string from #database_version that matches class regexp' do - assert_match @version_regexp, @connection.database_version - end unless sqlserver_azure? + describe 'for database version' do - should 'return a 4 digit year fixnum for #database_year' do - assert_instance_of Fixnum, @connection.database_year - @supported_versions.must_include @connection.database_year - end + before do + @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP + @supported_versions = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS + @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)" + @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" + @sqlserver_2011_string1 = %|Microsoft SQL Server "Denali" (CTP1) - 11.0.1103.9 (Intel X86) Sep 24 2010 22:02:43 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition on Windows NT 6.0 (Build 6002: Service Pack 2)| + end - should 'return a code name if year not available' do - assert_equal "Denali", @version_regexp.match(@sqlserver_2011_string1)[1] - end + it 'return a string from #database_version that matches class regexp' do + assert_match @version_regexp, connection.database_version + end unless sqlserver_azure? + it 'return a 4 digit year fixnum for #database_year' do + assert_instance_of Fixnum, connection.database_year + @supported_versions.must_include connection.database_year end - should 'return true to #insert_sql? for inserts only' do - assert @connection.send(:insert_sql?,'INSERT...') - assert @connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") - assert !@connection.send(:insert_sql?,'UPDATE...') - assert !@connection.send(:insert_sql?,'SELECT...') + it 'return a code name if year not available' do + assert_equal "Denali", @version_regexp.match(@sqlserver_2011_string1)[1] end - context 'for #get_table_name' do - - should 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do - assert_equal '[funny_jokes]', @connection.send(:get_table_name,@basic_insert_sql) - assert_equal '[customers]', @connection.send(:get_table_name,@basic_update_sql) - assert_equal '[customers]', @connection.send(:get_table_name,@basic_select_sql) - end - - end + end - context 'with different language' do + it 'return true to #insert_sql? for inserts only' do + assert connection.send(:insert_sql?,'INSERT...') + assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") + assert !connection.send(:insert_sql?,'UPDATE...') + assert !connection.send(:insert_sql?,'SELECT...') + end - setup do - @default_language = @connection.user_options_language - end + describe 'for #get_table_name' do - teardown do - @connection.execute("SET LANGUAGE #{@default_language}") rescue nil - @connection.send :initialize_dateformatter - end + it 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do + assert_equal '[funny_jokes]', connection.send(:get_table_name,@basic_insert_sql) + assert_equal '[customers]', connection.send(:get_table_name,@basic_update_sql) + assert_equal '[customers]', connection.send(:get_table_name,@basic_select_sql) + end - should 'memoize users dateformat' do - @connection.execute("SET LANGUAGE us_english") rescue nil - dateformat = @connection.instance_variable_get(:@database_dateformat) - assert_equal 'mdy', dateformat - end + end - should 'have a dateformatter' do - assert Date::DATE_FORMATS[:_sqlserver_dateformat] - assert Time::DATE_FORMATS[:_sqlserver_dateformat] - end + describe 'with different language' do - should 'do a date insertion when language is german' do - @connection.execute("SET LANGUAGE deutsch") - @connection.send :initialize_dateformatter - assert_nothing_raised do - Task.create(starting: Time.utc(2000, 1, 31, 5, 42, 0), ending: Date.new(2006, 12, 31)) - end - end + before do + @default_language = connection.user_options_language + end + after do + connection.execute("SET LANGUAGE #{@default_language}") rescue nil + connection.send :initialize_dateformatter end - context 'testing #enable_default_unicode_types configuration' do + it 'memoize users dateformat' do + connection.execute("SET LANGUAGE us_english") rescue nil + dateformat = connection.instance_variable_get(:@database_dateformat) + assert_equal 'mdy', dateformat + end - should 'use non-unicode types when set to false' do - with_enable_default_unicode_types(false) do - assert_equal 'varchar', @connection.native_string_database_type - assert_equal 'varchar(max)', @connection.native_text_database_type - end - end + it 'have a dateformatter' do + assert Date::DATE_FORMATS[:_sqlserver_dateformat] + assert Time::DATE_FORMATS[:_sqlserver_dateformat] + end - should 'use unicode types when set to true' do - with_enable_default_unicode_types(true) do - assert_equal 'nvarchar', @connection.native_string_database_type - assert_equal 'nvarchar(max)', @connection.native_text_database_type - end + it 'do a date insertion when language is german' do + connection.execute("SET LANGUAGE deutsch") + connection.send :initialize_dateformatter + assert_nothing_raised do + Task.create(starting: Time.utc(2000, 1, 31, 5, 42, 0), ending: Date.new(2006, 12, 31)) end - end - context 'testing #lowercase_schema_reflection' do + end - setup do - UpperTestDefault.delete_all - UpperTestDefault.create COLUMN1: 'Got a minute?', COLUMN2: 419 - UpperTestDefault.create COLUMN1: 'Favorite number?', COLUMN2: 69 - end + describe 'testing #lowercase_schema_reflection' do - teardown do - @connection.lowercase_schema_reflection = false - end + before do + UpperTestDefault.delete_all + UpperTestDefault.create COLUMN1: 'Got a minute?', COLUMN2: 419 + UpperTestDefault.create COLUMN1: 'Favorite number?', COLUMN2: 69 + end - should 'not lowercase schema reflection by default' do - assert UpperTestDefault.columns_hash['COLUMN1'] - assert_equal 'Got a minute?', UpperTestDefault.first.COLUMN1 - assert_equal 'Favorite number?', UpperTestDefault.last.COLUMN1 - assert UpperTestDefault.columns_hash['COLUMN2'] - end + after do + connection.lowercase_schema_reflection = false + end - should 'lowercase schema reflection when set' do - @connection.lowercase_schema_reflection = true - UpperTestLowered.reset_column_information - assert UpperTestLowered.columns_hash['column1'] - assert_equal 'Got a minute?', UpperTestLowered.first.column1 - assert_equal 'Favorite number?', UpperTestLowered.last.column1 - assert UpperTestLowered.columns_hash['column2'] - end + it 'not lowercase schema reflection by default' do + assert UpperTestDefault.columns_hash['COLUMN1'] + assert_equal 'Got a minute?', UpperTestDefault.first.COLUMN1 + assert_equal 'Favorite number?', UpperTestDefault.last.COLUMN1 + assert UpperTestDefault.columns_hash['COLUMN2'] + end + it 'lowercase schema reflection when set' do + connection.lowercase_schema_reflection = true + UpperTestLowered.reset_column_information + assert UpperTestLowered.columns_hash['column1'] + assert_equal 'Got a minute?', UpperTestLowered.first.column1 + assert_equal 'Favorite number?', UpperTestLowered.last.column1 + assert UpperTestLowered.columns_hash['column2'] end end - context 'For identity inserts' do + describe 'For identity inserts' do - setup do + before do @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')" @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')" @identity_insert_sql_unordered = "INSERT INTO [funny_jokes] ([name],[id]) VALUES('Knock knock',420)" @@ -221,35 +202,35 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" end - should '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) + 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) end - should 'return false to #query_requires_identity_insert? for normal SQL' do + it 'return false to #query_requires_identity_insert? for normal SQL' do [@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql| - assert !@connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" + assert !connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" end end - should 'find identity column using #identity_column' do + it 'find identity column using #identity_column' do joke_id_column = Joke.columns.find { |c| c.name == 'id' } - assert_equal joke_id_column.name, @connection.send(:identity_column,Joke.table_name).name - assert_equal joke_id_column.sql_type, @connection.send(:identity_column,Joke.table_name).sql_type + assert_equal joke_id_column.name, connection.send(:identity_column,Joke.table_name).name + assert_equal joke_id_column.sql_type, connection.send(:identity_column,Joke.table_name).sql_type end - should 'return nil when calling #identity_column for a table_name with no identity' do - assert_nil @connection.send(:identity_column,Subscriber.table_name) + it 'return nil when calling #identity_column for a table_name with no identity' do + assert_nil connection.send(:identity_column,Subscriber.table_name) end unless sqlserver_azure? - should 'be able to disable referential integrity' do + it 'be able to disable referential integrity' do Minimalistic.delete_all - @connection.send :set_identity_insert, Minimalistic.table_name, false - @connection.execute_procedure :sp_MSforeachtable, 'ALTER TABLE ? CHECK CONSTRAINT ALL' + connection.send :set_identity_insert, Minimalistic.table_name, false + connection.execute_procedure :sp_MSforeachtable, 'ALTER TABLE ? CHECK CONSTRAINT ALL' o = Minimalistic.new o.id = 420 o.save! @@ -257,61 +238,61 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context 'For Quoting' do + describe 'For Quoting' do - should 'return 1 for #quoted_true' do - assert_equal '1', @connection.quoted_true + it 'return 1 for #quoted_true' do + assert_equal '1', connection.quoted_true end - should 'return 0 for #quoted_false' do - assert_equal '0', @connection.quoted_false + it 'return 0 for #quoted_false' do + assert_equal '0', connection.quoted_false end - should 'not escape backslash characters like abstract adapter' do + it 'not escape backslash characters like abstract adapter' do string_with_backslashs = "\\n" - assert_equal string_with_backslashs, @connection.quote_string(string_with_backslashs) + assert_equal string_with_backslashs, connection.quote_string(string_with_backslashs) end - should 'quote column names with brackets' do - assert_equal '[foo]', @connection.quote_column_name(:foo) - assert_equal '[foo]', @connection.quote_column_name('foo') - assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar') + it 'quote column names with brackets' do + assert_equal '[foo]', connection.quote_column_name(:foo) + assert_equal '[foo]', connection.quote_column_name('foo') + assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar') end - should 'not quote already quoted column names with brackets' do - assert_equal '[foo]', @connection.quote_column_name('[foo]') - assert_equal '[foo].[bar]', @connection.quote_column_name('[foo].[bar]') + it 'not quote already quoted column names with brackets' do + assert_equal '[foo]', connection.quote_column_name('[foo]') + assert_equal '[foo].[bar]', connection.quote_column_name('[foo].[bar]') end - should 'quote table names like columns' do - assert_equal '[foo].[bar]', @connection.quote_column_name('foo.bar') - assert_equal '[foo].[bar].[baz]', @connection.quote_column_name('foo.bar.baz') + it 'quote table names like columns' do + assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar') + assert_equal '[foo].[bar].[baz]', connection.quote_column_name('foo.bar.baz') end - context "#quote" do + describe "#quote" do - context "string and multibyte values" do + describe "string and multibyte values" do - context "on an activerecord :integer column" do + describe "on an activerecord :integer column" do - setup do + before do @column = Post.columns_hash['id'] end - should "return 0 for empty string" do - assert_equal '0', @connection.quote('', @column) + it "return 0 for empty string" do + assert_equal '0', connection.quote('', @column) end end - context "on an activerecord :string column or with any value" do + describe "on an activerecord :string column or with any value" do - should "surround it when N'...'" do - assert_equal "N'foo'", @connection.quote("foo") + it "surround it when N'...'" do + assert_equal "N'foo'", connection.quote("foo") end - should "escape all single quotes by repeating them" do - assert_equal "N'''quotation''s'''", @connection.quote("'quotation's'") + it "escape all single quotes by repeating them" do + assert_equal "N'''quotation''s'''", connection.quote("'quotation's'") end end @@ -320,68 +301,68 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - context "#quoted_datetime" do + describe "#quoted_datetime" do - setup do + before do @iso_string = '2001-02-03T04:05:06-0700' @date = Date.parse @iso_string @time = Time.parse @iso_string @datetime = DateTime.parse @iso_string end - context "with a Date" do + describe "with a Date" do - should "return a dd-mm-yyyy date string" do - assert_equal '02-03-2001', @connection.quoted_datetime(@date) + it "return a dd-mm-yyyy date string" do + assert_equal '02-03-2001', connection.quoted_datetime(@date) end end - context "when the ActiveRecord default timezone is UTC" do + describe "when the ActiveRecord default timezone is UTC" do - setup do + before do @old_activerecord_timezone = ActiveRecord::Base.default_timezone ActiveRecord::Base.default_timezone = :utc end - teardown do + after do ActiveRecord::Base.default_timezone = @old_activerecord_timezone @old_activerecord_timezone = nil end - context "with a Time" do + describe "with a Time" do - should "return an ISO 8601 datetime string" do - assert_equal '2001-02-03T11:05:06.000', @connection.quoted_datetime(@time) + it "return an ISO 8601 datetime string" do + assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@time) end end - context "with a DateTime" do + describe "with a DateTime" do - should "return an ISO 8601 datetime string" do - assert_equal '2001-02-03T11:05:06', @connection.quoted_datetime(@datetime) + it "return an ISO 8601 datetime string" do + assert_equal '2001-02-03T11:05:06', connection.quoted_datetime(@datetime) end end - context "with an ActiveSupport::TimeWithZone" do + describe "with an ActiveSupport::TimeWithZone" do - context "wrapping a datetime" do + describe "wrapping a datetime" do - should "return an ISO 8601 datetime string with milliseconds" do + it "return an ISO 8601 datetime string with milliseconds" do Time.use_zone('Eastern Time (US & Canada)') do - assert_equal '2001-02-03T11:05:06.000', @connection.quoted_datetime(@datetime.in_time_zone) + assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@datetime.in_time_zone) end end end - context "wrapping a time" do + describe "wrapping a time" do - should "return an ISO 8601 datetime string with milliseconds" do + it "return an ISO 8601 datetime string with milliseconds" do Time.use_zone('Eastern Time (US & Canada)') do - assert_equal '2001-02-03T11:05:06.000', @connection.quoted_datetime(@time.in_time_zone) + assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@time.in_time_zone) end end @@ -397,8 +378,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'When disabling referential integrity' do - setup do - @connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } + before do + connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } @parent = FkTestHasPk.create! @member = FkTestHasFk.create!(fk_id: @parent.id) end @@ -427,7 +408,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "run the database consistency checker useroptions command" do keys = [:textsize, :language, :isolation_level, :dateformat] - user_options = @connection.user_options + user_options = connection.user_options keys.each do |key| msg = "Expected key:#{key} in user_options:#{user_options.inspect}" assert user_options.key?(key), msg @@ -435,7 +416,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return a underscored key hash with indifferent access of the results" do - user_options = @connection.user_options + user_options = connection.user_options assert_equal 'read committed', user_options['isolation_level'] assert_equal 'read committed', user_options[:isolation_level] end @@ -446,32 +427,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "barf if the requested isolation level is not valid" do assert_raise(ArgumentError) do - @connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end + connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end end end describe "with a valid isolation level" do - setup do + before do @t1 = tasks(:first_task) @t2 = tasks(:another_task) assert @t1, 'Tasks :first_task should be in AR fixtures' assert @t2, 'Tasks :another_task should be in AR fixtures' - good_isolation_level = @connection.user_options_isolation_level.blank? || @connection.user_options_isolation_level =~ /read committed/i - assert good_isolation_level, "User isolation level is not at a happy starting place: #{@connection.user_options_isolation_level.inspect}" + good_isolation_level = connection.user_options_isolation_level.blank? || connection.user_options_isolation_level =~ /read committed/i + assert good_isolation_level, "User isolation level is not at a happy starting place: #{connection.user_options_isolation_level.inspect}" end it 'allow #run_with_isolation_level to not take a block to set it' do begin - @connection.run_with_isolation_level 'READ UNCOMMITTED' - assert_match %r|read uncommitted|i, @connection.user_options_isolation_level + connection.run_with_isolation_level 'READ UNCOMMITTED' + assert_match %r|read uncommitted|i, connection.user_options_isolation_level ensure - @connection.run_with_isolation_level 'READ COMMITTED' + connection.run_with_isolation_level 'READ COMMITTED' end end it 'return block value using #run_with_isolation_level' do - assert_equal Task.all.sort, @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } + assert_equal Task.all.sort, connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } end it 'pass a read uncommitted isolation level test' do @@ -480,7 +461,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase Task.transaction do @t2.starting = Time.now @t2.save - @dirty_t2 = @connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) } + @dirty_t2 = connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) } raise ActiveRecord::ActiveRecordError end rescue @@ -502,35 +483,35 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'returning from #type_to_sql' do it 'create integers when no limit supplied' do - assert_equal 'integer', @connection.type_to_sql(:integer) + assert_equal 'integer', connection.type_to_sql(:integer) end it 'create integers when limit is 4' do - assert_equal 'integer', @connection.type_to_sql(:integer, 4) + assert_equal 'integer', connection.type_to_sql(:integer, 4) end it 'create integers when limit is 3' do - assert_equal 'integer', @connection.type_to_sql(:integer, 3) + assert_equal 'integer', connection.type_to_sql(:integer, 3) end it 'create smallints when limit is less than 3' do - assert_equal 'smallint', @connection.type_to_sql(:integer, 2) - assert_equal 'smallint', @connection.type_to_sql(:integer, 1) + assert_equal 'smallint', connection.type_to_sql(:integer, 2) + assert_equal 'smallint', connection.type_to_sql(:integer, 1) end it 'create bigints when limit is greateer than 4' do - assert_equal 'bigint', @connection.type_to_sql(:integer, 5) - assert_equal 'bigint', @connection.type_to_sql(:integer, 6) - assert_equal 'bigint', @connection.type_to_sql(:integer, 7) - assert_equal 'bigint', @connection.type_to_sql(:integer, 8) + assert_equal 'bigint', connection.type_to_sql(:integer, 5) + assert_equal 'bigint', connection.type_to_sql(:integer, 6) + assert_equal 'bigint', connection.type_to_sql(:integer, 7) + assert_equal 'bigint', connection.type_to_sql(:integer, 8) end it 'create floats when no limit supplied' do - assert_equal 'float(8)', @connection.type_to_sql(:float) + assert_equal 'float(8)', connection.type_to_sql(:float) end it 'create floats when limit is supplied' do - assert_equal 'float(27)', @connection.type_to_sql(:float, 27) + assert_equal 'float(27)', connection.type_to_sql(:float, 27) end end @@ -539,31 +520,31 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'For indexes' do - setup do + before do @desc_index_name = 'idx_credit_limit_test_desc' - @connection.execute "CREATE INDEX [#{@desc_index_name}] ON [accounts] (credit_limit DESC)" + connection.execute "CREATE INDEX [#{@desc_index_name}] ON [accounts] (credit_limit DESC)" end - teardown do - @connection.execute "DROP INDEX [#{@desc_index_name}] ON [accounts]" + after do + connection.execute "DROP INDEX [#{@desc_index_name}] ON [accounts]" end it 'have indexes with descending order' do - assert @connection.indexes('accounts').find { |i| i.name == @desc_index_name } + assert connection.indexes('accounts').find { |i| i.name == @desc_index_name } end end describe 'For views' do - describe 'using @connection.views' do + describe 'using connection.views' do it 'return an array' do - assert_instance_of Array, @connection.views + assert_instance_of Array, connection.views end it 'find CustomersView table name' do - @connection.views.must_include 'customers_view' + connection.views.must_include 'customers_view' end it 'work with dynamic finders' do @@ -575,19 +556,19 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it 'not contain system views' do systables = ['sysconstraints','syssegments'] systables.each do |systable| - assert !@connection.views.include?(systable), "This systable #{systable} should not be in the views array." + assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array." end end it 'allow the connection#view_information method to return meta data on the view' do - view_info = @connection.send(:view_information,'customers_view') + view_info = connection.send(:view_information,'customers_view') assert_equal('customers_view', view_info['TABLE_NAME']) assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION']) end it 'allow the connection#view_table_name method to return true table_name for the view' do - assert_equal 'customers', @connection.send(:view_table_name,'customers_view') - assert_equal 'topics', @connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' + assert_equal 'customers', connection.send(:view_table_name,'customers_view') + assert_equal 'topics', connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' end end @@ -663,16 +644,16 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'doing identity inserts' do - setup do + before do @view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" end it 'respond true/tablename to #query_requires_identity_insert?' do - assert_equal '[customers_view]', @connection.send(:query_requires_identity_insert?,@view_insert_sql) + assert_equal '[customers_view]', connection.send(:query_requires_identity_insert?,@view_insert_sql) end it 'be able to do an identity insert' do - assert_nothing_raised { @connection.execute(@view_insert_sql) } + assert_nothing_raised { connection.execute(@view_insert_sql) } assert CustomersView.find(420) end From 74cb8d49a0cb6238f9f6acaf95e8ce51cc5e057b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 15:21:20 -0500 Subject: [PATCH 0210/1412] Removed various version and inspection methods. They include: * database_version * database_year * product_level * product_version * edition --- CHANGELOG.md | 11 +++- .../connection_adapters/sqlserver_adapter.rb | 46 ++----------- test/cases/adapter_test_sqlserver.rb | 65 ++++--------------- 3 files changed, 24 insertions(+), 98 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 023cfcb8a..6dbec7239 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,11 +22,16 @@ * SQL Server versions < 2012 which do not support OFFSET and FETCH. http://bit.ly/1B5Bwsd * The `enable_default_unicode_types` option. Default to national types all the time. Use SQL type name in migrations if needed. -* Native type configs for older DB support. Includes the following with new default value. +* Native type configs for older DB support. Includes the following with new default value: * native_string_database_type => `nvarchar` * native_text_database_type => `nvarchar(max)` * native_binary_database_type => `varbinary(max)` - +* Various version and inspection methods removed. These include: + * database_version + * database_year + * product_level + * product_version + * edition #### Fixed @@ -34,6 +39,6 @@ #### Security -* n/a +* The connection's `inspect` method no longer returns sensitive connection info. Very basic now. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 43f66478c..d3a5d4a14 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -35,7 +35,7 @@ class SQLServerAdapter < AbstractAdapter ADAPTER_NAME = 'SQLServer'.freeze - attr_reader :database_version, :database_year, :spid, :product_level, :product_version, :edition + attr_reader :spid cattr_accessor :auto_connect, :cs_equality_operator, :lowercase_schema_reflection, :auto_connect_duration, :showplan_option @@ -50,27 +50,9 @@ def initialize(connection, logger, pool, config) @config = config @connection_options = config connect - @database_version = select_value 'SELECT @@version', 'SCHEMA' - @database_year = begin - if @database_version =~ /Azure/i - @sqlserver_azure = true - @database_version.match(/\s-\s([0-9.]+)/)[1] - year = 2012 - else - year = DATABASE_VERSION_REGEXP.match(@database_version)[1] - year == 'Denali' ? 2011 : year.to_i - end - rescue - 0 - end - @product_level = select_value "SELECT CAST(SERVERPROPERTY('productlevel') AS VARCHAR(128))", 'SCHEMA' - @product_version = select_value "SELECT CAST(SERVERPROPERTY('productversion') AS VARCHAR(128))", 'SCHEMA' - @edition = select_value "SELECT CAST(SERVERPROPERTY('edition') AS VARCHAR(128))", 'SCHEMA' + @sqlserver_azure = !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i) initialize_dateformatter use_database - unless @sqlserver_azure == true || SUPPORTED_VERSIONS.include?(@database_year) - raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported. We got back #{@database_version}." - end end # === Abstract Adapter ========================================== # @@ -108,7 +90,7 @@ def supports_index_sort_order? end def supports_partial_index? - @database_year >= 2008 + true end def supports_explain? @@ -178,26 +160,6 @@ def sqlserver? true end - def sqlserver_2005? - @database_year == 2005 - end - - def sqlserver_2008? - @database_year == 2008 - end - - def sqlserver_2011? - @database_year == 2011 - end - - def sqlserver_2012? - @database_year == 2012 - end - - def sqlserver_2014? - @database_year == 2014 - end - def sqlserver_azure? @sqlserver_azure end @@ -207,7 +169,7 @@ def version end def inspect - "#<#{self.class} version: #{version}, year: #{@database_year}, product_level: #{@product_level.inspect}, product_version: #{@product_version.inspect}, edition: #{@edition.inspect}, connection_options: #{@connection_options.inspect}>" + "#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>" end def auto_connect diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 479ee3bc0..e2258c4d8 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -28,6 +28,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" end + it 'has basic and non-senstive information in the adpaters inspect method' do + string = connection.inspect + string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} + string.must_match %r{version\: \d.\d} + string.must_match %r{mode: (dblib|odbc)} + string.must_match %r{azure: (true|false)} + string.wont_match %r{host} + string.wont_match %r{password} + string.wont_match %r{username} + string.wont_match %r{port} + end + it 'has a 128 max #table_alias_length' do assert connection.table_alias_length <= 128 end @@ -40,34 +52,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'SQLServer', connection.adapter_name end - it 'include version in inspect' do - assert_match(/version\: \d.\d/, ) - end - - it 'include database product level in inspect' do - assert_match(/product_level\: "\w+/, connection.inspect) - end - - it 'include database product version in inspect' do - assert_match(/product_version\: "\d+/, connection.inspect) - end - - it 'include database edition in inspect' do - assert_match(/edition\: "\w+/, connection.inspect) - end - - it 'set database product level' do - assert_match(/\w+/, connection.product_level) - end - - it 'set database product version' do - assert_match(/\d+/, connection.product_version) - end - - it 'set database edition' do - assert_match(/\w+/, connection.edition) - end - it 'support migrations' do assert connection.supports_migrations? end @@ -87,31 +71,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - describe 'for database version' do - - before do - @version_regexp = ActiveRecord::ConnectionAdapters::SQLServerAdapter::DATABASE_VERSION_REGEXP - @supported_versions = ActiveRecord::ConnectionAdapters::SQLServerAdapter::SUPPORTED_VERSIONS - @sqlserver_2005_string = "Microsoft SQL Server 2005 - 9.00.3215.00 (Intel X86)" - @sqlserver_2008_string = "Microsoft SQL Server 2008 (RTM) - 10.0.1600.22 (Intel X86)" - @sqlserver_2011_string1 = %|Microsoft SQL Server "Denali" (CTP1) - 11.0.1103.9 (Intel X86) Sep 24 2010 22:02:43 Copyright (c) Microsoft Corporation Enterprise Evaluation Edition on Windows NT 6.0 (Build 6002: Service Pack 2)| - end - - it 'return a string from #database_version that matches class regexp' do - assert_match @version_regexp, connection.database_version - end unless sqlserver_azure? - - it 'return a 4 digit year fixnum for #database_year' do - assert_instance_of Fixnum, connection.database_year - @supported_versions.must_include connection.database_year - end - - it 'return a code name if year not available' do - assert_equal "Denali", @version_regexp.match(@sqlserver_2011_string1)[1] - end - - end - it 'return true to #insert_sql? for inserts only' do assert connection.send(:insert_sql?,'INSERT...') assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") From b36440080b7d7ae2c66930d606a664b9c7e5a96c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 15:42:45 -0500 Subject: [PATCH 0211/1412] Remove no-op quoting of datetime in cast object. --- .../connection_adapters/sqlserver/type/datetime.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index bc67ba456..d1287a2dd 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,13 +6,6 @@ class DateTime < ActiveRecord::Type::DateTime include Castable - def type_cast_for_database(value) - value = super(value) - return unless value - return value unless value =~ ConnectionAdapters::Column::Format::ISO_DATETIME - "#{value.to_s(:db)}#{cast_usec_for_database(value)}" - end - private @@ -29,11 +22,6 @@ def cast_usec(value) (ss_seconds * 1_000_000).to_i end - def cast_usec_for_database(value) - usec = cast_usec(value) - '.' + (BigDecimal(usec.to_s) / 1_000_000).round(3).to_s.split('.').last - end - def second_precision 0.00333 end From e530d4b0818cdf2d3760cbc2515b2d6a488cad83 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 16:27:00 -0500 Subject: [PATCH 0212/1412] Allow FOCUS_TEST env to be multiple files for guard. --- Guardfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Guardfile b/Guardfile index 681a07ea9..2ccfced5a 100644 --- a/Guardfile +++ b/Guardfile @@ -15,7 +15,9 @@ guard :minitest, { } do # Our project watchers. if ENV['FOCUS_TEST'] - watch(%r{.*}) { ENV['FOCUS_TEST'] } if ENV['FOCUS_TEST'] + ENV['FOCUS_TEST'].split(',').map(&:strip).each do |file| + watch(%r{.*}) { file } + end else watch(%r{^test/cases/\w+_test_sqlserver.rb$}) watch(%r{^lib/active_record/connection_adapters/sqlserver/([^/]+)\.rb$}) { |m| "test/cases/#{m[1]}_test_sqlserver.rb" } From 579d57caae14b0fdb004a787eeeee2618cfb8b4c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 16:30:30 -0500 Subject: [PATCH 0213/1412] Fix date formats in other language tests. --- .../connection_adapters/sqlserver/quoting.rb | 16 +++--- .../sqlserver/type/datetime.rb | 2 +- .../connection_adapters/sqlserver/utils.rb | 12 +++++ test/cases/adapter_test_sqlserver.rb | 53 +++++++++---------- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 3a7c5c90a..ca82138c2 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -45,13 +45,15 @@ def unquoted_false end def quoted_date(value) - if value.acts_like?(:time) && value.respond_to?(:usec) - precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last - "#{super}.#{precision}" - elsif value.acts_like?(:date) - value.to_s(:_sqlserver_dateformat) - else - super + SQLServer::Utils.with_sqlserver_db_date_formats do + if value.acts_like?(:time) && value.respond_to?(:usec) + precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last + "#{super}.#{precision}" + elsif value.acts_like?(:date) + value.to_s(:_sqlserver_dateformat) + else + super + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index d1287a2dd..c69f2374b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -16,7 +16,7 @@ def cast_value(value) end def cast_usec(value) - return 0 if value.usec.zero? + return 0 if !value.respond_to?(:usec) || value.usec.zero? seconds = value.usec.to_f / 1_000_000.0 ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3) (ss_seconds * 1_000_000).to_i diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 7b7fe87a1..d79b5c035 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -112,6 +112,18 @@ def extract_identifiers(name) SQLServer::Utils::Name.new(name) end + def with_sqlserver_db_date_formats + old_db_format_date = Date::DATE_FORMATS[:db] + old_db_format_time = Time::DATE_FORMATS[:db] + date_format = Date::DATE_FORMATS[:_sqlserver_dateformat] + Date::DATE_FORMATS[:db] = "#{date_format}" + Time::DATE_FORMATS[:db] = "#{date_format} %H:%M:%S" + yield + ensure + Date::DATE_FORMATS[:db] = old_db_format_date + Time::DATE_FORMATS[:db] = old_db_format_time + end + end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index e2258c4d8..353b3168a 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,7 +1,7 @@ require 'cases/helper_sqlserver' require 'models/topic' +require 'models/task' -# require 'models/task' # require 'models/reply' # require 'models/joke' # require 'models/subscriber' @@ -18,15 +18,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase - # fixtures :tasks, :posts - let(:connection) { ActiveRecord::Base.connection } - before do - @basic_insert_sql = "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" - @basic_update_sql = "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" - @basic_select_sql = "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" - end + let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } + let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } + let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } + it 'has basic and non-senstive information in the adpaters inspect method' do string = connection.inspect @@ -52,7 +49,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'SQLServer', connection.adapter_name end - it 'support migrations' do + it 'supports migrations' do assert connection.supports_migrations? end @@ -60,32 +57,28 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert connection.supports_ddl_transactions? end - it 'allow owner table name prefixs like dbo. to still allow table_exists? to return true' do + it 'allow owner table name prefixs like dbo to still allow table exists to return true' do begin - assert_equal 'tasks', Task.table_name - assert Task.table_exists? - Task.table_name = 'dbo.tasks' - assert Task.table_exists?, 'Tasks table name of dbo.tasks should return true for exists.' + assert_equal 'topics', Topic.table_name + assert Topic.table_exists? + Topic.table_name = 'dbo.topics' + assert Topic.table_exists?, 'Tasks table name of dbo.topics should return true for exists.' ensure - Task.table_name = 'tasks' + Topic.table_name = 'topics' end end - it 'return true to #insert_sql? for inserts only' do + it 'return true to insert sql query for inserts only' do assert connection.send(:insert_sql?,'INSERT...') assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") assert !connection.send(:insert_sql?,'UPDATE...') assert !connection.send(:insert_sql?,'SELECT...') end - describe 'for #get_table_name' do - - it 'return quoted table name from basic INSERT, UPDATE and SELECT statements' do - assert_equal '[funny_jokes]', connection.send(:get_table_name,@basic_insert_sql) - assert_equal '[customers]', connection.send(:get_table_name,@basic_update_sql) - assert_equal '[customers]', connection.send(:get_table_name,@basic_select_sql) - end - + it 'return quoted table name from basic INSERT UPDATE and SELECT statements' do + assert_equal '[funny_jokes]', connection.send(:get_table_name, basic_insert_sql) + assert_equal '[customers]', connection.send(:get_table_name, basic_update_sql) + assert_equal '[customers]', connection.send(:get_table_name, basic_select_sql) end describe 'with different language' do @@ -99,22 +92,24 @@ class AdapterTestSQLServer < ActiveRecord::TestCase connection.send :initialize_dateformatter end - it 'memoize users dateformat' do + it 'memos users dateformat' do connection.execute("SET LANGUAGE us_english") rescue nil dateformat = connection.instance_variable_get(:@database_dateformat) assert_equal 'mdy', dateformat end - it 'have a dateformatter' do + it 'has a dateformatter' do assert Date::DATE_FORMATS[:_sqlserver_dateformat] assert Time::DATE_FORMATS[:_sqlserver_dateformat] end - it 'do a date insertion when language is german' do + it 'does a datetime insertion when language is german' do connection.execute("SET LANGUAGE deutsch") connection.send :initialize_dateformatter assert_nothing_raised do - Task.create(starting: Time.utc(2000, 1, 31, 5, 42, 0), ending: Date.new(2006, 12, 31)) + starting = Time.utc(2000, 1, 31, 5, 42, 0) + ending = Date.new(2006, 12, 31) + Task.create! starting: starting, ending: ending end end @@ -171,7 +166,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'return false to #query_requires_identity_insert? for normal SQL' do - [@basic_insert_sql, @basic_update_sql, @basic_select_sql].each do |sql| + [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| assert !connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" end end From 6100d23a80dd73dd5add96e72c9bbfae4af633b4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 22:03:56 -0500 Subject: [PATCH 0214/1412] Fix schema cache and lowercase schema setting. --- .../sqlserver/schema_cache.rb | 75 ++++++++++++------- test/cases/adapter_test_sqlserver.rb | 23 +++--- test/models/sqlserver/upper.rb | 3 + test/models/sqlserver/uppered.rb | 3 + test/models/upper_test_default.rb | 3 - test/models/upper_test_lowered.rb | 3 - test/schema/sqlserver_specific_schema.rb | 10 +-- 7 files changed, 71 insertions(+), 49 deletions(-) create mode 100644 test/models/sqlserver/upper.rb create mode 100644 test/models/sqlserver/uppered.rb delete mode 100644 test/models/upper_test_default.rb delete mode 100644 test/models/upper_test_lowered.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index 99b92f633..603abad9b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -7,59 +7,78 @@ class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache def initialize(conn) super - @table_names = nil - @view_names = nil + @views = {} @view_information = {} end # Superclass Overrides + def primary_keys(table_name) + super(table_name_key(table_name)) + end + def table_exists?(table_name) - return false if table_name.blank? - key = table_name_key(table_name) - return @tables[key] if @tables.key? key - @tables[key] = connection.table_exists?(table_name) + name = table_name_key(table_name) + super(name) || view_exists?(name) + end + + def add(table_name) + super(table_name_key(table_name)) + end + + def tables(name) + super(table_name_key(name)) + end + + def columns(table_name) + super(table_name_key(table_name)) + end + + def columns_hash(table_name) + super(table_name_key(table_name)) end def clear! super - @table_names = nil - @view_names = nil + @views.clear @view_information.clear end + def size + super + [@views, @view_information].map{ |x| x.size }.inject(:+) + end + def clear_table_cache!(table_name) - key = table_name_key(table_name) - super(key) + table_name = table_name_key(table_name) super(table_name) - # SQL Server Specific - if @table_names - @table_names.delete key - @table_names.delete table_name - end - if @view_names - @view_names.delete key - @view_names.delete table_name - end - @view_information.delete key + @views.delete table_name + @view_information.delete table_name end - # SQL Server Specific + def marshal_dump + super + [@views, @view_information] + end - def table_names - @table_names ||= connection.tables + def marshal_load(array) + @views, @view_information = array[-2..-1] + super(array[0..-3]) end + # SQL Server Specific + def view_names - @view_names ||= connection.views + @views.select{ |k,v| v }.keys end def view_exists?(table_name) - table_exists?(table_name) + name = table_name_key(table_name) + prepare_views if @views.empty? + return @views[name] if @views.key? name + @views[name] = connection.views.include?(name) end def view_information(table_name) - key = table_name_key(table_name) + name = table_name_key(table_name) return @view_information[key] if @view_information.key? key @view_information[key] = connection.send(:view_information, table_name) end @@ -71,6 +90,10 @@ def table_name_key(table_name) SQLServer::Utils.extract_identifiers(table_name).object end + def prepare_views + connection.views.each { |view| @views[view] = true } + end + end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 353b3168a..775d7a0cc 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -118,9 +118,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'testing #lowercase_schema_reflection' do before do - UpperTestDefault.delete_all - UpperTestDefault.create COLUMN1: 'Got a minute?', COLUMN2: 419 - UpperTestDefault.create COLUMN1: 'Favorite number?', COLUMN2: 69 + SSTestUpper.delete_all + SSTestUpper.create COLUMN1: 'Got a minute?', COLUMN2: 419 + SSTestUpper.create COLUMN1: 'Favorite number?', COLUMN2: 69 end after do @@ -128,19 +128,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'not lowercase schema reflection by default' do - assert UpperTestDefault.columns_hash['COLUMN1'] - assert_equal 'Got a minute?', UpperTestDefault.first.COLUMN1 - assert_equal 'Favorite number?', UpperTestDefault.last.COLUMN1 - assert UpperTestDefault.columns_hash['COLUMN2'] + assert SSTestUpper.columns_hash['COLUMN1'] + assert_equal 'Got a minute?', SSTestUpper.first.COLUMN1 + assert_equal 'Favorite number?', SSTestUpper.last.COLUMN1 + assert SSTestUpper.columns_hash['COLUMN2'] end it 'lowercase schema reflection when set' do connection.lowercase_schema_reflection = true - UpperTestLowered.reset_column_information - assert UpperTestLowered.columns_hash['column1'] - assert_equal 'Got a minute?', UpperTestLowered.first.column1 - assert_equal 'Favorite number?', UpperTestLowered.last.column1 - assert UpperTestLowered.columns_hash['column2'] + assert SSTestUppered.columns_hash['column1'] + assert_equal 'Got a minute?', SSTestUppered.first.column1 + assert_equal 'Favorite number?', SSTestUppered.last.column1 + assert SSTestUppered.columns_hash['column2'] end end diff --git a/test/models/sqlserver/upper.rb b/test/models/sqlserver/upper.rb new file mode 100644 index 000000000..623f67b0f --- /dev/null +++ b/test/models/sqlserver/upper.rb @@ -0,0 +1,3 @@ +class SSTestUpper < ActiveRecord::Base + self.table_name = 'sst_upper_tests' +end diff --git a/test/models/sqlserver/uppered.rb b/test/models/sqlserver/uppered.rb new file mode 100644 index 000000000..cb3989dc5 --- /dev/null +++ b/test/models/sqlserver/uppered.rb @@ -0,0 +1,3 @@ +class SSTestUppered < ActiveRecord::Base + self.table_name = 'SST_UPPER_TESTS' +end diff --git a/test/models/upper_test_default.rb b/test/models/upper_test_default.rb deleted file mode 100644 index fae5dfe8d..000000000 --- a/test/models/upper_test_default.rb +++ /dev/null @@ -1,3 +0,0 @@ -class UpperTestDefault < ActiveRecord::Base - self.table_name = 'UPPER_TESTS' -end \ No newline at end of file diff --git a/test/models/upper_test_lowered.rb b/test/models/upper_test_lowered.rb deleted file mode 100644 index 01ceca5b3..000000000 --- a/test/models/upper_test_lowered.rb +++ /dev/null @@ -1,3 +0,0 @@ -class UpperTestLowered < ActiveRecord::Base - self.table_name = 'upper_tests' -end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 43111e3e6..58ff3e343 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -2,18 +2,18 @@ execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) - create_table :datatypes_migration, force: true do |t| + create_table :sst_datatypes_migration, force: true do |t| t.column :real, :real end - - - - create_table :UPPER_TESTS, force: true do |t| + create_table :SST_UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string t.column :COLUMN2, :integer end + + + create_table :float_data, force: true do |t| t.float :temperature t.float :temperature_8, limit: 8 From 15e158408148b3f8866a3a9a1471f9a97bc62793 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 22:39:14 -0500 Subject: [PATCH 0215/1412] Few more base tests passing. --- .../connection_adapters/sqlserver/quoting.rb | 2 +- .../sqlserver/schema_cache.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 134 +++--------------- 3 files changed, 19 insertions(+), 121 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index ca82138c2..68d2c0ad4 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -12,7 +12,7 @@ def quote_string(s) end def quote_column_name(name) - SQLServer::Utils.extract_identifiers(name).object_quoted + SQLServer::Utils.extract_identifiers(name).quoted end def quote_default_value(value, column) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index 603abad9b..4a0e5af23 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -79,8 +79,8 @@ def view_exists?(table_name) def view_information(table_name) name = table_name_key(table_name) - return @view_information[key] if @view_information.key? key - @view_information[key] = connection.send(:view_information, table_name) + return @view_information[name] if @view_information.key? name + @view_information[name] = connection.send(:view_information, table_name) end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 775d7a0cc..a2a7a6e31 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,11 +1,11 @@ require 'cases/helper_sqlserver' require 'models/topic' require 'models/task' +require 'models/subscriber' +require 'models/minimalistic' # require 'models/reply' # require 'models/joke' -# require 'models/subscriber' -# require 'models/minimalistic' # require 'models/post' # require 'models/sqlserver/fk_test_has_pk' # require 'models/sqlserver/fk_test_has_fk' @@ -144,7 +144,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'For identity inserts' do + describe 'identity inserts' do before do @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')" @@ -171,27 +171,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'find identity column using #identity_column' do - joke_id_column = Joke.columns.find { |c| c.name == 'id' } - assert_equal joke_id_column.name, connection.send(:identity_column,Joke.table_name).name - assert_equal joke_id_column.sql_type, connection.send(:identity_column,Joke.table_name).sql_type + task_id_column = Task.columns_hash['id'] + assert_equal task_id_column.name, connection.send(:identity_column, Task.table_name).name + assert_equal task_id_column.sql_type, connection.send(:identity_column, Task.table_name).sql_type end it 'return nil when calling #identity_column for a table_name with no identity' do - assert_nil connection.send(:identity_column,Subscriber.table_name) - end unless sqlserver_azure? - - it 'be able to disable referential integrity' do - Minimalistic.delete_all - connection.send :set_identity_insert, Minimalistic.table_name, false - connection.execute_procedure :sp_MSforeachtable, 'ALTER TABLE ? CHECK CONSTRAINT ALL' - o = Minimalistic.new - o.id = 420 - o.save! + assert_nil connection.send(:identity_column, Subscriber.table_name) end end - describe 'For Quoting' do + describe 'quoting' do it 'return 1 for #quoted_true' do assert_equal '1', connection.quoted_true @@ -222,114 +213,21 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal '[foo].[bar].[baz]', connection.quote_column_name('foo.bar.baz') end - describe "#quote" do - - describe "string and multibyte values" do - - describe "on an activerecord :integer column" do - - before do - @column = Post.columns_hash['id'] - end - - it "return 0 for empty string" do - assert_equal '0', connection.quote('', @column) - end - - end - - describe "on an activerecord :string column or with any value" do - - it "surround it when N'...'" do - assert_equal "N'foo'", connection.quote("foo") - end - - it "escape all single quotes by repeating them" do - assert_equal "N'''quotation''s'''", connection.quote("'quotation's'") - end - - end - - end - + it "return 0 for empty string" do + assert_equal '0', connection.quote('', Post.columns_hash['id']) end - describe "#quoted_datetime" do - - before do - @iso_string = '2001-02-03T04:05:06-0700' - @date = Date.parse @iso_string - @time = Time.parse @iso_string - @datetime = DateTime.parse @iso_string - end - - describe "with a Date" do - - it "return a dd-mm-yyyy date string" do - assert_equal '02-03-2001', connection.quoted_datetime(@date) - end - - end - - describe "when the ActiveRecord default timezone is UTC" do - - before do - @old_activerecord_timezone = ActiveRecord::Base.default_timezone - ActiveRecord::Base.default_timezone = :utc - end - - after do - ActiveRecord::Base.default_timezone = @old_activerecord_timezone - @old_activerecord_timezone = nil - end - - describe "with a Time" do - - it "return an ISO 8601 datetime string" do - assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@time) - end - - end - - describe "with a DateTime" do - - it "return an ISO 8601 datetime string" do - assert_equal '2001-02-03T11:05:06', connection.quoted_datetime(@datetime) - end - - end - - describe "with an ActiveSupport::TimeWithZone" do - - describe "wrapping a datetime" do - - it "return an ISO 8601 datetime string with milliseconds" do - Time.use_zone('Eastern Time (US & Canada)') do - assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@datetime.in_time_zone) - end - end - - end - - describe "wrapping a time" do - - it "return an ISO 8601 datetime string with milliseconds" do - Time.use_zone('Eastern Time (US & Canada)') do - assert_equal '2001-02-03T11:05:06.000', connection.quoted_datetime(@time.in_time_zone) - end - end - - end - - end - - end + it "surround string with national prefix" do + assert_equal "N'foo'", connection.quote("foo") + end + it "escape all single quotes by repeating them" do + assert_equal "N'''quotation''s'''", connection.quote("'quotation's'") end end - describe 'When disabling referential integrity' do + describe 'disabling referential integrity' do before do connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } From ea1bd824055cd51274833550475159e71aa5621f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 11 Jan 2015 22:53:12 -0500 Subject: [PATCH 0216/1412] Fixing disable referential integrity tests. --- test/cases/adapter_test_sqlserver.rb | 12 ++++++------ test/models/fk_test_has_fk.rb | 2 -- test/models/fk_test_has_pk.rb | 2 -- test/models/sqlserver/fk_has_fk.rb | 3 +++ test/models/sqlserver/fk_has_pk.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 18 +++++++++--------- 6 files changed, 21 insertions(+), 19 deletions(-) delete mode 100644 test/models/fk_test_has_fk.rb delete mode 100644 test/models/fk_test_has_pk.rb create mode 100644 test/models/sqlserver/fk_has_fk.rb create mode 100644 test/models/sqlserver/fk_has_pk.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a2a7a6e31..60e219830 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -230,23 +230,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'disabling referential integrity' do before do - connection.disable_referential_integrity { FkTestHasPk.delete_all; FkTestHasFk.delete_all } - @parent = FkTestHasPk.create! - @member = FkTestHasFk.create!(fk_id: @parent.id) + connection.disable_referential_integrity { SSTestHasPk.delete_all; SSTestHasFk.delete_all } + @parent = SSTestHasPk.create! + @member = SSTestHasFk.create!(fk_id: @parent.id) end it 'NOT ALLOW by default the deletion of a referenced parent' do - FkTestHasPk.connection.disable_referential_integrity { } + SSTestHasPk.connection.disable_referential_integrity { } assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end it 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do - FkTestHasPk.connection.disable_referential_integrity { @parent.destroy } + SSTestHasPk.connection.disable_referential_integrity { @parent.destroy } end it 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do assert_raise(ActiveRecord::StatementInvalid) do - FkTestHasPk.connection.disable_referential_integrity { } + SSTestHasPk.connection.disable_referential_integrity { } @parent.destroy end end diff --git a/test/models/fk_test_has_fk.rb b/test/models/fk_test_has_fk.rb deleted file mode 100644 index 23de5cf95..000000000 --- a/test/models/fk_test_has_fk.rb +++ /dev/null @@ -1,2 +0,0 @@ -class FkTestHasFk < ActiveRecord::Base -end diff --git a/test/models/fk_test_has_pk.rb b/test/models/fk_test_has_pk.rb deleted file mode 100644 index c76e294ce..000000000 --- a/test/models/fk_test_has_pk.rb +++ /dev/null @@ -1,2 +0,0 @@ -class FkTestHasPk < ActiveRecord::Base -end diff --git a/test/models/sqlserver/fk_has_fk.rb b/test/models/sqlserver/fk_has_fk.rb new file mode 100644 index 000000000..883c9dcee --- /dev/null +++ b/test/models/sqlserver/fk_has_fk.rb @@ -0,0 +1,3 @@ +class SSTestHasFk < ActiveRecord::Base + self.table_name = 'sst_has_fks' +end diff --git a/test/models/sqlserver/fk_has_pk.rb b/test/models/sqlserver/fk_has_pk.rb new file mode 100644 index 000000000..bb3889bdd --- /dev/null +++ b/test/models/sqlserver/fk_has_pk.rb @@ -0,0 +1,3 @@ +class SSTestHasPk < ActiveRecord::Base + self.table_name = 'sst_has_pks' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 58ff3e343..1ff3d74ab 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -11,6 +11,15 @@ t.column :COLUMN2, :integer end + create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } + create_table(:sst_has_pks, force: true) { } + execute <<-ADDFKSQL + ALTER TABLE sst_has_fks + ADD CONSTRAINT FK__sst_has_fks_id + FOREIGN KEY ([fk_id]) + REFERENCES [sst_has_pks] ([id]) + ADDFKSQL + @@ -47,15 +56,6 @@ t.column :smalldatetime, :smalldatetime end - create_table(:fk_test_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } - create_table(:fk_test_has_pks, force: true) { } - execute <<-ADDFKSQL - ALTER TABLE fk_test_has_fks - ADD CONSTRAINT FK__fk_test_has_fk_fk_id - FOREIGN KEY (#{quote_column_name('fk_id')}) - REFERENCES #{quote_table_name('fk_test_has_pks')} (#{quote_column_name('id')}) - ADDFKSQL - create_table :sql_server_unicodes, force: true do |t| t.column :nchar, :nchar t.column :nvarchar, :nvarchar From fac18b5d3d0a1be011539d5510090197d65d1011 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 12 Jan 2015 16:54:42 -0500 Subject: [PATCH 0217/1412] Fix database statements tests. --- .../sqlserver/schema_cache.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 103 +++++++++--------- test/schema/sqlserver_specific_schema.rb | 8 +- 3 files changed, 51 insertions(+), 62 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index 4a0e5af23..b9c2b846c 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -87,7 +87,7 @@ def view_information(table_name) private def table_name_key(table_name) - SQLServer::Utils.extract_identifiers(table_name).object + SQLServer::Utils.extract_identifiers(table_name).quoted end def prepare_views diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 60e219830..d0f3d1d6e 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -18,6 +18,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase + fixtures :tasks + let(:connection) { ActiveRecord::Base.connection } let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } @@ -253,28 +255,34 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'For DatabaseStatements' do - - describe "finding out what user_options are available" do + describe 'database statements' do - it "run the database consistency checker useroptions command" do - keys = [:textsize, :language, :isolation_level, :dateformat] - user_options = connection.user_options - keys.each do |key| - msg = "Expected key:#{key} in user_options:#{user_options.inspect}" - assert user_options.key?(key), msg - end + it "run the database consistency checker useroptions command" do + keys = [:textsize, :language, :isolation_level, :dateformat] + user_options = connection.user_options + keys.each do |key| + msg = "Expected key:#{key} in user_options:#{user_options.inspect}" + assert user_options.key?(key), msg end + end - it "return a underscored key hash with indifferent access of the results" do - user_options = connection.user_options - assert_equal 'read committed', user_options['isolation_level'] - assert_equal 'read committed', user_options[:isolation_level] - end + it "return a underscored key hash with indifferent access of the results" do + user_options = connection.user_options + assert_equal 'read committed', user_options['isolation_level'] + assert_equal 'read committed', user_options[:isolation_level] + end + + describe '#run_with_isolation_level' do - end unless sqlserver_azure? + let(:task1) { tasks(:first_task) } + let(:task2) { tasks(:another_task) } - describe "altering isolation levels" do + before do + assert task1, 'Tasks :first_task should be in AR fixtures' + assert task2, 'Tasks :another_task should be in AR fixtures' + good_isolation_level = connection.user_options_isolation_level.blank? || connection.user_options_isolation_level =~ /read committed/i + assert good_isolation_level, "User isolation level is not at a happy starting place: #{connection.user_options_isolation_level.inspect}" + end it "barf if the requested isolation level is not valid" do assert_raise(ArgumentError) do @@ -282,48 +290,35 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - describe "with a valid isolation level" do - - before do - @t1 = tasks(:first_task) - @t2 = tasks(:another_task) - assert @t1, 'Tasks :first_task should be in AR fixtures' - assert @t2, 'Tasks :another_task should be in AR fixtures' - good_isolation_level = connection.user_options_isolation_level.blank? || connection.user_options_isolation_level =~ /read committed/i - assert good_isolation_level, "User isolation level is not at a happy starting place: #{connection.user_options_isolation_level.inspect}" - end - - it 'allow #run_with_isolation_level to not take a block to set it' do - begin - connection.run_with_isolation_level 'READ UNCOMMITTED' - assert_match %r|read uncommitted|i, connection.user_options_isolation_level - ensure - connection.run_with_isolation_level 'READ COMMITTED' - end + it 'allow #run_with_isolation_level to not take a block to set it' do + begin + connection.run_with_isolation_level 'READ UNCOMMITTED' + assert_match %r|read uncommitted|i, connection.user_options_isolation_level + ensure + connection.run_with_isolation_level 'READ COMMITTED' end + end - it 'return block value using #run_with_isolation_level' do - assert_equal Task.all.sort, connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } - end + it 'return block value using #run_with_isolation_level' do + assert_equal Task.all.sort, connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } + end - it 'pass a read uncommitted isolation level test' do - assert_nil @t2.starting, 'Fixture should have this empty.' - begin - Task.transaction do - @t2.starting = Time.now - @t2.save - @dirty_t2 = connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(@t2.id) } - raise ActiveRecord::ActiveRecordError - end - rescue - 'Do Nothing' + it 'pass a read uncommitted isolation level test' do + assert_nil task2.starting, 'Fixture should have this empty.' + begin + Task.transaction do + task2.starting = Time.now + task2.save + @dirty_t2 = connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(task2.id) } + raise ActiveRecord::ActiveRecordError end - assert @dirty_t2, 'Should have a Task record from within block above.' - assert @dirty_t2.starting, 'Should have a dirty date.' - assert_nil Task.find(@t2.id).starting, 'Should be nil again from botched transaction above.' + rescue + 'Do Nothing' end - - end unless sqlserver_azure? + assert @dirty_t2, 'Should have a Task record from within block above.' + assert @dirty_t2.starting, 'Should have a dirty date.' + assert_nil Task.find(task2.id).starting, 'Should be nil again from botched transaction above.' + end end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 1ff3d74ab..30d0b3d9b 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -23,13 +23,7 @@ - create_table :float_data, force: true do |t| - t.float :temperature - t.float :temperature_8, limit: 8 - t.float :temperature_24, limit: 24 - t.float :temperature_32, limit: 32 - t.float :temperature_53, limit: 53 - end + create_table :defaults, force: true do |t| t.column :positive_integer, :integer, default: 1 From f8c7fe5540048b63586ca6a122ceafbada01dd01 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 12 Jan 2015 18:37:34 -0500 Subject: [PATCH 0218/1412] Refactor views test indentation. --- .../connection_adapters/sqlserver_column.rb | 4 + test/cases/adapter_test_sqlserver.rb | 279 ++++++++---------- test/models/customers_view.rb | 3 - test/models/sqlserver/customers_view.rb | 3 + test/schema/sqlserver_specific_schema.rb | 31 +- 5 files changed, 150 insertions(+), 170 deletions(-) delete mode 100644 test/models/customers_view.rb create mode 100644 test/models/sqlserver/customers_view.rb diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 5c875a40a..6ccc01df3 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -16,6 +16,10 @@ def sql_type_for_statement end end + def primary? + is_identity? || is_primary? + end + def is_identity? @sqlserver_options[:is_identity] end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index d0f3d1d6e..a7a694201 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -4,18 +4,6 @@ require 'models/subscriber' require 'models/minimalistic' -# require 'models/reply' -# require 'models/joke' -# require 'models/post' -# require 'models/sqlserver/fk_test_has_pk' -# require 'models/sqlserver/fk_test_has_fk' -# require 'models/sqlserver/customers_view' -# require 'models/sqlserver/string_defaults_big_view' -# require 'models/sqlserver/string_defaults_view' -# require 'models/sqlserver/topic' -# require 'models/sqlserver/upper_test_default' -# require 'models/sqlserver/upper_test_lowered' - class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks @@ -324,198 +312,179 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'For SchemaStatements' do - - describe 'returning from #type_to_sql' do - - it 'create integers when no limit supplied' do - assert_equal 'integer', connection.type_to_sql(:integer) - end - - it 'create integers when limit is 4' do - assert_equal 'integer', connection.type_to_sql(:integer, 4) - end - - it 'create integers when limit is 3' do - assert_equal 'integer', connection.type_to_sql(:integer, 3) - end - - it 'create smallints when limit is less than 3' do - assert_equal 'smallint', connection.type_to_sql(:integer, 2) - assert_equal 'smallint', connection.type_to_sql(:integer, 1) - end - - it 'create bigints when limit is greateer than 4' do - assert_equal 'bigint', connection.type_to_sql(:integer, 5) - assert_equal 'bigint', connection.type_to_sql(:integer, 6) - assert_equal 'bigint', connection.type_to_sql(:integer, 7) - assert_equal 'bigint', connection.type_to_sql(:integer, 8) - end - - it 'create floats when no limit supplied' do - assert_equal 'float(8)', connection.type_to_sql(:float) - end + describe 'schema statements' do - it 'create floats when limit is supplied' do - assert_equal 'float(27)', connection.type_to_sql(:float, 27) - end + it 'create integers when no limit supplied' do + assert_equal 'integer', connection.type_to_sql(:integer) + end + it 'create integers when limit is 4' do + assert_equal 'integer', connection.type_to_sql(:integer, 4) end - end + it 'create integers when limit is 3' do + assert_equal 'integer', connection.type_to_sql(:integer, 3) + end - describe 'For indexes' do + it 'create smallints when limit is less than 3' do + assert_equal 'smallint', connection.type_to_sql(:integer, 2) + assert_equal 'smallint', connection.type_to_sql(:integer, 1) + end - before do - @desc_index_name = 'idx_credit_limit_test_desc' - connection.execute "CREATE INDEX [#{@desc_index_name}] ON [accounts] (credit_limit DESC)" + it 'create bigints when limit is greateer than 4' do + assert_equal 'bigint', connection.type_to_sql(:integer, 5) + assert_equal 'bigint', connection.type_to_sql(:integer, 6) + assert_equal 'bigint', connection.type_to_sql(:integer, 7) + assert_equal 'bigint', connection.type_to_sql(:integer, 8) end - after do - connection.execute "DROP INDEX [#{@desc_index_name}] ON [accounts]" + it 'create floats when no limit supplied' do + assert_equal 'float(8)', connection.type_to_sql(:float) end - it 'have indexes with descending order' do - assert connection.indexes('accounts').find { |i| i.name == @desc_index_name } + it 'create floats when limit is supplied' do + assert_equal 'float(27)', connection.type_to_sql(:float, 27) end end - describe 'For views' do + describe 'indexes' do - describe 'using connection.views' do + let(:desc_index_name) { 'idx_credit_limit_test_desc' } - it 'return an array' do - assert_instance_of Array, connection.views - end - - it 'find CustomersView table name' do - connection.views.must_include 'customers_view' - end - - it 'work with dynamic finders' do - name = 'MetaSkills' - customer = CustomersView.create! name: name - assert_equal customer, CustomersView.find_by_name(name) + it 'have indexes with descending order' do + begin + connection.execute "CREATE INDEX [#{desc_index_name}] ON [accounts] (credit_limit DESC)" + assert connection.indexes('accounts').find { |i| i.name == desc_index_name } + ensure + connection.execute "DROP INDEX [#{desc_index_name}] ON [accounts]" end + end - it 'not contain system views' do - systables = ['sysconstraints','syssegments'] - systables.each do |systable| - assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array." - end - end + end - it 'allow the connection#view_information method to return meta data on the view' do - view_info = connection.send(:view_information,'customers_view') - assert_equal('customers_view', view_info['TABLE_NAME']) - assert_match(/CREATE VIEW customers_view/, view_info['VIEW_DEFINITION']) - end + describe 'views' do - it 'allow the connection#view_table_name method to return true table_name for the view' do - assert_equal 'customers', connection.send(:view_table_name,'customers_view') - assert_equal 'topics', connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' - end + # Using connection.views + it 'return an array' do + assert_instance_of Array, connection.views end - describe 'used by a class for table_name' do - - describe 'with same column names' do + it 'find SSTestCustomersView table name' do + connection.views.must_include 'sst_customers_view' + end - it 'have matching column objects' do - columns = ['id','name','balance'] - assert !CustomersView.columns.blank? - assert_equal columns.size, CustomersView.columns.size - columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - CustomersView.columns_hash[colname], - "Column name #{colname.inspect} was not found in these columns #{CustomersView.columns.map(&:name).inspect}" - end - end + it 'work with dynamic finders' do + name = 'MetaSkills' + customer = SSTestCustomersView.create! name: name + assert_equal customer, SSTestCustomersView.find_by_name(name) + end - it 'find identity column' do - assert CustomersView.columns_hash['id'].primary - end + it 'not contain system views' do + systables = ['sysconstraints','syssegments'] + systables.each do |systable| + assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array." + end + end - it 'find default values' do - assert_equal 0, CustomersView.new.balance - end + it 'allow the connection#view_information method to return meta data on the view' do + view_info = connection.send(:view_information,'sst_customers_view') + assert_equal('sst_customers_view', view_info['TABLE_NAME']) + assert_match(/CREATE VIEW sst_customers_view/, view_info['VIEW_DEFINITION']) + end - it 'respond true to table_exists?' do - assert CustomersView.table_exists? - end + it 'allow the connection#view_table_name method to return true table_name for the view' do + assert_equal 'customers', connection.send(:view_table_name,'sst_customers_view') + assert_equal 'topics', connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' + end - it 'have correct table name for all column objects' do - assert CustomersView.columns.all?{ |c| c.table_name == 'customers_view' }, - CustomersView.columns.map(&:table_name).inspect - end + # With same column names + it 'have matching column objects' do + columns = ['id','name','balance'] + assert !SSTestCustomersView.columns.blank? + assert_equal columns.size, SSTestCustomersView.columns.size + columns.each do |colname| + assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + SSTestCustomersView.columns_hash[colname], + "Column name #{colname.inspect} was not found in these columns #{SSTestCustomersView.columns.map(&:name).inspect}" end + end - describe 'with aliased column names' do - - it 'have matching column objects' do - columns = ['id','pretend_null'] - assert !StringDefaultsView.columns.blank? - assert_equal columns.size, StringDefaultsView.columns.size - columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - StringDefaultsView.columns_hash[colname], - "Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}" - end - end + it 'find identity column' do + pk_name = connection.primary_key(SSTestCustomersView.table_name) + pk_name.must_equal 'id' + pk_column = SSTestCustomersView.columns_hash[pk_name] + pk_column.must_be :primary? + end - it 'find identity column' do - assert StringDefaultsView.columns_hash['id'].primary - end + it 'find default values' do + assert_equal 0, SSTestCustomersView.new.balance + end - it 'find default values' do - assert_equal 'null', StringDefaultsView.new.pretend_null, - StringDefaultsView.columns_hash['pretend_null'].inspect - end + it 'respond true to table_exists?' do + assert SSTestCustomersView.table_exists? + end - it 'respond true to table_exists?' do - assert StringDefaultsView.table_exists? - end + it 'have correct table name for all column objects' do + assert SSTestCustomersView.columns.all?{ |c| c.table_name == 'sst_customers_view' }, + SSTestCustomersView.columns.map(&:table_name).inspect + end - it 'have correct table name for all column objects' do - assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, - StringDefaultsView.columns.map(&:table_name).inspect - end + # With aliased column names + it 'have matching column objects' do + columns = ['id','pretend_null'] + assert !StringDefaultsView.columns.blank? + assert_equal columns.size, StringDefaultsView.columns.size + columns.each do |colname| + assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + StringDefaultsView.columns_hash[colname], + "Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.columns.map(&:name).inspect}" end + end + it 'find identity column' do + assert StringDefaultsView.columns_hash['id'].primary end - describe 'doing identity inserts' do + it 'find default values' do + assert_equal 'null', StringDefaultsView.new.pretend_null, + StringDefaultsView.columns_hash['pretend_null'].inspect + end - before do - @view_insert_sql = "INSERT INTO [customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" - end + it 'respond true to table_exists?' do + assert StringDefaultsView.table_exists? + end - it 'respond true/tablename to #query_requires_identity_insert?' do - assert_equal '[customers_view]', connection.send(:query_requires_identity_insert?,@view_insert_sql) - end + it 'have correct table name for all column objects' do + assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, + StringDefaultsView.columns.map(&:table_name).inspect + end - it 'be able to do an identity insert' do - assert_nothing_raised { connection.execute(@view_insert_sql) } - assert CustomersView.find(420) - end + # Doing identity inserts + + let(:view_insert_sql) { "INSERT INTO [sst_customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" } + it 'respond true/tablename to #query_requires_identity_insert?' do + assert_equal '[sst_customers_view]', connection.send(:query_requires_identity_insert?, view_insert_sql) end - describe 'that have more than 4000 chars for their defintion' do + it 'be able to do an identity insert' do + assert_nothing_raised { connection.execute( view_insert_sql) } + assert SSTestCustomersView.find(420) + end - it 'cope with null returned for the defintion' do - assert_nothing_raised() { StringDefaultsBigView.columns } - end + # That have more than 4000 chars for their defintion - it 'using alternate view defintion still be able to find real default' do - assert_equal 'null', StringDefaultsBigView.new.pretend_null, - StringDefaultsBigView.columns_hash['pretend_null'].inspect - end + it 'cope with null returned for the defintion' do + assert_nothing_raised() { StringDefaultsBigView.columns } + end + it 'using alternate view defintion still be able to find real default' do + assert_equal 'null', StringDefaultsBigView.new.pretend_null, + StringDefaultsBigView.columns_hash['pretend_null'].inspect end end diff --git a/test/models/customers_view.rb b/test/models/customers_view.rb deleted file mode 100644 index 3e571271a..000000000 --- a/test/models/customers_view.rb +++ /dev/null @@ -1,3 +0,0 @@ -class CustomersView < ActiveRecord::Base - self.table_name = 'customers_view' -end \ No newline at end of file diff --git a/test/models/sqlserver/customers_view.rb b/test/models/sqlserver/customers_view.rb new file mode 100644 index 000000000..5c5a994e2 --- /dev/null +++ b/test/models/sqlserver/customers_view.rb @@ -0,0 +1,3 @@ +class SSTestCustomersView < ActiveRecord::Base + self.table_name = 'sst_customers_view' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 30d0b3d9b..d6629ed04 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -20,6 +20,24 @@ REFERENCES [sst_has_pks] ([id]) ADDFKSQL + # Views + + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_customers_view') DROP VIEW sst_customers_view" + execute <<-CUSTOMERSVIEW + CREATE VIEW sst_customers_view AS + SELECT id, name, balance + FROM customers + CUSTOMERSVIEW + + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_view') DROP VIEW string_defaults_view" + execute <<-STRINGDEFAULTSVIEW + CREATE VIEW string_defaults_view AS + SELECT id, string_with_pretend_null_one as pretend_null + FROM string_defaults + STRINGDEFAULTSVIEW + + + @@ -143,19 +161,8 @@ execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'quoted-view2') DROP VIEW [quoted-view2]" execute "CREATE VIEW [quoted-view2] AS \n /*#{'x'*4000}}*/ \n SELECT * FROM [quoted-table]" - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'customers_view') DROP VIEW customers_view" - execute <<-CUSTOMERSVIEW - CREATE VIEW customers_view AS - SELECT id, name, balance - FROM customers - CUSTOMERSVIEW - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_view') DROP VIEW string_defaults_view" - execute <<-STRINGDEFAULTSVIEW - CREATE VIEW string_defaults_view AS - SELECT id, string_with_pretend_null_one as pretend_null - FROM string_defaults - STRINGDEFAULTSVIEW + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_big_view') DROP VIEW string_defaults_big_view" execute <<-STRINGDEFAULTSBIGVIEW From aa5358398fb6d2113fb30d05b1e3bac74c12ff12 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 14 Jan 2015 21:05:52 -0500 Subject: [PATCH 0219/1412] More tests passing. Standardize SchemaCache in/out policy. --- .../sqlserver/schema_cache.rb | 68 +++-- .../sqlserver/schema_statements.rb | 47 ++-- .../connection_adapters/sqlserver_column.rb | 4 - test/cases/adapter_test_sqlserver.rb | 66 ++--- test/cases/named_scoping_test_sqlserver.rb | 6 - test/cases/schema_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 261 ++++++------------ test/models/no_pk_data.rb | 3 - test/models/post.rb | 3 - test/models/sql_server_dollar_table_name.rb | 3 - test/models/sql_server_natural_pk_data.rb | 4 - test/models/sql_server_natural_pk_int_data.rb | 3 - test/models/sql_server_quoted_table.rb | 3 - test/models/sql_server_quoted_view_1.rb | 3 - test/models/sql_server_quoted_view_2.rb | 3 - test/models/sqlserver/dollar_table_name.rb | 3 + .../edge_schema.rb} | 10 +- test/models/sqlserver/natural_pk_data.rb | 4 + test/models/sqlserver/natural_pk_int_data.rb | 3 + test/models/sqlserver/no_pk_data.rb | 3 + test/models/sqlserver/quoted_table.rb | 3 + test/models/sqlserver/quoted_view_1.rb | 3 + test/models/sqlserver/quoted_view_2.rb | 3 + test/models/sqlserver/string_default.rb | 3 + .../sqlserver/string_defaults_big_view.rb | 3 + test/models/sqlserver/string_defaults_view.rb | 3 + test/models/string_default.rb | 2 - test/models/string_defaults_big_view.rb | 3 - test/models/string_defaults_view.rb | 3 - test/schema/sqlserver_specific_schema.rb | 167 ++++++----- 30 files changed, 296 insertions(+), 399 deletions(-) delete mode 100644 test/cases/named_scoping_test_sqlserver.rb delete mode 100644 test/models/no_pk_data.rb delete mode 100644 test/models/post.rb delete mode 100644 test/models/sql_server_dollar_table_name.rb delete mode 100644 test/models/sql_server_natural_pk_data.rb delete mode 100644 test/models/sql_server_natural_pk_int_data.rb delete mode 100644 test/models/sql_server_quoted_table.rb delete mode 100644 test/models/sql_server_quoted_view_1.rb delete mode 100644 test/models/sql_server_quoted_view_2.rb create mode 100644 test/models/sqlserver/dollar_table_name.rb rename test/models/{sql_server_edge_schema.rb => sqlserver/edge_schema.rb} (77%) create mode 100644 test/models/sqlserver/natural_pk_data.rb create mode 100644 test/models/sqlserver/natural_pk_int_data.rb create mode 100644 test/models/sqlserver/no_pk_data.rb create mode 100644 test/models/sqlserver/quoted_table.rb create mode 100644 test/models/sqlserver/quoted_view_1.rb create mode 100644 test/models/sqlserver/quoted_view_2.rb create mode 100644 test/models/sqlserver/string_default.rb create mode 100644 test/models/sqlserver/string_defaults_big_view.rb create mode 100644 test/models/sqlserver/string_defaults_view.rb delete mode 100644 test/models/string_default.rb delete mode 100644 test/models/string_defaults_big_view.rb delete mode 100644 test/models/string_defaults_view.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb index b9c2b846c..427794697 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb @@ -3,8 +3,6 @@ module ConnectionAdapters module SQLServer class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache - attr_reader :view_information - def initialize(conn) super @views = {} @@ -14,28 +12,32 @@ def initialize(conn) # Superclass Overrides def primary_keys(table_name) - super(table_name_key(table_name)) + name = key(table_name) + @primary_keys[name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil end def table_exists?(table_name) - name = table_name_key(table_name) - super(name) || view_exists?(name) - end - - def add(table_name) - super(table_name_key(table_name)) + name = key(table_name) + prepare_tables_and_views + return @tables[name] if @tables.key? name + table_exists = @tables[name] = connection.table_exists?(table_name) + table_exists || view_exists?(table_name) end def tables(name) - super(table_name_key(name)) + super(key(name)) end def columns(table_name) - super(table_name_key(table_name)) + name = key(table_name) + @columns[name] ||= connection.columns(table_name) end def columns_hash(table_name) - super(table_name_key(table_name)) + name = key(table_name) + @columns_hash[name] ||= Hash[columns(table_name).map { |col| + [col.name, col] + }] end def clear! @@ -49,10 +51,13 @@ def size end def clear_table_cache!(table_name) - table_name = table_name_key(table_name) - super(table_name) - @views.delete table_name - @view_information.delete table_name + name = key(table_name) + @columns.delete name + @columns_hash.delete name + @primary_keys.delete name + @tables.delete name + @views.delete name + @view_information.delete name end def marshal_dump @@ -66,19 +71,15 @@ def marshal_load(array) # SQL Server Specific - def view_names - @views.select{ |k,v| v }.keys - end - def view_exists?(table_name) - name = table_name_key(table_name) - prepare_views if @views.empty? + name = key(table_name) + prepare_tables_and_views return @views[name] if @views.key? name - @views[name] = connection.views.include?(name) + @views[name] = connection.views.include?(table_name) end def view_information(table_name) - name = table_name_key(table_name) + name = key(table_name) return @view_information[name] if @view_information.key? name @view_information[name] = connection.send(:view_information, table_name) end @@ -86,12 +87,25 @@ def view_information(table_name) private - def table_name_key(table_name) - SQLServer::Utils.extract_identifiers(table_name).quoted + def identifier(table_name) + SQLServer::Utils.extract_identifiers(table_name) + end + + def key(table_name) + identifier(table_name).quoted + end + + def prepare_tables_and_views + prepare_views if @views.empty? + prepare_tables if @tables.empty? + end + + def prepare_tables + connection.tables.each { |table| @tables[key(table)] = true } end def prepare_views - connection.views.each { |view| @views[view] = true } + connection.views.each { |view| @views[key(view)] = true } end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5d0a0cf0a..45fc06d81 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -187,10 +187,9 @@ def initialize_native_database_types end def column_definitions(table_name) - db_name = SQLServer::Utils.extract_identifiers(table_name).database - db_name_with_period = "#{db_name}." if db_name - table_schema = SQLServer::Utils.extract_identifiers(table_name).schema - table_name = SQLServer::Utils.extract_identifiers(table_name).object + identifier = SQLServer::Utils.extract_identifiers(table_name) + database = "#{identifier.database_quoted}." if identifier.database_quoted + table_name = identifier.quoted sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -203,7 +202,7 @@ def column_definitions(table_name) columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH - ELSE COL_LENGTH('#{db_name_with_period}'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) + ELSE COL_LENGTH('#{database}'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) END AS [length], CASE WHEN columns.IS_NULLABLE = 'YES' THEN 1 @@ -214,32 +213,32 @@ def column_definitions(table_name) ELSE NULL END AS [is_primary], c.is_identity AS [is_identity] - FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns - LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC + FROM #{database}INFORMATION_SCHEMA.COLUMNS columns + LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' - LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU + LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.COLUMN_NAME = columns.COLUMN_NAME AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA - INNER JOIN #{db_name}.sys.schemas AS s + INNER JOIN #{identifier.database_quoted}.sys.schemas AS s ON s.name = columns.TABLE_SCHEMA AND s.schema_id = s.schema_id - INNER JOIN #{db_name}.sys.objects AS o + INNER JOIN #{identifier.database_quoted}.sys.objects AS o ON s.schema_id = o.schema_id AND o.is_ms_shipped = 0 AND o.type IN ('U', 'V') AND o.name = columns.TABLE_NAME - INNER JOIN #{db_name}.sys.columns AS c + INNER JOIN #{identifier.database_quoted}.sys.columns AS c ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME WHERE columns.TABLE_NAME = @0 - AND columns.TABLE_SCHEMA = #{table_schema.blank? ? 'schema_name()' : '@1'} + AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : '@1'} ORDER BY columns.ordinal_position }.gsub(/[ \t\r\n]+/, ' ') - binds = [['table_name', table_name]] - binds << ['table_schema', table_schema] unless table_schema.blank? + binds = [['table_name', identifier.object]] + binds << ['table_schema', identifier.schema] unless identifier.schema.blank? results = do_exec_query(sql, 'SCHEMA', binds) results.map do |ci| ci = ci.symbolize_keys @@ -257,10 +256,10 @@ def column_definitions(table_name) else ci[:type] end - if ci[:default_value].nil? && schema_cache.view_names.include?(table_name) + if ci[:default_value].nil? && schema_cache.view_exists?(table_name) real_table_name = table_name_or_views_table_name(table_name) real_column_name = views_real_column_name(table_name, ci[:name]) - col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'" + col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{database}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'" ci[:default_value] = select_value col_default_sql, 'SCHEMA' end ci[:default_value] = case ci[:default_value] @@ -306,13 +305,14 @@ def remove_indexes(table_name, column_name) # === SQLServer Specific (Misc Helpers) ========================= # def get_table_name(sql) - if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i + tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i Regexp.last_match[3] || Regexp.last_match[4] elsif sql =~ /FROM\s+([^\(\s]+)\s*/i Regexp.last_match[1] else nil end + SQLServer::Utils.extract_identifiers(tn).object end def default_constraint_name(table_name, column_name) @@ -344,19 +344,18 @@ def view_information(table_name) view_info = view_info.with_indifferent_access if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000 view_info[:VIEW_DEFINITION] = begin - select_values("EXEC sp_helptext #{quote_table_name(table_name)}", 'SCHEMA').join - rescue - warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;" - nil - end + select_values("EXEC sp_helptext #{quote_table_name(table_name)}", 'SCHEMA').join + rescue + warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;" + nil + end end end view_info end def table_name_or_views_table_name(table_name) - unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object - schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name + schema_cache.view_exists?(table_name) ? view_table_name(table_name) : table_name end def views_real_column_name(table_name, column_name) diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 6ccc01df3..5c875a40a 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -16,10 +16,6 @@ def sql_type_for_statement end end - def primary? - is_identity? || is_primary? - end - def is_identity? @sqlserver_options[:is_identity] end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a7a694201..336ffca68 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,6 +1,7 @@ require 'cases/helper_sqlserver' require 'models/topic' require 'models/task' +require 'models/post' require 'models/subscriber' require 'models/minimalistic' @@ -65,10 +66,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert !connection.send(:insert_sql?,'SELECT...') end - it 'return quoted table name from basic INSERT UPDATE and SELECT statements' do - assert_equal '[funny_jokes]', connection.send(:get_table_name, basic_insert_sql) - assert_equal '[customers]', connection.send(:get_table_name, basic_update_sql) - assert_equal '[customers]', connection.send(:get_table_name, basic_select_sql) + it 'return unquoted table name object from basic INSERT UPDATE and SELECT statements' do + assert_equal 'funny_jokes', connection.send(:get_table_name, basic_insert_sql) + assert_equal 'customers', connection.send(:get_table_name, basic_update_sql) + assert_equal 'customers', connection.send(:get_table_name, basic_select_sql) end describe 'with different language' do @@ -413,10 +414,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'find identity column' do - pk_name = connection.primary_key(SSTestCustomersView.table_name) - pk_name.must_equal 'id' - pk_column = SSTestCustomersView.columns_hash[pk_name] - pk_column.must_be :primary? + SSTestCustomersView.primary_key.must_equal 'id' + connection.primary_key(SSTestCustomersView.table_name).must_equal 'id' + SSTestCustomersView.columns_hash['id'].must_be :is_identity? end it 'find default values' do @@ -427,40 +427,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert SSTestCustomersView.table_exists? end - it 'have correct table name for all column objects' do - assert SSTestCustomersView.columns.all?{ |c| c.table_name == 'sst_customers_view' }, - SSTestCustomersView.columns.map(&:table_name).inspect - end - # With aliased column names it 'have matching column objects' do columns = ['id','pretend_null'] - assert !StringDefaultsView.columns.blank? - assert_equal columns.size, StringDefaultsView.columns.size + assert !SSTestStringDefaultsView.columns.blank? + assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - StringDefaultsView.columns_hash[colname], - "Column name #{colname.inspect} was not found in these columns #{StringDefaultsView.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 it 'find identity column' do - assert StringDefaultsView.columns_hash['id'].primary + SSTestStringDefaultsView.primary_key.must_equal 'id' + connection.primary_key(SSTestStringDefaultsView.table_name).must_equal 'id' + SSTestStringDefaultsView.columns_hash['id'].must_be :is_identity? end it 'find default values' do - assert_equal 'null', StringDefaultsView.new.pretend_null, - StringDefaultsView.columns_hash['pretend_null'].inspect + assert_equal 'null', SSTestStringDefaultsView.new.pretend_null, + SSTestStringDefaultsView.columns_hash['pretend_null'].inspect end it 'respond true to table_exists?' do - assert StringDefaultsView.table_exists? - end - - it 'have correct table name for all column objects' do - assert StringDefaultsView.columns.all?{ |c| c.table_name == 'string_defaults_view' }, - StringDefaultsView.columns.map(&:table_name).inspect + assert SSTestStringDefaultsView.table_exists? end # Doing identity inserts @@ -479,31 +471,15 @@ class AdapterTestSQLServer < ActiveRecord::TestCase # That have more than 4000 chars for their defintion it 'cope with null returned for the defintion' do - assert_nothing_raised() { StringDefaultsBigView.columns } + assert_nothing_raised() { SSTestStringDefaultsBigView.columns } end it 'using alternate view defintion still be able to find real default' do - assert_equal 'null', StringDefaultsBigView.new.pretend_null, - StringDefaultsBigView.columns_hash['pretend_null'].inspect + assert_equal 'null', SSTestStringDefaultsBigView.new.pretend_null, + SSTestStringDefaultsBigView.columns_hash['pretend_null'].inspect end end end - -module ActiveRecord - class AdapterTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_update_prepared_statement] - # Like PostgreSQL, SQL Server does not support null bytes in strings. - # DECLARE @mybin1 binary(5), @mybin2 binary(5) - # SET @mybin1 = 0x00 - # SELECT 'a'+CONVERT(varchar(5), @mybin1) + 'aaaaa' - # This is not run for PostgreSQL at the rails level and the same should happen for SQL Server - # Until that patch is made to rails we are preventing this test from running in this gem. - include ARTest::SQLServer::CoercedTest - - fixtures :authors - end -end diff --git a/test/cases/named_scoping_test_sqlserver.rb b/test/cases/named_scoping_test_sqlserver.rb deleted file mode 100644 index 3aee2d6fa..000000000 --- a/test/cases/named_scoping_test_sqlserver.rb +++ /dev/null @@ -1,6 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/sqlserver/post' - -# This file is really just to ensure we fix the order by on the -# :ranked_by_comments scope of Post diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 1e741d61f..a62c8b108 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -12,7 +12,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase should 'find primary key for tables with odd schema' do assert_equal 'legacy_id', @connection.primary_key('natural_pk_data') - assert SqlServerNaturalPkData.columns_hash['legacy_id'].primary + assert SSTestNaturalPkData.columns_hash['legacy_id'].primary end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 964f9017b..7618df389 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,37 +1,36 @@ require 'cases/helper_sqlserver' -require 'models/sqlserver/no_pk_data' -require 'models/sqlserver/sql_server_dollar_table_name' -require 'models/sqlserver/sql_server_edge_schema' -require 'models/sqlserver/sql_server_natural_pk_int_data' -require 'models/sqlserver/sql_server_quoted_table' -require 'models/sqlserver/sql_server_quoted_view_1' -require 'models/sqlserver/sql_server_quoted_view_2' -require 'models/sqlserver/sql_server_tinyint_pk' -require 'models/sqlserver/string_default' + class SpecificSchemaTestSQLServer < ActiveRecord::TestCase - should 'be able to complex count tables with no primary key' do - NoPkData.delete_all - 10.times { |n| NoPkData.create! name: "Test#{n}" } - assert_equal 1, NoPkData.where(name: 'Test5').count + after { SSTestEdgeSchema.delete_all } + + it 'handle dollar symbols' do + SSTestDollarTableName.new.save + SSTestDollarTableName.limit(20).offset(1) end - should 'quote table names properly even when they are views' do - obj = SqlServerQuotedTable.create! - assert_nothing_raised { SqlServerQuotedTable.first } - obj = SqlServerQuotedView1.create! - assert_nothing_raised { SqlServerQuotedView1.first } - obj = SqlServerQuotedView2.create! - assert_nothing_raised { SqlServerQuotedView2.first } + it 'be able to complex count tables with no primary key' do + SSTestNoPkData.delete_all + 10.times { |n| SSTestNoPkData.create! name: "Test#{n}" } + assert_equal 1, SSTestNoPkData.where(name: 'Test5').count end - should 'cope with multi line defaults' do - default = StringDefault.new + it 'quote table names properly even when they are views' do + obj = SSTestQuotedTable.create! + assert_nothing_raised { assert SSTestQuotedTable.first } + obj = SSTestQuotedView1.create! + assert_nothing_raised { assert SSTestQuotedView1.first } + obj = SSTestQuotedView2.create! + assert_nothing_raised { assert SSTestQuotedView2.first } + end + + it 'cope with multi line defaults' do + default = SSTestStringDefault.new assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default end - should 'default strings before save' do - default = StringDefault.new + it 'default strings before save' do + default = SSTestStringDefault.new assert_equal nil, default.string_with_null_default assert_equal 'null', default.string_with_pretend_null_one assert_equal '(null)', default.string_with_pretend_null_two @@ -40,8 +39,8 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_equal '(3)', default.string_with_pretend_paren_three end - should 'default strings after save' do - default = StringDefault.create + it 'default strings after save' do + default = SSTestStringDefault.create assert_equal nil, default.string_with_null_default assert_equal 'null', default.string_with_pretend_null_one assert_equal '(null)', default.string_with_pretend_null_two @@ -49,164 +48,86 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_equal '(NULL)', default.string_with_pretend_null_four end - context 'Testing edge case schemas' do - - setup do - @edge_class = SqlServerEdgeSchema - end - - context 'with tinyint primary key' do - - should 'work with identity inserts and finders' do - record = SqlServerTinyintPk.new name: '1' - record.id = 1 - record.save! - assert_equal record, SqlServerTinyintPk.find(1) - end - - end - - context 'with natural primary keys' do - - should 'work with identity inserts' do - record = SqlServerNaturalPkData.new name: 'Test', description: 'Natural identity inserts.' - record.id = '12345ABCDE' - assert record.save - assert_equal '12345ABCDE', record.reload.id - end - - should 'work with identity inserts when the key is an int' do - record = SqlServerNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.' - record.id = 12 - assert record.save - assert_equal 12, record.reload.id - end - - should 'use primary key for row table order in pagination sql' do - sql = /OVER \(ORDER BY \[natural_pk_data\]\.\[legacy_id\] ASC\)/ - assert_sql(sql) { SqlServerNaturalPkData.limit(5).offset(5).load } - end - - end - - context 'with special quoted column' do - - should 'work as normal' do - @edge_class.delete_all - r = @edge_class.create! 'crazy]]quote' => 'crazyqoute' - assert @edge_class.columns_hash['crazy]]quote'] - assert_equal r, @edge_class.where('crazy]]quote' => 'crazyqoute').first - end - - end - - context 'with column names that have spaces' do - - should 'create record using a custom attribute reader and be able to load it back in' do - value = 'Saved value into a column that has a space in the name.' - record = @edge_class.create! with_spaces: value - assert_equal value, @edge_class.find(record.id).with_spaces - end + # Natural primary keys. - end - - context 'with description column' do - - setup do - @da = @edge_class.create! description: 'A' - @db = @edge_class.create! description: 'B' - @dc = @edge_class.create! description: 'C' - end - - teardown { @edge_class.delete_all } - - should 'allow all sorts of ordering without adapter munging it up' do - assert_equal ['A','B','C'], @edge_class.order('description').map(&:description) - assert_equal ['A','B','C'], @edge_class.order('description asc').map(&:description) - assert_equal ['A','B','C'], @edge_class.order('description ASC').map(&:description) - assert_equal ['C','B','A'], @edge_class.order('description desc').map(&:description) - assert_equal ['C','B','A'], @edge_class.order('description DESC').map(&:description) - end - - end - - context 'with bigint column' do - - setup do - @b5k = 5000 - @bi5k = @edge_class.create! bigint: @b5k, description: 'Five Thousand' - @bnum = 9_000_000_000_000_000_000 - @bimjr = @edge_class.create! bigint: @bnum, description: 'Close to max bignum' - end - - should 'can find by biginit' do - assert_equal @bi5k, @edge_class.find_by_bigint(@b5k) - assert_equal @b5k, @edge_class.select('bigint').where(bigint: @b5k).first.bigint - assert_equal @bimjr, @edge_class.find_by_bigint(@bnum) - assert_equal @bnum, @edge_class.select('bigint').where(bigint: @bnum).first.bigint - end - - end + it 'work with identity inserts' do + record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.' + record.id = '12345ABCDE' + assert record.save + assert_equal '12345ABCDE', record.reload.id + end - context 'with tinyint column' do + it 'work with identity inserts when the key is an int' do + record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.' + record.id = 12 + assert record.save + assert_equal 12, record.reload.id + end - setup do - @tiny1 = @edge_class.create! tinyint: 1 - @tiny255 = @edge_class.create! tinyint: 255 - end + it 'use primary key for row table order in pagination sql' do + sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY/ + assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load } + end - should 'not treat tinyint like boolean as mysql does' do - assert_equal 1, @edge_class.find_by_tinyint(1).tinyint - assert_equal 255, @edge_class.find_by_tinyint(255).tinyint - end - - should 'throw an error when going out of our tiny int bounds' do - assert_raise(ActiveRecord::StatementInvalid) { @edge_class.create! tinyint: 256 } - end + # Special quoted column - end + it 'work as normal' do + SSTestEdgeSchema.delete_all + r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute' + assert SSTestEdgeSchema.columns_hash['crazy]]quote'] + assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first + end - context 'with uniqueidentifier column' do - - setup do - @newid = ActiveRecord::Base.connection.newid_function - assert_guid @newid - end + # With column names that have spaces - should 'allow a simple insert and read of a column without a default function' do - obj = @edge_class.create! guid: @newid - assert_equal @newid, @edge_class.find(obj.id).guid - end + it 'create record using a custom attribute reader and be able to load it back in' do + value = 'Saved value into a column that has a space in the name.' + record = SSTestEdgeSchema.create! with_spaces: value + assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces + end - should 'record the default function name in the column definition but still show a nil real default, will use one day for insert/update' do - newid_column = @edge_class.columns_hash['guid_newid'] - assert newid_column.default_function.present? - assert_nil newid_column.default - assert_equal 'newid()', newid_column.default_function - newseqid_column = @edge_class.columns_hash['guid_newseqid'] - assert newseqid_column.default_function.present? - assert_nil newseqid_column.default - assert_equal 'newsequentialid()', newseqid_column.default_function - end + # With description column + + it 'allow all sorts of ordering without adapter munging it up with special description column' do + SSTestEdgeSchema.create! description: 'A' + SSTestEdgeSchema.create! description: 'B' + SSTestEdgeSchema.create! description: 'C' + assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description) + assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description) + assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description) + assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description) + assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description) + end - should 'use model callback to set get a new guid' do - obj = @edge_class.new - obj.new_id_setting = true - obj.save! - assert_guid obj.guid_newid - end + # With uniqueidentifier column - end + let(:newid) { ActiveRecord::Base.connection.newid_function } - context 'with strange table names' do + it 'returns a new id via connection newid_function' do + assert_guid newid + end - should 'handle dollar symbols' do - SqlServerDollarTableName.new.save - SqlServerDollarTableName.limit(20).offset(1) - end + it 'allow a simple insert and read of a column without a default function' do + obj = SSTestEdgeSchema.create! guid: newid + assert_equal newid, SSTestEdgeSchema.find(obj.id).guid + end - end + it 'record the default function name in the column definition but still show a nil real default, will use one day for insert/update' do + newid_column = SSTestEdgeSchema.columns_hash['guid_newid'] + assert newid_column.default_function.present? + assert_nil newid_column.default + assert_equal 'newid()', newid_column.default_function + newseqid_column = SSTestEdgeSchema.columns_hash['guid_newseqid'] + assert newseqid_column.default_function.present? + assert_nil newseqid_column.default + assert_equal 'newsequentialid()', newseqid_column.default_function + end + it 'use model callback to set get a new guid' do + obj = SSTestEdgeSchema.new + obj.new_id_setting = true + obj.save! + assert_guid obj.guid_newid end diff --git a/test/models/no_pk_data.rb b/test/models/no_pk_data.rb deleted file mode 100644 index f19cb75ea..000000000 --- a/test/models/no_pk_data.rb +++ /dev/null @@ -1,3 +0,0 @@ -class NoPkData < ActiveRecord::Base - self.table_name = 'no_pk_data' -end \ No newline at end of file diff --git a/test/models/post.rb b/test/models/post.rb deleted file mode 100644 index ba846ea4a..000000000 --- a/test/models/post.rb +++ /dev/null @@ -1,3 +0,0 @@ -class Post < ActiveRecord::Base - scope :ranked_by_comments, -> { order("comments_count DESC, id ASC") } -end diff --git a/test/models/sql_server_dollar_table_name.rb b/test/models/sql_server_dollar_table_name.rb deleted file mode 100644 index 9247abe7b..000000000 --- a/test/models/sql_server_dollar_table_name.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerDollarTableName < ActiveRecord::Base - self.table_name = 'my$strange_table' -end \ No newline at end of file diff --git a/test/models/sql_server_natural_pk_data.rb b/test/models/sql_server_natural_pk_data.rb deleted file mode 100644 index ba4d381db..000000000 --- a/test/models/sql_server_natural_pk_data.rb +++ /dev/null @@ -1,4 +0,0 @@ -class SqlServerNaturalPkData < ActiveRecord::Base - self.table_name = 'natural_pk_data' - self.primary_key = 'legacy_id' -end \ No newline at end of file diff --git a/test/models/sql_server_natural_pk_int_data.rb b/test/models/sql_server_natural_pk_int_data.rb deleted file mode 100644 index 47cd20609..000000000 --- a/test/models/sql_server_natural_pk_int_data.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerNaturalPkIntData < ActiveRecord::Base - self.table_name = 'natural_pk_int_data' -end \ No newline at end of file diff --git a/test/models/sql_server_quoted_table.rb b/test/models/sql_server_quoted_table.rb deleted file mode 100644 index 9218ddaae..000000000 --- a/test/models/sql_server_quoted_table.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerQuotedTable < ActiveRecord::Base - self.table_name = 'quoted-table' -end \ No newline at end of file diff --git a/test/models/sql_server_quoted_view_1.rb b/test/models/sql_server_quoted_view_1.rb deleted file mode 100644 index 96c81dfb7..000000000 --- a/test/models/sql_server_quoted_view_1.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerQuotedView1 < ActiveRecord::Base - self.table_name = 'quoted-view1' -end \ No newline at end of file diff --git a/test/models/sql_server_quoted_view_2.rb b/test/models/sql_server_quoted_view_2.rb deleted file mode 100644 index 2ca207c4d..000000000 --- a/test/models/sql_server_quoted_view_2.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerQuotedView2 < ActiveRecord::Base - self.table_name = 'quoted-view2' -end \ No newline at end of file diff --git a/test/models/sqlserver/dollar_table_name.rb b/test/models/sqlserver/dollar_table_name.rb new file mode 100644 index 000000000..b43e89600 --- /dev/null +++ b/test/models/sqlserver/dollar_table_name.rb @@ -0,0 +1,3 @@ +class SSTestDollarTableName < ActiveRecord::Base + self.table_name = 'sst_my$strange_table' +end diff --git a/test/models/sql_server_edge_schema.rb b/test/models/sqlserver/edge_schema.rb similarity index 77% rename from test/models/sql_server_edge_schema.rb rename to test/models/sqlserver/edge_schema.rb index 1d4131344..d84145dae 100644 --- a/test/models/sql_server_edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -1,6 +1,11 @@ -class SqlServerEdgeSchema < ActiveRecord::Base +class SSTestEdgeSchema < ActiveRecord::Base + + self.table_name = 'sst_edge_schemas' + attr_accessor :new_id_setting + before_create :set_new_id + def with_spaces read_attribute :'with spaces' end @@ -10,8 +15,9 @@ def with_spaces=(value) end protected + def set_new_id self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting end -end \ No newline at end of file +end diff --git a/test/models/sqlserver/natural_pk_data.rb b/test/models/sqlserver/natural_pk_data.rb new file mode 100644 index 000000000..0dbf67552 --- /dev/null +++ b/test/models/sqlserver/natural_pk_data.rb @@ -0,0 +1,4 @@ +class SSTestNaturalPkData < ActiveRecord::Base + self.table_name = 'sst_natural_pk_data' + self.primary_key = 'legacy_id' +end diff --git a/test/models/sqlserver/natural_pk_int_data.rb b/test/models/sqlserver/natural_pk_int_data.rb new file mode 100644 index 000000000..2a624f774 --- /dev/null +++ b/test/models/sqlserver/natural_pk_int_data.rb @@ -0,0 +1,3 @@ +class SSTestNaturalPkIntData < ActiveRecord::Base + self.table_name = 'sst_natural_pk_int_data' +end diff --git a/test/models/sqlserver/no_pk_data.rb b/test/models/sqlserver/no_pk_data.rb new file mode 100644 index 000000000..65c7c1ab2 --- /dev/null +++ b/test/models/sqlserver/no_pk_data.rb @@ -0,0 +1,3 @@ +class SSTestNoPkData < ActiveRecord::Base + self.table_name = 'sst_no_pk_data' +end diff --git a/test/models/sqlserver/quoted_table.rb b/test/models/sqlserver/quoted_table.rb new file mode 100644 index 000000000..87b0fba30 --- /dev/null +++ b/test/models/sqlserver/quoted_table.rb @@ -0,0 +1,3 @@ +class SSTestQuotedTable < ActiveRecord::Base + self.table_name = 'sst_quoted-table' +end diff --git a/test/models/sqlserver/quoted_view_1.rb b/test/models/sqlserver/quoted_view_1.rb new file mode 100644 index 000000000..ff5d3495a --- /dev/null +++ b/test/models/sqlserver/quoted_view_1.rb @@ -0,0 +1,3 @@ +class SSTestQuotedView1 < ActiveRecord::Base + self.table_name = 'sst_quoted-view1' +end diff --git a/test/models/sqlserver/quoted_view_2.rb b/test/models/sqlserver/quoted_view_2.rb new file mode 100644 index 000000000..bd03a9acd --- /dev/null +++ b/test/models/sqlserver/quoted_view_2.rb @@ -0,0 +1,3 @@ +class SSTestQuotedView2 < ActiveRecord::Base + self.table_name = 'sst_quoted-view2' +end diff --git a/test/models/sqlserver/string_default.rb b/test/models/sqlserver/string_default.rb new file mode 100644 index 000000000..b8938e2e8 --- /dev/null +++ b/test/models/sqlserver/string_default.rb @@ -0,0 +1,3 @@ +class SSTestStringDefault < ActiveRecord::Base + self.table_name = 'sst_string_defaults' +end diff --git a/test/models/sqlserver/string_defaults_big_view.rb b/test/models/sqlserver/string_defaults_big_view.rb new file mode 100644 index 000000000..a0626abc4 --- /dev/null +++ b/test/models/sqlserver/string_defaults_big_view.rb @@ -0,0 +1,3 @@ +class SSTestStringDefaultsBigView < ActiveRecord::Base + self.table_name = 'sst_string_defaults_big_view' +end diff --git a/test/models/sqlserver/string_defaults_view.rb b/test/models/sqlserver/string_defaults_view.rb new file mode 100644 index 000000000..01a83942f --- /dev/null +++ b/test/models/sqlserver/string_defaults_view.rb @@ -0,0 +1,3 @@ +class SSTestStringDefaultsView < ActiveRecord::Base + self.table_name = 'sst_string_defaults_view' +end diff --git a/test/models/string_default.rb b/test/models/string_default.rb deleted file mode 100644 index 31c8eaea3..000000000 --- a/test/models/string_default.rb +++ /dev/null @@ -1,2 +0,0 @@ -class StringDefault < ActiveRecord::Base -end \ No newline at end of file diff --git a/test/models/string_defaults_big_view.rb b/test/models/string_defaults_big_view.rb deleted file mode 100644 index 30a2da5c1..000000000 --- a/test/models/string_defaults_big_view.rb +++ /dev/null @@ -1,3 +0,0 @@ -class StringDefaultsBigView < ActiveRecord::Base - self.table_name = 'string_defaults_big_view' -end \ No newline at end of file diff --git a/test/models/string_defaults_view.rb b/test/models/string_defaults_view.rb deleted file mode 100644 index d8f702f00..000000000 --- a/test/models/string_defaults_view.rb +++ /dev/null @@ -1,3 +0,0 @@ -class StringDefaultsView < ActiveRecord::Base - self.table_name = 'string_defaults_view' -end \ No newline at end of file diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index d6629ed04..08a324786 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,16 +1,78 @@ ActiveRecord::Schema.define do + # Exhaustive Data Types + execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) create_table :sst_datatypes_migration, force: true do |t| t.column :real, :real end + + # Edge Cases + + create_table 'sst_my$strange_table', force: true do |t| + t.column :number, :real + end + create_table :SST_UPPER_TESTS, force: true do |t| t.column :COLUMN1, :string t.column :COLUMN2, :integer end + create_table :sst_no_pk_data, force: true, id: false do |t| + t.string :name + end + + create_table 'sst_quoted-table', force: true do |t| + end + 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]" + + create_table :sst_string_defaults, force: true do |t| + t.column :string_with_null_default, :string, default: nil + t.column :string_with_pretend_null_one, :string, default: 'null' + t.column :string_with_pretend_null_two, :string, default: '(null)' + t.column :string_with_pretend_null_three, :string, default: 'NULL' + t.column :string_with_pretend_null_four, :string, default: '(NULL)' + t.column :string_with_pretend_paren_three, :string, default: '(3)' + t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line." + end + + create_table :sst_edge_schemas, force: true do |t| + t.string :description + t.column :guid, :uniqueidentifier + t.column 'crazy]]quote', :string + t.column 'with spaces', :string + end + execute %|ALTER TABLE [sst_edge_schemas] ADD [guid_newid] uniqueidentifier DEFAULT NEWID();| + execute %|ALTER TABLE [sst_edge_schemas] ADD [guid_newseqid] uniqueidentifier DEFAULT NEWSEQUENTIALID();| + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_data') DROP TABLE sst_natural_pk_data" + execute <<-NATURALPKTABLESQL + CREATE TABLE sst_natural_pk_data( + parent_id int, + name nvarchar(255), + description nvarchar(1000), + legacy_id nvarchar(10) NOT NULL PRIMARY KEY + ) + NATURALPKTABLESQL + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_int_data') DROP TABLE sst_natural_pk_int_data" + execute <<-NATURALPKINTTABLESQL + CREATE TABLE sst_natural_pk_int_data( + legacy_id int NOT NULL PRIMARY KEY, + parent_id int, + name nvarchar(255), + description nvarchar(1000) + ) + NATURALPKINTTABLESQL + + + # Constraints + create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } create_table(:sst_has_pks, force: true) { } execute <<-ADDFKSQL @@ -29,13 +91,23 @@ FROM customers CUSTOMERSVIEW - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_view') DROP VIEW string_defaults_view" + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_string_defaults_view') DROP VIEW sst_string_defaults_view" execute <<-STRINGDEFAULTSVIEW - CREATE VIEW string_defaults_view AS + CREATE VIEW sst_string_defaults_view AS SELECT id, string_with_pretend_null_one as pretend_null - FROM string_defaults + FROM sst_string_defaults STRINGDEFAULTSVIEW + execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_string_defaults_big_view') DROP VIEW sst_string_defaults_big_view" + execute <<-STRINGDEFAULTSBIGVIEW + CREATE VIEW sst_string_defaults_big_view AS + SELECT id, string_with_pretend_null_one as pretend_null + /*#{'x'*4000}}*/ + FROM sst_string_defaults + STRINGDEFAULTSBIGVIEW + + + @@ -49,16 +121,6 @@ t.column :decimal_number, :decimal, precision: 3, scale: 2, default: 2.78 end - create_table :string_defaults, force: true do |t| - t.column :string_with_null_default, :string, default: nil - t.column :string_with_pretend_null_one, :string, default: 'null' - t.column :string_with_pretend_null_two, :string, default: '(null)' - t.column :string_with_pretend_null_three, :string, default: 'NULL' - t.column :string_with_pretend_null_four, :string, default: '(NULL)' - t.column :string_with_pretend_paren_three, :string, default: '(3)' - t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line." - end - create_table :sql_server_chronics, force: true do |t| t.column :date, :date t.column :time, :time @@ -90,25 +152,6 @@ # TODO: Add some different native binary types and test. end - create_table 'my$strange_table', force: true do |t| - t.column :number, :real - end - - create_table :sql_server_edge_schemas, force: true do |t| - t.string :description - t.column :bigint, :bigint - t.column :tinyint, :tinyint - t.column :guid, :uniqueidentifier - t.column 'crazy]]quote', :string - t.column 'with spaces', :string - end - execute %|ALTER TABLE [sql_server_edge_schemas] ADD [guid_newid] uniqueidentifier DEFAULT NEWID();| - execute %|ALTER TABLE [sql_server_edge_schemas] ADD [guid_newseqid] uniqueidentifier DEFAULT NEWSEQUENTIALID();| unless sqlserver_azure? - - create_table :no_pk_data, force: true, id: false do |t| - t.string :name - end - # http://blogs.msdn.com/b/craigfr/archive/2008/03/19/ranking-functions-row-number.aspx execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'order_row_number') DROP TABLE order_row_number" execute <<-ORDERROWNUMBERSQL @@ -126,26 +169,6 @@ INSERT [order_row_number] VALUES (1, 8, 1) ORDERROWNUMBERSQL - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'natural_pk_data') DROP TABLE natural_pk_data" - execute <<-NATURALPKTABLESQL - CREATE TABLE natural_pk_data( - parent_id int, - name nvarchar(255), - description nvarchar(1000), - legacy_id nvarchar(10) NOT NULL PRIMARY KEY - ) - NATURALPKTABLESQL - - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'natural_pk_int_data') DROP TABLE natural_pk_int_data" - execute <<-NATURALPKINTTABLESQL - CREATE TABLE natural_pk_int_data( - legacy_id int NOT NULL PRIMARY KEY, - parent_id int, - name nvarchar(255), - description nvarchar(1000) - ) - NATURALPKINTTABLESQL - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'tinyint_pk_table') DROP TABLE tinyint_pk_table" execute <<-TINYITPKTABLE CREATE TABLE tinyint_pk_table( @@ -154,23 +177,15 @@ ) TINYITPKTABLE - create_table 'quoted-table', force: true do |t| - end - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'quoted-view1') DROP VIEW [quoted-view1]" - execute "CREATE VIEW [quoted-view1] AS SELECT * FROM [quoted-table]" - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'quoted-view2') DROP VIEW [quoted-view2]" - execute "CREATE VIEW [quoted-view2] AS \n /*#{'x'*4000}}*/ \n SELECT * FROM [quoted-table]" - execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'string_defaults_big_view') DROP VIEW string_defaults_big_view" - execute <<-STRINGDEFAULTSBIGVIEW - CREATE VIEW string_defaults_big_view AS - SELECT id, string_with_pretend_null_one as pretend_null - /*#{'x'*4000}}*/ - FROM string_defaults - STRINGDEFAULTSBIGVIEW + + + + + # Another schema. @@ -211,26 +226,4 @@ ) NATURALPKTABLESQLINOTHERSCHEMA - - # Azure needs clustered indexes - if sqlserver_azure? - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_schema_migrations_version') CREATE CLUSTERED INDEX [idx_schema_migrations_version] ON [schema_migrations] ([version])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_countries_ctryid') CREATE CLUSTERED INDEX [idx_countries_ctryid] ON [countries] ([country_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_treaty_id_trtyid') CREATE CLUSTERED INDEX [idx_treaty_id_trtyid] ON [treaties] ([treaty_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_no_pk_data_name') CREATE CLUSTERED INDEX [idx_no_pk_data_name] ON [no_pk_data] ([name])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_developers_projects_did_pid') CREATE CLUSTERED INDEX [idx_developers_projects_did_pid] ON [developers_projects] ([developer_id],[project_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_categories_posts_cid_pid') CREATE CLUSTERED INDEX [idx_categories_posts_cid_pid] ON [categories_posts] ([category_id],[post_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_dashboards_dashboard_id') CREATE CLUSTERED INDEX [idx_dashboards_dashboard_id] ON [dashboards] ([dashboard_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_edges_source_id_sink_id') CREATE CLUSTERED INDEX [idx_edges_source_id_sink_id] ON [edges] ([source_id],[sink_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_goofy_string_id_id') CREATE CLUSTERED INDEX [idx_goofy_string_id_id] ON [goofy_string_id] ([id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_lessons_students_lid_sid') CREATE CLUSTERED INDEX [idx_lessons_students_lid_sid] ON [lessons_students] ([lesson_id],[student_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_mateys_pid_tid') CREATE CLUSTERED INDEX [idx_mateys_pid_tid] ON [mateys] ([pirate_id],[target_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_minivans_minivan_id') CREATE CLUSTERED INDEX [idx_minivans_minivan_id] ON [minivans] ([minivan_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_pirates_paid_pid') CREATE CLUSTERED INDEX [idx_parrots_pirates_paid_pid] ON [parrots_pirates] ([parrot_id],[pirate_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_parrots_treasures_pid_tid') CREATE CLUSTERED INDEX [idx_parrots_treasures_pid_tid] ON [parrots_treasures] ([parrot_id],[treasure_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_speedometers_speedometer_id') CREATE CLUSTERED INDEX [idx_speedometers_speedometer_id] ON [speedometers] ([speedometer_id])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_subscribers_nick') CREATE CLUSTERED INDEX [idx_subscribers_nick] ON [subscribers] ([nick])" - execute "IF NOT EXISTS (SELECT [name] FROM [sys].[indexes] WHERE [name] = N'idx_countries_treaties_cid_tid') CREATE CLUSTERED INDEX [idx_countries_treaties_cid_tid] ON [countries_treaties] ([country_id],[treaty_id])" - end - end From 8243ee5610040f3ac8293fd641dfa5cbfcee492f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 14 Jan 2015 22:32:29 -0500 Subject: [PATCH 0220/1412] Remove #activity_stats, easy to add for another gem or per user if needed. --- .../sqlserver/database_statements.rb | 45 ------------------- test/cases/connection_test_sqlserver.rb | 12 ----- 2 files changed, 57 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fd7ad41fd..e8d1aa398 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -190,51 +190,6 @@ def newsequentialid_function select_value 'SELECT NEWSEQUENTIALID()' end - def activity_stats - select_all %| - SELECT - [session_id] = s.session_id, - [user_process] = CONVERT(CHAR(1), s.is_user_process), - [login] = s.login_name, - [database] = ISNULL(db_name(r.database_id), N''), - [task_state] = ISNULL(t.task_state, N''), - [command] = ISNULL(r.command, N''), - [application] = ISNULL(s.program_name, N''), - [wait_time_ms] = ISNULL(w.wait_duration_ms, 0), - [wait_type] = ISNULL(w.wait_type, N''), - [wait_resource] = ISNULL(w.resource_description, N''), - [blocked_by] = ISNULL(CONVERT (varchar, w.blocking_session_id), ''), - [head_blocker] = - CASE - -- session has an active request, is blocked, but is blocking others - WHEN r2.session_id IS NOT NULL AND r.blocking_session_id = 0 THEN '1' - -- session is idle but has an open tran and is blocking others - WHEN r.session_id IS NULL THEN '1' - ELSE '' - END, - [total_cpu_ms] = s.cpu_time, - [total_physical_io_mb] = (s.reads + s.writes) * 8 / 1024, - [memory_use_kb] = s.memory_usage * 8192 / 1024, - [open_transactions] = ISNULL(r.open_transaction_count,0), - [login_time] = s.login_time, - [last_request_start_time] = s.last_request_start_time, - [host_name] = ISNULL(s.host_name, N''), - [net_address] = ISNULL(c.client_net_address, N''), - [execution_context_id] = ISNULL(t.exec_context_id, 0), - [request_id] = ISNULL(r.request_id, 0), - [workload_group] = N'' - FROM sys.dm_exec_sessions s LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id) - LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id) - LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id AND r.request_id = t.request_id) - LEFT OUTER JOIN - (SELECT *, ROW_NUMBER() OVER (PARTITION BY waiting_task_address ORDER BY wait_duration_ms DESC) AS row_num - FROM sys.dm_os_waiting_tasks - ) w ON (t.task_address = w.waiting_task_address) AND w.row_num = 1 - LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id) - WHERE db_name(r.database_id) = '#{current_database}' - ORDER BY s.session_id| - end - # === SQLServer Specific (Rake/Test Helpers) ==================== # def recreate_database diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 9d17f682e..4a03184d3 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -234,18 +234,6 @@ def execute_with_deadlock_exception(sql, *args) end - context 'Diagnostics' do - - should 'testing #activity_stats' do - stats = @connection.activity_stats - assert !stats.empty? - assert stats.all? { |s| s.has_key?("session_id") } - assert stats.all? { |s| s["database"] == @connection.current_database } - end - - end - - private From 981087b872f093cc149a679fc746aee63e127cba Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 14 Jan 2015 22:33:18 -0500 Subject: [PATCH 0221/1412] Use object identifier more often. --- .../connection_adapters/sqlserver/schema_statements.rb | 7 +++---- test/schema/sqlserver_specific_schema.rb | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 45fc06d81..6259e4d01 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -189,7 +189,6 @@ def initialize_native_database_types def column_definitions(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) database = "#{identifier.database_quoted}." if identifier.database_quoted - table_name = identifier.quoted sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -338,13 +337,13 @@ def view_table_name(table_name) end def view_information(table_name) - table_name = SQLServer::Utils.extract_identifiers(table_name).object - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'", 'SCHEMA' + identifier = SQLServer::Utils.extract_identifiers(table_name) + view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA' if view_info view_info = view_info.with_indifferent_access if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000 view_info[:VIEW_DEFINITION] = begin - select_values("EXEC sp_helptext #{quote_table_name(table_name)}", 'SCHEMA').join + 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 diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 08a324786..66fdcfb5f 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -82,6 +82,7 @@ REFERENCES [sst_has_pks] ([id]) ADDFKSQL + # Views execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_customers_view') DROP VIEW sst_customers_view" From 3fb340c65cdcfd242a23de8cf23d877f7e454c4d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 15 Jan 2015 23:02:32 -0500 Subject: [PATCH 0222/1412] Stronger better default values. Views working too! --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/quoting.rb | 2 +- .../sqlserver/schema_statements.rb | 44 +++++--- .../connection_adapters/sqlserver_adapter.rb | 5 + test/cases/adapter_test_sqlserver.rb | 4 - test/cases/column_test_sqlserver.rb | 104 +++++++++++++----- test/schema/datatypes/2012.sql | 50 ++++----- 7 files changed, 137 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6dbec7239..634e565b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ * product_level * product_version * edition +* Removed tests for old issue #164. Handled by core types now. #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 68d2c0ad4..4d16c7af9 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -19,7 +19,7 @@ def quote_default_value(value, column) if column.type == :uuid && value =~ /\(\)/ value else - quote(value) + quote(value, column) end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6259e4d01..175b74d8f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -241,6 +241,7 @@ def column_definitions(table_name) results = do_exec_query(sql, 'SCHEMA', binds) results.map do |ci| ci = ci.symbolize_keys + ci[:_type] = ci[:type] ci[:type] = case ci[:type] when /^bit|image|text|ntext|datetime$/ ci[:type] @@ -255,22 +256,35 @@ def column_definitions(table_name) else ci[:type] end - if ci[:default_value].nil? && schema_cache.view_exists?(table_name) - real_table_name = table_name_or_views_table_name(table_name) - real_column_name = views_real_column_name(table_name, ci[:name]) - col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{database}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'" - ci[:default_value] = select_value col_default_sql, 'SCHEMA' + ci[:default_value], + ci[:default_function] = begin + default = ci[:default_value] + if default.nil? && schema_cache.view_exists?(table_name) + default = select_value " + SELECT c.COLUMN_DEFAULT + FROM #{database}INFORMATION_SCHEMA.COLUMNS c + WHERE c.TABLE_NAME = '#{table_name_or_views_table_name(table_name)}' + AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA' + end + case default + when nil + [nil, nil] + when /\A\((\w+\(\))\)\Z/ + default_function = Regexp.last_match[1] + [nil, default_function] + when /\A\(N'(.*)'\)\Z/m + string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1]) + [string_literal, nil] + else + type = case ci[:type] + when /smallint|int|bigint/ then ci[:_type] + else ci[:type] + end + value = default.match(/\A\((.*)\)\Z/m)[1] + value = select_value "SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA' + [value, nil] + end end - ci[:default_value] = case ci[:default_value] - when nil, '(null)', '(NULL)' - nil - when /\A\((\w+\(\))\)\Z/ - ci[:default_function] = Regexp.last_match[1] - nil - else - match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m) - match_data ? match_data[1].gsub("''", "'") : nil - end ci[:null] = ci[:is_nullable].to_i == 1 ci.delete(:is_nullable) ci[:is_primary] = ci[:is_primary].to_i == 1 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d3a5d4a14..56737c01a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -192,9 +192,14 @@ def initialize_type_map(m) 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) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 336ffca68..52eb52415 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -204,10 +204,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal '[foo].[bar].[baz]', connection.quote_column_name('foo.bar.baz') end - it "return 0 for empty string" do - assert_equal '0', connection.quote('', Post.columns_hash['id']) - end - it "surround string with national prefix" do assert_equal "N'foo'", connection.quote("foo") end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index d71383352..f4693d5bd 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -32,7 +32,8 @@ def assert_obj_set_and_save(attribute, value) col = column('bigint') col.sql_type.must_equal 'bigint(8)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 42 + obj.bigint.must_equal 42 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::BigInteger @@ -49,7 +50,8 @@ def assert_obj_set_and_save(attribute, value) col = column('int') col.sql_type.must_equal 'int(4)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 42 + obj.int.must_equal 42 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Integer @@ -66,7 +68,8 @@ def assert_obj_set_and_save(attribute, value) col = column('smallint') col.sql_type.must_equal 'smallint(2)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 42 + obj.smallint.must_equal 42 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallInteger @@ -83,7 +86,8 @@ def assert_obj_set_and_save(attribute, value) col = column('tinyint') col.sql_type.must_equal 'tinyint(1)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 42 + obj.tinyint.must_equal 42 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::TinyInteger @@ -100,7 +104,8 @@ def assert_obj_set_and_save(attribute, value) col = column('bit') col.sql_type.must_equal 'bit' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal true + obj.bit.must_equal true col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Boolean @@ -121,7 +126,8 @@ def assert_obj_set_and_save(attribute, value) col = column('decimal_9_2') col.sql_type.must_equal 'decimal(9,2)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal BigDecimal('12345.01') + obj.decimal_9_2.must_equal BigDecimal('12345.01') col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Decimal @@ -131,7 +137,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 9 type.scale.must_equal 2 obj.decimal_9_2 = '1234567.8901' - obj.decimal_9_2.must_equal BigDecimal('1234567.8901') + obj.decimal_9_2.must_equal BigDecimal('1234567.8901') # Cast from user one day. obj.save! obj.reload.decimal_9_2.must_equal BigDecimal('1234567.89') end @@ -139,11 +145,14 @@ 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.default_function.must_equal nil type = col.cast_type type.precision.must_equal 16 type.scale.must_equal 4 obj.decimal_16_4 = '1234567.8901001' - obj.decimal_16_4.must_equal BigDecimal('1234567.8901001') + obj.decimal_16_4.must_equal BigDecimal('1234567.8901001') # Cast from user one day. obj.save! obj.reload.decimal_16_4.must_equal BigDecimal('1234567.8901') end @@ -152,7 +161,8 @@ def assert_obj_set_and_save(attribute, value) col = column('numeric_18_0') col.sql_type.must_equal 'numeric(18,0)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal BigDecimal('191') + obj.numeric_18_0.must_equal BigDecimal('191') col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Decimal @@ -162,16 +172,37 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 18 type.scale.must_equal 0 obj.numeric_18_0 = '192.1' - obj.numeric_18_0.must_equal BigDecimal('192.1') + obj.numeric_18_0.must_equal BigDecimal('192.1') # Cast from user one day. obj.save! 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.null.must_equal true + col.default.must_equal BigDecimal('12345678901234567890.01') + obj.numeric_36_2.must_equal BigDecimal('12345678901234567890.01') + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Decimal + type.type.must_equal :decimal + type.must_be :number? + type.limit.must_equal nil + type.precision.must_equal 36 + type.scale.must_equal 2 + obj.numeric_36_2 = '192.123' + obj.numeric_36_2.must_equal BigDecimal('192.123') # Cast from user one day. + obj.save! + obj.reload.numeric_36_2.must_equal BigDecimal('192.12') + end + it 'money' do col = column('money') col.sql_type.must_equal 'money' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal BigDecimal('4.20') + obj.money.must_equal BigDecimal('4.20') col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Money @@ -190,7 +221,8 @@ def assert_obj_set_and_save(attribute, value) col = column('smallmoney') col.sql_type.must_equal 'smallmoney' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal BigDecimal('4.20') + obj.smallmoney.must_equal BigDecimal('4.20') col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallMoney @@ -213,7 +245,8 @@ def assert_obj_set_and_save(attribute, value) col = column('float') col.sql_type.must_equal 'float(53)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 123.00000001 + obj.float.must_equal 123.00000001 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Float @@ -231,6 +264,9 @@ def assert_obj_set_and_save(attribute, value) it 'float(25)' do col = column('float_25') col.sql_type.must_equal 'float(53)' + col.null.must_equal true + col.default.must_equal 420.11 + col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Float type.type.must_equal :float @@ -241,7 +277,8 @@ def assert_obj_set_and_save(attribute, value) col = column('real') col.sql_type.must_equal 'real(24)' col.null.must_equal true - col.default.must_equal nil + col.default.must_be_close_to 123.45, 0.01 + obj.real.must_be_close_to 123.45, 0.01 col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Float @@ -250,10 +287,10 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_equal 24 type.precision.must_equal nil type.scale.must_equal nil - obj.float = '214748.36461' - obj.float.must_equal 214748.36461 + obj.real = '214748.36461' + obj.real.must_be_close_to 214748.36461, 0.01 obj.save! - obj.reload.float.must_equal 214748.36461 + obj.reload.real.must_be_close_to 214748.36461, 0.01 end # Date and Time @@ -262,7 +299,8 @@ def assert_obj_set_and_save(attribute, value) col = column('date') col.sql_type.must_equal 'date' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal '0001-01-01' # TODO: None type casted default. Really want Date.civil(0001, 1, 1). + obj.date.must_equal Date.civil(0001, 1, 1) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Date @@ -289,7 +327,8 @@ def assert_obj_set_and_save(attribute, value) col = column('datetime') col.sql_type.must_equal 'datetime' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) + obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::DateTime @@ -314,7 +353,8 @@ def assert_obj_set_and_save(attribute, value) col = column('smalldatetime') col.sql_type.must_equal 'smalldatetime' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) + obj.smalldatetime.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallDateTime @@ -401,7 +441,8 @@ def assert_obj_set_and_save(attribute, value) col = column('char_10') col.sql_type.must_equal 'char(10)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal '1234567890' + obj.char_10.must_equal '1234567890' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Char @@ -421,7 +462,8 @@ def assert_obj_set_and_save(attribute, value) col = column('varchar_50') col.sql_type.must_equal 'varchar(50)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test varchar_50' + obj.varchar_50.must_equal 'test varchar_50' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Varchar @@ -438,7 +480,8 @@ def assert_obj_set_and_save(attribute, value) col = column('varchar_max') col.sql_type.must_equal 'varchar(max)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test varchar_max' + obj.varchar_max.must_equal 'test varchar_max' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::VarcharMax @@ -455,7 +498,8 @@ def assert_obj_set_and_save(attribute, value) col = column('text') col.sql_type.must_equal 'text' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test text' + obj.text.must_equal 'test text' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Text @@ -474,7 +518,8 @@ def assert_obj_set_and_save(attribute, value) col = column('nchar_10') col.sql_type.must_equal 'nchar(10)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal '12345678åå' + obj.nchar_10.must_equal '12345678åå' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeChar @@ -494,7 +539,8 @@ def assert_obj_set_and_save(attribute, value) col = column('nvarchar_50') col.sql_type.must_equal 'nvarchar(50)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test nvarchar_50 åå' + obj.nvarchar_50.must_equal 'test nvarchar_50 åå' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeVarchar @@ -511,7 +557,8 @@ def assert_obj_set_and_save(attribute, value) col = column('nvarchar_max') col.sql_type.must_equal 'nvarchar(max)' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test nvarchar_max åå' + obj.nvarchar_max.must_equal 'test nvarchar_max åå' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeVarcharMax @@ -528,7 +575,8 @@ def assert_obj_set_and_save(attribute, value) col = column('ntext') col.sql_type.must_equal 'ntext' col.null.must_equal true - col.default.must_equal nil + col.default.must_equal 'test ntext åå' + obj.ntext.must_equal 'test ntext åå' col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeText diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 5b02d425f..f22d93bea 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -8,37 +8,37 @@ IF EXISTS ( CREATE TABLE [datatypes] ( -- Exact Numerics [id] [int] NOT NULL IDENTITY(1,1) PRIMARY KEY, - [bigint] [bigint] NULL, - [int] [int] NULL, - [smallint] [smallint] NULL, - [tinyint] [tinyint] NULL, - [bit] [bit] NULL, - [decimal_9_2] [decimal](9, 2) NULL, - [decimal_16_4] [decimal](16, 4) NULL, - [numeric_18_0] [numeric](18, 0) NULL, - [numeric_36_2] [numeric](36, 2) NULL, - [money] [money] NULL, - [smallmoney] [smallmoney] NULL, + [bigint] [bigint] NULL DEFAULT 42, + [int] [int] NULL DEFAULT 42, + [smallint] [smallint] NULL DEFAULT 42, + [tinyint] [tinyint] NULL DEFAULT 42, + [bit] [bit] NULL DEFAULT 1, + [decimal_9_2] [decimal](9, 2) NULL DEFAULT 12345.01, + [decimal_16_4] [decimal](16, 4) NULL DEFAULT 1234567.89, + [numeric_18_0] [numeric](18, 0) NULL DEFAULT 191, + [numeric_36_2] [numeric](36, 2) NULL DEFAULT 12345678901234567890.01, + [money] [money] NULL DEFAULT 4.20, + [smallmoney] [smallmoney] NULL DEFAULT 4.20, -- Approximate Numerics - [float] [float] NULL, - [float_25] [float](25) NULL, - [real] [real] NULL, + [float] [float] NULL DEFAULT 123.00000001, + [float_25] [float](25) NULL DEFAULT 420.11, + [real] [real] NULL DEFAULT 123.45, -- Date and Time - [date] [date] NULL, - [datetime] [datetime] NULL, - [smalldatetime] [smalldatetime] NULL, + [date] [date] NULL DEFAULT '0001-01-01', + [datetime] [datetime] NULL DEFAULT '1753-01-01T00:00:00.000', + [smalldatetime] [smalldatetime] NULL DEFAULT '1901-01-01T15:45:00.000Z', [time_2] [time](2) NULL, [time_7] [time](7) NULL, -- Character Strings - [char_10] [char](10) NULL, - [varchar_50] [varchar](50) NULL, - [varchar_max] [varchar](max) NULL, - [text] [text] NULL, + [char_10] [char](10) NULL DEFAULT '1234567890', + [varchar_50] [varchar](50) NULL DEFAULT 'test varchar_50', + [varchar_max] [varchar](max) NULL DEFAULT 'test varchar_max', + [text] [text] NULL DEFAULT 'test text', -- Unicode Character Strings - [nchar_10] [nchar](10) NULL, - [nvarchar_50] [nvarchar](50) NULL, - [nvarchar_max] [nvarchar](max) NULL, - [ntext] [ntext] NULL, + [nchar_10] [nchar](10) NULL DEFAULT N'12345678åå', + [nvarchar_50] [nvarchar](50) NULL DEFAULT N'test nvarchar_50 åå', + [nvarchar_max] [nvarchar](max) NULL DEFAULT N'test nvarchar_max åå', + [ntext] [ntext] NULL DEFAULT N'test ntext åå', -- Binary Strings [binary_49] [binary](49) NULL, [varbinary_49] [varbinary](49) NULL, From 6ca1afad34f76a4868022cbd1e324de9583f3ee4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 15 Jan 2015 23:06:22 -0500 Subject: [PATCH 0223/1412] Remove dead model. --- test/models/numeric_data.rb | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 test/models/numeric_data.rb diff --git a/test/models/numeric_data.rb b/test/models/numeric_data.rb deleted file mode 100644 index 129f265a0..000000000 --- a/test/models/numeric_data.rb +++ /dev/null @@ -1,3 +0,0 @@ -class NumericData < ActiveRecord::Base - self.table_name = 'numeric_data' -end From 5a5dd78d1749b3a5e06d691f740d9ade41707eed Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 15 Jan 2015 23:17:08 -0500 Subject: [PATCH 0224/1412] Move all coerced tests to a new dir. We likely have two many of these and I do not know if anyone when back and checked that we are trumping tests we do not need to trump or are out of the test framework now. --- test/cases/{ => coerced}/associations_test_sqlserver.rb | 0 test/cases/{ => coerced}/attribute_methods_test_sqlserver.rb | 0 test/cases/{ => coerced}/base_test_sqlserver.rb | 0 test/cases/{ => coerced}/batches_test_sqlserver.rb | 0 .../cases/{ => coerced}/belongs_to_associations_test_sqlserver.rb | 0 test/cases/{ => coerced}/bind_parameter_test_sqlserver.rb | 0 test/cases/{ => coerced}/calculations_test_sqlserver.rb | 0 test/cases/{ => coerced}/connection_management_test_sqlserver.rb | 0 test/cases/{ => coerced}/eager_test_sqlserver.rb | 0 test/cases/{ => coerced}/finder_test_sqlserver.rb | 0 .../has_and_belongs_to_many_associations_test_sqlserver.rb | 0 test/cases/{ => coerced}/inheritance_test_sqlserver.rb | 0 test/cases/{ => coerced}/persistence_test_sqlserver.rb | 0 test/cases/{ => coerced}/predicate_builder_test_sqlserver.rb | 0 test/cases/{ => coerced}/query_cache_test_sqlserver.rb | 0 test/cases/{ => coerced}/relations_test_sqlserver.rb | 0 test/cases/{ => coerced}/uniqueness_validation_test_sqlserver.rb | 0 test/cases/{ => coerced}/where_chain_test_sqlserver.rb | 0 18 files changed, 0 insertions(+), 0 deletions(-) rename test/cases/{ => coerced}/associations_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/attribute_methods_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/base_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/batches_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/belongs_to_associations_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/bind_parameter_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/calculations_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/connection_management_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/eager_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/finder_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/has_and_belongs_to_many_associations_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/inheritance_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/persistence_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/predicate_builder_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/query_cache_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/relations_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/uniqueness_validation_test_sqlserver.rb (100%) rename test/cases/{ => coerced}/where_chain_test_sqlserver.rb (100%) diff --git a/test/cases/associations_test_sqlserver.rb b/test/cases/coerced/associations_test_sqlserver.rb similarity index 100% rename from test/cases/associations_test_sqlserver.rb rename to test/cases/coerced/associations_test_sqlserver.rb diff --git a/test/cases/attribute_methods_test_sqlserver.rb b/test/cases/coerced/attribute_methods_test_sqlserver.rb similarity index 100% rename from test/cases/attribute_methods_test_sqlserver.rb rename to test/cases/coerced/attribute_methods_test_sqlserver.rb diff --git a/test/cases/base_test_sqlserver.rb b/test/cases/coerced/base_test_sqlserver.rb similarity index 100% rename from test/cases/base_test_sqlserver.rb rename to test/cases/coerced/base_test_sqlserver.rb diff --git a/test/cases/batches_test_sqlserver.rb b/test/cases/coerced/batches_test_sqlserver.rb similarity index 100% rename from test/cases/batches_test_sqlserver.rb rename to test/cases/coerced/batches_test_sqlserver.rb diff --git a/test/cases/belongs_to_associations_test_sqlserver.rb b/test/cases/coerced/belongs_to_associations_test_sqlserver.rb similarity index 100% rename from test/cases/belongs_to_associations_test_sqlserver.rb rename to test/cases/coerced/belongs_to_associations_test_sqlserver.rb diff --git a/test/cases/bind_parameter_test_sqlserver.rb b/test/cases/coerced/bind_parameter_test_sqlserver.rb similarity index 100% rename from test/cases/bind_parameter_test_sqlserver.rb rename to test/cases/coerced/bind_parameter_test_sqlserver.rb diff --git a/test/cases/calculations_test_sqlserver.rb b/test/cases/coerced/calculations_test_sqlserver.rb similarity index 100% rename from test/cases/calculations_test_sqlserver.rb rename to test/cases/coerced/calculations_test_sqlserver.rb diff --git a/test/cases/connection_management_test_sqlserver.rb b/test/cases/coerced/connection_management_test_sqlserver.rb similarity index 100% rename from test/cases/connection_management_test_sqlserver.rb rename to test/cases/coerced/connection_management_test_sqlserver.rb diff --git a/test/cases/eager_test_sqlserver.rb b/test/cases/coerced/eager_test_sqlserver.rb similarity index 100% rename from test/cases/eager_test_sqlserver.rb rename to test/cases/coerced/eager_test_sqlserver.rb diff --git a/test/cases/finder_test_sqlserver.rb b/test/cases/coerced/finder_test_sqlserver.rb similarity index 100% rename from test/cases/finder_test_sqlserver.rb rename to test/cases/coerced/finder_test_sqlserver.rb diff --git a/test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb similarity index 100% rename from test/cases/has_and_belongs_to_many_associations_test_sqlserver.rb rename to test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb diff --git a/test/cases/inheritance_test_sqlserver.rb b/test/cases/coerced/inheritance_test_sqlserver.rb similarity index 100% rename from test/cases/inheritance_test_sqlserver.rb rename to test/cases/coerced/inheritance_test_sqlserver.rb diff --git a/test/cases/persistence_test_sqlserver.rb b/test/cases/coerced/persistence_test_sqlserver.rb similarity index 100% rename from test/cases/persistence_test_sqlserver.rb rename to test/cases/coerced/persistence_test_sqlserver.rb diff --git a/test/cases/predicate_builder_test_sqlserver.rb b/test/cases/coerced/predicate_builder_test_sqlserver.rb similarity index 100% rename from test/cases/predicate_builder_test_sqlserver.rb rename to test/cases/coerced/predicate_builder_test_sqlserver.rb diff --git a/test/cases/query_cache_test_sqlserver.rb b/test/cases/coerced/query_cache_test_sqlserver.rb similarity index 100% rename from test/cases/query_cache_test_sqlserver.rb rename to test/cases/coerced/query_cache_test_sqlserver.rb diff --git a/test/cases/relations_test_sqlserver.rb b/test/cases/coerced/relations_test_sqlserver.rb similarity index 100% rename from test/cases/relations_test_sqlserver.rb rename to test/cases/coerced/relations_test_sqlserver.rb diff --git a/test/cases/uniqueness_validation_test_sqlserver.rb b/test/cases/coerced/uniqueness_validation_test_sqlserver.rb similarity index 100% rename from test/cases/uniqueness_validation_test_sqlserver.rb rename to test/cases/coerced/uniqueness_validation_test_sqlserver.rb diff --git a/test/cases/where_chain_test_sqlserver.rb b/test/cases/coerced/where_chain_test_sqlserver.rb similarity index 100% rename from test/cases/where_chain_test_sqlserver.rb rename to test/cases/coerced/where_chain_test_sqlserver.rb From 0a71327a14ffda26239e659826914b2e35117c35 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 16 Jan 2015 20:08:30 -0500 Subject: [PATCH 0225/1412] Connection tests! --- .../sqlserver/database_statements.rb | 17 +- .../connection_adapters/sqlserver_adapter.rb | 24 ++- lib/active_record/sqlserver_base.rb | 2 + test/cases/adapter_test_sqlserver.rb | 3 - test/cases/connection_test_sqlserver.rb | 177 +++++++++--------- test/cases/helper_sqlserver.rb | 12 +- 6 files changed, 120 insertions(+), 115 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e8d1aa398..f46492882 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseStatements + def select_rows(sql, name = nil, binds = []) do_exec_query sql, name, binds, fetch: :rows end @@ -56,17 +57,19 @@ def rollback_db_transaction do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end + include Savepoints + def create_savepoint(name = current_savepoint_name) disable_auto_reconnect { do_execute "SAVE TRANSACTION #{name}" } end - def release_savepoint(name = current_savepoint_name) - end - def rollback_to_savepoint(name = current_savepoint_name) disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{name}" } end + def release_savepoint(name = current_savepoint_name) + end + def add_limit_offset!(_sql, _options) raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.' end @@ -116,8 +119,8 @@ def execute_procedure(proc_name, *variables) def use_database(database = nil) return if sqlserver_azure? - name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]) - do_execute "USE #{name}" unless database.blank? + name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted + do_execute "USE #{name}" unless name.blank? end def user_options @@ -135,7 +138,6 @@ def user_options end end - # TODO: Rails 4 now supports isolation levels def user_options_dateformat if sqlserver_azure? select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA' @@ -245,6 +247,7 @@ def charset select_value "SELECT SERVERPROPERTY('SqlCharSetName')" end + protected def select(sql, name = nil, binds = []) @@ -281,7 +284,6 @@ def do_exec_query(sql, name, binds, options = {}) if options[:fetch] != :rows options[:ar_result] = true end - explaining = name == 'EXPLAIN' names_and_types = [] params = [] @@ -406,6 +408,7 @@ def finish_statement_handle(handle) end handle end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 56737c01a..ac439a88f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -37,8 +37,8 @@ class SQLServerAdapter < AbstractAdapter attr_reader :spid - cattr_accessor :auto_connect, :cs_equality_operator, - :lowercase_schema_reflection, :auto_connect_duration, :showplan_option + cattr_accessor :auto_connect, :auto_connect_duration, instance_accessor: false + cattr_accessor :cs_equality_operator, :lowercase_schema_reflection, :showplan_option def initialize(connection, logger, pool, config) super(connection, logger, pool) @@ -81,10 +81,6 @@ def supports_bulk_alter? false end - def supports_savepoints? - true - end - def supports_index_sort_order? true end @@ -107,6 +103,7 @@ def disable_referential_integrity # === Abstract Adapter (Connection Management) ================== # def active? + return false unless @connection case @connection_options[:mode] when :dblib return @connection.active? @@ -118,14 +115,13 @@ def active? end def reconnect! - reset_transaction + super disconnect! connect - active? end def disconnect! - reset_transaction + super @spid = nil case @connection_options[:mode] when :dblib @@ -133,6 +129,7 @@ def disconnect! when :odbc @connection.disconnect rescue nil end + @connection = nil end def reset! @@ -173,11 +170,11 @@ def inspect end def auto_connect - @@auto_connect.is_a?(FalseClass) ? false : true + self.class.auto_connect.is_a?(FalseClass) ? false : true end def auto_connect_duration - @@auto_connect_duration ||= 10 + self.class.auto_connect_duration ||= 10 end def cs_equality_operator @@ -380,9 +377,10 @@ def auto_reconnected? @auto_connecting = true count = 0 while count <= (auto_connect_duration / 2) - result = reconnect! + disconnect! + reconnect! ActiveRecord::Base.did_retry_sqlserver_connection(self, count) - return true if result + return true if active? sleep 2**count count += 1 end diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index e1b8f673f..4b1b4921a 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -1,5 +1,6 @@ module ActiveRecord class Base + def self.sqlserver_connection(config) #:nodoc: config = config.symbolize_keys config.reverse_merge! mode: :dblib @@ -24,5 +25,6 @@ def self.did_retry_sqlserver_connection(connection, count) def self.did_lose_sqlserver_connection(connection) logger.info "CONNECTION LOST: #{connection.class.name}" end + end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 52eb52415..be87e5752 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -9,13 +9,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks - let(:connection) { ActiveRecord::Base.connection } - let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } - it 'has basic and non-senstive information in the adpaters inspect method' do string = connection.inspect string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 4a03184d3..5b99b101c 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,6 +1,6 @@ require 'cases/helper_sqlserver' require 'models/reply' -require 'models/sqlserver/topic' +require 'models/topic' class ConnectionTestSQLServer < ActiveRecord::TestCase @@ -8,11 +8,10 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase fixtures :topics, :accounts - setup do - @connection = ActiveRecord::Base.connection - end + before { assert connection.active? } + after { connection.reconnect! } - should 'affect rows' do + it 'affect rows' do topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } updated = Topic.update(topic_data.keys, topic_data.values) assert_equal 2, updated.size @@ -21,181 +20,172 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert_equal 2, Topic.delete([1, 2]) end - should 'allow usage of :database connection option to remove setting from dsn' do - assert_equal 'activerecord_unittest', @connection.current_database + 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 + 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' + connection.use_database + assert_equal 'activerecord_unittest', connection.current_database, 'Would default back to connection options' end end unless sqlserver_azure? - context 'ODBC connection management' do + describe 'ODBC connection management' do - should 'return finished ODBC statement handle from #execute without block' do + it 'return finished ODBC statement handle from #execute without block' do assert_all_odbc_statements_used_are_closed do - @connection.execute('SELECT * FROM [topics]') + connection.execute('SELECT * FROM [topics]') end end - should 'finish ODBC statement handle from #execute with block' do + it 'finish ODBC statement handle from #execute with block' do assert_all_odbc_statements_used_are_closed do - @connection.execute('SELECT * FROM [topics]') { } + connection.execute('SELECT * FROM [topics]') { } end end - should 'finish connection from #raw_select' do + it 'finish connection from #raw_select' do assert_all_odbc_statements_used_are_closed do - @connection.send(:raw_select,'SELECT * FROM [topics]') + connection.send(:raw_select,'SELECT * FROM [topics]') end end - should 'execute without block closes statement' do + it 'execute without block closes statement' do assert_all_odbc_statements_used_are_closed do - @connection.execute("SELECT 1") + connection.execute("SELECT 1") end end - should 'execute with block closes statement' do + it 'execute with block closes statement' do assert_all_odbc_statements_used_are_closed do - @connection.execute("SELECT 1") do |sth| + connection.execute("SELECT 1") do |sth| assert !sth.finished?, "Statement should still be alive within block" end end end - should 'insert with identity closes statement' do + it 'insert with identity closes statement' do assert_all_odbc_statements_used_are_closed do - @connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", [] + connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", [] end end - should 'insert without identity closes statement' do + it 'insert without identity closes statement' do assert_all_odbc_statements_used_are_closed do - @connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", [] + connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", [] end end - should 'active closes statement' do + it 'active closes statement' do assert_all_odbc_statements_used_are_closed do - @connection.active? + connection.active? end end end if connection_mode_odbc? - context 'Connection management' do + describe 'Connection management' do - setup do - assert @connection.active? + it 'set spid on connect' do + assert_instance_of Fixnum, connection.spid end - should 'set spid on connect' do - assert_instance_of Fixnum, @connection.spid + it 'reset spid on disconnect!' do + connection.disconnect! + assert connection.spid.nil? end - should 'reset spid on disconnect!' do - @connection.disconnect! - assert @connection.spid.nil? + it 'reset the connection' do + connection.disconnect! + connection.raw_connection.must_be_nil end - should 'be able to disconnect and reconnect at will' do - @connection.disconnect! - assert !@connection.active? - @connection.reconnect! - assert @connection.active? + it 'be able to disconnect and reconnect at will' do + disconnect_raw_connection! + assert !connection.active? + connection.reconnect! + assert connection.active? end - should 'auto reconnect when setting is on' do + it 'auto reconnect when setting is on' do with_auto_connect(true) do - @connection.disconnect! + disconnect_raw_connection! assert_nothing_raised() { Topic.count } - assert @connection.active? + assert connection.active? end end - should 'not auto reconnect when setting is off' do + it 'not auto reconnect when setting is off' do with_auto_connect(false) do - @connection.disconnect! + disconnect_raw_connection! assert_raise(ActiveRecord::LostConnection) { Topic.count } end end - should 'not auto reconnect on commit transaction' do - @connection.disconnect! - assert_raise(ActiveRecord::LostConnection) { @connection.commit_db_transaction } - end - - should 'gracefully ignore lost connections on rollback transaction' do - @connection.disconnect! - assert_nothing_raised { @connection.rollback_db_transaction } - end - - should 'not auto reconnect on create savepoint' do - @connection.disconnect! - assert_raise(ActiveRecord::LostConnection) { @connection.create_savepoint } + it 'not auto reconnect on commit transaction' do + disconnect_raw_connection! + assert_raise(ActiveRecord::LostConnection) { connection.commit_db_transaction } end - should 'not auto reconnect on rollback to savepoint ' do - @connection.disconnect! - assert_raise(ActiveRecord::LostConnection) { @connection.rollback_to_savepoint } + it 'gracefully ignore lost connections on rollback transaction' do + disconnect_raw_connection! + assert_nothing_raised { connection.rollback_db_transaction } end - context 'testing #disable_auto_reconnect' do + describe 'testing #disable_auto_reconnect' do - should 'when auto reconnect setting is on' do + it 'when auto reconnect setting is on' do with_auto_connect(true) do - @connection.send(:disable_auto_reconnect) do - assert !@connection.class.auto_connect + connection.send(:disable_auto_reconnect) do + assert !connection.class.auto_connect end - assert @connection.class.auto_connect + assert connection.class.auto_connect end end - should 'when auto reconnect setting is off' do + it 'when auto reconnect setting is off' do with_auto_connect(false) do - @connection.send(:disable_auto_reconnect) do - assert !@connection.class.auto_connect + connection.send(:disable_auto_reconnect) do + assert !connection.class.auto_connect end - assert !@connection.class.auto_connect + assert !connection.class.auto_connect end end end - context 'with a deadlock victim exception (1205)' do + describe 'with a deadlock victim exception 1205' do - context 'outside a transaction' do + describe 'outside a transaction' do - setup do + before do @query = "SELECT 1 as [one]" - @expected = @connection.execute(@query) + @expected = connection.execute(@query) # Execute the query to get a handle of the expected result, which # will be returned after a simulated deadlock victim (1205). - raw_conn = @connection.instance_variable_get(:@connection) + raw_conn = connection.raw_connection stubbed_handle = raw_conn.execute(@query) - @connection.send(:finish_statement_handle, stubbed_handle) + connection.send(:finish_statement_handle, stubbed_handle) raw_conn.stubs(:execute).raises(deadlock_victim_exception(@query)).then.returns(stubbed_handle) end - should 'raise ActiveRecord::DeadlockVictim' do + it 'raise ActiveRecord::DeadlockVictim' do assert_raise(ActiveRecord::DeadlockVictim) do - assert_equal @expected, @connection.execute(@query) + assert_equal @expected, connection.execute(@query) end end end - context 'within a transaction' do + describe 'within a transaction' do - setup do + before do @query = "SELECT 1 as [one]" - @expected = @connection.execute(@query) + @expected = connection.execute(@query) # We "stub" the execute method to simulate raising a deadlock victim exception once. - @connection.class.class_eval do + connection.class.class_eval do def execute_with_deadlock_exception(sql, *args) if !@raised_deadlock_exception && sql == "SELECT 1 as [one]" sql = "RAISERROR('Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}', 13, 1)" @@ -210,20 +200,20 @@ def execute_with_deadlock_exception(sql, *args) end end - teardown do + after do # Cleanup the "stubbed" execute method. - @connection.class.class_eval do + connection.class.class_eval do alias :execute :execute_without_deadlock_exception remove_method :execute_with_deadlock_exception remove_method :execute_without_deadlock_exception end - @connection.send(:remove_instance_variable, :@raised_deadlock_exception) + connection.send(:remove_instance_variable, :@raised_deadlock_exception) end - should 'raise ActiveRecord::DeadlockVictim if retry disabled' do + it 'raise ActiveRecord::DeadlockVictim if retry disabled' do assert_raise(ActiveRecord::DeadlockVictim) do ActiveRecord::Base.transaction do - assert_equal @expected, @connection.execute(@query) + assert_equal @expected, connection.execute(@query) end end end @@ -237,8 +227,17 @@ def execute_with_deadlock_exception(sql, *args) private + def disconnect_raw_connection! + case connection.instance_variable_get(:@connection_options)[:mode] + when :dblib + connection.raw_connection.close rescue nil + when :odbc + connection.raw_connection.disconnect rescue nil + end + end + def assert_all_odbc_statements_used_are_closed(&block) - odbc = @connection.raw_connection.class.parent + odbc = connection.raw_connection.class.parent existing_handles = [] ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h } existing_handle_ids = existing_handles.map(&:object_id) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 93027fb74..73a84c222 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -10,22 +10,28 @@ module ActiveRecord class TestCase < ActiveSupport::TestCase + let(:logger) { ActiveRecord::Base.logger } + let(:connection) { ActiveRecord::Base.connection } + class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end end + + private + def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end def with_auto_connect(boolean) - existing = ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect - ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = boolean + existing = connection.auto_connect + connection.class.auto_connect = boolean yield ensure - ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = existing + connection.class.auto_connect = existing end end From 9a40ffb8bd9139dddcc9299dc8bf1ab40099fed4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 10:46:35 -0500 Subject: [PATCH 0226/1412] Finish database statement tests. --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 36 +++++++----- .../connection_adapters/sqlserver/utils.rb | 8 ++- .../database_statements_test_sqlserver.rb | 58 +++++++++---------- test/cases/utils_test_sqlserver.rb | 5 ++ 5 files changed, 61 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 634e565b0..0da5123c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ #### Changed +* The `create_database` now takes an options hash. Only key/value now is `collation`. Unknown keys just use raw values for SQL. * Complete rewrite of our Arel visitor. Focuing on 2012 and upward so we can make FETCH happen. * Testing enhancements. * Guard support, check our Guardfile. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f46492882..ebacdcf39 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -70,10 +70,6 @@ def rollback_to_savepoint(name = current_savepoint_name) def release_savepoint(name = current_savepoint_name) end - def add_limit_offset!(_sql, _options) - raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.' - end - def empty_insert_statement_value 'DEFAULT VALUES' end @@ -201,21 +197,25 @@ def recreate_database end def recreate_database!(database = nil) - current_db = current_database - database ||= current_db - this_db = database.to_s == current_db - do_execute 'USE master' if this_db + database ||= current_database drop_database(database) create_database(database) ensure - use_database(current_db) if this_db + use_database(database) end def drop_database(database) retry_count = 0 max_retries = 1 + name = SQLServer::Utils.extract_identifiers(database) begin - do_execute "DROP DATABASE #{SQLServer::Utils.extract_identifiers(database)}" + do_execute " + USE master + IF EXISTS ( + SELECT * FROM [sys].[databases] + WHERE name = #{quote(name.object)} + ) DROP DATABASE #{name} + ".squish rescue ActiveRecord::StatementInvalid => err if err.message =~ /because it is currently in use/i raise if retry_count >= max_retries @@ -230,13 +230,19 @@ def drop_database(database) end end - def create_database(database, collation = @connection_options[:collation]) + def create_database(database, options = {}) name = SQLServer::Utils.extract_identifiers(database) - if collation - do_execute "CREATE DATABASE #{name} COLLATE #{collation}" - else - do_execute "CREATE DATABASE #{name}" + options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys) + options = options.select { |_, v| v.present? } + option_string = options.inject("") do |memo, (key, value)| + memo += case key + when :collation + " COLLATE #{value}" + else + "" + end end + do_execute "CREATE DATABASE #{name}#{option_string}" end def current_database diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index d79b5c035..06f8dfe54 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -11,7 +11,9 @@ module Utils class Name SEPARATOR = "." - SCANNER = /\]?\./ + UNQUOTED_SCANNER = /\]?\./ + QUOTED_SCANNER = /\A\[.*?\]\./ + QUOTED_CHECKER = /\A\[/ attr_reader :server, :database, :schema, :object attr_reader :raw_name @@ -60,11 +62,11 @@ def parse_raw_name @parts = [] return if raw_name.blank? scanner = StringScanner.new(raw_name) - matched = scanner.scan_until(SCANNER) + matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER) while matched part = matched[0..-2] @parts << (part.blank? ? nil : unquote(part)) - matched = scanner.scan_until(SCANNER) + matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER) end case @parts.length when 3 diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index 40996dcf1..eba8892f3 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -4,54 +4,54 @@ class DatabaseStatementsTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false - setup do - @connection = ActiveRecord::Base.connection - end + after { connection.use_database } - should 'create database' do - @connection.create_database 'activerecord_unittest3' #, 'SQL_Latin1_General_CP1_CI_AS' - database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" + it 'create database' do + connection.create_database 'activerecord_unittest3' + database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" assert_equal 'activerecord_unittest3', database_name end - should 'drop database' do - @connection.drop_database 'activerecord_unittest3' - database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" + it 'drop database' do + connection.drop_database 'activerecord_unittest3' + database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" assert_equal nil, database_name end - should 'create/use/drop database with name with dots' do - @connection.create_database 'activerecord.unittest' - database_name = @connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'" + it 'create/use/drop database with name with dots' do + connection.drop_database '[activerecord.unittest]' rescue nil + connection.create_database '[activerecord.unittest]' + database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'" assert_equal 'activerecord.unittest', database_name - @connection.use_database 'activerecord.unittest' - @connection.use_database - @connection.drop_database 'activerecord.unittest' + connection.use_database '[activerecord.unittest]' + connection.use_database + connection.drop_database '[activerecord.unittest]' end context 'with collation' do - teardown do - @connection.drop_database 'activerecord_unittest3' - end - should 'create database with default collation for the server' do - @connection.create_database 'activerecord_unittest3' - default_collation = @connection.select_value "SELECT SERVERPROPERTY('Collation')" - database_collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + after { connection.drop_database 'activerecord_unittest3' } + + it 'create database with default collation for the server' do + connection.create_database 'activerecord_unittest3' + default_collation = connection.select_value "SELECT SERVERPROPERTY('Collation')" + database_collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal default_collation, database_collation end - should 'create database with collation set by the method' do - @connection.create_database 'activerecord_unittest3', 'SQL_Latin1_General_CP1_CI_AS' - collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + it 'create database with collation set by the method' do + connection.create_database 'activerecord_unittest3', collation: 'SQL_Latin1_General_CP1_CI_AS' + collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation end - should 'create database with collation set by the config' do - @connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS' - @connection.create_database 'activerecord_unittest3' - collation = @connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" + it 'create database with collation set by the config' do + connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS' + connection.create_database 'activerecord_unittest3' + collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation end + end + end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 2fd60d8cf..3652a2247 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -83,6 +83,11 @@ class UtilsTestSQLServer < ActiveRecord::TestCase Utils.extract_identifiers(:object).object.must_equal 'object' end + it 'allows identifiers with periods to work' do + Utils.extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' + Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' + end + end end From 29564fed2f6be01a8b8c8bd0952937f18be8c3c1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 11:11:34 -0500 Subject: [PATCH 0227/1412] Allow rake to run ActiveRecord test case(s). --- Rakefile | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Rakefile b/Rakefile index b9217b325..7aab7d59a 100644 --- a/Rakefile +++ b/Rakefile @@ -2,7 +2,15 @@ require 'rake/testtask' require_relative 'test/support/paths_sqlserver' def test_files - return ENV['TEST_FILES'].split(',') if ENV['TEST_FILES'] + if files = ENV['AR_TEST_FILES'] + files = files.split(',').map do |file| + File.join ARTest::SQLServer.root_activerecord, file.strip + end + return files.unshift 'test/cases/helper_sqlserver.rb' + end + if files = ENV['TEST_FILES'] + return files.split(',').map(&:strip) + end sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') ar_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") if ENV['SQLSERVER_ONLY'] From 82244bad8c197a0d80e56c0bb1b3086ced1c4ea8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 11:15:43 -0500 Subject: [PATCH 0228/1412] This test passes anyway. $ bundle exec rake AR_TEST_FILES="test/cases/disconnected_test.rb" --- test/cases/disconnected_test_sqlserver.rb | 34 ----------------------- 1 file changed, 34 deletions(-) delete mode 100644 test/cases/disconnected_test_sqlserver.rb diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb deleted file mode 100644 index f3da74056..000000000 --- a/test/cases/disconnected_test_sqlserver.rb +++ /dev/null @@ -1,34 +0,0 @@ -require 'cases/helper_sqlserver' -require 'cases/disconnected_test' - -class TestDisconnectedAdapter < ActiveRecord::TestCase - def setup - skip "TestDisconnectedAdapterSQLServer instead " - end -end - -class TestDisconnectedAdapterSQLServer < ActiveRecord::TestCase - self.use_transactional_fixtures = false - - def setup - skip "in-memory database mustn't disconnect" if in_memory_db? - @connection = ActiveRecord::Base.connection - end - - def teardown - return if in_memory_db? - spec = ActiveRecord::Base.connection_config - ActiveRecord::Base.establish_connection(spec) - end - - test "can't execute statements while disconnected" do - with_auto_connect(false) do - @connection.execute "SELECT count(*) from products" - @connection.disconnect! - assert_raises(ActiveRecord::LostConnection) do - @connection.execute "SELECT count(*) from products" - end - end - end - -end From aabba93fa6dbb5cede518ee5b087975c590689fd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 11:41:19 -0500 Subject: [PATCH 0229/1412] Exec proc test. --- .../cases/execute_procedure_test_sqlserver.rb | 21 +++++++------------ test/schema/sqlserver_specific_schema.rb | 9 -------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index ea28efa92..78afb2e53 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -2,26 +2,22 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase - setup do - @klass = ActiveRecord::Base - end - - should 'execute a simple procedure' do - tables = @klass.execute_procedure :sp_tables + it 'execute a simple procedure' do + tables = ActiveRecord::Base.execute_procedure :sp_tables assert_instance_of Array, tables assert tables.first.respond_to?(:keys) end - should 'take parameter arguments' do - tables = @klass.execute_procedure :sp_tables, 'sql_server_chronics' + it 'take parameter arguments' do + tables = ActiveRecord::Base.execute_procedure :sp_tables, 'datatypes' table_info = tables.first assert_equal 1, tables.size assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" end - should 'allow multiple result sets to be returned' do - results1, results2 = @klass.execute_procedure('sp_helpconstraint','accounts') + it 'allow multiple result sets to be returned' do + results1, results2 = ActiveRecord::Base.execute_procedure('sp_helpconstraint','accounts') assert_instance_of Array, results1 assert results1.first.respond_to?(:keys) assert results1.first['Object Name'] @@ -31,13 +27,12 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase assert results2.first['constraint_type'] end - should 'take named parameter arguments' do - tables = @klass.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys' + it 'take named parameter arguments' do + tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys' table_info = tables.first assert_equal 1, tables.size assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" end - end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 66fdcfb5f..47845409b 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -122,15 +122,6 @@ t.column :decimal_number, :decimal, precision: 3, scale: 2, default: 2.78 end - create_table :sql_server_chronics, force: true do |t| - t.column :date, :date - t.column :time, :time - t.column :datetime, :datetime - t.column :timestamp, :timestamp - t.column :ss_timestamp, :ss_timestamp unless sqlserver_azure? - t.column :smalldatetime, :smalldatetime - end - create_table :sql_server_unicodes, force: true do |t| t.column :nchar, :nchar t.column :nvarchar, :nvarchar From aaea46c3204985429b75b0250e9537ecaf35a384 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 11:59:52 -0500 Subject: [PATCH 0230/1412] Better tables for us. Always use `sst_` prefix. --- .../cases/execute_procedure_test_sqlserver.rb | 2 +- test/models/sqlserver/datatype.rb | 2 +- test/schema/datatypes/2012.sql | 24 +++++++++---------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 78afb2e53..574fd5e43 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -9,7 +9,7 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase end it 'take parameter arguments' do - tables = ActiveRecord::Base.execute_procedure :sp_tables, 'datatypes' + tables = ActiveRecord::Base.execute_procedure :sp_tables, 'sst_datatypes' table_info = tables.first assert_equal 1, tables.size assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" diff --git a/test/models/sqlserver/datatype.rb b/test/models/sqlserver/datatype.rb index 5df536c52..d9b24d830 100644 --- a/test/models/sqlserver/datatype.rb +++ b/test/models/sqlserver/datatype.rb @@ -1,3 +1,3 @@ class SSTestDatatype < ActiveRecord::Base - self.table_name = :datatypes + self.table_name = :sst_datatypes end diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index f22d93bea..9dfd69312 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -2,10 +2,10 @@ IF EXISTS ( SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_NAME = N'datatypes' -) DROP TABLE [datatypes] + WHERE TABLE_NAME = N'sst_datatypes' +) DROP TABLE [sst_datatypes] -CREATE TABLE [datatypes] ( +CREATE TABLE [sst_datatypes] ( -- Exact Numerics [id] [int] NOT NULL IDENTITY(1,1) PRIMARY KEY, [bigint] [bigint] NULL DEFAULT 42, @@ -52,12 +52,12 @@ CREATE TABLE [datatypes] ( -- [datetimeoffset_2] [datetimeoffset](2) NULL, -- [datetimeoffset_7] [datetimeoffset](7) NULL, -- --- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 71, '0001-01-01T00:00:00.0000000Z' ) --- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 72, '1984-01-24T04:20:00.0000000-08:00' ) --- INSERT INTO [datatypes] ([id], [datetime2_7]) VALUES ( 73, '9999-12-31T23:59:59.9999999Z' ) --- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 81, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.00 -08:00 --- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 82, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.00 +00:00 --- INSERT INTO [datatypes] ([id], [datetimeoffset_2]) VALUES ( 83, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.99 +00:00 --- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 84, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.0000000 -08:00 --- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 85, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.0000000 +00:00 --- INSERT INTO [datatypes] ([id], [datetimeoffset_7]) VALUES ( 86, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.9999999 +00:00 +-- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 71, '0001-01-01T00:00:00.0000000Z' ) +-- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 72, '1984-01-24T04:20:00.0000000-08:00' ) +-- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 73, '9999-12-31T23:59:59.9999999Z' ) +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 81, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.00 -08:00 +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 82, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.00 +00:00 +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 83, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.99 +00:00 +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 84, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.0000000 -08:00 +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 85, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.0000000 +00:00 +-- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 86, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.9999999 +00:00 From 10c1a1eb8ea8eb7ed2e4da39eb669b674129acd6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 12:12:53 -0500 Subject: [PATCH 0231/1412] Migrations test. --- .../invalid_connection_test_sqlserver.rb | 0 .../cases/coerced/migration_test_sqlserver.rb | 13 ++++++ test/cases/migration_test_sqlserver.rb | 46 +++++++------------ 3 files changed, 30 insertions(+), 29 deletions(-) rename test/cases/{ => coerced}/invalid_connection_test_sqlserver.rb (100%) create mode 100644 test/cases/coerced/migration_test_sqlserver.rb diff --git a/test/cases/invalid_connection_test_sqlserver.rb b/test/cases/coerced/invalid_connection_test_sqlserver.rb similarity index 100% rename from test/cases/invalid_connection_test_sqlserver.rb rename to test/cases/coerced/invalid_connection_test_sqlserver.rb diff --git a/test/cases/coerced/migration_test_sqlserver.rb b/test/cases/coerced/migration_test_sqlserver.rb new file mode 100644 index 000000000..da1987f22 --- /dev/null +++ b/test/cases/coerced/migration_test_sqlserver.rb @@ -0,0 +1,13 @@ +require 'cases/helper_sqlserver' +require 'models/person' + +class MigrationTest < ActiveRecord::TestCase + COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] + include ARTest::SQLServer::CoercedTest + + # TODO: put a real test here + def test_coerced_test_migrator_db_has_no_schema_migrations_table + assert true + end + +end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index a9275ebf1..f0679793e 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -3,58 +3,54 @@ class MigrationTestSQLServer < ActiveRecord::TestCase - setup do - @connection = ActiveRecord::Base.connection - end - - context 'For transactions' do + describe 'For transactions' do - setup do + before do @trans_test_table1 = 'sqlserver_trans_table1' @trans_test_table2 = 'sqlserver_trans_table2' @trans_tables = [@trans_test_table1,@trans_test_table2] end - teardown do + after do @trans_tables.each do |table_name| - ActiveRecord::Migration.drop_table(table_name) if @connection.tables.include?(table_name) + ActiveRecord::Migration.drop_table(table_name) if connection.tables.include?(table_name) end end - should 'not create a tables if error in migrations' do + it 'not create a tables if error in migrations' do begin migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table' - ActiveRecord::Migrator.up(migrations_dir) + quietly { ActiveRecord::Migrator.up(migrations_dir) } rescue Exception => 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 + connection.tables.wont_include @trans_test_table1 + connection.tables.wont_include @trans_test_table2 end end - context 'For changing column' do + describe 'For changing column' do - should 'not raise exception when column contains default constraint' do + it 'not raise exception when column contains default constraint' do lock_version_column = Person.columns_hash['lock_version'] assert_equal :integer, lock_version_column.type assert lock_version_column.default.present? - assert_nothing_raised { @connection.change_column 'people', 'lock_version', :string } + assert_nothing_raised { connection.change_column 'people', 'lock_version', :string } Person.reset_column_information lock_version_column = Person.columns_hash['lock_version'] assert_equal :string, lock_version_column.type assert lock_version_column.default.nil? end - should 'not drop the default contraint if just renaming' do + it 'not drop the default contraint if just renaming' do find_default = lambda do - @connection.execute_procedure(:sp_helpconstraint, 'defaults', 'nomsg').select do |row| + connection.execute_procedure(:sp_helpconstraint, 'defaults', 'nomsg').select do |row| row['constraint_type'] == "DEFAULT on column decimal_number" end.last end default_before = find_default.call - @connection.change_column :defaults, :decimal_number, :decimal, precision: 4 + connection.change_column :defaults, :decimal_number, :decimal, precision: 4 default_after = find_default.call assert default_after assert_equal default_before['constraint_keys'], default_after['constraint_keys'] @@ -62,17 +58,9 @@ class MigrationTestSQLServer < ActiveRecord::TestCase end -end - -if ActiveRecord::TestCase.sqlserver_azure? - class MigrationTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] - include ARTest::SQLServer::CoercedTest - - # TODO: put a real test here - def test_coerced_test_migrator_db_has_no_schema_migrations_table - assert true - end + def quietly + silence_stream(STDOUT) { silence_stream(STDERR) { yield } } end + end From ad66f64c95487df953464fdbad11594f75e714d7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 12:16:42 -0500 Subject: [PATCH 0232/1412] FETCH can not happen with this lying around. --- test/cases/offset_and_limit_test_sqlserver.rb | 127 ------------------ 1 file changed, 127 deletions(-) delete mode 100644 test/cases/offset_and_limit_test_sqlserver.rb diff --git a/test/cases/offset_and_limit_test_sqlserver.rb b/test/cases/offset_and_limit_test_sqlserver.rb deleted file mode 100644 index f20bfa01f..000000000 --- a/test/cases/offset_and_limit_test_sqlserver.rb +++ /dev/null @@ -1,127 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/job' -require 'models/person' -require 'models/reference' -require 'models/book' -require 'models/author' -require 'models/subscription' -require 'models/post' -require 'models/comment' -require 'models/categorization' -require 'models/sqlserver/sql_server_order_row_number' - -class OffsetAndLimitTestSQLServer < ActiveRecord::TestCase - - fixtures :jobs, :people, :references, :subscriptions, - :authors, :posts, :comments, :categorizations - - setup :create_10_books - teardown :destroy_all_books - - - context 'When selecting with limit' do - - should 'alter sql to limit number of records returned' do - assert_sql(/SELECT TOP \(10\)/) { Book.limit(10).load } - end - - end - - context 'When selecting with offset' do - - should 'have limit (top) of 9223372036854775807 if only offset is passed' do - assert_sql(/SELECT TOP \(9223372036854775807\) \[__rnt\]\.\* FROM.*WHERE \[__rnt\]\.\[__rn\] > \(1\)/) { Book.offset(1).load } - end - - should 'support calling exists?' do - assert Book.offset(3).exists? - end - - end - - context 'When selecting with limit and offset' do - - should 'work with fully qualified table and columns in select' do - books = Book.select('books.id, books.name').limit(3).offset(5) - assert_equal Book.all[5,3].map(&:id), books.map(&:id) - end - - should 'alter SQL to limit number of records returned offset by specified amount' do - sql = %|EXEC sp_executesql N'SELECT TOP (3) [__rnt].* FROM ( SELECT ROW_NUMBER() OVER (ORDER BY [books].[id] ASC) AS [__rn], [books].* FROM [books] ) AS [__rnt] WHERE [__rnt].[__rn] > (5) ORDER BY [__rnt].[__rn] ASC'| - assert_sql(sql) { Book.limit(3).offset(5).load } - end - - should 'add locks to deepest sub select' do - pattern = /FROM \[books\]\s+WITH \(NOLOCK\)/ - assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).count } - assert_sql(pattern) { Book.limit(3).lock('WITH (NOLOCK)').offset(5).load } - - end - - should 'have valid sort order' do - order_row_numbers = SqlServerOrderRowNumber.offset(7).order("c DESC").select("c, ROW_NUMBER() OVER (ORDER BY c ASC) AS [dummy]").map(&:c) - assert_equal [2, 1, 0], order_row_numbers - end - - should 'work with through associations' do - assert_equal people(:david), jobs(:unicyclist).people.limit(1).offset(1).first - end - - should 'work with through uniq associations' do - david = authors(:david) - mary = authors(:mary) - thinking = posts(:thinking) - # Mary has duplicate categorizations to the thinking post. - assert_equal [thinking, thinking], mary.categorized_posts.load - assert_equal [thinking], mary.unique_categorized_posts.limit(2).offset(0).load - # Paging thru David's uniq ordered comments, with count too. - assert_equal [1, 2, 3, 5, 6, 7, 8, 9, 10, 12], david.ordered_uniq_comments.map(&:id) - assert_equal [3, 5], david.ordered_uniq_comments.limit(2).offset(2).map(&:id) - assert_equal 2, david.ordered_uniq_comments.limit(2).offset(2).count - assert_equal [8, 9, 10, 12], david.ordered_uniq_comments.limit(5).offset(6).map(&:id) - assert_equal 4, david.ordered_uniq_comments.limit(5).offset(6).count - end - - should 'remove [__rnt] table names from relation reflection and hence do not eager loading' do - create_10_books - create_10_books - assert_queries(1) { Book.includes(:subscriptions).limit(10).offset(10).references(:subscriptions).load } - end - - - context 'with count' do - - should 'pass a gauntlet of window tests' do - Book.first.destroy - Book.first.destroy - Book.first.destroy - assert_equal 7, Book.count - assert_equal 1, Book.limit(1).offset(1).size - assert_equal 1, Book.limit(1).offset(5).size - assert_equal 1, Book.limit(1).offset(6).size - assert_equal 0, Book.limit(1).offset(7).size - assert_equal 3, Book.limit(3).offset(4).size - assert_equal 2, Book.limit(3).offset(5).size - assert_equal 1, Book.limit(3).offset(6).size - assert_equal 0, Book.limit(3).offset(7).size - assert_equal 0, Book.limit(3).offset(8).size - end - - end - - end - - - protected - - def create_10_books - Book.delete_all - @books = (1..10).map {|i| Book.create!} - end - - def destroy_all_books - @books.each { |b| b.destroy } - end - -end - From 59e3e064f2c507fe607e6f859262ef9ebd090a0b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 13:20:34 -0500 Subject: [PATCH 0233/1412] Order all green. --- test/cases/order_test_sqlserver.rb | 277 ++++++++++++++--------------- 1 file changed, 138 insertions(+), 139 deletions(-) diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index f538a226e..839a4e1a9 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -5,144 +5,143 @@ class OrderTestSQLServer < ActiveRecord::TestCase fixtures :posts - context 'Order by' do - - should 'not mangel complex order clauses' do - xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END" - xyz_post = Post.create title: 'XYZ Post', body: 'Test cased orders.' - assert_equal xyz_post, Post.order(Arel::Nodes::Ordering.new(Arel.sql(xyz_order))).first - end - - should 'support column' do - order = "title" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support column ASC' do - order = "title ASC" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support column DESC' do - order = "title DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support column as symbol' do - order = :title - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support table and column' do - order = "posts.title" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support quoted column' do - order = "[title]" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support quoted table and column' do - order = "[posts].[title]" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support primary: column, secondary: column' do - order = "title DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: table and column, secondary: column' do - order = "posts.title DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: case expression, secondary: column' do - order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: quoted table and column, secondary: case expresion' do - order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC" - post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support inline function' do - order = "LEN(title)" - post1 = Post.create title: 'A', body: 'AAA Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support inline function with parameters' do - order = "SUBSTRING(title, 1, 3)" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support inline function with parameters DESC' do - order = "SUBSTRING(title, 1, 3) DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - end - - should 'support primary: inline function, secondary: column' do - order = "LEN(title), body" - post1 = Post.create title: 'A', body: 'AAA Test cased orders.' - post2 = Post.create title: 'A', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: inline function, secondary: column with direction' do - order = "LEN(title) ASC, body DESC" - post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'A', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: column, secondary: inline function' do - order = "body DESC, LEN(title)" - post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: case expression, secondary: inline function' do - order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC" - post1 = Post.create title: 'ZZZ Post', body: 'Z' - post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end - - should 'support primary: inline function, secondary: case expression' do - order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Z' - post2 = Post.create title: 'Post', body: 'Z' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second - end + it 'not mangel complex order clauses' do + xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END" + xyz_post = Post.create title: 'XYZ Post', body: 'Test cased orders.' + assert_equal xyz_post, Post.order(Arel.sql(xyz_order)).first end + + it 'support column' do + order = "title" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support column ASC' do + order = "title ASC" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support column DESC' do + order = "title DESC" + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support column as symbol' do + order = :title + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support table and column' do + order = "posts.title" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support quoted column' do + order = "[title]" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support quoted table and column' do + order = "[posts].[title]" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support primary: column, secondary: column' do + order = "title DESC, body" + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: table and column, secondary: column' do + order = "posts.title DESC, body" + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: case expression, secondary: column' do + order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body" + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: quoted table and column, secondary: case expresion' do + order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC" + post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support inline function' do + order = "LEN(title)" + post1 = Post.create title: 'A', body: 'AAA Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support inline function with parameters' do + order = "SUBSTRING(title, 1, 3)" + post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support inline function with parameters DESC' do + order = "SUBSTRING(title, 1, 3) DESC" + post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + end + + it 'support primary: inline function, secondary: column' do + order = "LEN(title), body" + post1 = Post.create title: 'A', body: 'AAA Test cased orders.' + post2 = Post.create title: 'A', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: inline function, secondary: column with direction' do + order = "LEN(title) ASC, body DESC" + post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'A', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: column, secondary: inline function' do + order = "body DESC, LEN(title)" + post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.' + post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: case expression, secondary: inline function' do + order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC" + post1 = Post.create title: 'ZZZ Post', body: 'Z' + post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + it 'support primary: inline function, secondary: case expression' do + order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC" + post1 = Post.create title: 'ZZZ Post', body: 'Z' + post2 = Post.create title: 'Post', body: 'Z' + assert_equal post1, Post.order(order).first + assert_equal post2, Post.order(order).second + end + + end From 1a910990b1a60c2b7c6786d169b44aa47f648610 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 16:00:27 -0500 Subject: [PATCH 0234/1412] Locking now done. Default lock is now "WITH (UPDLOCK)". Fixes #368 --- CHANGELOG.md | 2 +- lib/arel/visitors/sqlserver.rb | 42 +++++++++++++- .../pessimistic_locking_test_sqlserver.rb | 56 +++++++++++-------- 3 files changed, 76 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0da5123c2..5261b5acc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ #### Fixed -* n/a +* Default lock is now "WITH (UPDLOCK)". Fixes #368 #### Security diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 02957347f..1e79420b9 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -16,6 +16,12 @@ def visit_Arel_Nodes_BindParam o, collector collector.add_bind(o) { |i| "@#{i-1}" } end + def visit_Arel_Nodes_Lock o, collector + o.expr = Arel.sql('WITH (UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/ + collector << SPACE + visit o.expr, collector + end + def visit_Arel_Nodes_Offset o, collector collector << OFFSET visit o.expr, collector @@ -29,6 +35,7 @@ def visit_Arel_Nodes_Limit o, collector end def visit_Arel_Nodes_SelectStatement o, collector + @select_statement = o if o.with collector = visit o.with, collector collector << SPACE @@ -38,12 +45,41 @@ def visit_Arel_Nodes_SelectStatement o, collector } collector = visit_Orders_And_Let_Fetch_Happen o, collector collector = visit_Make_Fetch_Happen o, collector - collector = visit o.lock, collector if o.lock + collector + ensure + @select_statement = nil + end + + def visit_Arel_Nodes_JoinSource o, collector + if o.left + collector = visit o.left, collector + collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector + end + if o.right.any? + collector << " " if o.left + collector = inject_join o.right, collector, ' ' + end collector end + def visit_Arel_Nodes_OuterJoin o, collector + collector << "LEFT OUTER JOIN " + collector = visit o.left, collector + collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true + collector << " " + visit o.right, collector + end + # SQLServer ToSql/Visitor (Additions) + def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} + if select_statement_lock? + collector = visit @select_statement.lock, collector + collector << SPACE if options[:space] + end + collector + end + def visit_Orders_And_Let_Fetch_Happen o, collector if (o.limit || o.offset) && o.orders.empty? table = table_From_Statement o @@ -71,6 +107,10 @@ def visit_Make_Fetch_Happen o, collector # SQLServer Helpers + def select_statement_lock? + @select_statement && @select_statement.lock + end + def table_From_Statement o core = o.cores.first if Arel::Table === core.from diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index fb9f4b756..fafe6d092 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -4,42 +4,49 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase - self.use_transactional_fixtures = false fixtures :people, :readers - setup do - Person.columns; Reader.columns # Avoid introspection queries during tests. + before do + Person.columns + Reader.columns end - context 'For simple finds with default lock option' do + it 'uses with updlock by default' do + assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH \(UPDLOCK\)| do + Person.lock(true).to_a.must_equal Person.all.to_a + end + end - should 'lock with simple find' do + describe 'For simple finds with default lock option' do + + it 'lock with simple find' do assert_nothing_raised do Person.transaction do - Person.lock(true).find(1) + Person.lock(true).find(1).must_equal Person.find(1) end end end - should 'lock with scoped find' do + it 'lock with scoped find' do assert_nothing_raised do Person.transaction do Person.lock(true).scoping do - Person.find(1) + Person.find(1).must_equal Person.find(1) end end end end - should 'lock with eager find' do + it 'lock with eager find' do assert_nothing_raised do Person.transaction do - Person.lock(true).includes(:readers).find(1) + person = Person.lock(true).includes(:readers).find(1) + person.must_equal Person.find(1) end end end - should 'reload with lock when #lock! called' do + it 'reload with lock when #lock! called' do assert_nothing_raised do Person.transaction do person = Person.find 1 @@ -50,29 +57,34 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - should 'simply add lock to find all' do - assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH \(NOLOCK\)| do - Person.lock('WITH (NOLOCK)').load + it 'can add a custom lock directive' do + assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do + Person.lock('WITH(HOLDLOCK, ROWLOCK)').load end end end - context 'For paginated finds' do + describe 'For paginated finds' do - setup do + before do + Person.delete_all 20.times { |n| Person.create!(first_name: "Thing_#{n}") } end - should 'cope with eager loading un-locked paginated' do - eager_ids_sql = /SELECT TOP \(5\).*FROM \[people\] WITH \(NOLOCK\)/ - loader_sql = /FROM \[people\] WITH \(NOLOCK\).*WHERE \[people\]\.\[id\] IN/ - assert_sql(eager_ids_sql,loader_sql) do - Person.lock('WITH (NOLOCK)').limit(5).offset(10).includes(:readers).references(:readers).load + it 'copes with eager loading un-locked paginated' do + eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH \(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH \(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/ + loader_sql = /SELECT.*FROM \[people\] WITH \(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ + assert_sql(eager_ids_sql, loader_sql) do + people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a + people[0].first_name.must_equal 'Thing_10' + people[1].first_name.must_equal 'Thing_11' + people[2].first_name.must_equal 'Thing_12' + people[3].first_name.must_equal 'Thing_13' + people[4].first_name.must_equal 'Thing_14' end end end - end From e88c19fc8e532ed46c4bac387745e6c5e4b73983 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 22:37:29 -0500 Subject: [PATCH 0235/1412] Schema dumping for the win! --- CHANGELOG.md | 3 +- .../sqlserver/schema_statements.rb | 24 +-- .../connection_adapters/sqlserver/type.rb | 1 + .../sqlserver/type/binary.rb | 3 + .../sqlserver/type/char.rb | 3 + .../sqlserver/type/small_money.rb | 4 + .../sqlserver/type/text.rb | 3 + .../sqlserver/type/unicode_char.rb | 3 + .../sqlserver/type/unicode_text.rb | 3 + .../sqlserver/type/unicode_varchar.rb | 3 + .../sqlserver/type/varbinary.rb | 3 + .../sqlserver/type/varbinary_max.rb | 4 + .../sqlserver/type/varchar.rb | 4 + .../sqlserver/type/varchar_max.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 2 +- .../{ => coerced}/resolver_test_sqlserver.rb | 0 test/cases/column_test_sqlserver.rb | 22 +-- test/cases/schema_dumper_test_sqlserver.rb | 159 ++++++++++-------- test/schema/sqlserver_specific_schema.rb | 22 --- 19 files changed, 149 insertions(+), 119 deletions(-) rename test/cases/{ => coerced}/resolver_test_sqlserver.rb (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5261b5acc..cc995cf60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * New `ActiveRecord::Type` objects. See `active_record/connection_adapters/sqlserver/type` dir. * New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. +* Support for most all SQL Server types in schema statements and dumping. #### Changed @@ -22,7 +23,7 @@ #### Removed * SQL Server versions < 2012 which do not support OFFSET and FETCH. http://bit.ly/1B5Bwsd -* The `enable_default_unicode_types` option. Default to national types all the time. Use SQL type name in migrations if needed. +* The `enable_default_unicode_types` option. Default to national types all the time. * Native type configs for older DB support. Includes the following with new default value: * native_string_database_type => `nvarchar` * native_text_database_type => `nvarchar(max)` diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 175b74d8f..321cfac4a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -163,25 +163,29 @@ def views def initialize_native_database_types { primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', - string: { name: 'nvarchar', limit: 255 }, - text: { name: 'nvarchar(max)' }, integer: { name: 'int', limit: 4 }, - float: { name: 'float', limit: 8 }, + boolean: { name: 'bit' }, decimal: { name: 'decimal' }, + money: { name: 'money' }, + smallmoney: { name: 'smallmoney' }, + float: { name: 'float', limit: 8 }, + real: { name: 'real' }, + date: { name: 'date' }, datetime: { name: 'datetime' }, timestamp: { name: 'datetime' }, time: { name: 'time' }, - date: { name: 'date' }, - binary: { name: 'varbinary(max)' }, - boolean: { name: 'bit' }, - uuid: { name: 'uniqueidentifier' }, - # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them. char: { name: 'char' }, + varchar: { name: 'varchar' }, varchar_max: { name: 'varchar(max)' }, + text_basic: { name: 'text' }, nchar: { name: 'nchar' }, - nvarchar: { name: 'nvarchar', limit: 255 }, - nvarchar_max: { name: 'nvarchar(max)' }, + string: { name: 'nvarchar', limit: 255 }, + text: { name: 'nvarchar(max)' }, ntext: { name: 'ntext' }, + binary_basic: { name: 'binary' }, + varbinary: { name: 'varbinary' }, + binary: { name: 'varbinary(max)' }, + uuid: { name: 'uniqueidentifier' }, ss_timestamp: { name: 'timestamp' } } end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 2ce9925a5..18b63a564 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -13,6 +13,7 @@ require 'active_record/connection_adapters/sqlserver/type/small_money.rb' # Approximate Numerics require 'active_record/connection_adapters/sqlserver/type/float.rb' +require 'active_record/connection_adapters/sqlserver/type/real.rb' # Date and Time require 'active_record/connection_adapters/sqlserver/type/date.rb' require 'active_record/connection_adapters/sqlserver/type/datetime.rb' diff --git a/lib/active_record/connection_adapters/sqlserver/type/binary.rb b/lib/active_record/connection_adapters/sqlserver/type/binary.rb index 68d5f7d6b..cad49bd4f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/binary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/binary.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Binary < ActiveRecord::Type::Binary + def type + :binary_basic + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index c69c028d6..f75ea8785 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Char < String + def type + :char + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 7d7388bba..6f889abe7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -10,6 +10,10 @@ def initialize(options = {}) @scale = 4 end + def type + :smallmoney + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/text.rb b/lib/active_record/connection_adapters/sqlserver/type/text.rb index daed3b11d..bc472046c 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/text.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Text < VarcharMax + def type + :text_basic + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb index f777b873d..7795029a5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -4,6 +4,9 @@ module SQLServer module Type class UnicodeChar < UnicodeString + def type + :nchar + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb index e61759158..017b6595b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb @@ -4,6 +4,9 @@ module SQLServer module Type class UnicodeText < UnicodeVarcharMax + def type + :ntext + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 153d6d3d1..96b17d709 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -4,6 +4,9 @@ module SQLServer module Type class UnicodeVarchar < UnicodeChar + def type + :string + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index 69dd8f4a1..a168e7c20 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Varbinary < Binary + def type + :varbinary + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index 5ae61933a..65d82edcf 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -9,6 +9,10 @@ def initialize(options = {}) @limit = 2_147_483_647 end + def type + :binary + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index 06ab885c0..1e01a443d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -4,6 +4,10 @@ module SQLServer module Type class Varchar < Char + def type + :varchar + end + end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 933f20527..601cec9f1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -10,7 +10,7 @@ def initialize(options = {}) end def type - :text + :varchar_max end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index ac439a88f..c05c3d589 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -208,7 +208,7 @@ def initialize_type_map(m) m.register_type 'smallmoney', SQLServer::Type::SmallMoney.new # Approximate Numerics register_class_with_limit m, %r{\Afloat}, SQLServer::Type::Float - m.alias_type %r{\Areal}i, 'float' + register_class_with_limit m, %r{\Areal}, SQLServer::Type::Real # Date and Time m.register_type 'date', SQLServer::Type::Date.new m.register_type 'datetime', SQLServer::Type::DateTime.new diff --git a/test/cases/resolver_test_sqlserver.rb b/test/cases/coerced/resolver_test_sqlserver.rb similarity index 100% rename from test/cases/resolver_test_sqlserver.rb rename to test/cases/coerced/resolver_test_sqlserver.rb diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index f4693d5bd..8030bae69 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -226,7 +226,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallMoney - type.type.must_equal :money + type.type.must_equal :smallmoney type.must_be :number? type.limit.must_equal nil type.precision.must_equal 10 @@ -281,8 +281,8 @@ def assert_obj_set_and_save(attribute, value) obj.real.must_be_close_to 123.45, 0.01 col.default_function.must_equal nil type = col.cast_type - type.must_be_instance_of Type::Float - type.type.must_equal :float + type.must_be_instance_of Type::Real + type.type.must_equal :real type.must_be :number? type.limit.must_equal 24 type.precision.must_equal nil @@ -446,7 +446,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Char - type.type.must_equal :string + type.type.must_equal :char type.wont_be :number? type.limit.must_equal 10 type.precision.must_equal nil @@ -467,7 +467,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Varchar - type.type.must_equal :string + type.type.must_equal :varchar type.wont_be :number? type.limit.must_equal 50 type.precision.must_equal nil @@ -485,7 +485,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::VarcharMax - type.type.must_equal :text + type.type.must_equal :varchar_max type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil @@ -503,7 +503,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Text - type.type.must_equal :text + type.type.must_equal :text_basic type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil @@ -523,7 +523,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeChar - type.type.must_equal :string + type.type.must_equal :nchar type.wont_be :number? type.limit.must_equal 10 type.precision.must_equal nil @@ -580,7 +580,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::UnicodeText - type.type.must_equal :text + type.type.must_equal :ntext type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil @@ -602,7 +602,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Binary - type.type.must_equal :binary + type.type.must_equal :binary_basic type.wont_be :number? type.limit.must_equal 49 type.precision.must_equal nil @@ -624,7 +624,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::Varbinary - type.type.must_equal :binary + type.type.must_equal :varbinary type.wont_be :number? type.limit.must_equal 49 type.precision.must_equal nil diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 77f78d533..eb3253b53 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,103 +1,116 @@ require 'cases/helper_sqlserver' -require 'cases/schema_dumper_test' require 'stringio' - class SchemaDumperTestSQLServer < ActiveRecord::TestCase - setup :find_all_tables - - context 'For primary keys' do - - should 'honor nonstandards' do - table_dump('movies') do |output| - match = output.match(%r{create_table "movies"(.*)do}) - assert_not_nil(match, "nonstandardpk table not found") - assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" - end - end - + before { all_tables } + + let(:all_tables) { ActiveRecord::Base.connection.tables } + let(:schema) { @generated_schema } + + it 'sst_datatypes' do + generate_schema_for_table 'sst_datatypes' + # Exact Numerics + assert_line :bigint, type: 'integer', limit: '8', precision: nil, scale: nil, default: '42' + assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42' + assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42' + assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42' + assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true' + assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01' + assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0' + assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01' + assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2' + assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2' + # Approximate Numerics + assert_line :float, type: 'float', limit: '53', precision: nil, scale: nil, default: '123.00000001' + assert_line :float_25, type: 'float', limit: '53', precision: nil, scale: nil, default: '420.11' + assert_line :real, type: 'real', limit: '24', precision: nil, scale: nil, default: %r{123.4[45]} + # Date and Time + assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" + assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" + assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" + assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil + assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + # Character Strings + assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" + assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" + assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\"" + assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\"" + # Unicode Character Strings + assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\"" + assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\"" + assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\"" + assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\"" + # Binary Strings + assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil + assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil + assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil end - context 'For integers' do - - should 'include limit constraint that match logic for smallint and bigint in #extract_limit' do - table_dump('integer_limits') do |output| - assert_match %r{c_int_1.*limit: 2}, output - assert_match %r{c_int_2.*limit: 2}, output - assert_match %r{c_int_3.*}, output - assert_match %r{c_int_4.*}, output - assert_no_match %r{c_int_3.*:limit}, output - assert_no_match %r{c_int_4.*:limit}, output - assert_match %r{c_int_5.*limit: 8}, output - assert_match %r{c_int_6.*limit: 8}, output - assert_match %r{c_int_7.*limit: 8}, output - assert_match %r{c_int_8.*limit: 8}, output - end + it 'primary_key' do + generate_schema_for_table('movies') do |output| + match = output.match(%r{create_table "movies"(.*)do}) + assert_not_nil(match, "nonstandardpk table not found") + assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" end - - end - - context 'For strings' do - - should 'have varchar(max) dumped as text' do - table_dump('sql_server_strings') do |output| - assert_match %r{t.text.*varchar_max}, output - end - end - end private - def find_all_tables - @all_tables ||= ActiveRecord::Base.connection.tables - end - - def standard_dump(ignore_tables = []) + def generate_schema_for_table(*table_names) stream = StringIO.new - ActiveRecord::SchemaDumper.ignore_tables = [*ignore_tables] + ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) - stream.string + @generated_schema = stream.string + yield @generated_schema if block_given? + @schema_lines = Hash.new + type_matcher = /\A\s+t\.\w+\s+"(.*?)",/ + @generated_schema.each_line do |line| + next unless line =~ type_matcher + @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line) + end + @generated_schema end - def table_dump(*table_names) - stream = StringIO.new - ActiveRecord::SchemaDumper.ignore_tables = @all_tables-table_names - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) - yield stream.string - stream.string + def line(column_name) + @schema_lines[column_name.to_s] end -end - - -class SchemaDumperTest < ActiveRecord::TestCase + def assert_line(column_name, options={}) + line = line(column_name) + assert line, "Count not find line with column name: #{column_name.inspect}" + line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type) + line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit) + line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision) + line.scale.must_equal options[:scale], "Scale of #{options[:scale].inspect} not found in:\n #{line}" if options.key?(:scale) + line.default.must_equal options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(String) + line.default.must_match options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(Regexp) + end - COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] + class SchemaLine - include ARTest::SQLServer::CoercedTest + attr_reader :line - def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal - output = standard_dump - assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output - end + def self.match(method_name, pattern) + define_method(method_name) { line.match(pattern).try :[], 1 } + end - def test_coerced_types_line_up - column_definition_lines.each do |column_set| - next if column_set.empty? + def initialize(line) + @line = line + end - lengths = column_set.map do |column| - if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) - match[0].length - end - end + match :type_method, %r{\A\s+t\.(.*?)\s} + match :limit, %r{\slimit:\s(.*?)[,\s]} + match :default, %r{\sdefault:\s(.*)\n} + match :precision, %r{\sprecision:\s(.*?)[,\s]} + match :scale, %r{\sscale:\s(.*?)[,\s]} - assert_equal 1, lengths.uniq.length + def to_s + line end + end end - diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 47845409b..f8b080cef 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -122,28 +122,6 @@ t.column :decimal_number, :decimal, precision: 3, scale: 2, default: 2.78 end - create_table :sql_server_unicodes, force: true do |t| - t.column :nchar, :nchar - t.column :nvarchar, :nvarchar - t.column :ntext, :ntext - t.column :ntext_10, :ntext, limit: 10 - t.column :nchar_10, :nchar, limit: 10 - t.column :nvarchar_100, :nvarchar, limit: 100 - t.column :nvarchar_max, :nvarchar_max - t.column :nvarchar_max_10, :nvarchar_max, limit: 10 - end - - create_table :sql_server_strings, force: true do |t| - t.column :char, :char - t.column :char_10, :char, limit: 10 - t.column :varchar_max, :varchar_max - t.column :varchar_max_10, :varchar_max, limit: 10 - end - - create_table :sql_server_binary_types, force: true do |t| - # TODO: Add some different native binary types and test. - end - # http://blogs.msdn.com/b/craigfr/archive/2008/03/19/ranking-functions-row-number.aspx execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'order_row_number') DROP TABLE order_row_number" execute <<-ORDERROWNUMBERSQL From 17eaddc7355930e10d05425eb2713641024e72a2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 17 Jan 2015 22:37:55 -0500 Subject: [PATCH 0236/1412] Missing files from last commit. --- .../sqlserver/type/real.rb | 17 ++++++++++ .../coerced/schema_dumper_test_sqlserver.rb | 32 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/real.rb create mode 100644 test/cases/coerced/schema_dumper_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/type/real.rb b/lib/active_record/connection_adapters/sqlserver/type/real.rb new file mode 100644 index 000000000..4b102bd41 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/real.rb @@ -0,0 +1,17 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Real < Float + + include Castable + + def type + :real + end + + end + end + end + end +end diff --git a/test/cases/coerced/schema_dumper_test_sqlserver.rb b/test/cases/coerced/schema_dumper_test_sqlserver.rb new file mode 100644 index 000000000..267e7036d --- /dev/null +++ b/test/cases/coerced/schema_dumper_test_sqlserver.rb @@ -0,0 +1,32 @@ +require 'cases/helper_sqlserver' +require 'cases/schema_dumper_test' +require 'stringio' + +class SchemaDumperTest < ActiveRecord::TestCase + + COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] + + include ARTest::SQLServer::CoercedTest + + def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal + output = standard_dump + assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output + end + + def test_coerced_types_line_up + column_definition_lines.each do |column_set| + next if column_set.empty? + + lengths = column_set.map do |column| + if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) + match[0].length + end + end + + assert_equal 1, lengths.uniq.length + end + end + +end + + From b59081e9bfd98c5b3828e84bbab7aef2b58dfa94 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 08:37:20 -0500 Subject: [PATCH 0237/1412] Follow Rails convention and remove varying character default limits. --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 4 ++-- .../sqlserver/type/unicode_varchar.rb | 5 +++++ .../connection_adapters/sqlserver/type/varchar.rb | 6 +++++- .../connection_adapters/sqlserver_adapter.rb | 1 + test/cases/schema_dumper_test_sqlserver.rb | 14 +++++++++++++- test/models/sqlserver/datatype_migration.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 9 +++++---- 8 files changed, 35 insertions(+), 8 deletions(-) create mode 100644 test/models/sqlserver/datatype_migration.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index cc995cf60..0befbf70a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Guard support, check our Guardfile. * Use `ARTest` namespace with `SQLServer` module for our helpers/objects. * Simple 2012 schmea addition and extensive column/type_cast object tests. +* Follow Rails convention and remove varying character default limits. #### Deprecated diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 321cfac4a..6f1d03a70 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -175,11 +175,11 @@ def initialize_native_database_types timestamp: { name: 'datetime' }, time: { name: 'time' }, char: { name: 'char' }, - varchar: { name: 'varchar' }, + varchar: { name: 'varchar', limit: 8000 }, varchar_max: { name: 'varchar(max)' }, text_basic: { name: 'text' }, nchar: { name: 'nchar' }, - string: { name: 'nvarchar', limit: 255 }, + string: { name: 'nvarchar', limit: 4000 }, text: { name: 'nvarchar(max)' }, ntext: { name: 'ntext' }, binary_basic: { name: 'binary' }, diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 96b17d709..ed8fafc4d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -4,6 +4,11 @@ module SQLServer module Type class UnicodeVarchar < UnicodeChar + def initialize(options = {}) + super + @limit = 4000 if @limit.to_i == 0 + end + def type :string end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index 1e01a443d..e5a65176b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -4,11 +4,15 @@ module SQLServer module Type class Varchar < Char + def initialize(options = {}) + super + @limit = 8000 if @limit.to_i == 0 + end + def type :varchar end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c05c3d589..93bc57efb 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -226,6 +226,7 @@ def initialize_type_map(m) # 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 'ntext', SQLServer::Type::UnicodeText.new # Binary Strings diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index eb3253b53..61d6da8d2 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,5 +1,4 @@ require 'cases/helper_sqlserver' -require 'stringio' class SchemaDumperTestSQLServer < ActiveRecord::TestCase @@ -55,10 +54,23 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase end end + it 'a string type is nvarchar without a limit' do + generate_schema_for_table 'sst_datatypes_migration' + SSTestDatatypeMigration.columns_hash['string_col'].sql_type.must_equal 'nvarchar(4000)' + assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil + end + + it 'a text type is nvarchar max' do + generate_schema_for_table 'sst_datatypes_migration' + SSTestDatatypeMigration.columns_hash['text_col'].sql_type.must_equal 'nvarchar(max)' + assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil + end + private def generate_schema_for_table(*table_names) + require 'stringio' stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) diff --git a/test/models/sqlserver/datatype_migration.rb b/test/models/sqlserver/datatype_migration.rb new file mode 100644 index 000000000..e3752a3f3 --- /dev/null +++ b/test/models/sqlserver/datatype_migration.rb @@ -0,0 +1,3 @@ +class SSTestDatatypeMigration < ActiveRecord::Base + self.table_name = :sst_datatypes_migration +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index f8b080cef..2464b9b5e 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -5,10 +5,13 @@ execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) create_table :sst_datatypes_migration, force: true do |t| - t.column :real, :real + # Rails conventions + t.string :string_col + t.text :text_col + # Our type methods + # ... end - # Edge Cases create_table 'sst_my$strange_table', force: true do |t| @@ -70,7 +73,6 @@ ) NATURALPKINTTABLESQL - # Constraints create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } @@ -82,7 +84,6 @@ REFERENCES [sst_has_pks] ([id]) ADDFKSQL - # Views execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_customers_view') DROP VIEW sst_customers_view" From 7c2fabb3dd61e7d861a4c45ebf5316349dc877e9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 10:19:09 -0500 Subject: [PATCH 0238/1412] Make sure basic Rails types match up in schema statements. --- .../sqlserver/schema_creation.rb | 2 + .../sqlserver/schema_statements.rb | 6 +-- .../sqlserver/table_definition.rb | 5 +- .../sqlserver/type/big_integer.rb | 4 +- .../sqlserver/type/float.rb | 5 ++ test/cases/adapter_test_sqlserver.rb | 2 +- test/cases/column_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 50 +++++++++++++------ test/schema/sqlserver_specific_schema.rb | 22 ++++++-- 9 files changed, 68 insertions(+), 30 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index c6c3fc044..66da82353 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer class SchemaCreation < AbstractAdapter::SchemaCreation + private def visit_ColumnDefinition(o) @@ -50,6 +51,7 @@ def visit_TableDefinition(o) create_sql end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6f1d03a70..5ed90dfae 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -168,7 +168,7 @@ def initialize_native_database_types decimal: { name: 'decimal' }, money: { name: 'money' }, smallmoney: { name: 'smallmoney' }, - float: { name: 'float', limit: 8 }, + float: { name: 'float', limit: 24 }, real: { name: 'real' }, date: { name: 'date' }, datetime: { name: 'datetime' }, @@ -402,7 +402,6 @@ def strip_ident_from_update(sql) # There has to be a better way to handle this, but this'll do for now. table_name = get_table_name(sql) id_column = identity_column(table_name) - if id_column regex_col_name = Regexp.quote(quote_column_name(id_column.name)) if sql =~ /, #{regex_col_name} = @?[0-9]*/ @@ -437,10 +436,11 @@ def identity_column(table_name) schema_cache.columns(table_name).find(&:is_identity?) end + private def create_table_definition(name, temporary, options, as = nil) - TableDefinition.new native_database_types, name, temporary, options, as + SQLServer::TableDefinition.new native_database_types, name, temporary, options, as end end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index c956eb62f..c4a2a6688 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + def uuid(name, options = {}) column(name, 'uniqueidentifier', options) end @@ -13,10 +14,6 @@ def primary_key(name, type = :primary_key, options = {}) column name, type, options end - def column(name, type = nil, options = {}) - super - self - end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb index 13a8c4b01..b95424d86 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -4,7 +4,9 @@ module SQLServer module Type class BigInteger < Integer - + def type + :bigint + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index 1629a8198..083ec0230 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -6,6 +6,11 @@ class Float < ActiveRecord::Type::Float include Castable + def initialize(options = {}) + super + @limit ||= 24 + end + end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index be87e5752..741869ed7 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -333,7 +333,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'create floats when no limit supplied' do - assert_equal 'float(8)', connection.type_to_sql(:float) + assert_equal 'float(24)', connection.type_to_sql(:float) end it 'create floats when limit is supplied' do diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 8030bae69..bbc2b807d 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -37,7 +37,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::BigInteger - type.type.must_equal :integer + type.type.must_equal :bigint type.must_be :number? type.limit.must_equal 8 assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 61d6da8d2..f947f0767 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -10,7 +10,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'sst_datatypes' do generate_schema_for_table 'sst_datatypes' # Exact Numerics - assert_line :bigint, type: 'integer', limit: '8', precision: nil, scale: nil, default: '42' + assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42' assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42' assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42' assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42' @@ -46,6 +46,38 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil end + it 'sst_datatypes_migration' do + columns = SSTestDatatypeMigration.columns_hash + 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 'real(24)' + columns['string_col'].sql_type.must_equal 'nvarchar(4000)' + columns['text_col'].sql_type.must_equal 'nvarchar(max)' + columns['datetime_col'].sql_type.must_equal 'datetime' + columns['timestamp_col'].sql_type.must_equal 'datetime' + 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', limit: '4', precision: nil, scale: nil, default: nil + assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil + assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil + assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil + assert_line :float_col, type: 'real', limit: '24', precision: nil, scale: nil, default: nil + assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil + assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil + assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil + assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + end + + # Special Cases + it 'primary_key' do generate_schema_for_table('movies') do |output| match = output.match(%r{create_table "movies"(.*)do}) @@ -54,18 +86,6 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase end end - it 'a string type is nvarchar without a limit' do - generate_schema_for_table 'sst_datatypes_migration' - SSTestDatatypeMigration.columns_hash['string_col'].sql_type.must_equal 'nvarchar(4000)' - assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil - end - - it 'a text type is nvarchar max' do - generate_schema_for_table 'sst_datatypes_migration' - SSTestDatatypeMigration.columns_hash['text_col'].sql_type.must_equal 'nvarchar(max)' - assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil - end - private @@ -77,7 +97,7 @@ def generate_schema_for_table(*table_names) @generated_schema = stream.string yield @generated_schema if block_given? @schema_lines = Hash.new - type_matcher = /\A\s+t\.\w+\s+"(.*?)",/ + type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/ @generated_schema.each_line do |line| next unless line =~ type_matcher @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line) @@ -91,7 +111,7 @@ def line(column_name) def assert_line(column_name, options={}) line = line(column_name) - assert line, "Count not find line with column name: #{column_name.inspect}" + assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type) line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit) line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision) diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 2464b9b5e..c77eee419 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -5,17 +5,29 @@ execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) create_table :sst_datatypes_migration, force: true do |t| - # Rails conventions - t.string :string_col - t.text :text_col - # Our type methods + # 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_col + t.timestamp :timestamp_col + t.time :time_col + t.date :date_col + t.binary :binary_col + + + # Our type methods. Make sure we can reverse schema dump for [sst_datatypes] table. # ... end # Edge Cases create_table 'sst_my$strange_table', force: true do |t| - t.column :number, :real + t.string :name end create_table :SST_UPPER_TESTS, force: true do |t| From 001e72366265d21d2727a0a95adcd096c80a83a3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 11:20:23 -0500 Subject: [PATCH 0239/1412] Make sure all datatypes can dump to and from. --- .../sqlserver/schema_statements.rb | 6 +-- .../sqlserver/table_definition.rb | 52 +++++++++++++++++-- .../sqlserver/type/float.rb | 5 +- .../sqlserver/type/varbinary.rb | 5 ++ .../connection_adapters/sqlserver_adapter.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 6 +-- test/cases/column_test_sqlserver.rb | 24 +++------ test/cases/schema_dumper_test_sqlserver.rb | 30 +++++++++-- test/schema/datatypes/2012.sql | 2 - test/schema/sqlserver_specific_schema.rb | 15 ++++-- 10 files changed, 103 insertions(+), 46 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5ed90dfae..8e13b9bfe 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -168,7 +168,7 @@ def initialize_native_database_types decimal: { name: 'decimal' }, money: { name: 'money' }, smallmoney: { name: 'smallmoney' }, - float: { name: 'float', limit: 24 }, + float: { name: 'float' }, real: { name: 'real' }, date: { name: 'date' }, datetime: { name: 'datetime' }, @@ -183,7 +183,7 @@ def initialize_native_database_types text: { name: 'nvarchar(max)' }, ntext: { name: 'ntext' }, binary_basic: { name: 'binary' }, - varbinary: { name: 'varbinary' }, + varbinary: { name: 'varbinary', limit: 8000 }, binary: { name: 'varbinary(max)' }, uuid: { name: 'uniqueidentifier' }, ss_timestamp: { name: 'timestamp' } @@ -254,7 +254,7 @@ def column_definitions(table_name) when /^numeric|decimal$/i "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})" when /^float|real$/i - "#{ci[:type]}(#{ci[:numeric_precision]})" + "#{ci[:type]}" when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/ ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})" else diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index c4a2a6688..1c546b691 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -3,10 +3,6 @@ module ConnectionAdapters module SQLServer class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition - def uuid(name, options = {}) - column(name, 'uniqueidentifier', options) - end - def primary_key(name, type = :primary_key, options = {}) return super unless type == :uuid options[:default] = options.fetch(:default, 'NEWID()') @@ -14,6 +10,54 @@ def primary_key(name, type = :primary_key, options = {}) column name, type, options end + def real(name, options = {}) + column(name, :real, options) + end + + def money(name, options = {}) + column(name, :money, options) + end + + def smallmoney(name, options = {}) + column(name, :smallmoney, options) + end + + def char(name, options = {}) + column(name, :char, options) + end + + def varchar(name, options = {}) + column(name, :varchar, options) + end + + def varchar_max(name, options = {}) + column(name, :varchar_max, options) + end + + def text_basic(name, options = {}) + column(name, :text_basic, options) + end + + def nchar(name, options = {}) + column(name, :nchar, options) + end + + def ntext(name, options = {}) + column(name, :ntext, options) + end + + def binary_basic(name, options = {}) + column(name, :binary_basic, options) + end + + def varbinary(name, options = {}) + column(name, :varbinary, options) + end + + def uuid(name, options = {}) + column(name, 'uniqueidentifier', options) + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index 083ec0230..486dc30ff 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -6,9 +6,8 @@ class Float < ActiveRecord::Type::Float include Castable - def initialize(options = {}) - super - @limit ||= 24 + def type + :float end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index a168e7c20..d09449f8a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -4,6 +4,11 @@ module SQLServer module Type class Varbinary < Binary + def initialize(options = {}) + super + @limit = 8000 if @limit.to_i == 0 + end + def type :varbinary end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 93bc57efb..2985ad66c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -207,8 +207,8 @@ def initialize_type_map(m) m.register_type 'money', SQLServer::Type::Money.new m.register_type 'smallmoney', SQLServer::Type::SmallMoney.new # Approximate Numerics - register_class_with_limit m, %r{\Afloat}, SQLServer::Type::Float - register_class_with_limit m, %r{\Areal}, SQLServer::Type::Real + 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 'datetime', SQLServer::Type::DateTime.new diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 741869ed7..9bc080f7f 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -333,11 +333,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'create floats when no limit supplied' do - assert_equal 'float(24)', connection.type_to_sql(:float) - end - - it 'create floats when limit is supplied' do - assert_equal 'float(27)', connection.type_to_sql(:float, 27) + assert_equal 'float', connection.type_to_sql(:float) end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index bbc2b807d..0e94951ee 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -241,9 +241,9 @@ def assert_obj_set_and_save(attribute, value) # Float limits are adjusted to 24 or 53 by the database as per http://msdn.microsoft.com/en-us/library/ms173773.aspx # Floats with a limit of <= 24 are reduced to reals by sqlserver on creation. - it 'float(53)' do + it 'float' do col = column('float') - col.sql_type.must_equal 'float(53)' + col.sql_type.must_equal 'float' col.null.must_equal true col.default.must_equal 123.00000001 obj.float.must_equal 123.00000001 @@ -252,7 +252,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be_instance_of Type::Float type.type.must_equal :float type.must_be :number? - type.limit.must_equal 53 + type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil obj.float = '214748.36461' @@ -261,21 +261,9 @@ def assert_obj_set_and_save(attribute, value) obj.reload.float.must_equal 214748.36461 end - it 'float(25)' do - col = column('float_25') - col.sql_type.must_equal 'float(53)' - col.null.must_equal true - col.default.must_equal 420.11 - col.default_function.must_equal nil - type = col.cast_type - type.must_be_instance_of Type::Float - type.type.must_equal :float - type.limit.must_equal 53 - end - - it 'real(24)' do + it 'real' do col = column('real') - col.sql_type.must_equal 'real(24)' + col.sql_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 @@ -284,7 +272,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be_instance_of Type::Real type.type.must_equal :real type.must_be :number? - type.limit.must_equal 24 + type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil obj.real = '214748.36461' diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index f947f0767..3d8d606bf 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -21,9 +21,8 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2' assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2' # Approximate Numerics - assert_line :float, type: 'float', limit: '53', precision: nil, scale: nil, default: '123.00000001' - assert_line :float_25, type: 'float', limit: '53', precision: nil, scale: nil, default: '420.11' - assert_line :real, type: 'real', limit: '24', precision: nil, scale: nil, default: %r{123.4[45]} + assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' + assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} # Date and Time assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" @@ -54,7 +53,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase 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 'real(24)' + 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_col'].sql_type.must_equal 'datetime' @@ -66,7 +65,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil - assert_line :float_col, type: 'real', limit: '24', precision: nil, scale: nil, default: nil + assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil @@ -74,6 +73,27 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + # Our type methods. + columns['real_col'].sql_type.must_equal 'real' + columns['money_col'].sql_type.must_equal 'money' + columns['smallmoney_col'].sql_type.must_equal 'smallmoney' + columns['char_col'].sql_type.must_equal 'char(1)' + columns['varchar_col'].sql_type.must_equal 'varchar(8000)' + columns['text_basic_col'].sql_type.must_equal 'text' + columns['nchar_col'].sql_type.must_equal 'nchar(1)' + columns['ntext_col'].sql_type.must_equal 'ntext' + columns['binary_basic_col'].sql_type.must_equal 'binary(1)' + columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)' + assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil + assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil + assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil + assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil + assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil + assert_line :text_basic_col, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :nchar_col, type: 'nchar', limit: '1', precision: nil, scale: nil, default: nil + assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil + assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil end # Special Cases diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 9dfd69312..97faa2396 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -21,7 +21,6 @@ CREATE TABLE [sst_datatypes] ( [smallmoney] [smallmoney] NULL DEFAULT 4.20, -- Approximate Numerics [float] [float] NULL DEFAULT 123.00000001, - [float_25] [float](25) NULL DEFAULT 420.11, [real] [real] NULL DEFAULT 123.45, -- Date and Time [date] [date] NULL DEFAULT '0001-01-01', @@ -45,7 +44,6 @@ CREATE TABLE [sst_datatypes] ( [varbinary_max] [varbinary](max) NULL, ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - -- Date and Time (TODO) -- -------------------- -- [datetime2_7] [datetime2](7) NULL, diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index c77eee419..2a2651551 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -18,10 +18,17 @@ t.time :time_col t.date :date_col t.binary :binary_col - - - # Our type methods. Make sure we can reverse schema dump for [sst_datatypes] table. - # ... + # Our type methods. + t.real :real_col + t.money :money_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.varbinary :varbinary_col end # Edge Cases From aedc3e266d41fc559596a435e9f25793d8fd6d95 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 11:28:33 -0500 Subject: [PATCH 0240/1412] Growing up. Only we say if a type is valid. Abstract is always true. --- .../connection_adapters/sqlserver/schema_statements.rb | 1 + lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 8e13b9bfe..ae0506f3e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -164,6 +164,7 @@ def initialize_native_database_types { primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', integer: { name: 'int', limit: 4 }, + bigint: { name: 'bigint' }, boolean: { name: 'bit' }, decimal: { name: 'decimal' }, money: { name: 'money' }, diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 2985ad66c..d895fdf31 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -57,6 +57,10 @@ def initialize(connection, logger, pool, config) # === Abstract Adapter ========================================== # + def valid_type?(type) + !native_database_types[type].nil? + end + def adapter_name ADAPTER_NAME end From 3baaffeefd188eb8ce6a62a9a4d73806eb22f7d4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 11:33:51 -0500 Subject: [PATCH 0241/1412] Remove #columns_for_distinct since abstract default is same. --- .../connection_adapters/sqlserver/schema_statements.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index ae0506f3e..e4e546a2f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -55,15 +55,6 @@ def new_column(name, default, cast_type, sql_type = nil, null = true, sqlserver_ SQLServerColumn.new name, default, cast_type, sql_type, null, sqlserver_options end - # like postgres, sqlserver requires the ORDER BY columns in the select list for distinct queries, and - # requires that the ORDER BY include the distinct column. Unfortunately, sqlserver does not support - # DISTINCT ON () like Posgres, or FIRST_VALUE() like Oracle (at least before SQL Server 2012). Because - # of these facts, we don't actually add any extra columns for distinct, but instead have to create - # a subquery with ROW_NUMBER() and DENSE_RANK() in our monkey-patches to Arel. - def columns_for_distinct(columns, _orders) #:nodoc: - columns - end - def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" rename_table_indexes(table_name, new_name) From 25ac8329ead1ac2da52e96c1f1577497eff8b38a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 11:51:52 -0500 Subject: [PATCH 0242/1412] Indent a bit and prepare for UUID type. --- .../connection_adapters/sqlserver_adapter.rb | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d895fdf31..c29ae2aa3 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -208,35 +208,35 @@ def initialize_type_map(m) SQLServer::Type::Decimal.new precision: precision, scale: scale 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.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 'datetime', SQLServer::Type::DateTime.new - m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new + m.register_type 'date', SQLServer::Type::Date.new + m.register_type 'datetime', SQLServer::Type::DateTime.new + m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new m.register_type %r{\Atime}i do |sql_type| scale = extract_scale(sql_type) precision = extract_precision(sql_type) 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 '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 '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 end def translate_exception(e, message) From 29b6cc7e10656c2a141272502b060e23da4c976e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 11:53:29 -0500 Subject: [PATCH 0243/1412] Indendt more for UUID prep. --- .../connection_adapters/sqlserver_adapter.rb | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c29ae2aa3..46e05590b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -192,51 +192,51 @@ def cs_equality_operator def initialize_type_map(m) 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 + 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) SQLServer::Type::Decimal.new precision: precision, scale: scale 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 'datetime', SQLServer::Type::DateTime.new - m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new + m.register_type 'date', SQLServer::Type::Date.new + m.register_type 'datetime', SQLServer::Type::DateTime.new + m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new m.register_type %r{\Atime}i do |sql_type| scale = extract_scale(sql_type) precision = extract_precision(sql_type) 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 '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 '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 end def translate_exception(e, message) From 54254f95c9dd4a335cdb0aa7292ce53dc4ae825d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 12:12:17 -0500 Subject: [PATCH 0244/1412] Prepare whitespace for UUID. --- test/cases/schema_dumper_test_sqlserver.rb | 56 +++++++++++----------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 3d8d606bf..985ca0ec3 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -10,39 +10,39 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'sst_datatypes' do generate_schema_for_table 'sst_datatypes' # Exact Numerics - assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42' - assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42' - assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42' - assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42' - assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true' - assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01' - assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0' - assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01' - assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2' - assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2' + assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42' + assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42' + assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42' + assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42' + assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true' + assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01' + assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0' + assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01' + assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2' + assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2' # Approximate Numerics - assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' - assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} + assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' + assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} # Date and Time - assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" - assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" - assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" - assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil - assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" + assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" + assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" + assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil + assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: nil # Character Strings - assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" - assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" - assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\"" - assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\"" + assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" + assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" + assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\"" + assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\"" # Unicode Character Strings - assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\"" - assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\"" - assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\"" - assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\"" + assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\"" + assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\"" + assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\"" + assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\"" # Binary Strings - assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil - assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil - assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil + assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil + assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil end it 'sst_datatypes_migration' do From ab34f08c56e766bbe1d16a7c58a28290d8e5dc4a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 13:20:05 -0500 Subject: [PATCH 0245/1412] UUID now passing with real type. --- .../sqlserver/schema_creation.rb | 3 +- .../sqlserver/table_definition.rb | 2 +- .../connection_adapters/sqlserver/type.rb | 2 + .../sqlserver/type/uuid.rb | 23 +++ .../connection_adapters/sqlserver_adapter.rb | 10 +- test/cases/column_test_sqlserver.rb | 26 ++++ test/cases/schema_dumper_test_sqlserver.rb | 4 + test/cases/specific_schema_test_sqlserver.rb | 38 +---- test/cases/uuid_test_sqlserver.rb | 138 ++++-------------- test/models/sqlserver/edge_schema.rb | 10 -- test/models/sqlserver/uuid.rb | 3 + test/schema/datatypes/2012.sql | 2 + test/schema/sqlserver_specific_schema.rb | 15 +- 13 files changed, 105 insertions(+), 171 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/uuid.rb create mode 100644 test/models/sqlserver/uuid.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 66da82353..6adfcf55d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -16,9 +16,8 @@ def visit_ColumnDefinition(o) def add_column_options!(sql, options) column = options.fetch(:column) { return super } - if [:uniqueidentifier, :uuid].include?(column.type) && options[:default] =~ /\(\)/ + if (column.type == :uuid || column.type == :uniqueidentifier) && options[:default] =~ /\(\)/ sql << " DEFAULT #{options.delete(:default)}" - super else super end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 1c546b691..87e53751f 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -55,7 +55,7 @@ def varbinary(name, options = {}) end def uuid(name, options = {}) - column(name, 'uniqueidentifier', options) + column(name, :uniqueidentifier, options) end end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 18b63a564..380f4aab3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -35,3 +35,5 @@ require 'active_record/connection_adapters/sqlserver/type/binary.rb' require 'active_record/connection_adapters/sqlserver/type/varbinary.rb' require 'active_record/connection_adapters/sqlserver/type/varbinary_max.rb' +# Other Data Types +require 'active_record/connection_adapters/sqlserver/type/uuid.rb' diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb new file mode 100644 index 000000000..5edfb7b27 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -0,0 +1,23 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Uuid < String + + ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x + + alias_method :type_cast_for_database, :type_cast_from_database + + def type + :uuid + end + + def type_cast(value) + value.to_s[ACCEPTABLE_UUID, 0] + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 46e05590b..76b14ac5d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -61,6 +61,10 @@ def valid_type?(type) !native_database_types[type].nil? end + def schema_creation + SQLServer::SchemaCreation.new self + end + def adapter_name ADAPTER_NAME end @@ -151,10 +155,6 @@ def primary_key(table_name) identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) end - def schema_creation - SQLServer::SchemaCreation.new self - end - # === SQLServer Specific (DB Reflection) ======================== # def sqlserver? @@ -237,6 +237,8 @@ def initialize_type_map(m) 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 end def translate_exception(e, message) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 0e94951ee..0c230822d 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -644,6 +644,32 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :varbinary_max, binary_data end + # Other Data Types + + it 'uniqueidentifier' do + col = column('uniqueidentifier') + col.sql_type.must_equal 'uniqueidentifier' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal 'newid()' + type = col.cast_type + type.must_be_instance_of Type::Uuid + type.type.must_equal :uuid + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal nil + type.scale.must_equal nil + # Basic set and save. + obj.uniqueidentifier = "this will not qualify as valid" + obj.uniqueidentifier.must_equal nil + obj.save! ; obj.reload + obj.uniqueidentifier.must_match 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.uniqueidentifier.must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" + end + end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 985ca0ec3..a44adf86c 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -43,6 +43,8 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + # Other Data Types + assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil end it 'sst_datatypes_migration' do @@ -84,6 +86,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['ntext_col'].sql_type.must_equal 'ntext' columns['binary_basic_col'].sql_type.must_equal 'binary(1)' columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)' + columns['uuid_col'].sql_type.must_equal 'uniqueidentifier' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil @@ -94,6 +97,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil + assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil end # Special Cases diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 7618df389..3cb00c0dd 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -99,42 +99,12 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description) end - # With uniqueidentifier column - - let(:newid) { ActiveRecord::Base.connection.newid_function } + # For uniqueidentifier model helpers it 'returns a new id via connection newid_function' do - assert_guid newid - end - - it 'allow a simple insert and read of a column without a default function' do - obj = SSTestEdgeSchema.create! guid: newid - assert_equal newid, SSTestEdgeSchema.find(obj.id).guid - end - - it 'record the default function name in the column definition but still show a nil real default, will use one day for insert/update' do - newid_column = SSTestEdgeSchema.columns_hash['guid_newid'] - assert newid_column.default_function.present? - assert_nil newid_column.default - assert_equal 'newid()', newid_column.default_function - newseqid_column = SSTestEdgeSchema.columns_hash['guid_newseqid'] - assert newseqid_column.default_function.present? - assert_nil newseqid_column.default - assert_equal 'newsequentialid()', newseqid_column.default_function - end - - it 'use model callback to set get a new guid' do - obj = SSTestEdgeSchema.new - obj.new_id_setting = true - obj.save! - assert_guid obj.guid_newid - end - - - protected - - def assert_guid(guid) - assert_match %r|\w{8}-\w{4}-\w{4}-\w{4}-\w{12}|, guid + acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID + db_uuid = ActiveRecord::Base.connection.newid_function + db_uuid.must_match(acceptable_uuid) end end diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index a340a34b2..cb2aa4930 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,127 +1,41 @@ require 'cases/helper_sqlserver' -class SQLServerUUIDTest < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'sql_server_uuids' - end - - def setup - @connection = ActiveRecord::Base.connection - - @connection.reconnect! - - @connection.transaction do - @connection.create_table('sql_server_uuids', id: :uuid, default: 'NEWSEQUENTIALID()') do |t| - t.string 'name' - t.uuid 'other_uuid', default: 'NEWID()' - end - end - end - - def teardown - @connection.execute "IF OBJECT_ID('sql_server_uuids', 'U') IS NOT NULL DROP TABLE sql_server_uuids" - end - - def test_id_is_uuid - assert_equal :uuid, UUID.columns_hash['id'].type - assert UUID.primary_key - end +class SQLServerUuidTest < ActiveRecord::TestCase - def test_id_has_a_default - u = UUID.create - assert_not_nil u.id - end + let(:acceptable_uuid) { ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID } - def test_auto_create_uuid - u = UUID.create - u.reload - assert_not_nil u.other_uuid + it 'has a uuid primary key' do + SSTestUuid.columns_hash['id'].type.must_equal :uuid + assert SSTestUuid.primary_key end - def test_pk_and_sequence_for_uuid_primary_key - pk, seq = @connection.pk_and_sequence_for('sql_server_uuids') - assert_equal 'id', pk - assert_equal nil, seq + it 'can create with a new pk' do + obj = SSTestUuid.create! + obj.id.must_be :present? + obj.id.must_match acceptable_uuid end - def primary_key_for_uuid_primary_key - assert_equal 'id', @connection.primary_key('sql_server_uuids') + it 'can create other uuid column on reload' do + obj = SSTestUuid.create! + obj.reload + obj.other_uuid.must_match acceptable_uuid end - def test_change_column_default - @connection.add_column :sql_server_uuids, :thingy, :uuid, null: false, default: "NEWSEQUENTIALID()" - UUID.reset_column_information - column = UUID.columns.find { |c| c.name == 'thingy' } - assert_equal "newsequentialid()", column.default_function - - @connection.change_column :sql_server_uuids, :thingy, :uuid, null: false, default: "NEWID()" - - UUID.reset_column_information - column = UUID.columns.find { |c| c.name == 'thingy' } - assert_equal "newid()", column.default_function + it 'can find uuid pk via connection' do + connection.primary_key(SSTestUuid.table_name).must_equal 'id' end -end -class SQLServerUUIDTestNilDefault < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'sql_server_uuids' + it 'changing column default' do + table_name = SSTestUuid.table_name + connection.add_column table_name, :thingy, :uuid, null: false, default: "NEWSEQUENTIALID()" + SSTestUuid.reset_column_information + column = SSTestUuid.columns_hash['thingy'] + column.default_function.must_equal "newsequentialid()" + # Now to a different function. + connection.change_column table_name, :thingy, :uuid, null: false, default: "NEWID()" + SSTestUuid.reset_column_information + column = SSTestUuid.columns_hash['thingy'] + column.default_function.must_equal "newid()" end - def setup - @connection = ActiveRecord::Base.connection - - @connection.reconnect! - - @connection.transaction do - @connection.create_table('sql_server_uuids', id: false) do |t| - t.primary_key :id, :uuid, default: nil - t.string 'name' - end - end - end - - def teardown - @connection.execute "IF OBJECT_ID('sql_server_uuids', 'U') IS NOT NULL DROP TABLE sql_server_uuids" - end - -end - -class SQLServerUUIDTestInverseOf < ActiveRecord::TestCase - class UuidPost < ActiveRecord::Base - self.table_name = 'sql_server_uuid_posts' - has_many :uuid_comments, inverse_of: :uuid_post - end - - class UuidComment < ActiveRecord::Base - self.table_name = 'sql_server_uuid_comments' - belongs_to :uuid_post - end - - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - @connection.transaction do - @connection.create_table('sql_server_uuid_posts', id: :uuid) do |t| - t.string 'title' - end - @connection.create_table('sql_server_uuid_comments', id: :uuid) do |t| - t.uuid :uuid_post_id, default: 'NEWID()' - t.string 'content' - end - end - end - - def teardown - @connection.transaction do - @connection.execute "IF OBJECT_ID('sql_server_uuid_comments', 'U') IS NOT NULL DROP TABLE sql_server_uuid_comments" - @connection.execute "IF OBJECT_ID('sql_server_uuid_posts', 'U') IS NOT NULL DROP TABLE sql_server_uuid_posts" - end - end - - def test_collection_association_with_uuid - post = UuidPost.create! - comment = post.uuid_comments.create! - assert post.uuid_comments.find(comment.id) - end end diff --git a/test/models/sqlserver/edge_schema.rb b/test/models/sqlserver/edge_schema.rb index d84145dae..4d14bf81d 100644 --- a/test/models/sqlserver/edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -2,10 +2,6 @@ class SSTestEdgeSchema < ActiveRecord::Base self.table_name = 'sst_edge_schemas' - attr_accessor :new_id_setting - - before_create :set_new_id - def with_spaces read_attribute :'with spaces' end @@ -14,10 +10,4 @@ def with_spaces=(value) write_attribute :'with spaces', value end - protected - - def set_new_id - self[:guid_newid] ||= self.class.connection.newid_function if new_id_setting - end - end diff --git a/test/models/sqlserver/uuid.rb b/test/models/sqlserver/uuid.rb new file mode 100644 index 000000000..f03a7c373 --- /dev/null +++ b/test/models/sqlserver/uuid.rb @@ -0,0 +1,3 @@ +class SSTestUuid < ActiveRecord::Base + self.table_name = 'sst_uuids' +end diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 97faa2396..098d6df9d 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -42,6 +42,8 @@ CREATE TABLE [sst_datatypes] ( [binary_49] [binary](49) NULL, [varbinary_49] [varbinary](49) NULL, [varbinary_max] [varbinary](max) NULL, + -- Other Data Types + [uniqueidentifier] [uniqueidentifier] NULL DEFAULT NEWID(), ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -- Date and Time (TODO) diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 2a2651551..54d002487 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -29,10 +29,17 @@ t.ntext :ntext_col t.binary_basic :binary_basic_col t.varbinary :varbinary_col + t.uuid :uuid_col end # Edge Cases + 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 + end + create_table 'sst_my$strange_table', force: true do |t| t.string :name end @@ -65,7 +72,6 @@ create_table :sst_edge_schemas, force: true do |t| t.string :description - t.column :guid, :uniqueidentifier t.column 'crazy]]quote', :string t.column 'with spaces', :string end @@ -135,13 +141,6 @@ - - create_table :defaults, force: true do |t| - t.column :positive_integer, :integer, default: 1 - t.column :negative_integer, :integer, default: -1 - t.column :decimal_number, :decimal, precision: 3, scale: 2, default: 2.78 - end - # http://blogs.msdn.com/b/craigfr/archive/2008/03/19/ranking-functions-row-number.aspx execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'order_row_number') DROP TABLE order_row_number" execute <<-ORDERROWNUMBERSQL From 04f22a1ec31a40898b87eef7480eed72e391e2b8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 13:43:31 -0500 Subject: [PATCH 0246/1412] More cleanup. --- .../sqlserver/schema_creation.rb | 28 -------------- test/cases/specific_schema_test_sqlserver.rb | 6 +++ test/models/sql_server_order_row_number.rb | 3 -- test/models/sql_server_string.rb | 2 - test/models/sql_server_tinyint_pk.rb | 3 -- test/models/sql_server_unicode.rb | 2 - test/models/sqlserver/tinyint_pk.rb | 3 ++ test/models/table_with_real_column.rb | 2 - test/schema/sqlserver_specific_schema.rb | 37 ++++--------------- 9 files changed, 17 insertions(+), 69 deletions(-) delete mode 100644 test/models/sql_server_order_row_number.rb delete mode 100644 test/models/sql_server_string.rb delete mode 100644 test/models/sql_server_tinyint_pk.rb delete mode 100644 test/models/sql_server_unicode.rb create mode 100644 test/models/sqlserver/tinyint_pk.rb delete mode 100644 test/models/table_with_real_column.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 6adfcf55d..6f5d1ea4d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -23,34 +23,6 @@ def add_column_options!(sql, options) end end - def visit_TableDefinition(o) - quoted_name = "#{quote_table_name((o.temporary ? '#' : '') + o.name.to_s)} " - - if o.as - if o.as.is_a?(ActiveRecord::Relation) - select = o.as.to_sql - elsif o.as.is_a?(String) - select = o.as - else - raise 'Only able to generate a table from a SELECT statement passed as a String or ActiveRecord::Relation' - end - - create_sql = 'SELECT * INTO ' - create_sql << quoted_name - create_sql << 'FROM (' - create_sql << select - create_sql << ') AS __sq' - - else - create_sql = "CREATE TABLE " - create_sql << quoted_name - create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " - create_sql << "#{o.options}" - end - - create_sql - end - end end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 3cb00c0dd..f9f3baa87 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -9,6 +9,12 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase SSTestDollarTableName.limit(20).offset(1) end + it 'models can use tinyint pk tables' do + obj = SSTestTinyintPk.create! name: '1' + obj.id.is_a? Fixnum + SSTestTinyintPk.find(obj.id).must_equal obj + end + it 'be able to complex count tables with no primary key' do SSTestNoPkData.delete_all 10.times { |n| SSTestNoPkData.create! name: "Test#{n}" } diff --git a/test/models/sql_server_order_row_number.rb b/test/models/sql_server_order_row_number.rb deleted file mode 100644 index c7f7c4cee..000000000 --- a/test/models/sql_server_order_row_number.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerOrderRowNumber < ActiveRecord::Base - self.table_name = 'order_row_number' -end \ No newline at end of file diff --git a/test/models/sql_server_string.rb b/test/models/sql_server_string.rb deleted file mode 100644 index b206ed7de..000000000 --- a/test/models/sql_server_string.rb +++ /dev/null @@ -1,2 +0,0 @@ -class SqlServerString < ActiveRecord::Base -end \ No newline at end of file diff --git a/test/models/sql_server_tinyint_pk.rb b/test/models/sql_server_tinyint_pk.rb deleted file mode 100644 index 4fbcb6b4d..000000000 --- a/test/models/sql_server_tinyint_pk.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerTinyintPk < ActiveRecord::Base - self.table_name = 'tinyint_pk_table' -end \ No newline at end of file diff --git a/test/models/sql_server_unicode.rb b/test/models/sql_server_unicode.rb deleted file mode 100644 index c712f3ffa..000000000 --- a/test/models/sql_server_unicode.rb +++ /dev/null @@ -1,2 +0,0 @@ -class SqlServerUnicode < ActiveRecord::Base -end \ No newline at end of file diff --git a/test/models/sqlserver/tinyint_pk.rb b/test/models/sqlserver/tinyint_pk.rb new file mode 100644 index 000000000..121a321c3 --- /dev/null +++ b/test/models/sqlserver/tinyint_pk.rb @@ -0,0 +1,3 @@ +class SSTestTinyintPk < ActiveRecord::Base + self.table_name = 'sst_tinyint_pk' +end diff --git a/test/models/table_with_real_column.rb b/test/models/table_with_real_column.rb deleted file mode 100644 index ceddb5047..000000000 --- a/test/models/table_with_real_column.rb +++ /dev/null @@ -1,2 +0,0 @@ -class TableWithRealColumn < ActiveRecord::Base -end \ No newline at end of file diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 54d002487..448b6323e 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -75,8 +75,6 @@ t.column 'crazy]]quote', :string t.column 'with spaces', :string end - execute %|ALTER TABLE [sst_edge_schemas] ADD [guid_newid] uniqueidentifier DEFAULT NEWID();| - execute %|ALTER TABLE [sst_edge_schemas] ADD [guid_newseqid] uniqueidentifier DEFAULT NEWSEQUENTIALID();| execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_data') DROP TABLE sst_natural_pk_data" execute <<-NATURALPKTABLESQL @@ -98,6 +96,14 @@ ) NATURALPKINTTABLESQL + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_tinyint_pk') DROP TABLE sst_tinyint_pk" + execute <<-TINYITPKTABLE + CREATE TABLE sst_tinyint_pk( + id tinyint IDENTITY NOT NULL PRIMARY KEY, + name nvarchar(255) + ) + TINYITPKTABLE + # Constraints create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } @@ -141,33 +147,6 @@ - # http://blogs.msdn.com/b/craigfr/archive/2008/03/19/ranking-functions-row-number.aspx - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'order_row_number') DROP TABLE order_row_number" - execute <<-ORDERROWNUMBERSQL - CREATE TABLE [order_row_number] (id int IDENTITY, a int, b int, c int) - CREATE UNIQUE CLUSTERED INDEX [idx_order_row_number_id] ON [order_row_number] ([id]) - INSERT [order_row_number] VALUES (0, 1, 8) - INSERT [order_row_number] VALUES (0, 3, 6) - INSERT [order_row_number] VALUES (0, 5, 4) - INSERT [order_row_number] VALUES (0, 7, 2) - INSERT [order_row_number] VALUES (0, 9, 0) - INSERT [order_row_number] VALUES (1, 0, 9) - INSERT [order_row_number] VALUES (1, 2, 7) - INSERT [order_row_number] VALUES (1, 4, 5) - INSERT [order_row_number] VALUES (1, 6, 3) - INSERT [order_row_number] VALUES (1, 8, 1) - ORDERROWNUMBERSQL - - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'tinyint_pk_table') DROP TABLE tinyint_pk_table" - execute <<-TINYITPKTABLE - CREATE TABLE tinyint_pk_table( - id tinyint NOT NULL PRIMARY KEY, - name nvarchar(255) - ) - TINYITPKTABLE - - - From 4e5dbff8832a80e18565957efdc56932faae2ed4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 13:59:08 -0500 Subject: [PATCH 0247/1412] Schema specific test done. --- test/cases/schema_test_sqlserver.rb | 49 ++++++++----------- .../sql_server_natural_pk_data_schema.rb | 3 -- test/schema/sqlserver_specific_schema.rb | 31 +++--------- 3 files changed, 28 insertions(+), 55 deletions(-) delete mode 100644 test/models/sql_server_natural_pk_data_schema.rb diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index a62c8b108..bfc32f7c5 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,53 +1,46 @@ require 'cases/helper_sqlserver' -require 'models/sqlserver/sql_server_natural_pk_data' -require 'models/sqlserver/sql_server_natural_pk_data_schema' class SchemaTestSQLServer < ActiveRecord::TestCase - setup do - @connection = ActiveRecord::Base.connection - end - - context 'When table is dbo schema' do + describe 'When table is dbo schema' do - should 'find primary key for tables with odd schema' do - assert_equal 'legacy_id', @connection.primary_key('natural_pk_data') - assert SSTestNaturalPkData.columns_hash['legacy_id'].primary + it 'find primary key for tables with odd schema' do + connection.primary_key('natural_pk_data').must_equal 'legacy_id' end end - context 'When table is in non-dbo schema' do + describe 'When table is in non-dbo schema' do - should 'work with #table_exists?' do - assert @connection.table_exists?('test.sql_server_schema_natural_id') + it 'work with table exists' do + assert connection.table_exists?('test.sst_schema_natural_id') + assert connection.table_exists?('[test].[sst_schema_natural_id]') end - should 'find primary key for tables with odd schema' do - assert_equal 'legacy_id', @connection.primary_key('test.sql_server_schema_natural_id') - assert SqlServerNaturalPkDataSchema.columns_hash['legacy_id'].primary + it 'find primary key for tables with odd schema' do + connection.primary_key('test.sst_schema_natural_id').must_equal 'legacy_id' end - should "have only one identity column" do - columns = @connection.columns("test.sql_server_schema_identity") + it "have only one identity column" do + columns = connection.columns("test.sst_schema_identity") assert_equal 2, columns.size - assert_equal 1, columns.select{ |c| c.primary }.size + assert_equal 1, columns.select{ |c| c.is_identity? }.size end - should "read only column properties for table in specific schema" do - test_columns = @connection.columns("test.sql_server_schema_columns") - dbo_columns = @connection.columns("dbo.sql_server_schema_columns") - columns = @connection.columns("sql_server_schema_columns") # This returns table from dbo schema + it "read only column properties for table in specific schema" do + test_columns = connection.columns("test.sst_schema_columns") + dbo_columns = connection.columns("dbo.sst_schema_columns") + columns = connection.columns("sst_schema_columns") # This returns table from dbo schema 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.primary }.size - assert_equal 1, dbo_columns.select{ |c| c.primary }.size - assert_equal 1, columns.select{ |c| c.primary }.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 end - should "return correct varchar and nvarchar column limit (length) when table is in non dbo schema" do - columns = @connection.columns("test.sql_server_schema_columns") + it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do + columns = connection.columns("test.sst_schema_columns") assert_equal 255, columns.find {|c| c.name == 'name'}.limit assert_equal 1000, columns.find {|c| c.name == 'description'}.limit assert_equal 255, columns.find {|c| c.name == 'n_name'}.limit diff --git a/test/models/sql_server_natural_pk_data_schema.rb b/test/models/sql_server_natural_pk_data_schema.rb deleted file mode 100644 index 0ff4cbb20..000000000 --- a/test/models/sql_server_natural_pk_data_schema.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SqlServerNaturalPkDataSchema < ActiveRecord::Base - self.table_name = 'test.sql_server_schema_natural_id' -end \ No newline at end of file diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 448b6323e..75a120426 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -139,33 +139,16 @@ FROM sst_string_defaults STRINGDEFAULTSBIGVIEW - - - - - - - - - - - - - - - - - # Another schema. - create_table :sql_server_schema_columns, force: true do |t| + create_table :sst_schema_columns, force: true do |t| t.column :field1 , :integer end execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test') EXEC sp_executesql N'CREATE SCHEMA test'" - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_columns' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_columns" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_columns' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_columns" execute <<-SIMILIARTABLEINOTHERSCHEMA - CREATE TABLE test.sql_server_schema_columns( + CREATE TABLE test.sst_schema_columns( id int IDENTITY NOT NULL primary key, filed_1 int, field_2 int, @@ -176,17 +159,17 @@ ) SIMILIARTABLEINOTHERSCHEMA - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_identity' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_identity" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_identity' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_identity" execute <<-SIMILIARTABLEINOTHERSCHEMA - CREATE TABLE test.sql_server_schema_identity( + CREATE TABLE test.sst_schema_identity( id int IDENTITY NOT NULL primary key, filed_1 int ) SIMILIARTABLEINOTHERSCHEMA - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sql_server_schema_natural_id' and TABLE_SCHEMA = 'test') DROP TABLE test.sql_server_schema_natural_id" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_natural_id' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_natural_id" execute <<-NATURALPKTABLESQLINOTHERSCHEMA - CREATE TABLE test.sql_server_schema_natural_id( + CREATE TABLE test.sst_schema_natural_id( parent_id int, name nvarchar(255), description nvarchar(1000), From 483210edcf1bd50a573ddca6ac1c2abd19e8d04f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 14:01:16 -0500 Subject: [PATCH 0248/1412] Remove sold dead weight. --- test/cases/scratch_test_sqlserver.rb | 12 ------------ test/cases/session_test_sqlserver.rb | 18 ------------------ test/cases/sqlserver_test_case.rb | 17 ----------------- 3 files changed, 47 deletions(-) delete mode 100644 test/cases/scratch_test_sqlserver.rb delete mode 100644 test/cases/session_test_sqlserver.rb delete mode 100644 test/cases/sqlserver_test_case.rb diff --git a/test/cases/scratch_test_sqlserver.rb b/test/cases/scratch_test_sqlserver.rb deleted file mode 100644 index e140b9696..000000000 --- a/test/cases/scratch_test_sqlserver.rb +++ /dev/null @@ -1,12 +0,0 @@ -require 'cases/helper_sqlserver' - -class ScratchTestSQLServer < ActiveRecord::TestCase - - # TODO: put a real test here - should 'pass' do - assert true - end - - -end - diff --git a/test/cases/session_test_sqlserver.rb b/test/cases/session_test_sqlserver.rb deleted file mode 100644 index f942ac6fd..000000000 --- a/test/cases/session_test_sqlserver.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'cases/helper_sqlserver' -require 'action_dispatch' - -module ActiveRecord - class SessionStore - class SessionTest < ActiveRecord::TestCase - - setup :reset_column_information_for_each_test - - protected - - def reset_column_information_for_each_test - Session.reset_column_information - end - - end - end -end diff --git a/test/cases/sqlserver_test_case.rb b/test/cases/sqlserver_test_case.rb deleted file mode 100644 index 02d6f1c24..000000000 --- a/test/cases/sqlserver_test_case.rb +++ /dev/null @@ -1,17 +0,0 @@ -require 'cases/test_case.rb' - -# TODO: I'm struggling to figure out how to unsubscribe from only one 'sql.active_record' -# This is a temporary hack until we can just get the sqlserver_ignored regex in rails -ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').each do |listener| - if listener.inspect =~ /ActiveRecord::SQLCounter/ - ActiveSupport::Notifications.unsubscribe(listener) - end -end - -module ActiveRecord - class SQLCounter - sqlserver_ignored = [%r|SELECT SCOPE_IDENTITY|, %r{INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)},%r|SELECT @@version|, %r|SELECT @@TRANCOUNT|, %r{(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION}] - ignored_sql.concat sqlserver_ignored - end - ActiveSupport::Notifications.subscribe('sql.active_record', SQLCounter.new) -end \ No newline at end of file From 82ce211ab15e659c84a027fabc1fd08d8b33e54b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 14:04:22 -0500 Subject: [PATCH 0249/1412] Transaction test with coerced to folder. --- .../coerced/transaction_test_sqlserver.rb | 18 +++++++ test/cases/transaction_test_sqlserver.rb | 50 ++++++------------- 2 files changed, 33 insertions(+), 35 deletions(-) create mode 100644 test/cases/coerced/transaction_test_sqlserver.rb diff --git a/test/cases/coerced/transaction_test_sqlserver.rb b/test/cases/coerced/transaction_test_sqlserver.rb new file mode 100644 index 000000000..aba4868ac --- /dev/null +++ b/test/cases/coerced/transaction_test_sqlserver.rb @@ -0,0 +1,18 @@ +require 'cases/helper_sqlserver' + +class TransactionTest < ActiveRecord::TestCase + include ARTest::SQLServer::CoercedTest + + COERCED_TESTS = [:test_releasing_named_savepoints] + + def test_coerced_releasing_named_savepoints + Topic.transaction do + Topic.connection.create_savepoint("another") + Topic.connection.release_savepoint("another") + + # The origin rails test tries to re-release the savepoint, but + # since sqlserver doesn't have the concept of releasing, it doesn't + # fail, so we just omit that part here + end + end +end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 3ace7ae18..1ab662ff5 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -6,34 +6,31 @@ class TransactionTestSQLServer < ActiveRecord::TestCase self.use_transactional_fixtures = false - setup :delete_ships + before { delete_ships } - context 'Testing transaction basics' do + it 'allow ActiveRecord::Rollback to work in 1 transaction block' do + Ship.transaction do + Ship.create! name: 'Black Pearl' + raise ActiveRecord::Rollback + end + assert_no_ships + end - should 'allow ActiveRecord::Rollback to work in 1 transaction block' do + it 'allow nested transactions to totally rollback' do + begin Ship.transaction do Ship.create! name: 'Black Pearl' - raise ActiveRecord::Rollback - end - assert_no_ships - end - - should 'allow nested transactions to totally rollback' do - begin 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 => e - assert_no_ships end + rescue Exception => e + assert_no_ships end - end + protected def delete_ships @@ -45,20 +42,3 @@ def assert_no_ships end end - -class TransactionTest < ActiveRecord::TestCase - include ARTest::SQLServer::CoercedTest - - COERCED_TESTS = [:test_releasing_named_savepoints] - - def test_coerced_releasing_named_savepoints - Topic.transaction do - Topic.connection.create_savepoint("another") - Topic.connection.release_savepoint("another") - - # The origin rails test tries to re-release the savepoint, but - # since sqlserver doesn't have the concept of releasing, it doesn't - # fail, so we just omit that part here - end - end -end From 5a0ffa6f8060a48a21a63f549386b160ad526b2f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 14:15:47 -0500 Subject: [PATCH 0250/1412] Move test for quoted [schem].table into other test. --- test/cases/specific_schema_test_sqlserver.rb | 2 ++ test/cases/table_name_test_sqlserver.rb | 38 -------------------- test/models/sqlserver/quoted_table.rb | 6 +++- 3 files changed, 7 insertions(+), 39 deletions(-) delete mode 100644 test/cases/table_name_test_sqlserver.rb diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index f9f3baa87..25234b335 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -24,6 +24,8 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'quote table names properly even when they are views' do obj = SSTestQuotedTable.create! assert_nothing_raised { assert SSTestQuotedTable.first } + obj = SSTestQuotedTableUser.create! + assert_nothing_raised { assert SSTestQuotedTableUser.first } obj = SSTestQuotedView1.create! assert_nothing_raised { assert SSTestQuotedView1.first } obj = SSTestQuotedView2.create! diff --git a/test/cases/table_name_test_sqlserver.rb b/test/cases/table_name_test_sqlserver.rb deleted file mode 100644 index e945acfee..000000000 --- a/test/cases/table_name_test_sqlserver.rb +++ /dev/null @@ -1,38 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/order' - -class SqlServerRailsOrders < ActiveRecord::Base - self.table_name = 'rails.orders' -end - -class TableNameTestSQLServer < ActiveRecord::TestCase - - self.use_transactional_fixtures = false - - setup do - Order.table_name = '[orders]' - Order.reset_column_information - end - - should 'load columns with escaped table name for model' do - assert_equal 4, Order.columns.length - end - - should 'not re-escape table name if it is escaped already for SQL queries' do - assert_sql(/SELECT \[orders\]\.\* FROM \[orders\]/) { Order.all.load } - end - - context 'Table scoped to user.table_name' do - - setup do - @klass = SqlServerRailsOrders - end - - should 'have no issue doing basic column reflection' do - assert_nothing_raised() { @klass.columns } - end - - end - - -end diff --git a/test/models/sqlserver/quoted_table.rb b/test/models/sqlserver/quoted_table.rb index 87b0fba30..ce9d283a8 100644 --- a/test/models/sqlserver/quoted_table.rb +++ b/test/models/sqlserver/quoted_table.rb @@ -1,3 +1,7 @@ class SSTestQuotedTable < ActiveRecord::Base - self.table_name = 'sst_quoted-table' + self.table_name = '[sst_quoted-table]' +end + +class SSTestQuotedTableUser < ActiveRecord::Base + self.table_name = '[dbo].[sst_quoted-table]' end From c7cd29356f9bc32735d61873d50cbc9fb13bc469 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 14:26:30 -0500 Subject: [PATCH 0251/1412] EXPLAIN it to them! --- test/cases/showplan_test_sqlserver.rb | 44 +++++++++++---------------- 1 file changed, 18 insertions(+), 26 deletions(-) diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 446fd92da..17ad877b3 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -5,47 +5,47 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase fixtures :cars - context 'Unprepare previously prepared SQL' do + describe 'Unprepare previously prepared SQL' do - should 'from simple statement' do + it 'from simple statement' do plan = Car.where(id: 1).explain - assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") - assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' + plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" + plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end - should 'from multiline statement' do + it 'from multiline statement' do plan = Car.where("\n id = 1 \n").explain - assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)") - assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' + plan.must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)" + plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end - should 'from prepared statement ...' do + it 'from prepared statement ...' do plan = Car.where(name: ',').limit(1).explain - assert plan.include?("SELECT TOP (1) [cars].* FROM [cars] WHERE [cars].[name] = N','") - assert plan.include?("TOP EXPRESSION"), 'make sure we do not showplan the sp_executesql' - assert plan.include?("Clustered Index Scan"), 'make sure we do not showplan the sp_executesql' + plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" + plan.must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' + plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' end end - context 'With SHOWPLAN_TEXT option' do + describe 'With SHOWPLAN_TEXT option' do - should 'use simple table printer' do + it 'use simple table printer' do with_showplan_option('SHOWPLAN_TEXT') do plan = Car.where(id: 1).explain - assert plan.starts_with?("EXPLAIN for: SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1") - assert plan.include?("Clustered Index Seek"), 'make sure we do not showplan the sp_executesql' + plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" + plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end end end - context 'With SHOWPLAN_XML option' do + describe 'With SHOWPLAN_XML option' do - should 'show formatted xml' do + it 'show formatted xml' do with_showplan_option('SHOWPLAN_XML') do plan = Car.where(id: 1).explain - assert plan.include?('ShowPlanXML') + plan.must_include 'ShowPlanXML' end end @@ -54,14 +54,6 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase private - def base - ActiveRecord::Base - end - - def connection - base.connection - end - def with_showplan_option(option) old_option = ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = option From 3952f40ed6ca07bf3565dd635e5d067ff09e2c11 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 15:43:08 -0500 Subject: [PATCH 0252/1412] Try like hell to find a tables PK. --- lib/arel/visitors/sqlserver.rb | 14 +++- .../coerced/associations_test_sqlserver.rb | 16 ----- .../attribute_methods_test_sqlserver.rb | 41 ----------- test/cases/coerced/base_test_sqlserver.rb | 24 ------- test/cases/coerced/batches_test_sqlserver.rb | 28 -------- .../belongs_to_associations_test_sqlserver.rb | 19 ----- .../coerced/bind_parameter_test_sqlserver.rb | 33 --------- .../coerced/calculations_test_sqlserver.rb | 59 --------------- .../connection_management_test_sqlserver.rb | 38 ---------- test/cases/coerced/eager_test_sqlserver.rb | 22 ------ test/cases/coerced/finder_test_sqlserver.rb | 54 -------------- ...ngs_to_many_associations_test_sqlserver.rb | 26 ------- .../coerced/inheritance_test_sqlserver.rb | 37 ---------- .../invalid_connection_test_sqlserver.rb | 10 --- .../cases/coerced/migration_test_sqlserver.rb | 13 ---- .../coerced/persistence_test_sqlserver.rb | 71 ------------------- .../predicate_builder_test_sqlserver.rb | 20 ------ .../coerced/query_cache_test_sqlserver.rb | 22 ------ .../cases/coerced/relations_test_sqlserver.rb | 37 ---------- test/cases/coerced/resolver_test_sqlserver.rb | 46 ------------ .../coerced/schema_dumper_test_sqlserver.rb | 32 --------- .../coerced/transaction_test_sqlserver.rb | 18 ----- .../uniqueness_validation_test_sqlserver.rb | 42 ----------- .../coerced/where_chain_test_sqlserver.rb | 19 ----- 24 files changed, 13 insertions(+), 728 deletions(-) delete mode 100644 test/cases/coerced/associations_test_sqlserver.rb delete mode 100644 test/cases/coerced/attribute_methods_test_sqlserver.rb delete mode 100644 test/cases/coerced/base_test_sqlserver.rb delete mode 100644 test/cases/coerced/batches_test_sqlserver.rb delete mode 100644 test/cases/coerced/belongs_to_associations_test_sqlserver.rb delete mode 100644 test/cases/coerced/bind_parameter_test_sqlserver.rb delete mode 100644 test/cases/coerced/calculations_test_sqlserver.rb delete mode 100644 test/cases/coerced/connection_management_test_sqlserver.rb delete mode 100644 test/cases/coerced/eager_test_sqlserver.rb delete mode 100644 test/cases/coerced/finder_test_sqlserver.rb delete mode 100644 test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb delete mode 100644 test/cases/coerced/inheritance_test_sqlserver.rb delete mode 100644 test/cases/coerced/invalid_connection_test_sqlserver.rb delete mode 100644 test/cases/coerced/migration_test_sqlserver.rb delete mode 100644 test/cases/coerced/persistence_test_sqlserver.rb delete mode 100644 test/cases/coerced/predicate_builder_test_sqlserver.rb delete mode 100644 test/cases/coerced/query_cache_test_sqlserver.rb delete mode 100644 test/cases/coerced/relations_test_sqlserver.rb delete mode 100644 test/cases/coerced/resolver_test_sqlserver.rb delete mode 100644 test/cases/coerced/schema_dumper_test_sqlserver.rb delete mode 100644 test/cases/coerced/transaction_test_sqlserver.rb delete mode 100644 test/cases/coerced/uniqueness_validation_test_sqlserver.rb delete mode 100644 test/cases/coerced/where_chain_test_sqlserver.rb diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 1e79420b9..0919d1565 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -83,7 +83,7 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} def visit_Orders_And_Let_Fetch_Happen o, collector if (o.limit || o.offset) && o.orders.empty? table = table_From_Statement o - column = table.primary_key || table.columns.first + column = primary_Key_From_Table(table) o.orders = [column.asc] end unless o.orders.empty? @@ -122,6 +122,18 @@ def table_From_Statement o end end + def primary_Key_From_Table t + return t.primary_key if t.primary_key + if engine_pk = t.engine.primary_key + pk = t.engine.arel_table[engine_pk] + return pk if pk + end + pk = t.engine.connection.schema_cache.primary_keys(t.engine.table_name) + return pk if pk + column_name = t.engine.columns.first.try(:name) + column_name ? t[column_name] : nil + end + end end end diff --git a/test/cases/coerced/associations_test_sqlserver.rb b/test/cases/coerced/associations_test_sqlserver.rb deleted file mode 100644 index c2e1e9257..000000000 --- a/test/cases/coerced/associations_test_sqlserver.rb +++ /dev/null @@ -1,16 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/owner' - -class HasManyThroughAssociationsTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_has_many_through_obeys_order_on_through_association] - # Rails does not do a case-insensive comparison - # Until that patch is made to rails we are preventing this test from running in this gem. - - include ARTest::SQLServer::CoercedTest - def test_coerced_has_many_through_obeys_order_on_through_association - owner = owners(:blackbeard) - # assert owner.toys.to_sql.include?("pets.name desc") # What's currently in rails - assert owner.toys.to_sql.downcase.include?("pets.name desc") - assert_equal ["parrot", "bulbul"], owner.toys.map { |r| r.pet.name } - end -end diff --git a/test/cases/coerced/attribute_methods_test_sqlserver.rb b/test/cases/coerced/attribute_methods_test_sqlserver.rb deleted file mode 100644 index 2f38c77f9..000000000 --- a/test/cases/coerced/attribute_methods_test_sqlserver.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/developer' -require 'models/topic' -require 'models/sqlserver/topic' - -class AttributeMethodsTestSQLServer < ActiveRecord::TestCase -end - -class AttributeMethodsTest < ActiveRecord::TestCase - - COERCED_TESTS = [ - :test_read_attributes_before_type_cast_on_datetime, - :test_typecast_attribute_from_select_to_false, - :test_typecast_attribute_from_select_to_true - ] - - include ARTest::SQLServer::CoercedTest - - fixtures :developers - - def test_coerced_read_attributes_before_type_cast_on_datetime - developer = Developer.first - if developer.created_at_before_type_cast.is_a?(String) - assert_equal "#{developer.created_at.to_s(:db)}.000" , developer.attributes_before_type_cast["created_at"] - end - end - - def test_coerced_typecast_attribute_from_select_to_false - topic = Topic.create(title: 'Budget') - topic = Topic.all.merge!(select: "topics.*, CASE WHEN 1=2 THEN 1 ELSE 0 END as is_test").first - assert !topic.is_test? - end - - def test_coerced_typecast_attribute_from_select_to_true - topic = Topic.create(title: 'Budget') - topic = Topic.all.merge!(select: "topics.*, CASE WHEN 2=2 THEN 1 ELSE 0 END as is_test").first - assert topic.is_test? - end - - -end diff --git a/test/cases/coerced/base_test_sqlserver.rb b/test/cases/coerced/base_test_sqlserver.rb deleted file mode 100644 index c3cfee246..000000000 --- a/test/cases/coerced/base_test_sqlserver.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/auto_id' - -class BasicsTest < ActiveRecord::TestCase - - include ARTest::SQLServer::CoercedTest - - COERCED_TESTS = [ - :test_column_names_are_escaped - ] - - it 'operates as other database adapters when finding primary keys, standard is postgresql adapter' do - assert_nil Post.where(id:'').first - assert_nil Post.where(id:nil).first - assert_raise(ActiveRecord::RecordNotFound) { Post.find('') } - assert_raise(ActiveRecord::RecordNotFound) { Post.find(nil) } - end - - def test_coerced_column_names_are_escaped - assert_equal "[foo]]bar]", ActiveRecord::Base.connection.quote_column_name("foo]bar") - end - -end diff --git a/test/cases/coerced/batches_test_sqlserver.rb b/test/cases/coerced/batches_test_sqlserver.rb deleted file mode 100644 index 58f2416c5..000000000 --- a/test/cases/coerced/batches_test_sqlserver.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' - -class BatchesTestSQLServer < ActiveRecord::TestCase -end - -class EachTest < ActiveRecord::TestCase - - COERCED_TESTS = [ - :test_find_in_batches_should_quote_batch_order - ] - - include ARTest::SQLServer::CoercedTest - - fixtures :posts - - def test_coerced_find_in_batches_should_quote_batch_order - c = Post.connection - assert_sql(/ORDER BY \[posts\]\.\[id\]/) do - Post.find_in_batches(batch_size: 1) do |batch| - assert_kind_of Array, batch - assert_kind_of Post, batch.first - end - end - end - - -end diff --git a/test/cases/coerced/belongs_to_associations_test_sqlserver.rb b/test/cases/coerced/belongs_to_associations_test_sqlserver.rb deleted file mode 100644 index 4a2d3b5c9..000000000 --- a/test/cases/coerced/belongs_to_associations_test_sqlserver.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'cases/helper_sqlserver' - -class BelongsToAssociationsTestSQLServer < ActiveRecord::TestCase -end - -class BelongsToAssociationsTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_belongs_to_with_primary_key_joins_on_correct_column] - - include ARTest::SQLServer::CoercedTest - - def test_coerced_belongs_to_with_primary_key_joins_on_correct_column - sql = Client.joins(:firm_with_primary_key).to_sql - assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql) - assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql) - end - - -end diff --git a/test/cases/coerced/bind_parameter_test_sqlserver.rb b/test/cases/coerced/bind_parameter_test_sqlserver.rb deleted file mode 100644 index dad6c4b61..000000000 --- a/test/cases/coerced/bind_parameter_test_sqlserver.rb +++ /dev/null @@ -1,33 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/topic' -require 'models/sqlserver/topic' -require 'cases/bind_parameter_test' - -# We don't coerce here because these tests are located inside of an if block -# and don't seem to be able to be properly overriden with the coerce -# functionality -module ActiveRecord - class BindParameterTest - def test_binds_are_logged - sub = @connection.substitute_at(@pk, 0) - binds = [[@pk, 1]] - sql = "select * from topics where id = #{sub}" - - @connection.exec_query(sql, 'SQL', binds) - - message = @listener.calls.find { |args| args[4][:sql].include? sql } - assert_equal binds, message[4][:binds] - end - - def test_binds_are_logged_after_type_cast - sub = @connection.substitute_at(@pk, 0) - binds = [[@pk, "3"]] - sql = "select * from topics where id = #{sub}" - - @connection.exec_query(sql, 'SQL', binds) - - message = @listener.calls.find { |args| args[4][:sql].include? sql } - assert_equal [[@pk, 3]], message[4][:binds] - end - end -end diff --git a/test/cases/coerced/calculations_test_sqlserver.rb b/test/cases/coerced/calculations_test_sqlserver.rb deleted file mode 100644 index e75299371..000000000 --- a/test/cases/coerced/calculations_test_sqlserver.rb +++ /dev/null @@ -1,59 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/company' -require 'models/topic' -require 'models/edge' -require 'models/club' -require 'models/organization' - -class CalculationsTestSQLServer < ActiveRecord::TestCase -end - -class CalculationsTest < ActiveRecord::TestCase - - COERCED_TESTS = [ - :test_should_return_decimal_average_of_integer_field, - :test_should_sum_expression, - :test_limit_is_kept, - :test_limit_with_offset_is_kept, - :test_offset_is_kept - ] - - include ARTest::SQLServer::CoercedTest - - fixtures :accounts - - def test_coerced_should_return_decimal_average_of_integer_field - # Other DBs return 3.5 like this. - # Account.all.map(&:id).inspect # => [1, 2, 3, 4, 5, 6] - # (1+2+3+4+5+6)/6.0 # => 3.5 - # But SQL Server does something like this. Bogus! - # (1+2+3+4+5+6)/6 # => 3 - value = Account.average(:id) - assert_equal 3, value - end - - def test_coerced_should_sum_expression - assert_equal 636, Account.sum("2 * credit_limit") - end - - def test_coerced_limit_is_kept - queries = assert_sql { Account.limit(1).count } - assert_equal 1, queries.length - assert_match(/TOP \(1\)/, queries.first) - end - - def test_coerced_limit_with_offset_is_kept - queries = assert_sql { Account.limit(1).offset(1).count } - assert_equal 1, queries.length - assert_match(/TOP \(1\)/, queries.first) - assert_match(/\[__rn\] > \(1\)/, queries.first) - end - - def test_coerced_offset_is_kept - queries = assert_sql { Account.offset(1).count } - assert_equal 1, queries.length - assert_match(/\[__rn\] > \(1\)/, queries.first) - end - - -end diff --git a/test/cases/coerced/connection_management_test_sqlserver.rb b/test/cases/coerced/connection_management_test_sqlserver.rb deleted file mode 100644 index c2b087cb6..000000000 --- a/test/cases/coerced/connection_management_test_sqlserver.rb +++ /dev/null @@ -1,38 +0,0 @@ -#This rails pull request will make this code unnecessary https://github.com/rails/rails/pull/13745 - -require "cases/helper_sqlserver" -require "rack" - -module ActiveRecord - module ConnectionAdapters - class ConnectionManagementTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_connection_pool_per_pid] - - include ARTest::SQLServer::CoercedTest - - #https://www.ruby-forum.com/topic/4221299 - def test_coerced_connection_pool_per_pid - return skip('must support fork') unless Process.respond_to?(:fork) - - object_id = ActiveRecord::Base.connection.object_id - - rd, wr = IO.pipe - rd.binmode - wr.binmode - pid = fork { - rd.close - wr.write Marshal.dump ActiveRecord::Base.connection.object_id - wr.close - exit! - } - - wr.close - - Process.waitpid pid - assert_not_equal object_id, Marshal.load(rd.read) - rd.close - end - end - end -end diff --git a/test/cases/coerced/eager_test_sqlserver.rb b/test/cases/coerced/eager_test_sqlserver.rb deleted file mode 100644 index 8b7f73a0c..000000000 --- a/test/cases/coerced/eager_test_sqlserver.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/comment' -require 'models/author' - -class EagerAssociationTestSQLServer < ActiveRecord::TestCase -end - -class EagerAssociationTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_count_with_include] - - include ARTest::SQLServer::CoercedTest - - fixtures :posts, :comments, :authors - - def test_coerced_count_with_include - assert_equal 3, authors(:david).posts_with_comments.where("len(comments.body) > 15").references(:comments).count - end - - -end diff --git a/test/cases/coerced/finder_test_sqlserver.rb b/test/cases/coerced/finder_test_sqlserver.rb deleted file mode 100644 index d6361a2f8..000000000 --- a/test/cases/coerced/finder_test_sqlserver.rb +++ /dev/null @@ -1,54 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/event' -require 'models/author' -require 'models/post' -require 'models/categorization' -require 'models/sqlserver/topic' -# require 'cases/finder_test.rb' - -class FinderTestSQLServer < ActiveRecord::TestCase -end - -class FinderTest < ActiveRecord::TestCase - fixtures :authors, :author_addresses, :posts, :categorizations - COERCED_TESTS = [ - :test_exists_does_not_select_columns_without_alias, - :test_string_sanitation, - :test_take_and_first_and_last_with_integer_should_use_sql_limit - ] - - include ARTest::SQLServer::CoercedTest - - - def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct_and_offset - # Based on test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - # and issue https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/306 - assert_equal( - posts(:welcome, :thinking), - Post.all.merge!( - :includes => { authors: :author_address }, - :order => ['authors.name DESC'], - :limit => 2, - :offset => 1 - ).to_a - ) - end - - def test_coerced_exists_does_not_select_columns_without_alias - assert_sql(/SELECT TOP \(1\) 1 AS one FROM \[topics\]/i) do - Topic.exists? - end - end - - def test_coerced_string_sanitation - assert_not_equal "N'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") - assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") - end - - def test_coerced_take_and_first_and_last_with_integer_should_use_sql_limit - assert_sql(/TOP \(3\)/) { Topic.take(3).entries } - assert_sql(/TOP \(2\)/) { Topic.first(2).entries } - assert_sql(/TOP \(5\)/) { Topic.last(5).entries } - end -end - diff --git a/test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb b/test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb deleted file mode 100644 index 60dad5b3d..000000000 --- a/test/cases/coerced/has_and_belongs_to_many_associations_test_sqlserver.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'cases/helper_sqlserver' - -class HasAndBelongsToManyAssociationsTestSQLServer < ActiveRecord::TestCase -end - -class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase - - COERCED_TESTS = [ - :test_count_with_finder_sql, - :test_caching_of_columns - ] - - include ARTest::SQLServer::CoercedTest - - # TODO: put a real test here - def test_coerced_count_with_finder_sql - assert true - end - - # TODO: put a real test here - def test_coerced_caching_of_columns - assert true - end - - -end diff --git a/test/cases/coerced/inheritance_test_sqlserver.rb b/test/cases/coerced/inheritance_test_sqlserver.rb deleted file mode 100644 index 8be48a882..000000000 --- a/test/cases/coerced/inheritance_test_sqlserver.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/company' -require 'models/project' -require 'models/subscriber' - -class InheritanceTestSQLServer < ActiveRecord::TestCase -end - -class InheritanceTest < ActiveRecord::TestCase - - fixtures :companies, :projects, :subscribers, :accounts - - COERCED_TESTS = [ - :test_a_bad_type_column, - :test_eager_load_belongs_to_primary_key_quoting - ] - - include ARTest::SQLServer::CoercedTest - - def test_coerced_a_bad_type_column - Company.connection.execute "SET IDENTITY_INSERT [companies] ON" - Company.connection.insert "INSERT INTO companies ([id], [type], [name]) VALUES(100, N'bad_class!', N'Not happening')" - Company.connection.execute "SET IDENTITY_INSERT [companies] OFF" - assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } - end - - def test_coerced_eager_load_belongs_to_primary_key_quoting - con = Account.connection - assert_sql(/\[companies\]\.\[id\] IN \(1\)/) do - Account.includes(:firm).find(1) - end - end - - -end - - diff --git a/test/cases/coerced/invalid_connection_test_sqlserver.rb b/test/cases/coerced/invalid_connection_test_sqlserver.rb deleted file mode 100644 index ffa7b6dbf..000000000 --- a/test/cases/coerced/invalid_connection_test_sqlserver.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'cases/helper_sqlserver' -require 'cases/invalid_connection_test' - -class TestAdapterWithInvalidConnection < ActiveRecord::TestCase - def setup - #The activerecord test arbitrarily used mysql (needed to use somthing that wasn't sqlite). - #It makes much more sense for us to use sqlserver - Bird.establish_connection adapter: 'sqlserver', database: 'i_do_not_exist' - end -end diff --git a/test/cases/coerced/migration_test_sqlserver.rb b/test/cases/coerced/migration_test_sqlserver.rb deleted file mode 100644 index da1987f22..000000000 --- a/test/cases/coerced/migration_test_sqlserver.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/person' - -class MigrationTest < ActiveRecord::TestCase - COERCED_TESTS = [:test_migrator_db_has_no_schema_migrations_table] - include ARTest::SQLServer::CoercedTest - - # TODO: put a real test here - def test_coerced_test_migrator_db_has_no_schema_migrations_table - assert true - end - -end diff --git a/test/cases/coerced/persistence_test_sqlserver.rb b/test/cases/coerced/persistence_test_sqlserver.rb deleted file mode 100644 index 430497ba2..000000000 --- a/test/cases/coerced/persistence_test_sqlserver.rb +++ /dev/null @@ -1,71 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/comment' -require 'models/author' -require 'models/topic' -require 'models/reply' -require 'models/category' -require 'models/company' -require 'models/developer' -require 'models/project' -require 'models/minimalistic' -require 'models/warehouse_thing' -require 'models/parrot' -require 'models/minivan' -require 'models/person' -require 'models/sqlserver/topic' -require 'rexml/document' - -class PersistenceTestSQLServer < ActiveRecord::TestCase -end - -class PersistenceTest < ActiveRecord::TestCase - - fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts, :minivans - - COERCED_TESTS = [:test_update_all_doesnt_ignore_order, :test_update_columns_changing_id, :test_update_attributes] - - include ARTest::SQLServer::CoercedTest - - def test_coerced_update_all_doesnt_ignore_order - assert_equal authors(:david).id + 1, authors(:mary).id - test_update_with_order_succeeds = lambda do |order| - begin - Author.order(order).update_all('id = id + 1') - rescue ActiveRecord::ActiveRecordError - false - end - end - if test_update_with_order_succeeds.call('id DESC') - assert !test_update_with_order_succeeds.call('id ASC') - else - assert_sql(/UPDATE .* \(SELECT .* ORDER BY id DESC\)/i) do - test_update_with_order_succeeds.call('id DESC') - end - end - end - - def test_coerced_update_attributes - topic = Topic.find(1) - assert !topic.approved? - assert_equal "The First Topic", topic.title - - topic.update_attributes("approved" => true, "title" => "The First Topic Updated") - topic.reload - assert topic.approved? - assert_equal "The First Topic Updated", topic.title - - topic.update_attributes(approved: false, title: "The First Topic") - topic.reload - assert !topic.approved? - assert_equal "The First Topic", topic.title - - assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do - Topic.create!(id: 3, title: "Hm is it possible?") - end - assert_not_equal "Hm is it possible?", Topic.find(3).title - end - -end - - diff --git a/test/cases/coerced/predicate_builder_test_sqlserver.rb b/test/cases/coerced/predicate_builder_test_sqlserver.rb deleted file mode 100644 index 3242ae634..000000000 --- a/test/cases/coerced/predicate_builder_test_sqlserver.rb +++ /dev/null @@ -1,20 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/topic' -require 'models/sqlserver/topic' - -module ActiveRecord - class PredicateBuilderTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_registering_new_handlers] - - include ARTest::SQLServer::CoercedTest - - def test_coerced_registering_new_handlers - ActiveRecord::PredicateBuilder.register_handler(Regexp, proc do |column, value| - Arel::Nodes::InfixOperation.new('~', column, value.source) - end) - - assert_match %r{\[topics\].\[title\] ~ N'rails'}i, Topic.where(title: /rails/).to_sql - end - end -end diff --git a/test/cases/coerced/query_cache_test_sqlserver.rb b/test/cases/coerced/query_cache_test_sqlserver.rb deleted file mode 100644 index 8bede2012..000000000 --- a/test/cases/coerced/query_cache_test_sqlserver.rb +++ /dev/null @@ -1,22 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/task' - -class QueryCacheTestSQLServer < ActiveRecord::TestCase -end - -class QueryCacheTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_cache_does_not_wrap_string_results_in_arrays] - - include ARTest::SQLServer::CoercedTest - - fixtures :tasks - - def test_coerced_cache_does_not_wrap_string_results_in_arrays - Task.cache do - assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") - end - end - - -end diff --git a/test/cases/coerced/relations_test_sqlserver.rb b/test/cases/coerced/relations_test_sqlserver.rb deleted file mode 100644 index 5657b126c..000000000 --- a/test/cases/coerced/relations_test_sqlserver.rb +++ /dev/null @@ -1,37 +0,0 @@ -require "cases/helper_sqlserver" -require 'models/post' - -class RelationTest < ActiveRecord::TestCase - COERCED_TESTS = [ - :test_merging_reorders_bind_params, - :test_to_sql_on_eager_join - ] - # Until that patch is made to rails we are preventing this test from running in this gem. - include ARTest::SQLServer::CoercedTest - fixtures :posts - - def test_coerced_merging_reorders_bind_params - post = Post.first - id_column = Post.columns_hash['id'] - title_column = Post.columns_hash['title'] - - bvr = Post.connection.substitute_at id_column, 1 - right = Post.where(id: bvr) - right.bind_values += [[id_column, post.id]] - - bvl = Post.connection.substitute_at title_column, 0 - left = Post.where(title: bvl) - left.bind_values += [[title_column, post.title]] - - merged = left.merge(right) - assert_equal post, merged.first - end - - def test_coerced_to_sql_on_eager_join - expected = assert_sql { - Post.eager_load(:last_comment).order('comments.id DESC').to_a - }.first - actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql - assert_equal expected.include?(actual), true - end -end diff --git a/test/cases/coerced/resolver_test_sqlserver.rb b/test/cases/coerced/resolver_test_sqlserver.rb deleted file mode 100644 index 83714db8e..000000000 --- a/test/cases/coerced/resolver_test_sqlserver.rb +++ /dev/null @@ -1,46 +0,0 @@ -require "cases/helper" - -module ActiveRecord - module ConnectionAdapters - class ConnectionSpecification - - class ResolverTest < ActiveRecord::TestCase - - include ARTest::SQLServer::CoercedTest - - COERCED_TESTS = [ - :test_url_host_no_db, - :test_url_host_db, - :test_url_port - ] - - def test_coerced_test_url_host_no_db - spec = resolve 'sqlserver://foo?encoding=utf8' - assert_equal({ - "adapter" => "sqlserver", - "host" => "foo", - "encoding" => "utf8" }, spec) - end - - def test_coerced_test_url_host_db - spec = resolve 'sqlserver://foo/bar?encoding=utf8' - assert_equal({ - "adapter" => "sqlserver", - "database" => "bar", - "host" => "foo", - "encoding" => "utf8" }, spec) - end - - def test_coerced_test_url_port - spec = resolve 'sqlserver://foo:123?encoding=utf8' - assert_equal({ - "adapter" => "sqlserver", - "port" => 123, - "host" => "foo", - "encoding" => "utf8" }, spec) - end - end - - end - end -end diff --git a/test/cases/coerced/schema_dumper_test_sqlserver.rb b/test/cases/coerced/schema_dumper_test_sqlserver.rb deleted file mode 100644 index 267e7036d..000000000 --- a/test/cases/coerced/schema_dumper_test_sqlserver.rb +++ /dev/null @@ -1,32 +0,0 @@ -require 'cases/helper_sqlserver' -require 'cases/schema_dumper_test' -require 'stringio' - -class SchemaDumperTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_schema_dump_keeps_large_precision_integer_columns_as_decimal, :test_types_line_up] - - include ARTest::SQLServer::CoercedTest - - def test_coerced_schema_dump_keeps_large_precision_integer_columns_as_decimal - output = standard_dump - assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38,\s+scale: 0}, output - end - - def test_coerced_types_line_up - column_definition_lines.each do |column_set| - next if column_set.empty? - - lengths = column_set.map do |column| - if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean|uuid)\s+"/) - match[0].length - end - end - - assert_equal 1, lengths.uniq.length - end - end - -end - - diff --git a/test/cases/coerced/transaction_test_sqlserver.rb b/test/cases/coerced/transaction_test_sqlserver.rb deleted file mode 100644 index aba4868ac..000000000 --- a/test/cases/coerced/transaction_test_sqlserver.rb +++ /dev/null @@ -1,18 +0,0 @@ -require 'cases/helper_sqlserver' - -class TransactionTest < ActiveRecord::TestCase - include ARTest::SQLServer::CoercedTest - - COERCED_TESTS = [:test_releasing_named_savepoints] - - def test_coerced_releasing_named_savepoints - Topic.transaction do - Topic.connection.create_savepoint("another") - Topic.connection.release_savepoint("another") - - # The origin rails test tries to re-release the savepoint, but - # since sqlserver doesn't have the concept of releasing, it doesn't - # fail, so we just omit that part here - end - end -end diff --git a/test/cases/coerced/uniqueness_validation_test_sqlserver.rb b/test/cases/coerced/uniqueness_validation_test_sqlserver.rb deleted file mode 100644 index e5f294e0f..000000000 --- a/test/cases/coerced/uniqueness_validation_test_sqlserver.rb +++ /dev/null @@ -1,42 +0,0 @@ -# encoding: utf-8 -require 'cases/helper_sqlserver' -require 'models/event' - -class Event < ActiveRecord::Base - before_validation :strip_mb_chars_for_sqlserver - protected - def strip_mb_chars_for_sqlserver - self.title = title.mb_chars.to(4).to_s if title && title.is_utf8? - end -end - -class UniquenessValidationTestSQLServer < ActiveRecord::TestCase -end - -class UniquenessValidationTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_validate_uniqueness_with_limit_and_utf8] - - include ARTest::SQLServer::CoercedTest - - # I guess most databases just truncate a string when inserting. To pass this test we do a few things. - # First, we make sure the type is unicode safe, second we extend the limit to well beyond what is - # needed. At the top we make sure to auto truncate the :title string like other databases would do - # automatically. - # - # "一二三四五".mb_chars.size # => 5 - # "一二三四五六七八".mb_chars.size # => 8 - # "一二三四五六七八".mb_chars.to(4).to_s # => "一二三四五" - - def test_coerced_validate_uniqueness_with_limit_and_utf8 - Event.connection.change_column :events, :title, :nvarchar, limit: 30 - Event.reset_column_information - # Now the actual test copied from core. - e1 = Event.create(title: "一二三四五") - assert e1.valid?, "Could not create an event with a unique, 5 character title" - e2 = Event.create(title: "一二三四五六七八") - assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" - end - -end - diff --git a/test/cases/coerced/where_chain_test_sqlserver.rb b/test/cases/coerced/where_chain_test_sqlserver.rb deleted file mode 100644 index f690f61ac..000000000 --- a/test/cases/coerced/where_chain_test_sqlserver.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/comment' - -module ActiveRecord - class WhereChainTest < ActiveRecord::TestCase - - include ARTest::SQLServer::CoercedTest - - COERCED_TESTS = [:test_not_eq_with_array_parameter] - - def test_coerced_not_eq_with_array_parameter - expected = Arel::Nodes::Not.new("title = N'hello'") - relation = Post.where.not(['title = ?', 'hello']) - assert_equal([expected], relation.where_values) - end - - end -end From 5acebb3cd301e0565bdd80902514c92ebd58d531 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 16:23:16 -0500 Subject: [PATCH 0253/1412] Move rake path helpers to their own file. Run entire AR suite now. --- Gemfile | 2 ++ Rakefile | 22 +------------------ test/support/rake_helpers.rb | 42 ++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 21 deletions(-) create mode 100644 test/support/rake_helpers.rb diff --git a/Gemfile b/Gemfile index 4ca2e392e..da525e270 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,8 @@ source 'https://rubygems.org' gemspec +gem 'bcrypt' + if RbConfig::CONFIG["host_os"] =~ /darwin/ gem 'terminal-notifier-guard' end diff --git a/Rakefile b/Rakefile index 7aab7d59a..498601d9e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,26 +1,6 @@ require 'rake/testtask' require_relative 'test/support/paths_sqlserver' - -def test_files - if files = ENV['AR_TEST_FILES'] - files = files.split(',').map do |file| - File.join ARTest::SQLServer.root_activerecord, file.strip - end - return files.unshift 'test/cases/helper_sqlserver.rb' - end - if files = ENV['TEST_FILES'] - return files.split(',').map(&:strip) - end - sqlserver_cases = Dir.glob('test/cases/**/*_test_sqlserver.rb') - ar_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") - if ENV['SQLSERVER_ONLY'] - sqlserver_cases - elsif ENV['ACTIVERECORD_ONLY'] - ar_cases - else - sqlserver_cases + ar_cases - end -end +require_relative 'test/support/rake_helpers' task test: ['test:dblib'] task default: [:test] diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb new file mode 100644 index 000000000..b9bbc5d63 --- /dev/null +++ b/test/support/rake_helpers.rb @@ -0,0 +1,42 @@ + +TEST_HELPER = 'test/cases/helper_sqlserver.rb' + +def env_ar_test_files + return unless ENV['AR_TEST_FILES'] && !ENV['AR_TEST_FILES'].empty? + @env_ar_test_files ||= begin + files = ENV['AR_TEST_FILES'].split(',').map do |file| + File.join ARTest::SQLServer.root_activerecord, file.strip + end + files.unshift(TEST_HELPER) + end +end + +def env_test_files + return unless ENV['TEST_FILES'] && !ENV['TEST_FILES'].empty? + @env_test_files ||= ENV['TEST_FILES'].split(',').map(&:strip) +end + +def sqlserver_cases + @sqlserver_cases ||= Dir.glob('test/cases/**/*_test_sqlserver.rb') - [TEST_HELPER] +end + +def ar_cases + @ar_cases ||= begin + all_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") + adapters_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/adapters/**/*_test.rb") + [TEST_HELPER] + all_cases - adapters_cases + end +end + +def test_files + return env_ar_test_files if env_ar_test_files + return env_test_files if env_test_files + if ENV['SQLSERVER_ONLY'] + sqlserver_cases + elsif ENV['ACTIVERECORD_ONLY'] + ar_cases + else + sqlserver_cases + ar_cases + end +end + From dfb8458f7836a8f2fc7d2f453d8517318ef35bff Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 16:53:03 -0500 Subject: [PATCH 0254/1412] Fixed bugs exposed by core ActiveRecord tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit √ 27 - wrong number of arguments (2 for 1) database_statements.rb:77:in `case_sensitive_modifier' √ 19 - undefined method `gsub' for :Symbol sqlserver/utils.rb:106:in `quote_string' --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- lib/active_record/connection_adapters/sqlserver/utils.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index ebacdcf39..1e568834b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -74,7 +74,7 @@ def empty_insert_statement_value 'DEFAULT VALUES' end - def case_sensitive_modifier(node) + def case_sensitive_modifier(node, table_attribute) node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 06f8dfe54..dc3bb4a99 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -103,7 +103,7 @@ def parts extend self def quote_string(s) - s.gsub /\'/, "''" + s.to_s.gsub /\'/, "''" end def unquote_string(s) From 5bc019e4e6c94aa8a69b1cbdb3adeeb7e3adbbf2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 18 Jan 2015 19:40:46 -0500 Subject: [PATCH 0255/1412] Fixed bugs exposed by core ActiveRecord tests. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit √ 22 - RuntimeError: unsupported: String lib/arel/visitors/to_sql.rb:729:in `unsupported' --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 3 ++- .../connection_adapters/sqlserver_adapter.rb | 8 ++++---- lib/arel/visitors/sqlserver.rb | 6 ++++++ 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0befbf70a..89a7724d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Use `ARTest` namespace with `SQLServer` module for our helpers/objects. * Simple 2012 schmea addition and extensive column/type_cast object tests. * Follow Rails convention and remove varying character default limits. +* The `cs_equality_operator` is now s class configuration property only. #### Deprecated diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1e568834b..1fcb1c80f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -75,7 +75,8 @@ def empty_insert_statement_value end def case_sensitive_modifier(node, table_attribute) - node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node + node = Arel::Nodes.build_quoted node, table_attribute + Arel::Nodes::Bin.new(node) end # === SQLServer Specific ======================================== # diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 76b14ac5d..21845ad37 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -38,7 +38,10 @@ class SQLServerAdapter < AbstractAdapter attr_reader :spid cattr_accessor :auto_connect, :auto_connect_duration, instance_accessor: false - cattr_accessor :cs_equality_operator, :lowercase_schema_reflection, :showplan_option + cattr_accessor :cs_equality_operator, instance_accessor: false + cattr_accessor :lowercase_schema_reflection, :showplan_option + + self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS' def initialize(connection, logger, pool, config) super(connection, logger, pool) @@ -181,9 +184,6 @@ def auto_connect_duration self.class.auto_connect_duration ||= 10 end - def cs_equality_operator - @@cs_equality_operator || 'COLLATE Latin1_General_CS_AS_WS' - end protected diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 0919d1565..6b895c014 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -16,6 +16,12 @@ def visit_Arel_Nodes_BindParam o, collector collector.add_bind(o) { |i| "@#{i-1}" } end + def visit_Arel_Nodes_Bin o, collector + visit o.expr, collector + collector << ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator + collector << SPACE + end + def visit_Arel_Nodes_Lock o, collector o.expr = Arel.sql('WITH (UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/ collector << SPACE From 9367e94aebdca9543c2f099894eb70400c08f9a3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Jan 2015 16:26:47 -0500 Subject: [PATCH 0256/1412] Badges. --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index e61663baa..8ee9474d5 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. +[![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) +[![Gitter chat](https://img.shields.io/badge/gitter-rails-sqlserver/activerecord-sqlserver-adapter-ae3dd2.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) + **This project is looking for a new maintainers. Join the [discusion here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/364)** The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. From 260acf2609c3d6453e58cfd6c91696bb40524984 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Jan 2015 18:39:54 -0500 Subject: [PATCH 0257/1412] Remove mini-shoulda. --- test/cases/database_statements_test_sqlserver.rb | 2 +- test/support/minitest_sqlserver.rb | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb index eba8892f3..610af7e2c 100644 --- a/test/cases/database_statements_test_sqlserver.rb +++ b/test/cases/database_statements_test_sqlserver.rb @@ -28,7 +28,7 @@ class DatabaseStatementsTestSQLServer < ActiveRecord::TestCase connection.drop_database '[activerecord.unittest]' end - context 'with collation' do + describe 'with collation' do after { connection.drop_database 'activerecord_unittest3' } diff --git a/test/support/minitest_sqlserver.rb b/test/support/minitest_sqlserver.rb index 433504dc7..c12a467ce 100644 --- a/test/support/minitest_sqlserver.rb +++ b/test/support/minitest_sqlserver.rb @@ -1,2 +1 @@ require 'minitest-spec-rails/init/active_support' -require 'minitest-spec-rails/init/mini_shoulda' From 14c8e499230433aafc5366785be0f5718620f1e4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Jan 2015 18:42:03 -0500 Subject: [PATCH 0258/1412] Note about `activity_stats` removal. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89a7724d4..4e60430a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ * product_version * edition * Removed tests for old issue #164. Handled by core types now. +* The `activity_stats` method. Please put this in a gem if needed. #### Fixed From cc18565ca8c9fac6e04e59b7d189802d10286144 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Jan 2015 18:55:18 -0500 Subject: [PATCH 0259/1412] Remove old DB reflection constants. --- lib/active_record/connection_adapters/sqlserver/version.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index dcc96149f..b29fcb8af 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -5,9 +5,6 @@ module Version VERSION = '4.2.0' - SUPPORTED_VERSIONS = [2005, 2008, 2010, 2011, 2012, 2014] - DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+"?(\d{4}|\w+)"?/ - end end end From f13fdd577eee89f7165bf024697ac5d102622425 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Jan 2015 19:00:09 -0500 Subject: [PATCH 0260/1412] Use book style spacing. Read paragraphs. --- .../connection_adapters/sqlserver/core_ext/explain.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/odbc.rb | 6 ++++++ .../connection_adapters/sqlserver/database_limits.rb | 2 ++ lib/active_record/connection_adapters/sqlserver/errors.rb | 4 ++++ lib/active_record/connection_adapters/sqlserver/showplan.rb | 2 ++ 5 files changed, 16 insertions(+) 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 1f8e8870e..cb24eddc4 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -3,6 +3,7 @@ module ConnectionAdapters module SQLServer module CoreExt module Explain + SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' SQLSERVER_PARAM_MATCHER = /@\d+ =/ @@ -28,6 +29,7 @@ def unprepare_sqlserver_statement(sql) sql end end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb index 1b7a64d41..712751a2b 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb @@ -3,21 +3,27 @@ module ConnectionAdapters module SQLServer module CoreExt module ODBC + module Statement + def finished? connected? false rescue ::ODBC::Error true end + end module Database + def run_block(*args) yield sth = run(*args) sth.drop end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 4c0e56c14..e822ca96a 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -2,6 +2,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseLimits + def table_alias_length 128 end @@ -41,6 +42,7 @@ def sql_query_length def joins_per_query 256 end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index c6ec03f27..2e1a41be2 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -1,4 +1,5 @@ module ActiveRecord + class LostConnection < WrappedDatabaseException end @@ -8,6 +9,7 @@ class DeadlockVictim < WrappedDatabaseException module ConnectionAdapters module SQLServer module Errors + LOST_CONNECTION_EXCEPTIONS = { dblib: ['TinyTds::Error'], odbc: ['ODBC::Error'] @@ -26,7 +28,9 @@ def lost_connection_exceptions def lost_connection_messages LOST_CONNECTION_MESSAGES[@connection_options[:mode]] end + end end end + end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 0f597637e..5dae263e2 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -5,6 +5,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Showplan + OPTION_ALL = 'SHOWPLAN_ALL' OPTION_TEXT = 'SHOWPLAN_TEXT' OPTION_XML = 'SHOWPLAN_XML' @@ -58,6 +59,7 @@ def showplan_printer else PrinterTable end end + end end end From 101b023ae4baafc82634cb6c4bd6f4d9e4caf375 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 07:32:16 -0500 Subject: [PATCH 0261/1412] ActiveRecord's test files need to be sorted. Even when `ActiveSupport::TestCase.test_order = :sorted` is verified. 73 failures, 52 errors, 2 skips 65 failures, 48 errors, 2 skips 63 failures, 49 errors, 2 skips 66 failures, 48 errors, 2 skips 64 failures, 47 errors, 2 skips $ bundle exec rake test ACTIVERECORD_ONLY=1 TESTOPTS="--seed=63915" 99 failures, 48 errors, 2 skips --seed 63915 99 failures, 48 errors, 2 skips --seed 63915 Using `.sort` on my ActiveRecord Rake test files helper. 65 failures, 48 errors, 2 skips 65 failures, 48 errors, 2 skips --- Rakefile | 1 + test/support/rake_helpers.rb | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 498601d9e..2af75bde3 100644 --- a/Rakefile +++ b/Rakefile @@ -12,6 +12,7 @@ namespace :test do Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files + t.warning = !!ENV['WARNING'] t.verbose = true end diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index b9bbc5d63..a0b8c58e9 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -1,5 +1,5 @@ -TEST_HELPER = 'test/cases/helper_sqlserver.rb' +SQLSERVER_TEST_HELPER = 'test/cases/helper_sqlserver.rb' def env_ar_test_files return unless ENV['AR_TEST_FILES'] && !ENV['AR_TEST_FILES'].empty? @@ -7,7 +7,7 @@ def env_ar_test_files files = ENV['AR_TEST_FILES'].split(',').map do |file| File.join ARTest::SQLServer.root_activerecord, file.strip end - files.unshift(TEST_HELPER) + files.sort.unshift(SQLSERVER_TEST_HELPER) end end @@ -17,14 +17,14 @@ def env_test_files end def sqlserver_cases - @sqlserver_cases ||= Dir.glob('test/cases/**/*_test_sqlserver.rb') - [TEST_HELPER] + @sqlserver_cases ||= Dir.glob('test/cases/**/*_test_sqlserver.rb') - [SQLSERVER_TEST_HELPER] end def ar_cases @ar_cases ||= begin all_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") adapters_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/adapters/**/*_test.rb") - [TEST_HELPER] + all_cases - adapters_cases + (all_cases - adapters_cases).sort.unshift(SQLSERVER_TEST_HELPER) end end From 0339e8eb620f7ae96bda4f77554887abea6e834b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 13:35:02 -0500 Subject: [PATCH 0262/1412] Make ENV vars use common prefix with different name sufffix. --- test/support/rake_helpers.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index a0b8c58e9..87dac1caa 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -2,9 +2,9 @@ SQLSERVER_TEST_HELPER = 'test/cases/helper_sqlserver.rb' def env_ar_test_files - return unless ENV['AR_TEST_FILES'] && !ENV['AR_TEST_FILES'].empty? + return unless ENV['TEST_FILES_AR'] && !ENV['TEST_FILES_AR'].empty? @env_ar_test_files ||= begin - files = ENV['AR_TEST_FILES'].split(',').map do |file| + files = ENV['TEST_FILES_AR'].split(',').map do |file| File.join ARTest::SQLServer.root_activerecord, file.strip end files.sort.unshift(SQLSERVER_TEST_HELPER) @@ -31,9 +31,9 @@ def ar_cases def test_files return env_ar_test_files if env_ar_test_files return env_test_files if env_test_files - if ENV['SQLSERVER_ONLY'] + if ENV['ONLY_SQLSERVER'] sqlserver_cases - elsif ENV['ACTIVERECORD_ONLY'] + elsif ENV['ONLY_ACTIVERECORD'] ar_cases else sqlserver_cases + ar_cases From 89211bf8adac98aadcf46daea115e4cd3726aef3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 13:35:42 -0500 Subject: [PATCH 0263/1412] Fix `ORDER BY items must appear in the select list if SELECT DISTINCT` Please see #306 for more details. --- .../connection_adapters/sqlserver/schema_statements.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e4e546a2f..3a2a6c8e2 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -131,6 +131,15 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) end end + def columns_for_distinct(columns, orders) + order_columns = orders.reject(&:blank?).map{ |s| + s = s.to_sql unless s.is_a?(String) + s.gsub(/\s+(?:ASC|DESC)\b/i, '') + .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '') + }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + [super, *order_columns].join(', ') + end + def change_column_null(table_name, column_name, allow_null, default = nil) column = detect_column_for! table_name, column_name if !allow_null.nil? && allow_null == false && !default.nil? From a1afe1ac4454e294c1599ea81aea668f4fa608af Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 14:19:34 -0500 Subject: [PATCH 0264/1412] Do not evaluate equality operator for numbers. Expression type int is invalid for COLLATE clause UniquenessValidationTest#test_validate_case_insensitive_uniqueness UniquenessValidationTest#test_validate_uniqueness_on_existing_relation: UniquenessValidationTest#test_validate_uniqueness_with_non_standard_table_names: UniquenessValidationTest#test_validate_uniqueness_with_object_arg: EnumTest#test_0031_validate uniqueness: 62 failures, 43 errors, 2 skips --- lib/arel/visitors/sqlserver.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 6b895c014..2570d2bd3 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -18,8 +18,11 @@ def visit_Arel_Nodes_BindParam o, collector def visit_Arel_Nodes_Bin o, collector visit o.expr, collector - collector << ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator - collector << SPACE + if o.expr.val.is_a? Numeric + collector + else + collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} " + end end def visit_Arel_Nodes_Lock o, collector From da151987af8e052d803752538cbc4aebb95124a1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 17:04:47 -0500 Subject: [PATCH 0265/1412] Coerced tests are back! --- Guardfile | 1 + test/cases/coerced/sanitize_test.rb | 23 +++++++++++++++++++++++ test/support/rake_helpers.rb | 20 +++++++++++--------- 3 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 test/cases/coerced/sanitize_test.rb diff --git a/Guardfile b/Guardfile index 2ccfced5a..e42018b61 100644 --- a/Guardfile +++ b/Guardfile @@ -20,6 +20,7 @@ guard :minitest, { end else watch(%r{^test/cases/\w+_test_sqlserver.rb$}) + watch(%r{^test/cases/coerced/\w+_test.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 diff --git a/test/cases/coerced/sanitize_test.rb b/test/cases/coerced/sanitize_test.rb new file mode 100644 index 000000000..6136f69de --- /dev/null +++ b/test/cases/coerced/sanitize_test.rb @@ -0,0 +1,23 @@ +require 'cases/helper_sqlserver' +require 'models/binary' +require 'models/author' +require 'models/post' + +class SanitizeTest < ActiveRecord::TestCase + + COERCED_TESTS = [:test_sanitize_sql_like_example_use_case] + + include ARTest::SQLServer::CoercedTest + + def test_sanitize_sql_like_example_use_case_coerced + searchable_post = Class.new(Post) do + def self.search(term) + where("title LIKE ?", sanitize_sql_like(term, '!')) + end + end + assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do + searchable_post.search("20% _reduction_!").to_a + end + end + +end diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index 87dac1caa..eeafced1c 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -4,10 +4,9 @@ def env_ar_test_files return unless ENV['TEST_FILES_AR'] && !ENV['TEST_FILES_AR'].empty? @env_ar_test_files ||= begin - files = ENV['TEST_FILES_AR'].split(',').map do |file| + ENV['TEST_FILES_AR'].split(',').map { |file| File.join ARTest::SQLServer.root_activerecord, file.strip - end - files.sort.unshift(SQLSERVER_TEST_HELPER) + }.sort end end @@ -17,7 +16,11 @@ def env_test_files end def sqlserver_cases - @sqlserver_cases ||= Dir.glob('test/cases/**/*_test_sqlserver.rb') - [SQLSERVER_TEST_HELPER] + @sqlserver_cases ||= Dir.glob('test/cases/*_test_sqlserver.rb') +end + +def ar_coerced + @ar_coerced ||= Dir.glob('test/cases/coerced/*test*.rb') end def ar_cases @@ -29,14 +32,13 @@ def ar_cases end def test_files - return env_ar_test_files if env_ar_test_files + return env_ar_test_files.unshift(SQLSERVER_TEST_HELPER) if env_ar_test_files return env_test_files if env_test_files if ENV['ONLY_SQLSERVER'] - sqlserver_cases + sqlserver_cases + ar_coerced elsif ENV['ONLY_ACTIVERECORD'] - ar_cases + (ar_coerced + ar_cases).unshift(SQLSERVER_TEST_HELPER) else - sqlserver_cases + ar_cases + (ar_coerced + ar_cases + sqlserver_cases).unshift(SQLSERVER_TEST_HELPER) end end - From 7c8cf33490a07fb1e3b95a26f8a478d5e5ab964a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 19:54:19 -0500 Subject: [PATCH 0266/1412] New way to coerce tests. Always after AR. --- Guardfile | 6 ++--- test/cases/coerced/sanitize_test.rb | 23 ---------------- test/cases/coerced_tests.rb | 37 ++++++++++++++++++++++++++ test/cases/helper_sqlserver.rb | 2 ++ test/support/coerced_test_sqlserver.rb | 29 +++++++++++--------- test/support/rake_helpers.rb | 17 +++++------- 6 files changed, 65 insertions(+), 49 deletions(-) delete mode 100644 test/cases/coerced/sanitize_test.rb create mode 100644 test/cases/coerced_tests.rb diff --git a/Guardfile b/Guardfile index e42018b61..3a452b4d4 100644 --- a/Guardfile +++ b/Guardfile @@ -11,7 +11,7 @@ guard :minitest, { autorun: false, include: ['lib', 'test', ar_lib, ar_test], test_folders: ['test'], - test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb"] + test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb", "*_tests.rb"] } do # Our project watchers. if ENV['FOCUS_TEST'] @@ -19,8 +19,8 @@ guard :minitest, { watch(%r{.*}) { file } end else - watch(%r{^test/cases/\w+_test_sqlserver.rb$}) - watch(%r{^test/cases/coerced/\w+_test.rb$}) + 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{^test/cases/helper_sqlserver\.rb$}) { 'test' } end diff --git a/test/cases/coerced/sanitize_test.rb b/test/cases/coerced/sanitize_test.rb deleted file mode 100644 index 6136f69de..000000000 --- a/test/cases/coerced/sanitize_test.rb +++ /dev/null @@ -1,23 +0,0 @@ -require 'cases/helper_sqlserver' -require 'models/binary' -require 'models/author' -require 'models/post' - -class SanitizeTest < ActiveRecord::TestCase - - COERCED_TESTS = [:test_sanitize_sql_like_example_use_case] - - include ARTest::SQLServer::CoercedTest - - def test_sanitize_sql_like_example_use_case_coerced - searchable_post = Class.new(Post) do - def self.search(term) - where("title LIKE ?", sanitize_sql_like(term, '!')) - end - end - assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do - searchable_post.search("20% _reduction_!").to_a - end - end - -end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb new file mode 100644 index 000000000..f14e07f71 --- /dev/null +++ b/test/cases/coerced_tests.rb @@ -0,0 +1,37 @@ +require 'cases/helper_sqlserver' + + +require 'models/post' +class SanitizeTest < ActiveRecord::TestCase + + coerce_tests :test_sanitize_sql_like_example_use_case + + def test_sanitize_sql_like_example_use_case_coerced + searchable_post = Class.new(Post) do + def self.search(term) + where("title LIKE ?", sanitize_sql_like(term, '!')) + end + end + assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do + searchable_post.search("20% _reduction_!").to_a + end + end + +end + + +require 'models/author' +class YamlSerializationTest < ActiveRecord::TestCase + + fixtures :authors + + 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 + author = Author.select('authors.*, 5 as posts_count').first + dumped = YAML.load(YAML.dump(author)) + assert_equal 5, author.posts_count + assert_equal 5, dumped.posts_count + end + +end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 73a84c222..7c2112d8b 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -10,6 +10,8 @@ module ActiveRecord class TestCase < ActiveSupport::TestCase + include ARTest::SQLServer::CoercedTest + let(:logger) { ActiveRecord::Base.logger } let(:connection) { ActiveRecord::Base.connection } diff --git a/test/support/coerced_test_sqlserver.rb b/test/support/coerced_test_sqlserver.rb index f87305222..7242c8e0b 100644 --- a/test/support/coerced_test_sqlserver.rb +++ b/test/support/coerced_test_sqlserver.rb @@ -2,36 +2,39 @@ module ARTest module SQLServer module CoercedTest - def self.included(base) - base.extend ClassMethods + extend ActiveSupport::Concern + + included do + cattr_accessor :coerced_tests, instance_accessor: false + self.coerced_tests = [] end module ClassMethods - def self.extended(base) - base.class_eval do - Array(coerced_tests).each do |method_name| - undefine_and_puts(method_name) - end + def coerce_tests(*names) + names.each do |n| + self.coerced_tests.push(n) + coerce_test!(n) end end - def coerced_tests - self.const_get(:COERCED_TESTS) rescue nil + def coerce_test!(method) + coerced_test_warning(method) end def method_added(method) - if coerced_tests && coerced_tests.include?(method) - undefine_and_puts(method) - end + coerced_test_warning(method) if coerced_tests.include?(method.to_sym) end - def undefine_and_puts(method) + private + + def coerced_test_warning(method) result = undef_method(method) rescue nil STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? end end + end end end diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index eeafced1c..2de86da33 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -1,5 +1,6 @@ -SQLSERVER_TEST_HELPER = 'test/cases/helper_sqlserver.rb' +SQLSERVER_HELPER = 'test/cases/helper_sqlserver.rb' +SQLSERVER_COERCED = 'test/cases/coerced_tests.rb' def env_ar_test_files return unless ENV['TEST_FILES_AR'] && !ENV['TEST_FILES_AR'].empty? @@ -19,26 +20,22 @@ def sqlserver_cases @sqlserver_cases ||= Dir.glob('test/cases/*_test_sqlserver.rb') end -def ar_coerced - @ar_coerced ||= Dir.glob('test/cases/coerced/*test*.rb') -end - def ar_cases @ar_cases ||= begin all_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") adapters_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/adapters/**/*_test.rb") - (all_cases - adapters_cases).sort.unshift(SQLSERVER_TEST_HELPER) + (all_cases - adapters_cases).sort end end def test_files - return env_ar_test_files.unshift(SQLSERVER_TEST_HELPER) if env_ar_test_files + return env_ar_test_files.unshift(SQLSERVER_HELPER) if env_ar_test_files return env_test_files if env_test_files if ENV['ONLY_SQLSERVER'] - sqlserver_cases + ar_coerced + sqlserver_cases + [SQLSERVER_COERCED] elsif ENV['ONLY_ACTIVERECORD'] - (ar_coerced + ar_cases).unshift(SQLSERVER_TEST_HELPER) + [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED]) else - (ar_coerced + ar_cases + sqlserver_cases).unshift(SQLSERVER_TEST_HELPER) + [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED] + sqlserver_cases) end end From 50ac80dc908fa7f35d7cbec3ae6b91e1e89329ab Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 20:31:28 -0500 Subject: [PATCH 0267/1412] Consistent test ENV names with Rakefile. --- Guardfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Guardfile b/Guardfile index 3a452b4d4..3c11163a6 100644 --- a/Guardfile +++ b/Guardfile @@ -14,8 +14,8 @@ guard :minitest, { test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb", "*_tests.rb"] } do # Our project watchers. - if ENV['FOCUS_TEST'] - ENV['FOCUS_TEST'].split(',').map(&:strip).each do |file| + if ENV['TEST_FILES'] + ENV['TEST_FILES'].split(',').map(&:strip).each do |file| watch(%r{.*}) { file } end else From fbd8a6e60086b7b9544f1cd475188611641d96c8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 20:31:50 -0500 Subject: [PATCH 0268/1412] Fixed `The ORDER BY clause is invalid in views...` HasManyAssociationsTest#test_clearing_an_association_collection: HasManyAssociationsTest#test_clearing_without_initial_access: HasManyAssociationsTest#test_delete_all_with_not_yet_loaded_association_collection: HasManyAssociationsTest#test_deleting: HasManyAssociationsTest#test_deleting_a_collection: HasManyAssociationsTest#test_deleting_a_item_which_is_not_in_the_collection: HasManyAssociationsTest#test_transaction_when_deleting_persisted: HasManyAssociationsTest#test_transactions_when_replacing_on_persisted: HasManyAssociationsTest#test_update_all_on_association_accessed_before_save: PersistenceTest#test_update_all_ignores_order_without_limit_from_association: 69 failures, 34 errors, 2 skips --- lib/arel/visitors/sqlserver.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 2570d2bd3..b4fafb967 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -25,6 +25,13 @@ def visit_Arel_Nodes_Bin o, collector end end + def visit_Arel_Nodes_UpdateStatement(o, a) + if o.orders.any? && o.limit.nil? + o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) + end + super + end + def visit_Arel_Nodes_Lock o, collector o.expr = Arel.sql('WITH (UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/ collector << SPACE From e429ecc227c4e5d4db27e9ce5a49971e3010f230 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Jan 2015 21:27:50 -0500 Subject: [PATCH 0269/1412] The number of rows provided for a FETCH clause must be greater then zero 67 failures, 29 errors, 2 skips --- lib/arel/visitors/sqlserver.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index b4fafb967..e8a832133 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -115,6 +115,13 @@ def visit_Orders_And_Let_Fetch_Happen o, collector end def visit_Make_Fetch_Happen o, collector + if o.limit + value = case o.limit.expr + when Numeric then o.limit.expr + when Arel::Nodes::Unary then o.limit.expr.expr + end + o.limit = nil if value == 0 + end o.offset = Nodes::Offset.new(0) if o.limit && !o.offset collector = visit o.offset, collector if o.offset collector = visit o.limit, collector if o.limit From e53d7c5a9814045216e9cf64c6fa2f811e49c935 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 21 Jan 2015 15:42:12 -0500 Subject: [PATCH 0270/1412] Coerce all TypeLookupTest like PostgreSQLAdapter. We have our own. 10 - ActiveRecord::ConnectionAdapters::TypeLookupTest 56 failures, 28 errors, 2 skips --- test/cases/coerced_tests.rb | 12 ++++++++++++ test/cases/helper_sqlserver.rb | 4 ++-- ...est_sqlserver.rb => coerceable_test_sqlserver.rb} | 6 +++++- 3 files changed, 19 insertions(+), 3 deletions(-) rename test/support/{coerced_test_sqlserver.rb => coerceable_test_sqlserver.rb} (83%) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f14e07f71..0d2c9fef2 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -35,3 +35,15 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced end end + + +module ActiveRecord + module ConnectionAdapters + class TypeLookupTest < ActiveRecord::TestCase + + coerce_all_tests! # Just like PostgreSQLAdapter does. + + end + end +end + diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 7c2112d8b..d191047a6 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -3,14 +3,14 @@ require 'support/minitest_sqlserver' require 'cases/helper' require 'support/load_schema_sqlserver' -require 'support/coerced_test_sqlserver' +require 'support/coerceable_test_sqlserver' require 'support/sql_counter_sqlserver' require 'mocha/mini_test' module ActiveRecord class TestCase < ActiveSupport::TestCase - include ARTest::SQLServer::CoercedTest + include ARTest::SQLServer::CoerceableTest let(:logger) { ActiveRecord::Base.logger } let(:connection) { ActiveRecord::Base.connection } diff --git a/test/support/coerced_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb similarity index 83% rename from test/support/coerced_test_sqlserver.rb rename to test/support/coerceable_test_sqlserver.rb index 7242c8e0b..04714fb29 100644 --- a/test/support/coerced_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -1,6 +1,6 @@ module ARTest module SQLServer - module CoercedTest + module CoerceableTest extend ActiveSupport::Concern @@ -22,6 +22,10 @@ def coerce_test!(method) coerced_test_warning(method) end + def coerce_all_tests! + instance_methods(false).each { |method| coerce_test!(method) if method.to_s =~ /\Atest/ } + end + def method_added(method) coerced_test_warning(method) if coerced_tests.include?(method.to_sym) end From dc0b0cbf6f2d58da35fe5c5448311dc0693d04e5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 21 Jan 2015 16:48:08 -0500 Subject: [PATCH 0271/1412] Update to latest Guard and how we coerce tests. --- Guardfile | 4 +++- test/support/coerceable_test_sqlserver.rb | 18 ++++++++++++------ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Guardfile b/Guardfile index 3c11163a6..86f67988a 100644 --- a/Guardfile +++ b/Guardfile @@ -1,6 +1,8 @@ + require_relative 'test/support/paths_sqlserver' -notification :terminal_notifier if Guard::Notifier::TerminalNotifier.available? +clearing :on +notification :terminal_notifier if defined?(TerminalNotifier) ignore %r{debug\.log} ar_lib = File.join ARTest::SQLServer.root_activerecord, 'lib' diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 04714fb29..fb2780834 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -11,10 +11,10 @@ module CoerceableTest module ClassMethods - def coerce_tests(*names) - names.each do |n| - self.coerced_tests.push(n) - coerce_test!(n) + def coerce_tests(*methods) + methods.each do |method| + self.coerced_tests.push(method) + coerced_test_warning(method) end end @@ -23,7 +23,13 @@ def coerce_test!(method) end def coerce_all_tests! - instance_methods(false).each { |method| coerce_test!(method) if method.to_s =~ /\Atest/ } + once = false + instance_methods(false).each do |method| + next unless method.to_s =~ /\Atest/ + undef_method(method) + once = true + end + STDOUT.puts "Info: Undefined all tests: #{self.name}" end def method_added(method) @@ -34,7 +40,7 @@ def method_added(method) def coerced_test_warning(method) result = undef_method(method) rescue nil - STDOUT.puts("Info: Undefined coerced test: #{self.name}##{method}") unless result.blank? + STDOUT.puts "Info: Undefined coerced test: #{self.name}##{method}" unless result.blank? end end From f92f02bded23028ff061cfcc1215a6c50a3b22e9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 21 Jan 2015 16:48:30 -0500 Subject: [PATCH 0272/1412] ActiveRecord::ConnectionAdapters::MergeAndResolveDefaultUrlConfigTest 48 failures, 24 errors, 2 skips --- test/cases/coerced_tests.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0d2c9fef2..b8140b685 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -47,3 +47,19 @@ class TypeLookupTest < ActiveRecord::TestCase end end + +module ActiveRecord + module ConnectionAdapters + class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase + + # All sorts of errors due to how we test. Even setting ENV['RAILS_ENV'] to + # a value of 'default_env' will still show tests failing. Just ignoring all + # of them since we have no monkey in this circus. + # + coerce_all_tests! + + end + end +end + + From 3177e6742f617504195b82b9a01e3832d87e16be Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 21 Jan 2015 21:34:39 -0500 Subject: [PATCH 0273/1412] Allow coerced tests to be a Regexp. --- test/support/coerceable_test_sqlserver.rb | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index fb2780834..8913bae17 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -11,17 +11,13 @@ module CoerceableTest module ClassMethods - def coerce_tests(*methods) + def coerce_tests!(*methods) methods.each do |method| self.coerced_tests.push(method) coerced_test_warning(method) end end - def coerce_test!(method) - coerced_test_warning(method) - end - def coerce_all_tests! once = false instance_methods(false).each do |method| @@ -32,14 +28,11 @@ def coerce_all_tests! STDOUT.puts "Info: Undefined all tests: #{self.name}" end - def method_added(method) - coerced_test_warning(method) if coerced_tests.include?(method.to_sym) - end - private def coerced_test_warning(method) - result = undef_method(method) rescue nil + method = instance_methods(false).detect { |m| m =~ method } if method.is_a?(Regexp) + result = undef_method(method) if method && method_defined?(method) STDOUT.puts "Info: Undefined coerced test: #{self.name}##{method}" unless result.blank? end From dbdc676a430ec8bb4a8313a50acfed75f610bfab Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 21 Jan 2015 21:34:58 -0500 Subject: [PATCH 0274/1412] Coercing a group of tests. ActiveRecord::WhereChainTest#test_not_eq_with_array_parameter InheritanceTest#test_eager_load_belongs_to_primary_key_quoting NestedRelationScopingTest#test_merge_options EachTest#test_find_in_batches_should_quote_batch_order ActiveRecord::PredicateBuilderTest#test_registering_new_handlers ActiveRecord::Migration::ChangeSchemaTest#test_create_table_with_bigint ActiveRecord::Migration::ChangeSchemaTest#test_create_table_with_defaults FinderTest#test_0006_find_by doesn't have implicit ordering FinderTest#test_0010_find_by! doesn't have implicit ordering RelationTest#test_0005_find_by doesn't have implicit ordering RelationTest#test_0009_find_by! doesn't have implicit ordering FinderTest#test_exists_does_not_select_columns_without_alias FinderTest#test_find_doesnt_have_implicit_ordering FinderTest#test_string_sanitation FinderTest#test_take_and_first_and_last_with_integer_should_use_sql_limit 33 failures, 26 errors, 2 skips --- test/cases/coerced_tests.rb | 151 +++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b8140b685..9d870b6fd 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -4,7 +4,7 @@ require 'models/post' class SanitizeTest < ActiveRecord::TestCase - coerce_tests :test_sanitize_sql_like_example_use_case + coerce_tests! :test_sanitize_sql_like_example_use_case def test_sanitize_sql_like_example_use_case_coerced searchable_post = Class.new(Post) do @@ -20,12 +20,13 @@ def self.search(term) end + require 'models/author' class YamlSerializationTest < ActiveRecord::TestCase fixtures :authors - coerce_tests :test_types_of_virtual_columns_are_not_changed_on_round_trip + 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 author = Author.select('authors.*, 5 as posts_count').first @@ -37,6 +38,7 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced end + module ActiveRecord module ConnectionAdapters class TypeLookupTest < ActiveRecord::TestCase @@ -48,6 +50,7 @@ class TypeLookupTest < ActiveRecord::TestCase end + module ActiveRecord module ConnectionAdapters class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase @@ -63,3 +66,147 @@ class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase end + +require 'models/post' +module ActiveRecord + class WhereChainTest < ActiveRecord::TestCase + + coerce_tests! :test_not_eq_with_array_parameter + def test_not_eq_with_array_parameter_coerced + expected = Arel::Nodes::Not.new("title = N'hello'") + relation = Post.where.not(['title = ?', 'hello']) + assert_equal([expected], relation.where_values) + end + + end +end + + + +require 'models/company' +class InheritanceTest < ActiveRecord::TestCase + + fixtures :companies, :projects, :subscribers, :accounts, :vegetables + + coerce_tests! :test_eager_load_belongs_to_primary_key_quoting + def test_eager_load_belongs_to_primary_key_quoting_coerced + con = Account.connection + assert_sql(/\[companies\]\.\[id\] IN \(1\)/) do + Account.all.merge!(:includes => :firm).find(1) + end + end + +end + + + +require 'models/developer' +require 'models/computer' +class NestedRelationScopingTest < ActiveRecord::TestCase + + fixtures :authors, :developers, :projects, :comments, :posts + + coerce_tests! :test_merge_options + def test_merge_options_coerced + Developer.where('salary = 80000').scoping do + Developer.limit(10).scoping do + devs = Developer.all + sql = devs.to_sql + assert_match '(salary = 80000)', sql + assert_match 'FETCH NEXT 10 ROWS ONLY', sql + end + end + end + +end + + + +require 'models/post' +require 'models/subscriber' +class EachTest < ActiveRecord::TestCase + + fixtures :posts, :subscribers + + coerce_tests! :test_find_in_batches_should_quote_batch_order + def test_find_in_batches_should_quote_batch_order_coerced + c = Post.connection + assert_sql(/ORDER BY \[posts\]\.\[id\]/) do + Post.find_in_batches(:batch_size => 1) do |batch| + assert_kind_of Array, batch + assert_kind_of Post, batch.first + end + end + end + +end + + + +require 'models/topic' +module ActiveRecord + class PredicateBuilderTest < ActiveRecord::TestCase + + coerce_tests! :test_registering_new_handlers + def test_registering_new_handlers_coerced + PredicateBuilder.register_handler(Regexp, proc do |column, value| + Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source)) + end) + assert_match %r{\[topics\]\.\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql + end + + end +end + + + +module ActiveRecord + class Migration + class ChangeSchemaTest < ActiveRecord::TestCase + + coerce_tests! :test_create_table_with_bigint, + :test_create_table_with_defaults # We test these. + + end + end +end + + + +require 'models/topic' +class FinderTest < ActiveRecord::TestCase + + coerce_tests! %r{doesn't have implicit ordering}, + :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH. + + coerce_tests! :test_exists_does_not_select_columns_without_alias + def test_exists_does_not_select_columns_without_alias_coerced + assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY/i) do + Topic.exists? + end + end + + coerce_tests! :test_string_sanitation + def test_string_sanitation_coerced + assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") + assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") + end + + coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit + def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced + assert_sql(/OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY/) { Topic.take(3).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY/) { Topic.first(2).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY/) { Topic.last(5).entries } + end + +end + + + +class RelationTest < ActiveRecord::TestCase + + coerce_tests! %r{doesn't have implicit ordering} # We have implicit ordering, via FETCH. + +end + + From 1e1c0a8d4a79c3843e60f80c284944bb503c3e88 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Jan 2015 22:36:10 -0500 Subject: [PATCH 0275/1412] Use super class `empty_insert_statement_value` which is same. --- .../connection_adapters/sqlserver/database_statements.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1fcb1c80f..75603b4dc 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -70,10 +70,6 @@ def rollback_to_savepoint(name = current_savepoint_name) def release_savepoint(name = current_savepoint_name) end - def empty_insert_statement_value - 'DEFAULT VALUES' - end - def case_sensitive_modifier(node, table_attribute) node = Arel::Nodes.build_quoted node, table_attribute Arel::Nodes::Bin.new(node) From d2a3b4f7103eee35b3c647977fe83ab314642d8f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Jan 2015 22:37:09 -0500 Subject: [PATCH 0276/1412] Use quoted PK in `sql_for_insert` with OUTPUT INSERTED. --- .../sqlserver/database_statements.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 75603b4dc..f82fc2665 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -258,12 +258,12 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = - if pk - sql.insert(sql.index(/ (DEFAULT )?VALUES/), " OUTPUT inserted.#{pk}") - else - "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" - end + sql = if pk + quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted + sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + else + "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" + end super end From b2b5c45555d91e292fc32632493be4cd55c406f7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Jan 2015 22:38:46 -0500 Subject: [PATCH 0277/1412] Allow the SQLServerColumn objects to store the real table name. --- .../sqlserver/schema_statements.rb | 13 ++++++++----- .../connection_adapters/sqlserver_column.rb | 4 ++++ test/cases/column_test_sqlserver.rb | 5 +++++ 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3a2a6c8e2..a2aed6be5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -45,7 +45,7 @@ def indexes(table_name, name = nil) def columns(table_name, _name = nil) return [] if table_name.blank? column_definitions(table_name).map do |ci| - sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function + sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function, :table_name cast_type = lookup_cast_type(ci[:type]) new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options end @@ -192,8 +192,10 @@ def initialize_native_database_types end def column_definitions(table_name) - identifier = SQLServer::Utils.extract_identifiers(table_name) - database = "#{identifier.database_quoted}." if identifier.database_quoted + identifier = SQLServer::Utils.extract_identifiers(table_name) + database = "#{identifier.database_quoted}." if identifier.database_quoted + view_exists = schema_cache.view_exists?(table_name) + view_tblnm = table_name_or_views_table_name(table_name) if view_exists sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -247,6 +249,7 @@ def column_definitions(table_name) results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] + ci[:table_name] = view_tblnm || table_name ci[:type] = case ci[:type] when /^bit|image|text|ntext|datetime$/ ci[:type] @@ -264,11 +267,11 @@ def column_definitions(table_name) ci[:default_value], ci[:default_function] = begin default = ci[:default_value] - if default.nil? && schema_cache.view_exists?(table_name) + if default.nil? && view_exists default = select_value " SELECT c.COLUMN_DEFAULT FROM #{database}INFORMATION_SCHEMA.COLUMNS c - WHERE c.TABLE_NAME = '#{table_name_or_views_table_name(table_name)}' + WHERE c.TABLE_NAME = '#{view_tblnm}' AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA' end case default diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 5c875a40a..5cd9fc931 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -16,6 +16,10 @@ def sql_type_for_statement end end + def table_name + @sqlserver_options[:table_name] + end + def is_identity? @sqlserver_options[:is_identity] end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 0c230822d..161f50c4a 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -3,6 +3,11 @@ class ColumnTestSQLServer < ActiveRecord::TestCase + it '#table_name' do + assert SSTestDatatype.columns.all? { |c| c.table_name == 'sst_datatypes' } + assert SSTestCustomersView.columns.all? { |c| c.table_name == 'customers' } + end + describe 'ActiveRecord::ConnectionAdapters::SQLServer::Type' do let(:obj) { SSTestDatatype.new } From 4b0de7486d23b32daab862ce36b6206ea51fa1b2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Jan 2015 23:35:00 -0500 Subject: [PATCH 0278/1412] Update coerced_tests format a bit. --- test/cases/coerced_tests.rb | 27 ++++++--------------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9d870b6fd..eec652abc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -5,7 +5,6 @@ class SanitizeTest < ActiveRecord::TestCase coerce_tests! :test_sanitize_sql_like_example_use_case - def test_sanitize_sql_like_example_use_case_coerced searchable_post = Class.new(Post) do def self.search(term) @@ -27,7 +26,6 @@ class YamlSerializationTest < ActiveRecord::TestCase fixtures :authors 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 author = Author.select('authors.*, 5 as posts_count').first dumped = YAML.load(YAML.dump(author)) @@ -41,27 +39,15 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced module ActiveRecord module ConnectionAdapters - class TypeLookupTest < ActiveRecord::TestCase - - coerce_all_tests! # Just like PostgreSQLAdapter does. - - end - end -end + # Just like PostgreSQLAdapter does. + TypeLookupTest.coerce_all_tests! if defined?(TypeLookupTest) + # All sorts of errors due to how we test. Even setting ENV['RAILS_ENV'] to + # a value of 'default_env' will still show tests failing. Just ignoring all + # of them since we have no monkey in this circus. + MergeAndResolveDefaultUrlConfigTest.coerce_all_tests! if defined?(MergeAndResolveDefaultUrlConfigTest) -module ActiveRecord - module ConnectionAdapters - class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase - - # All sorts of errors due to how we test. Even setting ENV['RAILS_ENV'] to - # a value of 'default_env' will still show tests failing. Just ignoring all - # of them since we have no monkey in this circus. - # - coerce_all_tests! - - end end end @@ -209,4 +195,3 @@ class RelationTest < ActiveRecord::TestCase end - From 111b8be76d099194e3f0127eda716802a39ab3c2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Jan 2015 23:47:38 -0500 Subject: [PATCH 0279/1412] Do not run coerceable tests during SS only. No fixtures munging! --- Guardfile | 2 +- test/cases/coerced_tests.rb | 8 -------- test/support/rake_helpers.rb | 2 +- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/Guardfile b/Guardfile index 86f67988a..2f8a80820 100644 --- a/Guardfile +++ b/Guardfile @@ -13,7 +13,7 @@ guard :minitest, { autorun: false, include: ['lib', 'test', ar_lib, ar_test], test_folders: ['test'], - test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb", "*_tests.rb"] + test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb"] } do # Our project watchers. if ENV['TEST_FILES'] diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index eec652abc..8e961ce10 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -23,8 +23,6 @@ def self.search(term) require 'models/author' class YamlSerializationTest < ActiveRecord::TestCase - fixtures :authors - 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 author = Author.select('authors.*, 5 as posts_count').first @@ -72,8 +70,6 @@ def test_not_eq_with_array_parameter_coerced require 'models/company' class InheritanceTest < ActiveRecord::TestCase - fixtures :companies, :projects, :subscribers, :accounts, :vegetables - coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced con = Account.connection @@ -90,8 +86,6 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase - fixtures :authors, :developers, :projects, :comments, :posts - coerce_tests! :test_merge_options def test_merge_options_coerced Developer.where('salary = 80000').scoping do @@ -112,8 +106,6 @@ def test_merge_options_coerced require 'models/subscriber' class EachTest < ActiveRecord::TestCase - fixtures :posts, :subscribers - coerce_tests! :test_find_in_batches_should_quote_batch_order def test_find_in_batches_should_quote_batch_order_coerced c = Post.connection diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index 2de86da33..aceaaf7ac 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -32,7 +32,7 @@ def test_files return env_ar_test_files.unshift(SQLSERVER_HELPER) if env_ar_test_files return env_test_files if env_test_files if ENV['ONLY_SQLSERVER'] - sqlserver_cases + [SQLSERVER_COERCED] + sqlserver_cases elsif ENV['ONLY_ACTIVERECORD'] [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED]) else From 1c35043cd09189fc86143c28b95d224a3aa65968 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 08:12:41 -0500 Subject: [PATCH 0280/1412] Simple scratchpad test for debugging. --- test/cases/scratchpad_test_sqlserver.rb | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 test/cases/scratchpad_test_sqlserver.rb diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb new file mode 100644 index 000000000..3cc9010e6 --- /dev/null +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -0,0 +1,9 @@ +require 'cases/helper_sqlserver' + +class ScratchpadTestSQLServer < ActiveRecord::TestCase + + it 'helps debug things' do + # + end + +end From f264abd5cb4a24d91bd51e2c68b1dfd6b64e226e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 08:40:44 -0500 Subject: [PATCH 0281/1412] Do not ues Regexp for ID insert guards. For example: ```ruby c = Company.connection c.with_identity_insert_enabled('companies') do c.insert "INSERT INTO companies (id, name) VALUES(100, N'Totally happening!')" end ``` --- CHANGELOG.md | 2 + .../sqlserver/database_statements.rb | 41 ++++++++++++++++--- .../sqlserver/schema_statements.rb | 16 +------- test/cases/adapter_test_sqlserver.rb | 10 ++--- test/cases/coerced_tests.rb | 8 ++++ 5 files changed, 50 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e60430a5..5f8f3fac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * Simple 2012 schmea addition and extensive column/type_cast object tests. * Follow Rails convention and remove varying character default limits. * The `cs_equality_operator` is now s class configuration property only. +* The `with_identity_insert_enabled(table_name)` is now public. #### Deprecated @@ -38,6 +39,7 @@ * edition * Removed tests for old issue #164. Handled by core types now. * The `activity_stats` method. Please put this in a gem if needed. +* We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f82fc2665..aa205a657 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -16,9 +16,7 @@ def execute(sql, name = nil) end def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil - with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) } - elsif update_sql?(sql) + if update_sql?(sql) sql = strip_ident_from_update(sql) do_exec_query(sql, name, binds) else @@ -26,9 +24,14 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) end end - # The abstract adapter ignores the last two parameters also def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil) - exec_query sql, name, binds, insert: true + id_insert = binds_have_identity_column?(binds) + id_table = table_name_from_binds(binds) if id_insert + if id_insert && id_table + with_identity_insert_enabled(id_table) { exec_query(sql, name, binds) } + else + exec_query(sql, name, binds) + end end def exec_delete(sql, name, binds) @@ -110,6 +113,14 @@ def execute_procedure(proc_name, *variables) end end + def with_identity_insert_enabled(table_name) + table_name = quote_table_name(table_name_or_views_table_name(table_name)) + set_identity_insert(table_name, true) + yield + ensure + set_identity_insert(table_name, false) + end + def use_database(database = nil) return if sqlserver_azure? name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted @@ -269,6 +280,26 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds) # === SQLServer Specific ======================================== # + def binds_have_identity_column?(binds) + binds.any? do |column_value| + column, value = column_value + SQLServerColumn === column && column.is_identity? + end + end + + def table_name_from_binds(binds) + binds.detect { |column_value| + column, value = column_value + SQLServerColumn === column + }.try(:first).try(:table_name) + end + + def set_identity_insert(table_name, enable = true) + do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" + rescue Exception + raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" + end + def valid_isolation_levels ['READ COMMITTED', 'READ UNCOMMITTED', 'REPEATABLE READ', 'SERIALIZABLE', 'SNAPSHOT'] end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a2aed6be5..a0b14b48d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -156,6 +156,7 @@ def views tables('VIEW') end + protected # === SQLServer Specific ======================================== # @@ -421,21 +422,6 @@ def update_sql?(sql) !(sql =~ /^\s*(UPDATE|EXEC sp_executesql N'UPDATE)/i).nil? end - def with_identity_insert_enabled(table_name) - table_name = quote_table_name(table_name_or_views_table_name(table_name)) - set_identity_insert(table_name, true) - yield - ensure - set_identity_insert(table_name, false) - end - - def set_identity_insert(table_name, enable = true) - sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" - do_execute sql, 'SCHEMA' - rescue Exception - raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" - end - def identity_column(table_name) schema_cache.columns(table_name).find(&:is_identity?) end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 9bc080f7f..8e99a66ee 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -446,14 +446,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase # Doing identity inserts - let(:view_insert_sql) { "INSERT INTO [sst_customers_view] ([id],[name],[balance]) VALUES (420,'Microsoft',0)" } - - it 'respond true/tablename to #query_requires_identity_insert?' do - assert_equal '[sst_customers_view]', connection.send(:query_requires_identity_insert?, view_insert_sql) - end - it 'be able to do an identity insert' do - assert_nothing_raised { connection.execute( view_insert_sql) } + customer = SSTestCustomersView.new + customer.id = 420 + customer.save! assert SSTestCustomersView.find(420) end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8e961ce10..d85c3cfbd 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -70,6 +70,14 @@ def test_not_eq_with_array_parameter_coerced require 'models/company' class InheritanceTest < ActiveRecord::TestCase + coerce_tests! :test_a_bad_type_column + def test_a_bad_type_column_coerced + Company.connection.with_identity_insert_enabled('companies') do + Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')" + end + assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } + end + coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced con = Account.connection From 8bfd152245cdb81d9a54929c94d03e2982fdfbcf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 12:07:42 -0500 Subject: [PATCH 0282/1412] TEST ORDER IS NOW FIXED. --- activerecord-sqlserver-adapter.gemspec | 1 + test/support/rake_helpers.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 66821aa92..80ec72d54 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,6 +18,7 @@ Gem::Specification.new do |spec| spec.add_dependency 'activerecord', '~> 4.2.0' spec.add_development_dependency 'bundler' spec.add_development_dependency 'guard-minitest' + spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. spec.add_development_dependency 'minitest-focus' spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index aceaaf7ac..58ff2303c 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -22,16 +22,16 @@ def sqlserver_cases def ar_cases @ar_cases ||= begin - all_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb") - adapters_cases = Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/adapters/**/*_test.rb") - (all_cases - adapters_cases).sort + Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject{ |x| x =~ /\/adapters\// }.sort end end def test_files - return env_ar_test_files.unshift(SQLSERVER_HELPER) if env_ar_test_files - return env_test_files if env_test_files - if ENV['ONLY_SQLSERVER'] + if env_ar_test_files + [SQLSERVER_HELPER] + env_ar_test_files + elsif env_test_files + env_test_files + elsif ENV['ONLY_SQLSERVER'] sqlserver_cases elsif ENV['ONLY_ACTIVERECORD'] [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED]) From ba63c413be2ab5b83a92df2bcc2eb29b20ca5274 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 14:44:05 -0500 Subject: [PATCH 0283/1412] Coerce tests. ActiveRecord::AdapterTest#test_update_prepared_statement BelongsToAssociationsTest#test_belongs_to_does_not_use_order_by BelongsToAssociationsTest#test_belongs_to_with_primary_key_joins_on_correct_column EagerAssociationTest#test_0013_including association based on sql condition and no database column EagerAssociationTest#test_count_with_include: AttributeMethodsTest#test_typecast_attribute_from_select_to_false AttributeMethodsTest#test_typecast_attribute_from_select_to_true BasicsTest#test_column_names_are_escaped: 29 failures, 17 errors --- test/cases/coerced_tests.rb | 250 +++++++++++++++++++++++++----------- 1 file changed, 177 insertions(+), 73 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d85c3cfbd..c00b932e2 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1,40 +1,73 @@ require 'cases/helper_sqlserver' -require 'models/post' -class SanitizeTest < ActiveRecord::TestCase +module ActiveRecord + class AdapterTest < ActiveRecord::TestCase + + # As far as I can tell, SQL Server does not support null bytes in strings. + coerce_tests! :test_update_prepared_statement - coerce_tests! :test_sanitize_sql_like_example_use_case - def test_sanitize_sql_like_example_use_case_coerced - searchable_post = Class.new(Post) do - def self.search(term) - where("title LIKE ?", sanitize_sql_like(term, '!')) - end - end - assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do - searchable_post.search("20% _reduction_!").to_a - end + end +end + + + + +require 'models/topic' +class AttributeMethodsTest < ActiveRecord::TestCase + + coerce_tests! :test_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 + assert !topic.is_test? + end + + coerce_tests! :test_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 + assert topic.is_test? 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 - author = Author.select('authors.*, 5 as posts_count').first - dumped = YAML.load(YAML.dump(author)) - assert_equal 5, author.posts_count - assert_equal 5, dumped.posts_count +class BasicsTest < ActiveRecord::TestCase + + coerce_tests! :test_column_names_are_escaped + def test_column_names_are_escaped_coerced + conn = ActiveRecord::Base.connection + classname = conn.class.name[/[^:]*$/] + badchar = "'" + quoted = conn.quote_column_name "foo#{badchar}bar" + assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted) end end + +class BelongsToAssociationsTest < ActiveRecord::TestCase + + # Since @client.firm is a single first/top, and we use FETCH the order clause is used. + coerce_tests! :test_belongs_to_does_not_use_order_by + + coerce_tests! :test_belongs_to_with_primary_key_joins_on_correct_column + def test_belongs_to_with_primary_key_joins_on_correct_column_coerced + sql = Client.joins(:firm_with_primary_key).to_sql + assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql) + assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql) + end + +end + + + + module ActiveRecord module ConnectionAdapters @@ -51,22 +84,84 @@ module ConnectionAdapters + require 'models/post' -module ActiveRecord - class WhereChainTest < ActiveRecord::TestCase +require 'models/subscriber' +class EachTest < ActiveRecord::TestCase - coerce_tests! :test_not_eq_with_array_parameter - def test_not_eq_with_array_parameter_coerced - expected = Arel::Nodes::Not.new("title = N'hello'") - relation = Post.where.not(['title = ?', 'hello']) - assert_equal([expected], relation.where_values) + coerce_tests! :test_find_in_batches_should_quote_batch_order + def test_find_in_batches_should_quote_batch_order_coerced + c = Post.connection + assert_sql(/ORDER BY \[posts\]\.\[id\]/) do + Post.find_in_batches(:batch_size => 1) do |batch| + assert_kind_of Array, batch + assert_kind_of Post, batch.first + end + end + end + +end + + + + +require 'models/owner' +class Owner < ActiveRecord::Base + scope :including_last_pet, -> { + select('owners.*, (select TOP (1) p.pet_id from pets p where p.owner_id = owners.owner_id order by p.name desc ) as last_pet_id'). + includes(:last_pet) + } +end +class EagerAssociationTest < ActiveRecord::TestCase + + # Use LEN() vs length() function. + coerce_tests! :test_count_with_include + def test_count_with_include_coerced + assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count + end + + # Use TOP (1) in scope vs limit 1. + coerce_tests! %r{including association based on sql condition and no database column} + it "including association based on sql condition and no database column coerced" do + assert_equal pets(:parrot), Owner.including_last_pet.first.last_pet + end + +end + + + + +require 'models/topic' +class FinderTest < ActiveRecord::TestCase + + coerce_tests! %r{doesn't have implicit ordering}, + :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH. + + coerce_tests! :test_exists_does_not_select_columns_without_alias + def test_exists_does_not_select_columns_without_alias_coerced + assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY/i) do + Topic.exists? end + end + + coerce_tests! :test_string_sanitation + def test_string_sanitation_coerced + assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") + assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") + end + coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit + def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced + assert_sql(/OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY/) { Topic.take(3).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY/) { Topic.first(2).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY/) { Topic.last(5).entries } end + end + require 'models/company' class InheritanceTest < ActiveRecord::TestCase @@ -90,6 +185,22 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced + +module ActiveRecord + class Migration + class ChangeSchemaTest < ActiveRecord::TestCase + + # We test these. + coerce_tests! :test_create_table_with_bigint, + :test_create_table_with_defaults + + end + end +end + + + + require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase @@ -110,24 +221,6 @@ def test_merge_options_coerced -require 'models/post' -require 'models/subscriber' -class EachTest < ActiveRecord::TestCase - - coerce_tests! :test_find_in_batches_should_quote_batch_order - def test_find_in_batches_should_quote_batch_order_coerced - c = Post.connection - assert_sql(/ORDER BY \[posts\]\.\[id\]/) do - Post.find_in_batches(:batch_size => 1) do |batch| - assert_kind_of Array, batch - assert_kind_of Post, batch.first - end - end - end - -end - - require 'models/topic' module ActiveRecord @@ -146,52 +239,63 @@ def test_registering_new_handlers_coerced -module ActiveRecord - class Migration - class ChangeSchemaTest < ActiveRecord::TestCase - coerce_tests! :test_create_table_with_bigint, - :test_create_table_with_defaults # We test these. +class RelationTest < ActiveRecord::TestCase + + coerce_tests! %r{doesn't have implicit ordering} # We have implicit ordering, via FETCH. - end - end end -require 'models/topic' -class FinderTest < ActiveRecord::TestCase - coerce_tests! %r{doesn't have implicit ordering}, - :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH. +require 'models/post' +class SanitizeTest < ActiveRecord::TestCase - coerce_tests! :test_exists_does_not_select_columns_without_alias - def test_exists_does_not_select_columns_without_alias_coerced - assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY/i) do - Topic.exists? + coerce_tests! :test_sanitize_sql_like_example_use_case + def test_sanitize_sql_like_example_use_case_coerced + searchable_post = Class.new(Post) do + def self.search(term) + where("title LIKE ?", sanitize_sql_like(term, '!')) + end + end + assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do + searchable_post.search("20% _reduction_!").to_a end end - coerce_tests! :test_string_sanitation - def test_string_sanitation_coerced - assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") - assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") - end +end + - coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit - def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced - assert_sql(/OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY/) { Topic.take(3).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY/) { Topic.first(2).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY/) { Topic.last(5).entries } - end + +require 'models/post' +module ActiveRecord + class WhereChainTest < ActiveRecord::TestCase + + coerce_tests! :test_not_eq_with_array_parameter + def test_not_eq_with_array_parameter_coerced + expected = Arel::Nodes::Not.new("title = N'hello'") + relation = Post.where.not(['title = ?', 'hello']) + assert_equal([expected], relation.where_values) + end + + end end -class RelationTest < ActiveRecord::TestCase - coerce_tests! %r{doesn't have implicit ordering} # We have implicit ordering, via FETCH. +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 + author = Author.select('authors.*, 5 as posts_count').first + dumped = YAML.load(YAML.dump(author)) + assert_equal 5, author.posts_count + assert_equal 5, dumped.posts_count + end end From 262cc60c8b51f1bdb797a474aca779fbfd6a9d67 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 14:52:09 -0500 Subject: [PATCH 0284/1412] Aliased `ActiveRecord::Type::SQLServer` to `ActiveRecord::ConnectionAdapters::SQLServer::Type` --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver/type.rb | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f8f3fac0..871592a37 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Added * New `ActiveRecord::Type` objects. See `active_record/connection_adapters/sqlserver/type` dir. +* Aliased `ActiveRecord::Type::SQLServer` to `ActiveRecord::ConnectionAdapters::SQLServer::Type` * New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. * Support for most all SQL Server types in schema statements and dumping. diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 380f4aab3..34aadcf48 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -37,3 +37,9 @@ require 'active_record/connection_adapters/sqlserver/type/varbinary_max.rb' # Other Data Types require 'active_record/connection_adapters/sqlserver/type/uuid.rb' + +module ActiveRecord + module Type + SQLServer = ConnectionAdapters::SQLServer::Type + end +end From 26829d5cfe8d855d2a3428cd71ef27390aab620b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 15:42:37 -0500 Subject: [PATCH 0285/1412] More coerced tests. BasicsTest#test_numeric_fields BasicsTest#test_respect_internal_encoding ActiveRecord::BindParameterTest#test_binds_are_logged ActiveRecord::BindParameterTest#test_binds_are_logged_after_type_cast CalculationsTest#test_limit_is_kept CalculationsTest#test_limit_with_offset_is_kept CalculationsTest#test_pluck_with_includes_limit_and_empty_result 25 failures, 15 errors, 2 skips --- test/cases/coerced_tests.rb | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c00b932e2..fbd1dc141 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -46,6 +46,15 @@ def test_column_names_are_escaped_coerced assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted) end + # This test has a few problems. First, it would require that we use + # the `Type::SQLServer::BigInteger.new(limit: 8)` for the `world_population` + # attribute. Second, since we allow the DB to win at casting for TinyTDS, + # it always comes back as a BigDecimal. + coerce_tests! :test_numeric_fields + + # Just like PostgreSQLAdapter does. + coerce_tests! :test_respect_internal_encoding + end @@ -68,6 +77,47 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced +module ActiveRecord + class BindParameterTest < ActiveRecord::TestCase + + # Never finds `sql` since we use `EXEC sp_executesql` wrappers. + coerce_tests! :test_binds_are_logged, + :test_binds_are_logged_after_type_cast + + end +end + + + + +class CalculationsTest < ActiveRecord::TestCase + + # Are decimal, not integer. + coerce_tests! :test_should_return_decimal_average_of_integer_field + def test_should_return_decimal_average_of_integer_field_coerced + value = Account.average(:id) + assert_equal BigDecimal('3.5').to_s, BigDecimal(value).to_s + end + + coerce_tests! :test_limit_is_kept + def test_limit_is_kept_coerced + queries = assert_sql { Account.limit(1).count } + assert_equal 1, queries.length + queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY} + end + + coerce_tests! :test_limit_with_offset_is_kept + def test_limit_with_offset_is_kept_coerced + queries = assert_sql { Account.limit(1).offset(1).count } + assert_equal 1, queries.length + queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY} + end + +end + + + + module ActiveRecord module ConnectionAdapters From 1a1771a6571f0af91745ceb5370bb9db4c6d806b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 16:59:49 -0500 Subject: [PATCH 0286/1412] More coercions. --- test/cases/coerced_tests.rb | 16 ++++++++++++++-- test/support/coerceable_test_sqlserver.rb | 8 +++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index fbd1dc141..97d728636 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -43,7 +43,7 @@ def test_column_names_are_escaped_coerced classname = conn.class.name[/[^:]*$/] badchar = "'" quoted = conn.quote_column_name "foo#{badchar}bar" - assert_equal("#{badchar}foo#{badchar * 2}bar#{badchar}", quoted) + assert_equal "[foo'bar]", quoted end # This test has a few problems. First, it would require that we use @@ -96,7 +96,7 @@ class CalculationsTest < ActiveRecord::TestCase coerce_tests! :test_should_return_decimal_average_of_integer_field def test_should_return_decimal_average_of_integer_field_coerced value = Account.average(:id) - assert_equal BigDecimal('3.5').to_s, BigDecimal(value).to_s + assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s end coerce_tests! :test_limit_is_kept @@ -118,6 +118,18 @@ def test_limit_with_offset_is_kept_coerced +class CoreTest < ActiveRecord::TestCase + + # I think fixtures are useing the wrong time zone and the `:first` + # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is + # getting local EST time for me and set to "09:28:00.0000000". + coerce_tests! :test_pretty_print_persisted + +end + + + + module ActiveRecord module ConnectionAdapters diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 8913bae17..dc6171ca1 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -31,9 +31,11 @@ def coerce_all_tests! private def coerced_test_warning(method) - method = instance_methods(false).detect { |m| m =~ method } if method.is_a?(Regexp) - result = undef_method(method) if method && method_defined?(method) - STDOUT.puts "Info: Undefined coerced test: #{self.name}##{method}" unless result.blank? + method = instance_methods(false).select { |m| m =~ method } if method.is_a?(Regexp) + Array(method).each do |m| + result = undef_method(m) if m && method_defined?(m) + STDOUT.puts "Info: Undefined coerced test: #{self.name}##{m}" unless result.blank? + end end end From 0449636e5798e0a37e9a0f5a54459ebb2e2e6ffd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 19:49:37 -0500 Subject: [PATCH 0287/1412] Allow FETCH with 0 ROWS. Thanks! http://sqlmag.com/t-sql/offsetfetch-part-1 --- lib/arel/visitors/sqlserver.rb | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index e8a832133..7d051c665 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -5,6 +5,7 @@ class SQLServer < Arel::Visitors::ToSql OFFSET = " OFFSET " ROWS = " ROWS" FETCH = " FETCH NEXT " + FETCH0 = " FETCH FIRST (SELECT 0) " ROWS_ONLY = " ROWS ONLY" @@ -45,9 +46,14 @@ def visit_Arel_Nodes_Offset o, collector end def visit_Arel_Nodes_Limit o, collector - collector << FETCH - visit o.expr, collector - collector << ROWS_ONLY + if node_value(o) == 0 + collector << FETCH0 + collector << ROWS_ONLY + else + collector << FETCH + visit o.expr, collector + collector << ROWS_ONLY + end end def visit_Arel_Nodes_SelectStatement o, collector @@ -115,13 +121,6 @@ def visit_Orders_And_Let_Fetch_Happen o, collector end def visit_Make_Fetch_Happen o, collector - if o.limit - value = case o.limit.expr - when Numeric then o.limit.expr - when Arel::Nodes::Unary then o.limit.expr.expr - end - o.limit = nil if value == 0 - end o.offset = Nodes::Offset.new(0) if o.limit && !o.offset collector = visit o.offset, collector if o.offset collector = visit o.limit, collector if o.limit @@ -130,6 +129,14 @@ def visit_Make_Fetch_Happen o, collector # SQLServer Helpers + def node_value(node) + case node.expr + when NilClass then nil + when Numeric then node.expr + when Arel::Nodes::Unary then node.expr.expr + end + end + def select_statement_lock? @select_statement && @select_statement.lock end From 0e6341735e61bb511f929b750ac4406dd930a556 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 23 Jan 2015 20:19:04 -0500 Subject: [PATCH 0288/1412] Unhack our bind executor forcing #to_i and casting. SerializedAttributeTest#test_serialize_attribute_can_be_serialized_in_an_integer_column: NoMethodError: undefined method `to_i' for ["life"]:Array --- .../connection_adapters/sqlserver/database_statements.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index aa205a657..7097ff4da 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -326,11 +326,6 @@ def do_exec_query(sql, name, binds, options = {}) next if ar_column && column.sql_type == 'timestamp' v = value names_and_types << if ar_column - if column.is_integer? && value.present? - v = value.to_i - # Reset the casted value to the bind as required by Rails 4.1 - binds[index] = [column, v] - end "@#{index} #{column.sql_type_for_statement}" elsif column.acts_like?(:string) "@#{index} nvarchar(max)" From fa593d265c5fc4e20c8f3694e14e2b78c0f3a421 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 12:19:37 -0500 Subject: [PATCH 0289/1412] Small refactor when making fetch happen. May extend further. --- lib/arel/visitors/sqlserver.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 7d051c665..efffb2453 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -103,11 +103,7 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} end def visit_Orders_And_Let_Fetch_Happen o, collector - if (o.limit || o.offset) && o.orders.empty? - table = table_From_Statement o - column = primary_Key_From_Table(table) - o.orders = [column.asc] - end + make_Fetch_Possible_And_Deterministic o unless o.orders.empty? collector << SPACE collector << ORDER_BY @@ -141,6 +137,17 @@ def select_statement_lock? @select_statement && @select_statement.lock end + def make_Fetch_Possible_And_Deterministic o + return if o.limit.nil? && o.offset.nil? + t = table_From_Statement o + pk = primary_Key_From_Table t + return unless pk + if o.orders.empty? + # Prefer deterministic vs a simple `(SELECT NULL)` expr. + o.orders = [pk.asc] + end + end + def table_From_Statement o core = o.cores.first if Arel::Table === core.from @@ -153,6 +160,7 @@ def table_From_Statement o end def primary_Key_From_Table t + return unless t return t.primary_key if t.primary_key if engine_pk = t.engine.primary_key pk = t.engine.arel_table[engine_pk] From a347f556a312bb4d56a9c8ebfcc2f28cc135641b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 16:15:35 -0500 Subject: [PATCH 0290/1412] Remove all auto recoonect & SQL retry logic. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was getting too complicated and I believe it stood in the way of passing a few tests along with the strong likelyhood of causing other errors. For example, I found out that using the super strong method of going into single DB user mode caused potential DB lock outs and other grief when used with our connection pool #reset! hook. I would not be surprised to find out that we played a hand in causing users grief in odd to find places when we tried doing to much for them. Lets just allow ActiveRecord and the connection pool to do what it needs and if we are no longer #active? then so be it. 🏃🔥 So passes Autoreconnectathor, son of Notourproblemalion. --- CHANGELOG.md | 1 + README.md | 9 --- .../sqlserver/database_statements.rb | 24 +++----- .../connection_adapters/sqlserver/errors.rb | 29 --------- .../connection_adapters/sqlserver_adapter.rb | 61 ++----------------- test/cases/connection_test_sqlserver.rb | 47 -------------- test/cases/helper_sqlserver.rb | 8 --- 7 files changed, 16 insertions(+), 163 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 871592a37..954c1f9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,7 @@ * Removed tests for old issue #164. Handled by core types now. * The `activity_stats` method. Please put this in a gem if needed. * We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. +* All auto reconnect and SQL retry logic. Got too complicated and stood in the way of AR's pool. Speed boost too. #### Fixed diff --git a/README.md b/README.md index 8ee9474d5..862fa5459 100644 --- a/README.md +++ b/README.md @@ -77,15 +77,6 @@ ActiveRecord::Base.table_name_prefix = 'dbo.' ``` -#### Auto Connecting - -By default the adapter will auto connect to lost DB connections. For every query it will retry at intervals of 2, 4, 8, 16 and 32 seconds. During each retry it will callback out to ActiveRecord::Base.did_retry_sqlserver_connection(connection,count). When all retries fail, it will callback to ActiveRecord::Base.did_lose_sqlserver_connection(connection). Both implementations of these methods are to write to the rails logger, however, they make great override points for notifications like Hoptoad. If you want to disable automatic reconnections use the following in an initializer. - -```ruby -ActiveRecord::ConnectionAdapters::SQLServerAdapter.auto_connect = false -``` - - #### Configure Connection & App Name We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just override the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. Also, TinyTDS supports an application name when it logs into SQL Server. This can be used to identify the connection in SQL Server's activity monitor. By default it will use the `appname` from your database.yml file or a lowercased version of your Rails::Application name. It is now possible to define a `configure_application_name` method that can give you per instance details. Below shows how you might use this to get the process id and thread id of the current connection. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7097ff4da..c491520df 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -53,21 +53,21 @@ def begin_db_transaction end def commit_db_transaction - disable_auto_reconnect { do_execute 'COMMIT TRANSACTION' } + do_execute 'COMMIT TRANSACTION' end def rollback_db_transaction - do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' + do_execute 'ROLLBACK TRANSACTION' end include Savepoints def create_savepoint(name = current_savepoint_name) - disable_auto_reconnect { do_execute "SAVE TRANSACTION #{name}" } + do_execute "SAVE TRANSACTION #{name}" end def rollback_to_savepoint(name = current_savepoint_name) - disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{name}" } + do_execute "ROLLBACK TRANSACTION #{name}" end def release_savepoint(name = current_savepoint_name) @@ -307,9 +307,7 @@ def valid_isolation_levels # === SQLServer Specific (Executing) ============================ # def do_execute(sql, name = 'SQL') - log(sql, name) do - with_sqlserver_error_handling { raw_connection_do(sql) } - end + log(sql, name) { raw_connection_do(sql) } end def do_exec_query(sql, name, binds, options = {}) @@ -375,13 +373,11 @@ def _raw_select(sql, options = {}) end def raw_connection_run(sql) - with_sqlserver_error_handling do - case @connection_options[:mode] - when :dblib - @connection.execute(sql) - when :odbc - block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql) - end + case @connection_options[:mode] + when :dblib + @connection.execute(sql) + when :odbc + block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql) end end diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 2e1a41be2..70f64cefc 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -1,36 +1,7 @@ module ActiveRecord - class LostConnection < WrappedDatabaseException - end - class DeadlockVictim < WrappedDatabaseException end - module ConnectionAdapters - module SQLServer - module Errors - - LOST_CONNECTION_EXCEPTIONS = { - dblib: ['TinyTds::Error'], - odbc: ['ODBC::Error'] - }.freeze - - LOST_CONNECTION_MESSAGES = { - dblib: [/closed connection/, /dead or not enabled/, /server failed/i], - odbc: [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i] - }.freeze - - def lost_connection_exceptions - exceptions = LOST_CONNECTION_EXCEPTIONS[@connection_options[:mode]] - @lost_connection_exceptions ||= exceptions ? exceptions.map { |e| e.constantize rescue nil }.compact : [] - end - - def lost_connection_messages - LOST_CONNECTION_MESSAGES[@connection_options[:mode]] - end - - end - end - end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 21845ad37..b540a74f5 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -30,14 +30,12 @@ class SQLServerAdapter < AbstractAdapter SQLServer::DatabaseStatements, SQLServer::Showplan, SQLServer::SchemaStatements, - SQLServer::DatabaseLimits, - SQLServer::Errors + SQLServer::DatabaseLimits ADAPTER_NAME = 'SQLServer'.freeze attr_reader :spid - cattr_accessor :auto_connect, :auto_connect_duration, instance_accessor: false cattr_accessor :cs_equality_operator, instance_accessor: false cattr_accessor :lowercase_schema_reflection, :showplan_option @@ -115,13 +113,9 @@ def disable_referential_integrity def active? return false unless @connection - case @connection_options[:mode] - when :dblib - return @connection.active? - end - raw_connection_do('SELECT 1') + raw_connection_do 'SELECT 1' true - rescue *lost_connection_exceptions + rescue TinyTds::Error, ODBC::Error false end @@ -144,7 +138,8 @@ def disconnect! end def reset! - remove_database_connections_and_rollback {} + reset_transaction + do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end # === Abstract Adapter (Misc Support) =========================== # @@ -176,14 +171,6 @@ def inspect "#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>" end - def auto_connect - self.class.auto_connect.is_a?(FalseClass) ? false : true - end - - def auto_connect_duration - self.class.auto_connect_duration ||= 10 - end - protected @@ -249,8 +236,6 @@ def translate_exception(e, message) InvalidForeignKey.new(message, e) when /has been chosen as the deadlock victim/i DeadlockVictim.new(message, e) - when *lost_connection_messages - LostConnection.new(message, e) else super end @@ -268,8 +253,6 @@ def connect end @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first configure_connection - rescue - raise unless @auto_connecting end def dblib_connect(config) @@ -363,40 +346,6 @@ def remove_database_connections_and_rollback(database = nil) end if block_given? end - def with_sqlserver_error_handling - yield - rescue Exception => e - case translate_exception(e, e.message) - when LostConnection then retry if auto_reconnected? - end - raise - end - - def disable_auto_reconnect - old_auto_connect, self.class.auto_connect = self.class.auto_connect, false - yield - ensure - self.class.auto_connect = old_auto_connect - end - - def auto_reconnected? - return false unless auto_connect - @auto_connecting = true - count = 0 - while count <= (auto_connect_duration / 2) - disconnect! - reconnect! - ActiveRecord::Base.did_retry_sqlserver_connection(self, count) - return true if active? - sleep 2**count - count += 1 - end - ActiveRecord::Base.did_lose_sqlserver_connection(self) - false - ensure - @auto_connecting = false - end - end end end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 5b99b101c..facadcb63 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -109,53 +109,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert connection.active? end - it 'auto reconnect when setting is on' do - with_auto_connect(true) do - disconnect_raw_connection! - assert_nothing_raised() { Topic.count } - assert connection.active? - end - end - - it 'not auto reconnect when setting is off' do - with_auto_connect(false) do - disconnect_raw_connection! - assert_raise(ActiveRecord::LostConnection) { Topic.count } - end - end - - it 'not auto reconnect on commit transaction' do - disconnect_raw_connection! - assert_raise(ActiveRecord::LostConnection) { connection.commit_db_transaction } - end - - it 'gracefully ignore lost connections on rollback transaction' do - disconnect_raw_connection! - assert_nothing_raised { connection.rollback_db_transaction } - end - - describe 'testing #disable_auto_reconnect' do - - it 'when auto reconnect setting is on' do - with_auto_connect(true) do - connection.send(:disable_auto_reconnect) do - assert !connection.class.auto_connect - end - assert connection.class.auto_connect - end - end - - it 'when auto reconnect setting is off' do - with_auto_connect(false) do - connection.send(:disable_auto_reconnect) do - assert !connection.class.auto_connect - end - assert !connection.class.auto_connect - end - end - - end - describe 'with a deadlock victim exception 1205' do describe 'outside a transaction' do diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index d191047a6..af2665e61 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -28,14 +28,6 @@ def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end - def with_auto_connect(boolean) - existing = connection.auto_connect - connection.class.auto_connect = boolean - yield - ensure - connection.class.auto_connect = existing - end - end end From 75f7ef0e1c471a03893a7a620ec22e3a77a03c54 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 18:52:27 -0500 Subject: [PATCH 0291/1412] Different format for WITH(table optoins). --- CHANGELOG.md | 2 +- lib/arel/visitors/sqlserver.rb | 2 +- test/cases/pessimistic_locking_test_sqlserver.rb | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 954c1f9e1..8ed0b557c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -45,7 +45,7 @@ #### Fixed -* Default lock is now "WITH (UPDLOCK)". Fixes #368 +* Default lock is now "WITH(UPDLOCK)". Fixes #368 #### Security diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index efffb2453..4ac6e5ccf 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -34,7 +34,7 @@ def visit_Arel_Nodes_UpdateStatement(o, a) 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 o.expr.to_s =~ /FOR UPDATE/ collector << SPACE visit o.expr, collector end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index fafe6d092..0513b7689 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -12,7 +12,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it 'uses with updlock by default' do - assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH \(UPDLOCK\)| do + assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do Person.lock(true).to_a.must_equal Person.all.to_a end end @@ -73,8 +73,8 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it 'copes with eager loading un-locked paginated' do - eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH \(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH \(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/ - loader_sql = /SELECT.*FROM \[people\] WITH \(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ + eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/ + loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ assert_sql(eager_ids_sql, loader_sql) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a people[0].first_name.must_equal 'Thing_10' From d885a9eaad8e1411b7cba725307756cdac795e70 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 18:53:57 -0500 Subject: [PATCH 0292/1412] Use ActiveRecord tranasaction interface vs our own `run_with_isolation_level`. --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 31 +++++------ .../sqlserver/transaction.rb | 52 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 5 ++ test/cases/adapter_test_sqlserver.rb | 50 ------------------ test/cases/coerced_tests.rb | 23 ++++++++ test/cases/transaction_test_sqlserver.rb | 17 ++++++ 7 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/transaction.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed0b557c..5dad6b4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ * Follow Rails convention and remove varying character default limits. * The `cs_equality_operator` is now s class configuration property only. * The `with_identity_insert_enabled(table_name)` is now public. +* Use ActiveRecord tranasaction interface vs our own `run_with_isolation_level`. #### Deprecated diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index c491520df..335159f45 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -52,6 +52,20 @@ def begin_db_transaction do_execute 'BEGIN TRANSACTION' end + def transaction_isolation_levels + super.merge snapshot: "SNAPSHOT" + end + + def begin_isolated_db_transaction(isolation) + set_transaction_isolation_level transaction_isolation_levels.fetch(isolation) + begin_db_transaction + end + + def set_transaction_isolation_level(isolation_level) + do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}" + begin_db_transaction + end + def commit_db_transaction do_execute 'COMMIT TRANSACTION' end @@ -175,19 +189,6 @@ def user_options_language end end - def run_with_isolation_level(isolation_level) - unless valid_isolation_levels.include?(isolation_level.upcase) - raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." - end - initial_isolation_level = user_options_isolation_level || 'READ COMMITTED' - do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}" - begin - yield - ensure - do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}" - end if block_given? - end - def newid_function select_value 'SELECT NEWID()' end @@ -300,10 +301,6 @@ def set_identity_insert(table_name, enable = true) raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end - def valid_isolation_levels - ['READ COMMITTED', 'READ UNCOMMITTED', 'REPEATABLE READ', 'SERIALIZABLE', 'SNAPSHOT'] - end - # === SQLServer Specific (Executing) ============================ # def do_execute(sql, name = 'SQL') diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb new file mode 100644 index 000000000..bdd93de37 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -0,0 +1,52 @@ +require 'active_record/connection_adapters/abstract/transaction' + +module ActiveRecord + module ConnectionAdapters + + module SQLServerTransaction + + private + + def sqlserver? + connection.respond_to?(:sqlserver?) && connection.sqlserver? + end + + def current_isolation_level + return unless sqlserver? + level = connection.user_options_isolation_level + level.blank? ? 'READ COMMITTED' : level.upcase + end + + end + + Transaction.include SQLServerTransaction + + module SQLServerRealTransaction + + attr_reader :starting_isolation_level + + def initialize(connection, options) + @connection = connection + @starting_isolation_level = current_isolation_level if options[:isolation] + super + end + + def commit + super + reset_starting_isolation_level + end + + private + + def reset_starting_isolation_level + if sqlserver? && starting_isolation_level + connection.set_transaction_isolation_level(starting_isolation_level) + end + end + + end + + RealTransaction.include SQLServerRealTransaction + + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b540a74f5..1b0999df1 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -10,6 +10,7 @@ require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' require 'active_record/connection_adapters/sqlserver/database_statements' +require 'active_record/connection_adapters/sqlserver/transaction' require 'active_record/connection_adapters/sqlserver/errors' require 'active_record/connection_adapters/sqlserver/schema_cache' require 'active_record/connection_adapters/sqlserver/schema_creation' @@ -102,6 +103,10 @@ def supports_explain? true end + def supports_transaction_isolation? + true + end + def disable_referential_integrity do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" yield diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 8e99a66ee..ba9e0a915 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -254,56 +254,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'read committed', user_options[:isolation_level] end - describe '#run_with_isolation_level' do - - let(:task1) { tasks(:first_task) } - let(:task2) { tasks(:another_task) } - - before do - assert task1, 'Tasks :first_task should be in AR fixtures' - assert task2, 'Tasks :another_task should be in AR fixtures' - good_isolation_level = connection.user_options_isolation_level.blank? || connection.user_options_isolation_level =~ /read committed/i - assert good_isolation_level, "User isolation level is not at a happy starting place: #{connection.user_options_isolation_level.inspect}" - end - - it "barf if the requested isolation level is not valid" do - assert_raise(ArgumentError) do - connection.run_with_isolation_level 'INVALID ISOLATION LEVEL' do; end - end - end - - it 'allow #run_with_isolation_level to not take a block to set it' do - begin - connection.run_with_isolation_level 'READ UNCOMMITTED' - assert_match %r|read uncommitted|i, connection.user_options_isolation_level - ensure - connection.run_with_isolation_level 'READ COMMITTED' - end - end - - it 'return block value using #run_with_isolation_level' do - assert_equal Task.all.sort, connection.run_with_isolation_level('READ UNCOMMITTED') { Task.all.sort } - end - - it 'pass a read uncommitted isolation level test' do - assert_nil task2.starting, 'Fixture should have this empty.' - begin - Task.transaction do - task2.starting = Time.now - task2.save - @dirty_t2 = connection.run_with_isolation_level('READ UNCOMMITTED') { Task.find(task2.id) } - raise ActiveRecord::ActiveRecordError - end - rescue - 'Do Nothing' - end - assert @dirty_t2, 'Should have a Task record from within block above.' - assert @dirty_t2.starting, 'Should have a dirty date.' - assert_nil Task.find(task2.id).starting, 'Should be nil again from botched transaction above.' - end - - end - end describe 'schema statements' do diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 97d728636..43d0a2e4c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -331,6 +331,29 @@ def self.search(term) + +class TransactionIsolationTest < ActiveRecord::TestCase + + # SQL Server will lock the table for counts even when both + # connections are `READ COMMITTED`. So we bypass with `READPAST`. + coerce_tests! %r{read committed} + test "read committed coerced" do + Tag.transaction(isolation: :read_committed) do + assert_equal 0, Tag.count + Tag2.transaction do + Tag2.create + assert_equal 0, Tag.lock('WITH(READPAST)').count + end + end + assert_equal 1, Tag.count + end + + # I really need some help understanding this one. + coerce_tests! %r{repeatable read} + +end + + require 'models/post' module ActiveRecord class WhereChainTest < ActiveRecord::TestCase diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 1ab662ff5..2be91c3ee 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -30,6 +30,23 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end end + it 'can use an isolation level and reverts back to starting isolation level' do + begin + in_level = nil + begin_level = connection.user_options_isolation_level + begin_level.must_match %r{read committed} + Ship.transaction(isolation: :serializable) do + Ship.create! name: 'Black Pearl' + in_level = connection.user_options_isolation_level + end + after_level = connection.user_options_isolation_level + in_level.must_match %r{serializable}i + after_level.must_match %r{read committed} + ensure + connection.set_transaction_isolation_level 'READ COMMITTED' + end + end + protected From 7d0a082e5e7e63b7df305ebfb121fd48c8f22d94 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 20:34:11 -0500 Subject: [PATCH 0293/1412] More passing tests. TransactionTest#test_releasing_named_savepoints DefaultScopingTest#test_order_in_default_scope_should_not_prevail RelationTest#test_order_using_scoping: SchemaDumperDefaultsTest#test_schema_dump_defaults_with_universally_supported_types SchemaDumperTest#test_types_line_up SchemaDumperTest#test_schema_dump_keeps_large_precision_integer_columns_as_decimal 16 failures, 11 errors, 2 skips --- .../sqlserver/type/datetime.rb | 4 ++ .../sqlserver/type/time.rb | 13 ++++- test/cases/coerced_tests.rb | 50 ++++++++++++++++++- test/cases/column_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 4 +- 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index c69f2374b..ec7f95eaf 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,6 +6,10 @@ class DateTime < ActiveRecord::Type::DateTime include Castable + def type_cast_for_schema(value) + value.acts_like?(:string) ? "'#{value}'" : super + end + private diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 1b3689bf1..08744e744 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -6,6 +6,11 @@ module SQLServer module Type class Time < ActiveRecord::Type::Time + def initialize(options = {}) + super + @precision = nil if @precision == 7 + end + # When FreeTDS/TinyTDS casts this data type natively. # include Castable @@ -14,7 +19,13 @@ def type_cast_for_database(value) Quoter.new super, self end + def type_cast_for_schema(value) + value.acts_like?(:string) ? "'#{value}'" : super + end + def quote_ss(value) + return unless value + value = cast_value(value) if value.acts_like?(:string) date = value.to_s(:_sqlserver_time) frac = quote_usec(value) "'#{date}.#{frac}'" @@ -34,7 +45,7 @@ def cast_usec(value) end def usec_to_seconds_frction(value) - (value.usec.to_f / 1_000_000.0).round(precision) + (value.usec.to_f / 1_000_000.0).round(precision || 7) end def quote_usec(value) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 43d0a2e4c..b38a450b8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -147,6 +147,16 @@ module ConnectionAdapters +class DefaultScopingTest < ActiveRecord::TestCase + + # We are not doing order duplicate removal anymore. + coerce_tests! :test_order_in_default_scope_should_not_prevail + +end + + + + require 'models/post' require 'models/subscriber' class EachTest < ActiveRecord::TestCase @@ -304,7 +314,11 @@ def test_registering_new_handlers_coerced class RelationTest < ActiveRecord::TestCase - coerce_tests! %r{doesn't have implicit ordering} # We have implicit ordering, via FETCH. + # We have implicit ordering, via FETCH. + coerce_tests! %r{doesn't have implicit ordering} + + # We are not doing order duplicate removal anymore. + coerce_tests! :test_order_using_scoping end @@ -331,7 +345,41 @@ def self.search(term) +class SchemaDumperTest < ActiveRecord::TestCase + + # We have precision to 38. + coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal + def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced + output = standard_dump + assert_match %r{t.integer\s+"atoms_in_universe",\s+precision: 38}, output + end + + # This accidently returns the wrong number because of our tables too. + coerce_tests! :test_types_line_up + +end + + + +require 'models/topic' +class TransactionTest < ActiveRecord::TestCase + + coerce_tests! :test_releasing_named_savepoints + def test_releasing_named_savepoints_coerced + Topic.transaction do + Topic.connection.create_savepoint("another") + Topic.connection.release_savepoint("another") + # We do not have a notion of releasing, so this does nothing vs raise an error. + Topic.connection.release_savepoint("another") + end + end + +end + + + +require 'models/tag' class TransactionIsolationTest < ActiveRecord::TestCase # SQL Server will lock the table for counts even when both diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 161f50c4a..68d915a04 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -414,7 +414,7 @@ def assert_obj_set_and_save(attribute, value) type.type.must_equal :time type.wont_be :number? type.limit.must_equal nil - type.precision.must_equal 7 + type.precision.must_equal nil, 'so it is clean in schema dumper' type.scale.must_equal nil # Time's #usec precision (low) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index a44adf86c..8c7a2eb18 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -28,7 +28,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil - assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + assert_line :time_7, type: 'time', limit: nil, precision: nil, scale: nil, default: nil # Character Strings assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" @@ -72,7 +72,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil # Our type methods. From c5a859fcfa68624a6f44a7379686aa2bed10f158 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 22:01:41 -0500 Subject: [PATCH 0294/1412] Pass a few more tests. --- test/cases/coerced_tests.rb | 11 ++++++++++- test/cases/helper_sqlserver.rb | 2 ++ test/cases/utils_test_sqlserver.rb | 28 +++++++++++++-------------- test/support/sql_counter_sqlserver.rb | 3 ++- 4 files changed, 27 insertions(+), 17 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b38a450b8..43e1550ef 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -320,6 +320,15 @@ class RelationTest < ActiveRecord::TestCase # We are not doing order duplicate removal anymore. coerce_tests! :test_order_using_scoping + # Account for our `EXEC sp_executesql...` statements. + coerce_tests! :test_to_sql_on_eager_join + def test_to_sql_on_eager_join_coerced + expected = assert_sql { Post.eager_load(:last_comment).order('comments.id DESC').to_a }.first + actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql + actual = "EXEC sp_executesql N'#{ActiveRecord::ConnectionAdapters::SQLServer::Utils.quote_string(actual)}'" + assert_equal expected, actual + end + end @@ -351,7 +360,7 @@ class SchemaDumperTest < ActiveRecord::TestCase coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced output = standard_dump - assert_match %r{t.integer\s+"atoms_in_universe",\s+precision: 38}, output + assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output end # This accidently returns the wrong number because of our tables too. diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index af2665e61..9809600ef 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -10,6 +10,8 @@ module ActiveRecord class TestCase < ActiveSupport::TestCase + SQLServer = ActiveRecord::ConnectionAdapters::SQLServer + include ARTest::SQLServer::CoerceableTest let(:logger) { ActiveRecord::Base.logger } diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 3652a2247..8200ec5fe 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -2,14 +2,12 @@ class UtilsTestSQLServer < ActiveRecord::TestCase - Utils = ActiveRecord::ConnectionAdapters::SQLServer::Utils - it '.quote_string' do - Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users" + SQLServer::Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users" end it '.unquote_string' do - Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" + SQLServer::Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" end describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do @@ -44,7 +42,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it 'extracts and returns #object identifier unquoted by default or quoted as needed' do valid_names.each do |n| - name = Utils.extract_identifiers(n) + name = SQLServer::Utils.extract_identifiers(n) name.object.must_equal 'object', "With #{n.inspect} for #object" name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted" end @@ -55,12 +53,12 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do present, blank = send(:"#{part}_names") present.each do |n| - name = Utils.extract_identifiers(n) + name = SQLServer::Utils.extract_identifiers(n) name.send(:"#{part}").must_equal "#{part}", "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| - name = Utils.extract_identifiers(n) + name = SQLServer::Utils.extract_identifiers(n) name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method" name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method" end @@ -69,23 +67,23 @@ class UtilsTestSQLServer < ActiveRecord::TestCase end it 'does not blow up on nil or blank string name' do - Utils.extract_identifiers(nil).object.must_be_nil - Utils.extract_identifiers(' ').object.must_be_nil + SQLServer::Utils.extract_identifiers(nil).object.must_be_nil + SQLServer::Utils.extract_identifiers(' ').object.must_be_nil end it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do - Utils.extract_identifiers('object').quoted.must_equal '[object]' - Utils.extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' - Utils.extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' + SQLServer::Utils.extract_identifiers('object').quoted.must_equal '[object]' + SQLServer::Utils.extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' + SQLServer::Utils.extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' end it 'can take a symbol argument' do - Utils.extract_identifiers(:object).object.must_equal 'object' + SQLServer::Utils.extract_identifiers(:object).object.must_equal 'object' end it 'allows identifiers with periods to work' do - Utils.extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' - Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' + SQLServer::Utils.extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' + SQLServer::Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' end end diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 47bf85538..c230f5ee3 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -10,7 +10,8 @@ def ignored_sql /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/, /SELECT @@version/, /SELECT @@TRANCOUNT/, - /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/ ] + /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, + /SELECT CAST\(.* AS .*\) AS value/ ] end def sql_counter_listenters From c07f93761ff8f7410ca3f3a40ac458051a302ac3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Jan 2015 22:54:23 -0500 Subject: [PATCH 0295/1412] Another test coerced. --- test/cases/coerced_tests.rb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 43e1550ef..b25a132c5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -294,6 +294,16 @@ def test_merge_options_coerced +require 'models/topic' +class PersistenceTest < ActiveRecord::TestCase + + # We can not UPDATE identity columns. + coerce_tests! :test_update_columns_changing_id + +end + + + require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase @@ -312,6 +322,22 @@ def test_registering_new_handlers_coerced +require 'models/task' +class QueryCacheTest < ActiveRecord::TestCase + + coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays + def test_cache_does_not_wrap_string_results_in_arrays_coerced + Task.cache do + assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + end + end + +end + + + + +require 'models/post' class RelationTest < ActiveRecord::TestCase # We have implicit ordering, via FETCH. @@ -329,6 +355,9 @@ def test_to_sql_on_eager_join_coerced assert_equal expected, actual end + # We are not doing order duplicate removal anymore. + coerce_tests! :test_default_scope_order_with_scope_order + end From 8aff88b897c79cd7388af6f1da1f0037fcebb558 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 00:05:53 -0500 Subject: [PATCH 0296/1412] Docs. --- CHANGELOG.md | 2 ++ .../sqlserver/schema_creation.rb | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dad6b4a0..e2bfb3dd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Aliased `ActiveRecord::Type::SQLServer` to `ActiveRecord::ConnectionAdapters::SQLServer::Type` * New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. * Support for most all SQL Server types in schema statements and dumping. +* Support create table with query from relation. #### Changed @@ -43,6 +44,7 @@ * The `activity_stats` method. Please put this in a gem if needed. * We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. * All auto reconnect and SQL retry logic. Got too complicated and stood in the way of AR's pool. Speed boost too. +* The adapter will no longer try to remove duplicate order by clauses. Use relation `reorder`, `unscope`, etc. #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 6f5d1ea4d..c0b95556b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -14,6 +14,22 @@ def visit_ColumnDefinition(o) sql end + def visit_TableDefinition(o) + if o.as + visitor = o.as.connection.visitor + table_name = o.temporary ? "##{o.name}" : o.name + select_into = "SELECT " + select_into << "(#{visitor.accept(o.as.arel.projections, Arel::Collectors::PlainString.new).value}) " + select_into << "INTO " + select_into << "#{quote_table_name(table_name)} " + select_into << "FROM " + select_into << "#{visitor.accept(o.as.arel.froms[0], Arel::Collectors::PlainString.new).value}" + else + o.instance_variable_set :@as, nil + super + end + end + def add_column_options!(sql, options) column = options.fetch(:column) { return super } if (column.type == :uuid || column.type == :uniqueidentifier) && options[:default] =~ /\(\)/ From ce570d6ab24706a9473a52b0c20eee88fede2b45 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 10:27:30 -0500 Subject: [PATCH 0297/1412] Better bind types & params for `sp_executesql`. Fixes #239. Fixed: RuntimeError: Unknown bind columns. We can account for this. * HasManyAssociationsTest#test_with_polymorphic_has_many_with_custom_columns_name: * HasOneAssociationsTest#test_with_polymorphic_has_one_with_custom_columns_name: 12 failures, 6 errors, 2 skips --- CHANGELOG.md | 1 + .../sqlserver/core_ext/explain.rb | 2 +- .../sqlserver/database_statements.rb | 65 ++++++++++--------- .../sqlserver/schema_statements.rb | 14 +++- .../connection_adapters/sqlserver/showplan.rb | 2 +- 5 files changed, 48 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2bfb3dd2..96d999158 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -49,6 +49,7 @@ #### Fixed * Default lock is now "WITH(UPDLOCK)". Fixes #368 +* Better bind types & params for `sp_executesql`. Fixes #239. #### Security 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 cb24eddc4..02721d01e 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -16,7 +16,7 @@ def exec_explain(queries) # This is somewhat hacky, but it should reliably reformat our prepared sql statment # which uses sp_executesql to just the first argument, then unquote it. Likewise our - # do_exec_query method should substitude the @n args withe the quoted values. + # `sp_executesql` method should substitude the @n args withe the quoted values. def unprepare_sqlserver_statement(sql) if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 335159f45..a6a114734 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -4,7 +4,7 @@ module SQLServer module DatabaseStatements def select_rows(sql, name = nil, binds = []) - do_exec_query sql, name, binds, fetch: :rows + sp_executesql sql, name, binds, fetch: :rows end def execute(sql, name = nil) @@ -18,9 +18,9 @@ def execute(sql, name = nil) def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) if update_sql?(sql) sql = strip_ident_from_update(sql) - do_exec_query(sql, name, binds) + sp_executesql(sql, name, binds) else - do_exec_query(sql, name, binds) + sp_executesql(sql, name, binds) end end @@ -307,42 +307,45 @@ def do_execute(sql, name = 'SQL') log(sql, name) { raw_connection_do(sql) } end - def do_exec_query(sql, name, binds, options = {}) - # This allows non-AR code to utilize the binds - # handling code, e.g. select_rows() - if options[:fetch] != :rows - options[:ar_result] = true - end - explaining = name == 'EXPLAIN' - names_and_types = [] - params = [] + def sp_executesql(sql, name, binds, options = {}) + options[:ar_result] = true if options[:fetch] != :rows + types, params = sp_executesql_types_and_parameters(binds) + sql = sp_executesql_sql(sql, types, params, name) + raw_select sql, name, binds, options + end + + def sp_executesql_types_and_parameters(binds) + types, params = [], [] binds.each_with_index do |(column, value), index| - ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column) - next if ar_column && column.sql_type == 'timestamp' - v = value - names_and_types << if ar_column - "@#{index} #{column.sql_type_for_statement}" - elsif column.acts_like?(:string) - "@#{index} nvarchar(max)" - elsif column.is_a?(Fixnum) - v = value.to_i - "@#{index} int" - else - raise 'Unknown bind columns. We can account for this.' - end - quoted_value = ar_column ? quote(v, column) : quote(v, nil) - params << (explaining ? quoted_value : "@#{index} = #{quoted_value}") + types << "@#{index} #{sp_executesql_sql_type(column, value)}" + params << quote(value, column) end - if explaining - params.each_with_index do |param, index| + [types, params] + end + + def sp_executesql_sql_type(column, value) + return column.sql_type_for_statement if SQLServerColumn === column + if value.is_a?(Numeric) + 'int' + # We can do more here later. + else + 'nvarchar(max)' + end + end + + def sp_executesql_sql(sql, types, params, name) + if name == 'EXPLAIN' + params.each.with_index do |param, index| substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values. sql.sub! substitute_at_finder, param end else + types = quote(types.join(', ')) + params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp. sql = "EXEC sp_executesql #{quote(sql)}" - sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty? + sql << ", #{types}, #{params}" unless params.empty? end - raw_select sql, name, binds, options + sql end def raw_connection_do(sql) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a0b14b48d..65b07fea3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -244,9 +244,9 @@ def column_definitions(table_name) AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : '@1'} ORDER BY columns.ordinal_position }.gsub(/[ \t\r\n]+/, ' ') - binds = [['table_name', identifier.object]] - binds << ['table_schema', identifier.schema] unless identifier.schema.blank? - results = do_exec_query(sql, 'SCHEMA', binds) + binds = [[info_schema_table_name_column, identifier.object]] + binds << [info_schema_table_schema_column, identifier.schema] unless identifier.schema.blank? + results = sp_executesql(sql, 'SCHEMA', binds) results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] @@ -302,6 +302,14 @@ def column_definitions(table_name) end end + def info_schema_table_name_column + @info_schema_table_name_column ||= new_column 'table_name', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true + end + + def info_schema_table_schema_column + @info_schema_table_schema_column ||= new_column 'table_schema', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true + end + def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA' constraints.each do |constraint| diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 5dae263e2..7c89ae89a 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -13,7 +13,7 @@ module Showplan def explain(arel, binds = []) sql = to_sql(arel) - result = with_showplan_on { do_exec_query(sql, 'EXPLAIN', binds) } + result = with_showplan_on { sp_executesql(sql, 'EXPLAIN', binds) } printer = showplan_printer.new(result) printer.pp end From cc914ecbc4e1f6507dd95e62fbd925c376ac504e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 10:44:20 -0500 Subject: [PATCH 0298/1412] Few more coerced tests. ActiveRecord::Migration::ColumnAttributesTest#test_add_column_without_limit TestAdapterWithInvalidConnection#test_0001_inspect on Model class does not raise: ActiveRecord::Migration::ColumnsTest#test_rename_column_preserves_default_value_not_null 10 failures, 5 errors, 2 skips --- test/cases/coerced_tests.rb | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b25a132c5..7f28c29c7 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -118,6 +118,48 @@ def test_limit_with_offset_is_kept_coerced +module ActiveRecord + class Migration + class ColumnAttributesTest < ActiveRecord::TestCase + + # We have a default 4000 varying character limit. + coerce_tests! :test_add_column_without_limit + def test_add_column_without_limit_coerced + add_column :test_models, :description, :string, limit: nil + TestModel.reset_column_information + TestModel.columns_hash["description"].limit.must_equal 4000 + end + + end + end +end + + + + +module ActiveRecord + class Migration + class ColumnsTest + + # Our defaults are reall 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 + default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default + assert_equal 70000, default_before + rename_column "test_models", "salary", "annual_salary" + assert TestModel.column_names.include?("annual_salary") + default_after = connection.columns("test_models").find { |c| c.name == "annual_salary" }.default + assert_equal 70000, default_after + end + + end + end +end + + + + class CoreTest < ActiveRecord::TestCase # I think fixtures are useing the wrong time zone and the `:first` @@ -399,6 +441,17 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced + +class TestAdapterWithInvalidConnection < ActiveRecord::TestCase + + # We trust Rails on this since we do not want to install mysql. + coerce_tests! %r{inspect on Model class does not raise} + +end + + + + require 'models/topic' class TransactionTest < ActiveRecord::TestCase From da0156ffbe92b080d948070dd94cc3c474afea0f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 10:48:49 -0500 Subject: [PATCH 0299/1412] Move test in alpha order. --- test/cases/coerced_tests.rb | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7f28c29c7..172530f6f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -118,6 +118,21 @@ def test_limit_with_offset_is_kept_coerced +module ActiveRecord + class Migration + class ChangeSchemaTest < ActiveRecord::TestCase + + # We test these. + coerce_tests! :test_create_table_with_bigint, + :test_create_table_with_defaults + + end + end +end + + + + module ActiveRecord class Migration class ColumnAttributesTest < ActiveRecord::TestCase @@ -300,21 +315,6 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced -module ActiveRecord - class Migration - class ChangeSchemaTest < ActiveRecord::TestCase - - # We test these. - coerce_tests! :test_create_table_with_bigint, - :test_create_table_with_defaults - - end - end -end - - - - require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase From 62bfe25add787ae7345cb5b8d33d6cc3a990f055 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 11:45:35 -0500 Subject: [PATCH 0300/1412] Support create table with query from relation or select statement. HasOneAssociationsTest#test_has_one_does_not_use_order_by MigrationTest#test_create_table_with_query: 9 failures, 4 errors, 2 skips --- CHANGELOG.md | 2 +- .../connection_adapters/sqlserver/schema_creation.rb | 11 +++-------- test/cases/coerced_tests.rb | 10 ++++++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96d999158..19e292fe7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ * Aliased `ActiveRecord::Type::SQLServer` to `ActiveRecord::ConnectionAdapters::SQLServer::Type` * New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. * Support for most all SQL Server types in schema statements and dumping. -* Support create table with query from relation. +* Support create table with query from relation or select statement. #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index c0b95556b..0989c7a7a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -16,14 +16,9 @@ def visit_ColumnDefinition(o) def visit_TableDefinition(o) if o.as - visitor = o.as.connection.visitor - table_name = o.temporary ? "##{o.name}" : o.name - select_into = "SELECT " - select_into << "(#{visitor.accept(o.as.arel.projections, Arel::Collectors::PlainString.new).value}) " - select_into << "INTO " - select_into << "#{quote_table_name(table_name)} " - select_into << "FROM " - select_into << "#{visitor.accept(o.as.arel.froms[0], Arel::Collectors::PlainString.new).value}" + table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name) + projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures + select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}" else o.instance_variable_set :@as, nil super diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 172530f6f..367d01439 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -291,6 +291,16 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced +class HasOneAssociationsTest < ActiveRecord::TestCase + + # We use OFFSET/FETCH vs TOP. So we always have an order. + coerce_tests! :test_has_one_does_not_use_order_by + +end + + + + require 'models/company' class InheritanceTest < ActiveRecord::TestCase From 1ebfceb394b002f13b2507b4d2220d94b9d22a26 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 13:21:39 -0500 Subject: [PATCH 0301/1412] Fixing a few more tests. PersistenceTest#test_update_all_doesnt_ignore_order ActiveRecord::Migration::IndexTest#test_rename_index_too_long 7 failures, 4 errors, 2 skips --- .../sqlserver/schema_statements.rb | 9 +++++---- test/cases/coerced_tests.rb | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 65b07fea3..6aba2697d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -72,7 +72,6 @@ def change_column(table_name, column_name, type, options = {}) sql_commands = [] indexes = [] column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } - if options_include_default?(options) || (column_object && column_object.type != type.to_sym) remove_default_constraint(table_name, column_name) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } @@ -84,7 +83,6 @@ def change_column(table_name, column_name, type, options = {}) if options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(options[:default], column_object)} FOR #{quote_column_name(column_name)}" end - # 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(', ')})" @@ -102,13 +100,16 @@ def change_column_default(table_name, column_name, default) def rename_column(table_name, column_name, new_column_name) schema_cache.clear_table_cache!(table_name) detect_column_for! table_name, column_name - do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'" + identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}") + execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN' rename_column_indexes(table_name, column_name, new_column_name) schema_cache.clear_table_cache!(table_name) end def rename_index(table_name, old_name, new_name) - execute "EXEC sp_rename N'#{table_name}.#{old_name}', N'#{new_name}', N'INDEX'" + raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length + identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}") + execute_procedure :sp_rename, identifier.quoted, new_name, 'INDEX' end def remove_index!(table_name, index_name) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 367d01439..dbf1a5fd3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -352,10 +352,25 @@ class PersistenceTest < ActiveRecord::TestCase # We can not UPDATE identity columns. coerce_tests! :test_update_columns_changing_id + # Previous test required updating a identity column. + coerce_tests! :test_update_all_doesnt_ignore_order + def test_update_all_doesnt_ignore_order_coerced + david, mary = authors(:david), authors(:mary) + david.id.must_equal 1 + mary.id.must_equal 2 + david.name.wont_equal mary.name + assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do + Author.where('[id] > 1').order(:id).update_all(name: 'Test') + end + david.reload.name.must_equal 'David' + mary.reload.name.must_equal 'Test' + end + end + require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase From b305c2d90312c9471714e54c22d34060b7a01e43 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 13:41:27 -0500 Subject: [PATCH 0302/1412] Coerce a test. --- test/cases/coerced_tests.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index dbf1a5fd3..8f758fe1e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -168,6 +168,17 @@ def test_rename_column_preserves_default_value_not_null_coerced assert_equal 70000, default_after end + # Dropping the column removes the single index. + 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 + assert_equal 1, connection.indexes('test_models').size + remove_column("test_models", "hat_size") + assert_equal [], connection.indexes('test_models').map(&:name) + end + end end end From 988ae88bed6e65427091246d897056965954c6c6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 14:24:00 -0500 Subject: [PATCH 0303/1412] MigrationTest#test_add_table_with_decimals_coerced 5 failures, 4 errors, 3 skips --- test/cases/coerced_tests.rb | 47 +++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8f758fe1e..e6ae8ffcf 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -336,6 +336,53 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced +class BigNumber < ActiveRecord::Base + attribute :value_of_e, Type::SQLServer::Integer.new + attribute :my_house_population, Type::SQLServer::Integer.new +end +class MigrationTest < ActiveRecord::TestCase + + coerce_tests! :test_add_table_with_decimals + def test_add_table_with_decimals_coerced + Person.connection.drop_table :big_numbers rescue nil + assert !BigNumber.table_exists? + GiveMeBigNumbers.up + assert BigNumber.create( + :bank_balance => 1586.43, + :big_bank_balance => BigDecimal("1000234000567.95"), + :world_population => 6000000000, + :my_house_population => 3, + :value_of_e => BigDecimal("2.7182818284590452353602875") + ) + b = BigNumber.first + assert_not_nil b + assert_not_nil b.bank_balance + assert_not_nil b.big_bank_balance + assert_not_nil b.world_population + assert_not_nil b.my_house_population + assert_not_nil b.value_of_e + # SQLServer: We rock and cast during assignment. + assert_kind_of BigDecimal, b.world_population + assert_equal BigDecimal('6000000000'), b.world_population + # TODO: Our trust the DB policy breaks this expectation. Review SQLServer::Type::Castable module. + skip + assert_kind_of Fixnum, b.my_house_population + assert_equal 3, b.my_house_population + assert_kind_of BigDecimal, b.bank_balance + assert_equal BigDecimal("1586.43"), b.bank_balance + assert_kind_of BigDecimal, b.big_bank_balance + assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance + assert_kind_of BigDecimal, b.value_of_e + assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e + GiveMeBigNumbers.down + assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first } + end + +end + + + + require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase From 77f6e8596d2851410dff8ca68369611c78894638 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 14:57:07 -0500 Subject: [PATCH 0304/1412] Prepare for pre release. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index b29fcb8af..54b0a7775 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.0' + VERSION = '4.2.0.pre1' end end From b65e45eaa629b5e3f518f188545e683b04d84a4e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 15:49:31 -0500 Subject: [PATCH 0305/1412] Use ActiveRecord's `attributes_for_update` hook to avoid identity inserts. I found a better way! cc @annaswims --- CHANGELOG.md | 3 ++- .../sqlserver/core_ext/attribute_methods.rb | 25 +++++++++++++++++++ .../sqlserver/database_statements.rb | 7 +----- .../sqlserver/schema_statements.rb | 20 --------------- .../connection_adapters/sqlserver_adapter.rb | 1 + 5 files changed, 29 insertions(+), 27 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 19e292fe7..ef08493a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,9 +42,10 @@ * edition * Removed tests for old issue #164. Handled by core types now. * The `activity_stats` method. Please put this in a gem if needed. -* We no longger use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. +* We no longer use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. * All auto reconnect and SQL retry logic. Got too complicated and stood in the way of AR's pool. Speed boost too. * The adapter will no longer try to remove duplicate order by clauses. Use relation `reorder`, `unscope`, etc. +* We no longer use regular expressions to remove identity columns from updates. Now with `attributes_for_update` AR hook. #### Fixed 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 new file mode 100644 index 000000000..eecb5e34e --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb @@ -0,0 +1,25 @@ +require 'active_record/attribute_methods' + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module AttributeMethods + + + private + + def attributes_for_update(attribute_names) + super.reject do |name| + column = self.class.columns_hash[name] + column && column.respond_to?(:is_identity?) && column.is_identity? + end + end + + end + end + end + end +end + +ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AttributeMethods diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index a6a114734..b432a2c5a 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -16,12 +16,7 @@ def execute(sql, name = nil) end def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - if update_sql?(sql) - sql = strip_ident_from_update(sql) - sp_executesql(sql, name, binds) - else - sp_executesql(sql, name, binds) - end + sp_executesql(sql, name, binds) end def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6aba2697d..b598aef86 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -411,26 +411,6 @@ def insert_sql?(sql) !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? end - def strip_ident_from_update(sql) - # We can't update Identiy columns in sqlserver. So, strip out the id from the update. - # There has to be a better way to handle this, but this'll do for now. - table_name = get_table_name(sql) - id_column = identity_column(table_name) - if id_column - regex_col_name = Regexp.quote(quote_column_name(id_column.name)) - if sql =~ /, #{regex_col_name} = @?[0-9]*/ - sql = sql.gsub(/, #{regex_col_name} = @?[0-9]*/, '') - elsif sql =~ /\s#{regex_col_name} = @?[0-9]*,/ - sql = sql.gsub(/\s#{regex_col_name} = @?[0-9]*,/, '') - end - end - sql - end - - def update_sql?(sql) - !(sql =~ /^\s*(UPDATE|EXEC sp_executesql N'UPDATE)/i).nil? - end - def identity_column(table_name) schema_cache.columns(table_name).find(&:is_identity?) end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1b0999df1..348f53bba 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -6,6 +6,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/relation' +require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' require 'active_record/connection_adapters/sqlserver/version' require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' From ccca64fa643c701adb7298a29a553680be2c59f8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 15:55:13 -0500 Subject: [PATCH 0306/1412] Remove old relation hack for ignorning derived row number table. --- .../sqlserver/core_ext/relation.rb | 17 ----------------- .../connection_adapters/sqlserver_adapter.rb | 1 - 2 files changed, 18 deletions(-) delete mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb deleted file mode 100644 index df2a65def..000000000 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +++ /dev/null @@ -1,17 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - module CoreExt - module Relation - private - - def tables_in_string(string) - super - ['__rnt'] - end - end - end - end - end -end - -ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Relation diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 348f53bba..b94d9b5bd 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -5,7 +5,6 @@ require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' -require 'active_record/connection_adapters/sqlserver/core_ext/relation' require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' require 'active_record/connection_adapters/sqlserver/version' require 'active_record/connection_adapters/sqlserver/type' From 8eff660a75bff5b042c1e9ecfbe54aef61047ce2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 16:22:08 -0500 Subject: [PATCH 0307/1412] Coerce test. --- test/cases/coerced_tests.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e6ae8ffcf..7a67cf04c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -424,6 +424,30 @@ def test_update_all_doesnt_ignore_order_coerced mary.reload.name.must_equal 'Test' end + # We can not UPDATE identity columns. + coerce_tests! :test_update_attributes + def test_update_attributes_coerced + topic = Topic.find(1) + assert !topic.approved? + assert_equal "The First Topic", topic.title + topic.update_attributes("approved" => true, "title" => "The First Topic Updated") + topic.reload + assert topic.approved? + assert_equal "The First Topic Updated", topic.title + topic.update_attributes(approved: false, title: "The First Topic") + topic.reload + assert !topic.approved? + assert_equal "The First Topic", topic.title + # SQLServer: Here is where it breaks down. No exceptions. + # assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do + # topic.update_attributes(id: 3, title: "Hm is it possible?") + # end + # assert_not_equal "Hm is it possible?", Topic.find(3).title + # topic.update_attributes(id: 1234) + # assert_nothing_raised { topic.reload } + # assert_equal topic.title, Topic.find(1234).title + end + end From e3141a9a0f98e74370343a771c9574af837b40ad Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 17:40:47 -0500 Subject: [PATCH 0308/1412] Implement our own binary timestamp type again. Remove `substitute_at` hack. --- .../connection_adapters/sqlserver/quoting.rb | 5 -- .../sqlserver/table_definition.rb | 4 ++ .../connection_adapters/sqlserver/type.rb | 1 + .../sqlserver/type/timestamp.rb | 15 ++++++ .../connection_adapters/sqlserver_adapter.rb | 1 + test/cases/column_test_sqlserver.rb | 23 +++++++++ test/cases/schema_dumper_test_sqlserver.rb | 51 ++++++++++--------- test/schema/datatypes/2012.sql | 1 + test/schema/sqlserver_specific_schema.rb | 1 + 9 files changed, 73 insertions(+), 29 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/timestamp.rb diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 4d16c7af9..3678459ae 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -23,11 +23,6 @@ def quote_default_value(value, column) end end - def substitute_at(column, _unused = 0) - return nil if column.respond_to?(:sql_type) && column.sql_type == 'timestamp' - super - end - def quoted_true QUOTED_TRUE end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 87e53751f..b438f4844 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -58,6 +58,10 @@ def uuid(name, options = {}) column(name, :uniqueidentifier, options) end + def ss_timestamp(name, options = {}) + column(name, :ss_timestamp, options) + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 34aadcf48..dc982c72b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -37,6 +37,7 @@ require 'active_record/connection_adapters/sqlserver/type/varbinary_max.rb' # Other Data Types require 'active_record/connection_adapters/sqlserver/type/uuid.rb' +require 'active_record/connection_adapters/sqlserver/type/timestamp.rb' module ActiveRecord module Type diff --git a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb new file mode 100644 index 000000000..7ed713bd5 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb @@ -0,0 +1,15 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Timestamp < Binary + + def type + :ss_timestamp + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b94d9b5bd..8a9e9499d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -231,6 +231,7 @@ def initialize_type_map(m) 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 end def translate_exception(e, message) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 68d915a04..575d109c4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -675,6 +675,29 @@ def assert_obj_set_and_save(attribute, value) obj.uniqueidentifier.must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" end + it 'timestamp' do + col = column('timestamp') + col.sql_type.must_equal 'timestamp' + col.null.must_equal true + col.default.must_equal nil + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Timestamp + type.type.must_equal :ss_timestamp + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal nil + type.scale.must_equal nil + # Basic read. + obj.timestamp.must_equal nil + obj.save! ; obj.reload + obj.timestamp.must_match %r|\000| + obj.timestamp + # Can set another attribute + obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" + obj.save! + end + end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 8c7a2eb18..53f2dfd02 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -45,36 +45,37 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil # Other Data Types assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil + assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end it 'sst_datatypes_migration' do columns = SSTestDatatypeMigration.columns_hash 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['datetime_col'].sql_type.must_equal 'datetime' - columns['timestamp_col'].sql_type.must_equal 'datetime' - 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', limit: '4', precision: nil, scale: nil, default: nil - assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil - assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil - assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil - assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil - assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil - assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil - assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil - assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + 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_col'].sql_type.must_equal 'datetime' + columns['timestamp_col'].sql_type.must_equal 'datetime' + 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', limit: '4', precision: nil, scale: nil, default: nil + assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil + assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil + assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil + assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil + assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil + assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil + assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil + assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil + assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil # Our type methods. columns['real_col'].sql_type.must_equal 'real' columns['money_col'].sql_type.must_equal 'money' @@ -87,6 +88,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['binary_basic_col'].sql_type.must_equal 'binary(1)' columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)' columns['uuid_col'].sql_type.must_equal 'uniqueidentifier' + columns['sstimestamp_col'].sql_type.must_equal 'timestamp' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil @@ -98,6 +100,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil + assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end # Special Cases diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 098d6df9d..8bc5a3946 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -44,6 +44,7 @@ CREATE TABLE [sst_datatypes] ( [varbinary_max] [varbinary](max) NULL, -- Other Data Types [uniqueidentifier] [uniqueidentifier] NULL DEFAULT NEWID(), + [timestamp] [timestamp] NULL, ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] -- Date and Time (TODO) diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 75a120426..501b812a3 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -30,6 +30,7 @@ t.binary_basic :binary_basic_col t.varbinary :varbinary_col t.uuid :uuid_col + t.ss_timestamp :sstimestamp_col end # Edge Cases From 90c72985f045187ce7854a3242d8bd460fe1713f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 17:53:40 -0500 Subject: [PATCH 0309/1412] Turn on bulk `supports_bulk_alter?`. --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef08493a9..d7f52b1a6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * The `cs_equality_operator` is now s class configuration property only. * The `with_identity_insert_enabled(table_name)` is now public. * Use ActiveRecord tranasaction interface vs our own `run_with_isolation_level`. +* Turn on bulk `supports_bulk_alter?`. #### Deprecated diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 8a9e9499d..604422b9e 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -88,7 +88,7 @@ def supports_ddl_transactions? end def supports_bulk_alter? - false + true end def supports_index_sort_order? From d8f4426b500941088b99ba04f5f4f1e80741f7dd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 18:12:40 -0500 Subject: [PATCH 0310/1412] Documentation changes for running tests. --- RUNNING_UNIT_TESTS.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 8ebe4bc40..f1a4fa9cd 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -14,7 +14,6 @@ Default testing uses DBLIB with TinyTDS. - http://twitpic.com/9bsj7z/full - http://twitpic.com/9bsjdx/full - http://twitpic.com/9bsjl7/full -* $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git * $ bundle install * $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' @@ -28,18 +27,16 @@ The connection files make certain assumptions. For instance, the ODBC connection ## Cloning The Repos -Clone adapter git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git. The master branch is the one under development for Rails 3, track the repos 2-3-stable branch for 2.x development. - -The tests of this adapter depend on the existence of the Rails which under the 3.1 version and above is automatically cloned for you with bundler. However you can clone Rails from git://github.com/rails/rails.git and set the `RAILS_SOURCE` environment variable so bundler will use another local path instead. +The tests of this adapter depend on the existence of the Rails which are automatically cloned for you with bundler. However you can clone Rails from git://github.com/rails/rails.git and set the `RAILS_SOURCE` environment variable so bundler will use another local path instead. ``` $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git ``` -Optionally, you an just let bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. +Suggest just letting bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. ``` -$ export RAILS_VERSION='3.2.13' +$ export RAILS_VERSION='4.2.0' $ bundle install ``` @@ -96,4 +93,3 @@ By default, Bundler will download the Rails git repo and use the git tag that ma ## Current Expected Failures * Misc Date/Time erros when using ODBC mode. -* Misc Date/Time erros when testing SQL Server 2005. From ce3ba8f75349dd56cc07a13fe01d2c215dbb26f1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 18:21:31 -0500 Subject: [PATCH 0311/1412] Blew away my DB. Fixed a few broken tests due to refactor/renames. --- test/cases/migration_test_sqlserver.rb | 6 +++--- test/cases/schema_test_sqlserver.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index f0679793e..0a512f174 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -45,12 +45,12 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it 'not drop the default contraint if just renaming' do find_default = lambda do - connection.execute_procedure(:sp_helpconstraint, 'defaults', 'nomsg').select do |row| - row['constraint_type'] == "DEFAULT on column decimal_number" + connection.execute_procedure(:sp_helpconstraint, 'sst_string_defaults', 'nomsg').select do |row| + row['constraint_type'] == "DEFAULT on column string_with_pretend_paren_three" end.last end default_before = find_default.call - connection.change_column :defaults, :decimal_number, :decimal, precision: 4 + connection.change_column :sst_string_defaults, :string_with_pretend_paren_three, :string, limit: 255 default_after = find_default.call assert default_after assert_equal default_before['constraint_keys'], default_after['constraint_keys'] diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index bfc32f7c5..9f1840b0c 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -5,7 +5,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase describe 'When table is dbo schema' do it 'find primary key for tables with odd schema' do - connection.primary_key('natural_pk_data').must_equal 'legacy_id' + connection.primary_key('sst_natural_pk_data').must_equal 'legacy_id' end end From d99e18c364ed209b5bd6f0cd8b28be3e3ebff21b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 18:38:46 -0500 Subject: [PATCH 0312/1412] Let's here it for our view support :) --- .../connection_adapters/sqlserver_adapter.rb | 4 ++++ test/cases/coerced_tests.rb | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 604422b9e..384a0db0c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -107,6 +107,10 @@ def supports_transaction_isolation? true end + def supports_views? + true + end + def disable_referential_integrity do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" yield diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7a67cf04c..622859ff7 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -617,6 +617,16 @@ def test_not_eq_with_array_parameter_coerced +class ViewWithPrimaryKeyTest < ActiveRecord::TestCase + + # We do better than ActiveRecord and find the views PK. + coerce_tests! :test_does_not_assume_id_column_as_primary_key + +end + + + + require 'models/author' class YamlSerializationTest < ActiveRecord::TestCase From 275c775a7116022ce17bfd4440d8d26e822e8d2e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 22:26:55 -0500 Subject: [PATCH 0313/1412] Different Gitter Badge. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 862fa5459..b5083148c 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) -[![Gitter chat](https://img.shields.io/badge/gitter-rails-sqlserver/activerecord-sqlserver-adapter-ae3dd2.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) +[![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) **This project is looking for a new maintainers. Join the [discusion here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/364)** From 815775d584d7852e22c2f1675136d37a7d8cb3eb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 22:45:18 -0500 Subject: [PATCH 0314/1412] Update README. --- README.md | 69 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index b5083148c..a51b2c08e 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,11 @@ [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) -**This project is looking for a new maintainers. Join the [discusion here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/364)** +![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) -The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2000, you are still in the right spot. Just install the latest 2.3.x version of the adapter. Note, we follow a rational versioning policy that tracks ActiveRecord. That means that our 2.3.x version of the adapter is only for the latest 2.3 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. +## Code Name Kantishna - -## What's New - -* Rails 4.0 and 4.1 support -* Ruby 2.0 and 2.1 support +The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. #### Testing Rake Tasks Support @@ -32,32 +28,39 @@ Account.execute_procedure :update_totals, 'admin', nil, true Account.execute_procedure :update_totals, named: 'params' ``` -#### Native Data Type Support -Currently the following custom data types have been tested for schema definitions. - -* char -* nchar -* nvarchar -* ntext -* varchar(max) -* nvarchar(max) +#### Native Data Type Support -For example: +We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. ```ruby -create_table :sql_server_custom_types, force: true do |t| - t.column :ten_code, :char, limit: 10 - t.column :ten_code_utf8, :nchar, limit: 10 - t.column :title_utf8, :nvarchar - t.column :body, :varchar_max # Creates varchar(max) - t.column :body_utf8, :ntext - t.column :body2_utf8, :nvarchar_max # Creates nvarchar(max) -end +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' } +timestamp: { name: 'datetime' } +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' } ``` -Manually creating a `varchar(max)` is not necessary since this is the default type created when specifying a `:text` field. As time goes on we will be testing other SQL Server specific data types are handled correctly when created in a migration. - #### Force Schema To Lowercase @@ -130,6 +133,7 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X ``` **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. + ## Versions The adapter follows a rational versioning policy that also tracks ActiveRecord's major and minor version. That means the latest 3.1.x version of the adapter will always work for the latest 3.1.x version of ActiveRecord. @@ -141,22 +145,20 @@ The adapter has no strict gem dependencies outside of ActiveRecord. You will hav ```ruby gem 'tiny_tds' -gem 'activerecord-sqlserver-adapter', '~> 4.0.0' +gem 'activerecord-sqlserver-adapter', '~> 4.2.0' ``` -If you want to use ruby ODBC, please use at least version 0.99992 since that contains fixes for both native types as well as fixes for proper encoding support under 1.9. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too! +If you want to use ruby ODBC, please use the latest least. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too! http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/platform-installation - ## Contributing -If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Google list, IRC, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the `RUNNING_UNIT_TESTS` file for the details of how to run the unit tests. +If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Gitter, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the `RUNNING_UNIT_TESTS` file for the details of how to run the unit tests. * Github: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter -* Google Group: http://groups.google.com/group/rails-sqlserver-adapter -* IRC Room: #rails-sqlserver on irc.freenode.net +* Gitter: https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter ## Credits & Contributions @@ -169,6 +171,7 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- * metaskills (Ken Collins) * Annaswims (Annaswims) +* wbond (Will Bond) * Thirdshift (Garrett Hart) * h-lame (Murray Steele) * vegantech From 5a1a1d5d72dada046f3b260e707ad7d0f1a11f27 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 23:00:20 -0500 Subject: [PATCH 0315/1412] Bundle gem tasks. --- Rakefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Rakefile b/Rakefile index 2af75bde3..7ebb70ad6 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,4 @@ +require 'bundler/gem_tasks' require 'rake/testtask' require_relative 'test/support/paths_sqlserver' require_relative 'test/support/rake_helpers' From 5a249a2834badb7739fde7c86d10d731e2ebd64d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 25 Jan 2015 23:01:01 -0500 Subject: [PATCH 0316/1412] Prepare for 4.2.0.pre --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 54b0a7775..13fef05c3 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.0.pre1' + VERSION = '4.2.0.pre' end end From 8abb18a4401624a8210de4f98ac30f780e28e8c3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 10:23:11 -0500 Subject: [PATCH 0317/1412] Distinct One As One Is So Not Fetch. Fixes #374. --- lib/arel/visitors/sqlserver.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 4ac6e5ccf..43a3c904c 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -58,6 +58,7 @@ def visit_Arel_Nodes_Limit o, collector def visit_Arel_Nodes_SelectStatement o, collector @select_statement = o + distinct_One_As_One_Is_So_Not_Fetch o if o.with collector = visit o.with, collector collector << SPACE @@ -126,6 +127,7 @@ def visit_Make_Fetch_Happen o, collector # SQLServer Helpers def node_value(node) + return nil unless node case node.expr when NilClass then nil when Numeric then node.expr @@ -148,6 +150,17 @@ def make_Fetch_Possible_And_Deterministic o end end + def distinct_One_As_One_Is_So_Not_Fetch o + core = o.cores.first + distinct = Nodes::Distinct === core.set_quantifier + oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE } + limitone = node_value(o.limit) == 1 + if distinct && oneasone && limitone && !o.offset + core.projections = [Arel.sql("TOP(1) 1 AS [one]")] + o.limit = nil + end + end + def table_From_Statement o core = o.cores.first if Arel::Table === core.from From e178bcba0c2f550a6ff0d517eac8aa9aaf8a69e0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 13:14:38 -0500 Subject: [PATCH 0318/1412] Note pre release. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index a51b2c08e..604134d0d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. +The 4.2 gem is in pre-release till we get a few more [bugs](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/milestones) worked out. Bundle to the pre version or this repos master directory. + +```ruby +gem 'activerecord-sqlserver-adapter', '~> 4.2.0.pre' +``` + #### Testing Rake Tasks Support From 51aab54da5209bdcc8ea40c98c4d2d95311d1a92 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 19:15:41 -0500 Subject: [PATCH 0319/1412] Foreign Key Support Fixes #375. --- CHANGELOG.md | 1 + .../sqlserver/schema_creation.rb | 12 ++++++++ .../sqlserver/schema_statements.rb | 24 +++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 4 +++ test/cases/coerced_tests.rb | 29 +++++++++++++++++++ 5 files changed, 70 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7f52b1a6..55494a2f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. * Support for most all SQL Server types in schema statements and dumping. * Support create table with query from relation or select statement. +* Foreign Key Support Fixes #375. #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 0989c7a7a..134dd8c92 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -34,6 +34,18 @@ def add_column_options!(sql, options) end end + def action_sql(action, dependency) + case dependency + when :restrict + raise ArgumentError, <<-MSG.strip_heredoc + '#{dependency}' is not supported for :on_update or :on_delete. + Supported values are: :nullify, :cascade + MSG + else + super + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b598aef86..6b6486feb 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -116,6 +116,30 @@ def remove_index!(table_name, index_name) do_execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end + def foreign_keys(table_name) + identifier = SQLServer::Utils.extract_identifiers(table_name) + fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema + fk_info.map do |row| + from_table = identifier.object + to_table = row['PKTABLE_NAME'] + options = { + name: row['FK_NAME'], + column: row['FKCOLUMN_NAME'], + primary_key: row['PKCOLUMN_NAME'], + on_update: extract_foreign_key_action('update', row['FK_NAME']), + on_delete: extract_foreign_key_action('delete', row['FK_NAME']) + } + ForeignKeyDefinition.new from_table, to_table, options + end + end + + def extract_foreign_key_action(action, fk_name) + case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'") + when 'CASCADE' then :cascade + when 'SET_NULL' then :nullify + end + end + def type_to_sql(type, limit = nil, precision = nil, scale = nil) type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 384a0db0c..5a9f434c6 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -111,6 +111,10 @@ def supports_views? true end + def supports_foreign_keys? + true + end + def disable_referential_integrity do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" yield diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 622859ff7..41bb82048 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -126,6 +126,13 @@ class ChangeSchemaTest < ActiveRecord::TestCase coerce_tests! :test_create_table_with_bigint, :test_create_table_with_defaults + + end + class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase + + # In SQL Server you have to delete the tables yourself in the right order. + coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects + end end end @@ -302,6 +309,28 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced +module ActiveRecord + class Migration + class ForeignKeyTest < ActiveRecord::TestCase + + # We do not support :restrict. + coerce_tests! :test_add_on_delete_restrict_foreign_key + def test_add_on_delete_restrict_foreign_key_coerced + assert_raises ArgumentError do + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_delete: :restrict + end + assert_raises ArgumentError do + @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :restrict + end + end + + end + end +end + + + + class HasOneAssociationsTest < ActiveRecord::TestCase # We use OFFSET/FETCH vs TOP. So we always have an order. From 5550f6f8ce097cdebc6cb2a5864d2bc8ce2a762b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 21:26:08 -0500 Subject: [PATCH 0320/1412] We are deterministic if you are explicit about your tie breakers. --- test/cases/coerced_tests.rb | 19 ++++++++++ test/cases/fetch_test_sqlserver.rb | 57 ++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100755 test/cases/fetch_test_sqlserver.rb diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 41bb82048..66dd69fbb 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -412,6 +412,25 @@ def test_add_table_with_decimals_coerced +class NamedScopingTest < ActiveRecord::TestCase + + # This works now because we add an `order(:id)` sort to break the order tie for deterministic results. + coerce_tests! :test_scopes_honor_current_scopes_from_when_defined + def test_scopes_honor_current_scopes_from_when_defined_coerced + assert !Post.ranked_by_comments.order(:id).limit_by(5).empty? + assert !authors(:david).posts.ranked_by_comments.order(:id).limit_by(5).empty? + assert_not_equal Post.ranked_by_comments.order(:id).limit_by(5), authors(:david).posts.ranked_by_comments.order(:id).limit_by(5) + assert_not_equal Post.order(:id).top(5), authors(:david).posts.order(:id).top(5) + # Oracle sometimes sorts differently if WHERE condition is changed + assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id) + assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5) + end + +end + + + + require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb new file mode 100755 index 000000000..787185d22 --- /dev/null +++ b/test/cases/fetch_test_sqlserver.rb @@ -0,0 +1,57 @@ +require 'cases/helper_sqlserver' +require 'models/book' + +class FetchTestSqlserver < ActiveRecord::TestCase + + let(:books) { @books } + + before { create_10_books } + + it 'work with fully qualified table and columns in select' do + books = Book.select('books.id, books.name').limit(3).offset(5) + assert_equal Book.all[5,3].map(&:id), books.map(&:id) + end + + describe 'count' do + + it 'gauntlet' do + books[0].destroy + books[1].destroy + books[2].destroy + assert_equal 7, Book.count + assert_equal 1, Book.limit(1).offset(1).count + assert_equal 1, Book.limit(1).offset(5).count + assert_equal 1, Book.limit(1).offset(6).count + assert_equal 0, Book.limit(1).offset(7).count + assert_equal 3, Book.limit(3).offset(4).count + assert_equal 2, Book.limit(3).offset(5).count + assert_equal 1, Book.limit(3).offset(6).count + assert_equal 0, Book.limit(3).offset(7).count + assert_equal 0, Book.limit(3).offset(8).count + end + + end + + describe 'order' do + + it 'gauntlet' do + Book.where(name:'Name-10').delete_all + Book.order(:name).limit(1).offset(1).map(&:name).must_equal ['Name-2'] + Book.order(:name).limit(2).offset(2).map(&:name).must_equal ['Name-3', 'Name-4'] + Book.order(:name).limit(2).offset(7).map(&:name).must_equal ['Name-8', 'Name-9'] + Book.order(:name).limit(3).offset(7).map(&:name).must_equal ['Name-8', 'Name-9'] + Book.order(:name).limit(3).offset(9).map(&:name).must_equal [] + end + + end + + + protected + + def create_10_books + Book.delete_all + @books = (1..10).map { |i| Book.create! name: "Name-#{i}" } + end + +end + From c97af4bca30320fd1d27c03fcc2dce4a9d6bdd89 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 21:58:21 -0500 Subject: [PATCH 0321/1412] Fixes #377. --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 66dd69fbb..3892b94ba 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -304,6 +304,9 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced assert_sql(/OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY/) { Topic.last(5).entries } end + # This fails only when run in the full test suite task. Just taking it out of the mix. + coerce_tests! :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct + end From fae0b601c7c76b399169e7f6cd605ba73749b7d4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 22:26:42 -0500 Subject: [PATCH 0322/1412] Turn off bulk alter. --- CHANGELOG.md | 1 - lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55494a2f1..5880ca2e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,6 @@ * The `cs_equality_operator` is now s class configuration property only. * The `with_identity_insert_enabled(table_name)` is now public. * Use ActiveRecord tranasaction interface vs our own `run_with_isolation_level`. -* Turn on bulk `supports_bulk_alter?`. #### Deprecated diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5a9f434c6..81900e2a8 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -88,7 +88,7 @@ def supports_ddl_transactions? end def supports_bulk_alter? - true + false end def supports_index_sort_order? From 38ef8628270980ebc14900acc8501a717d9e3b56 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 22:38:53 -0500 Subject: [PATCH 0323/1412] Prepare for 4.2.0 release. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 13fef05c3..b29fcb8af 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.0.pre' + VERSION = '4.2.0' end end From a3f3869679ed014669084971315db4c79ee24414 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 28 Jan 2015 22:43:45 -0500 Subject: [PATCH 0324/1412] Remove pre-release note in README. --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 604134d0d..a51b2c08e 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,6 @@ The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. -The 4.2 gem is in pre-release till we get a few more [bugs](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/milestones) worked out. Bundle to the pre version or this repos master directory. - -```ruby -gem 'activerecord-sqlserver-adapter', '~> 4.2.0.pre' -``` - #### Testing Rake Tasks Support From f711dd01fdbcdc0d7f4552ee1ebf887160a48f1f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 08:57:38 -0500 Subject: [PATCH 0325/1412] Notes on running focused tests. --- RUNNING_UNIT_TESTS.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index f1a4fa9cd..12ebd1566 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -17,6 +17,16 @@ Default testing uses DBLIB with TinyTDS. * $ bundle install * $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' +Focusing tests. Use the `ONLY_` env vars to run either ours or the ActiveRecord cases. Use the `TEST_FILES` env variants to focus on specific test(s), use commas for multiple cases. Note, you have to use different env vars to focus only on ours or a core ActiveRecord case. There may be failures when focusing on an ActiveRecord case since our coereced test files is not loaded in this scenerio. + +``` +$ bundle exec rake test ONLY_SQLSERVER=1 +$ bundle exec rake test ONLY_ACTIVERECORD=1 + +$ bundle exec rake test TEST_FILES="test/cases/adapter_test_sqlserver.rb" +$ bundle exec rake test TEST_FILES_AR="test/cases/finder_test.rb" +``` + ## Creating the test databases From dbb559f3ae85ed7e89bb478d3357bdb902026002 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 08:57:54 -0500 Subject: [PATCH 0326/1412] Remove deadlock retry artifacts. --- lib/active_record/sqlserver_base.rb | 8 --- test/cases/connection_test_sqlserver.rb | 74 ------------------------- 2 files changed, 82 deletions(-) diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 4b1b4921a..0279f7182 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -18,13 +18,5 @@ def self.sqlserver_connection(config) #:nodoc: ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) end - def self.did_retry_sqlserver_connection(connection, count) - logger.info "CONNECTION RETRY: #{connection.class.name} retry ##{count}." - end - - def self.did_lose_sqlserver_connection(connection) - logger.info "CONNECTION LOST: #{connection.class.name}" - end - end end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index facadcb63..934f217b2 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -109,72 +109,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert connection.active? end - describe 'with a deadlock victim exception 1205' do - - describe 'outside a transaction' do - - before do - @query = "SELECT 1 as [one]" - @expected = connection.execute(@query) - # Execute the query to get a handle of the expected result, which - # will be returned after a simulated deadlock victim (1205). - raw_conn = connection.raw_connection - stubbed_handle = raw_conn.execute(@query) - connection.send(:finish_statement_handle, stubbed_handle) - raw_conn.stubs(:execute).raises(deadlock_victim_exception(@query)).then.returns(stubbed_handle) - end - - it 'raise ActiveRecord::DeadlockVictim' do - assert_raise(ActiveRecord::DeadlockVictim) do - assert_equal @expected, connection.execute(@query) - end - end - - end - - describe 'within a transaction' do - - before do - @query = "SELECT 1 as [one]" - @expected = connection.execute(@query) - # We "stub" the execute method to simulate raising a deadlock victim exception once. - connection.class.class_eval do - def execute_with_deadlock_exception(sql, *args) - if !@raised_deadlock_exception && sql == "SELECT 1 as [one]" - sql = "RAISERROR('Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}', 13, 1)" - @raised_deadlock_exception = true - elsif @raised_deadlock_exception == true && sql =~ /RAISERROR\('Transaction \(Process ID \d+\) was deadlocked on lock resources with another process and has been chosen as the deadlock victim\. Rerun the transaction\.: SELECT 1 as \[one\]', 13, 1\)/ - sql = "SELECT 1 as [one]" - end - execute_without_deadlock_exception(sql, *args) - end - alias :execute_without_deadlock_exception :execute - alias :execute :execute_with_deadlock_exception - end - end - - after do - # Cleanup the "stubbed" execute method. - connection.class.class_eval do - alias :execute :execute_without_deadlock_exception - remove_method :execute_with_deadlock_exception - remove_method :execute_without_deadlock_exception - end - connection.send(:remove_instance_variable, :@raised_deadlock_exception) - end - - it 'raise ActiveRecord::DeadlockVictim if retry disabled' do - assert_raise(ActiveRecord::DeadlockVictim) do - ActiveRecord::Base.transaction do - assert_equal @expected, connection.execute(@query) - end - end - end - - end - - end if connection_mode_dblib? # Since it is easier to test, but feature should work in ODBC too. - end @@ -205,12 +139,4 @@ def assert_all_odbc_statements_used_are_closed(&block) GC.enable end - def deadlock_victim_exception(sql) - require 'tiny_tds/error' - error = TinyTds::Error.new("Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}") - error.severity = 13 - error.db_error_number = 1205 - error - end - end From 169d79f6f7f8484fd8977ad69c7fcf6af704954a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 14:00:09 -0500 Subject: [PATCH 0327/1412] Guard against empty view definitions when `sb_helptext` fails silently. Fixes #337. --- CHANGELOG.md | 7 +++++++ .../connection_adapters/sqlserver/schema_statements.rb | 1 + 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5880ca2e6..9ad401f1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +## v4.2.1 + +#### Fixed + +* Guard against empty view definitions when `sb_helptext` fails silently. Fixes #337. + + ## v4.2.0 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6b6486feb..612b89613 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -415,6 +415,7 @@ def table_name_or_views_table_name(table_name) def views_real_column_name(table_name, column_name) view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] + return column_name unless view_definition match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name end From 775b3c36a958d386568ecda74251333f13e6c78c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 15:11:18 -0500 Subject: [PATCH 0328/1412] Proper table/column escaping in the `change_column_null` method. Fixes #355. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 6 ++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad401f1f..7011ab748 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Fixed * Guard against empty view definitions when `sb_helptext` fails silently. Fixes #337. +* Proper table/column escaping in the `change_column_null` method. Fixes #355. ## v4.2.0 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 612b89613..03438437d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -166,11 +166,13 @@ def columns_for_distinct(columns, orders) end def change_column_null(table_name, column_name, allow_null, default = nil) + table_id = SQLServer::Utils.extract_identifiers(table_name) + column_id = SQLServer::Utils.extract_identifiers(column_name) column = detect_column_for! table_name, column_name if !allow_null.nil? && allow_null == false && !default.nil? - do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL") + do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end - sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}" + sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, column.limit, column.precision, column.scale}" sql << ' NOT NULL' if !allow_null.nil? && allow_null == false do_execute sql end From 10531e02bf096d3744f3a2dc18b3fd6c06990191 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 15:58:03 -0500 Subject: [PATCH 0329/1412] Use `send :include` for modules for 1.9 compatibility. Fixes #383. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/transaction.rb | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7011ab748..1f27bcebb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Guard against empty view definitions when `sb_helptext` fails silently. Fixes #337. * Proper table/column escaping in the `change_column_null` method. Fixes #355. +* Use `send :include` for modules for 1.9 compatibility. Fixes #383. ## v4.2.0 diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index bdd93de37..f9d90582e 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -19,7 +19,7 @@ def current_isolation_level end - Transaction.include SQLServerTransaction + Transaction.send :include, SQLServerTransaction module SQLServerRealTransaction @@ -46,7 +46,7 @@ def reset_starting_isolation_level end - RealTransaction.include SQLServerRealTransaction + RealTransaction.send :include, SQLServerRealTransaction end end From 98fed04badb25b9ff4a34a2c40afbd8dc019c503 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 29 Jan 2015 15:58:22 -0500 Subject: [PATCH 0330/1412] Prepare for v4.2.1. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index b29fcb8af..92a3d4cdc 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.0' + VERSION = '4.2.1' end end From 348361d94553c49033c2698200485cda1915e8d2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Feb 2015 15:57:44 -0500 Subject: [PATCH 0331/1412] DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. --- CHANGELOG.md | 7 + README.md | 9 +- .../sqlserver/database_statements.rb | 65 -------- .../sqlserver/database_tasks.rb | 44 ++++++ .../connection_adapters/sqlserver_adapter.rb | 17 +-- .../tasks/sqlserver_database_tasks.rb | 131 ++++++++++++++++ test/cases/coerced_tests.rb | 14 ++ .../database_statements_test_sqlserver.rb | 57 ------- test/cases/helper_sqlserver.rb | 5 +- test/cases/rake_test_sqlserver.rb | 140 ++++++++++++++++++ 10 files changed, 347 insertions(+), 142 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/database_tasks.rb create mode 100644 lib/active_record/tasks/sqlserver_database_tasks.rb delete mode 100644 test/cases/database_statements_test_sqlserver.rb create mode 100644 test/cases/rake_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f27bcebb..73437514d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +## v4.2.2 + +#### Added + +* DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. + + ## v4.2.1 #### Fixed diff --git a/README.md b/README.md index a51b2c08e..6cf6fdbc1 100644 --- a/README.md +++ b/README.md @@ -8,14 +8,7 @@ ## Code Name Kantishna -The SQL Server adapter for ActiveRecord. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. - - -#### Testing Rake Tasks Support - -This is a long story, but if you are not working with a legacy database and you can trust your schema.rb to setup your local development or test database, then we have adapter level support for rails :db rake tasks. Please read this wiki page for full details. - -http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/rails-db-rake-tasks +The SQL Server adapter for ActiveRecord v4.2 using SQL Server 2012 or higher. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. #### Executing Stored Procedures diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b432a2c5a..bb9b16607 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -192,71 +192,6 @@ def newsequentialid_function select_value 'SELECT NEWSEQUENTIALID()' end - # === SQLServer Specific (Rake/Test Helpers) ==================== # - - def recreate_database - remove_database_connections_and_rollback do - do_execute "EXEC sp_MSforeachtable 'DROP TABLE ?'" - end - end - - def recreate_database!(database = nil) - database ||= current_database - drop_database(database) - create_database(database) - ensure - use_database(database) - end - - def drop_database(database) - retry_count = 0 - max_retries = 1 - name = SQLServer::Utils.extract_identifiers(database) - begin - do_execute " - USE master - IF EXISTS ( - SELECT * FROM [sys].[databases] - WHERE name = #{quote(name.object)} - ) DROP DATABASE #{name} - ".squish - rescue ActiveRecord::StatementInvalid => err - if err.message =~ /because it is currently in use/i - raise if retry_count >= max_retries - retry_count += 1 - remove_database_connections_and_rollback(database) - retry - elsif err.message =~ /does not exist/i - nil - else - raise - end - end - end - - def create_database(database, options = {}) - name = SQLServer::Utils.extract_identifiers(database) - options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys) - options = options.select { |_, v| v.present? } - option_string = options.inject("") do |memo, (key, value)| - memo += case key - when :collation - " COLLATE #{value}" - else - "" - end - end - do_execute "CREATE DATABASE #{name}#{option_string}" - end - - def current_database - select_value 'SELECT DB_NAME()' - end - - def charset - select_value "SELECT SERVERPROPERTY('SqlCharSetName')" - end - protected diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb new file mode 100644 index 000000000..e2fe3fc3c --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -0,0 +1,44 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module DatabaseTasks + + def create_database(database, options = {}) + name = SQLServer::Utils.extract_identifiers(database) + options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys) + options = options.select { |_, v| v.present? } + option_string = options.inject("") do |memo, (key, value)| + memo += case key + when :collation + " COLLATE #{value}" + else + "" + end + end + do_execute "CREATE DATABASE #{name}#{option_string}" + end + + def drop_database(database) + name = SQLServer::Utils.extract_identifiers(database) + do_execute "DROP DATABASE #{name}" + end + + def current_database + select_value 'SELECT DB_NAME()' + end + + def charset + select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'SqlCharSetName')" + end + + def collation + select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')" + end + + end + end + end +end + + + diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 81900e2a8..70c5969b5 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -10,6 +10,7 @@ require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' require 'active_record/connection_adapters/sqlserver/database_statements' +require 'active_record/connection_adapters/sqlserver/database_tasks' require 'active_record/connection_adapters/sqlserver/transaction' require 'active_record/connection_adapters/sqlserver/errors' require 'active_record/connection_adapters/sqlserver/schema_cache' @@ -21,6 +22,7 @@ require 'active_record/connection_adapters/sqlserver/utils' require 'active_record/sqlserver_base' require 'active_record/connection_adapters/sqlserver_column' +require 'active_record/tasks/sqlserver_database_tasks' module ActiveRecord module ConnectionAdapters @@ -31,7 +33,8 @@ class SQLServerAdapter < AbstractAdapter SQLServer::DatabaseStatements, SQLServer::Showplan, SQLServer::SchemaStatements, - SQLServer::DatabaseLimits + SQLServer::DatabaseLimits, + SQLServer::DatabaseTasks ADAPTER_NAME = 'SQLServer'.freeze @@ -250,6 +253,8 @@ def translate_exception(e, message) InvalidForeignKey.new(message, e) when /has been chosen as the deadlock victim/i DeadlockVictim.new(message, e) + when /database .* does not exist/i + NoDatabaseError.new(message, e) else super end @@ -350,16 +355,6 @@ def initialize_dateformatter ::Time::DATE_FORMATS[:_sqlserver_dateformat] = dateformat end - def remove_database_connections_and_rollback(database = nil) - name = SQLServer::Utils.extract_identifiers(database || current_database) - do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" - begin - yield - ensure - do_execute "ALTER DATABASE #{name} SET MULTI_USER" - end if block_given? - end - end end end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb new file mode 100644 index 000000000..57dedbd50 --- /dev/null +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -0,0 +1,131 @@ +require 'active_record/tasks/database_tasks' +require 'shellwords' +require 'ipaddr' +require 'socket' + +module ActiveRecord + module Tasks + + class SQLServerDatabaseTasks + + DEFAULT_COLLATION = 'SQL_Latin1_General_CP1_CI_AS' + + delegate :connection, :establish_connection, :clear_active_connections!, + to: ActiveRecord::Base + + def initialize(configuration) + @configuration = configuration + end + + def create(master_established = false) + establish_master_connection unless master_established + connection.create_database configuration['database'], configuration.merge('collation' => default_collation) + establish_connection configuration + rescue ActiveRecord::StatementInvalid => error + if /database .* already exists/i === error.message + raise DatabaseAlreadyExists + else + raise + end + end + + def drop + establish_master_connection + connection.drop_database configuration['database'] + end + + def charset + connection.charset + end + + def collation + connection.collation + end + + def purge + clear_active_connections! + drop + create true + end + + def structure_dump(filename) + command = [ + "defncopy", + "-S #{Shellwords.escape(configuration['host'])}", + "-D #{Shellwords.escape(configuration['database'])}", + "-U #{Shellwords.escape(configuration['username'])}", + "-P #{Shellwords.escape(configuration['password'])}", + "-o #{Shellwords.escape(filename)}", + ] + table_args = connection.tables.map { |t| Shellwords.escape(t) } + command.concat(table_args) + view_args = connection.views.map { |v| Shellwords.escape(v) } + command.concat(view_args) + raise 'Error dumping database' unless Kernel.system(command.join(' ')) + 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!(/text\(\d+\)/, 'text') # Fix text(16) column defs + File.open(filename, "w") { |file| file.puts dump } + end + + def structure_load(filename) + connection.execute File.read(filename) + end + + + private + + def configuration + @configuration + end + + def default_collation + configuration['collation'] || DEFAULT_COLLATION + end + + def establish_master_connection + establish_connection configuration.merge('database' => 'master') + end + + end + + module DatabaseTasksSQLServer + + extend ActiveSupport::Concern + + module ClassMethods + + LOCAL_IPADDR = [ + IPAddr.new('192.168.0.0/16'), + IPAddr.new('10.0.0.0/8'), + IPAddr.new('172.16.0.0/12') + ] + + private + + def local_database?(configuration) + super || local_ipaddr?(configuration_host_ip(configuration)) + end + + def configuration_host_ip(configuration) + return nil unless configuration['host'] + Socket::getaddrinfo(configuration['host'], 'echo', Socket::AF_INET)[0][3] + end + + def local_ipaddr?(host_ip) + return false unless host_ip + LOCAL_IPADDR.any? { |ip| ip.include?(host_ip) } + end + + end + + end + + DatabaseTasks.register_task %r{sqlserver}, SQLServerDatabaseTasks + DatabaseTasks.send :include, DatabaseTasksSQLServer + + end +end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3892b94ba..99286fb23 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -222,6 +222,20 @@ module ConnectionAdapters +module ActiveRecord + class DatabaseTasksCreateAllTest < ActiveRecord::TestCase + # We extend `local_database?` so that common VM IPs can be used. + coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases + end + class DatabaseTasksDropAllTest < ActiveRecord::TestCase + # We extend `local_database?` so that common VM IPs can be used. + coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases + end +end + + + + class DefaultScopingTest < ActiveRecord::TestCase # We are not doing order duplicate removal anymore. diff --git a/test/cases/database_statements_test_sqlserver.rb b/test/cases/database_statements_test_sqlserver.rb deleted file mode 100644 index 610af7e2c..000000000 --- a/test/cases/database_statements_test_sqlserver.rb +++ /dev/null @@ -1,57 +0,0 @@ -require 'cases/helper_sqlserver' - -class DatabaseStatementsTestSQLServer < ActiveRecord::TestCase - - self.use_transactional_fixtures = false - - after { connection.use_database } - - it 'create database' do - connection.create_database 'activerecord_unittest3' - database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" - assert_equal 'activerecord_unittest3', database_name - end - - it 'drop database' do - connection.drop_database 'activerecord_unittest3' - database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'" - assert_equal nil, database_name - end - - it 'create/use/drop database with name with dots' do - connection.drop_database '[activerecord.unittest]' rescue nil - connection.create_database '[activerecord.unittest]' - database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'" - assert_equal 'activerecord.unittest', database_name - connection.use_database '[activerecord.unittest]' - connection.use_database - connection.drop_database '[activerecord.unittest]' - end - - describe 'with collation' do - - after { connection.drop_database 'activerecord_unittest3' } - - it 'create database with default collation for the server' do - connection.create_database 'activerecord_unittest3' - default_collation = connection.select_value "SELECT SERVERPROPERTY('Collation')" - database_collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" - assert_equal default_collation, database_collation - end - - it 'create database with collation set by the method' do - connection.create_database 'activerecord_unittest3', collation: 'SQL_Latin1_General_CP1_CI_AS' - collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" - assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation - end - - it 'create database with collation set by the config' do - connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS' - connection.create_database 'activerecord_unittest3' - collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation" - assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation - end - - end - -end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 9809600ef..9ca49eef7 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -15,7 +15,6 @@ class TestCase < ActiveSupport::TestCase include ARTest::SQLServer::CoerceableTest let(:logger) { ActiveRecord::Base.logger } - let(:connection) { ActiveRecord::Base.connection } class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end @@ -30,6 +29,10 @@ def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end + def connection + ActiveRecord::Base.connection + end + end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb new file mode 100644 index 000000000..19de32a0f --- /dev/null +++ b/test/cases/rake_test_sqlserver.rb @@ -0,0 +1,140 @@ +require 'cases/helper_sqlserver' + +class SQLServerRakeTest < ActiveRecord::TestCase + + self.use_transactional_fixtures = false + + let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } + let(:new_database) { 'activerecord_unittest_tasks' } + let(:default_configuration) { ARTest.connection_config['arunit'] } + let(:configuration) { default_configuration.merge('database' => new_database) } + + before do + connection.disconnect! + end + + after do + ActiveRecord::Base.establish_connection(default_configuration) + connection.drop_database(new_database) rescue nil + end + +end + +class SQLServerRakeCreateTest < SQLServerRakeTest + + it 'establishes connection to database after create ' do + db_tasks.create configuration + connection.current_database.must_equal(new_database) + end + + it 'creates database with default collation' do + db_tasks.create configuration + connection.collation.must_equal 'SQL_Latin1_General_CP1_CI_AS' + end + + it 'creates database with given collation' do + db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') + connection.collation.must_equal 'Latin1_General_CI_AS' + end + + it 'prints error message when database exists' do + db_tasks.create configuration + message = capture(:stderr) { db_tasks.create configuration } + message.must_match %r{activerecord_unittest_tasks already exists} + end + +end + +class SQLServerRakeDropTest < SQLServerRakeTest + + it 'drops database and uses master' do + db_tasks.create configuration + db_tasks.drop configuration + connection.current_database.must_equal 'master' + end + + it 'prints error message when database does not exist' do + message = capture(:stderr) { db_tasks.drop configuration.merge('database' => 'doesnotexist') } + message.must_match %r{'doesnotexist' does not exist} + end + +end + +class SQLServerRakePurgeTest < SQLServerRakeTest + + before do + db_tasks.create(configuration) + connection.create_table :users, force: true do |t| + t.string :name, :email + t.timestamps null: false + end + end + + it 'clears active connections, drops database, and recreates with established connection' do + connection.current_database.must_equal(new_database) + connection.tables.must_include 'users' + db_tasks.purge(configuration) + connection.current_database.must_equal(new_database) + connection.tables.wont_include 'users' + end + +end + +class SQLServerRakeCharsetTest < SQLServerRakeTest + + before { db_tasks.create(configuration) } + + it 'retrieves charset' do + db_tasks.charset(configuration).must_equal 'iso_1' + end + +end + +class SQLServerRakeCollationTest < SQLServerRakeTest + + before { db_tasks.create(configuration) } + + it 'retrieves collation' do + db_tasks.collation(configuration).must_equal 'SQL_Latin1_General_CP1_CI_AS' + end + +end + +class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest + + let(:filename) { File.join ARTest::SQLServer.migrations_root, 'structure.sql' } + let(:filedata) { File.read(filename) } + + before do + db_tasks.create(configuration) + connection.create_table :users, force: true do |t| + t.string :name, :email + t.text :background1 + t.text_basic :background2 + t.timestamps null: false + end + end + + after do + FileUtils.rm_rf(filename) + end + + it 'dumps structure and accounts for defncopy oddities' do + db_tasks.structure_dump configuration, filename + filedata.wont_match %r{\AUSE.*\z} + filedata.wont_match %r{\AGO.*\z} + filedata.must_match %r{email\s+nvarchar\(4000\)} + filedata.must_match %r{background1\s+nvarchar\(max\)} + filedata.must_match %r{background2\s+text\s+} + end + + it 'can load dumped structure' do + db_tasks.structure_dump configuration, filename + filedata.must_match %r{CREATE TABLE dbo\.users} + db_tasks.purge(configuration) + connection.tables.wont_include 'users' + db_tasks.load_schema_for configuration, :sql, filename + connection.tables.must_include 'users' + end + +end From d5afc58be3badcf3f16c49eb6ee01fa748febe4c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Feb 2015 16:07:55 -0500 Subject: [PATCH 0332/1412] Note fix number. --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 73437514d..2f6a8d22c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Added -* DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. +* DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. Fixes #380. ## v4.2.1 From 7acfc14f0bb8135a57ffc0858588dc0cce3ff077 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Feb 2015 16:35:34 -0500 Subject: [PATCH 0333/1412] Provide class config for `use_output_inserted` (default true) for insert SQL. Fixed #381. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 2 +- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f6a8d22c..7bebf4c1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Added * DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. Fixes #380. +* Provide class config for `use_output_inserted` (default true) for insert SQL. Fixed #381. ## v4.2.1 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index bb9b16607..37ec75f04 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -200,7 +200,7 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = if pk + sql = if pk && self.class.use_output_inserted quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" else diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 70c5969b5..6322f24ff 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -41,9 +41,11 @@ class SQLServerAdapter < AbstractAdapter attr_reader :spid cattr_accessor :cs_equality_operator, instance_accessor: false + cattr_accessor :use_output_inserted, instance_accessor: false cattr_accessor :lowercase_schema_reflection, :showplan_option self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS' + self.use_output_inserted = true def initialize(connection, logger, pool, config) super(connection, logger, pool) From 17a8b163e428981b6981a97676a42de6a6ae71d9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Feb 2015 16:42:36 -0500 Subject: [PATCH 0334/1412] Prepare for v4.2.2 --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 92a3d4cdc..0daec04c8 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.1' + VERSION = '4.2.2' end end From bfa8cab9faed80fc7db7709f9710ec7d93a95668 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Feb 2015 16:47:43 -0500 Subject: [PATCH 0335/1412] Add a troubleshooting section. Fixes #342. --- RUNNING_UNIT_TESTS.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 12ebd1566..c02a89572 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -100,6 +100,13 @@ $ bundle exec rake test:odbc By default, Bundler will download the Rails git repo and use the git tag that matches the dependency version in our gemspec. If you want to test another version of Rails, you can either temporarily change the :tag for Rails in the Gemfile. Likewise, you can clone the Rails repo your self to another directory and use the `RAILS_SOURCE` environment variable. +## Troubleshooting + +* Make sure your firewall is off or allows SQL Server traffic both ways, typically on port 1433. +* Ensure that you are running on a local admin login to create the Rails user. +* Possibly change the SQL Server TCP/IP properties in "SQL Server Configuration Manager -> SQL Server Network Configuration -> Protocols for MSSQLSERVER", and ensure that TCP/IP is enabled and the appropriate entries on the "IP Addresses" tab are enabled. + + ## Current Expected Failures * Misc Date/Time erros when using ODBC mode. From 0384b92fb4e6228b58e91c4a747d14144f9c033a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 12 Feb 2015 12:23:54 -0500 Subject: [PATCH 0336/1412] Master is now v4.2.3 unreleased. --- CHANGELOG.md | 4 ++++ lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bebf4c1d..2f5992440 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v4.2.3 + +* n/a + ## v4.2.2 diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 0daec04c8..d88638e8d 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.2' + VERSION = '4.2.3' end end From 8c492a054be2e69fd151bf4dba254b0b01999fe7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 12 Feb 2015 12:24:33 -0500 Subject: [PATCH 0337/1412] Fix SET defaults when using Azure. --- CHANGELOG.md | 2 +- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f5992440..b82157f3e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ ## v4.2.3 -* n/a +* Fix SET defaults when using Azure. ## v4.2.2 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6322f24ff..7aaed266d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -297,7 +297,7 @@ def dblib_connect(config) client.execute('SET ANSI_NULL_DFLT_ON ON').do client.execute('SET IMPLICIT_TRANSACTIONS OFF').do client.execute('SET ANSI_PADDING ON').do - client.execute('SET QUOTED_IDENTIFIER ON') + client.execute('SET QUOTED_IDENTIFIER ON').do client.execute('SET ANSI_WARNINGS ON').do else client.execute('SET ANSI_DEFAULTS ON').do From eee322a2cbd6e1489a78d9ac4fcbda484d5f007b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 17 Feb 2015 22:06:49 -0500 Subject: [PATCH 0338/1412] Make rollback transaction transcount aware for implicit error rollbacks. Fixes #390 --- CHANGELOG.md | 2 ++ .../connection_adapters/sqlserver/database_statements.rb | 2 +- test/cases/helper_sqlserver.rb | 7 +++++++ test/cases/transaction_test_sqlserver.rb | 1 + test/cases/uuid_test_sqlserver.rb | 7 +++++++ test/config.yml | 1 + 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b82157f3e..aee793ab1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## v4.2.3 * Fix SET defaults when using Azure. +* Test insert 4-byte unicode chars. +* Make rollback transaction transcount aware for implicit error rollbacks. Fixes #390 ## v4.2.2 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 37ec75f04..0be04d370 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -66,7 +66,7 @@ def commit_db_transaction end def rollback_db_transaction - do_execute 'ROLLBACK TRANSACTION' + do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end include Savepoints diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 9ca49eef7..22e0e582e 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -33,6 +33,13 @@ def connection ActiveRecord::Base.connection end + def with_use_output_inserted_disabled + ActiveRecord::ConnectionAdapters::SQLServerAdapter.use_output_inserted = false + yield + ensure + ActiveRecord::ConnectionAdapters::SQLServerAdapter.use_output_inserted = true + end + end end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 2be91c3ee..5f9b8d23f 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require 'cases/helper_sqlserver' require 'models/ship' require 'models/developer' diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index cb2aa4930..ca19578eb 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,3 +1,4 @@ +# encoding: UTF-8 require 'cases/helper_sqlserver' class SQLServerUuidTest < ActiveRecord::TestCase @@ -10,6 +11,7 @@ class SQLServerUuidTest < ActiveRecord::TestCase end it 'can create with a new pk' do + # Type::Uuid::ACCEPTABLE_UUID obj = SSTestUuid.create! obj.id.must_be :present? obj.id.must_match acceptable_uuid @@ -38,4 +40,9 @@ class SQLServerUuidTest < ActiveRecord::TestCase column.default_function.must_equal "newid()" end + it 'can insert even when use_output_inserted to false ' do + obj = with_use_output_inserted_disabled { SSTestUuid.create!(name: "😢") } + obj.id.must_be :nil? + end + end diff --git a/test/config.yml b/test/config.yml index bc9c142b7..3521df9b6 100644 --- a/test/config.yml +++ b/test/config.yml @@ -10,6 +10,7 @@ default_connection_info: &default_connection_info password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> + encoding: utf8 connections: From 6fd5ff226a1fffb5f5fd5938cf9fb9d916a22695 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 21 Feb 2015 18:59:02 -0500 Subject: [PATCH 0339/1412] Make sure Guard is part of the bundle. --- activerecord-sqlserver-adapter.gemspec | 1 + 1 file changed, 1 insertion(+) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 80ec72d54..e1a232629 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,6 +17,7 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.0' spec.add_development_dependency 'bundler' + spec.add_development_dependency 'guard' spec.add_development_dependency 'guard-minitest' spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. spec.add_development_dependency 'minitest-focus' From 791ae183b40d88bc3b51b611c77074be8cbda2c5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 24 Feb 2015 16:20:13 -0500 Subject: [PATCH 0340/1412] Compatible with Rails 4.2.1. --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- .../connection_adapters/sqlserver/version.rb | 2 +- test/cases/column_test_sqlserver.rb | 8 -------- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aee793ab1..96b43c1df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ + +## v4.2.4 + +* Compatible with Rails 4.2.1. + + ## v4.2.3 * Fix SET defaults when using Azure. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 0be04d370..9ea17473b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -65,7 +65,7 @@ def commit_db_transaction do_execute 'COMMIT TRANSACTION' end - def rollback_db_transaction + def exec_rollback_db_transaction do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' end @@ -75,7 +75,7 @@ def create_savepoint(name = current_savepoint_name) do_execute "SAVE TRANSACTION #{name}" end - def rollback_to_savepoint(name = current_savepoint_name) + def exec_rollback_to_savepoint(name = current_savepoint_name) do_execute "ROLLBACK TRANSACTION #{name}" end diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index d88638e8d..462222ec9 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.3' + VERSION = '4.2.4' end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 575d109c4..1de9a3dcd 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -46,9 +46,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be :number? type.limit.must_equal 8 assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 - assert_raises(RangeError) { new_obj.bigint = -9_223_372_036_854_775_809 } assert_obj_set_and_save :bigint, 9_223_372_036_854_775_807 - assert_raises(RangeError) { new_obj.bigint = 9_223_372_036_854_775_808 } end it 'int(4)' do @@ -64,9 +62,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be :number? type.limit.must_equal 4 assert_obj_set_and_save :int, -2_147_483_648 - assert_raises(RangeError) { new_obj.int = -2_147_483_649 } assert_obj_set_and_save :int, 2_147_483_647 - assert_raises(RangeError) { new_obj.int = 2_147_483_648 } end it 'smallint(2)' do @@ -82,9 +78,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be :number? type.limit.must_equal 2 assert_obj_set_and_save :smallint, -32_768 - assert_raises(RangeError) { new_obj.smallint = -32_769 } assert_obj_set_and_save :smallint, 32_767 - assert_raises(RangeError) { new_obj.smallint = 32_768 } end it 'tinyint(1)' do @@ -100,9 +94,7 @@ def assert_obj_set_and_save(attribute, value) type.must_be :number? type.limit.must_equal 1 assert_obj_set_and_save :tinyint, 0 - assert_raises(RangeError) { new_obj.tinyint = -1 } assert_obj_set_and_save :tinyint, 255 - assert_raises(RangeError) { new_obj.tinyint = 256 } end it 'bit' do From 2224ad505cb8b068e4e9cf8baee4e79e52acad3d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 26 Feb 2015 19:15:09 -0500 Subject: [PATCH 0341/1412] Fix schema limit reflection for char/varchar. Fixes #394. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 8 +++++++- test/models/sqlserver/dot_table_name.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 4 ++++ 5 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/models/sqlserver/dot_table_name.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 96b43c1df..4327e5f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## v4.2.4 * Compatible with Rails 4.2.1. +* Fix schema limit reflection for char/varchar. Fixes #394. ## v4.2.3 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 03438437d..e2a9a9764 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -235,7 +235,7 @@ def column_definitions(table_name) columns.DATETIME_PRECISION AS datetime_precision, columns.ordinal_position, CASE - WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH + WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH ELSE COL_LENGTH('#{database}'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) END AS [length], CASE diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 25234b335..74e040377 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -5,10 +5,16 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase after { SSTestEdgeSchema.delete_all } it 'handle dollar symbols' do - SSTestDollarTableName.new.save + SSTestDollarTableName.create! SSTestDollarTableName.limit(20).offset(1) end + it 'handle dot table names' do + SSTestDotTableName.create! name: 'test' + SSTestDotTableName.limit(20).offset(1) + SSTestDotTableName.where(name: 'test').first.must_be :present? + end + it 'models can use tinyint pk tables' do obj = SSTestTinyintPk.create! name: '1' obj.id.is_a? Fixnum diff --git a/test/models/sqlserver/dot_table_name.rb b/test/models/sqlserver/dot_table_name.rb new file mode 100644 index 000000000..13799ab72 --- /dev/null +++ b/test/models/sqlserver/dot_table_name.rb @@ -0,0 +1,3 @@ +class SSTestDotTableName < ActiveRecord::Base + self.table_name = '[dbo].[some.Name]' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 501b812a3..486952a99 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -41,6 +41,10 @@ t.uuid :uuid_nil_default, default: nil end + create_table '[some.Name]', force: true do |t| + t.varchar :name + end + create_table 'sst_my$strange_table', force: true do |t| t.string :name end From 5ac1fbae21d1b4538460b73a0cc30c0d4200c1db Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 26 Feb 2015 19:18:34 -0500 Subject: [PATCH 0342/1412] Master is for 4.2.1.rc2 --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index e1a232629..d3f6b1520 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 4.2.0' + spec.add_dependency 'activerecord', '~> 4.2.1.rc2' spec.add_development_dependency 'bundler' spec.add_development_dependency 'guard' spec.add_development_dependency 'guard-minitest' From bfcc993ad0ba0897e0f1bb5b25038b7a36e336ec Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 20 Mar 2015 07:47:35 -0400 Subject: [PATCH 0343/1412] Prepare for v4.2.1 --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index d3f6b1520..9e7b1b57a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -15,7 +15,7 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 4.2.1.rc2' + spec.add_dependency 'activerecord', '~> 4.2.1' spec.add_development_dependency 'bundler' spec.add_development_dependency 'guard' spec.add_development_dependency 'guard-minitest' From 07c835ab3e3372f81713aee973ecfdc584a96eee Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 28 May 2015 22:03:44 -0400 Subject: [PATCH 0344/1412] Fix DB rollback when reversable add_column has several options. #359. --- CHANGELOG.md | 11 +++++++++++ .../sqlserver/schema_statements.rb | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4327e5f4e..c4cedf21d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,23 @@ +## v4.2.5 + +#### Fixed + +* DB rollback when reversable add_column has several options. Fixes #359 + + ## v4.2.4 +#### Fixed + * Compatible with Rails 4.2.1. * Fix schema limit reflection for char/varchar. Fixes #394. ## v4.2.3 +#### Fixed + * Fix SET defaults when using Azure. * Test insert 4-byte unicode chars. * Make rollback transaction transcount aware for implicit error rollbacks. Fixes #390 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e2a9a9764..c9ee206d5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -60,7 +60,7 @@ def rename_table(table_name, new_name) rename_table_indexes(table_name, new_name) end - def remove_column(table_name, column_name, _type = nil) + def remove_column(table_name, column_name, type = nil, options = {}) raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) From b917f4bc70d51eb4efcbaf04c670a6e0e46c753d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 28 May 2015 22:04:24 -0400 Subject: [PATCH 0345/1412] Master is now 4.2.5. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 462222ec9..dbd0b838f 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.4' + VERSION = '4.2.5' end end From 0391dbcf7a7e79445574fc80fea7ce3fb71e96d5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 16 Jul 2015 19:07:11 -0400 Subject: [PATCH 0346/1412] CODE OF CONDUCT --- CODE_OF_CONDUCT.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 000000000..4a4d8e858 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,31 @@ +Contributor Code of Conduct + +As contributors and maintainers of this project, we pledge to respect all +people who contribute through reporting issues, posting feature requests, +updating documentation, submitting pull requests or patches, and other +activities. + +We are committed to making participation in this project a harassment-free +experience for everyone, regardless of level of experience, gender, gender +identity and expression, sexual orientation, disability, personal appearance, +body size, race, ethnicity, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual +language or imagery, derogatory comments or personal attacks, trolling, public +or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct. Project maintainers who do not +follow the Code of Conduct may be removed from the project team. + +This code of conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by opening an issue or contacting one or more of the project +maintainers. + +This Code of Conduct is adapted from the Contributor Covenant +(http://contributor-covenant.org), version 1.1.0, available at +http://contributor-covenant.org/version/1/1/0/ From f505023d5effb7389b6a18abea0bad5c308ba663 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 15:22:53 -0400 Subject: [PATCH 0347/1412] Appveyor --- appveyor.yml | 32 ++++++++++++++++++++++++++++++++ test/appveyor/dbsetup.ps1 | 27 +++++++++++++++++++++++++++ test/appveyor/dbsetup.sql | 11 +++++++++++ 3 files changed, 70 insertions(+) create mode 100644 appveyor.yml create mode 100644 test/appveyor/dbsetup.ps1 create mode 100644 test/appveyor/dbsetup.sql diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000..0a44d805d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,32 @@ +version: 4.2.5.{build} +init: + - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% + - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% + - SET RAKEOPT=-rdevkit +clone_depth: 5 +skip_tags: true +matrix: + fast_finish: true +install: + - ruby --version + - gem --version + - bundle install --without odbc +build: off +test_script: + - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" + - timeout /t 4 /nobreak > NUL + - ps: Start-Service 'MSSQL$SQL2014' + - timeout /t 4 /nobreak > NUL + - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" + - ps: Stop-Service 'MSSQL$SQL2014' + - ps: Start-Service 'MSSQL$SQL2012SP1' + - timeout /t 4 /nobreak > NUL + - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" +environment: + matrix: + # - ruby_version: "193" + # - ruby_version: "200-x64" + # - ruby_version: "21-x64" + - ruby_version: "22-x64" diff --git a/test/appveyor/dbsetup.ps1 b/test/appveyor/dbsetup.ps1 new file mode 100644 index 000000000..7890a86e6 --- /dev/null +++ b/test/appveyor/dbsetup.ps1 @@ -0,0 +1,27 @@ + +Write-Output "Setting up..." +[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.Smo") | Out-Null +[reflection.assembly]::LoadWithPartialName("Microsoft.SqlServer.SqlWmiManagement") | Out-Null + +Write-Output "Setting variables..." +$serverName = $env:COMPUTERNAME +$instances = @('SQL2012SP1', 'SQL2014') +$smo = 'Microsoft.SqlServer.Management.Smo.' +$wmi = new-object ($smo + 'Wmi.ManagedComputer') + +Write-Output "Configure Instances..." +foreach ($instance in $instances) { + Write-Output "Instance $instance ..." + Write-Output "Enable TCP/IP and port 1433..." + $uri = "ManagedComputer[@Name='$serverName']/ServerInstance[@Name='$instance']/ServerProtocol[@Name='Tcp']" + $tcp = $wmi.GetSmoObject($uri) + $tcp.IsEnabled = $true + foreach ($ipAddress in $Tcp.IPAddresses) { + $ipAddress.IPAddressProperties["TcpDynamicPorts"].Value = "" + $ipAddress.IPAddressProperties["TcpPort"].Value = "1433" + } + $tcp.Alter() +} + +Set-Service SQLBrowser -StartupType Manual +Start-Service SQLBrowser diff --git a/test/appveyor/dbsetup.sql b/test/appveyor/dbsetup.sql new file mode 100644 index 000000000..fb96fb1be --- /dev/null +++ b/test/appveyor/dbsetup.sql @@ -0,0 +1,11 @@ +CREATE DATABASE [activerecord_unittest]; +CREATE DATABASE [activerecord_unittest2]; +GO +CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; +EXEC sp_addrolemember N'sysadmin', N'rails'; +GO +USE [activerecord_unittest]; +CREATE USER [rails] FOR LOGIN [rails]; +GO +EXEC sp_addrolemember N'db_owner', N'rails'; +GO From e7831299bf4d527690a15191a3c1d05529572e63 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 15:26:57 -0400 Subject: [PATCH 0348/1412] Use 0.7.0 TinyTDS gem. --- Gemfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index da525e270..2b46a249d 100644 --- a/Gemfile +++ b/Gemfile @@ -34,8 +34,7 @@ group :tinytds do if ENV['TINYTDS_SOURCE'] gem 'tiny_tds', path: ENV['TINYTDS_SOURCE'] else - # TODO: [Rails4] Change back... segfault caused by tiny_tds 0.6.1 - gem 'tiny_tds', git:"https://github.com/rails-sqlserver/tiny_tds.git" + gem 'tiny_tds' end end From 095abf79fb42e168cc0d6cddaa3e6c20496df125 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 15:45:59 -0400 Subject: [PATCH 0349/1412] [Appveyor] Create DB user before giving roles. --- test/appveyor/dbsetup.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/test/appveyor/dbsetup.sql b/test/appveyor/dbsetup.sql index fb96fb1be..c5f4f38b0 100644 --- a/test/appveyor/dbsetup.sql +++ b/test/appveyor/dbsetup.sql @@ -2,6 +2,7 @@ CREATE DATABASE [activerecord_unittest]; CREATE DATABASE [activerecord_unittest2]; GO CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; +GO EXEC sp_addrolemember N'sysadmin', N'rails'; GO USE [activerecord_unittest]; From 77712a7cb9fe7829086aed4a6dab6c47d43716f7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 15:51:25 -0400 Subject: [PATCH 0350/1412] [Appveyor] Assign all DB roles at one time. --- test/appveyor/dbsetup.sql | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/appveyor/dbsetup.sql b/test/appveyor/dbsetup.sql index c5f4f38b0..b998cef7c 100644 --- a/test/appveyor/dbsetup.sql +++ b/test/appveyor/dbsetup.sql @@ -3,10 +3,9 @@ CREATE DATABASE [activerecord_unittest2]; GO CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; GO -EXEC sp_addrolemember N'sysadmin', N'rails'; -GO USE [activerecord_unittest]; CREATE USER [rails] FOR LOGIN [rails]; GO EXEC sp_addrolemember N'db_owner', N'rails'; +EXEC sp_addrolemember N'sysadmin', N'rails'; GO From d78de3fa0dfb20e9eea6514bab7d2dcdc5fba10f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 15:57:35 -0400 Subject: [PATCH 0351/1412] [Appveyor] Better SP to add sysadmin role. --- test/appveyor/dbsetup.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/appveyor/dbsetup.sql b/test/appveyor/dbsetup.sql index b998cef7c..1619f9e38 100644 --- a/test/appveyor/dbsetup.sql +++ b/test/appveyor/dbsetup.sql @@ -7,5 +7,5 @@ USE [activerecord_unittest]; CREATE USER [rails] FOR LOGIN [rails]; GO EXEC sp_addrolemember N'db_owner', N'rails'; -EXEC sp_addrolemember N'sysadmin', N'rails'; +EXEC master..sp_addsrvrolemember @loginame = N'rails', @rolename = N'sysadmin' GO From 372d9cd4d7165202f86e9a7a1383901cd92c0935 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 16:22:03 -0400 Subject: [PATCH 0352/1412] [Appveyor] Try to force nokogiri load. --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 2b46a249d..68e60cb25 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gemspec gem 'bcrypt' +gem 'nokogiri' if RbConfig::CONFIG["host_os"] =~ /darwin/ gem 'terminal-notifier-guard' From 7785e88e624ef44431de83af8aba6caf53502ed0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 16:43:16 -0400 Subject: [PATCH 0353/1412] [Appyveor] Avoid Ruby 2.2 till Nokogiri is installable. --- Gemfile | 1 - appveyor.yml | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 68e60cb25..2b46a249d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,6 @@ source 'https://rubygems.org' gemspec gem 'bcrypt' -gem 'nokogiri' if RbConfig::CONFIG["host_os"] =~ /darwin/ gem 'terminal-notifier-guard' diff --git a/appveyor.yml b/appveyor.yml index 0a44d805d..6731c01fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,5 +28,4 @@ environment: matrix: # - ruby_version: "193" # - ruby_version: "200-x64" - # - ruby_version: "21-x64" - - ruby_version: "22-x64" + - ruby_version: "21-x64" From 2f09d668e6db41604b854568536a7c7e97649603 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 16:57:44 -0400 Subject: [PATCH 0354/1412] Notes on creating test DBs and user. --- RUNNING_UNIT_TESTS.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index c02a89572..1427f7ec8 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -9,7 +9,7 @@ This process is much easier than it has been before! Default testing uses DBLIB with TinyTDS. * Setup two databases in SQL Server, [activerecord_unittest] and [activerecord_unittest2] -* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. +* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. See the following screenshots or the SQL snippet below. - http://twitpic.com/9bsiyp/full - http://twitpic.com/9bsj7z/full - http://twitpic.com/9bsjdx/full @@ -17,6 +17,20 @@ Default testing uses DBLIB with TinyTDS. * $ bundle install * $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' +```sql +CREATE DATABASE [activerecord_unittest]; +CREATE DATABASE [activerecord_unittest2]; +GO +CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; +GO +USE [activerecord_unittest]; +CREATE USER [rails] FOR LOGIN [rails]; +GO +EXEC sp_addrolemember N'db_owner', N'rails'; +EXEC master..sp_addsrvrolemember @loginame = N'rails', @rolename = N'sysadmin' +GO +``` + Focusing tests. Use the `ONLY_` env vars to run either ours or the ActiveRecord cases. Use the `TEST_FILES` env variants to focus on specific test(s), use commas for multiple cases. Note, you have to use different env vars to focus only on ours or a core ActiveRecord case. There may be failures when focusing on an ActiveRecord case since our coereced test files is not loaded in this scenerio. ``` From da6a42750965962ce9ff8dfdcc68f576423c49c5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 16:59:38 -0400 Subject: [PATCH 0355/1412] [Appveyor] Use tzinfo-data gem. --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 2b46a249d..ab06b2f30 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gemspec gem 'bcrypt' +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ gem 'terminal-notifier-guard' From 112315273ef8922b0608fc21cd4b167bcd706843 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 17:04:46 -0400 Subject: [PATCH 0356/1412] [Appveyor] Add README badge for master branch. --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 6cf6fdbc1..3e2924922 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. -[![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) -[![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) +[![Build status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) From 7f0ec4c60d3cca7612d969c5f7889cab445f797b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 17:17:49 -0400 Subject: [PATCH 0357/1412] [Appveyor] Set TZ. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 6731c01fe..461877a83 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,6 +3,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit + - SET TZ='America/New_York' clone_depth: 5 skip_tags: true matrix: From 48a4f3fe55834b516328a4b4c9c2342709c9f697 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 18:19:31 -0400 Subject: [PATCH 0358/1412] [Appveyor] Add tzinfo before data. --- Gemfile | 1 + appveyor.yml | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ab06b2f30..c5c71856e 100644 --- a/Gemfile +++ b/Gemfile @@ -2,6 +2,7 @@ source 'https://rubygems.org' gemspec gem 'bcrypt' +gem 'tzinfo' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ diff --git a/appveyor.yml b/appveyor.yml index 461877a83..6731c01fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,6 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TZ='America/New_York' clone_depth: 5 skip_tags: true matrix: From c70deb2de08b50106ceb0dbf9009e6974af1a8f0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 18:35:26 -0400 Subject: [PATCH 0359/1412] [Appveyor] Set TZ via tzutil. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 6731c01fe..bfc553269 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,6 @@ version: 4.2.5.{build} init: + - C:\Windows\System32\tzutil /s "Eastern Standard Time" - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit From 17a9681351ebfcde068efc6d1fa48e7dc386038b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 18:52:49 -0400 Subject: [PATCH 0360/1412] [Appveyor] Remove TZ hacks. Will ignore tests. --- Gemfile | 1 - appveyor.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/Gemfile b/Gemfile index c5c71856e..ab06b2f30 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,6 @@ source 'https://rubygems.org' gemspec gem 'bcrypt' -gem 'tzinfo' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ diff --git a/appveyor.yml b/appveyor.yml index bfc553269..6731c01fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,6 +1,5 @@ version: 4.2.5.{build} init: - - C:\Windows\System32\tzutil /s "Eastern Standard Time" - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit From 2f900253112e38494067d09596ced31c0b7561fa Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 18:53:18 -0400 Subject: [PATCH 0361/1412] [Appveyor] Ignore structure dumps till http://git.io/v3tBk --- test/cases/helper_sqlserver.rb | 2 ++ test/cases/rake_test_sqlserver.rb | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 22e0e582e..2f2a5c762 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -20,6 +20,7 @@ class << self def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end + def host_windows? ; RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ; end end @@ -28,6 +29,7 @@ def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end + def host_windows? ; self.host_windows? ; end def connection ActiveRecord::Base.connection diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 19de32a0f..b8840f31b 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -120,6 +120,8 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'dumps structure and accounts for defncopy oddities' do + # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk + skip if host_windows? db_tasks.structure_dump configuration, filename filedata.wont_match %r{\AUSE.*\z} filedata.wont_match %r{\AGO.*\z} @@ -129,6 +131,8 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'can load dumped structure' do + # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk + skip if host_windows? db_tasks.structure_dump configuration, filename filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) From e446f1a0dd84bf514d73a77392c642979d57e256 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 19:02:49 -0400 Subject: [PATCH 0362/1412] [Appveyor] Final coerce tests for Windows. --- test/cases/coerced_tests.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 99286fb23..1459c3065 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1,6 +1,23 @@ require 'cases/helper_sqlserver' +# Windows/Appveyor +if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + # All of these are due to Time.local(2000).zone. See http://git.io/v3t0o + class BelongsToAssociationsTest < ActiveRecord::TestCase + coerce_tests! :test_belongs_to_with_touch_option_on_touch_without_updated_at_attributes + end + class BasicsTest < ActiveRecord::TestCase + coerce_tests! :test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc + coerce_tests! :test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local + coerce_tests! :test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local + end + class DirtyTest < ActiveRecord::TestCase + coerce_tests! :test_save_always_should_update_timestamps_when_serialized_attributes_are_present + end +end + + module ActiveRecord class AdapterTest < ActiveRecord::TestCase From cfcefde78c89d3aa4368c8ec30f80d8785efb112 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 19:14:55 -0400 Subject: [PATCH 0363/1412] [Appveyor] public --- test/cases/helper_sqlserver.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 2f2a5c762..03374c836 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -23,9 +23,6 @@ def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end def host_windows? ; RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ; end end - - private - def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end From c36782985306ec895b4bca55f6b81928e159a36c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 19:24:23 -0400 Subject: [PATCH 0364/1412] [Appveyor] Fix host windows check. --- test/cases/helper_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 03374c836..573ed2536 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -26,7 +26,7 @@ def host_windows? ; RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ; end def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end def sqlserver_azure? ; self.class.sqlserver_azure? ; end - def host_windows? ; self.host_windows? ; end + def host_windows? ; self.class.host_windows? ; end def connection ActiveRecord::Base.connection From afc14fafa25ec5a9e32c33f006d6010f1165d0e5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 19:34:34 -0400 Subject: [PATCH 0365/1412] [Appveyor] Coerce final test. --- test/cases/coerced_tests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1459c3065..12a1ed418 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -14,6 +14,7 @@ class BasicsTest < ActiveRecord::TestCase end class DirtyTest < ActiveRecord::TestCase coerce_tests! :test_save_always_should_update_timestamps_when_serialized_attributes_are_present + coerce_tests! :test_previous_changes # Coupled to above test. end end From ffb5a2131e62eb79943f4cd7667fff45d6bb5a48 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 19:49:11 -0400 Subject: [PATCH 0366/1412] [Appveyor] Run multiple rubies. --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 6731c01fe..e853116aa 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,6 +26,6 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" environment: matrix: - # - ruby_version: "193" - # - ruby_version: "200-x64" + - ruby_version: "193" + - ruby_version: "200-x64" - ruby_version: "21-x64" From 831e74f887a381439c40643c19cd88268213b103 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 9 Aug 2015 20:05:09 -0400 Subject: [PATCH 0367/1412] [Appveyor] Remove Ruby 1.9. --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e853116aa..c025702a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,6 +26,5 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" environment: matrix: - - ruby_version: "193" - - ruby_version: "200-x64" - ruby_version: "21-x64" + - ruby_version: "200-x64" From ab8bdd40a9c2cce71e1a9d82f88c1b393f1a014a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 16 Aug 2015 09:43:59 -0400 Subject: [PATCH 0368/1412] [ci skip] Update docs for better Rails versioning. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3e2924922..6b552ac05 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,9 @@ ## Code Name Kantishna -The SQL Server adapter for ActiveRecord v4.2 using SQL Server 2012 or higher. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter. We follow a rational versioning policy that tracks ActiveRecord. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. We also have stable branches for each major/minor release of ActiveRecord. +The SQL Server adapter for ActiveRecord v4.2 using SQL Server 2012 or higher. + +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. #### Executing Stored Procedures From 6791ca58b8a20a1553c1d8855831a74e4ad9d62f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 16 Aug 2015 16:29:47 -0400 Subject: [PATCH 0369/1412] Better running unit test docs. --- RUNNING_UNIT_TESTS.md | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 1427f7ec8..fdeed143a 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -9,28 +9,10 @@ This process is much easier than it has been before! Default testing uses DBLIB with TinyTDS. * Setup two databases in SQL Server, [activerecord_unittest] and [activerecord_unittest2] -* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. See the following screenshots or the SQL snippet below. - - http://twitpic.com/9bsiyp/full - - http://twitpic.com/9bsj7z/full - - http://twitpic.com/9bsjdx/full - - http://twitpic.com/9bsjl7/full +* Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. More details below with DDL SQL examples. * $ bundle install * $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' -```sql -CREATE DATABASE [activerecord_unittest]; -CREATE DATABASE [activerecord_unittest2]; -GO -CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; -GO -USE [activerecord_unittest]; -CREATE USER [rails] FOR LOGIN [rails]; -GO -EXEC sp_addrolemember N'db_owner', N'rails'; -EXEC master..sp_addsrvrolemember @loginame = N'rails', @rolename = N'sysadmin' -GO -``` - Focusing tests. Use the `ONLY_` env vars to run either ours or the ActiveRecord cases. Use the `TEST_FILES` env variants to focus on specific test(s), use commas for multiple cases. Note, you have to use different env vars to focus only on ours or a core ActiveRecord case. There may be failures when focusing on an ActiveRecord case since our coereced test files is not loaded in this scenerio. ``` @@ -48,6 +30,19 @@ The default names for the test databases are `activerecord_unittest` and `active The connection files make certain assumptions. For instance, the ODBC connection assumes you have a DSN setup that matches the name of the default database names. Remember too you have to set an environment variable for the DSN of the adapter, see the connection.rb file that matches your connection mode for details. +```sql +CREATE DATABASE [activerecord_unittest]; +CREATE DATABASE [activerecord_unittest2]; +GO +CREATE LOGIN [rails] WITH PASSWORD = '', CHECK_POLICY = OFF, DEFAULT_DATABASE = [activerecord_unittest]; +GO +USE [activerecord_unittest]; +CREATE USER [rails] FOR LOGIN [rails]; +GO +EXEC sp_addrolemember N'db_owner', N'rails'; +EXEC master..sp_addsrvrolemember @loginame = N'rails', @rolename = N'sysadmin' +GO +``` ## Cloning The Repos From e46e4952b44c9c923edac03b672c253ea7eef56e Mon Sep 17 00:00:00 2001 From: Faisal Mansoor Date: Thu, 17 Sep 2015 14:23:27 -0700 Subject: [PATCH 0370/1412] customize case comparision methods to improvie performace --- activerecord-sqlserver-adapter.gemspec | 2 +- bin/console | 18 ++++++++++++++++++ .../sqlserver/database_statements.rb | 16 ++++++++++++++++ .../sqlserver/schema_statements.rb | 3 ++- .../connection_adapters/sqlserver_column.rb | 8 ++++++++ lib/console.rb | 3 +++ 6 files changed, 48 insertions(+), 2 deletions(-) create mode 100755 bin/console create mode 100644 lib/console.rb diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 9e7b1b57a..b78fe0f0f 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'pry' + spec.add_development_dependency 'pry-byebug' spec.add_development_dependency 'rake' end diff --git a/bin/console b/bin/console new file mode 100755 index 000000000..b4a7988e1 --- /dev/null +++ b/bin/console @@ -0,0 +1,18 @@ +#!/bin/sh +# Run a Ruby REPL. + +set -e + +cd $(dirname "$0")/.. +PRY_PATH=$(which pry) +echo $PRY_PATH + +if [ -x $PRY_PATH ] +then + exec bundle exec $PRY_PATH -Ilib -r activerecord-sqlserver-adapter -r console +else + red='\033[0;31m' + NC='\033[0m' + echo "${red}Pry was not found or not executable. Make sure \`which pry\` returns an executable.${NC}" + +fi \ No newline at end of file diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9ea17473b..9ac0a69aa 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -87,6 +87,22 @@ def case_sensitive_modifier(node, table_attribute) Arel::Nodes::Bin.new(node) end + def case_sensitive_comparison(table, attribute, column, value) + if column.case_sensitive? + table[attribute].eq(value) + else + super + end + end + + def case_insensitive_comparison(table, attribute, column, value) + if column.case_sensitive? + super + else + table[attribute].eq(value) + end + end + # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c9ee206d5..99e3d47e9 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -45,7 +45,7 @@ def indexes(table_name, name = nil) def columns(table_name, _name = nil) return [] if table_name.blank? column_definitions(table_name).map do |ci| - sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function, :table_name + sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function, :table_name, :collation cast_type = lookup_cast_type(ci[:type]) new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options end @@ -233,6 +233,7 @@ def column_definitions(table_name) columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, columns.DATETIME_PRECISION AS datetime_precision, + columns.COLLATION_NAME AS collation, columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 5cd9fc931..80ba50d5b 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -40,6 +40,14 @@ def is_real? @sql_type =~ /real/i end + def collation + @sqlserver_options[:collation] + end + + def case_sensitive? + collation && !collation.match(/_CI/) + end + end end end diff --git a/lib/console.rb b/lib/console.rb new file mode 100644 index 000000000..48cca5ed0 --- /dev/null +++ b/lib/console.rb @@ -0,0 +1,3 @@ +Pry.config.prompt = lambda do |context, nesting, pry| + "[sqlserver] #{context} > " +end \ No newline at end of file From 628738b010c46f8a07197b0423c6e52aba5a8877 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Nov 2015 21:08:03 -0500 Subject: [PATCH 0371/1412] Fix bundle install. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index ab06b2f30..869e27a21 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ else spec = eval(File.read('activerecord-sqlserver-adapter.gemspec')) version = spec.dependencies.detect{ |d|d.name == 'activerecord' }.requirement.requirements.first.last.version major, minor, tiny = version.split('.') - uri = URI.parse "http://rubygems.org/api/v1/versions/activerecord.yaml" + uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml" YAML.load(Net::HTTP.get(uri)).select do |data| a, b, c = data['number'].split('.') !data['prerelease'] && major == a && (minor.nil? || minor == b) From a3855bcaaf742252fd22301c0cd29454521e1ef5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Nov 2015 17:09:13 -0500 Subject: [PATCH 0372/1412] Allow config to use `tds_version`. --- test/config.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/config.yml b/test/config.yml index 3521df9b6..86cc756d1 100644 --- a/test/config.yml +++ b/test/config.yml @@ -8,7 +8,6 @@ default_connection_info: &default_connection_info database: activerecord_unittest username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> - azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> encoding: utf8 @@ -19,11 +18,15 @@ connections: <<: *default_connection_info appname: SQLServerAdptrUnit dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> + tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> + azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> arunit2: <<: *default_connection_info database: activerecord_unittest2 appname: SQLServerAdptrUnit2 dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> + tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> + azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> odbc: arunit: From f5a9e8aa38fb167a0b42c0b716f1e4aca67386f7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Nov 2015 18:26:46 -0500 Subject: [PATCH 0373/1412] Remove Pry stuff. --- activerecord-sqlserver-adapter.gemspec | 2 +- bin/console | 18 ------------------ lib/console.rb | 3 --- 3 files changed, 1 insertion(+), 22 deletions(-) delete mode 100755 bin/console delete mode 100644 lib/console.rb diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index b78fe0f0f..b31b1791b 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -24,6 +24,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'pry-byebug' + spec.add_development_dependency 'byebug' spec.add_development_dependency 'rake' end diff --git a/bin/console b/bin/console deleted file mode 100755 index b4a7988e1..000000000 --- a/bin/console +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/sh -# Run a Ruby REPL. - -set -e - -cd $(dirname "$0")/.. -PRY_PATH=$(which pry) -echo $PRY_PATH - -if [ -x $PRY_PATH ] -then - exec bundle exec $PRY_PATH -Ilib -r activerecord-sqlserver-adapter -r console -else - red='\033[0;31m' - NC='\033[0m' - echo "${red}Pry was not found or not executable. Make sure \`which pry\` returns an executable.${NC}" - -fi \ No newline at end of file diff --git a/lib/console.rb b/lib/console.rb deleted file mode 100644 index 48cca5ed0..000000000 --- a/lib/console.rb +++ /dev/null @@ -1,3 +0,0 @@ -Pry.config.prompt = lambda do |context, nesting, pry| - "[sqlserver] #{context} > " -end \ No newline at end of file From 3ac0f4ac1532f7a0f20f0d008250b9fb04755873 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Nov 2015 18:42:06 -0500 Subject: [PATCH 0374/1412] Fixes #414 note. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c4cedf21d..8345d65a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed +* Improve case comparision performace per column. Fixes #414 * DB rollback when reversable add_column has several options. Fixes #359 From 08b5d03f38681f7adb2e8d8a9a9a297c22a6a45e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 08:27:55 -0500 Subject: [PATCH 0375/1412] Update appveyor. --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index c025702a2..04b5cfa2a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,10 @@ matrix: fast_finish: true install: - ruby --version + - gem update --system + - gem install bundler - gem --version + - bundle --version - bundle install --without odbc build: off test_script: From 26c28557eda8c31dc09c08edf694adbcd508b244 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 09:28:00 -0500 Subject: [PATCH 0376/1412] Revert "Update appveyor." This reverts commit 08b5d03f38681f7adb2e8d8a9a9a297c22a6a45e. --- appveyor.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 04b5cfa2a..c025702a2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,10 +9,7 @@ matrix: fast_finish: true install: - ruby --version - - gem update --system - - gem install bundler - gem --version - - bundle --version - bundle install --without odbc build: off test_script: From 7f79ed4fba6f9baa28128034214ba47523ee0d77 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 09:32:22 -0500 Subject: [PATCH 0377/1412] Use 2.1 and 2.2 for appveyor. --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c025702a2..cec2e2144 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,4 +27,5 @@ test_script: environment: matrix: - ruby_version: "21-x64" - - ruby_version: "200-x64" + - ruby_version: "22-x64" + From 114f0fd8b124869fa687db7c9a6c06fbf19d74e3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 10:06:29 -0500 Subject: [PATCH 0378/1412] Ingore net/http ssl warnings for gemfile. --- Gemfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 869e27a21..4aa11bab0 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,10 @@ else version = spec.dependencies.detect{ |d|d.name == 'activerecord' }.requirement.requirements.first.last.version major, minor, tiny = version.split('.') uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml" - YAML.load(Net::HTTP.get(uri)).select do |data| + http = Net::HTTP.new(uri.host, uri.port) + http.use_ssl = true + http.verify_mode = OpenSSL::SSL::VERIFY_NONE + YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).select do |data| a, b, c = data['number'].split('.') !data['prerelease'] && major == a && (minor.nil? || minor == b) end.first['number'] From 3db259ea697db9803c631e2fe054796a632f6cef Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 14:57:43 -0500 Subject: [PATCH 0379/1412] Test false primary non-SQL based primary dumps. Fixes #395 --- test/cases/schema_dumper_test_sqlserver.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 53f2dfd02..0f6f5987c 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -100,12 +100,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil - assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil + assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end # Special Cases - it 'primary_key' do + it 'honor nonstandard primary keys' do generate_schema_for_table('movies') do |output| match = output.match(%r{create_table "movies"(.*)do}) assert_not_nil(match, "nonstandardpk table not found") @@ -113,6 +113,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase end end + it 'no id with model driven primary key' do + output = generate_schema_for_table 'sst_no_pk_data' + output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} + assert_line :name, type: 'string', limit: '4000' + end + private From 0aebcc8abff4aba1451b39f0d228bcc862bc38b1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Nov 2015 15:16:22 -0500 Subject: [PATCH 0380/1412] Use 2.0 and 2.1 --- appveyor.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index cec2e2144..92fa4272e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,6 +26,5 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" environment: matrix: + - ruby_version: "200-x64" - ruby_version: "21-x64" - - ruby_version: "22-x64" - From 370689c32a69d804a56e13c97b57cf63925a2902 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Nov 2015 19:17:52 -0500 Subject: [PATCH 0381/1412] Coerce FK order test. --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 12a1ed418..db702cb26 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -627,6 +627,9 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced # This accidently returns the wrong number because of our tables too. coerce_tests! :test_types_line_up + # This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off. + coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues + end From 75e9f01cbb507760addac26bedbc07525474b582 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Nov 2015 19:18:32 -0500 Subject: [PATCH 0382/1412] Better column definitions for default objects. Fixes #412 --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 2 ++ test/cases/specific_schema_test_sqlserver.rb | 6 ++++++ test/models/sqlserver/object_default.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 8 ++++++++ 5 files changed, 20 insertions(+) create mode 100644 test/models/sqlserver/object_default.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8345d65a4..4c0e6daf3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Improve case comparision performace per column. Fixes #414 * DB rollback when reversable add_column has several options. Fixes #359 +* Better column definitions for default objects. Fixes #412 ## v4.2.4 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 99e3d47e9..4ab57142f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -312,6 +312,8 @@ def column_definitions(table_name) when /\A\(N'(.*)'\)\Z/m string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1]) [string_literal, nil] + when /CREATE DEFAULT/mi + [nil, nil] else type = case ci[:type] when /smallint|int|bigint/ then ci[:_type] diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 74e040377..ffc5ab626 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -62,6 +62,12 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_equal '(NULL)', default.string_with_pretend_null_four end + it 'default objects work' do + obj = SSTestObjectDefault.create! name: 'MetaSkills' + obj.date.must_be_nil 'since this is set on insert' + obj.reload.date.must_be_instance_of Date + end + # Natural primary keys. it 'work with identity inserts' do diff --git a/test/models/sqlserver/object_default.rb b/test/models/sqlserver/object_default.rb new file mode 100644 index 000000000..b3950166b --- /dev/null +++ b/test/models/sqlserver/object_default.rb @@ -0,0 +1,3 @@ +class SSTestObjectDefault < ActiveRecord::Base + self.table_name = 'sst_defaultobjects' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 486952a99..e3c619c63 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -109,6 +109,14 @@ ) TINYITPKTABLE + execute "DROP DEFAULT [sst_getdateobject];" rescue nil + execute "CREATE DEFAULT [sst_getdateobject] AS getdate();" rescue nil + create_table 'sst_defaultobjects', force: true do |t| + t.string :name + t.date :date + end + execute "sp_bindefault 'sst_getdateobject', 'sst_defaultobjects.date'" + # Constraints create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } From df8456db07aa5dff2b3ad454a2b10ee41dc33d99 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 22 Nov 2015 11:15:04 -0500 Subject: [PATCH 0383/1412] Tests for decimal scale. See Rails commit. http://git.io/vGotB --- CHANGELOG.md | 1 + test/cases/column_test_sqlserver.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0e6daf3..45d55075e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed +* Tests for decimal scale. See Rails commit. http://git.io/vGotB * Improve case comparision performace per column. Fixes #414 * DB rollback when reversable add_column has several options. Fixes #359 * Better column definitions for default objects. Fixes #412 diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 1de9a3dcd..2c507912c 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -134,7 +134,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 9 type.scale.must_equal 2 obj.decimal_9_2 = '1234567.8901' - obj.decimal_9_2.must_equal BigDecimal('1234567.8901') # Cast from user one day. + obj.decimal_9_2.must_equal BigDecimal('1234567.89') obj.save! obj.reload.decimal_9_2.must_equal BigDecimal('1234567.89') end @@ -149,7 +149,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 16 type.scale.must_equal 4 obj.decimal_16_4 = '1234567.8901001' - obj.decimal_16_4.must_equal BigDecimal('1234567.8901001') # Cast from user one day. + obj.decimal_16_4.must_equal BigDecimal('1234567.8901') obj.save! obj.reload.decimal_16_4.must_equal BigDecimal('1234567.8901') end @@ -169,7 +169,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 18 type.scale.must_equal 0 obj.numeric_18_0 = '192.1' - obj.numeric_18_0.must_equal BigDecimal('192.1') # Cast from user one day. + obj.numeric_18_0.must_equal BigDecimal('192') obj.save! obj.reload.numeric_18_0.must_equal BigDecimal('192') end @@ -189,7 +189,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 36 type.scale.must_equal 2 obj.numeric_36_2 = '192.123' - obj.numeric_36_2.must_equal BigDecimal('192.123') # Cast from user one day. + obj.numeric_36_2.must_equal BigDecimal('192.12') obj.save! obj.reload.numeric_36_2.must_equal BigDecimal('192.12') end @@ -209,7 +209,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 19 type.scale.must_equal 4 obj.money = '922337203685477.58061' - obj.money.must_equal BigDecimal('922337203685477.58061') + obj.money.must_equal BigDecimal('922337203685477.5806') obj.save! obj.reload.money.must_equal BigDecimal('922337203685477.5806') end @@ -229,7 +229,7 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 10 type.scale.must_equal 4 obj.smallmoney = '214748.36461' - obj.smallmoney.must_equal BigDecimal('214748.36461') + obj.smallmoney.must_equal BigDecimal('214748.3646') obj.save! obj.reload.smallmoney.must_equal BigDecimal('214748.3646') end From d66ed892e2f29f78417ed5c0c2d31d0c0f2d3aee Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 22 Nov 2015 11:38:32 -0500 Subject: [PATCH 0384/1412] Remove Type::Castable hacks for core type objects to force trust the DB. Allows Rails 5 attributes. --- CHANGELOG.md | 4 ++ .../connection_adapters/sqlserver/type.rb | 2 - .../sqlserver/type/boolean.rb | 1 - .../sqlserver/type/castable.rb | 15 ------- .../sqlserver/type/core_ext/value.rb | 39 ------------------- .../sqlserver/type/date.rb | 2 - .../sqlserver/type/datetime.rb | 2 - .../sqlserver/type/decimal.rb | 1 - .../sqlserver/type/float.rb | 2 - .../sqlserver/type/integer.rb | 1 - .../sqlserver/type/real.rb | 2 - .../sqlserver/type/smalldatetime.rb | 2 - .../sqlserver/type/time.rb | 3 -- test/cases/coerced_tests.rb | 8 ++-- 14 files changed, 8 insertions(+), 76 deletions(-) delete mode 100644 lib/active_record/connection_adapters/sqlserver/type/castable.rb delete mode 100644 lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 45d55075e..c1de8f002 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,10 @@ ## v4.2.5 +#### Removed + +* Remove Type::Castable hacks for core type objects to force trust the DB. Allows Rails 5 attributes. + #### Fixed * Tests for decimal scale. See Rails commit. http://git.io/vGotB diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index dc982c72b..298000274 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,6 +1,4 @@ require 'active_record/type' -require 'active_record/connection_adapters/sqlserver/type/core_ext/value.rb' -require 'active_record/connection_adapters/sqlserver/type/castable.rb' require 'active_record/connection_adapters/sqlserver/type/quoter.rb' # Exact Numerics require 'active_record/connection_adapters/sqlserver/type/integer.rb' diff --git a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb index 97fbae592..545decd17 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb @@ -4,7 +4,6 @@ module SQLServer module Type class Boolean < ActiveRecord::Type::Boolean - include Castable end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/castable.rb b/lib/active_record/connection_adapters/sqlserver/type/castable.rb deleted file mode 100644 index de8444c71..000000000 --- a/lib/active_record/connection_adapters/sqlserver/type/castable.rb +++ /dev/null @@ -1,15 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - module Type - module Castable - - def type_cast_from_database(value) - type_cast_from_ss_database? ? super : value - end - - end - end - end - end -end diff --git a/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb b/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb deleted file mode 100644 index 4069b723a..000000000 --- a/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +++ /dev/null @@ -1,39 +0,0 @@ -module ActiveRecord - module Type - class Value - - module SQLServerBehavior - - extend ActiveSupport::Concern - - included do - self.type_cast_from_ss_database = false - end - - module ClassMethods - - def type_cast_from_ss_database - @@type_cast_from_ss_database - end - - def type_cast_from_ss_database=(boolean) - @@type_cast_from_ss_database = !!boolean - end - - end - - def type_cast_from_ss_database? - self.class.type_cast_from_ss_database - end - - def type_cast_from_database(value) - type_cast_from_ss_database? ? super : value - end - - end - - include SQLServerBehavior - - 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 866ce8b75..8560d69a7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -4,8 +4,6 @@ module SQLServer module Type class Date < ActiveRecord::Type::Date - # When FreeTDS/TinyTDS casts this data type natively. - # include Castable end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index ec7f95eaf..df71f3a2e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -4,8 +4,6 @@ module SQLServer module Type class DateTime < ActiveRecord::Type::DateTime - include Castable - def type_cast_for_schema(value) value.acts_like?(:string) ? "'#{value}'" : super end diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb index c999bec92..4695a585e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb @@ -4,7 +4,6 @@ module SQLServer module Type class Decimal < ActiveRecord::Type::Decimal - include Castable end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index 486dc30ff..f99353f65 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -4,8 +4,6 @@ module SQLServer module Type class Float < ActiveRecord::Type::Float - include Castable - def type :float end diff --git a/lib/active_record/connection_adapters/sqlserver/type/integer.rb b/lib/active_record/connection_adapters/sqlserver/type/integer.rb index cbaf10f26..edfd1d6ae 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/integer.rb @@ -4,7 +4,6 @@ module SQLServer module Type class Integer < ActiveRecord::Type::Integer - include Castable end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/real.rb b/lib/active_record/connection_adapters/sqlserver/type/real.rb index 4b102bd41..de459e6ae 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/real.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/real.rb @@ -4,8 +4,6 @@ module SQLServer module Type class Real < Float - include Castable - def type :real end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 5c035d57b..5644cc79f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -4,8 +4,6 @@ module SQLServer module Type class SmallDateTime < DateTime - include Castable - private diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 08744e744..3e5eb51d2 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -11,9 +11,6 @@ def initialize(options = {}) @precision = nil if @precision == 7 end - # When FreeTDS/TinyTDS casts this data type natively. - # include Castable - def type_cast_for_database(value) return if value.nil? Quoter.new super, self diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index db702cb26..cfad8c6c2 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -64,11 +64,11 @@ def test_column_names_are_escaped_coerced assert_equal "[foo'bar]", quoted end - # This test has a few problems. First, it would require that we use - # the `Type::SQLServer::BigInteger.new(limit: 8)` for the `world_population` - # attribute. Second, since we allow the DB to win at casting for TinyTDS, - # it always comes back as a BigDecimal. + # PENDING: [Rails5.x] Remove coerced tests and use simple symbol types.. + # This test has a few problems. First, it would require that we use the + # `Type::SQLServer::BigInteger.new(limit: 8)` for the `world_population` attribute. coerce_tests! :test_numeric_fields + coerce_tests! :test_numeric_fields_with_scale # Just like PostgreSQLAdapter does. coerce_tests! :test_respect_internal_encoding From d8d0a0c6a83dec7d16678d0a7f486b46e13771ec Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 22 Nov 2015 12:13:33 -0500 Subject: [PATCH 0385/1412] Few test coercions prepare for Rails 5. --- test/cases/coerced_tests.rb | 39 +++++-------------------------------- 1 file changed, 5 insertions(+), 34 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cfad8c6c2..eef439b54 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -73,6 +73,10 @@ def test_column_names_are_escaped_coerced # Just like PostgreSQLAdapter does. coerce_tests! :test_respect_internal_encoding + # Caused in Rails v4.2.5 by adding `firm_id` column in this http://git.io/vBfMs + # commit. Trust Rails has this covered. + coerce_tests! :test_find_keeps_multiple_group_values + end @@ -406,41 +410,8 @@ class BigNumber < ActiveRecord::Base end class MigrationTest < ActiveRecord::TestCase + # PENDING: [Rails5.x] Remove coerced tests and use simple symbol types. coerce_tests! :test_add_table_with_decimals - def test_add_table_with_decimals_coerced - Person.connection.drop_table :big_numbers rescue nil - assert !BigNumber.table_exists? - GiveMeBigNumbers.up - assert BigNumber.create( - :bank_balance => 1586.43, - :big_bank_balance => BigDecimal("1000234000567.95"), - :world_population => 6000000000, - :my_house_population => 3, - :value_of_e => BigDecimal("2.7182818284590452353602875") - ) - b = BigNumber.first - assert_not_nil b - assert_not_nil b.bank_balance - assert_not_nil b.big_bank_balance - assert_not_nil b.world_population - assert_not_nil b.my_house_population - assert_not_nil b.value_of_e - # SQLServer: We rock and cast during assignment. - assert_kind_of BigDecimal, b.world_population - assert_equal BigDecimal('6000000000'), b.world_population - # TODO: Our trust the DB policy breaks this expectation. Review SQLServer::Type::Castable module. - skip - assert_kind_of Fixnum, b.my_house_population - assert_equal 3, b.my_house_population - assert_kind_of BigDecimal, b.bank_balance - assert_equal BigDecimal("1586.43"), b.bank_balance - assert_kind_of BigDecimal, b.big_bank_balance - assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance - assert_kind_of BigDecimal, b.value_of_e - assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e - GiveMeBigNumbers.down - assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first } - end end From b6d0c74ac02477ec2c7d198c0a324cdbf77a0dc2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 22 Nov 2015 13:06:00 -0500 Subject: [PATCH 0386/1412] Update gemspec. --- activerecord-sqlserver-adapter.gemspec | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index b31b1791b..6d4e8c5f6 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -6,11 +6,12 @@ Gem::Specification.new do |spec| spec.name = 'activerecord-sqlserver-adapter' spec.version = ActiveRecord::ConnectionAdapters::SQLServer::Version::VERSION spec.platform = Gem::Platform::RUBY + spec.license = 'MIT' spec.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] 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 = spec.summary + spec.description = 'ActiveRecord SQL Server Adapter. SQL Server 2012 and upward.' 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)/}) From c39a78109fd9d58d3710be6279f881a76949bc04 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 22 Nov 2015 13:46:29 -0500 Subject: [PATCH 0387/1412] Master is now 4.2.6. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index dbd0b838f..ddbb69870 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.5' + VERSION = '4.2.6' end end From 016cdc6d90663b0cd3124bc9a876182e2906d0e6 Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Mon, 23 Nov 2015 15:58:48 +0100 Subject: [PATCH 0388/1412] Use fully qualified database name when retrieving column definitions to support linked servers. --- .../sqlserver/schema_statements.rb | 18 +++++++++--------- .../connection_adapters/sqlserver/utils.rb | 4 ++++ test/cases/utils_test_sqlserver.rb | 8 ++++++++ 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 4ab57142f..0cb10a407 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -221,7 +221,7 @@ def initialize_native_database_types def column_definitions(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) - database = "#{identifier.database_quoted}." if identifier.database_quoted + database = identifier.fully_qualified_database_quoted view_exists = schema_cache.view_exists?(table_name) view_tblnm = table_name_or_views_table_name(table_name) if view_exists sql = %{ @@ -237,7 +237,7 @@ def column_definitions(table_name) columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH - ELSE COL_LENGTH('#{database}'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) + ELSE COL_LENGTH('#{database}.'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) END AS [length], CASE WHEN columns.IS_NULLABLE = 'YES' THEN 1 @@ -248,24 +248,24 @@ def column_definitions(table_name) ELSE NULL END AS [is_primary], c.is_identity AS [is_identity] - FROM #{database}INFORMATION_SCHEMA.COLUMNS columns - LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC + FROM #{database}.INFORMATION_SCHEMA.COLUMNS columns + LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' - LEFT OUTER JOIN #{database}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU + LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.COLUMN_NAME = columns.COLUMN_NAME AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA - INNER JOIN #{identifier.database_quoted}.sys.schemas AS s + INNER JOIN #{database}.sys.schemas AS s ON s.name = columns.TABLE_SCHEMA AND s.schema_id = s.schema_id - INNER JOIN #{identifier.database_quoted}.sys.objects AS o + INNER JOIN #{database}.sys.objects AS o ON s.schema_id = o.schema_id AND o.is_ms_shipped = 0 AND o.type IN ('U', 'V') AND o.name = columns.TABLE_NAME - INNER JOIN #{identifier.database_quoted}.sys.columns AS c + INNER JOIN #{database}.sys.columns AS c ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME WHERE columns.TABLE_NAME = @0 @@ -299,7 +299,7 @@ def column_definitions(table_name) if default.nil? && view_exists default = select_value " SELECT c.COLUMN_DEFAULT - FROM #{database}INFORMATION_SCHEMA.COLUMNS c + FROM #{database}.INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{view_tblnm}' AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA' end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index dc3bb4a99..faa8a1502 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -39,6 +39,10 @@ def server_quoted server ? quote(server) : server end + def fully_qualified_database_quoted + [server_quoted, database_quoted].compact.join(SEPARATOR) + end + def to_s quoted end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 8200ec5fe..b68aa9ee4 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -86,6 +86,14 @@ class UtilsTestSQLServer < ActiveRecord::TestCase SQLServer::Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' end + it 'can return fully qualified quoted table name' do + name = SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[object]') + name.fully_qualified_database_quoted.must_equal '[server.name].[database]' + + name = SQLServer::Utils.extract_identifiers('server.database.schema.object') + name.fully_qualified_database_quoted.must_equal '[server].[database]' + end + end end From 631c67759a201ddab22b8bccecf9371bc1a627c4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 23 Nov 2015 11:25:25 -0500 Subject: [PATCH 0389/1412] Allow linked servers for table names. Fixes #426. Thanks @jippeholwerda --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1de8f002..bc2c98de5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +## v4.2.6 + +#### Fixed + +* Allow linked servers for table names. Fixes #426. Thanks @jippeholwerda + + ## v4.2.5 #### Removed From caee4bbc88d6f36354ee2f311625768d99b503a1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 23 Nov 2015 11:27:09 -0500 Subject: [PATCH 0390/1412] Remove white space. --- test/cases/utils_test_sqlserver.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index b68aa9ee4..b312db899 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -89,7 +89,6 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it 'can return fully qualified quoted table name' do name = SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[object]') name.fully_qualified_database_quoted.must_equal '[server.name].[database]' - name = SQLServer::Utils.extract_identifiers('server.database.schema.object') name.fully_qualified_database_quoted.must_equal '[server].[database]' end From 92a73ca8c699c92911b9aa8ea201b34903b61bf9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 23 Nov 2015 11:27:16 -0500 Subject: [PATCH 0391/1412] Master is now 4.2.7. --- lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index ddbb69870..00aa9e669 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.6' + VERSION = '4.2.7' end end From 31924f1df6483ecb54681d1c16f2154fda242043 Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Tue, 24 Nov 2015 17:43:16 +0100 Subject: [PATCH 0392/1412] Fixed linked server compatability for SELECT queries. --- lib/arel/visitors/sqlserver.rb | 8 +++ ...lly_qualified_identifier_test_sqlserver.rb | 58 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/cases/fully_qualified_identifier_test_sqlserver.rb diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 43a3c904c..0046193d8 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -13,6 +13,14 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) + def visit_Arel_Attributes_Attribute o, collector + join_name = o.relation.table_alias || + ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( + o.relation.name + ).object_quoted + collector << "#{quote_table_name join_name}.#{quote_column_name o.name}" + end + def visit_Arel_Nodes_BindParam o, collector collector.add_bind(o) { |i| "@#{i-1}" } end diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb new file mode 100644 index 000000000..a64c8b84a --- /dev/null +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -0,0 +1,58 @@ +require 'cases/helper_sqlserver' + +class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase + it 'should use fully qualified table name in select from clause' do + table = Arel::Table.new("[my.server].db.schema.table") + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]" + assert_equal expected_sql, table.project(Arel.star).to_sql + end + + it 'should not use fully qualified table name in select projections' do + table = Arel::Table.new("[my.server].db.schema.table") + expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]" + assert_equal expected_sql, table.project(table[:name]).to_sql + end + + it 'should not use fully qualified table name in where clause' do + table = Arel::Table.new("[my.server].db.schema.table") + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" + assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql + end + + it 'should not use fully qualified table name in order clause' do + table = Arel::Table.new("[my.server].db.schema.table") + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" + assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql + end + + it 'should use table name in select projections' do + table = Arel::Table.new(:table) + expected_sql = "SELECT [table].[name] FROM [table]" + assert_equal expected_sql, table.project(table[:name]).to_sql + end + + it 'should use fully qualified table name in insert statement' do + manager = Arel::InsertManager.new(Arel::Table.engine) + manager.into Arel::Table.new("[my.server].db.schema.table") + manager.values = manager.create_values [Arel.sql('*')], %w{ a } + expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" + assert_equal expected_sql, manager.to_sql + end + + it 'should use fully qualified table name in update statement' do + table = Arel::Table.new("[my.server].db.schema.table") + manager = Arel::UpdateManager.new(Arel::Table.engine) + manager.table(table).where(table[:id].eq(42)) + manager.set([[table[:name], "Bob"]]) + expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42" + assert_equal expected_sql, manager.to_sql + end + + it 'should use fully qualified table name in delete statement' do + table = Arel::Table.new("[my.server].db.schema.table") + manager = Arel::DeleteManager.new(Arel::Table.engine) + manager.from(table).where(table[:id].eq(42)) + expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" + assert_equal expected_sql, manager.to_sql + end +end From f90848aea20d8eb17093dfea10ac00cc81a2010c Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Thu, 26 Nov 2015 17:30:49 +0100 Subject: [PATCH 0393/1412] Using a different way of doing linked server queries/commands. It is now not necessary to specify the remote server name in the table name. It can be configured on the connection. --- .../sqlserver/database_statements.rb | 2 +- .../sqlserver/schema_statements.rb | 6 ++++- .../connection_adapters/sqlserver/utils.rb | 4 +++ .../connection_adapters/sqlserver_adapter.rb | 8 ++++++ lib/arel/visitors/sqlserver.rb | 27 +++++++++++++------ test/cases/adapter_test_sqlserver.rb | 20 ++++++++++++++ test/cases/utils_test_sqlserver.rb | 19 +++++++++++++ 7 files changed, 76 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9ac0a69aa..4120c3edd 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -216,7 +216,7 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = if pk && self.class.use_output_inserted + sql = if pk && self.class.use_output_inserted && !remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" else diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 0cb10a407..c115a7cf0 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -220,7 +220,11 @@ def initialize_native_database_types end def column_definitions(table_name) - identifier = SQLServer::Utils.extract_identifiers(table_name) + if remote_server? + identifier = SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") + else + identifier = SQLServer::Utils.extract_identifiers(table_name) + end database = identifier.fully_qualified_database_quoted view_exists = schema_cache.view_exists?(table_name) view_tblnm = table_name_or_views_table_name(table_name) if view_exists diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index faa8a1502..ae272e560 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -43,6 +43,10 @@ def fully_qualified_database_quoted [server_quoted, database_quoted].compact.join(SEPARATOR) end + def fully_qualified? + parts.compact.size == 4 + end + def to_s quoted end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 7aaed266d..33d594891 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -181,6 +181,14 @@ def sqlserver_azure? @sqlserver_azure end + def remote_server? + !!database_prefix and SQLServer::Utils.extract_identifiers(@connection_options[:database_prefix]).fully_qualified? + end + + def database_prefix + @connection_options[:database_prefix] + end + def version self.class::VERSION end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 0046193d8..e518f1b7f 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -13,14 +13,6 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) - def visit_Arel_Attributes_Attribute o, collector - join_name = o.relation.table_alias || - ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( - o.relation.name - ).object_quoted - collector << "#{quote_table_name join_name}.#{quote_column_name o.name}" - end - def visit_Arel_Nodes_BindParam o, collector collector.add_bind(o) { |i| "@#{i-1}" } end @@ -81,6 +73,19 @@ def visit_Arel_Nodes_SelectStatement o, collector @select_statement = nil end + def visit_Arel_Table o, collector + table_name = if o.engine.connection.remote_server? + remote_server_table_name(o) + else + quote_table_name(o.name) + end + if o.table_alias + collector << "#{table_name} #{quote_table_name o.table_alias}" + else + collector << table_name + end + end + def visit_Arel_Nodes_JoinSource o, collector if o.left collector = visit o.left, collector @@ -193,6 +198,12 @@ def primary_Key_From_Table t column_name ? t[column_name] : nil end + def remote_server_table_name o + ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( + "#{o.engine.connection.database_prefix}#{o.name}" + ).quoted + end + end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index ba9e0a915..a9beeada3 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -416,5 +416,25 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end + describe 'remote_server?' do + after do + connection.instance_variable_get(:@connection_options).delete(:database_prefix) + end + + it 'should return false if database_prefix is not configured' do + assert_equal false, connection.remote_server? + end + + it 'should return true if database_prefix has been set' do + connection.instance_variable_get(:@connection_options)[:database_prefix] = "server.database.schema." + assert_equal true, connection.remote_server? + end + + it 'should return false if database_prefix has been set incorrectly' do + connection.instance_variable_get(:@connection_options)[:database_prefix] = "server.database.schema" + assert_equal false, connection.remote_server? + end + end + end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index b68aa9ee4..7969d1b66 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -86,6 +86,25 @@ class UtilsTestSQLServer < ActiveRecord::TestCase SQLServer::Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' end + it 'should indicate if a name is fully qualitified' do + SQLServer::Utils.extract_identifiers('object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('schema.object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('database.schema.object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('database.object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('server...object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('server.database..object').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true + SQLServer::Utils.extract_identifiers('server.database.schema.').fully_qualified?.must_equal true + SQLServer::Utils.extract_identifiers('[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false + SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true + SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true + end + it 'can return fully qualified quoted table name' do name = SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[object]') name.fully_qualified_database_quoted.must_equal '[server.name].[database]' From 188f986ba58ff3c2bea3304467b03e3584a4bfca Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Thu, 26 Nov 2015 18:38:37 +0100 Subject: [PATCH 0394/1412] Fixed failing tests. --- ...lly_qualified_identifier_test_sqlserver.rb | 102 ++++++++++-------- 1 file changed, 57 insertions(+), 45 deletions(-) diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index a64c8b84a..cb8eab58b 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -1,58 +1,70 @@ require 'cases/helper_sqlserver' class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase - it 'should use fully qualified table name in select from clause' do - table = Arel::Table.new("[my.server].db.schema.table") - expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]" - assert_equal expected_sql, table.project(Arel.star).to_sql + describe 'local server' do + it 'should use table name in select projections' do + table = Arel::Table.new(:table) + expected_sql = "SELECT [table].[name] FROM [table]" + assert_equal expected_sql, table.project(table[:name]).to_sql + end end - it 'should not use fully qualified table name in select projections' do - table = Arel::Table.new("[my.server].db.schema.table") - expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]" - assert_equal expected_sql, table.project(table[:name]).to_sql - end + describe 'remote server' do + before do + connection.instance_variable_get(:@connection_options)[:database_prefix] = "[my.server].db.schema." + end - it 'should not use fully qualified table name in where clause' do - table = Arel::Table.new("[my.server].db.schema.table") - expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" - assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql - end + after do + connection.instance_variable_get(:@connection_options).delete(:database_prefix) + end - it 'should not use fully qualified table name in order clause' do - table = Arel::Table.new("[my.server].db.schema.table") - expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" - assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql - end + it 'should use fully qualified table name in select from clause' do + table = Arel::Table.new(:table) + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]" + assert_equal expected_sql, table.project(Arel.star).to_sql + end - it 'should use table name in select projections' do - table = Arel::Table.new(:table) - expected_sql = "SELECT [table].[name] FROM [table]" - assert_equal expected_sql, table.project(table[:name]).to_sql - end + it 'should not use fully qualified table name in select projections' do + table = Arel::Table.new(:table) + expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]" + assert_equal expected_sql, table.project(table[:name]).to_sql + end - it 'should use fully qualified table name in insert statement' do - manager = Arel::InsertManager.new(Arel::Table.engine) - manager.into Arel::Table.new("[my.server].db.schema.table") - manager.values = manager.create_values [Arel.sql('*')], %w{ a } - expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" - assert_equal expected_sql, manager.to_sql - end + it 'should not use fully qualified table name in where clause' do + table = Arel::Table.new(:table) + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" + assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql + end - it 'should use fully qualified table name in update statement' do - table = Arel::Table.new("[my.server].db.schema.table") - manager = Arel::UpdateManager.new(Arel::Table.engine) - manager.table(table).where(table[:id].eq(42)) - manager.set([[table[:name], "Bob"]]) - expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42" - assert_equal expected_sql, manager.to_sql - end + it 'should not use fully qualified table name in order clause' do + table = Arel::Table.new(:table) + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" + assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql + end + + it 'should use fully qualified table name in insert statement' do + manager = Arel::InsertManager.new(Arel::Table.engine) + manager.into Arel::Table.new(:table) + manager.values = manager.create_values [Arel.sql('*')], %w{ a } + expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" + assert_equal expected_sql, manager.to_sql + end + + it 'should use fully qualified table name in update statement' do + table = Arel::Table.new(:table) + manager = Arel::UpdateManager.new(Arel::Table.engine) + manager.table(table).where(table[:id].eq(42)) + manager.set([[table[:name], "Bob"]]) + expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42" + assert_equal expected_sql, manager.to_sql + end - it 'should use fully qualified table name in delete statement' do - table = Arel::Table.new("[my.server].db.schema.table") - manager = Arel::DeleteManager.new(Arel::Table.engine) - manager.from(table).where(table[:id].eq(42)) - expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" - assert_equal expected_sql, manager.to_sql + it 'should use fully qualified table name in delete statement' do + table = Arel::Table.new(:table) + manager = Arel::DeleteManager.new(Arel::Table.engine) + manager.from(table).where(table[:id].eq(42)) + expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" + assert_equal expected_sql, manager.to_sql + end end end From dca09a0e6debeb75c9d99f99099851000baea667 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 28 Nov 2015 19:33:10 -0500 Subject: [PATCH 0395/1412] Set version dynamically for Appveyor --- VERSION | 1 + appveyor.yml | 1 + lib/active_record/connection_adapters/sqlserver/version.rb | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..4739c61f1 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +4.2.7 diff --git a/appveyor.yml b/appveyor.yml index 92fa4272e..822c9dc11 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -8,6 +8,7 @@ skip_tags: true matrix: fast_finish: true install: + - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version - gem --version - bundle install --without odbc diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 00aa9e669..3692bdc20 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer module Version - VERSION = '4.2.7' + VERSION = File.read(File.expand_path("../../../../../VERSION", __FILE__)).chomp end end From d94599b7f14a11a3ee12f599c2699976c3bcae83 Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Thu, 10 Dec 2015 14:57:01 +0100 Subject: [PATCH 0396/1412] Use proper module for the sqlserver_connection method consistent with how other adapters work. --- lib/active_record/sqlserver_base.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 0279f7182..8786b5f88 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -1,7 +1,6 @@ module ActiveRecord - class Base - - def self.sqlserver_connection(config) #:nodoc: + module ConnectionHandling + def sqlserver_connection(config) #:nodoc: config = config.symbolize_keys config.reverse_merge! mode: :dblib mode = config[:mode].to_s.downcase.underscore.to_sym @@ -17,6 +16,5 @@ def self.sqlserver_connection(config) #:nodoc: end ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) end - end end From 6bac2b176b429a1b655a6937bd15cf2e79e8160d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 28 Nov 2015 19:34:57 -0500 Subject: [PATCH 0397/1412] Dynamic appveyor version. --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 822c9dc11..fa0589343 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,3 @@ -version: 4.2.5.{build} init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% From e66106af02b297d070efbf9d60c267973b2f8c33 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 20 Dec 2015 18:36:45 -0500 Subject: [PATCH 0398/1412] Allow a specific "pre" version of TinyTDS to be tested. --- Gemfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Gemfile b/Gemfile index 4aa11bab0..ae7ac6c89 100644 --- a/Gemfile +++ b/Gemfile @@ -37,6 +37,8 @@ end group :tinytds do if ENV['TINYTDS_SOURCE'] gem 'tiny_tds', path: ENV['TINYTDS_SOURCE'] + elsif ENV['TINYTDS_VERSION'] + gem 'tiny_tds', ENV['TINYTDS_VERSION'] else gem 'tiny_tds' end From fe137e0a6dbcfc14517f379f427aaa8c6daeacb8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 23 Dec 2015 08:48:02 -0500 Subject: [PATCH 0399/1412] Code cleanup for #426 and #427 linked server support. * Rename `remote_server?` to `database_prefix_remote_server?` * Make `database_prefix_remote_server?` stronger by checking if #object is left blank. Misc refactors. * Remove `@config` init ivar. Not used. * Use `connection_options` helper in tests for terse win. cc @jippeholwerda --- CHANGELOG.md | 2 +- .../sqlserver/database_statements.rb | 2 +- .../sqlserver/schema_statements.rb | 6 +- .../connection_adapters/sqlserver_adapter.rb | 7 +- lib/arel/visitors/sqlserver.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 24 ++++--- ...lly_qualified_identifier_test_sqlserver.rb | 12 +++- test/cases/utils_test_sqlserver.rb | 66 +++++++++++-------- 8 files changed, 71 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc2c98de5..d123651e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Fixed -* Allow linked servers for table names. Fixes #426. Thanks @jippeholwerda +* Allow linked servers for table names. Fixes #426. Fixes #427. Thanks @jippeholwerda ## v4.2.5 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 4120c3edd..402571547 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -216,7 +216,7 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = if pk && self.class.use_output_inserted && !remote_server? + sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" else diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c115a7cf0..9430722a5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -220,10 +220,10 @@ def initialize_native_database_types end def column_definitions(table_name) - if remote_server? - identifier = SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") + identifier = if database_prefix_remote_server? + SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") else - identifier = SQLServer::Utils.extract_identifiers(table_name) + SQLServer::Utils.extract_identifiers(table_name) end database = identifier.fully_qualified_database_quoted view_exists = schema_cache.view_exists?(table_name) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 33d594891..90c2adddd 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -54,7 +54,6 @@ def initialize(connection, logger, pool, config) @visitor = Arel::Visitors::SQLServer.new self @prepared_statements = true # Our Responsibility - @config = config @connection_options = config connect @sqlserver_azure = !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i) @@ -181,8 +180,10 @@ def sqlserver_azure? @sqlserver_azure end - def remote_server? - !!database_prefix and SQLServer::Utils.extract_identifiers(@connection_options[:database_prefix]).fully_qualified? + def database_prefix_remote_server? + return false if database_prefix.blank? + name = SQLServer::Utils.extract_identifiers(database_prefix) + name.fully_qualified? && name.object.blank? end def database_prefix diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index e518f1b7f..67ac50e21 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -74,7 +74,7 @@ def visit_Arel_Nodes_SelectStatement o, collector end def visit_Arel_Table o, collector - table_name = if o.engine.connection.remote_server? + table_name = if o.engine.connection.database_prefix_remote_server? remote_server_table_name(o) else quote_table_name(o.name) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a9beeada3..36967323c 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -416,24 +416,28 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'remote_server?' do + describe 'database_prefix_remote_server?' do + + let(:connection_options) { connection.instance_variable_get(:@connection_options) } + after do - connection.instance_variable_get(:@connection_options).delete(:database_prefix) + connection_options.delete(:database_prefix) end - it 'should return false if database_prefix is not configured' do - assert_equal false, connection.remote_server? + it 'returns false if database_prefix is not configured' do + assert_equal false, connection.database_prefix_remote_server? end - it 'should return true if database_prefix has been set' do - connection.instance_variable_get(:@connection_options)[:database_prefix] = "server.database.schema." - assert_equal true, connection.remote_server? + it 'returns true if database_prefix has been set' do + connection_options[:database_prefix] = "server.database.schema." + assert_equal true, connection.database_prefix_remote_server? end - it 'should return false if database_prefix has been set incorrectly' do - connection.instance_variable_get(:@connection_options)[:database_prefix] = "server.database.schema" - assert_equal false, connection.remote_server? + it 'returns false if database_prefix has been set incorrectly' do + connection_options[:database_prefix] = "server.database.schema" + assert_equal false, connection.database_prefix_remote_server? end + end end diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index cb8eab58b..c6b9c22db 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -1,21 +1,27 @@ require 'cases/helper_sqlserver' class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase + + let(:connection_options) { connection.instance_variable_get(:@connection_options) } + describe 'local server' do + it 'should use table name in select projections' do table = Arel::Table.new(:table) expected_sql = "SELECT [table].[name] FROM [table]" assert_equal expected_sql, table.project(table[:name]).to_sql end + end describe 'remote server' do + before do - connection.instance_variable_get(:@connection_options)[:database_prefix] = "[my.server].db.schema." + connection_options[:database_prefix] = "[my.server].db.schema." end after do - connection.instance_variable_get(:@connection_options).delete(:database_prefix) + connection_options.delete :database_prefix end it 'should use fully qualified table name in select from clause' do @@ -66,5 +72,7 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" assert_equal expected_sql, manager.to_sql end + end + end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 79f72346f..fa000f35b 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -42,7 +42,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it 'extracts and returns #object identifier unquoted by default or quoted as needed' do valid_names.each do |n| - name = SQLServer::Utils.extract_identifiers(n) + name = extract_identifiers(n) name.object.must_equal 'object', "With #{n.inspect} for #object" name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted" end @@ -53,12 +53,12 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do present, blank = send(:"#{part}_names") present.each do |n| - name = SQLServer::Utils.extract_identifiers(n) + name = extract_identifiers(n) name.send(:"#{part}").must_equal "#{part}", "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| - name = SQLServer::Utils.extract_identifiers(n) + name = extract_identifiers(n) name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method" name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method" end @@ -67,51 +67,59 @@ class UtilsTestSQLServer < ActiveRecord::TestCase end it 'does not blow up on nil or blank string name' do - SQLServer::Utils.extract_identifiers(nil).object.must_be_nil - SQLServer::Utils.extract_identifiers(' ').object.must_be_nil + extract_identifiers(nil).object.must_be_nil + extract_identifiers(' ').object.must_be_nil end it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do - SQLServer::Utils.extract_identifiers('object').quoted.must_equal '[object]' - SQLServer::Utils.extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' - SQLServer::Utils.extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' + extract_identifiers('object').quoted.must_equal '[object]' + extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' + extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' end it 'can take a symbol argument' do - SQLServer::Utils.extract_identifiers(:object).object.must_equal 'object' + extract_identifiers(:object).object.must_equal 'object' end it 'allows identifiers with periods to work' do - SQLServer::Utils.extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' - SQLServer::Utils.extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' + extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' + extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' end it 'should indicate if a name is fully qualitified' do - SQLServer::Utils.extract_identifiers('object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('schema.object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('database.schema.object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('database.object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('server...object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('server.database..object').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true - SQLServer::Utils.extract_identifiers('server.database.schema.').fully_qualified?.must_equal true - SQLServer::Utils.extract_identifiers('[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false - SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true - SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true + extract_identifiers('object').fully_qualified?.must_equal false + extract_identifiers('schema.object').fully_qualified?.must_equal false + extract_identifiers('database.schema.object').fully_qualified?.must_equal false + extract_identifiers('database.object').fully_qualified?.must_equal false + extract_identifiers('server...object').fully_qualified?.must_equal false + extract_identifiers('server.database..object').fully_qualified?.must_equal false + extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true + extract_identifiers('server.database.schema.').fully_qualified?.must_equal true + extract_identifiers('[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false + extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true + extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true end it 'can return fully qualified quoted table name' do - name = SQLServer::Utils.extract_identifiers('[server.name].[database].[schema].[object]') + name = extract_identifiers('[my.server].db.schema.') + name.fully_qualified_database_quoted.must_equal '[my.server].[db]' + name = extract_identifiers('[server.name].[database].[schema].[object]') name.fully_qualified_database_quoted.must_equal '[server.name].[database]' - name = SQLServer::Utils.extract_identifiers('server.database.schema.object') + name = extract_identifiers('server.database.schema.object') name.fully_qualified_database_quoted.must_equal '[server].[database]' end end + private + + def extract_identifiers(name) + SQLServer::Utils.extract_identifiers(name) + end + end From d49481e2f1cfcc40ebdd9a759bae5a7949a65568 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 23 Dec 2015 09:20:48 -0500 Subject: [PATCH 0400/1412] v4.2.7 change log. --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d123651e4..48d6fb683 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,16 @@ +## v4.2.7 + +#### Changed + +* Make linked servers stronger. Fixes #427. Thanks @jippeholwerda + + ## v4.2.6 #### Fixed -* Allow linked servers for table names. Fixes #426. Fixes #427. Thanks @jippeholwerda +* Allow linked servers for table names. Fixes #426. Thanks @jippeholwerda ## v4.2.5 From bf9899041d7849c79a689a97d9eb74a571865211 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 23 Dec 2015 10:06:43 -0500 Subject: [PATCH 0401/1412] Change log. Fixes #431 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48d6fb683..61192c9f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Changed * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda +* Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda ## v4.2.6 From 4655790a301a4100b211d70f1ccfdab95c9abc05 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 23 Dec 2015 15:37:59 -0500 Subject: [PATCH 0402/1412] Test TinyTDS v0.9.5.beta.3 --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index fa0589343..6497cb5bd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit + - SET TINYTDS_VERSION=0.9.5.beta.3 clone_depth: 5 skip_tags: true matrix: From d1b7dccb060dc2f729dbfd5baf7ee5a4ada9b275 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 23 Dec 2015 17:34:06 -0500 Subject: [PATCH 0403/1412] Clean up tests by adding ARTest::SQLServer::ConnectionReflection. --- test/cases/adapter_test_sqlserver.rb | 2 - test/cases/connection_test_sqlserver.rb | 6 +-- ...lly_qualified_identifier_test_sqlserver.rb | 2 - test/cases/helper_sqlserver.rb | 24 +++++------- test/support/connection_reflection.rb | 37 +++++++++++++++++++ 5 files changed, 49 insertions(+), 22 deletions(-) create mode 100644 test/support/connection_reflection.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 36967323c..1ff2848d7 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -418,8 +418,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'database_prefix_remote_server?' do - let(:connection_options) { connection.instance_variable_get(:@connection_options) } - after do connection_options.delete(:database_prefix) end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 934f217b2..d318ce80a 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -29,7 +29,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase connection.use_database assert_equal 'activerecord_unittest', connection.current_database, 'Would default back to connection options' end - end unless sqlserver_azure? + end unless connection_sqlserver_azure? describe 'ODBC connection management' do @@ -83,7 +83,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase end end - end if connection_mode_odbc? + end if connection_odbc? describe 'Connection management' do @@ -115,7 +115,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase private def disconnect_raw_connection! - case connection.instance_variable_get(:@connection_options)[:mode] + case connection_options[:mode] when :dblib connection.raw_connection.close rescue nil when :odbc diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index c6b9c22db..02bcbb7d4 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -2,8 +2,6 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase - let(:connection_options) { connection.instance_variable_get(:@connection_options) } - describe 'local server' do it 'should use table name in select projections' do diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 573ed2536..158d30c5e 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -5,6 +5,7 @@ require 'support/load_schema_sqlserver' require 'support/coerceable_test_sqlserver' require 'support/sql_counter_sqlserver' +require 'support/connection_reflection' require 'mocha/mini_test' module ActiveRecord @@ -12,31 +13,24 @@ class TestCase < ActiveSupport::TestCase SQLServer = ActiveRecord::ConnectionAdapters::SQLServer - include ARTest::SQLServer::CoerceableTest + include ARTest::SQLServer::CoerceableTest, + ARTest::SQLServer::ConnectionReflection let(:logger) { ActiveRecord::Base.logger } - class << self - def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end - def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end - def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end - def host_windows? ; RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ; end - end - def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end - def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end - def sqlserver_azure? ; self.class.sqlserver_azure? ; end - def host_windows? ; self.class.host_windows? ; end + private - def connection - ActiveRecord::Base.connection + def host_windows? + RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end def with_use_output_inserted_disabled - ActiveRecord::ConnectionAdapters::SQLServerAdapter.use_output_inserted = false + klass = ActiveRecord::ConnectionAdapters::SQLServerAdapter + klass.use_output_inserted = false yield ensure - ActiveRecord::ConnectionAdapters::SQLServerAdapter.use_output_inserted = true + klass.use_output_inserted = true end end diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb new file mode 100644 index 000000000..1c2e08e5c --- /dev/null +++ b/test/support/connection_reflection.rb @@ -0,0 +1,37 @@ +module ARTest + module SQLServer + module ConnectionReflection + + extend ActiveSupport::Concern + + included { extend ConnectionReflection } + + def connection + ActiveRecord::Base.connection + end + + def connection_options + connection.instance_variable_get :@connection_options + end + + def connection_dblib? + connection_options[:mode] == :dblib + end + + def connection_dblib_73? + return false unless connection_dblib? + rc = connection.raw_connection + rc.respond_to?(:tds_73?) && rc.tds_73? + end + + def connection_odbc? + connection_options[:mode] == :odbc + end + + def connection_sqlserver_azure? + connection.sqlserver_azure? + end + + end + end +end From 8338d1236d974c2594d3f6cb9f4cb0674402a078 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 26 Dec 2015 09:42:55 -0500 Subject: [PATCH 0404/1412] Note usage of v0.9.5 of TinyTDS in tests. --- CHANGELOG.md | 1 + appveyor.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61192c9f1..fb91a7cc8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Changed +* Test now use latest v0.9.5 of TinyTDS. * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda * Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda diff --git a/appveyor.yml b/appveyor.yml index 6497cb5bd..f46f1d02a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.3 + - SET TINYTDS_VERSION=0.9.5.beta.4 clone_depth: 5 skip_tags: true matrix: From 19e2432ce350d8d863c60f20ce98d6376f2175a5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 26 Dec 2015 11:35:06 -0500 Subject: [PATCH 0405/1412] Silence structure dumps to avoid TinyTDS binstub puts. --- test/cases/rake_test_sqlserver.rb | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index b8840f31b..f5f821bd3 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -122,7 +122,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it 'dumps structure and accounts for defncopy oddities' do # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk skip if host_windows? - db_tasks.structure_dump configuration, filename + structure_dump configuration, filename filedata.wont_match %r{\AUSE.*\z} filedata.wont_match %r{\AGO.*\z} filedata.must_match %r{email\s+nvarchar\(4000\)} @@ -133,7 +133,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it 'can load dumped structure' do # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk skip if host_windows? - db_tasks.structure_dump configuration, filename + structure_dump configuration, filename filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) connection.tables.wont_include 'users' @@ -141,4 +141,14 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest connection.tables.must_include 'users' end + private + + def structure_dump(configuration, filename) + original_stdout = $stdout + $stdout = StringIO.new + db_tasks.structure_dump(configuration, filename) + ensure + $stdout = original_stdout + end + end From c33ebc2a222037fbaec78c8aa6b0dd55da79cff6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 26 Dec 2015 12:06:41 -0500 Subject: [PATCH 0406/1412] Silence structure dumps to avoid TinyTDS binstub puts. --- test/cases/helper_sqlserver.rb | 14 ++++++++++++++ test/cases/migration_test_sqlserver.rb | 5 ----- test/cases/rake_test_sqlserver.rb | 14 ++------------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 158d30c5e..2a659b5c0 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -33,6 +33,20 @@ def with_use_output_inserted_disabled klass.use_output_inserted = true end + def silence_stream(stream) + old_stream = stream.dup + stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') + stream.sync = true + yield + ensure + stream.reopen(old_stream) + old_stream.close + end + + def quietly + silence_stream(STDOUT) { silence_stream(STDERR) { yield } } + end + end end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 0a512f174..d880222c9 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -58,9 +58,4 @@ class MigrationTestSQLServer < ActiveRecord::TestCase end - - def quietly - silence_stream(STDOUT) { silence_stream(STDERR) { yield } } - end - end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index f5f821bd3..7b15bfb88 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -122,7 +122,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it 'dumps structure and accounts for defncopy oddities' do # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk skip if host_windows? - structure_dump configuration, filename + quietly { db_tasks.structure_dump configuration, filename } filedata.wont_match %r{\AUSE.*\z} filedata.wont_match %r{\AGO.*\z} filedata.must_match %r{email\s+nvarchar\(4000\)} @@ -133,7 +133,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it 'can load dumped structure' do # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk skip if host_windows? - structure_dump configuration, filename + quietly { db_tasks.structure_dump configuration, filename } filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) connection.tables.wont_include 'users' @@ -141,14 +141,4 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest connection.tables.must_include 'users' end - private - - def structure_dump(configuration, filename) - original_stdout = $stdout - $stdout = StringIO.new - db_tasks.structure_dump(configuration, filename) - ensure - $stdout = original_stdout - end - end From 5342848e6b924a5fbdce2b48872804fcbf78830e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 09:02:24 -0500 Subject: [PATCH 0407/1412] Support 2008 Datatypes Using TDSVER=7.3. Fixes #433 * All datetime casting uses the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. * Removed `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks. * Removed `Quoter` value type which allowed column => type special case quoting. cc @sgrif * Every time datatype has perfect micro/nano second handling. Includes schema dumping support. --- CHANGELOG.md | 15 ++ README.md | 66 ++++--- appveyor.yml | 4 +- .../connection_adapters/sqlserver/quoting.rb | 20 +-- .../sqlserver/schema_statements.rb | 5 + .../sqlserver/table_definition.rb | 8 + .../connection_adapters/sqlserver/type.rb | 4 +- .../sqlserver/type/date.rb | 9 + .../sqlserver/type/datetime.rb | 30 ++-- .../sqlserver/type/datetime2.rb | 17 ++ .../sqlserver/type/datetimeoffset.rb | 31 ++++ .../sqlserver/type/quoter.rb | 32 ---- .../sqlserver/type/smalldatetime.rb | 12 +- .../sqlserver/type/time.rb | 42 ++--- .../sqlserver/type/time_value_fractional.rb | 72 ++++++++ .../connection_adapters/sqlserver/utils.rb | 12 -- .../connection_adapters/sqlserver_adapter.rb | 17 +- test/cases/coerced_tests.rb | 7 + test/cases/column_test_sqlserver.rb | 167 +++++++++++++----- test/cases/schema_dumper_test_sqlserver.rb | 39 ++-- test/schema/datatypes/2012.sql | 26 +-- test/schema/sqlserver_specific_schema.rb | 26 +-- 22 files changed, 436 insertions(+), 225 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/datetime2.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb delete mode 100644 lib/active_record/connection_adapters/sqlserver/type/quoter.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fb91a7cc8..91e230827 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,26 @@ ## v4.2.7 +#### Added + +* Support 2008 Datatypes Using TDSVER=7.3. Fixes #433 + #### Changed * Test now use latest v0.9.5 of TinyTDS. * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda * Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda +* All datetime casting using the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. + +#### Removed + +* The `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks. +* The `Quoter` value type which allowed column => type special case quoting. + +#### Fixed + +* Every time datatype has perfect micro/nano second handling. +* All supported datatypes dump defaults properly to schema.rb ## v4.2.6 diff --git a/README.md b/README.md index 6b552ac05..bec8e6a15 100644 --- a/README.md +++ b/README.md @@ -25,36 +25,50 @@ Account.execute_procedure :update_totals, named: 'params' #### Native Data Type Support -We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. +We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. ```ruby -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' } -timestamp: { name: 'datetime' } -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' } +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', precision: 7 } +datetimeoffset: { name: 'datetimeoffset', precision: 7 } +smalldatetime: { name: 'smalldatetime' } +timestamp: { name: 'datetime' } +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' } ``` +The following types require TDS version 7.3 with TinyTDS. This requires the latest FreeTDS v0.95 or higher. + +* date +* datetime2 +* datetimeoffset +* time + +Set `tds_version` in your database.yml or the `TDSVER` environment variable to `7.3` to ensure you are using the proper protocol version till 7.3 becomes the default. + +**Zone Conversion** - The `[datetimeoffset]` type is the only ActiveRecord time based datatype that does not cast the zone to ActiveRecord's default - typically UTC. As intended, this datatype is meant to maintain the zone you pass to it and/or retreived from the database. + #### Force Schema To Lowercase diff --git a/appveyor.yml b/appveyor.yml index f46f1d02a..450e2c4cd 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.4 + - SET TINYTDS_VERSION=0.9.5.beta.7 clone_depth: 5 skip_tags: true matrix: @@ -20,11 +20,13 @@ test_script: - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - ps: Stop-Service 'MSSQL$SQL2014' - ps: Start-Service 'MSSQL$SQL2012SP1' - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: matrix: - ruby_version: "200-x64" diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 3678459ae..767b4faaf 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -40,15 +40,10 @@ def unquoted_false end def quoted_date(value) - SQLServer::Utils.with_sqlserver_db_date_formats do - if value.acts_like?(:time) && value.respond_to?(:usec) - precision = (BigDecimal(value.usec.to_s) / 1_000_000).round(3).to_s.split('.').last - "#{super}.#{precision}" - elsif value.acts_like?(:date) - value.to_s(:_sqlserver_dateformat) - else - super - end + if value.acts_like?(:date) + Type::Date.new.type_cast_for_database(value) + else value.acts_like?(:time) + Type::DateTime.new.type_cast_for_database(value) end end @@ -59,8 +54,6 @@ def _quote(value) case value when Type::Binary::Data "0x#{value.hex}" - when SQLServer::Type::Quoter - value.quote_ss_value when String, ActiveSupport::Multibyte::Chars if value.is_utf8? "#{QUOTED_STRING_PREFIX}#{super}" @@ -72,11 +65,6 @@ def _quote(value) end end - def quoted_value_acts_like_time_filter(value) - zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal - value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9430722a5..82fb5aaae 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -201,6 +201,9 @@ def initialize_native_database_types real: { name: 'real' }, date: { name: 'date' }, datetime: { name: 'datetime' }, + datetime2: { name: 'datetime2' }, + datetimeoffset: { name: 'datetimeoffset' }, + smalldatetime: { name: 'smalldatetime' }, timestamp: { name: 'datetime' }, time: { name: 'time' }, char: { name: 'char' }, @@ -286,6 +289,8 @@ def column_definitions(table_name) ci[:type] = case ci[:type] when /^bit|image|text|ntext|datetime$/ ci[:type] + when /^datetime2|datetimeoffset$/i + "#{ci[:type]}(#{ci[:datetime_precision]})" when /^time$/i "#{ci[:type]}(#{ci[:datetime_precision]})" when /^numeric|decimal$/i diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index b438f4844..1fcc5f757 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -18,6 +18,14 @@ def money(name, options = {}) column(name, :money, options) end + def datetime2(name, options = {}) + column(name, :datetime2, options) + end + + def datetimeoffset(name, options = {}) + column(name, :datetimeoffset, options) + end + def smallmoney(name, options = {}) column(name, :smallmoney, options) end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 298000274..d115417c7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,5 +1,4 @@ require 'active_record/type' -require 'active_record/connection_adapters/sqlserver/type/quoter.rb' # Exact Numerics require 'active_record/connection_adapters/sqlserver/type/integer.rb' require 'active_record/connection_adapters/sqlserver/type/big_integer.rb' @@ -13,8 +12,11 @@ require 'active_record/connection_adapters/sqlserver/type/float.rb' require 'active_record/connection_adapters/sqlserver/type/real.rb' # Date and Time +require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb' require 'active_record/connection_adapters/sqlserver/type/date.rb' require 'active_record/connection_adapters/sqlserver/type/datetime.rb' +require 'active_record/connection_adapters/sqlserver/type/datetime2.rb' +require 'active_record/connection_adapters/sqlserver/type/datetimeoffset.rb' require 'active_record/connection_adapters/sqlserver/type/smalldatetime.rb' require 'active_record/connection_adapters/sqlserver/type/time.rb' # Character Strings diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 8560d69a7..ae086b1ce 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -4,6 +4,15 @@ module SQLServer module Type class Date < ActiveRecord::Type::Date + def type_cast_for_database(value) + return unless value.present? + return value if value.acts_like?(:string) + value.to_s(:_sqlserver_dateformat) + end + + def type_cast_for_schema(value) + type_cast_for_database(value).inspect + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index df71f3a2e..0ab38f03a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -4,28 +4,34 @@ module SQLServer module Type class DateTime < ActiveRecord::Type::DateTime + include TimeValueFractional + + def type_cast_for_database(value) + return super unless value.acts_like?(:time) + value = zone_conversion(value) + datetime = value.to_s(:_sqlserver_datetime) + "#{datetime}".tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end + end + def type_cast_for_schema(value) - value.acts_like?(:string) ? "'#{value}'" : super + type_cast_for_database(value).inspect end private def cast_value(value) - value = value.respond_to?(:usec) ? value : super + value = value.acts_like?(:time) ? value : super return unless value - value.change usec: cast_usec(value) - end - - def cast_usec(value) - return 0 if !value.respond_to?(:usec) || value.usec.zero? - seconds = value.usec.to_f / 1_000_000.0 - ss_seconds = ((seconds * (1 / second_precision)).round / (1 / second_precision)).round(3) - (ss_seconds * 1_000_000).to_i + cast_fractional(value) end - def second_precision - 0.00333 + def zone_conversion(value) + method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal + value.respond_to?(method) ? value.send(method) : value end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb new file mode 100644 index 000000000..02ac6010b --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -0,0 +1,17 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DateTime2 < DateTime + + include TimeValueFractional2 + + def type + :datetime2 + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb new file mode 100644 index 000000000..17b4a1a50 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -0,0 +1,31 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DateTimeOffset < DateTime2 + + def type + :datetimeoffset + end + + def type_cast_for_database(value) + return super unless value.acts_like?(:time) + value.to_s :_sqlserver_datetimeoffset + end + + def type_cast_for_schema(value) + type_cast_for_database(value).inspect + end + + + private + + def zone_conversion(value) + value + end + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/type/quoter.rb b/lib/active_record/connection_adapters/sqlserver/type/quoter.rb deleted file mode 100644 index b596ab40b..000000000 --- a/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +++ /dev/null @@ -1,32 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - module Type - class Quoter - - attr_reader :value, :type - - def initialize(value, type = nil) - @value = value - @type = type - end - - def to_s - @value.to_s - end - alias_method :to_str, :to_s - - def ==(other) - other == to_s || super - end - alias_method :eql?, :== - - def quote_ss_value - type.quote_ss(value) - end - - end - end - end - end -end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 5644cc79f..42b1cdc0d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -4,15 +4,15 @@ module SQLServer module Type class SmallDateTime < DateTime + def type + :smalldatetime + end - private - def cast_usec(value) - 0 - end + private - def cast_usec_for_database(value) - '.000' + def cast_fractional(value) + value.change usec: 0 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 3e5eb51d2..36245f479 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -1,52 +1,36 @@ -Time::DATE_FORMATS[:_sqlserver_time] = '%H:%M:%S' - module ActiveRecord module ConnectionAdapters module SQLServer module Type class Time < ActiveRecord::Type::Time - def initialize(options = {}) - super - @precision = nil if @precision == 7 - end + include TimeValueFractional2 def type_cast_for_database(value) - return if value.nil? - Quoter.new super, self + return super unless value.acts_like?(:time) + time = value.to_s(:_sqlserver_time) + "#{time}".tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end end def type_cast_for_schema(value) - value.acts_like?(:string) ? "'#{value}'" : super - end - - def quote_ss(value) - return unless value - value = cast_value(value) if value.acts_like?(:string) - date = value.to_s(:_sqlserver_time) - frac = quote_usec(value) - "'#{date}.#{frac}'" + type_cast_for_database(value).inspect end private def cast_value(value) - value = value.respond_to?(:usec) ? value.change(year: 2000, month: 01, day: 01) : super + value = value.acts_like?(:time) ? value : super return if value.blank? - value.change usec: cast_usec(value) - end - - def cast_usec(value) - (usec_to_seconds_frction(value) * 1_000_000).to_i - end - - def usec_to_seconds_frction(value) - (value.usec.to_f / 1_000_000.0).round(precision || 7) + value = value.change year: 2000, month: 01, day: 01 + cast_fractional(value) end - def quote_usec(value) - usec_to_seconds_frction(value).to_s.split('.').last + def fractional_scale + precision end 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 new file mode 100644 index 000000000..cd70a7419 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb @@ -0,0 +1,72 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + + module TimeValueFractional + + private + + def cast_fractional(value) + return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + frac_seconds = (seconds * fractional_operator).to_i + value.change fractional_property => frac_seconds + end + + def quote_fractional(value) + seconds = (value.send(fractional_property).to_f / fractional_operator.to_f).round(fractional_scale) + seconds.to_s.split('.').last.to(fractional_scale-1) + end + + def fractional_property + :usec + end + + def fractional_digits + 6 + end + + def fractional_operator + 10 ** fractional_digits + end + + def fractional_precision + 0.00333 + end + + def fractional_scale + 3 + end + + end + + module TimeValueFractional2 + + include TimeValueFractional + + private + + def fractional_property + :nsec + end + + def fractional_digits + 9 + end + + def fractional_precision + 0.0000001 + end + + def fractional_scale + precision + end + + end + + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index ae272e560..7d364013a 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -122,18 +122,6 @@ def extract_identifiers(name) SQLServer::Utils::Name.new(name) end - def with_sqlserver_db_date_formats - old_db_format_date = Date::DATE_FORMATS[:db] - old_db_format_time = Time::DATE_FORMATS[:db] - date_format = Date::DATE_FORMATS[:_sqlserver_dateformat] - Date::DATE_FORMATS[:db] = "#{date_format}" - Time::DATE_FORMATS[:db] = "#{date_format} %H:%M:%S" - yield - ensure - Date::DATE_FORMATS[:db] = old_db_format_date - Time::DATE_FORMATS[:db] = old_db_format_time - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 90c2adddd..032a29093 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -230,6 +230,14 @@ def initialize_type_map(m) # Date and Time m.register_type 'date', SQLServer::Type::Date.new m.register_type 'datetime', SQLServer::Type::DateTime.new + m.register_type %r{\Adatetime2}i do |sql_type| + precision = extract_precision(sql_type) + SQLServer::Type::DateTime2.new precision: precision + end + m.register_type %r{\Adatetimeoffset}i do |sql_type| + 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| scale = extract_scale(sql_type) @@ -362,8 +370,13 @@ def initialize_dateformatter a, b, c = @database_dateformat.each_char.to_a [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 + ::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}" + } end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index eef439b54..9d070d103 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -603,6 +603,13 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced end +class SchemaDumperDefaultsTest < ActiveRecord::TestCase + + # These date formats do not match ours. We got these covered in our dumper tests. + coerce_tests! :test_schema_dump_defaults_with_universally_supported_types + +end + diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 2c507912c..07adbd86a 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -284,7 +284,7 @@ def assert_obj_set_and_save(attribute, value) col = column('date') col.sql_type.must_equal 'date' col.null.must_equal true - col.default.must_equal '0001-01-01' # TODO: None type casted default. Really want Date.civil(0001, 1, 1). + col.default.must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' obj.date.must_equal Date.civil(0001, 1, 1) col.default_function.must_equal nil type = col.cast_type @@ -312,8 +312,8 @@ def assert_obj_set_and_save(attribute, value) col = column('datetime') col.sql_type.must_equal 'datetime' col.null.must_equal true - col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) - obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 000) + col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>" + obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>" col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::DateTime @@ -322,7 +322,7 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil - # Can save .003 seconds and return again. + # Can save to proper accuracy and return again. obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000) obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! @@ -334,6 +334,80 @@ def assert_obj_set_and_save(attribute, value) obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" end + it 'datetime2' do + skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? + col = column('datetime2_7') + col.sql_type.must_equal 'datetime2(7)' + col.null.must_equal true + col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>" + obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::DateTime2 + type.type.must_equal :datetime2 + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Can save 100 nanosecond precisoins and return again. + obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000)) + obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.save! + obj.reload.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + # With other precisions. + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) + col = column('datetime2_3') + col.cast_type.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.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" + col = column('datetime2_1') + col.cast_type.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.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" + end + + it 'datetimeoffset' do + skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? + col = column('datetimeoffset_7') + col.sql_type.must_equal 'datetimeoffset(7)' + col.null.must_equal true + col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" + obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::DateTimeOffset + type.type.must_equal :datetimeoffset + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Can save 100 nanosecond precisoins and return again. + obj.datetimeoffset_7 = Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456755) + obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.save! ; obj.reload + obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + # With other precisions. + time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) + col = column('datetimeoffset_3') + col.cast_type.precision.must_equal 3 + obj.datetimeoffset_3 = time + obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + # TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44 + return + obj.save! ; obj.reload + obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + col = column('datetime2_1') + col.cast_type.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.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" + end + it 'smalldatetime' do col = column('smalldatetime') col.sql_type.must_equal 'smalldatetime' @@ -343,7 +417,7 @@ def assert_obj_set_and_save(attribute, value) col.default_function.must_equal nil type = col.cast_type type.must_be_instance_of Type::SmallDateTime - type.type.must_equal :datetime + type.type.must_equal :smalldatetime type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil @@ -355,7 +429,41 @@ def assert_obj_set_and_save(attribute, value) obj.reload.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end + it 'time(7)' do + skip 'time() not supported in this protocal version' unless connection_dblib_73? + col = column('time_7') + col.sql_type.must_equal 'time(7)' + col.null.must_equal true + col.default.must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" + col.default_function.must_equal nil + type = col.cast_type + type.must_be_instance_of Type::Time + type.type.must_equal :time + type.wont_be :number? + type.limit.must_equal nil + type.precision.must_equal 7 + type.scale.must_equal nil + # Time's #usec precision (low micro) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + # Time's #usec precision (high micro) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + # Time's #usec precision (high nano rounded) + obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + obj.save! ; obj.reload + obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + end + it 'time(2)' do + skip 'time() not supported in this protocal version' unless connection_dblib_73? col = column('time_2') col.sql_type.must_equal 'time(2)' col.null.must_equal true @@ -368,56 +476,21 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_equal nil type.precision.must_equal 2 type.scale.must_equal nil - # Always uses ActiveRecord's 2000-01-01 convention too. + # Always uses TinyTDS/Windows 2000-01-01 convention too. obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) - # Midnight the beggining of the day. - obj.time_2 = Time.utc(2000, 01, 01).midnight.change(usec: 0) - obj.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 00, 00, 00, 0) - # The end of day. - obj.time_2 = Time.utc(2000, 01, 01).end_of_day.change(usec: 0) - obj.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 23, 59, 59, 0) + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.reload.time_2.usec}> vs <30000>" + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" # Time's #usec precision (below 2 precision) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" - obj.save! - obj.reload.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.reload.time_2.usec}> vs <0>" - end - - it 'time(7)' do - col = column('time_7') - col.sql_type.must_equal 'time(7)' - col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil - type = col.cast_type - type.must_be_instance_of Type::Time - type.type.must_equal :time - type.wont_be :number? - type.limit.must_equal nil - type.precision.must_equal nil, 'so it is clean in schema dumper' - type.scale.must_equal nil - # Time's #usec precision (low) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <300>" - obj.save! - obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.reload.time_7.usec}> vs <300>" - # Time's #usec precision (high) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" - obj.save! - obj.reload.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.reload.time_7.usec}> vs <234567>" + obj.save! ; obj.reload + obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end # Character Strings diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 0f6f5987c..7845915a4 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -24,11 +24,18 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} # Date and Time - assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "'0001-01-01'" - assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1753-01-01 00:00:00'" - assert_line :smalldatetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "'1901-01-01 15:45:00'" + assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "\"01-01-0001\"" + assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "\"01-01-1753 00:00:00.123\"" + if connection_dblib_73? + assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: "\"12-31-9999 23:59:59.9999999\"" + assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: '3', scale: nil, default: nil + assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: '1', scale: nil, default: nil + end + assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "\"01-01-1901 15:45:00\"" + if connection_dblib_73? + assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: "\"04:20:00.2883215\"" assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil - assert_line :time_7, type: 'time', limit: nil, precision: nil, scale: nil, default: nil + end # Character Strings assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" @@ -73,12 +80,14 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: nil, scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil # Our type methods. columns['real_col'].sql_type.must_equal 'real' columns['money_col'].sql_type.must_equal 'money' + 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)' @@ -91,6 +100,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['sstimestamp_col'].sql_type.must_equal 'timestamp' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil + assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil @@ -145,12 +155,19 @@ def line(column_name) def assert_line(column_name, options={}) line = line(column_name) assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" - line.type_method.must_equal options[:type], "Type of #{options[:type].inspect} not found in:\n #{line}" if options.key?(:type) - line.limit.must_equal options[:limit], "Limit of #{options[:limit].inspect} not found in:\n #{line}" if options.key?(:limit) - line.precision.must_equal options[:precision], "Precision of #{options[:precision].inspect} not found in:\n #{line}" if options.key?(:precision) - line.scale.must_equal options[:scale], "Scale of #{options[:scale].inspect} not found in:\n #{line}" if options.key?(:scale) - line.default.must_equal options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(String) - line.default.must_match options[:default], "Default of #{options[:default].inspect} not found in:\n #{line}" if options.key?(:default) && options[:default].is_a?(Regexp) + [:type, :limit, :precision, :scale, :default].each do |key| + next unless options.key?(key) + actual = key == :type ? line.send(:type_method) : line.send(key) + expected = options[key] + message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" + if expected.nil? + actual.must_be_nil message + elsif expected.is_a?(Regexp) + actual.must_match expected, message + else + actual.must_equal expected, message + end + end end class SchemaLine diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 8bc5a3946..5c444551d 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -24,10 +24,16 @@ CREATE TABLE [sst_datatypes] ( [real] [real] NULL DEFAULT 123.45, -- Date and Time [date] [date] NULL DEFAULT '0001-01-01', - [datetime] [datetime] NULL DEFAULT '1753-01-01T00:00:00.000', + [datetime] [datetime] NULL DEFAULT '1753-01-01T00:00:00.123', + [datetime2_7] [datetime2](7) NULL DEFAULT '9999-12-31 23:59:59.9999999', + [datetime2_3] [datetime2](3) NULL, + [datetime2_1] [datetime2](1) NULL, + [datetimeoffset_7] [datetimeoffset](7) NULL DEFAULT '1984-01-24 04:20:00.1234567 -08:00', + [datetimeoffset_3] [datetimeoffset](3) NULL, + [datetimeoffset_1] [datetimeoffset](1) NULL, [smalldatetime] [smalldatetime] NULL DEFAULT '1901-01-01T15:45:00.000Z', + [time_7] [time](7) NULL DEFAULT '04:20:00.2883215', [time_2] [time](2) NULL, - [time_7] [time](7) NULL, -- Character Strings [char_10] [char](10) NULL DEFAULT '1234567890', [varchar_50] [varchar](50) NULL DEFAULT 'test varchar_50', @@ -46,19 +52,3 @@ CREATE TABLE [sst_datatypes] ( [uniqueidentifier] [uniqueidentifier] NULL DEFAULT NEWID(), [timestamp] [timestamp] NULL, ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY] - --- Date and Time (TODO) --- -------------------- --- [datetime2_7] [datetime2](7) NULL, --- [datetimeoffset_2] [datetimeoffset](2) NULL, --- [datetimeoffset_7] [datetimeoffset](7) NULL, --- --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 71, '0001-01-01T00:00:00.0000000Z' ) --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 72, '1984-01-24T04:20:00.0000000-08:00' ) --- INSERT INTO [sst_datatypes] ([id], [datetime2_7]) VALUES ( 73, '9999-12-31T23:59:59.9999999Z' ) --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 81, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.00 -08:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 82, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.00 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_2]) VALUES ( 83, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.99 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 84, '1984-01-24T04:20:00.0000000-08:00' ) -- 1984-01-24 04:20:00.0000000 -08:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 85, '1984-01-24T04:20:00.0000000Z' ) -- 1984-01-24 04:20:00.0000000 +00:00 --- INSERT INTO [sst_datatypes] ([id], [datetimeoffset_7]) VALUES ( 86, '9999-12-31T23:59:59.9999999Z' ) -- 9999-12-31 23:59:59.9999999 +00:00 diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index e3c619c63..e7c865854 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -19,18 +19,20 @@ t.date :date_col t.binary :binary_col # Our type methods. - t.real :real_col - t.money :money_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.varbinary :varbinary_col - t.uuid :uuid_col - t.ss_timestamp :sstimestamp_col + t.real :real_col + t.money :money_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.varbinary :varbinary_col + t.uuid :uuid_col + t.ss_timestamp :sstimestamp_col end # Edge Cases From 11297e7031850241327c3742a1402a6aeb78e339 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 10:06:22 -0500 Subject: [PATCH 0408/1412] Windows now tests defncopy!!! --- CHANGELOG.md | 2 +- test/cases/rake_test_sqlserver.rb | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 91e230827..42f26fb18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ #### Changed -* Test now use latest v0.9.5 of TinyTDS. +* Test now use latest v0.9.5 of TinyTDS. Includes tests for `defncopy` Windows binstub. * Make linked servers stronger. Fixes #427. Thanks @jippeholwerda * Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda * All datetime casting using the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 7b15bfb88..a84294cc6 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -120,8 +120,6 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'dumps structure and accounts for defncopy oddities' do - # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk - skip if host_windows? quietly { db_tasks.structure_dump configuration, filename } filedata.wont_match %r{\AUSE.*\z} filedata.wont_match %r{\AGO.*\z} @@ -131,8 +129,6 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'can load dumped structure' do - # CHANGED: [TinyTDS] When utilities are available http://git.io/v3tBk - skip if host_windows? quietly { db_tasks.structure_dump configuration, filename } filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) From 27005bf3619668b12859d7cde0cf7276697091bd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 10:31:21 -0500 Subject: [PATCH 0409/1412] Quietly does nothing on windows host. Appveyor. --- test/cases/helper_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 2a659b5c0..0835e6e94 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -44,7 +44,7 @@ def silence_stream(stream) end def quietly - silence_stream(STDOUT) { silence_stream(STDERR) { yield } } + host_windows? ? yield : silence_stream(STDOUT) { silence_stream(STDERR) { yield } } end end From 9d4c0325a921c2625d729b6c894f8935a06cb016 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 14:43:05 -0500 Subject: [PATCH 0410/1412] Use 0.9.5.beta.8 for Appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 450e2c4cd..d2c9ac298 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.7 + - SET TINYTDS_VERSION=0.9.5.beta.8 clone_depth: 5 skip_tags: true matrix: From d75553b9e822a436bbe29157939132970f384cdb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jan 2016 17:15:01 -0500 Subject: [PATCH 0411/1412] Test TinyTDS v0.9.5.beta.9 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d2c9ac298..d086353ea 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.8 + - SET TINYTDS_VERSION=0.9.5.beta.9 clone_depth: 5 skip_tags: true matrix: From caa10cdb4e705c1f1f753f92c749dd429f0f0a53 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 4 Jan 2016 05:31:34 -0500 Subject: [PATCH 0412/1412] Appveyor `SET TINYTDS_VERSION=0.9.5.beta.10` --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d086353ea..ab95c153c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.9 + - SET TINYTDS_VERSION=0.9.5.beta.10 clone_depth: 5 skip_tags: true matrix: From bd2a3720ba4ee287389f7013d9ed209d33cf6d05 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 16 Jan 2016 12:22:43 -0500 Subject: [PATCH 0413/1412] TINYTDS_VERSION=0.9.5.beta.11 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index ab95c153c..f9d7632af 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.10 + - SET TINYTDS_VERSION=0.9.5.beta.11 clone_depth: 5 skip_tags: true matrix: From 6890cbb739b6dfd9ea68ffcbacf64a34fa95c9ce Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 16 Jan 2016 14:25:31 -0500 Subject: [PATCH 0414/1412] Ignore defncopy for now in Windows build --- test/cases/helper_sqlserver.rb | 2 +- test/cases/rake_test_sqlserver.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 0835e6e94..2a659b5c0 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -44,7 +44,7 @@ def silence_stream(stream) end def quietly - host_windows? ? yield : silence_stream(STDOUT) { silence_stream(STDERR) { yield } } + silence_stream(STDOUT) { silence_stream(STDERR) { yield } } end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index a84294cc6..a5930568b 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -120,6 +120,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'dumps structure and accounts for defncopy oddities' do + skip 'debug defncopy on windows later' if host_windows? quietly { db_tasks.structure_dump configuration, filename } filedata.wont_match %r{\AUSE.*\z} filedata.wont_match %r{\AGO.*\z} @@ -129,6 +130,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest end it 'can load dumped structure' do + skip 'debug defncopy on windows later' if host_windows? quietly { db_tasks.structure_dump configuration, filename } filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) From c64e6941714f4f31dbe6e4d428821626cd68f0ae Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 16 Jan 2016 19:08:52 -0500 Subject: [PATCH 0415/1412] Partial indexes using `:where` in schema dumper. Fixes #153 I have altered the deal :pray: I dont alter it any further. Furthermore, I wish you to wear this :dress: and :baby: bonnet. cc @hmadison --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 3 ++- test/cases/coerced_tests.rb | 7 +++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 42f26fb18..920af52e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ * Every time datatype has perfect micro/nano second handling. * All supported datatypes dump defaults properly to schema.rb +* Partial indexes using `:where` in schema dumper. Fixes #153 ## v4.2.6 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 82fb5aaae..b940d05b8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -32,12 +32,13 @@ def indexes(table_name, name = nil) else name = index[:index_name] unique = index[:index_description] =~ /unique/ + where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}") columns = index[:index_keys].split(',').map do |column| column.strip! column.gsub! '(-)', '' if column.ends_with?('(-)') column end - indexes << IndexDefinition.new(table_name, name, unique, columns) + indexes << IndexDefinition.new(table_name, name, unique, columns, nil, nil, where) end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9d070d103..9c38b7896 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -601,6 +601,13 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced # This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off. coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues + # 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(/add_index.*company_partial_index/).first.strip + assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition + end + end class SchemaDumperDefaultsTest < ActiveRecord::TestCase From 4517ba673eb808844841e8fe2ed050533d414f35 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 24 Jan 2016 13:47:19 -0500 Subject: [PATCH 0416/1412] Azure-Friendly Disable Referential Integrity. Fixes #421 --- CHANGELOG.md | 7 +++++++ .../connection_adapters/sqlserver/utils.rb | 8 ++++++++ .../connection_adapters/sqlserver_adapter.rb | 18 ++++++++++++++++-- test/cases/utils_test_sqlserver.rb | 4 ++++ 4 files changed, 35 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 920af52e7..080f2d19b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,11 @@ +## v4.2.8 + +#### Fixed + +* Azure-Friendly Disable Referential Integrity. No more `sp_MSforeachtable` usage. Fixes #421 + + ## v4.2.7 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 7d364013a..b70ed1038 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -55,6 +55,10 @@ def quoted parts.map{ |p| quote(p) if p }.join SEPARATOR end + def quoted_raw + quote @raw_name + end + def ==(o) o.class == self.class && o.parts == parts end @@ -114,6 +118,10 @@ def quote_string(s) s.to_s.gsub /\'/, "''" end + def quoted_raw(name) + SQLServer::Utils::Name.new(name).quoted_raw + end + def unquote_string(s) s.to_s.gsub(/\'\'/, "'") end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 032a29093..63777fc5b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -120,10 +120,11 @@ def supports_foreign_keys? end def disable_referential_integrity - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'" + tables = tables_with_referential_integrity + tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" } yield ensure - do_execute "EXEC sp_MSforeachtable 'ALTER TABLE ? CHECK CONSTRAINT ALL'" + tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" } end # === Abstract Adapter (Connection Management) ================== # @@ -161,6 +162,19 @@ def reset! # === Abstract Adapter (Misc Support) =========================== # + def tables_with_referential_integrity + schemas_and_tables = select_rows <<-SQL.strip_heredoc + SELECT s.name, o.name + FROM sys.foreign_keys i + INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID + INNER JOIN sys.schemas s ON o.schema_id = s.schema_id + SQL + schemas_and_tables.map do |schema_table| + schema, table = schema_table + "#{SQLServer::Utils.quoted_raw(schema)}.#{SQLServer::Utils.quoted_raw(table)}" + end + end + def pk_and_sequence_for(table_name) pk = primary_key(table_name) pk ? [pk, nil] : nil diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index fa000f35b..492fdabf0 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -10,6 +10,10 @@ class UtilsTestSQLServer < ActiveRecord::TestCase SQLServer::Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" end + it '.quoted_raw' do + SQLServer::Utils.quoted_raw("some.Name").must_equal "[some.Name]" + end + describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do let(:valid_names) { valid_names_unquoted + valid_names_quoted } From 1f95f231eecde86816d7139028cc0c0cff40dc66 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 26 Jan 2016 19:49:08 -0500 Subject: [PATCH 0417/1412] [Azure] Core tests friendly. --- .../sqlserver/database_statements.rb | 4 ++-- test/cases/adapter_test_sqlserver.rb | 2 ++ test/cases/rake_test_sqlserver.rb | 13 +++++++++++-- test/cases/transaction_test_sqlserver.rb | 4 ++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 402571547..6bd3f5e59 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -179,8 +179,8 @@ def user_options_isolation_level if sqlserver_azure? sql = %(SELECT CASE [transaction_isolation_level] WHEN 0 THEN NULL - WHEN 1 THEN 'READ UNCOMITTED' - WHEN 2 THEN 'READ COMITTED' + WHEN 1 THEN 'READ UNCOMMITTED' + WHEN 2 THEN 'READ COMMITTED' WHEN 3 THEN 'REPEATABLE READ' WHEN 4 THEN 'SERIALIZABLE' WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level] diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 1ff2848d7..e34669474 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -240,6 +240,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe 'database statements' do it "run the database consistency checker useroptions command" do + skip 'on azure' if connection_sqlserver_azure? keys = [:textsize, :language, :isolation_level, :dateformat] user_options = connection.user_options keys.each do |key| @@ -249,6 +250,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return a underscored key hash with indifferent access of the results" do + skip 'on azure' if connection_sqlserver_azure? user_options = connection.user_options assert_equal 'read committed', user_options['isolation_level'] assert_equal 'read committed', user_options[:isolation_level] diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index a5930568b..53621ead8 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -4,16 +4,25 @@ class SQLServerRakeTest < ActiveRecord::TestCase self.use_transactional_fixtures = false + cattr_accessor :azure_skip + self.azure_skip = connection_sqlserver_azure? + let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } let(:new_database) { 'activerecord_unittest_tasks' } let(:default_configuration) { ARTest.connection_config['arunit'] } let(:configuration) { default_configuration.merge('database' => new_database) } - before do + before { skip 'on azure' if azure_skip } + before { disconnect! unless azure_skip } + after { reconnect unless azure_skip } + + private + + def disconnect! connection.disconnect! end - after do + def reconnect ActiveRecord::Base.establish_connection(default_configuration) connection.drop_database(new_database) rescue nil end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 5f9b8d23f..f88211325 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -35,14 +35,14 @@ class TransactionTestSQLServer < ActiveRecord::TestCase begin in_level = nil begin_level = connection.user_options_isolation_level - begin_level.must_match %r{read committed} + begin_level.must_match %r{read committed}i Ship.transaction(isolation: :serializable) do Ship.create! name: 'Black Pearl' in_level = connection.user_options_isolation_level end after_level = connection.user_options_isolation_level in_level.must_match %r{serializable}i - after_level.must_match %r{read committed} + after_level.must_match %r{read committed}i ensure connection.set_transaction_isolation_level 'READ COMMITTED' end From 44a97d3182c2da8f55b8e7ab4ab6d9f8f131d633 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 26 Jan 2016 21:55:01 -0500 Subject: [PATCH 0418/1412] Test TinyTDS v0.9.5.beta.12 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f9d7632af..03f8980da 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.11 + - SET TINYTDS_VERSION=0.9.5.beta.12 clone_depth: 5 skip_tags: true matrix: From 39d36a145ab063312edf0979df9658b2f8c26735 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 26 Jan 2016 22:55:58 -0500 Subject: [PATCH 0419/1412] Add Azure to Appveyor builds --- appveyor.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index 03f8980da..7b0321f9e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,7 +27,14 @@ test_script: - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + - timeout /t 4 /nobreak > NUL + - bundle exec rake ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% + - bundle exec rake ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: + CI_AZURE_HOST: + secure: D7mY9Aj6qBEvGf5MhSyLBHOU2IehhtseaTm1VqlBfmo= + CI_AZURE_PASS: + secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - ruby_version: "200-x64" - ruby_version: "21-x64" From 6a1ba3e512002fef46f51d12ad0a4573d8a55b08 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 1 Feb 2016 09:29:33 -0500 Subject: [PATCH 0420/1412] Master is now future v4.2.8 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4739c61f1..ad9e446d8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.7 +4.2.8 From 35fd2618f6dc657c480cac794c21e89c78a262ab Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 2 Feb 2016 19:29:47 -0500 Subject: [PATCH 0421/1412] Fix Azure tests. --- appveyor.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 7b0321f9e..d0b0e7a44 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,8 +28,9 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - timeout /t 4 /nobreak > NUL - - bundle exec rake ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% - - bundle exec rake ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% + - timeout /t 4 /nobreak > NUL + - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: CI_AZURE_HOST: secure: D7mY9Aj6qBEvGf5MhSyLBHOU2IehhtseaTm1VqlBfmo= From bcf05c8bff3ef3c5a2a4bf7aaf6a71a8d36312f1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 6 Feb 2016 14:09:15 -0500 Subject: [PATCH 0422/1412] New Azure hosts. --- appveyor.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d0b0e7a44..a8c0db7be 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -29,11 +29,11 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - timeout /t 4 /nobreak > NUL - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% - - timeout /t 4 /nobreak > NUL - - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + # - timeout /t 4 /nobreak > NUL + # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: CI_AZURE_HOST: - secure: D7mY9Aj6qBEvGf5MhSyLBHOU2IehhtseaTm1VqlBfmo= + secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: From cb5530e52eac29823e04898841706577cdc1a4b9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 6 Feb 2016 18:58:09 -0500 Subject: [PATCH 0423/1412] Use v0.9.5.rc.1 TinyTDS. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index a8c0db7be..f722fa561 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.beta.12 + - SET TINYTDS_VERSION=0.9.5.rc.1 clone_depth: 5 skip_tags: true matrix: From d735224b5cc6c500f5ee327e0f01d91927c294d8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 6 Feb 2016 20:14:52 -0500 Subject: [PATCH 0424/1412] Debug Azure. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f722fa561..d91fd3c6c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -28,7 +28,7 @@ test_script: - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - timeout /t 4 /nobreak > NUL - - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% + - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" environment: From 4417a15a26b7c2040dba9b157d593e7f4d0e5bc8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 10:12:01 -0500 Subject: [PATCH 0425/1412] Debug --- Gemfile | 2 +- Rakefile | 2 + appveyor.yml | 38 ++++++++++--------- .../connection_adapters/sqlserver_adapter.rb | 1 + 4 files changed, 25 insertions(+), 18 deletions(-) diff --git a/Gemfile b/Gemfile index ae7ac6c89..36b74d271 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gemspec -gem 'bcrypt' +gem 'bcrypt', platforms: [:ruby] gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ diff --git a/Rakefile b/Rakefile index 7ebb70ad6..08701eb62 100644 --- a/Rakefile +++ b/Rakefile @@ -6,6 +6,8 @@ require_relative 'test/support/rake_helpers' task test: ['test:dblib'] task default: [:test] +puts ENV.inspect + namespace :test do %w(dblib odbc).each do |mode| diff --git a/appveyor.yml b/appveyor.yml index d91fd3c6c..09ce2fc45 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,28 +14,32 @@ install: - bundle install --without odbc build: off test_script: - - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" - - timeout /t 4 /nobreak > NUL - - ps: Start-Service 'MSSQL$SQL2014' - - timeout /t 4 /nobreak > NUL - - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - ps: Stop-Service 'MSSQL$SQL2014' - - ps: Start-Service 'MSSQL$SQL2012SP1' - - timeout /t 4 /nobreak > NUL - - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - timeout /t 4 /nobreak > NUL - - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 + # - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" + # - timeout /t 4 /nobreak > NUL + # - ps: Start-Service 'MSSQL$SQL2014' + # - timeout /t 4 /nobreak > NUL + # - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" + # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + # - ps: Stop-Service 'MSSQL$SQL2014' + # - ps: Start-Service 'MSSQL$SQL2012SP1' + # - timeout /t 4 /nobreak > NUL + # - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" + # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + # - timeout /t 4 /nobreak > NUL + # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_USER=%CI_AZURE_USER% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= + CI_AZURE_USER: + secure: Z10Hf0Lppp+/FJNeQcpd7C2djhVBXBVDVeyxSSQ9Y3o= CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - - ruby_version: "200-x64" - - ruby_version: "21-x64" + # - ruby_version: "200-x64" + # - ruby_version: "21-x64" + ruby_version: "22-x64" diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 63777fc5b..d6354c35d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -308,6 +308,7 @@ def connect end def dblib_connect(config) + puts config.inspect TinyTds::Client.new( dataserver: config[:dataserver], host: config[:host], From 98104c4ddc0428598b1c146d7ca599d6daac0515 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 10:16:15 -0500 Subject: [PATCH 0426/1412] Debug. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 09ce2fc45..3c907e2e8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -42,4 +42,4 @@ environment: matrix: # - ruby_version: "200-x64" # - ruby_version: "21-x64" - ruby_version: "22-x64" + - ruby_version: "22-x64" From af553601593de6a439bbc98293df260c964a3333 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 10:29:07 -0500 Subject: [PATCH 0427/1412] Debug --- Rakefile | 2 -- appveyor.yml | 5 ++--- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Rakefile b/Rakefile index 08701eb62..7ebb70ad6 100644 --- a/Rakefile +++ b/Rakefile @@ -6,8 +6,6 @@ require_relative 'test/support/rake_helpers' task test: ['test:dblib'] task default: [:test] -puts ENV.inspect - namespace :test do %w(dblib odbc).each do |mode| diff --git a/appveyor.yml b/appveyor.yml index 3c907e2e8..936cdc02e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,12 +31,11 @@ test_script: # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_USER=%CI_AZURE_USER% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 + - ruby -e "require 'net/http' ; Net::HTTP.get_print(URI.parse('http://ipecho.net/plain'))" + - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= - CI_AZURE_USER: - secure: Z10Hf0Lppp+/FJNeQcpd7C2djhVBXBVDVeyxSSQ9Y3o= CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d6354c35d..63777fc5b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -308,7 +308,6 @@ def connect end def dblib_connect(config) - puts config.inspect TinyTds::Client.new( dataserver: config[:dataserver], host: config[:host], From 60b117c1633243d8f6a4396fab9a308fcac86ed4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:02:32 -0500 Subject: [PATCH 0428/1412] Debug --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 936cdc02e..5c2afa3d5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,7 +31,7 @@ test_script: # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - ruby -e "require 'net/http' ; Net::HTTP.get_print(URI.parse('http://ipecho.net/plain'))" + - ruby -e "require 'tiny_tds' ; c = TinyTds::Client.new(host: ENV['CI_AZURE_HOST'], username: 'rails', password: ENV['CI_AZURE_PASS'], database: 'activerecord_unittest', azure: true) ; puts c.execute("SELECT 1 AS [one]").each ; c.close" - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: From 59743cb7a265e7703c444f1d3409bce97dab6714 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:04:51 -0500 Subject: [PATCH 0429/1412] Debug --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 5c2afa3d5..f21f8d36f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,7 +31,7 @@ test_script: # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - ruby -e "require 'tiny_tds' ; c = TinyTds::Client.new(host: ENV['CI_AZURE_HOST'], username: 'rails', password: ENV['CI_AZURE_PASS'], database: 'activerecord_unittest', azure: true) ; puts c.execute("SELECT 1 AS [one]").each ; c.close" + - ruby %APPVEYOR_BUILD_FOLDER%\test\debug.rb - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: From aa0f197316a3837bbde9d2ecd195652be969eacd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:07:43 -0500 Subject: [PATCH 0430/1412] Debug --- test/debug.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/debug.rb diff --git a/test/debug.rb b/test/debug.rb new file mode 100644 index 000000000..885d46ec9 --- /dev/null +++ b/test/debug.rb @@ -0,0 +1,12 @@ +require 'tiny_tds' + +c = TinyTds::Client.new( + host: ENV['CI_AZURE_HOST'], + username: 'rails', + password: ENV['CI_AZURE_PASS'], + database: 'activerecord_unittest', + azure: true +) + +puts c.execute("SELECT 1 AS [one]").each +c.close From 674d018ebdb8adbb49317077072790f9beac38fc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:14:40 -0500 Subject: [PATCH 0431/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 63777fc5b..7296b7161 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -308,7 +308,7 @@ def connect end def dblib_connect(config) - TinyTds::Client.new( + options = { dataserver: config[:dataserver], host: config[:host], port: config[:port], @@ -321,7 +321,9 @@ def dblib_connect(config) timeout: config_timeout(config), encoding: config_encoding(config), azure: config[:azure] - ).tap do |client| + } + puts options.except(:password).inspect + TinyTds::Client.new(options).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do From 067de49f2cb0d5d9d408ebee5809c4fcbb367431 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:30:25 -0500 Subject: [PATCH 0432/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 7296b7161..addaab91d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -317,9 +317,9 @@ def dblib_connect(config) database: config[:database], tds_version: config[:tds_version], appname: config_appname(config), - login_timeout: config_login_timeout(config), - timeout: config_timeout(config), - encoding: config_encoding(config), + # login_timeout: config_login_timeout(config), + # timeout: config_timeout(config), + # encoding: config_encoding(config), azure: config[:azure] } puts options.except(:password).inspect From beb01f5fa038d0317006b55cff9d3f154227c1b5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:43:02 -0500 Subject: [PATCH 0433/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index addaab91d..301abd468 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -309,7 +309,7 @@ def connect def dblib_connect(config) options = { - dataserver: config[:dataserver], + # dataserver: config[:dataserver], host: config[:host], port: config[:port], username: config[:username], From 71bb05f06179cc486ca6f89dceea0bda09baadc0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:50:50 -0500 Subject: [PATCH 0434/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 301abd468..b0df6ed2a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -311,7 +311,7 @@ def dblib_connect(config) options = { # dataserver: config[:dataserver], host: config[:host], - port: config[:port], + # port: config[:port], username: config[:username], password: config[:password], database: config[:database], From d0c6af08edd2892be05da9582962fde2cd98370a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:53:41 -0500 Subject: [PATCH 0435/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b0df6ed2a..601993741 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -315,7 +315,7 @@ def dblib_connect(config) username: config[:username], password: config[:password], database: config[:database], - tds_version: config[:tds_version], + # tds_version: config[:tds_version], appname: config_appname(config), # login_timeout: config_login_timeout(config), # timeout: config_timeout(config), From 32622c4331e9e8a4bf3feb7451bc72218a0a9a19 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 11:56:28 -0500 Subject: [PATCH 0436/1412] Debug --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 601993741..b91dd14ef 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -316,7 +316,7 @@ def dblib_connect(config) password: config[:password], database: config[:database], # tds_version: config[:tds_version], - appname: config_appname(config), + # appname: config_appname(config), # login_timeout: config_login_timeout(config), # timeout: config_timeout(config), # encoding: config_encoding(config), From 60568c211e921d5cf2883b55568022e8ef08821f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:09:14 -0500 Subject: [PATCH 0437/1412] Debug --- .../connection_adapters/sqlserver_adapter.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b91dd14ef..aacb4d7d6 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -322,7 +322,17 @@ def dblib_connect(config) # encoding: config_encoding(config), azure: config[:azure] } + puts "\n\n\ndblib_connect:" puts options.except(:password).inspect + c = ::TinyTds::Client.new( + host: ENV['CI_AZURE_HOST'], + username: 'rails', + password: ENV['CI_AZURE_PASS'], + database: 'activerecord_unittest', + azure: true + ) + puts c.execute("SELECT 1 AS [one]").each + c.close TinyTds::Client.new(options).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do From ba9041f34537a8000dcf9016446f2d963711f332 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:12:45 -0500 Subject: [PATCH 0438/1412] Debug --- test/cases/helper_sqlserver.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 2a659b5c0..afb394eb9 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,4 +1,16 @@ require 'bundler' ; Bundler.require :default, :development, :test + +puts "\n\n\nBOOT:" +c = ::TinyTds::Client.new( + host: ENV['CI_AZURE_HOST'], + username: 'rails', + password: ENV['CI_AZURE_PASS'], + database: 'activerecord_unittest', + azure: true +) +puts c.execute("SELECT 1 AS [one]").each +c.close + require 'support/paths_sqlserver' require 'support/minitest_sqlserver' require 'cases/helper' From 62771b1fadb0f67637ed82344e853719f3f73d9c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:14:44 -0500 Subject: [PATCH 0439/1412] Debug --- appveyor.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f21f8d36f..9f65c275f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -31,8 +31,8 @@ test_script: # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - ruby %APPVEYOR_BUILD_FOLDER%\test\debug.rb - - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 + - bundle exec ruby %APPVEYOR_BUILD_FOLDER%\test\debug.rb + - bundle exec rake --trace test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= From beae8ad7007a81a943ef5098c6b1fc0579ffdb08 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:21:08 -0500 Subject: [PATCH 0440/1412] Debug --- test/cases/helper_sqlserver.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index afb394eb9..3980bbae6 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,6 +1,7 @@ require 'bundler' ; Bundler.require :default, :development, :test puts "\n\n\nBOOT:" +require 'tiny_tds' c = ::TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], username: 'rails', From 1188b8018c301e3d02f93ec9141c876273d8a51f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:28:49 -0500 Subject: [PATCH 0441/1412] Debug --- test/debug.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/debug.rb b/test/debug.rb index 885d46ec9..c1aab036f 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,3 +1,4 @@ +require 'bundler' ; Bundler.require :default, :development, :test require 'tiny_tds' c = TinyTds::Client.new( From 01c5963ed797faea323df06b9ea14e54adcdd24c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:38:26 -0500 Subject: [PATCH 0442/1412] Debug --- appveyor.yml | 2 +- .../connection_adapters/sqlserver_adapter.rb | 30 ++++++------------- test/cases/helper_sqlserver.rb | 13 -------- test/debug.rb | 2 +- 4 files changed, 11 insertions(+), 36 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 9f65c275f..e070e0fc9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -32,7 +32,7 @@ test_script: # - timeout /t 4 /nobreak > NUL # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - bundle exec ruby %APPVEYOR_BUILD_FOLDER%\test\debug.rb - - bundle exec rake --trace test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 + # - bundle exec rake --trace test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index aacb4d7d6..63777fc5b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -308,32 +308,20 @@ def connect end def dblib_connect(config) - options = { - # dataserver: config[:dataserver], + TinyTds::Client.new( + dataserver: config[:dataserver], host: config[:host], - # port: config[:port], + port: config[:port], username: config[:username], password: config[:password], database: config[:database], - # tds_version: config[:tds_version], - # appname: config_appname(config), - # login_timeout: config_login_timeout(config), - # timeout: config_timeout(config), - # encoding: config_encoding(config), + tds_version: config[:tds_version], + appname: config_appname(config), + login_timeout: config_login_timeout(config), + timeout: config_timeout(config), + encoding: config_encoding(config), azure: config[:azure] - } - puts "\n\n\ndblib_connect:" - puts options.except(:password).inspect - c = ::TinyTds::Client.new( - host: ENV['CI_AZURE_HOST'], - username: 'rails', - password: ENV['CI_AZURE_PASS'], - database: 'activerecord_unittest', - azure: true - ) - puts c.execute("SELECT 1 AS [one]").each - c.close - TinyTds::Client.new(options).tap do |client| + ).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 3980bbae6..2a659b5c0 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,17 +1,4 @@ require 'bundler' ; Bundler.require :default, :development, :test - -puts "\n\n\nBOOT:" -require 'tiny_tds' -c = ::TinyTds::Client.new( - host: ENV['CI_AZURE_HOST'], - username: 'rails', - password: ENV['CI_AZURE_PASS'], - database: 'activerecord_unittest', - azure: true -) -puts c.execute("SELECT 1 AS [one]").each -c.close - require 'support/paths_sqlserver' require 'support/minitest_sqlserver' require 'cases/helper' diff --git a/test/debug.rb b/test/debug.rb index c1aab036f..ffe17a58d 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -require 'bundler' ; Bundler.require :default, :development, :test +# require 'bundler' ; Bundler.require :default, :development, :test require 'tiny_tds' c = TinyTds::Client.new( From ea3fba56d982281a5a09a71680271106cdc64e33 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:43:58 -0500 Subject: [PATCH 0443/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index ffe17a58d..6a2639070 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -# require 'bundler' ; Bundler.require :default, :development, :test +require 'bundler' ; Bundler.require :default#, :development, :test require 'tiny_tds' c = TinyTds::Client.new( From ae2fa748fc14f2986587f8b29753f90e15d09a8f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:47:35 -0500 Subject: [PATCH 0444/1412] Debug --- test/debug.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/debug.rb b/test/debug.rb index 6a2639070..bf2f5a1ad 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,13 @@ require 'bundler' ; Bundler.require :default#, :development, :test +require 'guard' +require 'guard-minitest' +require 'minitest' +require 'minitest-focus' +require 'minitest-spec-rails' +require 'mocha' +require 'nokogiri' +require 'byebug' +require 'rake' require 'tiny_tds' c = TinyTds::Client.new( From 57b50e410f999ec574d6b296e5bb66fd9ee5c2e6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:55:09 -0500 Subject: [PATCH 0445/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index bf2f5a1ad..2174c2fed 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -require 'bundler' ; Bundler.require :default#, :development, :test +require 'bundler' ; Bundler.require :default, :development#, :test require 'guard' require 'guard-minitest' require 'minitest' From e9aa4816d05e615fb247f51aebb57d9b35444246 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:55:35 -0500 Subject: [PATCH 0446/1412] Debug --- test/debug.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 2174c2fed..7a5ebd70c 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,13 +1,13 @@ -require 'bundler' ; Bundler.require :default, :development#, :test +require 'bundler' ; Bundler.require :default#, :development, :test require 'guard' require 'guard-minitest' -require 'minitest' -require 'minitest-focus' -require 'minitest-spec-rails' -require 'mocha' -require 'nokogiri' -require 'byebug' -require 'rake' +# require 'minitest' +# require 'minitest-focus' +# require 'minitest-spec-rails' +# require 'mocha' +# require 'nokogiri' +# require 'byebug' +# require 'rake' require 'tiny_tds' c = TinyTds::Client.new( From 7b6deafb6a450a24294f215e51dc0de7104f58d8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 12:57:59 -0500 Subject: [PATCH 0447/1412] Debug --- test/debug.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 7a5ebd70c..42569aa0a 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,6 +1,7 @@ -require 'bundler' ; Bundler.require :default#, :development, :test -require 'guard' -require 'guard-minitest' +require 'bundler/setup' +Bundler.require :default, :development, :test +# require 'guard' +# require 'guard-minitest' # require 'minitest' # require 'minitest-focus' # require 'minitest-spec-rails' From 85804392e35302f26010f9cf916d098ff799af26 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:01:42 -0500 Subject: [PATCH 0448/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 16 ++++++++-------- test/debug.rb | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 6d4e8c5f6..2bc5109c3 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,13 +18,13 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' spec.add_development_dependency 'bundler' - spec.add_development_dependency 'guard' - spec.add_development_dependency 'guard-minitest' - spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - spec.add_development_dependency 'minitest-focus' - spec.add_development_dependency 'minitest-spec-rails' - spec.add_development_dependency 'mocha' - spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'byebug' + # spec.add_development_dependency 'guard' + # spec.add_development_dependency 'guard-minitest' + # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + # spec.add_development_dependency 'minitest-focus' + # spec.add_development_dependency 'minitest-spec-rails' + # spec.add_development_dependency 'mocha' + # spec.add_development_dependency 'nokogiri' + # spec.add_development_dependency 'byebug' spec.add_development_dependency 'rake' end diff --git a/test/debug.rb b/test/debug.rb index 42569aa0a..a83b199f9 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,3 +1,4 @@ +# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' Bundler.require :default, :development, :test # require 'guard' From 3d630184953db88683ff67feb4e38a08d3cf3824 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:03:48 -0500 Subject: [PATCH 0449/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 2bc5109c3..62149fc37 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -25,6 +25,6 @@ Gem::Specification.new do |spec| # spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' - # spec.add_development_dependency 'byebug' + spec.add_development_dependency 'byebug' spec.add_development_dependency 'rake' end From df051c09cb64dff8e93d13371051a86bac273424 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:06:12 -0500 Subject: [PATCH 0450/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 14 +++++++------- test/debug.rb | 10 ---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 62149fc37..6d4e8c5f6 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,13 +18,13 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' spec.add_development_dependency 'bundler' - # spec.add_development_dependency 'guard' - # spec.add_development_dependency 'guard-minitest' - # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - # spec.add_development_dependency 'minitest-focus' - # spec.add_development_dependency 'minitest-spec-rails' - # spec.add_development_dependency 'mocha' - # spec.add_development_dependency 'nokogiri' + spec.add_development_dependency 'guard' + spec.add_development_dependency 'guard-minitest' + spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + spec.add_development_dependency 'minitest-focus' + spec.add_development_dependency 'minitest-spec-rails' + spec.add_development_dependency 'mocha' + spec.add_development_dependency 'nokogiri' spec.add_development_dependency 'byebug' spec.add_development_dependency 'rake' end diff --git a/test/debug.rb b/test/debug.rb index a83b199f9..323da83be 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,15 +1,5 @@ -# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' Bundler.require :default, :development, :test -# require 'guard' -# require 'guard-minitest' -# require 'minitest' -# require 'minitest-focus' -# require 'minitest-spec-rails' -# require 'mocha' -# require 'nokogiri' -# require 'byebug' -# require 'rake' require 'tiny_tds' c = TinyTds::Client.new( From ab8e305e983218626d0244c5704a149b9b0a5702 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:15:55 -0500 Subject: [PATCH 0451/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 1 - 1 file changed, 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 6d4e8c5f6..0f625fcb1 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -25,6 +25,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest-spec-rails' spec.add_development_dependency 'mocha' spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'byebug' spec.add_development_dependency 'rake' end From c20591c8d584025a1d95a2d4ff6db7bb2a7089a4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:21:17 -0500 Subject: [PATCH 0452/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index 323da83be..a74d60150 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,5 @@ require 'bundler/setup' -Bundler.require :default, :development, :test +Bundler.require :default#, :development, :test require 'tiny_tds' c = TinyTds::Client.new( From 3988aa3321d3c13ec91d6d9471c6517b411a9876 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 13:26:15 -0500 Subject: [PATCH 0453/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index a74d60150..323da83be 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,5 @@ require 'bundler/setup' -Bundler.require :default#, :development, :test +Bundler.require :default, :development, :test require 'tiny_tds' c = TinyTds::Client.new( From 5f222b348902fc6fb1cdf0ec4637dfb05bd595cf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:04:44 -0500 Subject: [PATCH 0454/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 0f625fcb1..de43fb8cd 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,13 +17,13 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' - spec.add_development_dependency 'bundler' - spec.add_development_dependency 'guard' - spec.add_development_dependency 'guard-minitest' - spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - spec.add_development_dependency 'minitest-focus' - spec.add_development_dependency 'minitest-spec-rails' - spec.add_development_dependency 'mocha' - spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'rake' + # spec.add_development_dependency 'bundler' + # spec.add_development_dependency 'guard' + # spec.add_development_dependency 'guard-minitest' + # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + # spec.add_development_dependency 'minitest-focus' + # spec.add_development_dependency 'minitest-spec-rails' + # spec.add_development_dependency 'mocha' + # spec.add_development_dependency 'nokogiri' + # spec.add_development_dependency 'rake' end From e13ba292e3b5e5ecddb7adecafe159ef8ef5aa45 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:11:58 -0500 Subject: [PATCH 0455/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index de43fb8cd..27d000a26 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,12 +17,12 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' - # spec.add_development_dependency 'bundler' + spec.add_development_dependency 'bundler' # spec.add_development_dependency 'guard' # spec.add_development_dependency 'guard-minitest' - # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - # spec.add_development_dependency 'minitest-focus' - # spec.add_development_dependency 'minitest-spec-rails' + spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + spec.add_development_dependency 'minitest-focus' + spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' # spec.add_development_dependency 'rake' From be08ffb97bd284b601151fd3ff675336e1182a63 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:23:47 -0500 Subject: [PATCH 0456/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 27d000a26..eee327193 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' - # spec.add_development_dependency 'rake' + spec.add_development_dependency 'rake' end From 2795fb2db6316581cf1cc40cb155349f4c4a4ff1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:29:01 -0500 Subject: [PATCH 0457/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index eee327193..1693f5000 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,12 +18,12 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' spec.add_development_dependency 'bundler' - # spec.add_development_dependency 'guard' - # spec.add_development_dependency 'guard-minitest' + spec.add_development_dependency 'guard' + spec.add_development_dependency 'guard-minitest' spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. spec.add_development_dependency 'minitest-focus' spec.add_development_dependency 'minitest-spec-rails' - # spec.add_development_dependency 'mocha' - # spec.add_development_dependency 'nokogiri' - spec.add_development_dependency 'rake' + spec.add_development_dependency 'mocha' + spec.add_development_dependency 'nokogiri' + # spec.add_development_dependency 'rake' end From 04b6662d889b7f9177379212bac5b1ddc9e75f14 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:34:25 -0500 Subject: [PATCH 0458/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 1693f5000..27d000a26 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -18,12 +18,12 @@ Gem::Specification.new do |spec| spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' spec.add_development_dependency 'bundler' - spec.add_development_dependency 'guard' - spec.add_development_dependency 'guard-minitest' + # spec.add_development_dependency 'guard' + # spec.add_development_dependency 'guard-minitest' spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. spec.add_development_dependency 'minitest-focus' spec.add_development_dependency 'minitest-spec-rails' - spec.add_development_dependency 'mocha' - spec.add_development_dependency 'nokogiri' + # spec.add_development_dependency 'mocha' + # spec.add_development_dependency 'nokogiri' # spec.add_development_dependency 'rake' end From 9e2d875b9cd805d243c264f60a981d876d3d9826 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:42:11 -0500 Subject: [PATCH 0459/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 27d000a26..4a7778eb3 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |spec| # spec.add_development_dependency 'guard' # spec.add_development_dependency 'guard-minitest' spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - spec.add_development_dependency 'minitest-focus' + # spec.add_development_dependency 'minitest-focus' spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' From d8058e237537aff45c4e0dca52a55cb6bbdd11a4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 14:59:15 -0500 Subject: [PATCH 0460/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 6 +++--- test/debug.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 4a7778eb3..de43fb8cd 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,12 +17,12 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' - spec.add_development_dependency 'bundler' + # spec.add_development_dependency 'bundler' # spec.add_development_dependency 'guard' # spec.add_development_dependency 'guard-minitest' - spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. # spec.add_development_dependency 'minitest-focus' - spec.add_development_dependency 'minitest-spec-rails' + # spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' # spec.add_development_dependency 'rake' diff --git a/test/debug.rb b/test/debug.rb index 323da83be..af8624b69 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,5 @@ require 'bundler/setup' -Bundler.require :default, :development, :test +Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( From 9511ef402f477ff05998b1a02c59f7b70dedf8e7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 15:11:47 -0500 Subject: [PATCH 0461/1412] Debug --- activerecord-sqlserver-adapter.gemspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index de43fb8cd..21f10c630 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -20,9 +20,9 @@ Gem::Specification.new do |spec| # spec.add_development_dependency 'bundler' # spec.add_development_dependency 'guard' # spec.add_development_dependency 'guard-minitest' - # spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. # spec.add_development_dependency 'minitest-focus' - # spec.add_development_dependency 'minitest-spec-rails' + spec.add_development_dependency 'minitest-spec-rails' # spec.add_development_dependency 'mocha' # spec.add_development_dependency 'nokogiri' # spec.add_development_dependency 'rake' From 039b7ca703a55f9614ce1e51d19ce572360d58f9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 15:33:05 -0500 Subject: [PATCH 0462/1412] Debug --- Gemfile | 7 +++++++ activerecord-sqlserver-adapter.gemspec | 9 --------- test/cases/helper_sqlserver.rb | 2 +- test/debug.rb | 3 +-- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/Gemfile b/Gemfile index 36b74d271..6c6f56e62 100644 --- a/Gemfile +++ b/Gemfile @@ -48,3 +48,10 @@ group :odbc do gem 'ruby-odbc' end +group :development do + gem 'guard' + gem 'guard-minitest' + gem 'mocha' + gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. + gem 'minitest-spec-rails' +end diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 21f10c630..8d906f2b3 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,13 +17,4 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 4.2.1' - # spec.add_development_dependency 'bundler' - # spec.add_development_dependency 'guard' - # spec.add_development_dependency 'guard-minitest' - spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. - # spec.add_development_dependency 'minitest-focus' - spec.add_development_dependency 'minitest-spec-rails' - # spec.add_development_dependency 'mocha' - # spec.add_development_dependency 'nokogiri' - # spec.add_development_dependency 'rake' end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 2a659b5c0..9403781d6 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,4 +1,4 @@ -require 'bundler' ; Bundler.require :default, :development, :test +require 'bundler/setup' ; Bundler.require :default, :development require 'support/paths_sqlserver' require 'support/minitest_sqlserver' require 'cases/helper' diff --git a/test/debug.rb b/test/debug.rb index af8624b69..12e9b8073 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,4 @@ -require 'bundler/setup' -Bundler.require :default, :development +require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( From 2154cb6168b8788e01b7c7bb16fcad1b193a9543 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 15:44:12 -0500 Subject: [PATCH 0463/1412] Debug --- test/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/test/config.yml b/test/config.yml index 86cc756d1..1ac751d06 100644 --- a/test/config.yml +++ b/test/config.yml @@ -9,6 +9,7 @@ default_connection_info: &default_connection_info username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> + login_timeout: 20 encoding: utf8 connections: From 4ce0fd948ccb4a3435229bee24becf772028cf1e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 15:46:33 -0500 Subject: [PATCH 0464/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index 12e9b8073..6e91592ef 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -require 'bundler/setup' ; Bundler.require :default, :development +# require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( From 7af109b961bad87077962bbf2b57d55d3cb52831 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 15:50:06 -0500 Subject: [PATCH 0465/1412] Debug --- test/debug.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 6e91592ef..d9e3981a6 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -# require 'bundler/setup' ; Bundler.require :default, :development +require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( @@ -6,7 +6,8 @@ username: 'rails', password: ENV['CI_AZURE_PASS'], database: 'activerecord_unittest', - azure: true + azure: true, + login_timeout: 20 ) puts c.execute("SELECT 1 AS [one]").each From dc98accb74aeb516f06c61c99fa84a759596ffc5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:02:00 -0500 Subject: [PATCH 0466/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index d9e3981a6..8a95e16e3 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -require 'bundler/setup' ; Bundler.require :default, :development +# require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( From a59298c7d89f52c49b0f49ffbf03bcc8e851e4b7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:13:47 -0500 Subject: [PATCH 0467/1412] Debug --- test/debug.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index 8a95e16e3..21f2e86f3 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,5 @@ -# require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' +require 'bundler/setup' ; Bundler.require :default, :development c = TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], From f37499d906da31878e795c753e0ee85c889fd1ca Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:18:06 -0500 Subject: [PATCH 0468/1412] Debug --- test/debug.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/debug.rb b/test/debug.rb index 21f2e86f3..49fe4c4db 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,5 @@ require 'tiny_tds' +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' ; Bundler.require :default, :development c = TinyTds::Client.new( From cfa0fd03305a7bc88fe4d328935e9cfc2fd32d63 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:23:52 -0500 Subject: [PATCH 0469/1412] Debug --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index e070e0fc9..cb6807886 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit + - SET TDSDUMP=%APPVEYOR_BUILD_FOLDER%\test\tdsdump.log - SET TINYTDS_VERSION=0.9.5.rc.1 clone_depth: 5 skip_tags: true @@ -42,3 +43,5 @@ environment: # - ruby_version: "200-x64" # - ruby_version: "21-x64" - ruby_version: "22-x64" +on_failure: + - find -name tdsdump.log | xargs cat From 131be6e7c463f3a194ab5d64acaa81d658740e49 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:29:00 -0500 Subject: [PATCH 0470/1412] Debug --- appveyor.yml | 2 +- test/debug.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index cb6807886..d1beec200 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -43,5 +43,5 @@ environment: # - ruby_version: "200-x64" # - ruby_version: "21-x64" - ruby_version: "22-x64" -on_failure: +on_finish: - find -name tdsdump.log | xargs cat diff --git a/test/debug.rb b/test/debug.rb index 49fe4c4db..3c974eae4 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,6 +1,6 @@ require 'tiny_tds' -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' ; Bundler.require :default, :development +# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +# require 'bundler/setup' ; Bundler.require :default, :development c = TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], From a792029e3de8ffb9ba9a6d00987d31f3c3b604a1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 16:40:22 -0500 Subject: [PATCH 0471/1412] Debug --- test/debug.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/debug.rb b/test/debug.rb index 3c974eae4..aa343332a 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -8,7 +8,8 @@ password: ENV['CI_AZURE_PASS'], database: 'activerecord_unittest', azure: true, - login_timeout: 20 + login_timeout: 20, + tds_version: '7.3' ) puts c.execute("SELECT 1 AS [one]").each From 95fcc7566ac05cfde20fcd9cd960ee0dde39c735 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 17:11:11 -0500 Subject: [PATCH 0472/1412] Debug --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index d1beec200..f33249c54 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ init: - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - SET TDSDUMP=%APPVEYOR_BUILD_FOLDER%\test\tdsdump.log - - SET TINYTDS_VERSION=0.9.5.rc.1 + - SET TINYTDS_VERSION=0.9.5.rc.2 clone_depth: 5 skip_tags: true matrix: From 5ba00d5e68f6a577dc0e6441227612c91eb8306f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 17:42:35 -0500 Subject: [PATCH 0473/1412] Debug --- test/debug.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index aa343332a..8705c63e2 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,6 +1,6 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' -# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -# require 'bundler/setup' ; Bundler.require :default, :development c = TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], From e012646dfa4ebdb346328c32cbebc6d7866d6267 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 17:45:36 -0500 Subject: [PATCH 0474/1412] Debug --- test/debug.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 8705c63e2..bddce77a0 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,5 @@ -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' ; Bundler.require :default, :development +# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +# require 'bundler/setup' ; Bundler.require :default, :development require 'tiny_tds' c = TinyTds::Client.new( From b85b36fac9a686a52bc4d971635128ff0b891d15 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 17:56:30 -0500 Subject: [PATCH 0475/1412] Debug --- test/debug.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index bddce77a0..166da0a5b 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,5 +1,7 @@ -# ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -# require 'bundler/setup' ; Bundler.require :default, :development +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +require 'bundler/setup' ; Bundler.require :default, :development + +sleep 5 require 'tiny_tds' c = TinyTds::Client.new( From ea2e0a2b8dffe778a3be3fcbc7a9420eabc566f6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 18:16:48 -0500 Subject: [PATCH 0476/1412] Debug --- test/debug.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 166da0a5b..4dcc2f18d 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,9 +1,8 @@ +require 'tiny_tds' +sleep 5 ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' ; Bundler.require :default, :development -sleep 5 -require 'tiny_tds' - c = TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], username: 'rails', From e619b51de34440fb8acba998fe02222fb0f512b9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 18:21:12 -0500 Subject: [PATCH 0477/1412] Debug --- test/debug.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/debug.rb b/test/debug.rb index 4dcc2f18d..edbd68134 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,7 +1,5 @@ +require 'rails/all' require 'tiny_tds' -sleep 5 -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) -require 'bundler/setup' ; Bundler.require :default, :development c = TinyTds::Client.new( host: ENV['CI_AZURE_HOST'], From 55db853a1f529ebda6ae8bfae39403a65d83e585 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 18:24:51 -0500 Subject: [PATCH 0478/1412] Debug --- test/config.yml | 1 - test/debug.rb | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/test/config.yml b/test/config.yml index 1ac751d06..86cc756d1 100644 --- a/test/config.yml +++ b/test/config.yml @@ -9,7 +9,6 @@ default_connection_info: &default_connection_info username: <%= ENV['ACTIVERECORD_UNITTEST_USER'] || 'rails' %> password: <%= ENV['ACTIVERECORD_UNITTEST_PASS'] || '' %> collation: <%= ENV['ACTIVERECORD_UNITTEST_COLLATION'] || nil %> - login_timeout: 20 encoding: utf8 connections: diff --git a/test/debug.rb b/test/debug.rb index edbd68134..99ef6950a 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,4 +1,4 @@ -require 'rails/all' +# require 'rails/all' require 'tiny_tds' c = TinyTds::Client.new( @@ -7,7 +7,6 @@ password: ENV['CI_AZURE_PASS'], database: 'activerecord_unittest', azure: true, - login_timeout: 20, tds_version: '7.3' ) From 140957b579b0204cf8ea4c5dd80be5d28df29cf7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 7 Feb 2016 18:29:56 -0500 Subject: [PATCH 0479/1412] Finish Debug --- appveyor.yml | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index f33249c54..669036e42 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,6 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TDSDUMP=%APPVEYOR_BUILD_FOLDER%\test\tdsdump.log - SET TINYTDS_VERSION=0.9.5.rc.2 clone_depth: 5 skip_tags: true @@ -15,33 +14,29 @@ install: - bundle install --without odbc build: off test_script: - # - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" + - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" + - timeout /t 4 /nobreak > NUL + - ps: Start-Service 'MSSQL$SQL2014' + - timeout /t 4 /nobreak > NUL + - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" + - ps: Stop-Service 'MSSQL$SQL2014' + - ps: Start-Service 'MSSQL$SQL2012SP1' + - timeout /t 4 /nobreak > NUL + - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" + - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" # - timeout /t 4 /nobreak > NUL - # - ps: Start-Service 'MSSQL$SQL2014' + # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 # - timeout /t 4 /nobreak > NUL - # - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" - # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - # - ps: Stop-Service 'MSSQL$SQL2014' - # - ps: Start-Service 'MSSQL$SQL2012SP1' - # - timeout /t 4 /nobreak > NUL - # - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - # - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - # - timeout /t 4 /nobreak > NUL - # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ONLY_SQLSERVER=1 - # - timeout /t 4 /nobreak > NUL - # - bundle exec rake test ACTIVERECORD_UNITTEST_AZURE=1 ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - - bundle exec ruby %APPVEYOR_BUILD_FOLDER%\test\debug.rb - # - bundle exec rake --trace test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 ONLY_SQLSERVER=1 TDSVER=7.3 + # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 TDSVER=7.3 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - # - ruby_version: "200-x64" - # - ruby_version: "21-x64" + - ruby_version: "200" + - ruby_version: "22" - ruby_version: "22-x64" -on_finish: - - find -name tdsdump.log | xargs cat From e50e2f9162b57a361a6e0965b19f6abb5f18caec Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 14 Feb 2016 19:41:33 -0500 Subject: [PATCH 0480/1412] Azure-Friendly DB create/drop. Fixes #442 Create allows edition options like: * MAXSIZE * EDITION * SERVICE_OBJECTIVE --- CHANGELOG.md | 2 + .../sqlserver/database_tasks.rb | 46 ++++++++++++++----- test/cases/rake_test_sqlserver.rb | 16 ++++++- test/config.yml | 2 + 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 080f2d19b..a627538df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ #### Fixed * Azure-Friendly Disable Referential Integrity. No more `sp_MSforeachtable` usage. Fixes #421 +* Azure-Friendly DB create/drop. Fixes #442 + - Create allows edition options like: MAXSIZE, EDITION, and SERVICE_OBJECTIVE. ## v4.2.7 diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index e2fe3fc3c..7c86443b5 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -5,17 +5,9 @@ module DatabaseTasks def create_database(database, options = {}) name = SQLServer::Utils.extract_identifiers(database) - options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys) - options = options.select { |_, v| v.present? } - option_string = options.inject("") do |memo, (key, value)| - memo += case key - when :collation - " COLLATE #{value}" - else - "" - end - end - do_execute "CREATE DATABASE #{name}#{option_string}" + db_options = create_database_options(options) + edition_options = create_database_edition_options(options) + do_execute "CREATE DATABASE #{name} #{db_options} #{edition_options}" end def drop_database(database) @@ -35,6 +27,38 @@ def collation select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')" end + + private + + def create_database_options(options={}) + keys = [:collate] + copts = @connection_options + 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] + copts = @connection_options + edition_options = { + maxsize: copts[:azure_maxsize], + edition: copts[:azure_edition], + service_objective: copts[:azure_service_objective] + }.merge(options.symbolize_keys).select { |_, v| + v.present? + }.slice(*keys).map { |k,v| + "#{k.to_s.upcase} = #{v}" + }.join(', ') + edition_options = "( #{edition_options} )" if edition_options.present? + edition_options + end + end end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 53621ead8..3845e7148 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -23,14 +23,24 @@ def disconnect! end def reconnect - ActiveRecord::Base.establish_connection(default_configuration) - connection.drop_database(new_database) rescue nil + config = default_configuration + if connection_sqlserver_azure? + ActiveRecord::Base.establish_connection(config.merge('database' => 'master')) + connection.drop_database(new_database) rescue nil + disconnect! + ActiveRecord::Base.establish_connection(config) + else + ActiveRecord::Base.establish_connection(config) + connection.drop_database(new_database) rescue nil + end end end class SQLServerRakeCreateTest < SQLServerRakeTest + self.azure_skip = false + it 'establishes connection to database after create ' do db_tasks.create configuration connection.current_database.must_equal(new_database) @@ -56,6 +66,8 @@ class SQLServerRakeCreateTest < SQLServerRakeTest class SQLServerRakeDropTest < SQLServerRakeTest + self.azure_skip = false + it 'drops database and uses master' do db_tasks.create configuration db_tasks.drop configuration diff --git a/test/config.yml b/test/config.yml index 86cc756d1..2595dcc76 100644 --- a/test/config.yml +++ b/test/config.yml @@ -20,6 +20,7 @@ connections: dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> + timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : nil %> arunit2: <<: *default_connection_info database: activerecord_unittest2 @@ -27,6 +28,7 @@ connections: dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> + timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : nil %> odbc: arunit: From 23cfd6395fb1af18a35609a286a58b7e5e412a14 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 14 Feb 2016 19:48:23 -0500 Subject: [PATCH 0481/1412] Appveyor uses 0.9.5.rc.3 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 669036e42..d48a1cfa6 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.rc.2 + - SET TINYTDS_VERSION=0.9.5.rc.3 clone_depth: 5 skip_tags: true matrix: From bdd8d8e7ba6051f57c6d4462a1ac8ef462b43a65 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 14 Feb 2016 22:17:23 -0500 Subject: [PATCH 0482/1412] Enable bcrypt for all again. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 6c6f56e62..0fc3dcf91 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gemspec -gem 'bcrypt', platforms: [:ruby] +gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ From 1cc09e5866d37663e51ce9e9e04d4043ba3efa5c Mon Sep 17 00:00:00 2001 From: Marcelo Casiraghi Date: Mon, 14 Mar 2016 14:37:36 -0300 Subject: [PATCH 0483/1412] Switch primary key column look up order --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 63777fc5b..91f7ce8e6 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -181,7 +181,7 @@ def pk_and_sequence_for(table_name) end def primary_key(table_name) - identity_column(table_name).try(:name) || schema_cache.columns(table_name).find(&:is_primary?).try(:name) + schema_cache.columns(table_name).find(&:is_primary?).try(:name) || identity_column(table_name).try(:name) end # === SQLServer Specific (DB Reflection) ======================== # From c48ec936113f94b35ea8e1a8f1da91a1de88d9ab Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 14 Mar 2016 20:20:32 -0400 Subject: [PATCH 0484/1412] Run tests with verbose false. --- CHANGELOG.md | 8 ++++++++ Rakefile | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a627538df..3041a5447 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v4.2.9 + +#### Changed + +* Run tests with verbose false. + + + ## v4.2.8 diff --git a/Rakefile b/Rakefile index 7ebb70ad6..ed71ab146 100644 --- a/Rakefile +++ b/Rakefile @@ -14,7 +14,7 @@ namespace :test do t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files t.warning = !!ENV['WARNING'] - t.verbose = true + t.verbose = false end end From f41bc15f1a88b58543de9025fe567e4e350d7622 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 14 Mar 2016 20:22:06 -0400 Subject: [PATCH 0485/1412] Conform to new data_sources interfaces. See: https://git.io/va4Fp --- CHANGELOG.md | 6 ++++-- VERSION | 2 +- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3041a5447..b2b58729e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ ## v4.2.9 -#### Changed +#### Fixed -* Run tests with verbose false. +* Conform to new data_sources interfaces. See: https://git.io/va4Fp +#### Changed +* Run tests with verbose false. ## v4.2.8 diff --git a/VERSION b/VERSION index ad9e446d8..27b8cad91 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.8 +4.2.9 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b940d05b8..e0b87a290 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,6 +7,10 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end + def data_sources + tables + views + end + def tables(table_type = 'BASE TABLE') select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME", 'SCHEMA' end From bcf12d77db7aed53709a055d41fd11f74b803f51 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 14 Mar 2016 20:32:52 -0400 Subject: [PATCH 0486/1412] The `primary_key` method falls back to Identity columns. Not the other way around. Fixes #454. Thanks @marceloeloelo --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b58729e..76e5009c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed * Conform to new data_sources interfaces. See: https://git.io/va4Fp +* The `primary_key` method falls back to Identity columns. Not the other way around. Fixes #454. Thanks @marceloeloelo #### Changed From 32968ae944c26f5c94ef8dca40b5f1c49891b776 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 14 Mar 2016 20:52:40 -0400 Subject: [PATCH 0487/1412] Ensure that `execute_procedure` returns proper time zones. Fixes #449 --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 3 ++- test/cases/execute_procedure_test_sqlserver.rb | 6 ++++++ test/schema/sqlserver_specific_schema.rb | 6 ++++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e5009c2..bebd61640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Conform to new data_sources interfaces. See: https://git.io/va4Fp * The `primary_key` method falls back to Identity columns. Not the other way around. Fixes #454. Thanks @marceloeloelo +* Ensure that `execute_procedure` returns proper time zones. Fixes #449 #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6bd3f5e59..26ae6f66e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -117,7 +117,8 @@ def execute_procedure(proc_name, *variables) case @connection_options[:mode] when :dblib result = @connection.execute(sql) - result.each(as: :hash, cache_rows: true) do |row| + options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc } + result.each(options) do |row| r = row.with_indifferent_access yield(r) if block_given? end diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 574fd5e43..be6c05e64 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -35,4 +35,10 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" end + it 'uses the proper timezone' do + date_proc = connection.execute_procedure('my_getutcdate').first['utcdate'] + date_base = connection.select_value('select GETUTCDATE()') + assert_equal date_base, date_proc + end + end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index e7c865854..9acb8ec52 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -119,6 +119,12 @@ end execute "sp_bindefault 'sst_getdateobject', 'sst_defaultobjects.date'" + execute "DROP PROCEDURE my_getutcdate" rescue nil + execute <<-SQL + CREATE PROCEDURE my_getutcdate AS + SELECT GETUTCDATE() utcdate + SQL + # Constraints create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } From dfc29b5a6ba042321f3db713fec1ab4310a24846 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 20 Mar 2016 10:18:52 -0400 Subject: [PATCH 0488/1412] Make .execute_procedure test for times ignore usec. --- test/cases/execute_procedure_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index be6c05e64..d8731cd6b 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -38,7 +38,7 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase it 'uses the proper timezone' do date_proc = connection.execute_procedure('my_getutcdate').first['utcdate'] date_base = connection.select_value('select GETUTCDATE()') - assert_equal date_base, date_proc + assert_equal date_base.change(usec: 0), date_proc.change(usec: 0) end end From 5390b491e94db736d84151acf4a156f5b90e4623 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 20 Mar 2016 10:23:53 -0400 Subject: [PATCH 0489/1412] Ensure small datetime/datetime2 fractionals are properly quoted. Fixes #457. --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- .../sqlserver/type/time_value_fractional.rb | 2 +- test/cases/column_test_sqlserver.rb | 5 +++++ test/cases/specific_schema_test_sqlserver.rb | 9 +++++++++ test/models/sqlserver/booking.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 6 ++++++ 7 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 test/models/sqlserver/booking.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bebd61640..fe1620b57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.10 + +#### Fixed + +* Ensure small datetime/datetime2 fractionals are properly quoted. Fixes #457. + + ## v4.2.9 #### Fixed diff --git a/VERSION b/VERSION index 27b8cad91..9580abf44 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.9 +4.2.10 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 cd70a7419..3ed1a76b6 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 @@ -17,7 +17,7 @@ def cast_fractional(value) def quote_fractional(value) seconds = (value.send(fractional_property).to_f / fractional_operator.to_f).round(fractional_scale) - seconds.to_s.split('.').last.to(fractional_scale-1) + seconds.to_d.to_s.split('.').last.to(fractional_scale-1) end def fractional_property diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 07adbd86a..ad7626cc4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -354,6 +354,11 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! obj.reload.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + # Can save small fraction nanosecond precisoins and return again. + obj.datetime2_7 = Time.utc(2008, 6, 21, 13, 30, 0, Rational(15020, 1000)) + obj.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + obj.save! + obj.reload.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" # With other precisions. time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column('datetime2_3') diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index ffc5ab626..e94eccb48 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -68,6 +68,15 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase obj.reload.date.must_be_instance_of Date end + it 'allows datetime2 as timestamps' do + SSTestBooking.columns_hash['created_at'].sql_type.must_equal 'datetime2(7)' + SSTestBooking.columns_hash['updated_at'].sql_type.must_equal 'datetime2(7)' + obj1 = SSTestBooking.new name: 'test1' + obj1.save! + obj1.created_at.must_be_instance_of Time + obj1.updated_at.must_be_instance_of Time + end + # Natural primary keys. it 'work with identity inserts' do diff --git a/test/models/sqlserver/booking.rb b/test/models/sqlserver/booking.rb new file mode 100644 index 000000000..8b38e4e52 --- /dev/null +++ b/test/models/sqlserver/booking.rb @@ -0,0 +1,3 @@ +class SSTestBooking < ActiveRecord::Base + self.table_name = 'sst_bookings' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 9acb8ec52..a4b542768 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -37,6 +37,12 @@ # Edge Cases + create_table 'sst_bookings', force: true do |t| + t.string :name + t.datetime2 :created_at, null: false + t.datetime2 :updated_at, null: false + end + create_table 'sst_uuids', force: true, id: :uuid do |t| t.string :name t.uuid :other_uuid, default: 'NEWID()' From def93e436c498642acd06a57892a8f57f95ffd50 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 5 Apr 2016 20:46:48 -0400 Subject: [PATCH 0490/1412] Update README & Sign. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bec8e6a15..88bdc5eff 100644 --- a/README.md +++ b/README.md @@ -197,5 +197,5 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- ## License -Copyright © 2008-2015. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. +Copyright © 2008-2016. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. From 956ce716e2c5a8e32123c0a5bed1fafd07c39987 Mon Sep 17 00:00:00 2001 From: Jippe Holwerda Date: Wed, 6 Apr 2016 15:28:44 +0200 Subject: [PATCH 0491/1412] Explicitly check if the connection of the table engine is the sql server adapter. Fixes #450. --- lib/arel/visitors/sqlserver.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 67ac50e21..ff590fc85 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -74,7 +74,10 @@ def visit_Arel_Nodes_SelectStatement o, collector end def visit_Arel_Table o, collector - table_name = if o.engine.connection.database_prefix_remote_server? + # Apparently, o.engine.connection can actually be a different adapter + # than sqlserver. Can be removed if fixed in ActiveRecord. See: + # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 + table_name = if o.engine.connection.respond_to?(:sqlserver?) and o.engine.connection.database_prefix_remote_server? remote_server_table_name(o) else quote_table_name(o.name) From 30f330700d40a4e1205683a0c7bca7c7fe0d63f6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 23 Apr 2016 21:13:28 -0400 Subject: [PATCH 0492/1412] Update Gemfile. --- Gemfile | 9 ++++++--- appveyor.yml | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 0fc3dcf91..4b779a0ff 100644 --- a/Gemfile +++ b/Gemfile @@ -48,10 +48,13 @@ group :odbc do gem 'ruby-odbc' end -group :development do - gem 'guard' - gem 'guard-minitest' +group :test do gem 'mocha' gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. gem 'minitest-spec-rails' end + +group :guard do + gem 'guard' + gem 'guard-minitest' +end diff --git a/appveyor.yml b/appveyor.yml index d48a1cfa6..21a505a9f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ install: - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version - gem --version - - bundle install --without odbc + - bundle install --without odbc guard build: off test_script: - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" From c0d7723b0409173123545891da3be6a270e2e287 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 23 Apr 2016 21:18:23 -0400 Subject: [PATCH 0493/1412] Update gemfile. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 4b779a0ff..0ec4e9a18 100644 --- a/Gemfile +++ b/Gemfile @@ -48,7 +48,7 @@ group :odbc do gem 'ruby-odbc' end -group :test do +group :development do gem 'mocha' gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. gem 'minitest-spec-rails' From 0894cf6e30ffd0d4b1f87173674d6ecc02c217fc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 23 Apr 2016 22:28:39 -0400 Subject: [PATCH 0494/1412] Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- lib/arel/visitors/sqlserver.rb | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe1620b57..ecde36cce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.11 + +#### Fixed + +* Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda + + ## v4.2.10 #### Fixed diff --git a/VERSION b/VERSION index 9580abf44..7cac85078 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.10 +4.2.11 diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index ff590fc85..19705d18d 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -77,7 +77,7 @@ def visit_Arel_Table o, collector # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 - table_name = if o.engine.connection.respond_to?(:sqlserver?) and o.engine.connection.database_prefix_remote_server? + table_name = if o.engine.connection.respond_to?(:sqlserver?) && o.engine.connection.database_prefix_remote_server? remote_server_table_name(o) else quote_table_name(o.name) From ad4eeb2f2bc24ffc6c3c14d9031bed8d62b135c3 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 23 Apr 2016 22:31:17 -0400 Subject: [PATCH 0495/1412] Document two methods for avoiding N'' quoting on char/varchar columns. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/quoting.rb | 8 +++----- .../connection_adapters/sqlserver/type/char.rb | 12 ++++++++++++ test/cases/specific_schema_test_sqlserver.rb | 15 +++++++++++++++ 4 files changed, 31 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecde36cce..d7125f074 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed * Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda +* Document two methods for avoiding N'' quoting on char/varchar columns. ## v4.2.10 diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 767b4faaf..724e8a312 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -54,12 +54,10 @@ def _quote(value) case value when Type::Binary::Data "0x#{value.hex}" + when ActiveRecord::Type::SQLServer::Char::Data + value.quoted when String, ActiveSupport::Multibyte::Chars - if value.is_utf8? - "#{QUOTED_STRING_PREFIX}#{super}" - else - super - end + "#{QUOTED_STRING_PREFIX}#{super}" else super end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index f75ea8785..dc13cb3ca 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -8,6 +8,18 @@ def type :char end + class Data + + def initialize(value) + @value = value.to_s + end + + def quoted + "'#{@value}'" + end + + end + end end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index e94eccb48..835fb3e63 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -107,6 +107,21 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first end + it 'various methods to bypass national quoted columns for any column, but primarily useful for char/varchar' do + value = Class.new do + def quoted_id + "'T'" + end + end + # Using ActiveRecord's quoted_id feature for objects. + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } + # Using our custom char type data. + data = ActiveRecord::Type::SQLServer::Char::Data + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T')).first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T')).first } + end + # With column names that have spaces it 'create record using a custom attribute reader and be able to load it back in' do From 1b1cd766db70d560f8c50091a559b8a8fe8290e9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 23 Apr 2016 22:34:29 -0400 Subject: [PATCH 0496/1412] Document previous change for N'' quoting escape hatch. --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7125f074..d46a69231 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ * Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda * Document two methods for avoiding N'' quoting on char/varchar columns. +#### Changed + +* Supporting escape hatch for N'' quoting. Remove `#is_utf8` string check in `#_quote` method. + This duplicated strings and forced encoding which was actually wasteful. + ## v4.2.10 From a35835a11d88f4bef2a6e0bb2c7213d159b3a16b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 24 Apr 2016 16:44:45 -0400 Subject: [PATCH 0497/1412] Avoiding N'' quoting on char/varchar columns. --- .../connection_adapters/sqlserver/type/char.rb | 6 ++++++ test/cases/specific_schema_test_sqlserver.rb | 3 +++ 2 files changed, 9 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index dc13cb3ca..68e1fd10f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -8,6 +8,12 @@ def type :char end + def type_cast_for_database(value) + return if value.nil? + return value if value.is_a?(Data) + Data.new(super) + end + class Data def initialize(value) diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 835fb3e63..34f339f33 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -120,6 +120,9 @@ def quoted_id data = ActiveRecord::Type::SQLServer::Char::Data assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T')).first } assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T')).first } + # Taking care of everything. + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first } end # With column names that have spaces From 0daeae6ce160b9754b97362a99891af79eeb2832 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 24 Apr 2016 16:56:23 -0400 Subject: [PATCH 0498/1412] First run failure of `change_column` while dropping constraint. Fixes #420. Thanks @GrumpyRainbow @rkr090 --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d46a69231..9a90a2ad2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda * Document two methods for avoiding N'' quoting on char/varchar columns. +* First run failure of `change_column` while dropping constraint. Fixes #420. Thanks @GrumpyRainbow @rkr090 #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e0b87a290..66907d6f3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -96,6 +96,7 @@ def change_column(table_name, column_name, type, options = {}) end def change_column_default(table_name, column_name, default) + schema_cache.clear_table_cache!(table_name) remove_default_constraint(table_name, column_name) column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(default, column_object)} FOR #{quote_column_name(column_name)}" From 99678f287d82728c164077acdb959fdc7bb336ef Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 24 Apr 2016 18:02:50 -0400 Subject: [PATCH 0499/1412] Rounding errors w/datetime2(0) types having no fractional seconds. Fixes #465. Thanks @alawton --- CHANGELOG.md | 1 + Gemfile | 1 + .../sqlserver/type/time_value_fractional.rb | 10 +++++++--- test/cases/column_test_sqlserver.rb | 7 +++++++ test/schema/datatypes/2012.sql | 1 + 5 files changed, 17 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a90a2ad2..c3e5f8ad4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda * Document two methods for avoiding N'' quoting on char/varchar columns. * First run failure of `change_column` while dropping constraint. Fixes #420. Thanks @GrumpyRainbow @rkr090 +* Rounding errors w/datetime2(0) types having no fractional seconds. Fixes #465. Thanks @alawton #### Changed diff --git a/Gemfile b/Gemfile index 0ec4e9a18..6ae57eb90 100644 --- a/Gemfile +++ b/Gemfile @@ -52,6 +52,7 @@ group :development do gem 'mocha' gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. gem 'minitest-spec-rails' + gem 'pry' end group :guard do 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 3ed1a76b6..8171835df 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 @@ -9,9 +9,13 @@ module TimeValueFractional def cast_fractional(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? - seconds = value.send(fractional_property).to_f / fractional_operator.to_f - seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) - frac_seconds = (seconds * fractional_operator).to_i + frac_seconds = if fractional_scale == 0 + 0 + else + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + (seconds * fractional_operator).to_i + end value.change fractional_property => frac_seconds end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index ad7626cc4..21f5dd871 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -373,6 +373,13 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" obj.save! ; obj.reload obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" + col = column('datetime2_0') + col.cast_type.precision.must_equal 0 + 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.datetime2_0.must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" end it 'datetimeoffset' do diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 5c444551d..044b78c97 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -28,6 +28,7 @@ CREATE TABLE [sst_datatypes] ( [datetime2_7] [datetime2](7) NULL DEFAULT '9999-12-31 23:59:59.9999999', [datetime2_3] [datetime2](3) NULL, [datetime2_1] [datetime2](1) NULL, + [datetime2_0] [datetime2](0) NULL, [datetimeoffset_7] [datetimeoffset](7) NULL DEFAULT '1984-01-24 04:20:00.1234567 -08:00', [datetimeoffset_3] [datetimeoffset](3) NULL, [datetimeoffset_1] [datetimeoffset](1) NULL, From aff42503a9175255eaffcabe42211ddc25e70864 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 28 Apr 2016 19:46:08 -0400 Subject: [PATCH 0500/1412] Use 1.0.1 of TinyTDS. --- appveyor.yml | 2 +- lib/active_record/tasks/sqlserver_database_tasks.rb | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 21a505a9f..e1dfa4687 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=0.9.5.rc.3 + - SET TINYTDS_VERSION=1.0.1 clone_depth: 5 skip_tags: true matrix: diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 57dedbd50..0474a8934 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -61,6 +61,7 @@ def structure_dump(filename) command.concat(table_args) view_args = connection.views.map { |v| Shellwords.escape(v) } command.concat(view_args) + puts command.inspect raise 'Error dumping database' unless Kernel.system(command.join(' ')) dump = File.read(filename) dump.gsub!(/^USE .*$\nGO\n/, '') # Strip db USE statements From 6ac6b87b1f80735f34187506bd76455a07cfc207 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 28 Apr 2016 20:25:32 -0400 Subject: [PATCH 0501/1412] Try Azure again. --- appveyor.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index e1dfa4687..35369be78 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -20,17 +20,13 @@ test_script: - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - ps: Stop-Service 'MSSQL$SQL2014' - ps: Start-Service 'MSSQL$SQL2012SP1' - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" ACTIVERECORD_UNITTEST_TDSVERSION="7.3" - # - timeout /t 4 /nobreak > NUL - # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 - # - timeout /t 4 /nobreak > NUL - # - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 TDSVER=7.3 + - timeout /t 4 /nobreak > NUL + - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= From 7175b05a70d89a05722f1c5cf74225d76126e63a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 29 Apr 2016 07:40:13 -0400 Subject: [PATCH 0502/1412] Azure fix. --- test/cases/rake_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 3845e7148..925ea9aa8 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -43,7 +43,7 @@ class SQLServerRakeCreateTest < SQLServerRakeTest it 'establishes connection to database after create ' do db_tasks.create configuration - connection.current_database.must_equal(new_database) + connection.current_database.must_equal(connection_sqlserver_azure? ? 'master' : new_database) end it 'creates database with default collation' do From c66215207a50577a11a749d25076a8bb509669e2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 May 2016 21:18:29 -0400 Subject: [PATCH 0503/1412] Test w/FreeTDS 1.0rc4 --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 35369be78..936701239 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=1.0.1 + - SET TINYTDS_VERSION=1.0.2 clone_depth: 5 skip_tags: true matrix: From 2723a23ace23ba39b5d6cd6d8218a4ba31166332 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 10 May 2016 05:17:02 -0400 Subject: [PATCH 0504/1412] Isolation levels not being reset on error. Fixes #469. Thanks @anthony --- CHANGELOG.md | 7 +++++ VERSION | 2 +- .../sqlserver/database_statements.rb | 1 - .../sqlserver/transaction.rb | 5 ++++ test/cases/transaction_test_sqlserver.rb | 30 +++++++++++-------- 5 files changed, 30 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e5f8ad4..5fba25063 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.12 + +#### Fixed + +* Isolation levels not being reset on error. Fixes #469. Thanks @anthony + + ## v4.2.11 #### Fixed diff --git a/VERSION b/VERSION index 7cac85078..a50236ab5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.11 +4.2.12 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 26ae6f66e..05ca2501f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -58,7 +58,6 @@ def begin_isolated_db_transaction(isolation) def set_transaction_isolation_level(isolation_level) do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}" - begin_db_transaction end def commit_db_transaction diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index f9d90582e..5516d698b 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -36,6 +36,11 @@ def commit reset_starting_isolation_level end + def rollback + super + reset_starting_isolation_level + end + private def reset_starting_isolation_level diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index f88211325..02e1a88b3 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -32,20 +32,24 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end it 'can use an isolation level and reverts back to starting isolation level' do - begin - in_level = nil - begin_level = connection.user_options_isolation_level - begin_level.must_match %r{read committed}i - Ship.transaction(isolation: :serializable) do - Ship.create! name: 'Black Pearl' - in_level = connection.user_options_isolation_level - end - after_level = connection.user_options_isolation_level - in_level.must_match %r{serializable}i - after_level.must_match %r{read committed}i - ensure - connection.set_transaction_isolation_level 'READ COMMITTED' + in_level = nil + begin_level = connection.user_options_isolation_level + begin_level.must_match %r{read committed}i + Ship.transaction(isolation: :serializable) do + Ship.create! name: 'Black Pearl' + in_level = connection.user_options_isolation_level end + after_level = connection.user_options_isolation_level + in_level.must_match %r{serializable}i + after_level.must_match %r{read committed}i + end + + it 'can use an isolation level and reverts back to starting isolation level under exceptions' do + connection.user_options_isolation_level.must_match %r{read committed}i + lambda { + Ship.transaction(isolation: :serializable) { Ship.create! } + }.must_raise(ActiveRecord::RecordInvalid) + connection.user_options_isolation_level.must_match %r{read committed}i end From 2c09b8fe3f76aa8dc17e6ad7cdc9efc790342f97 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 12 May 2016 15:10:27 -0400 Subject: [PATCH 0505/1412] Fix bundle load paths for ActiveRecord. --- test/cases/helper_sqlserver.rb | 3 ++- test/support/paths_sqlserver.rb | 12 +++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 9403781d6..011abe3de 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,5 +1,6 @@ -require 'bundler/setup' ; Bundler.require :default, :development require 'support/paths_sqlserver' +require 'bundler/setup' +Bundler.require :default, :development require 'support/minitest_sqlserver' require 'cases/helper' require 'support/load_schema_sqlserver' diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index acf18c937..c423d91a5 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -12,17 +12,19 @@ def test_root_sqlserver end def root_activerecord - Gem.loaded_specs['activerecord'].full_gem_path + File.join Gem.loaded_specs['rails'].full_gem_path, 'activerecord' end - def test_root_activerecord + def root_activerecord_lib + File.join root_activerecord, 'lib' + end + + def root_activerecord_test File.join root_activerecord, 'test' end def test_load_paths - ar_lib = File.join root_activerecord, 'lib' - ar_test = File.join root_activerecord, 'test' - ['lib', 'test', ar_lib, ar_test] + ['lib', 'test', root_activerecord_lib, root_activerecord_test] end def add_to_load_paths! From 257c9a0c97bae4e9a02c04f8d0214cc7213f709f Mon Sep 17 00:00:00 2001 From: Marcelo Casiraghi Date: Fri, 13 May 2016 10:55:18 -0300 Subject: [PATCH 0506/1412] Add to_s method to SQLServer::Type::Char::Data object --- lib/active_record/connection_adapters/sqlserver/type/char.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 68e1fd10f..0fd5e6d33 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -24,6 +24,10 @@ def quoted "'#{@value}'" end + def to_s + @value + end + end end From 407f70e9ddd1f9813b348c5e68c1c36ead7f8a52 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 13 May 2016 11:25:50 -0400 Subject: [PATCH 0507/1412] Add to_s method to SQLServer::Type::Char::Data. * Thanks @marceloeloelo. * Fixes #471. --- CHANGELOG.md | 7 +++++++ .../connection_adapters/sqlserver/type/char.rb | 1 + 2 files changed, 8 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5fba25063..654c9e33c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.13 + +#### Fixed + +* Add to_s method to SQLServer::Type::Char::Data. Thanks @marceloeloelo. + + ## v4.2.12 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 0fd5e6d33..c2c20062d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -27,6 +27,7 @@ def quoted def to_s @value end + alias_method :to_str, :to_s end From ccd7adc11e5233aa892269c50886d32c5b9ad990 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 May 2016 09:11:43 -0400 Subject: [PATCH 0508/1412] Test latest TinyTds and azure fix. --- appveyor.yml | 2 +- test/cases/rake_test_sqlserver.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 936701239..21a0b4abf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=1.0.2 + - SET TINYTDS_VERSION=1.0.3 clone_depth: 5 skip_tags: true matrix: diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 925ea9aa8..3845e7148 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -43,7 +43,7 @@ class SQLServerRakeCreateTest < SQLServerRakeTest it 'establishes connection to database after create ' do db_tasks.create configuration - connection.current_database.must_equal(connection_sqlserver_azure? ? 'master' : new_database) + connection.current_database.must_equal(new_database) end it 'creates database with default collation' do From 8e69bd9bf78b708394c0bc852bfad2d00d249163 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 May 2016 09:55:07 -0400 Subject: [PATCH 0509/1412] Use verbose tests to find slowness. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 21a0b4abf..de9f58276 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,6 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit + - SET TESTOPTS='-v' - SET TINYTDS_VERSION=1.0.3 clone_depth: 5 skip_tags: true From 3a270203acd23bbef96b81f9b6b42d285090f705 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 19 May 2016 09:15:25 -0400 Subject: [PATCH 0510/1412] Prepare for 4.2.13 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a50236ab5..f9a3d4991 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.12 +4.2.13 From e562a67224f6e01a2fb23ca6aeb6ad25a26afa58 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 24 May 2016 19:33:28 -0400 Subject: [PATCH 0511/1412] Use TinyTDS 1.0.4 with FreeTDS 1.0.0. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index de9f58276..a34c0a7de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ init: - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - SET TESTOPTS='-v' - - SET TINYTDS_VERSION=1.0.3 + - SET TINYTDS_VERSION=1.0.4 clone_depth: 5 skip_tags: true matrix: From 39c26d444de3cb7923c36674683f086480c11ec0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 24 May 2016 19:49:31 -0400 Subject: [PATCH 0512/1412] Fix rescue constants for optional connection gems. Fixes #475. --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- .../connection_adapters/sqlserver_adapter.rb | 9 ++++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 654c9e33c..4ca0e8f0d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.14 + +#### Fixed + +* Fix rescue constants for optional connection gems. Fixes #475. + + ## v4.2.13 #### Fixed diff --git a/VERSION b/VERSION index f9a3d4991..c0b0f22a1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.13 +4.2.14 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 91f7ce8e6..1079f0e35 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -133,7 +133,7 @@ def active? return false unless @connection raw_connection_do 'SELECT 1' true - rescue TinyTds::Error, ODBC::Error + rescue *connection_errors false end @@ -307,6 +307,13 @@ def connect configure_connection end + def connection_errors + @connection_errors ||= [].tap do |errors| + errors << TinyTds::Error if defined?(TinyTds::Error) + errors << ODBC::Error if defined?(ODBC::Error) + end + end + def dblib_connect(config) TinyTds::Client.new( dataserver: config[:dataserver], From e928fe8fdd21d6e5a3d880b8a00de1868a481686 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 May 2016 15:04:14 -0400 Subject: [PATCH 0513/1412] Removed errand puts statment from database tasks. --- CHANGELOG.md | 7 +++++++ lib/active_record/tasks/sqlserver_database_tasks.rb | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ca0e8f0d..9259e0b28 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v4.2.15 + +#### Fixed + +* Removed errand puts statment from database tasks. + + ## v4.2.14 #### Fixed diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 0474a8934..57dedbd50 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -61,7 +61,6 @@ def structure_dump(filename) command.concat(table_args) view_args = connection.views.map { |v| Shellwords.escape(v) } command.concat(view_args) - puts command.inspect raise 'Error dumping database' unless Kernel.system(command.join(' ')) dump = File.read(filename) dump.gsub!(/^USE .*$\nGO\n/, '') # Strip db USE statements From 13d5266faea642ecf0eb092db2d68463d677a8d4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 May 2016 15:18:58 -0400 Subject: [PATCH 0514/1412] Fix quoting of non-national columns. --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/type/char.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 10 ++++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9259e0b28..afbfb7f89 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed * Removed errand puts statment from database tasks. +* Fix quoting of non-national columns. ## v4.2.14 diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index c2c20062d..8c27ba817 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -21,7 +21,7 @@ def initialize(value) end def quoted - "'#{@value}'" + "'#{Utils.quote_string(@value)}'" end def to_s diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 34f339f33..1216e167d 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -125,6 +125,16 @@ def quoted_id assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first } end + it 'can update and hence properly quoted non-national char/varchar columns' do + o = SSTestDatatypeMigration.create! + o.varchar_col = "O'Reilly" + o.save! + o.reload.varchar_col.must_equal "O'Reilly" + o.varchar_col = nil + o.save! + o.reload.varchar_col.must_be_nil + end + # With column names that have spaces it 'create record using a custom attribute reader and be able to load it back in' do From 47091d2f57164c1aee0061d926a5cad0319ddff5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 May 2016 15:19:27 -0400 Subject: [PATCH 0515/1412] Prepare for 4.2.15 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c0b0f22a1..35a2a4450 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.14 +4.2.15 From 5705063a08ec4e73e5bcc2d1e41a90f903acd14d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 3 Jul 2016 09:02:08 -0400 Subject: [PATCH 0516/1412] Note Rails5 Work Has Begun --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 88bdc5eff..43a1bb491 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ [![Build status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) +**RAILS v5 Work Started: 2016-07-03** + ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) ## Code Name Kantishna From 73460520013a296ffce35dc7efcd7ed35a81112f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 11 Jul 2016 08:52:42 -0400 Subject: [PATCH 0517/1412] Update README.md --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 43a1bb491..8f3aee96d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,12 @@ [![Build status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) -**RAILS v5 Work Started: 2016-07-03** +## RAILS v5 COMING!!! + +The work for Rails v5 started a on 2016-07-03 and none of the work landed in master yet. The changes for adapters from v4.2 to v5.0 is one of the most dramatic I have seen and Rails 5 compatability will take several more weeks till it is ready. + +* **Can I help?** - Thanks so much! But no, not yet. There are some foundational changs coming to master in the next few weeks. Having multiple people involved at this stage is counter productive. Please stay tuned here for when that may change. +* **Why is it taking so long?** - I spent the last several months tryingn to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievment by that team. I thought it was more important to get the low level connectoin strong before doing the adaper work. We will get there soon. ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) From 8a76f93835438efaec177e8153f8fa578fdf0ded Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 11 Jul 2016 08:54:02 -0400 Subject: [PATCH 0518/1412] Rails v5 Notes --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8f3aee96d..1107efad1 100644 --- a/README.md +++ b/README.md @@ -5,10 +5,10 @@ ## RAILS v5 COMING!!! -The work for Rails v5 started a on 2016-07-03 and none of the work landed in master yet. The changes for adapters from v4.2 to v5.0 is one of the most dramatic I have seen and Rails 5 compatability will take several more weeks till it is ready. +The work for Rails v5 started a on 2016-07-03 and none of the work landed in master yet. The changes for adapters from v4.2 to v5.0 is one of the most dramatic I have seen and Rails 5 compatibility will take several more weeks till it is ready. -* **Can I help?** - Thanks so much! But no, not yet. There are some foundational changs coming to master in the next few weeks. Having multiple people involved at this stage is counter productive. Please stay tuned here for when that may change. -* **Why is it taking so long?** - I spent the last several months tryingn to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievment by that team. I thought it was more important to get the low level connectoin strong before doing the adaper work. We will get there soon. +* **Can I help?** - Thanks so much! But no, not yet. There are some foundational changes coming to master in the next few weeks. Having multiple people involved at this stage is counter productive. Please stay tuned here for when that may change. +* **Why is it taking so long?** - I spent the last several months trying to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievement by that team. I thought it was more important to get the low level connection strong before doing the adapter work. We will get there soon. ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) From 316e71c7c1a15e8a617f6b951dbd3e6b8ea971b6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 14 Jul 2016 16:18:16 -0400 Subject: [PATCH 0519/1412] Rails 5 Updates --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 1107efad1..a4c58b148 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,11 @@ The work for Rails v5 started a on 2016-07-03 and none of the work landed in master yet. The changes for adapters from v4.2 to v5.0 is one of the most dramatic I have seen and Rails 5 compatibility will take several more weeks till it is ready. +2016-07-14 UPDATE: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/rails5 + * **Can I help?** - Thanks so much! But no, not yet. There are some foundational changes coming to master in the next few weeks. Having multiple people involved at this stage is counter productive. Please stay tuned here for when that may change. * **Why is it taking so long?** - I spent the last several months trying to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievement by that team. I thought it was more important to get the low level connection strong before doing the adapter work. We will get there soon. +* **What branch you working on?** - Right now I am on the [rails5](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/rails5) branch. ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) From 67d1e62f98c1818a29a35dac9a116fa2ce2ee682 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 20 Aug 2016 18:33:04 -0400 Subject: [PATCH 0520/1412] Rails 5 (#502) [Rails5] Initial work for basic adapter tests passing. --- CHANGELOG.md | 202 +----------------- Gemfile | 31 ++- README.md | 4 - RUNNING_UNIT_TESTS.md | 17 -- Rakefile | 9 +- TODO.md | 48 +++++ VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- appveyor.yml | 2 +- .../sqlserver/core_ext/explain.rb | 23 +- .../sqlserver/core_ext/odbc.rb | 34 --- .../sqlserver/database_statements.rb | 95 ++------ .../connection_adapters/sqlserver/quoting.rb | 37 +++- .../sqlserver/schema_cache.rb | 114 ---------- .../sqlserver/schema_creation.rb | 18 -- .../sqlserver/schema_statements.rb | 111 ++++++---- .../connection_adapters/sqlserver/showplan.rb | 14 +- .../sqlserver/sql_type_metadata.rb | 20 ++ .../sqlserver/table_definition.rb | 2 +- .../sqlserver/transaction.rb | 2 +- .../connection_adapters/sqlserver/type.rb | 3 +- .../sqlserver/type/big_integer.rb | 4 + .../sqlserver/type/binary.rb | 6 + .../sqlserver/type/boolean.rb | 3 + .../sqlserver/type/char.rb | 13 +- .../sqlserver/type/date.rb | 8 +- .../sqlserver/type/datetime.rb | 9 +- .../sqlserver/type/datetime2.rb | 4 + .../sqlserver/type/datetimeoffset.rb | 7 +- .../sqlserver/type/decimal.rb | 9 + .../sqlserver/type/float.rb | 4 + .../sqlserver/type/integer.rb | 3 + .../sqlserver/type/money.rb | 4 + .../sqlserver/type/real.rb | 4 + .../sqlserver/type/small_integer.rb | 4 +- .../sqlserver/type/small_money.rb | 4 + .../sqlserver/type/smalldatetime.rb | 3 + .../sqlserver/type/text.rb | 4 + .../sqlserver/type/time.rb | 8 +- .../sqlserver/type/timestamp.rb | 4 + .../sqlserver/type/tiny_integer.rb | 3 + .../sqlserver/type/unicode_char.rb | 6 + .../sqlserver/type/unicode_text.rb | 4 + .../sqlserver/type/unicode_varchar.rb | 6 + .../sqlserver/type/unicode_varchar_max.rb | 4 + .../sqlserver/type/uuid.rb | 8 +- .../sqlserver/type/varbinary.rb | 6 + .../sqlserver/type/varbinary_max.rb | 4 + .../sqlserver/type/varchar.rb | 6 + .../sqlserver/type/varchar_max.rb | 4 + .../connection_adapters/sqlserver_adapter.rb | 105 ++++----- .../connection_adapters/sqlserver_column.rb | 33 +-- lib/active_record/sqlserver_base.rb | 6 +- lib/arel/visitors/sqlserver.rb | 15 +- test/cases/adapter_test_sqlserver.rb | 22 +- test/cases/coerced_tests.rb | 32 +-- test/cases/column_test_sqlserver.rb | 194 ++++++++--------- test/cases/connection_test_sqlserver.rb | 75 +------ ...lly_qualified_identifier_test_sqlserver.rb | 14 +- test/cases/helper_sqlserver.rb | 17 +- .../pessimistic_locking_test_sqlserver.rb | 2 +- test/cases/rake_test_sqlserver.rb | 34 +-- test/cases/schema_dumper_test_sqlserver.rb | 144 +++++++------ test/cases/schema_test_sqlserver.rb | 4 +- test/cases/showplan_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 2 +- test/cases/transaction_test_sqlserver.rb | 2 +- test/config.yml | 9 - .../1_table_will_never_be_created.rb | 2 +- test/support/coerceable_test_sqlserver.rb | 6 +- test/support/connection_reflection.rb | 4 - 71 files changed, 665 insertions(+), 1005 deletions(-) create mode 100644 TODO.md delete mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb delete mode 100644 lib/active_record/connection_adapters/sqlserver/schema_cache.rb create mode 100644 lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index afbfb7f89..033b75284 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,212 +1,22 @@ -## v4.2.15 -#### Fixed - -* Removed errand puts statment from database tasks. -* Fix quoting of non-national columns. - - -## v4.2.14 - -#### Fixed - -* Fix rescue constants for optional connection gems. Fixes #475. - - -## v4.2.13 - -#### Fixed - -* Add to_s method to SQLServer::Type::Char::Data. Thanks @marceloeloelo. - - -## v4.2.12 - -#### Fixed - -* Isolation levels not being reset on error. Fixes #469. Thanks @anthony - - -## v4.2.11 - -#### Fixed - -* Undefined method `database_prefix_remote_server?' Fixes #450. Thanks @jippeholwerda -* Document two methods for avoiding N'' quoting on char/varchar columns. -* First run failure of `change_column` while dropping constraint. Fixes #420. Thanks @GrumpyRainbow @rkr090 -* Rounding errors w/datetime2(0) types having no fractional seconds. Fixes #465. Thanks @alawton - -#### Changed - -* Supporting escape hatch for N'' quoting. Remove `#is_utf8` string check in `#_quote` method. - This duplicated strings and forced encoding which was actually wasteful. - - -## v4.2.10 - -#### Fixed - -* Ensure small datetime/datetime2 fractionals are properly quoted. Fixes #457. - - -## v4.2.9 - -#### Fixed - -* Conform to new data_sources interfaces. See: https://git.io/va4Fp -* The `primary_key` method falls back to Identity columns. Not the other way around. Fixes #454. Thanks @marceloeloelo -* Ensure that `execute_procedure` returns proper time zones. Fixes #449 - -#### Changed - -* Run tests with verbose false. - - -## v4.2.8 - -#### Fixed - -* Azure-Friendly Disable Referential Integrity. No more `sp_MSforeachtable` usage. Fixes #421 -* Azure-Friendly DB create/drop. Fixes #442 - - Create allows edition options like: MAXSIZE, EDITION, and SERVICE_OBJECTIVE. - - -## v4.2.7 - -#### Added - -* Support 2008 Datatypes Using TDSVER=7.3. Fixes #433 - -#### Changed - -* Test now use latest v0.9.5 of TinyTDS. Includes tests for `defncopy` Windows binstub. -* Make linked servers stronger. Fixes #427. Thanks @jippeholwerda -* Use proper module for the `sqlserver_connection` method. Fixes #431. Thanks @jippeholwerda -* All datetime casting using the `Time::DATE_FORMATS[:_sqlserver_*]` formats set after connection. - -#### Removed - -* The `SQLServer::Utils.with_sqlserver_db_date_formats` helper and `quoted_date` hacks. -* The `Quoter` value type which allowed column => type special case quoting. - -#### Fixed - -* Every time datatype has perfect micro/nano second handling. -* All supported datatypes dump defaults properly to schema.rb -* Partial indexes using `:where` in schema dumper. Fixes #153 - - -## v4.2.6 - -#### Fixed - -* Allow linked servers for table names. Fixes #426. Thanks @jippeholwerda - - -## v4.2.5 - -#### Removed - -* Remove Type::Castable hacks for core type objects to force trust the DB. Allows Rails 5 attributes. - -#### Fixed - -* Tests for decimal scale. See Rails commit. http://git.io/vGotB -* Improve case comparision performace per column. Fixes #414 -* DB rollback when reversable add_column has several options. Fixes #359 -* Better column definitions for default objects. Fixes #412 - - -## v4.2.4 - -#### Fixed - -* Compatible with Rails 4.2.1. -* Fix schema limit reflection for char/varchar. Fixes #394. - - -## v4.2.3 - -#### Fixed - -* Fix SET defaults when using Azure. -* Test insert 4-byte unicode chars. -* Make rollback transaction transcount aware for implicit error rollbacks. Fixes #390 - - -## v4.2.2 - -#### Added - -* DatabaseTasks support for all tasks! Uses FreeTDS `defncopy` for structure dump. Fixes #380. -* Provide class config for `use_output_inserted` (default true) for insert SQL. Fixed #381. - - -## v4.2.1 - -#### Fixed - -* Guard against empty view definitions when `sb_helptext` fails silently. Fixes #337. -* Proper table/column escaping in the `change_column_null` method. Fixes #355. -* Use `send :include` for modules for 1.9 compatibility. Fixes #383. - - -## v4.2.0 +## v5.0.0 #### Added -* New `ActiveRecord::Type` objects. See `active_record/connection_adapters/sqlserver/type` dir. -* Aliased `ActiveRecord::Type::SQLServer` to `ActiveRecord::ConnectionAdapters::SQLServer::Type` -* New `SQLServer::Utils::Name` object for decomposing and quoting SQL Server names/identifiers. -* Support for most all SQL Server types in schema statements and dumping. -* Support create table with query from relation or select statement. -* Foreign Key Support Fixes #375. +* ... #### Changed -* The `create_database` now takes an options hash. Only key/value now is `collation`. Unknown keys just use raw values for SQL. -* Complete rewrite of our Arel visitor. Focuing on 2012 and upward so we can make FETCH happen. -* Testing enhancements. - * Guard support, check our Guardfile. - * Use `ARTest` namespace with `SQLServer` module for our helpers/objects. - * Simple 2012 schmea addition and extensive column/type_cast object tests. -* Follow Rails convention and remove varying character default limits. -* The `cs_equality_operator` is now s class configuration property only. -* The `with_identity_insert_enabled(table_name)` is now public. -* Use ActiveRecord tranasaction interface vs our own `run_with_isolation_level`. +* ... #### Deprecated -* n/a +* ... #### Removed -* SQL Server versions < 2012 which do not support OFFSET and FETCH. http://bit.ly/1B5Bwsd -* The `enable_default_unicode_types` option. Default to national types all the time. -* Native type configs for older DB support. Includes the following with new default value: - * native_string_database_type => `nvarchar` - * native_text_database_type => `nvarchar(max)` - * native_binary_database_type => `varbinary(max)` -* Various version and inspection methods removed. These include: - * database_version - * database_year - * product_level - * product_version - * edition -* Removed tests for old issue #164. Handled by core types now. -* The `activity_stats` method. Please put this in a gem if needed. -* We no longer use regular expressions to fix identity inserts. Use ActiveRecord or public ID insert helper. -* All auto reconnect and SQL retry logic. Got too complicated and stood in the way of AR's pool. Speed boost too. -* The adapter will no longer try to remove duplicate order by clauses. Use relation `reorder`, `unscope`, etc. -* We no longer use regular expressions to remove identity columns from updates. Now with `attributes_for_update` AR hook. +* ODBC connection mode. Not been maintained since Rails 4.0. #### Fixed -* Default lock is now "WITH(UPDLOCK)". Fixes #368 -* Better bind types & params for `sp_executesql`. Fixes #239. - -#### Security - -* The connection's `inspect` method no longer returns sensitive connection info. Very basic now. - - +* ... diff --git a/Gemfile b/Gemfile index 6ae57eb90..02eab67df 100644 --- a/Gemfile +++ b/Gemfile @@ -16,16 +16,20 @@ else require 'net/http' require 'yaml' spec = eval(File.read('activerecord-sqlserver-adapter.gemspec')) - version = spec.dependencies.detect{ |d|d.name == 'activerecord' }.requirement.requirements.first.last.version - major, minor, tiny = version.split('.') - 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 - YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).select do |data| - a, b, c = data['number'].split('.') - !data['prerelease'] && major == a && (minor.nil? || minor == b) - end.first['number'] + ver = spec.dependencies.detect{ |d|d.name == 'activerecord' }.requirement.requirements.first.last.version + major, minor, tiny, pre = ver.split('.') + if !pre + 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 + YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).select do |data| + a, b, c = data['number'].split('.') + !data['prerelease'] && major == a && (minor.nil? || minor == b) + end.first['number'] + else + ver + end end gem 'rails', git: "git://github.com/rails/rails.git", tag: "v#{version}" end @@ -44,15 +48,10 @@ group :tinytds do end end -group :odbc do - gem 'ruby-odbc' -end - group :development do + gem 'byebug' gem 'mocha' - gem 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint. gem 'minitest-spec-rails' - gem 'pry' end group :guard do diff --git a/README.md b/README.md index a4c58b148..a195bbf3f 100644 --- a/README.md +++ b/README.md @@ -166,10 +166,6 @@ gem 'tiny_tds' gem 'activerecord-sqlserver-adapter', '~> 4.2.0' ``` -If you want to use ruby ODBC, please use the latest least. If you have any troubles installing the lower level libraries for the adapter, please consult the wiki pages for various platform installation guides. Tons of good info can be found and we ask that you contribute too! - -http://wiki.github.com/rails-sqlserver/activerecord-sqlserver-adapter/platform-installation - ## Contributing diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index fdeed143a..59ac591eb 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -28,8 +28,6 @@ $ bundle exec rake test TEST_FILES_AR="test/cases/finder_test.rb" The default names for the test databases are `activerecord_unittest` and `activerecord_unittest2`. If you want to use another database name then be sure to update the connection file that matches your connection method in test/connections/native_sqlserver_#{connection_method}/connection.rb. Define a user named 'rails' in SQL Server with all privileges granted for the test databases. Use an empty password for said user. -The connection files make certain assumptions. For instance, the ODBC connection assumes you have a DSN setup that matches the name of the default database names. Remember too you have to set an environment variable for the DSN of the adapter, see the connection.rb file that matches your connection mode for details. - ```sql CREATE DATABASE [activerecord_unittest]; CREATE DATABASE [activerecord_unittest2]; @@ -93,18 +91,6 @@ $ bundle exec rake test ## Testing Options -The Gemfile contains groups for `:tinytds` and `:odbc`. By default it will install both gems which allows you to run the full test suite in either connection mode. If for some reason any one of these is problematic or of no concern, you could always opt out of bundling either gem with something like this. - -``` -$ bundle install --without odbc -``` - -You can run different connection modes using the following rake commands. Again, the DBLIB connection mode using TinyTDS is the default test task. - -``` -$ bundle exec rake test:dblib -$ bundle exec rake test:odbc -``` By default, Bundler will download the Rails git repo and use the git tag that matches the dependency version in our gemspec. If you want to test another version of Rails, you can either temporarily change the :tag for Rails in the Gemfile. Likewise, you can clone the Rails repo your self to another directory and use the `RAILS_SOURCE` environment variable. @@ -116,6 +102,3 @@ By default, Bundler will download the Rails git repo and use the git tag that ma * Possibly change the SQL Server TCP/IP properties in "SQL Server Configuration Manager -> SQL Server Network Configuration -> Protocols for MSSQLSERVER", and ensure that TCP/IP is enabled and the appropriate entries on the "IP Addresses" tab are enabled. -## Current Expected Failures - -* Misc Date/Time erros when using ODBC mode. diff --git a/Rakefile b/Rakefile index ed71ab146..79a4a5885 100644 --- a/Rakefile +++ b/Rakefile @@ -8,7 +8,7 @@ task default: [:test] namespace :test do - %w(dblib odbc).each do |mode| + %w(dblib).each do |mode| Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths @@ -23,17 +23,12 @@ namespace :test do ENV['ARCONN'] = 'dblib' end - task 'odbc:env' do - ENV['ARCONN'] = 'odbc' - end - end task 'test:dblib' => 'test:dblib:env' -task 'test:odbc' => 'test:odbc:env' namespace :profile do - ['dblib', 'odbc'].each do |mode| + ['dblib'].each do |mode| namespace mode.to_sym do Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| profile_case = File.basename(test_file).sub('_profile_case.rb', '') diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..14b0a0bfb --- /dev/null +++ b/TODO.md @@ -0,0 +1,48 @@ + +## SHORT TERM + +Misc remidners while in the heat of adapting the adpater. + +* Try removing `sp_executesql_sql_type` all together. Do we have to add more types? +* Did we get the schema cache right? + + + +## LONG TERM + +After we get some tests passing + +* Is `primary_keys(table_name)` performant? Contribute to rails for abstract adapter. +* Check `sql_for_insert` can do without the table regular expresion. +* Do we need the `query_requires_identity_insert` check in `execute`? +* Will we have to add more Data types to our dates and use them in `quoted_date` or `quoted_string` or `_type_cast`? + + +#### Use #without_prepared_statement? + +I think we always send everything thru `sp_executesql`. Consider re-evaulating if there are no `binds` that we get any benefit from this. By doing so we also give the users the ability to turn this off completly. Would be neat to see how our prepared statments actually perform again. + +```ruby +def without_prepared_statement?(binds) + !prepared_statements || binds.empty? +end +``` + +Maybe just quick bail to `do_execute`. Maybe related: + +* [Do not cache prepared statements that are unlikely to have cache hits](https://github.com/rails/rails/commit/cbcdecd2) + + + + +#### Does Find By SQL Work? + +With binds and prepareable? + +```ruby +# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] +# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }] +# +def find_by_sql(sql, binds = [], preparable: nil) + result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable) +``` diff --git a/VERSION b/VERSION index 35a2a4450..0062ac971 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -4.2.15 +5.0.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 8d906f2b3..9001c00dc 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,5 +16,5 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 4.2.1' + spec.add_dependency 'activerecord', '~> 5.0.0' end diff --git a/appveyor.yml b/appveyor.yml index a34c0a7de..91782c17f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -12,7 +12,7 @@ install: - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version - gem --version - - bundle install --without odbc guard + - bundle install build: off test_script: - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" 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 02721d01e..bdacc573e 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -4,11 +4,14 @@ module SQLServer module CoreExt module Explain - SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' - SQLSERVER_PARAM_MATCHER = /@\d+ =/ + SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '.freeze + SQLSERVER_PARAM_MATCHER = /@\d+ = (.*)/ + SQLSERVER_NATIONAL_STRING_MATCHER = /N'(.*)'/m def exec_explain(queries) - unprepared_queries = queries.map { |sql, bind| [unprepare_sqlserver_statement(sql), bind] } + unprepared_queries = queries.map do |(sql, binds)| + [unprepare_sqlserver_statement(sql), binds] + end super(unprepared_queries) end @@ -20,11 +23,15 @@ def exec_explain(queries) def unprepare_sqlserver_statement(sql) if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) - executesql_args = executesql.split(', ') - found_args = executesql_args.reject! { |arg| arg =~ SQLSERVER_PARAM_MATCHER } - executesql_args.pop if found_args && executesql_args.many? - executesql = executesql_args.join(', ').strip.match(/N'(.*)'/m)[1] - Utils.unquote_string(executesql) + args = executesql.split(', ') + unprepared_sql = args.shift.strip.match(SQLSERVER_NATIONAL_STRING_MATCHER)[1] + unprepared_sql = Utils.unquote_string(unprepared_sql) + args = args.from(args.length / 2) + args.each_with_index do |arg, index| + value = arg.match(SQLSERVER_PARAM_MATCHER)[1] + unprepared_sql.sub! "@#{index}", value + end + unprepared_sql else sql end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb deleted file mode 100644 index 712751a2b..000000000 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +++ /dev/null @@ -1,34 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - module CoreExt - module ODBC - - module Statement - - def finished? - connected? - false - rescue ::ODBC::Error - true - end - - end - - module Database - - def run_block(*args) - yield sth = run(*args) - sth.drop - end - - end - - end - end - end - end -end - -ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Statement -ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Database diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 05ca2501f..9083ead03 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -19,11 +19,9 @@ def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) sp_executesql(sql, name, binds) end - def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil) - id_insert = binds_have_identity_column?(binds) - id_table = table_name_from_binds(binds) if id_insert - if id_insert && id_table - with_identity_insert_enabled(id_table) { exec_query(sql, name, binds) } + def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil) + if pk && id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name) { exec_query(sql, name, binds) } else exec_query(sql, name, binds) end @@ -122,18 +120,6 @@ def execute_procedure(proc_name, *variables) yield(r) if block_given? end result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row } - when :odbc - results = [] - raw_connection_run(sql) do |handle| - get_rows = lambda do - rows = handle_to_names_and_values handle, fetch: :all - rows.each_with_index { |r, i| rows[i] = r.with_indifferent_access } - results << rows - end - get_rows.call - get_rows.call while handle_more_results?(handle) - end - results.many? ? results : results.first end end end @@ -216,31 +202,21 @@ def select(sql, name = nil, binds = []) end def sql_for_insert(sql, pk, id_value, sequence_name, binds) - sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server? - quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted - sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" - else - "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" + if pk.nil? + table_name = query_requires_identity_insert?(sql) + pk = primary_key(table_name) end + sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server? + quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted + sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + else + "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" + end super end # === SQLServer Specific ======================================== # - def binds_have_identity_column?(binds) - binds.any? do |column_value| - column, value = column_value - SQLServerColumn === column && column.is_identity? - end - end - - def table_name_from_binds(binds) - binds.detect { |column_value| - column, value = column_value - SQLServerColumn === column - }.try(:first).try(:table_name) - end - def set_identity_insert(table_name, enable = true) do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" rescue Exception @@ -262,20 +238,21 @@ def sp_executesql(sql, name, binds, options = {}) def sp_executesql_types_and_parameters(binds) types, params = [], [] - binds.each_with_index do |(column, value), index| - types << "@#{index} #{sp_executesql_sql_type(column, value)}" - params << quote(value, column) + binds.each_with_index do |attr, index| + types << "@#{index} #{sp_executesql_sql_type(attr)}" + params << type_cast(attr.value_for_database) end [types, params] end - def sp_executesql_sql_type(column, value) - return column.sql_type_for_statement if SQLServerColumn === column - if value.is_a?(Numeric) - 'int' - # We can do more here later. + def sp_executesql_sql_type(attr) + return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) + case value = attr.value_for_database + when Numeric + 'int'.freeze else - 'nvarchar(max)' + raise TypeError, "sp_executesql_sql_type can not find sql type for attr #{attr.inspect}" + quote(value) end end @@ -283,7 +260,7 @@ def sp_executesql_sql(sql, types, params, name) if name == 'EXPLAIN' params.each.with_index do |param, index| substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values. - sql.sub! substitute_at_finder, param + sql.sub! substitute_at_finder, param.to_s end else types = quote(types.join(', ')) @@ -298,8 +275,6 @@ def raw_connection_do(sql) case @connection_options[:mode] when :dblib @connection.execute(sql).do - when :odbc - @connection.do(sql) end ensure @update_sql = false @@ -322,16 +297,12 @@ def raw_connection_run(sql) case @connection_options[:mode] when :dblib @connection.execute(sql) - when :odbc - block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql) end end def handle_more_results?(handle) case @connection_options[:mode] when :dblib - when :odbc - handle.more_results end end @@ -339,8 +310,6 @@ def handle_to_names_and_values(handle, options = {}) case @connection_options[:mode] when :dblib handle_to_names_and_values_dblib(handle, options) - when :odbc - handle_to_names_and_values_odbc(handle, options) end end @@ -354,28 +323,10 @@ def handle_to_names_and_values_dblib(handle, options = {}) options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results end - def handle_to_names_and_values_odbc(handle, options = {}) - @connection.use_utc = ActiveRecord::Base.default_timezone == :utc - if options[:ar_result] - columns = lowercase_schema_reflection ? handle.columns(true).map { |c| c.name.downcase } : handle.columns(true).map { |c| c.name } - rows = handle.fetch_all || [] - ActiveRecord::Result.new(columns, rows) - else - case options[:fetch] - when :all - handle.each_hash || [] - when :rows - handle.fetch_all || [] - end - end - end - def finish_statement_handle(handle) case @connection_options[:mode] when :dblib handle.cancel if handle - when :odbc - handle.drop if handle && handle.respond_to?(:drop) && !handle.finished? end handle end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 724e8a312..c4c4da941 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -7,6 +7,18 @@ module Quoting QUOTED_FALSE = '0' QUOTED_STRING_PREFIX = 'N' + def fetch_type_metadata(sql_type, sqlserver_options = {}) + cast_type = lookup_cast_type(sql_type) + SQLServer::SqlTypeMetadata.new( + sql_type: sql_type, + type: cast_type.type, + limit: cast_type.limit, + precision: cast_type.precision, + scale: cast_type.scale, + sqlserver_options: sqlserver_options + ) + end + def quote_string(s) SQLServer::Utils.quote_string(s) end @@ -15,11 +27,12 @@ def quote_column_name(name) SQLServer::Utils.extract_identifiers(name).quoted end - def quote_default_value(value, column) - if column.type == :uuid && value =~ /\(\)/ + def quote_default_expression(value, column) + cast_type = lookup_cast_type(column.sql_type) + if cast_type.type == :uuid && value =~ /\(\)/ value else - quote(value, column) + super end end @@ -41,9 +54,9 @@ def unquoted_false def quoted_date(value) if value.acts_like?(:date) - Type::Date.new.type_cast_for_database(value) + Type::Date.new.serialize(value) else value.acts_like?(:time) - Type::DateTime.new.type_cast_for_database(value) + Type::DateTime.new.serialize(value) end end @@ -63,6 +76,20 @@ def _quote(value) end end + def _type_cast(value) + case value + when nil + "NULL" + when Symbol + _quote(value.to_s) + when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data + _quote(value) + when ActiveRecord::Type::SQLServer::Char::Data + value.quoted + else super + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb b/lib/active_record/connection_adapters/sqlserver/schema_cache.rb deleted file mode 100644 index 427794697..000000000 --- a/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +++ /dev/null @@ -1,114 +0,0 @@ -module ActiveRecord - module ConnectionAdapters - module SQLServer - class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache - - def initialize(conn) - super - @views = {} - @view_information = {} - end - - # Superclass Overrides - - def primary_keys(table_name) - name = key(table_name) - @primary_keys[name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil - end - - def table_exists?(table_name) - name = key(table_name) - prepare_tables_and_views - return @tables[name] if @tables.key? name - table_exists = @tables[name] = connection.table_exists?(table_name) - table_exists || view_exists?(table_name) - end - - def tables(name) - super(key(name)) - end - - def columns(table_name) - name = key(table_name) - @columns[name] ||= connection.columns(table_name) - end - - def columns_hash(table_name) - name = key(table_name) - @columns_hash[name] ||= Hash[columns(table_name).map { |col| - [col.name, col] - }] - end - - def clear! - super - @views.clear - @view_information.clear - end - - def size - super + [@views, @view_information].map{ |x| x.size }.inject(:+) - end - - def clear_table_cache!(table_name) - name = key(table_name) - @columns.delete name - @columns_hash.delete name - @primary_keys.delete name - @tables.delete name - @views.delete name - @view_information.delete name - end - - def marshal_dump - super + [@views, @view_information] - end - - def marshal_load(array) - @views, @view_information = array[-2..-1] - super(array[0..-3]) - end - - # SQL Server Specific - - def view_exists?(table_name) - name = key(table_name) - prepare_tables_and_views - return @views[name] if @views.key? name - @views[name] = connection.views.include?(table_name) - end - - def view_information(table_name) - name = key(table_name) - return @view_information[name] if @view_information.key? name - @view_information[name] = connection.send(:view_information, table_name) - end - - - private - - def identifier(table_name) - SQLServer::Utils.extract_identifiers(table_name) - end - - def key(table_name) - identifier(table_name).quoted - end - - def prepare_tables_and_views - prepare_views if @views.empty? - prepare_tables if @tables.empty? - end - - def prepare_tables - connection.tables.each { |table| @tables[key(table)] = true } - end - - def prepare_views - connection.views.each { |view| @views[key(view)] = true } - end - - end - end - end -end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 134dd8c92..b43354c91 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -5,15 +5,6 @@ class SchemaCreation < AbstractAdapter::SchemaCreation private - def visit_ColumnDefinition(o) - sql = super - if o.primary_key? && o.type == :uuid - sql << ' PRIMARY KEY ' - add_column_options!(sql, column_options(o)) - end - sql - end - def visit_TableDefinition(o) if o.as table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name) @@ -25,15 +16,6 @@ def visit_TableDefinition(o) end end - def add_column_options!(sql, options) - column = options.fetch(:column) { return super } - if (column.type == :uuid || column.type == :uniqueidentifier) && options[:default] =~ /\(\)/ - sql << " DEFAULT #{options.delete(:default)}" - else - super - end - end - def action_sql(action, dependency) case dependency when :restrict diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 66907d6f3..2c17d6741 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,23 +7,23 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end - def data_sources - tables + views - end - def tables(table_type = 'BASE TABLE') select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME", 'SCHEMA' end - def table_exists?(table_name) + def data_source_exists?(table_name) return false if table_name.blank? unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object - super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name) + super(unquoted_table_name) + end + + def views + tables('VIEW') end - def create_table(table_name, options = {}) + def create_table(table_name, comment: nil, **options) res = super - schema_cache.clear_table_cache!(table_name) + clear_cache! res end @@ -47,17 +47,41 @@ def indexes(table_name, name = nil) end end - def columns(table_name, _name = nil) + def columns(table_name) return [] if table_name.blank? column_definitions(table_name).map do |ci| - sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :default_function, :table_name, :collation - cast_type = lookup_cast_type(ci[:type]) - new_column ci[:name], ci[:default_value], cast_type, ci[:type], ci[:null], sqlserver_options + sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity + sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options + new_column( + ci[:name], + ci[:default_value], + sql_type_metadata, + ci[:null], + ci[:table_name], + ci[:default_function], + ci[:collation], + nil, + sqlserver_options + ) end end - def new_column(name, default, cast_type, sql_type = nil, null = true, sqlserver_options={}) - SQLServerColumn.new name, default, cast_type, sql_type, null, sqlserver_options + def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + SQLServerColumn.new( + name, + default, + sql_type_metadata, + null, table_name, + default_function, + collation, + comment, + sqlserver_options + ) + end + + def primary_keys(table_name) + primaries = columns(table_name).select(&:is_primary?).map(&:name) + primaries.present? ? primaries : identity_columns(table_name).map(&:name) end def rename_table(table_name, new_name) @@ -82,11 +106,11 @@ def change_column(table_name, column_name, type, options = {}) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } remove_indexes(table_name, column_name) end - sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? + sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false if options_include_default?(options) - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(options[:default], column_object)} FOR #{quote_column_name(column_name)}" + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}" end # Add any removed indexes back indexes.each do |index| @@ -96,20 +120,20 @@ def change_column(table_name, column_name, type, options = {}) end def change_column_default(table_name, column_name, default) - schema_cache.clear_table_cache!(table_name) + clear_cache! remove_default_constraint(table_name, column_name) column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } - do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_value(default, column_object)} FOR #{quote_column_name(column_name)}" - schema_cache.clear_table_cache!(table_name) + do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column_object)} FOR #{quote_column_name(column_name)}" + clear_cache! end def rename_column(table_name, column_name, new_column_name) - schema_cache.clear_table_cache!(table_name) + clear_cache! detect_column_for! table_name, column_name identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}") execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN' rename_column_indexes(table_name, column_name, new_column_name) - schema_cache.clear_table_cache!(table_name) + clear_cache! end def rename_index(table_name, old_name, new_name) @@ -183,12 +207,6 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute sql end - # === SQLServer Specific ======================================== # - - def views - tables('VIEW') - end - protected @@ -235,7 +253,7 @@ def column_definitions(table_name) SQLServer::Utils.extract_identifiers(table_name) end database = identifier.fully_qualified_database_quoted - view_exists = schema_cache.view_exists?(table_name) + view_exists = view_exists?(table_name) view_tblnm = table_name_or_views_table_name(table_name) if view_exists sql = %{ SELECT DISTINCT @@ -284,9 +302,11 @@ def column_definitions(table_name) WHERE columns.TABLE_NAME = @0 AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : '@1'} ORDER BY columns.ordinal_position - }.gsub(/[ \t\r\n]+/, ' ') - binds = [[info_schema_table_name_column, identifier.object]] - binds << [info_schema_table_schema_column, identifier.schema] unless identifier.schema.blank? + }.gsub(/[ \t\r\n]+/, ' ').strip + binds = [] + nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 + binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) + binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? results = sp_executesql(sql, 'SCHEMA', binds) results.map do |ci| ci = ci.symbolize_keys @@ -347,14 +367,6 @@ def column_definitions(table_name) end end - def info_schema_table_name_column - @info_schema_table_name_column ||= new_column 'table_name', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true - end - - def info_schema_table_schema_column - @info_schema_table_schema_column ||= new_column 'table_schema', nil, lookup_cast_type('nvarchar(128)'), 'nvarchar(128)', true - end - def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA' constraints.each do |constraint| @@ -407,8 +419,13 @@ def lowercase_schema_reflection_sql(node) # === SQLServer Specific (View Reflection) ====================== # + def view_exists?(table_name) + identifier = SQLServer::Utils.extract_identifiers(table_name) + views.include? identifier.object + end + def view_table_name(table_name) - view_info = schema_cache.view_information(table_name) + view_info = view_information(table_name) view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name end @@ -430,11 +447,11 @@ def view_information(table_name) end def table_name_or_views_table_name(table_name) - schema_cache.view_exists?(table_name) ? view_table_name(table_name) : table_name + view_exists?(table_name) ? view_table_name(table_name) : table_name end def views_real_column_name(table_name, column_name) - view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION] + view_definition = view_information(table_name)[:VIEW_DEFINITION] return column_name unless view_definition match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name @@ -445,7 +462,7 @@ def views_real_column_name(table_name, column_name) def query_requires_identity_insert?(sql) if insert_sql?(sql) table_name = get_table_name(sql) - id_column = identity_column(table_name) + id_column = identity_columns(table_name).first id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false else false @@ -456,15 +473,15 @@ def insert_sql?(sql) !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? end - def identity_column(table_name) - schema_cache.columns(table_name).find(&:is_identity?) + def identity_columns(table_name) + columns(table_name).select(&:is_identity?) end private - def create_table_definition(name, temporary, options, as = nil) - SQLServer::TableDefinition.new native_database_types, name, temporary, options, as + def create_table_definition(*args) + SQLServer::TableDefinition.new(*args) end end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 7c89ae89a..33839ca1e 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -28,32 +28,32 @@ def with_showplan_on end def set_showplan_option(enable = true) - sql = "SET #{option} #{enable ? 'ON' : 'OFF'}" + sql = "SET #{showplan_option} #{enable ? 'ON' : 'OFF'}" raw_connection_do(sql) rescue Exception - raise ActiveRecordError, "#{option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" + raise ActiveRecordError, "#{showplan_option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" end - def option + def showplan_option (SQLServerAdapter.showplan_option || OPTION_ALL).tap do |opt| raise(ArgumentError, "Unknown SHOWPLAN option #{opt.inspect} found.") if OPTIONS.exclude?(opt) end end def showplan_all? - option == OPTION_ALL + showplan_option == OPTION_ALL end def showplan_text? - option == OPTION_TEXT + showplan_option == OPTION_TEXT end def showplan_xml? - option == OPTION_XML + showplan_option == OPTION_XML end def showplan_printer - case option + case showplan_option when OPTION_XML then PrinterXml when OPTION_ALL, OPTION_TEXT then PrinterTable else PrinterTable diff --git a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb new file mode 100644 index 000000000..f5640ca68 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -0,0 +1,20 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + class SqlTypeMetadata < ActiveRecord::ConnectionAdapters::SqlTypeMetadata + + def initialize(**kwargs) + @sqlserver_options = kwargs.extract!(:sqlserver_options) + super(**kwargs) + end + + protected + + def attributes_for_hash + super + [@sqlserver_options] + end + + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 1fcc5f757..fd2f6eb1d 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -3,7 +3,7 @@ module ConnectionAdapters module SQLServer class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition - def primary_key(name, type = :primary_key, options = {}) + def primary_key(name, type = :primary_key, **options) return super unless type == :uuid options[:default] = options.fetch(:default, 'NEWID()') options[:primary_key] = true diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 5516d698b..81a58e968 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -25,7 +25,7 @@ module SQLServerRealTransaction attr_reader :starting_isolation_level - def initialize(connection, options) + def initialize(connection, options, run_commit_callbacks: false) @connection = connection @starting_isolation_level = current_isolation_level if options[:isolation] super diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index d115417c7..47bb07be5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,4 +1,6 @@ require 'active_record/type' +# Behaviors +require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb' # Exact Numerics require 'active_record/connection_adapters/sqlserver/type/integer.rb' require 'active_record/connection_adapters/sqlserver/type/big_integer.rb' @@ -12,7 +14,6 @@ require 'active_record/connection_adapters/sqlserver/type/float.rb' require 'active_record/connection_adapters/sqlserver/type/real.rb' # Date and Time -require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb' require 'active_record/connection_adapters/sqlserver/type/date.rb' require 'active_record/connection_adapters/sqlserver/type/datetime.rb' require 'active_record/connection_adapters/sqlserver/type/datetime2.rb' diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb index b95424d86..5b786aa24 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -8,6 +8,10 @@ def type :bigint end + def sqlserver_type + 'bigint'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/binary.rb b/lib/active_record/connection_adapters/sqlserver/type/binary.rb index cad49bd4f..06cb42b37 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/binary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/binary.rb @@ -8,6 +8,12 @@ def type :binary_basic end + def sqlserver_type + 'binary'.tap do |type| + type << "(#{limit})" if limit + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb index 545decd17..e2b48e701 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Boolean < ActiveRecord::Type::Boolean + def sqlserver_type + 'bit'.freeze + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 8c27ba817..dcf72ad7a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -8,20 +8,27 @@ def type :char end - def type_cast_for_database(value) + def serialize(value) return if value.nil? return value if value.is_a?(Data) Data.new(super) end + def sqlserver_type + 'char'.tap do |type| + type << "(#{limit})" if limit + end + end + class Data def initialize(value) - @value = value.to_s + @quoted_id = value.respond_to?(:quoted_id) + @value = @quoted_id ? value.quoted_id : value.to_s end def quoted - "'#{Utils.quote_string(@value)}'" + @quoted_id ? @value : "'#{Utils.quote_string(@value)}'" end def to_s diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index ae086b1ce..d283e60a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -4,14 +4,18 @@ module SQLServer module Type class Date < ActiveRecord::Type::Date - def type_cast_for_database(value) + def sqlserver_type + 'date'.freeze + end + + def serialize(value) return unless value.present? return value if value.acts_like?(:string) value.to_s(:_sqlserver_dateformat) end def type_cast_for_schema(value) - type_cast_for_database(value).inspect + serialize(value).inspect end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 0ab38f03a..4cc3485d3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,7 +6,11 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional - def type_cast_for_database(value) + def sqlserver_type + 'datetime'.freeze + end + + def serialize(value) return super unless value.acts_like?(:time) value = zone_conversion(value) datetime = value.to_s(:_sqlserver_datetime) @@ -17,10 +21,9 @@ def type_cast_for_database(value) end def type_cast_for_schema(value) - type_cast_for_database(value).inspect + serialize(value).inspect end - private def cast_value(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb index 02ac6010b..946418821 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -10,6 +10,10 @@ def type :datetime2 end + def sqlserver_type + "datetime2(#{precision.to_i})" + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 17b4a1a50..4798a3e94 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -8,15 +8,18 @@ def type :datetimeoffset end - def type_cast_for_database(value) + def serialize(value) return super unless value.acts_like?(:time) value.to_s :_sqlserver_datetimeoffset end def type_cast_for_schema(value) - type_cast_for_database(value).inspect + serialize(value).inspect end + def sqlserver_type + "datetimeoffset(#{precision.to_i})" + end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb index 4695a585e..1383de37e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb @@ -4,6 +4,15 @@ module SQLServer module Type class Decimal < ActiveRecord::Type::Decimal + def sqlserver_type + 'decimal'.tap do |type| + type << "(#{precision.to_i},#{scale.to_i})" if precision || scale + end + end + + def type_cast_for_schema(value) + value.is_a?(BigDecimal) ? value.to_s : value.inspect + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index f99353f65..d26433dd3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -8,6 +8,10 @@ def type :float end + def sqlserver_type + 'float'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/integer.rb b/lib/active_record/connection_adapters/sqlserver/type/integer.rb index edfd1d6ae..72f93e275 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/integer.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Integer < ActiveRecord::Type::Integer + def sqlserver_type + 'int'.freeze + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index a03d20b72..e0c93d02a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -14,6 +14,10 @@ def type :money end + def sqlserver_type + 'money'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/real.rb b/lib/active_record/connection_adapters/sqlserver/type/real.rb index de459e6ae..c0c143c52 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/real.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/real.rb @@ -8,6 +8,10 @@ def type :real end + def sqlserver_type + 'real'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb index f1761e5b4..ab2659455 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb @@ -4,7 +4,9 @@ module SQLServer module Type class SmallInteger < Integer - + def sqlserver_type + 'smallint'.freeze + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 6f889abe7..0ba487895 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -14,6 +14,10 @@ def type :smallmoney end + def sqlserver_type + 'smallmoney'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 42b1cdc0d..6bf93d3f0 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -8,6 +8,9 @@ def type :smalldatetime end + def sqlserver_type + 'smalldatetime'.freeze + end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/text.rb b/lib/active_record/connection_adapters/sqlserver/type/text.rb index bc472046c..8d49ff731 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/text.rb @@ -8,6 +8,10 @@ def type :text_basic end + def sqlserver_type + 'text'.freeze + end + 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 36245f479..5af08949e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -6,7 +6,7 @@ class Time < ActiveRecord::Type::Time include TimeValueFractional2 - def type_cast_for_database(value) + def serialize(value) return super unless value.acts_like?(:time) time = value.to_s(:_sqlserver_time) "#{time}".tap do |v| @@ -16,7 +16,11 @@ def type_cast_for_database(value) end def type_cast_for_schema(value) - type_cast_for_database(value).inspect + serialize(value).inspect + end + + def sqlserver_type + "time(#{precision.to_i})" end diff --git a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb index 7ed713bd5..b0e38a827 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb @@ -8,6 +8,10 @@ def type :ss_timestamp end + def sqlserver_type + 'timestamp'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb index 2f49a5313..739b51347 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb @@ -4,6 +4,9 @@ module SQLServer module Type class TinyInteger < Integer + def sqlserver_type + 'tinyint'.freeze + end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb index 7795029a5..6934073e1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -8,6 +8,12 @@ def type :nchar end + def sqlserver_type + 'nchar'.tap do |type| + type << "(#{limit})" if limit + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb index 017b6595b..c39e18a11 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb @@ -8,6 +8,10 @@ def type :ntext end + def sqlserver_type + 'ntext'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index ed8fafc4d..008320c79 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -13,6 +13,12 @@ def type :string end + def sqlserver_type + 'nvarchar'.tap do |type| + type << "(#{limit})" if limit + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index 7ba417c9e..5f6990a0b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -13,6 +13,10 @@ def type :text end + def sqlserver_type + 'nvarchar(max)'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index 5edfb7b27..9457f44c5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -6,13 +6,17 @@ class Uuid < String ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x - alias_method :type_cast_for_database, :type_cast_from_database + alias_method :serialize, :deserialize def type :uuid end - def type_cast(value) + def sqlserver_type + 'uniqueidentifier'.freeze + end + + def cast(value) value.to_s[ACCEPTABLE_UUID, 0] end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index d09449f8a..265af265b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -13,6 +13,12 @@ def type :varbinary end + def sqlserver_type + 'varbinary'.tap do |type| + type << "(#{limit})" if limit + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index 65d82edcf..a251c268a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -13,6 +13,10 @@ def type :binary end + def sqlserver_type + 'varbinary(max)'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index e5a65176b..b3091718a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -13,6 +13,12 @@ def type :varchar end + def sqlserver_type + 'varchar'.tap do |type| + type << "(#{limit})" if limit + end + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 601cec9f1..79fa56464 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -13,6 +13,10 @@ def type :varchar_max end + def sqlserver_type + 'varchar(max)'.freeze + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1079f0e35..aa3171a72 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -13,9 +13,9 @@ require 'active_record/connection_adapters/sqlserver/database_tasks' require 'active_record/connection_adapters/sqlserver/transaction' require 'active_record/connection_adapters/sqlserver/errors' -require 'active_record/connection_adapters/sqlserver/schema_cache' require 'active_record/connection_adapters/sqlserver/schema_creation' require 'active_record/connection_adapters/sqlserver/schema_statements' +require 'active_record/connection_adapters/sqlserver/sql_type_metadata' require 'active_record/connection_adapters/sqlserver/showplan' require 'active_record/connection_adapters/sqlserver/table_definition' require 'active_record/connection_adapters/sqlserver/quoting' @@ -42,27 +42,27 @@ class SQLServerAdapter < AbstractAdapter cattr_accessor :cs_equality_operator, instance_accessor: false cattr_accessor :use_output_inserted, instance_accessor: false - cattr_accessor :lowercase_schema_reflection, :showplan_option + cattr_accessor :showplan_option, instance_accessor: false + cattr_accessor :lowercase_schema_reflection self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS' self.use_output_inserted = true - def initialize(connection, logger, pool, config) - super(connection, logger, pool) - # AbstractAdapter Responsibility - @schema_cache = SQLServer::SchemaCache.new self - @visitor = Arel::Visitors::SQLServer.new self - @prepared_statements = true + def initialize(connection, logger = nil, config = {}) + super(connection, logger, config) # Our Responsibility @connection_options = config connect - @sqlserver_azure = !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i) initialize_dateformatter use_database end # === Abstract Adapter ========================================== # + def arel_visitor + Arel::Visitors::SQLServer.new self + end + def valid_type?(type) !native_database_types[type].nil? end @@ -71,10 +71,6 @@ def schema_creation SQLServer::SchemaCreation.new self end - def adapter_name - ADAPTER_NAME - end - def supports_migrations? true end @@ -83,10 +79,6 @@ def supports_primary_key? true end - def supports_count_distinct? - true - end - def supports_ddl_transactions? true end @@ -95,14 +87,26 @@ def supports_bulk_alter? false end + def supports_advisory_locks? + false + end + def supports_index_sort_order? true end + def supports_index_sort_order? + false + end + def supports_partial_index? true end + def supports_expression_index? + false + end + def supports_explain? true end @@ -111,14 +115,34 @@ def supports_transaction_isolation? true end + def supports_indexes_in_create? + false + end + + def supports_foreign_keys? + true + end + def supports_views? true end - def supports_foreign_keys? + def supports_datetime_with_precision? + false + end + + def supports_json? true end + def supports_comments? + false + end + + def supports_comments_in_create? + false + end + def disable_referential_integrity tables = tables_with_referential_integrity tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" } @@ -149,8 +173,6 @@ def disconnect! case @connection_options[:mode] when :dblib @connection.close rescue nil - when :odbc - @connection.disconnect rescue nil end @connection = nil end @@ -180,10 +202,6 @@ def pk_and_sequence_for(table_name) pk ? [pk, nil] : nil end - def primary_key(table_name) - schema_cache.columns(table_name).find(&:is_primary?).try(:name) || identity_column(table_name).try(:name) - end - # === SQLServer Specific (DB Reflection) ======================== # def sqlserver? @@ -191,7 +209,7 @@ def sqlserver? end def sqlserver_azure? - @sqlserver_azure + @sqlserver_azure ||= !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i) end def database_prefix_remote_server? @@ -212,6 +230,13 @@ def inspect "#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>" end + def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil) + result = from_clause + join_clause + where_clause + having_clause + result << offset if offset + result << limit if limit + result + end + protected @@ -281,13 +306,13 @@ def initialize_type_map(m) def translate_exception(e, message) case message when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i - RecordNotUnique.new(message, e) + RecordNotUnique.new(message) when /conflicted with the foreign key constraint/i - InvalidForeignKey.new(message, e) + InvalidForeignKey.new(message) when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message, e) + DeadlockVictim.new(message) when /database .* does not exist/i - NoDatabaseError.new(message, e) + NoDatabaseError.new(message) else super end @@ -300,8 +325,6 @@ def connect @connection = case config[:mode] when :dblib dblib_connect(config) - when :odbc - odbc_connect(config) end @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first configure_connection @@ -310,7 +333,6 @@ def connect def connection_errors @connection_errors ||= [].tap do |errors| errors << TinyTds::Error if defined?(TinyTds::Error) - errors << ODBC::Error if defined?(ODBC::Error) end end @@ -347,25 +369,6 @@ def dblib_connect(config) end end - def odbc_connect(config) - if config[:dsn].include?(';') - driver = ODBC::Driver.new.tap do |d| - d.name = config[:dsn_name] || 'Driver1' - d.attrs = config[:dsn].split(';').map { |atr| atr.split('=') }.reject { |kv| kv.size != 2 }.reduce({}) { |a, e| k, v = e ; a[k] = v ; a } - end - ODBC::Database.new.drvconnect(driver) - else - ODBC.connect config[:dsn], config[:username], config[:password] - end.tap do |c| - begin - c.use_time = true - c.use_utc = ActiveRecord::Base.default_timezone == :utc - rescue Exception - warn 'Ruby ODBC v0.99992 or higher is required.' - end - end - end - def config_appname(config) config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil end diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 80ba50d5b..db683ba84 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -2,22 +2,9 @@ module ActiveRecord module ConnectionAdapters class SQLServerColumn < Column - def initialize(name, default, cast_type, sql_type = nil, null = true, sqlserver_options = {}) - super(name, default, cast_type, sql_type, null) - @sqlserver_options = sqlserver_options.symbolize_keys - @default_function = @sqlserver_options[:default_function] - end - - def sql_type_for_statement - if is_integer? || is_real? - sql_type.sub(/\((\d+)?\)/, '') - else - sql_type - end - end - - def table_name - @sqlserver_options[:table_name] + def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + @sqlserver_options = sqlserver_options || {} + super(name, default, sql_type_metadata, null, table_name, default_function, collation, comment: comment) end def is_identity? @@ -29,19 +16,7 @@ def is_primary? end def is_utf8? - @sql_type =~ /nvarchar|ntext|nchar/i - end - - def is_integer? - @sql_type =~ /int/i - end - - def is_real? - @sql_type =~ /real/i - end - - def collation - @sqlserver_options[:collation] + sql_type =~ /nvarchar|ntext|nchar/i end def case_sensitive? diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 8786b5f88..4601b41d1 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -7,14 +7,10 @@ def sqlserver_connection(config) #:nodoc: case mode when :dblib require 'tiny_tds' - when :odbc - raise ArgumentError, 'Missing :dsn configuration.' unless config.key?(:dsn) - require 'odbc' - require 'active_record/connection_adapters/sqlserver/core_ext/odbc' else raise ArgumentError, "Unknown connection mode in #{config.inspect}." end - ConnectionAdapters::SQLServerAdapter.new(nil, logger, nil, config.merge(mode: mode)) + ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode)) end end end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 19705d18d..a88b34d40 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -77,7 +77,7 @@ def visit_Arel_Table o, collector # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 - table_name = if o.engine.connection.respond_to?(:sqlserver?) && o.engine.connection.database_prefix_remote_server? + table_name = if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? remote_server_table_name(o) else quote_table_name(o.name) @@ -190,25 +190,16 @@ def table_From_Statement o def primary_Key_From_Table t return unless t - return t.primary_key if t.primary_key - if engine_pk = t.engine.primary_key - pk = t.engine.arel_table[engine_pk] - return pk if pk - end - pk = t.engine.connection.schema_cache.primary_keys(t.engine.table_name) - return pk if pk - column_name = t.engine.columns.first.try(:name) + column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.second.try(:name) column_name ? t[column_name] : nil end def remote_server_table_name o ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( - "#{o.engine.connection.database_prefix}#{o.name}" + "#{o.class.engine.connection.database_prefix}#{o.name}" ).quoted end end end end - -Arel::Visitors::VISITORS['sqlserver'] = Arel::Visitors::SQLServer diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index e34669474..f19dfee51 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -17,7 +17,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase string = connection.inspect string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} string.must_match %r{version\: \d.\d} - string.must_match %r{mode: (dblib|odbc)} + string.must_match %r{mode: dblib} string.must_match %r{azure: (true|false)} string.wont_match %r{host} string.wont_match %r{password} @@ -96,7 +96,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase connection.send :initialize_dateformatter assert_nothing_raised do starting = Time.utc(2000, 1, 31, 5, 42, 0) - ending = Date.new(2006, 12, 31) + ending = Time.new(2006, 12, 31) Task.create! starting: starting, ending: ending end end @@ -158,14 +158,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - it 'find identity column using #identity_column' do + it 'find identity column using #identity_columns' do task_id_column = Task.columns_hash['id'] - assert_equal task_id_column.name, connection.send(:identity_column, Task.table_name).name - assert_equal task_id_column.sql_type, connection.send(:identity_column, Task.table_name).sql_type + assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name + assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type end - it 'return nil when calling #identity_column for a table_name with no identity' do - assert_nil connection.send(:identity_column, Subscriber.table_name) + it 'return an empty array when calling #identity_columns for a table_name with no identity' do + connection.send(:identity_columns, Subscriber.table_name).must_equal [] end end @@ -364,8 +364,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 0, SSTestCustomersView.new.balance end - it 'respond true to table_exists?' do - assert SSTestCustomersView.table_exists? + it 'respond true to data_source_exists?' do + assert SSTestCustomersView.connection.data_source_exists?(SSTestCustomersView.table_name) end # With aliased column names @@ -392,8 +392,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase SSTestStringDefaultsView.columns_hash['pretend_null'].inspect end - it 'respond true to table_exists?' do - assert SSTestStringDefaultsView.table_exists? + it 'respond true to data_source_exists?' do + assert SSTestStringDefaultsView.connection.data_source_exists?(SSTestStringDefaultsView.table_name) end # Doing identity inserts diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9c38b7896..02787e65e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -103,8 +103,7 @@ module ActiveRecord class BindParameterTest < ActiveRecord::TestCase # Never finds `sql` since we use `EXEC sp_executesql` wrappers. - coerce_tests! :test_binds_are_logged, - :test_binds_are_logged_after_type_cast + coerce_tests! :test_binds_are_logged end end @@ -404,19 +403,6 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced -class BigNumber < ActiveRecord::Base - attribute :value_of_e, Type::SQLServer::Integer.new - attribute :my_house_population, Type::SQLServer::Integer.new -end -class MigrationTest < ActiveRecord::TestCase - - # PENDING: [Rails5.x] Remove coerced tests and use simple symbol types. - coerce_tests! :test_add_table_with_decimals - -end - - - class NamedScopingTest < ActiveRecord::TestCase @@ -671,22 +657,6 @@ class TransactionIsolationTest < ActiveRecord::TestCase end -require 'models/post' -module ActiveRecord - class WhereChainTest < ActiveRecord::TestCase - - coerce_tests! :test_not_eq_with_array_parameter - def test_not_eq_with_array_parameter_coerced - expected = Arel::Nodes::Not.new("title = N'hello'") - relation = Post.where.not(['title = ?', 'hello']) - assert_equal([expected], relation.where_values) - end - - end -end - - - class ViewWithPrimaryKeyTest < ActiveRecord::TestCase diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 21f5dd871..954a9707b 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -36,14 +36,13 @@ def assert_obj_set_and_save(attribute, value) it 'bigint(8)' do col = column('bigint') col.sql_type.must_equal 'bigint(8)' + col.type.must_equal :bigint col.null.must_equal true col.default.must_equal 42 obj.bigint.must_equal 42 col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::BigInteger - type.type.must_equal :bigint - type.must_be :number? 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 @@ -52,14 +51,13 @@ def assert_obj_set_and_save(attribute, value) 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Integer - type.type.must_equal :integer - type.must_be :number? 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 @@ -68,14 +66,13 @@ def assert_obj_set_and_save(attribute, value) 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallInteger - type.type.must_equal :integer - type.must_be :number? type.limit.must_equal 2 assert_obj_set_and_save :smallint, -32_768 assert_obj_set_and_save :smallint, 32_767 @@ -84,14 +81,13 @@ def assert_obj_set_and_save(attribute, value) 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::TinyInteger - type.type.must_equal :integer - type.must_be :number? type.limit.must_equal 1 assert_obj_set_and_save :tinyint, 0 assert_obj_set_and_save :tinyint, 255 @@ -100,14 +96,13 @@ def assert_obj_set_and_save(attribute, value) 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Boolean - type.type.must_equal :boolean - type.wont_be :number? type.limit.must_equal nil obj.bit = 0 obj.bit.must_equal false @@ -122,14 +117,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.type.must_equal :decimal - type.must_be :number? type.limit.must_equal nil type.precision.must_equal 9 type.scale.must_equal 2 @@ -145,7 +139,7 @@ def assert_obj_set_and_save(attribute, value) col.default.must_equal BigDecimal('1234567.89') obj.decimal_16_4.must_equal BigDecimal('1234567.89') col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.precision.must_equal 16 type.scale.must_equal 4 obj.decimal_16_4 = '1234567.8901001' @@ -157,14 +151,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.type.must_equal :decimal - type.must_be :number? type.limit.must_equal nil type.precision.must_equal 18 type.scale.must_equal 0 @@ -177,14 +170,13 @@ def assert_obj_set_and_save(attribute, value) 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.type.must_equal :decimal - type.must_be :number? type.limit.must_equal nil type.precision.must_equal 36 type.scale.must_equal 2 @@ -197,14 +189,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Money - type.type.must_equal :money - type.must_be :number? type.limit.must_equal nil type.precision.must_equal 19 type.scale.must_equal 4 @@ -217,14 +208,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallMoney - type.type.must_equal :smallmoney - type.must_be :number? type.limit.must_equal nil type.precision.must_equal 10 type.scale.must_equal 4 @@ -241,14 +231,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Float - type.type.must_equal :float - type.must_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil @@ -261,14 +250,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Real - type.type.must_equal :real - type.must_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil @@ -283,14 +271,13 @@ 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 connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' obj.date.must_equal Date.civil(0001, 1, 1) col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Date - type.type.must_equal :date - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil @@ -298,7 +285,9 @@ def assert_obj_set_and_save(attribute, value) obj.date = '0001-01-01' obj.date.must_equal Date.civil(0001, 1, 1) obj.save! - obj.reload.date.must_equal Date.civil(0001, 1, 1) + obj.date.must_equal Date.civil(0001, 1, 1) + obj.reload + obj.date.must_equal Date.civil(0001, 1, 1) # Can keep and return assigned date. assert_obj_set_and_save :date, Date.civil(1972, 04, 14) # Can accept and cast time objects. @@ -311,14 +300,13 @@ def assert_obj_set_and_save(attribute, value) it 'datetime' do col = column('datetime') col.sql_type.must_equal 'datetime' + col.type.must_equal :datetime col.null.must_equal true col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>" obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>" col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime - type.type.must_equal :datetime - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil @@ -326,7 +314,9 @@ def assert_obj_set_and_save(attribute, value) obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000) obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.reload + obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" # Will cast to true DB value on attribute write, save and return again. obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 234567) obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" @@ -338,14 +328,13 @@ def assert_obj_set_and_save(attribute, value) skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? col = column('datetime2_7') col.sql_type.must_equal 'datetime2(7)' + col.type.must_equal :datetime2 col.null.must_equal true col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>" obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime2 - type.type.must_equal :datetime2 - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal 7 type.scale.must_equal nil @@ -353,7 +342,9 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000)) obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! - obj.reload.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.reload + obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" # Can save small fraction nanosecond precisoins and return again. obj.datetime2_7 = Time.utc(2008, 6, 21, 13, 30, 0, Rational(15020, 1000)) obj.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" @@ -362,19 +353,19 @@ def assert_obj_set_and_save(attribute, value) # With other precisions. time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column('datetime2_3') - col.cast_type.precision.must_equal 3 + connection.lookup_cast_type_from_column(col).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.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" col = column('datetime2_1') - col.cast_type.precision.must_equal 1 + connection.lookup_cast_type_from_column(col).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.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" col = column('datetime2_0') - col.cast_type.precision.must_equal 0 + connection.lookup_cast_type_from_column(col).precision.must_equal 0 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>" @@ -386,26 +377,27 @@ def assert_obj_set_and_save(attribute, value) skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? 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, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTimeOffset - type.type.must_equal :datetimeoffset - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal 7 type.scale.must_equal nil # Can save 100 nanosecond precisoins and return again. obj.datetimeoffset_7 = Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456755) obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" - obj.save! ; obj.reload + obj.save! + obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.reload obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # With other precisions. time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column('datetimeoffset_3') - col.cast_type.precision.must_equal 3 + connection.lookup_cast_type_from_column(col).precision.must_equal 3 obj.datetimeoffset_3 = time obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" # TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44 @@ -413,7 +405,7 @@ def assert_obj_set_and_save(attribute, value) obj.save! ; obj.reload obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column('datetime2_1') - col.cast_type.precision.must_equal 1 + connection.lookup_cast_type_from_column(col).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 @@ -423,35 +415,35 @@ 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, 01, 01, 15, 45, 00, 000) obj.smalldatetime.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallDateTime - type.type.must_equal :smalldatetime - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil # Will remove fractional seconds and return again. obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) - obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" + obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" obj.save! - obj.reload.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + obj.reload + obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end it 'time(7)' do skip 'time() not supported in this protocal version' unless connection_dblib_73? 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, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Time - type.type.must_equal :time - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal 7 type.scale.must_equal nil @@ -478,13 +470,12 @@ def assert_obj_set_and_save(attribute, value) skip 'time() not supported in this protocal version' unless connection_dblib_73? col = column('time_2') col.sql_type.must_equal 'time(2)' + col.type.must_equal :time col.null.must_equal true col.default.must_equal nil col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Time - type.type.must_equal :time - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal 2 type.scale.must_equal nil @@ -510,14 +501,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Char - type.type.must_equal :char - type.wont_be :number? type.limit.must_equal 10 type.precision.must_equal nil type.scale.must_equal nil @@ -531,14 +521,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Varchar - type.type.must_equal :varchar - type.wont_be :number? type.limit.must_equal 50 type.precision.must_equal nil type.scale.must_equal nil @@ -549,14 +538,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::VarcharMax - type.type.must_equal :varchar_max - type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil type.scale.must_equal nil @@ -567,14 +555,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Text - type.type.must_equal :text_basic - type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil type.scale.must_equal nil @@ -587,14 +574,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeChar - type.type.must_equal :nchar - type.wont_be :number? type.limit.must_equal 10 type.precision.must_equal nil type.scale.must_equal nil @@ -608,14 +594,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeVarchar - type.type.must_equal :string - type.wont_be :number? type.limit.must_equal 50 type.precision.must_equal nil type.scale.must_equal nil @@ -626,14 +611,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeVarcharMax - type.type.must_equal :text - type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil type.scale.must_equal nil @@ -644,14 +628,13 @@ 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.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeText - type.type.must_equal :ntext - type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil type.scale.must_equal nil @@ -667,13 +650,12 @@ def assert_obj_set_and_save(attribute, value) 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.default.must_equal nil col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Binary - type.type.must_equal :binary_basic - type.wont_be :number? type.limit.must_equal 49 type.precision.must_equal nil type.scale.must_equal nil @@ -689,13 +671,12 @@ 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.default.must_equal nil col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Varbinary - type.type.must_equal :varbinary - type.wont_be :number? type.limit.must_equal 49 type.precision.must_equal nil type.scale.must_equal nil @@ -711,13 +692,12 @@ 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.default.must_equal nil col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::VarbinaryMax - type.type.must_equal :binary - type.wont_be :number? type.limit.must_equal 2_147_483_647 type.precision.must_equal nil type.scale.must_equal nil @@ -731,13 +711,12 @@ 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.default.must_equal nil col.default_function.must_equal 'newid()' - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Uuid - type.type.must_equal :uuid - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil @@ -755,13 +734,12 @@ def assert_obj_set_and_save(attribute, value) it 'timestamp' do col = column('timestamp') col.sql_type.must_equal 'timestamp' + col.type.must_equal :ss_timestamp col.null.must_equal true col.default.must_equal nil col.default_function.must_equal nil - type = col.cast_type + type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Timestamp - type.type.must_equal :ss_timestamp - type.wont_be :number? type.limit.must_equal nil type.precision.must_equal nil type.scale.must_equal nil diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index d318ce80a..9a36120af 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -4,7 +4,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase - self.use_transactional_fixtures = false + self.use_transactional_tests = false fixtures :topics, :accounts @@ -31,61 +31,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase end end unless connection_sqlserver_azure? - describe 'ODBC connection management' do - - it 'return finished ODBC statement handle from #execute without block' do - assert_all_odbc_statements_used_are_closed do - connection.execute('SELECT * FROM [topics]') - end - end - - it 'finish ODBC statement handle from #execute with block' do - assert_all_odbc_statements_used_are_closed do - connection.execute('SELECT * FROM [topics]') { } - end - end - - it 'finish connection from #raw_select' do - assert_all_odbc_statements_used_are_closed do - connection.send(:raw_select,'SELECT * FROM [topics]') - end - end - - it 'execute without block closes statement' do - assert_all_odbc_statements_used_are_closed do - connection.execute("SELECT 1") - end - end - - it 'execute with block closes statement' do - assert_all_odbc_statements_used_are_closed do - connection.execute("SELECT 1") do |sth| - assert !sth.finished?, "Statement should still be alive within block" - end - end - end - - it 'insert with identity closes statement' do - assert_all_odbc_statements_used_are_closed do - connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", [] - end - end - - it 'insert without identity closes statement' do - assert_all_odbc_statements_used_are_closed do - connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", [] - end - end - - it 'active closes statement' do - assert_all_odbc_statements_used_are_closed do - connection.active? - end - end - - end if connection_odbc? - - describe 'Connection management' do it 'set spid on connect' do @@ -118,25 +63,7 @@ def disconnect_raw_connection! case connection_options[:mode] when :dblib connection.raw_connection.close rescue nil - when :odbc - connection.raw_connection.disconnect rescue nil end end - def assert_all_odbc_statements_used_are_closed(&block) - odbc = connection.raw_connection.class.parent - existing_handles = [] - ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h } - existing_handle_ids = existing_handles.map(&:object_id) - assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed" - GC.disable - yield - used_handles = [] - ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) } - assert used_handles.size > 0, "No statements were used within given block" - assert used_handles.all?(&:finished?), "Statement should have been closed within given block" - ensure - GC.enable - end - end diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index 02bcbb7d4..a454d4baf 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -37,7 +37,7 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase it 'should not use fully qualified table name in where clause' do table = Arel::Table.new(:table) expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" - assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql + quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql } end it 'should not use fully qualified table name in order clause' do @@ -47,28 +47,28 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase end it 'should use fully qualified table name in insert statement' do - manager = Arel::InsertManager.new(Arel::Table.engine) + manager = Arel::InsertManager.new manager.into Arel::Table.new(:table) manager.values = manager.create_values [Arel.sql('*')], %w{ a } expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" - assert_equal expected_sql, manager.to_sql + quietly { assert_equal expected_sql, manager.to_sql } end it 'should use fully qualified table name in update statement' do table = Arel::Table.new(:table) - manager = Arel::UpdateManager.new(Arel::Table.engine) + manager = Arel::UpdateManager.new manager.table(table).where(table[:id].eq(42)) manager.set([[table[:name], "Bob"]]) expected_sql = "UPDATE [my.server].[db].[schema].[table] SET [name] = N'Bob' WHERE [table].[id] = 42" - assert_equal expected_sql, manager.to_sql + quietly { assert_equal expected_sql, manager.to_sql } end it 'should use fully qualified table name in delete statement' do table = Arel::Table.new(:table) - manager = Arel::DeleteManager.new(Arel::Table.engine) + manager = Arel::DeleteManager.new manager.from(table).where(table[:id].eq(42)) expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" - assert_equal expected_sql, manager.to_sql + quietly { assert_equal expected_sql, manager.to_sql } end end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 011abe3de..042bba2e2 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -15,7 +15,8 @@ class TestCase < ActiveSupport::TestCase SQLServer = ActiveRecord::ConnectionAdapters::SQLServer include ARTest::SQLServer::CoerceableTest, - ARTest::SQLServer::ConnectionReflection + ARTest::SQLServer::ConnectionReflection, + ActiveSupport::Testing::Stream let(:logger) { ActiveRecord::Base.logger } @@ -34,20 +35,6 @@ def with_use_output_inserted_disabled klass.use_output_inserted = true end - def silence_stream(stream) - old_stream = stream.dup - stream.reopen(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ? 'NUL:' : '/dev/null') - stream.sync = true - yield - ensure - stream.reopen(old_stream) - old_stream.close - end - - def quietly - silence_stream(STDOUT) { silence_stream(STDERR) { yield } } - end - end end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 0513b7689..1a4b8940e 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -73,7 +73,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it 'copes with eager loading un-locked paginated' do - eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET 10 ROWS FETCH NEXT 5 ROWS ONLY/ + eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ assert_sql(eager_ids_sql, loader_sql) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 3845e7148..09b2bf664 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -2,7 +2,7 @@ class SQLServerRakeTest < ActiveRecord::TestCase - self.use_transactional_fixtures = false + self.use_transactional_tests = false cattr_accessor :azure_skip self.azure_skip = connection_sqlserver_azure? @@ -42,24 +42,24 @@ class SQLServerRakeCreateTest < SQLServerRakeTest self.azure_skip = false it 'establishes connection to database after create ' do - db_tasks.create configuration + quietly { db_tasks.create configuration } connection.current_database.must_equal(new_database) end it 'creates database with default collation' do - db_tasks.create configuration + quietly { db_tasks.create configuration } connection.collation.must_equal 'SQL_Latin1_General_CP1_CI_AS' end it 'creates database with given collation' do - db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') + quietly { db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') } connection.collation.must_equal 'Latin1_General_CI_AS' end it 'prints error message when database exists' do - db_tasks.create configuration + quietly { db_tasks.create configuration } message = capture(:stderr) { db_tasks.create configuration } - message.must_match %r{activerecord_unittest_tasks already exists} + message.must_match %r{activerecord_unittest_tasks.*already exists} end end @@ -69,8 +69,10 @@ class SQLServerRakeDropTest < SQLServerRakeTest self.azure_skip = false it 'drops database and uses master' do - db_tasks.create configuration - db_tasks.drop configuration + quietly do + db_tasks.create configuration + db_tasks.drop configuration + end connection.current_database.must_equal 'master' end @@ -84,7 +86,7 @@ class SQLServerRakeDropTest < SQLServerRakeTest class SQLServerRakePurgeTest < SQLServerRakeTest before do - db_tasks.create(configuration) + quietly { db_tasks.create(configuration) } connection.create_table :users, force: true do |t| t.string :name, :email t.timestamps null: false @@ -94,7 +96,7 @@ class SQLServerRakePurgeTest < SQLServerRakeTest it 'clears active connections, drops database, and recreates with established connection' do connection.current_database.must_equal(new_database) connection.tables.must_include 'users' - db_tasks.purge(configuration) + quietly { db_tasks.purge(configuration) } connection.current_database.must_equal(new_database) connection.tables.wont_include 'users' end @@ -103,7 +105,9 @@ class SQLServerRakePurgeTest < SQLServerRakeTest class SQLServerRakeCharsetTest < SQLServerRakeTest - before { db_tasks.create(configuration) } + before do + quietly { db_tasks.create(configuration) } + end it 'retrieves charset' do db_tasks.charset(configuration).must_equal 'iso_1' @@ -113,7 +117,9 @@ class SQLServerRakeCharsetTest < SQLServerRakeTest class SQLServerRakeCollationTest < SQLServerRakeTest - before { db_tasks.create(configuration) } + before do + quietly { db_tasks.create(configuration) } + end it 'retrieves collation' do db_tasks.collation(configuration).must_equal 'SQL_Latin1_General_CP1_CI_AS' @@ -127,7 +133,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest let(:filedata) { File.read(filename) } before do - db_tasks.create(configuration) + quietly { db_tasks.create(configuration) } connection.create_table :users, force: true do |t| t.string :name, :email t.text :background1 @@ -156,7 +162,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest filedata.must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) connection.tables.wont_include 'users' - db_tasks.load_schema_for configuration, :sql, filename + db_tasks.load_schema configuration, :sql, filename connection.tables.must_include 'users' end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 7845915a4..7b84d8ebb 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -10,48 +10,48 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'sst_datatypes' do generate_schema_for_table 'sst_datatypes' # Exact Numerics - assert_line :bigint, type: 'bigint', limit: '8', precision: nil, scale: nil, default: '42' - assert_line :int, type: 'integer', limit: '4', precision: nil, scale: nil, default: '42' - assert_line :smallint, type: 'integer', limit: '2', precision: nil, scale: nil, default: '42' - assert_line :tinyint, type: 'integer', limit: '1', precision: nil, scale: nil, default: '42' - assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: 'true' - assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: '9', scale: '2', default: '12345.01' - assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: '18', scale: '0', default: '191.0' - assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: '36', scale: '2', default: '12345678901234567890.01' - assert_line :money, type: 'money', limit: nil, precision: '19', scale: '4', default: '4.2' - assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: '4.2' + assert_line :bigint, type: 'bigint', limit: nil, precision: nil, scale: nil, default: 42 + assert_line :int, type: 'integer', limit: nil, precision: nil, scale: nil, default: 42 + assert_line :smallint, type: 'integer', limit: 2, precision: nil, scale: nil, default: 42 + assert_line :tinyint, type: 'integer', limit: 1, precision: nil, scale: nil, default: 42 + assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: true + assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: 9, scale: 2, default: 12345.01 + assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: 18, scale: 0, default: 191.0 + assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01 + assert_line :money, type: 'money', limit: nil, precision: 19, scale: 4, default: 4.2 + assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: 4.2 # Approximate Numerics - assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: '123.00000001' - assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: %r{123.4[45]} + assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: 123.00000001 + assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: 123.45 # Date and Time - assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "\"01-01-0001\"" - assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "\"01-01-1753 00:00:00.123\"" + assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "01-01-0001" + assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? - assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: "\"12-31-9999 23:59:59.9999999\"" - assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: '3', scale: nil, default: nil - assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: '1', scale: nil, default: nil + assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" + assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: 3, scale: nil, default: nil + assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: 1, scale: nil, default: nil end - assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "\"01-01-1901 15:45:00\"" + assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00" if connection_dblib_73? - assert_line :time_7, type: 'time', limit: nil, precision: '7', scale: nil, default: "\"04:20:00.2883215\"" - assert_line :time_2, type: 'time', limit: nil, precision: '2', scale: nil, default: nil + assert_line :time_7, type: 'time', limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" + assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil end # Character Strings - assert_line :char_10, type: 'char', limit: '10', precision: nil, scale: nil, default: "\"1234567890\"" - assert_line :varchar_50, type: 'varchar', limit: '50', precision: nil, scale: nil, default: "\"test varchar_50\"" - assert_line :varchar_max, type: 'varchar_max', limit: '2147483647', precision: nil, scale: nil, default: "\"test varchar_max\"" - assert_line :text, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: "\"test text\"" + assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :varchar_max, type: 'varchar_max', limit: 2147483647, precision: nil, scale: nil, default: "test varchar_max", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :text, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: "test text", collation: "SQL_Latin1_General_CP1_CI_AS" # Unicode Character Strings - assert_line :nchar_10, type: 'nchar', limit: '10', precision: nil, scale: nil, default: "\"12345678åå\"" - assert_line :nvarchar_50, type: 'string', limit: '50', precision: nil, scale: nil, default: "\"test nvarchar_50 åå\"" - assert_line :nvarchar_max, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: "\"test nvarchar_max åå\"" - assert_line :ntext, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: "\"test ntext åå\"" + assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :nvarchar_max, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :ntext, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: "test ntext åå", collation: "SQL_Latin1_General_CP1_CI_AS" # Binary Strings - assert_line :binary_49, type: 'binary_basic', limit: '49', precision: nil, scale: nil, default: nil - assert_line :varbinary_49, type: 'varbinary', limit: '49', precision: nil, scale: nil, default: nil - assert_line :varbinary_max, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil + assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil + assert_line :varbinary_max, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil # Other Data Types - assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil + assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: -> { "newid()" } assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end @@ -71,18 +71,18 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase 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', limit: '4', precision: nil, scale: nil, default: nil - assert_line :bigint_col, type: 'bigint', limit: '8', precision: nil, scale: nil, default: nil + assert_line :integer_col, type: 'integer', limit: nil, precision: nil, scale: nil, default: nil + assert_line :bigint_col, type: 'bigint', limit: nil, precision: nil, scale: nil, default: nil assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil - assert_line :decimal_col, type: 'decimal', limit: nil, precision: '18', scale: '0', default: nil + assert_line :decimal_col, type: 'decimal', limit: nil, precision: 18, scale: 0, default: nil assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil - assert_line :string_col, type: 'string', limit: '4000', precision: nil, scale: nil, default: nil - assert_line :text_col, type: 'text', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :string_col, type: 'string', limit: nil, precision: nil, scale: nil, default: nil + assert_line :text_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: '7', scale: nil, default: nil + assert_line :time_col, type: 'time', limit: nil, precision: 7, scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_col, type: 'binary', limit: '2147483647', precision: nil, scale: nil, default: nil + assert_line :binary_col, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil # Our type methods. columns['real_col'].sql_type.must_equal 'real' columns['money_col'].sql_type.must_equal 'money' @@ -99,16 +99,16 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['uuid_col'].sql_type.must_equal 'uniqueidentifier' columns['sstimestamp_col'].sql_type.must_equal 'timestamp' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil - assert_line :money_col, type: 'money', limit: nil, precision: '19', scale: '4', default: nil - assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: '7', scale: nil, default: nil - assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: '10', scale: '4', default: nil - assert_line :char_col, type: 'char', limit: '1', precision: nil, scale: nil, default: nil - assert_line :varchar_col, type: 'varchar', limit: '8000', precision: nil, scale: nil, default: nil - assert_line :text_basic_col, type: 'text_basic', limit: '2147483647', precision: nil, scale: nil, default: nil - assert_line :nchar_col, type: 'nchar', limit: '1', precision: nil, scale: nil, default: nil - assert_line :ntext_col, type: 'ntext', limit: '2147483647', precision: nil, scale: nil, default: nil - assert_line :binary_basic_col, type: 'binary_basic', limit: '1', precision: nil, scale: nil, default: nil - assert_line :varbinary_col, type: 'varbinary', limit: '8000', precision: nil, scale: nil, default: nil + assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil + assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: 7, scale: nil, default: nil + assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil + assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil + assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil + assert_line :text_basic_col, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil + assert_line :ntext_col, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil + assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end @@ -126,7 +126,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'no id with model driven primary key' do output = generate_schema_for_table 'sst_no_pk_data' output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} - assert_line :name, type: 'string', limit: '4000' + assert_line :name, type: 'string', limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CI_AS" end @@ -162,8 +162,12 @@ def assert_line(column_name, options={}) message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" if expected.nil? actual.must_be_nil message - elsif expected.is_a?(Regexp) - actual.must_match expected, message + elsif expected.is_a?(Array) + actual.must_include expected, message + elsif expected.is_a?(Float) + actual.must_be_close_to expected, 0.001 + elsif expected.is_a?(Proc) + actual.call.must_equal(expected.call) else actual.must_equal expected, message end @@ -172,24 +176,42 @@ def assert_line(column_name, options={}) class SchemaLine - attr_reader :line + LINE_PARSER = %r{t\.(\w+)\s+"(.*?)"[,\s+](.*)} - def self.match(method_name, pattern) - define_method(method_name) { line.match(pattern).try :[], 1 } + attr_reader :line, + :type_method, + :col_name, + :options + + def self.option(method_name) + define_method(method_name) { options.present? ? options[method_name.to_sym] : nil } end def initialize(line) @line = line + @type_method, @col_name, @options = parse_line end - match :type_method, %r{\A\s+t\.(.*?)\s} - match :limit, %r{\slimit:\s(.*?)[,\s]} - match :default, %r{\sdefault:\s(.*)\n} - match :precision, %r{\sprecision:\s(.*?)[,\s]} - match :scale, %r{\sscale:\s(.*?)[,\s]} + option :limit + option :precision + option :scale + option :default + option :collation def to_s - line + line.squish + end + + def inspect + "#" + end + + private + + def parse_line + _all, type_method, col_name, options = @line.match(LINE_PARSER).to_a + options = options.present? ? eval("{#{options}}") : {} + [type_method, col_name, options] end end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 9f1840b0c..a2887ca2e 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -13,8 +13,8 @@ class SchemaTestSQLServer < ActiveRecord::TestCase describe 'When table is in non-dbo schema' do it 'work with table exists' do - assert connection.table_exists?('test.sst_schema_natural_id') - assert connection.table_exists?('[test].[sst_schema_natural_id]') + assert connection.data_source_exists?('test.sst_schema_natural_id') + assert connection.data_source_exists?('[test].[sst_schema_natural_id]') end it 'find primary key for tables with odd schema' do diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 17ad877b3..a140f8e32 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -19,7 +19,7 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end - it 'from prepared statement ...' do + it 'from prepared statement' do plan = Car.where(name: ',').limit(1).explain plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" plan.must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 1216e167d..7af232cba 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -94,7 +94,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase end it 'use primary key for row table order in pagination sql' do - sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY/ + sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load } end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 02e1a88b3..6db739bfb 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -5,7 +5,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase - self.use_transactional_fixtures = false + self.use_transactional_tests = false before { delete_ships } diff --git a/test/config.yml b/test/config.yml index 2595dcc76..fb507593a 100644 --- a/test/config.yml +++ b/test/config.yml @@ -30,12 +30,3 @@ connections: azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : nil %> - odbc: - arunit: - <<: *default_connection_info - dsn: <%= ENV['ACTIVERECORD_UNITTEST_DSN'] || 'activerecord_unittest' %> - arunit2: - <<: *default_connection_info - database: activerecord_unittest2 - dsn: <%= ENV['ACTIVERECORD_UNITTEST2_DSN'] || 'activerecord_unittest2' %> - diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index 45d0e21fc..ffc29d634 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -2,7 +2,7 @@ class TableWillNeverBeCreated < ActiveRecord::Migration def self.up create_table(:sqlserver_trans_table1) { } - create_table(:sqlserver_trans_table2) { raise ActiveRecord::StatementInvalid } + create_table(:sqlserver_trans_table2) { raise('HELL') } end def self.down diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index dc6171ca1..346321539 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -34,7 +34,11 @@ def coerced_test_warning(method) method = instance_methods(false).select { |m| m =~ method } if method.is_a?(Regexp) Array(method).each do |m| result = undef_method(m) if m && method_defined?(m) - STDOUT.puts "Info: Undefined coerced test: #{self.name}##{m}" unless result.blank? + if result.blank? + STDOUT.puts "Warning: Unfound coerced test: #{self.name}##{m}" + else + STDOUT.puts "Info: Undefined coerced test: #{self.name}##{m}" + end end end diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index 1c2e08e5c..20ef1fd8a 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -24,10 +24,6 @@ def connection_dblib_73? rc.respond_to?(:tds_73?) && rc.tds_73? end - def connection_odbc? - connection_options[:mode] == :odbc - end - def connection_sqlserver_azure? connection.sqlserver_azure? end From b09f057edede00289e6053a8f382a3b6f6e6729f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 24 Nov 2016 08:20:01 -0500 Subject: [PATCH 0521/1412] Add Rails v4 Command Examples --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index a195bbf3f..c36bd4f22 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,15 @@ The work for Rails v5 started a on 2016-07-03 and none of the work landed in mas * **Why is it taking so long?** - I spent the last several months trying to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievement by that team. I thought it was more important to get the low level connection strong before doing the adapter work. We will get there soon. * **What branch you working on?** - Right now I am on the [rails5](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/rails5) branch. +#### Using Rails v4 + +Use these commands to use the latest of Rails v4 till our v5 release is ready. + +```shell +$ gem install rails -v 4.2.7.1 +$ rails _4.2.7.1_ new MYAPP --database=sqlserver +``` + ![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) ## Code Name Kantishna From e356aa512a565e5527c676c08e89dcbbb54e5f80 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 18 Oct 2016 19:22:57 -0400 Subject: [PATCH 0522/1412] [Util] More quoting utils. --- .../connection_adapters/sqlserver/quoting.rb | 8 ++++++++ .../connection_adapters/sqlserver/utils.rb | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index c4c4da941..0e8a334bb 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -23,6 +23,14 @@ def quote_string(s) SQLServer::Utils.quote_string(s) end + def quote_string_single(s) + SQLServer::Utils.quote_string_single(s) + end + + def quote_string_single_national(s) + SQLServer::Utils.quote_string_single_national(s) + end + def quote_column_name(name) SQLServer::Utils.extract_identifiers(name).quoted end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index b70ed1038..e6eedc94e 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -5,6 +5,8 @@ module ConnectionAdapters module SQLServer module Utils + QUOTED_STRING_PREFIX = 'N' + # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils. # @@ -118,6 +120,14 @@ def quote_string(s) s.to_s.gsub /\'/, "''" end + def quote_string_single(s) + "'#{quote_string(s)}'" + end + + def quote_string_single_national(s) + "#{QUOTED_STRING_PREFIX}'#{quote_string(s)}'" + end + def quoted_raw(name) SQLServer::Utils::Name.new(name).quoted_raw end From 86a29bfe3943cba4c37c2adf4dfd1fb98f128db1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 8 Dec 2016 21:51:14 -0500 Subject: [PATCH 0523/1412] Changing a few tests to harden things up. --- test/cases/column_test_sqlserver.rb | 31 +++++++++++----------- test/cases/schema_dumper_test_sqlserver.rb | 13 +++++++-- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 954a9707b..91a42fc8a 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -282,12 +282,12 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal nil type.scale.must_equal nil # Can cast strings. - obj.date = '0001-01-01' - obj.date.must_equal Date.civil(0001, 1, 1) + obj.date = '0001-04-01' + obj.date.must_equal Date.civil(0001, 4, 1) obj.save! - obj.date.must_equal Date.civil(0001, 1, 1) + obj.date.must_equal Date.civil(0001, 4, 1) obj.reload - obj.date.must_equal Date.civil(0001, 1, 1) + obj.date.must_equal Date.civil(0001, 4, 1) # Can keep and return assigned date. assert_obj_set_and_save :date, Date.civil(1972, 04, 14) # Can accept and cast time objects. @@ -311,17 +311,17 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal nil type.scale.must_equal nil # Can save to proper accuracy and return again. - obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 3000) - obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" + obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 3000) + obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" obj.reload - obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" # Will cast to true DB value on attribute write, save and return again. - obj.datetime = Time.utc(2010, 01, 01, 12, 34, 56, 234567) - obj.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 234567) + obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.save! - obj.reload.datetime.must_equal Time.utc(2010, 01, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" + obj.reload.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" end it 'datetime2' do @@ -388,12 +388,12 @@ def assert_obj_set_and_save(attribute, value) type.precision.must_equal 7 type.scale.must_equal nil # Can save 100 nanosecond precisoins and return again. - obj.datetimeoffset_7 = Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456755) - obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755) + obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.save! - obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload - obj.datetimeoffset_7.must_equal Time.new(2010, 01, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # With other precisions. time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column('datetimeoffset_3') @@ -401,7 +401,6 @@ def assert_obj_set_and_save(attribute, value) obj.datetimeoffset_3 = time obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" # TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44 - return obj.save! ; obj.reload obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column('datetime2_1') diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 7b84d8ebb..985766bc2 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -9,7 +9,6 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'sst_datatypes' do generate_schema_for_table 'sst_datatypes' - # Exact Numerics assert_line :bigint, type: 'bigint', limit: nil, precision: nil, scale: nil, default: 42 assert_line :int, type: 'integer', limit: nil, precision: nil, scale: nil, default: 42 assert_line :smallint, type: 'integer', limit: 2, precision: nil, scale: nil, default: 42 @@ -210,10 +209,20 @@ def inspect def parse_line _all, type_method, col_name, options = @line.match(LINE_PARSER).to_a - options = options.present? ? eval("{#{options}}") : {} + options = parse_options(options) [type_method, col_name, options] end + def parse_options(opts) + if opts.present? + eval "{#{opts}}" + else + {} + end + rescue SyntaxError + {} + end + end end From 000b9c79797a8635a569ab28abfbbc1eca29d2cf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 8 Dec 2016 21:52:42 -0500 Subject: [PATCH 0524/1412] [Rails5] Ensure date/time columns are not nil on create/update. Take 2. Creating or updating records resets the attributes by mapping the hash using ActiveRecord's `Attribute#forgetting_assignment` method. This method essentiall mimics the reload by setting the attributes to FromDatabase using the FromUser value "for the database". https://github.com/rails/rails/commit/07723c23 This is a problem for us because the `value_for_database` for date time objects are strings that can not be re-parsed due to SQL Server's time formats. For example, a date of August 18th 2016 would be formatted as '08-19-2016' and fail deserialize: ``` ruby ActiveModel::Type::Date.new.deserialize('08-19-2016') # => nil ``` Both ActiveModel's date and time types rescue `Date.parse` methods of `new_date` and `new_time` with nil. It was suggested that we use Data classes for our time types and we may explore that after this commit which essentially prepends a new `forgetting_assignment` method that checks the type. If it is a SQL Server date/time, it will just convert that value object to a FromDatabase using the existing value. cc @sgrif --- .../connection_adapters/sqlserver/quoting.rb | 8 +++++ .../sqlserver/type/date.rb | 25 ++++++++++++-- .../sqlserver/type/datetime.rb | 34 ++++++++++++++++--- .../sqlserver/type/datetimeoffset.rb | 5 +++ .../sqlserver/type/smalldatetime.rb | 2 +- .../sqlserver/type/time.rb | 2 +- .../sqlserver/type/time_value_fractional.rb | 2 +- 7 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 0e8a334bb..f259736a3 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -77,6 +77,10 @@ def _quote(value) "0x#{value.hex}" when ActiveRecord::Type::SQLServer::Char::Data value.quoted + when ActiveRecord::Type::SQLServer::Date::Data, + ActiveRecord::Type::SQLServer::DateTime::Data, + ActiveRecord::Type::SQLServer::DateTime2::Data + value.quoted when String, ActiveSupport::Multibyte::Chars "#{QUOTED_STRING_PREFIX}#{super}" else @@ -94,6 +98,10 @@ def _type_cast(value) _quote(value) when ActiveRecord::Type::SQLServer::Char::Data value.quoted + when ActiveRecord::Type::SQLServer::Date::Data, + ActiveRecord::Type::SQLServer::DateTime::Data, + ActiveRecord::Type::SQLServer::DateTime2::Data + value.quoted else super 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 d283e60a8..9564c7027 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -10,12 +10,31 @@ def sqlserver_type def serialize(value) return unless value.present? - return value if value.acts_like?(:string) - value.to_s(:_sqlserver_dateformat) + return value if value.is_a?(Data) + Data.new(super) + end + + def deserialize(value) + return value.value if value.is_a?(Data) + super end def type_cast_for_schema(value) - serialize(value).inspect + serialize(value).quoted + end + + class Data + + attr_reader :value + + def initialize(value) + @value = value + end + + def quoted + Utils.quote_string_single @value.to_s(:_sqlserver_dateformat) + end + end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 4cc3485d3..f724e4832 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -12,16 +12,40 @@ def sqlserver_type def serialize(value) return super unless value.acts_like?(:time) - value = zone_conversion(value) + Data.new super, self + end + + def deserialize(value) + datetime = value.is_a?(Data) ? value.value : super + return unless datetime + zone_conversion(datetime) + end + + def type_cast_for_schema(value) + serialize(value).quoted + end + + def quoted(value) datetime = value.to_s(:_sqlserver_datetime) - "#{datetime}".tap do |v| + datetime = "#{datetime}".tap do |v| fraction = quote_fractional(value) v << ".#{fraction}" unless fraction.to_i.zero? end + Utils.quote_string_single(datetime) end - def type_cast_for_schema(value) - serialize(value).inspect + class Data + + attr_reader :value, :type + + def initialize(value, type) + @value, @type = value, type + end + + def quoted + type.quoted(@value) + end + end private @@ -29,7 +53,7 @@ def type_cast_for_schema(value) def cast_value(value) value = value.acts_like?(:time) ? value : super return unless value - cast_fractional(value) + apply_seconds_precision(value) end def zone_conversion(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 4798a3e94..a5fe3dd72 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -23,6 +23,11 @@ def sqlserver_type private + def fast_string_to_time(string) + dateformat = ::Time::DATE_FORMATS[:_sqlserver_dateformat] + ::Time.strptime string, "#{dateformat} %H:%M:%S.%N %:z" + end + def zone_conversion(value) value end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 6bf93d3f0..67070e077 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -14,7 +14,7 @@ def sqlserver_type private - def cast_fractional(value) + def apply_seconds_precision(value) value.change usec: 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 5af08949e..21810409a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -30,7 +30,7 @@ def cast_value(value) value = value.acts_like?(:time) ? value : super return if value.blank? value = value.change year: 2000, month: 01, day: 01 - cast_fractional(value) + apply_seconds_precision(value) end def fractional_scale 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 8171835df..d8a2ff1cd 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 @@ -7,7 +7,7 @@ module TimeValueFractional private - def cast_fractional(value) + def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? frac_seconds = if fractional_scale == 0 0 From cac85f530c2aae899f74849429b2bda910ee2d7f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 19 Dec 2016 21:57:03 -0500 Subject: [PATCH 0525/1412] Reduce Appveyor. Add CircleCI & TravisCI. Removed `-x64` appveyor build versions till TinyTDS v1.1 is out. --- .travis.yml | 29 +++++++++++++++++++++++++++++ Gemfile | 2 +- README.md | 6 +++++- appveyor.yml | 10 +++++----- circle.yml | 34 ++++++++++++++++++++++++++++++++++ test/bin/.keep | 0 test/bin/install-freetds.sh | 18 ++++++++++++++++++ test/bin/install-openssl.sh | 16 ++++++++++++++++ test/bin/setup.sh | 17 +++++++++++++++++ 9 files changed, 125 insertions(+), 7 deletions(-) create mode 100644 .travis.yml create mode 100644 circle.yml create mode 100644 test/bin/.keep create mode 100755 test/bin/install-freetds.sh create mode 100755 test/bin/install-openssl.sh create mode 100755 test/bin/setup.sh diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..a3e7e6e44 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,29 @@ +sudo: required +cache: bundler +services: + - docker +env: + global: + - TESTOPTS="-v" + - TINYTDS_VERSION=1.0.5 + - ACTIVERECORD_UNITTEST_HOST=localhost + - ACTIVERECORD_UNITTEST_DATASERVER=localhost + - ONLY_SQLSERVER=1 +rvm: + - 2.2.5 + - 2.3.1 +before_install: + - export PATH=/opt/local/bin:$PATH + - docker info + - sudo ./test/bin/setup.sh + - sudo ./test/bin/install-openssl.sh + - openssl version + - sudo ./test/bin/install-freetds.sh + - tsql -C +install: + - export PATH=/opt/local/bin:$PATH + - gem install bundler + - bundle --version + - bundle install +script: + - bundle exec rake diff --git a/Gemfile b/Gemfile index 02eab67df..8b1fdefc3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ source 'https://rubygems.org' gemspec -gem 'bcrypt' +gem 'bcrypt', platforms: [:mri] gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ diff --git a/README.md b/README.md index c36bd4f22..c85cadce5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,11 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. -[![Build status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg?style=flat)](https://rubygems.org/gems/activerecord-sqlserver-adapter) [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) +* [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI +* [![CircleCI](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master.svg?style=svg)](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master) - CircleCI +* [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor +* [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version +* [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community ## RAILS v5 COMING!!! diff --git a/appveyor.yml b/appveyor.yml index 91782c17f..919ad215f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,8 @@ init: - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - SET TESTOPTS='-v' - - SET TINYTDS_VERSION=1.0.4 + - SET TINYTDS_VERSION=1.0.5 + - SET ONLY_SQLSERVER=1 clone_depth: 5 skip_tags: true matrix: @@ -26,14 +27,13 @@ test_script: - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" - - timeout /t 4 /nobreak > NUL - - bundle exec rake test ACTIVERECORD_UNITTEST_HOST=%CI_AZURE_HOST% ACTIVERECORD_UNITTEST_PASS=%CI_AZURE_PASS% ACTIVERECORD_UNITTEST_AZURE=1 environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - - ruby_version: "200" + # - ruby_version: "23-x64" + - ruby_version: "23" + # - ruby_version: "22-x64" - ruby_version: "22" - - ruby_version: "22-x64" diff --git a/circle.yml b/circle.yml new file mode 100644 index 000000000..1c273c10b --- /dev/null +++ b/circle.yml @@ -0,0 +1,34 @@ +general: + branches: + ignore: + - /dev.*/ + +machine: + environment: + PATH: /opt/local/bin:${PATH} + TESTOPTS: -v + TINYTDS_VERSION: 1.0.5 + ACTIVERECORD_UNITTEST_HOST: localhost + ACTIVERECORD_UNITTEST_DATASERVER: localhost + ONLY_SQLSERVER: 1 + services: + - docker + +dependencies: + override: + - sudo ./test/bin/install-openssl.sh + - openssl version + - sudo ./test/bin/install-freetds.sh + - tsql -C + - rvm-exec 2.2.5 bundle install + - rvm-exec 2.3.1 bundle install + +database: + post: + - docker info + - ./test/bin/setup.sh + +test: + override: + - rvm-exec 2.2.5 bundle exec rake test + - rvm-exec 2.3.1 bundle exec rake test diff --git a/test/bin/.keep b/test/bin/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/bin/install-freetds.sh b/test/bin/install-freetds.sh new file mode 100755 index 000000000..16e9f66fe --- /dev/null +++ b/test/bin/install-freetds.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -x +set -e + +FREETDS_VERSION=1.00.21 + +wget ftp://ftp.freetds.org/pub/freetds/stable/freetds-$FREETDS_VERSION.tar.gz +tar -xzf freetds-$FREETDS_VERSION.tar.gz +cd freetds-$FREETDS_VERSION +./configure --prefix=/opt/local \ + --with-openssl=/opt/local \ + --with-tdsver=7.3 +make +make install +cd .. +rm -rf freetds-$FREETDS_VERSION +rm freetds-$FREETDS_VERSION.tar.gz diff --git a/test/bin/install-openssl.sh b/test/bin/install-openssl.sh new file mode 100755 index 000000000..9f9851a17 --- /dev/null +++ b/test/bin/install-openssl.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -x +set -e + +OPENSSL_VERSION=1.0.2j + +wget https://www.openssl.org/source/openssl-$OPENSSL_VERSION.tar.gz +tar -xzf openssl-$OPENSSL_VERSION.tar.gz +cd openssl-$OPENSSL_VERSION +./config --prefix=/opt/local +make +make install +cd .. +rm -rf openssl-$OPENSSL_VERSION +rm openssl-$OPENSSL_VERSION.tar.gz diff --git a/test/bin/setup.sh b/test/bin/setup.sh new file mode 100755 index 000000000..4ad03244c --- /dev/null +++ b/test/bin/setup.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +set -x +set -e + +docker pull metaskills/mssql-server-linux-rails + +container=$(docker ps -a -q --filter ancestor=metaskills/mssql-server-linux-rails) +if [[ -z $container ]]; then + docker run -p 1433:1433 -d metaskills/mssql-server-linux-rails && sleep 10 + exit +fi + +container=$(docker ps -q --filter ancestor=metaskills/mssql-server-linux-rails) +if [[ -z $container ]]; then + docker start $container && sleep 10 +fi From 2935d98c54152e792c98bd1116005a8a8a01de12 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Dec 2016 12:28:22 -0500 Subject: [PATCH 0526/1412] Use TinyTDS v1.1.0 (#532) --- .travis.yml | 2 +- appveyor.yml | 6 +++--- circle.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index a3e7e6e44..f85ee8fc5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ services: env: global: - TESTOPTS="-v" - - TINYTDS_VERSION=1.0.5 + - TINYTDS_VERSION=1.1.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost - ONLY_SQLSERVER=1 diff --git a/appveyor.yml b/appveyor.yml index 919ad215f..d75979e2f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,7 +3,7 @@ init: - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - SET TESTOPTS='-v' - - SET TINYTDS_VERSION=1.0.5 + - SET TINYTDS_VERSION=1.1.0 - SET ONLY_SQLSERVER=1 clone_depth: 5 skip_tags: true @@ -33,7 +33,7 @@ environment: CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - # - ruby_version: "23-x64" + - ruby_version: "23-x64" - ruby_version: "23" - # - ruby_version: "22-x64" + - ruby_version: "22-x64" - ruby_version: "22" diff --git a/circle.yml b/circle.yml index 1c273c10b..dd4e3bc22 100644 --- a/circle.yml +++ b/circle.yml @@ -7,7 +7,7 @@ machine: environment: PATH: /opt/local/bin:${PATH} TESTOPTS: -v - TINYTDS_VERSION: 1.0.5 + TINYTDS_VERSION: 1.1.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost ONLY_SQLSERVER: 1 From 62da8185e2965e39ca57bd02244310df55a8f2b2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 20 Dec 2016 21:22:29 -0500 Subject: [PATCH 0527/1412] Update assertions for MT6 compatability. --- test/cases/column_test_sqlserver.rb | 194 +++++++++---------- test/cases/specific_schema_test_sqlserver.rb | 4 +- 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 91a42fc8a..f787eefb4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -40,7 +40,7 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 42 obj.bigint.must_equal 42 - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::BigInteger type.limit.must_equal 8 @@ -55,7 +55,7 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 42 obj.int.must_equal 42 - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Integer type.limit.must_equal 4 @@ -70,7 +70,7 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 42 obj.smallint.must_equal 42 - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallInteger type.limit.must_equal 2 @@ -85,7 +85,7 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 42 obj.tinyint.must_equal 42 - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::TinyInteger type.limit.must_equal 1 @@ -100,10 +100,10 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal true obj.bit.must_equal true - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Boolean - type.limit.must_equal nil + type.limit.must_be_nil obj.bit = 0 obj.bit.must_equal false obj.save! @@ -121,10 +121,10 @@ def assert_obj_set_and_save(attribute, value) 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_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 9 type.scale.must_equal 2 obj.decimal_9_2 = '1234567.8901' @@ -138,7 +138,7 @@ def assert_obj_set_and_save(attribute, value) 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_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.precision.must_equal 16 type.scale.must_equal 4 @@ -155,10 +155,10 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal BigDecimal('191') obj.numeric_18_0.must_equal BigDecimal('191') - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 18 type.scale.must_equal 0 obj.numeric_18_0 = '192.1' @@ -174,10 +174,10 @@ def assert_obj_set_and_save(attribute, value) 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_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Decimal - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 36 type.scale.must_equal 2 obj.numeric_36_2 = '192.123' @@ -193,10 +193,10 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal BigDecimal('4.20') obj.money.must_equal BigDecimal('4.20') - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Money - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 19 type.scale.must_equal 4 obj.money = '922337203685477.58061' @@ -212,10 +212,10 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal BigDecimal('4.20') obj.smallmoney.must_equal BigDecimal('4.20') - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallMoney - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 10 type.scale.must_equal 4 obj.smallmoney = '214748.36461' @@ -235,12 +235,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 123.00000001 obj.float.must_equal 123.00000001 - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Float - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil obj.float = '214748.36461' obj.float.must_equal 214748.36461 obj.save! @@ -254,12 +254,12 @@ def assert_obj_set_and_save(attribute, value) 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_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Real - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil obj.real = '214748.36461' obj.real.must_be_close_to 214748.36461, 0.01 obj.save! @@ -275,12 +275,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' obj.date.must_equal Date.civil(0001, 1, 1) - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Date - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil # Can cast strings. obj.date = '0001-04-01' obj.date.must_equal Date.civil(0001, 4, 1) @@ -304,12 +304,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>" obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>" - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil # Can save to proper accuracy and return again. obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 3000) obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" @@ -332,12 +332,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>" obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime2 - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 7 - type.scale.must_equal nil + type.scale.must_be_nil # Can save 100 nanosecond precisoins and return again. obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000)) obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" @@ -381,12 +381,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTimeOffset - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 7 - type.scale.must_equal nil + type.scale.must_be_nil # Can save 100 nanosecond precisoins and return again. obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755) obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" @@ -418,12 +418,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) obj.smalldatetime.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::SmallDateTime - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil # Will remove fractional seconds and return again. obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" @@ -440,12 +440,12 @@ def assert_obj_set_and_save(attribute, value) col.type.must_equal :time col.null.must_equal true col.default.must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Time - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 7 - type.scale.must_equal nil + type.scale.must_be_nil # Time's #usec precision (low micro) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" @@ -471,13 +471,13 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'time(2)' col.type.must_equal :time col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil + col.default.must_be_nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Time - type.limit.must_equal nil + type.limit.must_be_nil type.precision.must_equal 2 - type.scale.must_equal nil + type.scale.must_be_nil # Always uses TinyTDS/Windows 2000-01-01 convention too. obj.time_2 = Time.utc(2015, 01, 10, 15, 45, 00, 0) obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) @@ -504,12 +504,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal '1234567890' obj.char_10.must_equal '1234567890' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Char type.limit.must_equal 10 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. obj.char_10 = '012345' obj.char_10.strip.must_equal '012345' @@ -524,12 +524,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test varchar_50' obj.varchar_50.must_equal 'test varchar_50' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Varchar type.limit.must_equal 50 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :varchar_50, 'Hello World' end @@ -541,12 +541,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test varchar_max' obj.varchar_max.must_equal 'test varchar_max' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::VarcharMax type.limit.must_equal 2_147_483_647 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :varchar_max, 'Hello World' end @@ -558,12 +558,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test text' obj.text.must_equal 'test text' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Text type.limit.must_equal 2_147_483_647 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :text, 'Hello World' end @@ -577,12 +577,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal '12345678åå' obj.nchar_10.must_equal '12345678åå' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeChar type.limit.must_equal 10 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. obj.nchar_10 = "五六" obj.nchar_10.strip.must_equal "五六" @@ -597,12 +597,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test nvarchar_50 åå' obj.nvarchar_50.must_equal 'test nvarchar_50 åå' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeVarchar type.limit.must_equal 50 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :nvarchar_50, "一二34五六" end @@ -614,12 +614,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test nvarchar_max åå' obj.nvarchar_max.must_equal 'test nvarchar_max åå' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeVarcharMax type.limit.must_equal 2_147_483_647 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :nvarchar_max, "一二34五六" end @@ -631,12 +631,12 @@ def assert_obj_set_and_save(attribute, value) col.null.must_equal true col.default.must_equal 'test ntext åå' obj.ntext.must_equal 'test ntext åå' - col.default_function.must_equal nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::UnicodeText type.limit.must_equal 2_147_483_647 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. assert_obj_set_and_save :ntext, "一二34五六" end @@ -651,13 +651,13 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'binary(49)' col.type.must_equal :binary_basic col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil + col.default.must_be_nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Binary type.limit.must_equal 49 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. binary_data.encoding.must_equal Encoding::BINARY binary_data.length.must_equal 49 @@ -672,13 +672,13 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'varbinary(49)' col.type.must_equal :varbinary col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil + col.default.must_be_nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Varbinary type.limit.must_equal 49 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. binary_data_20 = binary_data.to(20) binary_data_20.encoding.must_equal Encoding::BINARY @@ -693,13 +693,13 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'varbinary(max)' col.type.must_equal :binary col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil + col.default.must_be_nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::VarbinaryMax type.limit.must_equal 2_147_483_647 - type.precision.must_equal nil - type.scale.must_equal nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic set and save. binary_data.encoding.must_equal Encoding::BINARY assert_obj_set_and_save :varbinary_max, binary_data @@ -712,16 +712,16 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'uniqueidentifier' col.type.must_equal :uuid col.null.must_equal true - col.default.must_equal nil + col.default.must_be_nil col.default_function.must_equal 'newid()' type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Uuid - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + 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_equal nil + obj.uniqueidentifier.must_be_nil obj.save! ; obj.reload obj.uniqueidentifier.must_match Type::Uuid::ACCEPTABLE_UUID obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" @@ -735,15 +735,15 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'timestamp' col.type.must_equal :ss_timestamp col.null.must_equal true - col.default.must_equal nil - col.default_function.must_equal nil + col.default.must_be_nil + col.default_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::Timestamp - type.limit.must_equal nil - type.precision.must_equal nil - type.scale.must_equal nil + type.limit.must_be_nil + type.precision.must_be_nil + type.scale.must_be_nil # Basic read. - obj.timestamp.must_equal nil + obj.timestamp.must_be_nil obj.save! ; obj.reload obj.timestamp.must_match %r|\000| obj.timestamp diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 7af232cba..896841c5a 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -45,7 +45,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'default strings before save' do default = SSTestStringDefault.new - assert_equal nil, default.string_with_null_default + assert_nil default.string_with_null_default assert_equal 'null', default.string_with_pretend_null_one assert_equal '(null)', default.string_with_pretend_null_two assert_equal 'NULL', default.string_with_pretend_null_three @@ -55,7 +55,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'default strings after save' do default = SSTestStringDefault.create - assert_equal nil, default.string_with_null_default + assert_nil default.string_with_null_default assert_equal 'null', default.string_with_pretend_null_one assert_equal '(null)', default.string_with_pretend_null_two assert_equal 'NULL', default.string_with_pretend_null_three From aa7586b92b4ca997f1cff33cf9fce3f474a21ec8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Dec 2016 20:53:26 -0500 Subject: [PATCH 0528/1412] Remove .rb from type requires. --- .../connection_adapters/sqlserver/type.rb | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 47bb07be5..0c9689799 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,44 +1,44 @@ require 'active_record/type' # Behaviors -require 'active_record/connection_adapters/sqlserver/type/time_value_fractional.rb' +require 'active_record/connection_adapters/sqlserver/type/time_value_fractional' # Exact Numerics -require 'active_record/connection_adapters/sqlserver/type/integer.rb' -require 'active_record/connection_adapters/sqlserver/type/big_integer.rb' -require 'active_record/connection_adapters/sqlserver/type/small_integer.rb' -require 'active_record/connection_adapters/sqlserver/type/tiny_integer.rb' -require 'active_record/connection_adapters/sqlserver/type/boolean.rb' -require 'active_record/connection_adapters/sqlserver/type/decimal.rb' -require 'active_record/connection_adapters/sqlserver/type/money.rb' -require 'active_record/connection_adapters/sqlserver/type/small_money.rb' +require 'active_record/connection_adapters/sqlserver/type/integer' +require 'active_record/connection_adapters/sqlserver/type/big_integer' +require 'active_record/connection_adapters/sqlserver/type/small_integer' +require 'active_record/connection_adapters/sqlserver/type/tiny_integer' +require 'active_record/connection_adapters/sqlserver/type/boolean' +require 'active_record/connection_adapters/sqlserver/type/decimal' +require 'active_record/connection_adapters/sqlserver/type/money' +require 'active_record/connection_adapters/sqlserver/type/small_money' # Approximate Numerics -require 'active_record/connection_adapters/sqlserver/type/float.rb' -require 'active_record/connection_adapters/sqlserver/type/real.rb' +require 'active_record/connection_adapters/sqlserver/type/float' +require 'active_record/connection_adapters/sqlserver/type/real' # Date and Time -require 'active_record/connection_adapters/sqlserver/type/date.rb' -require 'active_record/connection_adapters/sqlserver/type/datetime.rb' -require 'active_record/connection_adapters/sqlserver/type/datetime2.rb' -require 'active_record/connection_adapters/sqlserver/type/datetimeoffset.rb' -require 'active_record/connection_adapters/sqlserver/type/smalldatetime.rb' -require 'active_record/connection_adapters/sqlserver/type/time.rb' +require 'active_record/connection_adapters/sqlserver/type/date' +require 'active_record/connection_adapters/sqlserver/type/datetime' +require 'active_record/connection_adapters/sqlserver/type/datetime2' +require 'active_record/connection_adapters/sqlserver/type/datetimeoffset' +require 'active_record/connection_adapters/sqlserver/type/smalldatetime' +require 'active_record/connection_adapters/sqlserver/type/time' # Character Strings -require 'active_record/connection_adapters/sqlserver/type/string.rb' -require 'active_record/connection_adapters/sqlserver/type/char.rb' -require 'active_record/connection_adapters/sqlserver/type/varchar.rb' -require 'active_record/connection_adapters/sqlserver/type/varchar_max.rb' -require 'active_record/connection_adapters/sqlserver/type/text.rb' +require 'active_record/connection_adapters/sqlserver/type/string' +require 'active_record/connection_adapters/sqlserver/type/char' +require 'active_record/connection_adapters/sqlserver/type/varchar' +require 'active_record/connection_adapters/sqlserver/type/varchar_max' +require 'active_record/connection_adapters/sqlserver/type/text' # Unicode Character Strings -require 'active_record/connection_adapters/sqlserver/type/unicode_string.rb' -require 'active_record/connection_adapters/sqlserver/type/unicode_char.rb' -require 'active_record/connection_adapters/sqlserver/type/unicode_varchar.rb' -require 'active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb' -require 'active_record/connection_adapters/sqlserver/type/unicode_text.rb' +require 'active_record/connection_adapters/sqlserver/type/unicode_string' +require 'active_record/connection_adapters/sqlserver/type/unicode_char' +require 'active_record/connection_adapters/sqlserver/type/unicode_varchar' +require 'active_record/connection_adapters/sqlserver/type/unicode_varchar_max' +require 'active_record/connection_adapters/sqlserver/type/unicode_text' # Binary Strings -require 'active_record/connection_adapters/sqlserver/type/binary.rb' -require 'active_record/connection_adapters/sqlserver/type/varbinary.rb' -require 'active_record/connection_adapters/sqlserver/type/varbinary_max.rb' +require 'active_record/connection_adapters/sqlserver/type/binary' +require 'active_record/connection_adapters/sqlserver/type/varbinary' +require 'active_record/connection_adapters/sqlserver/type/varbinary_max' # Other Data Types -require 'active_record/connection_adapters/sqlserver/type/uuid.rb' -require 'active_record/connection_adapters/sqlserver/type/timestamp.rb' +require 'active_record/connection_adapters/sqlserver/type/uuid' +require 'active_record/connection_adapters/sqlserver/type/timestamp' module ActiveRecord module Type From 91af10262a17ea67e72de6acfaadd3b4f8976988 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 22 Dec 2016 22:36:06 -0500 Subject: [PATCH 0529/1412] Lock down to Minitest 5.9.x --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 8b1fdefc3..41fc1e351 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source 'https://rubygems.org' gemspec +gem 'minitest', '~> 5.9.0' gem 'bcrypt', platforms: [:mri] gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] From 96e734ea14eabc430e30186b4713eb88da43335d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Dec 2016 13:47:43 -0500 Subject: [PATCH 0530/1412] Clean up UUID test. --- test/cases/uuid_test_sqlserver.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index ca19578eb..cf99f0dd7 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -11,7 +11,6 @@ class SQLServerUuidTest < ActiveRecord::TestCase end it 'can create with a new pk' do - # Type::Uuid::ACCEPTABLE_UUID obj = SSTestUuid.create! obj.id.must_be :present? obj.id.must_match acceptable_uuid From 4692c57d39ca10109e12416a989fe91815d7e244 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 24 Dec 2016 13:52:09 -0500 Subject: [PATCH 0531/1412] All date/times use Data type cast wrapper. --- .../connection_adapters/sqlserver/quoting.rb | 22 ++++++---------- .../connection_adapters/sqlserver/type.rb | 1 + .../sqlserver/type/char.rb | 21 +++------------ .../sqlserver/type/data.rb | 26 +++++++++++++++++++ .../sqlserver/type/date.rb | 17 +++--------- .../sqlserver/type/datetime.rb | 14 ---------- .../sqlserver/type/datetimeoffset.rb | 10 +++---- .../sqlserver/type/time.rb | 15 ++++++----- .../sqlserver/type/uuid.rb | 9 +++++++ test/cases/specific_schema_test_sqlserver.rb | 7 ++--- 10 files changed, 70 insertions(+), 72 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/data.rb diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index f259736a3..a3e6e83fb 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -62,9 +62,9 @@ def unquoted_false def quoted_date(value) if value.acts_like?(:date) - Type::Date.new.serialize(value) + Type::Date.new.serialize(value).quoted else value.acts_like?(:time) - Type::DateTime.new.serialize(value) + Type::DateTime.new.serialize(value).quoted end end @@ -75,11 +75,7 @@ def _quote(value) case value when Type::Binary::Data "0x#{value.hex}" - when ActiveRecord::Type::SQLServer::Char::Data - value.quoted - when ActiveRecord::Type::SQLServer::Date::Data, - ActiveRecord::Type::SQLServer::DateTime::Data, - ActiveRecord::Type::SQLServer::DateTime2::Data + when ActiveRecord::Type::SQLServer::Data value.quoted when String, ActiveSupport::Multibyte::Chars "#{QUOTED_STRING_PREFIX}#{super}" @@ -94,14 +90,12 @@ def _type_cast(value) "NULL" when Symbol _quote(value.to_s) - when String, ActiveSupport::Multibyte::Chars, Type::Binary::Data + when String, ActiveSupport::Multibyte::Chars + _quote(value) + when Type::Binary::Data + _quote(value) + when ActiveRecord::Type::SQLServer::Data _quote(value) - when ActiveRecord::Type::SQLServer::Char::Data - value.quoted - when ActiveRecord::Type::SQLServer::Date::Data, - ActiveRecord::Type::SQLServer::DateTime::Data, - ActiveRecord::Type::SQLServer::DateTime2::Data - value.quoted else super end end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 0c9689799..c8291e433 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,5 +1,6 @@ require 'active_record/type' # Behaviors +require 'active_record/connection_adapters/sqlserver/type/data' require 'active_record/connection_adapters/sqlserver/type/time_value_fractional' # Exact Numerics require 'active_record/connection_adapters/sqlserver/type/integer' diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index dcf72ad7a..47fb3e0d6 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -11,7 +11,7 @@ def type def serialize(value) return if value.nil? return value if value.is_a?(Data) - Data.new(super) + Data.new super, self end def sqlserver_type @@ -20,22 +20,9 @@ def sqlserver_type end end - class Data - - def initialize(value) - @quoted_id = value.respond_to?(:quoted_id) - @value = @quoted_id ? value.quoted_id : value.to_s - end - - def quoted - @quoted_id ? @value : "'#{Utils.quote_string(@value)}'" - end - - def to_s - @value - end - alias_method :to_str, :to_s - + def quoted(value) + return value.quoted_id if value.respond_to?(:quoted_id) + Utils.quote_string_single(value) end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb new file mode 100644 index 000000000..609df8960 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -0,0 +1,26 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Data + + attr_reader :value, :type + + def initialize(value, type) + @value, @type = value, type + end + + def quoted + type.quoted(@value) + end + + def to_s + @value + end + alias_method :to_str, :to_s + + end + end + 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 9564c7027..851fe2d0f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -11,7 +11,7 @@ def sqlserver_type def serialize(value) return unless value.present? return value if value.is_a?(Data) - Data.new(super) + Data.new super, self end def deserialize(value) @@ -23,18 +23,9 @@ def type_cast_for_schema(value) serialize(value).quoted end - class Data - - attr_reader :value - - def initialize(value) - @value = value - end - - def quoted - Utils.quote_string_single @value.to_s(:_sqlserver_dateformat) - end - + def quoted(value) + date = value.to_s(:_sqlserver_dateformat) + Utils.quote_string_single(date) end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index f724e4832..3bf1a7b7d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -34,20 +34,6 @@ def quoted(value) Utils.quote_string_single(datetime) end - class Data - - attr_reader :value, :type - - def initialize(value, type) - @value, @type = value, type - end - - def quoted - type.quoted(@value) - end - - end - private def cast_value(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index a5fe3dd72..d1b1a88ba 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -8,11 +8,6 @@ def type :datetimeoffset end - def serialize(value) - return super unless value.acts_like?(:time) - value.to_s :_sqlserver_datetimeoffset - end - def type_cast_for_schema(value) serialize(value).inspect end @@ -21,6 +16,11 @@ def sqlserver_type "datetimeoffset(#{precision.to_i})" end + def quoted(value) + datetime = value.to_s(:_sqlserver_datetimeoffset) + Utils.quote_string_single(datetime) + end + private def fast_string_to_time(string) diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 21810409a..2a23fcb44 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -8,21 +8,24 @@ class Time < ActiveRecord::Type::Time def serialize(value) return super unless value.acts_like?(:time) - time = value.to_s(:_sqlserver_time) - "#{time}".tap do |v| - fraction = quote_fractional(value) - v << ".#{fraction}" unless fraction.to_i.zero? - end + Data.new super, self end def type_cast_for_schema(value) - serialize(value).inspect + serialize(value).quoted end def sqlserver_type "time(#{precision.to_i})" end + def quoted(value) + time = value.to_s(:_sqlserver_time).tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end + Utils.quote_string_single(time) + end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index 9457f44c5..c5e479986 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -16,10 +16,19 @@ def sqlserver_type 'uniqueidentifier'.freeze end + def serialize(value) + return unless value + Data.new super, self + end + def cast(value) value.to_s[ACCEPTABLE_UUID, 0] end + def quoted(value) + Utils.quote_string_single(value) if value + end + end end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 896841c5a..59df21bb6 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -117,9 +117,10 @@ def quoted_id assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } # Using our custom char type data. - data = ActiveRecord::Type::SQLServer::Char::Data - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T')).first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T')).first } + type = ActiveRecord::Type::SQLServer::Char + data = ActiveRecord::Type::SQLServer::Data + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T', type.new)).first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T', type.new)).first } # Taking care of everything. assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first } assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first } From 096e1b778b479a1d28bc73cb582a55acc30799dd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 27 Dec 2016 21:23:00 -0500 Subject: [PATCH 0532/1412] Refactor `_type_cast` & `_quote`. I made a mistake and made `_type_cast` do quoting too. This is now fixed and we have greatly simplified it and `_quote` and use these two to cast and quote as needed in our sp_executesql helpers since we need to format both cast and quoted SQL strings. Changed `sp_executesql_sql_type` to have a default `String` case to `nvarchar(max)` not ideal and there are places where we prefer the bind contain the proper SQLServer type. For example, uniquness validations yielding a `ActiveRecord::Relation::QueryAttribute` with a [basic cast type](https://github.com/rails/rails/blob/5-0-stable/activerecord/lib/active_record/validations/uniqueness.rb#L80) vs the in scope `cast_type` further up. Added `sp_executesql_sql_param` which looks out for all `SQLServer::Data` and quotes it accordingly. For example, date & time objects need single quote '' vs. national N'' quoting. * New base `SQLServer::Data` shared for all types. Delegates quoting decisoin back to type. * Time types use variouse flavors of (Date|Time)#strptime in concert with SQL Server date/time formats in fast string processing. --- .../sqlserver/database_statements.rb | 15 +++++++-- .../connection_adapters/sqlserver/quoting.rb | 13 ++------ .../sqlserver/type/data.rb | 4 +++ .../sqlserver/type/date.rb | 22 +++++++++---- .../sqlserver/type/datetime.rb | 32 +++++++++++-------- .../sqlserver/type/datetimeoffset.rb | 18 +---------- .../sqlserver/type/smalldatetime.rb | 4 +++ .../sqlserver/type/time.rb | 16 ++++++---- test/cases/column_test_sqlserver.rb | 9 +++++- 9 files changed, 78 insertions(+), 55 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9083ead03..aa6154b3d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -240,7 +240,7 @@ def sp_executesql_types_and_parameters(binds) types, params = [], [] binds.each_with_index do |attr, index| types << "@#{index} #{sp_executesql_sql_type(attr)}" - params << type_cast(attr.value_for_database) + params << sp_executesql_sql_param(attr) end [types, params] end @@ -250,9 +250,20 @@ def sp_executesql_sql_type(attr) case value = attr.value_for_database when Numeric 'int'.freeze + when String + 'nvarchar(max)'.freeze else raise TypeError, "sp_executesql_sql_type can not find sql type for attr #{attr.inspect}" - quote(value) + end + end + + def sp_executesql_sql_param(attr) + case attr.value_for_database + when Type::Binary::Data, + ActiveRecord::Type::SQLServer::Data + quote(attr.value_for_database) + else + quote(type_cast(attr.value_for_database)) end end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index a3e6e83fb..95d26ffa8 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -86,17 +86,10 @@ def _quote(value) def _type_cast(value) case value - when nil - "NULL" - when Symbol - _quote(value.to_s) - when String, ActiveSupport::Multibyte::Chars - _quote(value) - when Type::Binary::Data - _quote(value) when ActiveRecord::Type::SQLServer::Data - _quote(value) - else super + value.to_s + else + super end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 609df8960..a96d54cd1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -19,6 +19,10 @@ def to_s end alias_method :to_str, :to_s + def inspect + @value.inspect + end + 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 851fe2d0f..492377ef7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -10,13 +10,12 @@ def sqlserver_type def serialize(value) return unless value.present? - return value if value.is_a?(Data) - Data.new super, self + date = value.to_s(:_sqlserver_dateformat) + Data.new date, self end def deserialize(value) - return value.value if value.is_a?(Data) - super + value.is_a?(Data) ? super(value.value) : super end def type_cast_for_schema(value) @@ -24,8 +23,19 @@ def type_cast_for_schema(value) end def quoted(value) - date = value.to_s(:_sqlserver_dateformat) - Utils.quote_string_single(date) + Utils.quote_string_single(value) + end + + private + + def fast_string_to_date(string) + ::Date.strptime(string, fast_string_to_date_format) + rescue ArgumentError + super + end + + def fast_string_to_date_format + ::Date::DATE_FORMATS[:_sqlserver_dateformat] end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 3bf1a7b7d..480547022 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -12,13 +12,15 @@ def sqlserver_type def serialize(value) return super unless value.acts_like?(:time) - Data.new super, self + datetime = super.to_s(:_sqlserver_datetime).tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end + Data.new datetime, self end def deserialize(value) - datetime = value.is_a?(Data) ? value.value : super - return unless datetime - zone_conversion(datetime) + value.is_a?(Data) ? super(value.value) : super end def type_cast_for_schema(value) @@ -26,12 +28,7 @@ def type_cast_for_schema(value) end def quoted(value) - datetime = value.to_s(:_sqlserver_datetime) - datetime = "#{datetime}".tap do |v| - fraction = quote_fractional(value) - v << ".#{fraction}" unless fraction.to_i.zero? - end - Utils.quote_string_single(datetime) + Utils.quote_string_single(value) end private @@ -42,9 +39,18 @@ def cast_value(value) apply_seconds_precision(value) end - def zone_conversion(value) - method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal - value.respond_to?(method) ? value.send(method) : value + def fast_string_to_time(string) + fast_string_to_time_zone.strptime(string, fast_string_to_time_format).time + rescue ArgumentError + super + end + + def fast_string_to_time_format + "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N".freeze + end + + def fast_string_to_time_zone + ::Time.zone || ActiveSupport::TimeZone.new('UTC') end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index d1b1a88ba..9564dadda 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -8,28 +8,12 @@ def type :datetimeoffset end - def type_cast_for_schema(value) - serialize(value).inspect - end - def sqlserver_type "datetimeoffset(#{precision.to_i})" end def quoted(value) - datetime = value.to_s(:_sqlserver_datetimeoffset) - Utils.quote_string_single(datetime) - end - - private - - def fast_string_to_time(string) - dateformat = ::Time::DATE_FORMATS[:_sqlserver_dateformat] - ::Time.strptime string, "#{dateformat} %H:%M:%S.%N %:z" - end - - def zone_conversion(value) - value + Utils.quote_string_single(value) end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 67070e077..22c6c1856 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -14,6 +14,10 @@ def sqlserver_type private + def fast_string_to_time_format + ::Time::DATE_FORMATS[:_sqlserver_datetime] + end + def apply_seconds_precision(value) value.change usec: 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 2a23fcb44..5d8ca3c0f 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -8,7 +8,15 @@ class Time < ActiveRecord::Type::Time def serialize(value) return super unless value.acts_like?(:time) - Data.new super, self + time = value.to_s(:_sqlserver_time).tap do |v| + fraction = quote_fractional(value) + v << ".#{fraction}" unless fraction.to_i.zero? + end + Data.new time, self + end + + def deserialize(value) + value.is_a?(Data) ? super(value.value) : super end def type_cast_for_schema(value) @@ -20,11 +28,7 @@ def sqlserver_type end def quoted(value) - time = value.to_s(:_sqlserver_time).tap do |v| - fraction = quote_fractional(value) - v << ".#{fraction}" unless fraction.to_i.zero? - end - Utils.quote_string_single(time) + Utils.quote_string_single(value) end private diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index f787eefb4..d3a2e217e 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -281,7 +281,14 @@ def assert_obj_set_and_save(attribute, value) type.limit.must_be_nil type.precision.must_be_nil type.scale.must_be_nil - # Can cast strings. + # Can cast strings. SQL Server format. + obj.date = '04-01-0001' + obj.date.must_equal Date.civil(0001, 4, 1) + obj.save! + obj.date.must_equal Date.civil(0001, 4, 1) + obj.reload + obj.date.must_equal Date.civil(0001, 4, 1) + # Can cast strings. ISO format. obj.date = '0001-04-01' obj.date.must_equal Date.civil(0001, 4, 1) obj.save! From ae0296c2bbe08a70b2c793a5fd2c453d485a4015 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 30 Dec 2016 07:50:02 -0500 Subject: [PATCH 0533/1412] Update Rails 5 TODO. --- TODO.md | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/TODO.md b/TODO.md index 14b0a0bfb..493b947df 100644 --- a/TODO.md +++ b/TODO.md @@ -3,10 +3,7 @@ Misc remidners while in the heat of adapting the adpater. -* Try removing `sp_executesql_sql_type` all together. Do we have to add more types? -* Did we get the schema cache right? - - +* Review coerced tests. ## LONG TERM @@ -15,24 +12,7 @@ After we get some tests passing * Is `primary_keys(table_name)` performant? Contribute to rails for abstract adapter. * Check `sql_for_insert` can do without the table regular expresion. * Do we need the `query_requires_identity_insert` check in `execute`? -* Will we have to add more Data types to our dates and use them in `quoted_date` or `quoted_string` or `_type_cast`? - - -#### Use #without_prepared_statement? - -I think we always send everything thru `sp_executesql`. Consider re-evaulating if there are no `binds` that we get any benefit from this. By doing so we also give the users the ability to turn this off completly. Would be neat to see how our prepared statments actually perform again. - -```ruby -def without_prepared_statement?(binds) - !prepared_statements || binds.empty? -end -``` - -Maybe just quick bail to `do_execute`. Maybe related: - -* [Do not cache prepared statements that are unlikely to have cache hits](https://github.com/rails/rails/commit/cbcdecd2) - - +* Does the schema cache serialize properly since we conform to that now? #### Does Find By SQL Work? From 237ce43728c39e11c9a166bd1f31b9ae5cb5a743 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 30 Dec 2016 07:53:37 -0500 Subject: [PATCH 0534/1412] [Rails5] Performance optimizations. These changes make the full ActiveRecord test suite run about 100s faster from ~580s to ~480s. * Use the `schema_cache.columns` for the `#primary_keys` method. * Ensure that `exec_query` uses the proper arity/arg names. Namely `:prepare` opt. * Ensure all view checks/methods used in `#columns` only. * Removed view table name detection in `with_identity_insert_enabled` method for fixtures. * Removed `#select` abstract adapter override since it was the same. --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 16 +++++++--------- .../sqlserver/schema_statements.rb | 10 +++------- test/cases/adapter_test_sqlserver.rb | 9 --------- 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 033b75284..a421fb6cd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ #### Removed * ODBC connection mode. Not been maintained since Rails 4.0. +* View table name detection in `with_identity_insert_enabled` method for fixtures. Perf hit. #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index aa6154b3d..4f3602b8f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -15,8 +15,8 @@ def execute(sql, name = nil) end end - def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {}) - sp_executesql(sql, name, binds) + def exec_query(sql, name = 'SQL', binds = [], prepare: false) + sp_executesql(sql, name, binds, prepare: prepare) end def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil) @@ -125,7 +125,7 @@ def execute_procedure(proc_name, *variables) end def with_identity_insert_enabled(table_name) - table_name = quote_table_name(table_name_or_views_table_name(table_name)) + table_name = quote_table_name(table_name) set_identity_insert(table_name, true) yield ensure @@ -197,10 +197,6 @@ def newsequentialid_function protected - def select(sql, name = nil, binds = []) - exec_query(sql, name, binds) - end - def sql_for_insert(sql, pk, id_value, sequence_name, binds) if pk.nil? table_name = query_requires_identity_insert?(sql) @@ -231,8 +227,10 @@ def do_execute(sql, name = 'SQL') def sp_executesql(sql, name, binds, options = {}) options[:ar_result] = true if options[:fetch] != :rows - types, params = sp_executesql_types_and_parameters(binds) - sql = sp_executesql_sql(sql, types, params, name) + unless without_prepared_statement?(binds) + types, params = sp_executesql_types_and_parameters(binds) + sql = sp_executesql_sql(sql, types, params, name) + end raw_select sql, name, binds, options end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2c17d6741..386f8b277 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -80,7 +80,7 @@ def new_column(name, default, sql_type_metadata, null, table_name, default_funct end def primary_keys(table_name) - primaries = columns(table_name).select(&:is_primary?).map(&:name) + primaries = schema_cache.columns(table_name).select(&:is_primary?).map(&:name) primaries.present? ? primaries : identity_columns(table_name).map(&:name) end @@ -254,7 +254,7 @@ def column_definitions(table_name) end database = identifier.fully_qualified_database_quoted view_exists = view_exists?(table_name) - view_tblnm = table_name_or_views_table_name(table_name) if view_exists + view_tblnm = view_table_name(table_name) if view_exists sql = %{ SELECT DISTINCT #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, @@ -307,7 +307,7 @@ def column_definitions(table_name) nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? - results = sp_executesql(sql, 'SCHEMA', binds) + results = sp_executesql(sql, 'SCHEMA', binds, prepare: true) results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] @@ -446,10 +446,6 @@ def view_information(table_name) view_info end - def table_name_or_views_table_name(table_name) - view_exists?(table_name) ? view_table_name(table_name) : table_name - end - def views_real_column_name(table_name, column_name) view_definition = view_information(table_name)[:VIEW_DEFINITION] return column_name unless view_definition diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index f19dfee51..5ecb077ad 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -396,15 +396,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert SSTestStringDefaultsView.connection.data_source_exists?(SSTestStringDefaultsView.table_name) end - # Doing identity inserts - - it 'be able to do an identity insert' do - customer = SSTestCustomersView.new - customer.id = 420 - customer.save! - assert SSTestCustomersView.find(420) - end - # That have more than 4000 chars for their defintion it 'cope with null returned for the defintion' do From d82d7eb421cc382bd2b730bf23a9a4aeecba2722 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Jan 2017 14:07:08 -0500 Subject: [PATCH 0535/1412] [Rails 5] Fix some times coming back nil after save. As part of #528, a few errors were introduced. Since we have a `fast_string_to_time` implementation that uses a custom format which includes %N, parsing the time for DB from DB could fail. So we ensure we always have a nanosecond on the SQL even if it is 0. No need not to. --- .../connection_adapters/sqlserver/type/datetime.rb | 2 +- lib/active_record/connection_adapters/sqlserver/type/time.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 480547022..b6cd2f208 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -14,7 +14,7 @@ def serialize(value) return super unless value.acts_like?(:time) datetime = super.to_s(:_sqlserver_datetime).tap do |v| fraction = quote_fractional(value) - v << ".#{fraction}" unless fraction.to_i.zero? + v << ".#{fraction}" end Data.new datetime, self end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 5d8ca3c0f..3ca7348fd 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -10,7 +10,7 @@ def serialize(value) return super unless value.acts_like?(:time) time = value.to_s(:_sqlserver_time).tap do |v| fraction = quote_fractional(value) - v << ".#{fraction}" unless fraction.to_i.zero? + v << ".#{fraction}" end Data.new time, self end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 985766bc2..ee52d1e02 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -30,7 +30,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: 3, scale: nil, default: nil assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: 1, scale: nil, default: nil end - assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00" + assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? assert_line :time_7, type: 'time', limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil From 611e81af037f1e9db4633fb3f50b06eb24dc8503 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Jan 2017 20:06:47 -0500 Subject: [PATCH 0536/1412] [Rails5] Fix view_exists? into public. --- TODO.md | 4 ++++ .../connection_adapters/sqlserver/schema_statements.rb | 10 +++++----- test/cases/coerced_tests.rb | 9 ++++++--- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/TODO.md b/TODO.md index 493b947df..f62834fc3 100644 --- a/TODO.md +++ b/TODO.md @@ -13,6 +13,10 @@ After we get some tests passing * Check `sql_for_insert` can do without the table regular expresion. * Do we need the `query_requires_identity_insert` check in `execute`? * Does the schema cache serialize properly since we conform to that now? +* What does `supports_materialized_views?` means for SQL Server + http://michaeljswart.com/2014/12/materialized-views-in-sql-server/ + https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ + http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server #### Does Find By SQL Work? diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 386f8b277..6bc0e245b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -21,6 +21,11 @@ def views tables('VIEW') end + def view_exists?(table_name) + identifier = SQLServer::Utils.extract_identifiers(table_name) + super(identifier.object) + end + def create_table(table_name, comment: nil, **options) res = super clear_cache! @@ -419,11 +424,6 @@ def lowercase_schema_reflection_sql(node) # === SQLServer Specific (View Reflection) ====================== # - def view_exists?(table_name) - identifier = SQLServer::Utils.extract_identifiers(table_name) - views.include? identifier.object - end - def view_table_name(table_name) view_info = view_information(table_name) view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 02787e65e..1ee6382c8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -657,12 +657,15 @@ class TransactionIsolationTest < ActiveRecord::TestCase end - +require 'models/book' +class Paperback < ActiveRecord::Base; end class ViewWithPrimaryKeyTest < ActiveRecord::TestCase - + # We have a few fews and test is poor equality based. + coerce_tests! :test_views + # This is deprecated and we dont care in thsi version to pass it. + coerce_tests! :test_table_exists # We do better than ActiveRecord and find the views PK. coerce_tests! :test_does_not_assume_id_column_as_primary_key - end From 71a36f46a13d5c0ad539ade42e2430a1209be9d5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Jan 2017 10:09:31 -0500 Subject: [PATCH 0537/1412] [Rails5] Fix #change_column_default. --- .../connection_adapters/sqlserver/schema_statements.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6bc0e245b..492345221 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -124,11 +124,13 @@ def change_column(table_name, column_name, type, options = {}) sql_commands.each { |c| do_execute(c) } end - def change_column_default(table_name, column_name, default) + def change_column_default(table_name, column_name, default_or_changes) clear_cache! + column = column_for(table_name, column_name) + return unless column remove_default_constraint(table_name, column_name) - column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } - do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column_object)} FOR #{quote_column_name(column_name)}" + default = extract_new_default_value(default_or_changes) + do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}" clear_cache! end From 86af1c78676441fc49248a08e613dddfdc7ca9ef Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Jan 2017 12:27:52 -0500 Subject: [PATCH 0538/1412] [Rails5] Review all coerced tests. --- test/cases/coerced_tests.rb | 147 ++++++---------------- test/cases/helper_sqlserver.rb | 1 + test/support/coerceable_test_sqlserver.rb | 6 +- 3 files changed, 43 insertions(+), 111 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1ee6382c8..f0247cdd3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1,30 +1,11 @@ require 'cases/helper_sqlserver' -# Windows/Appveyor -if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ - # All of these are due to Time.local(2000).zone. See http://git.io/v3t0o - class BelongsToAssociationsTest < ActiveRecord::TestCase - coerce_tests! :test_belongs_to_with_touch_option_on_touch_without_updated_at_attributes - end - class BasicsTest < ActiveRecord::TestCase - coerce_tests! :test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc - coerce_tests! :test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local - coerce_tests! :test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local - end - class DirtyTest < ActiveRecord::TestCase - coerce_tests! :test_save_always_should_update_timestamps_when_serialized_attributes_are_present - coerce_tests! :test_previous_changes # Coupled to above test. - end -end - module ActiveRecord class AdapterTest < ActiveRecord::TestCase - # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement - end end @@ -33,7 +14,6 @@ class AdapterTest < ActiveRecord::TestCase require 'models/topic' class AttributeMethodsTest < ActiveRecord::TestCase - coerce_tests! :test_typecast_attribute_from_select_to_false def test_typecast_attribute_from_select_to_false_coerced Topic.create(:title => 'Budget') @@ -47,14 +27,12 @@ def test_typecast_attribute_from_select_to_true_coerced topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first assert topic.is_test? end - end class BasicsTest < ActiveRecord::TestCase - coerce_tests! :test_column_names_are_escaped def test_column_names_are_escaped_coerced conn = ActiveRecord::Base.connection @@ -76,14 +54,12 @@ def test_column_names_are_escaped_coerced # Caused in Rails v4.2.5 by adding `firm_id` column in this http://git.io/vBfMs # commit. Trust Rails has this covered. coerce_tests! :test_find_keeps_multiple_group_values - end class BelongsToAssociationsTest < ActiveRecord::TestCase - # Since @client.firm is a single first/top, and we use FETCH the order clause is used. coerce_tests! :test_belongs_to_does_not_use_order_by @@ -93,7 +69,6 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql) assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql) end - end @@ -101,10 +76,8 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced module ActiveRecord class BindParameterTest < ActiveRecord::TestCase - # Never finds `sql` since we use `EXEC sp_executesql` wrappers. coerce_tests! :test_binds_are_logged - end end @@ -112,7 +85,6 @@ class BindParameterTest < ActiveRecord::TestCase class CalculationsTest < ActiveRecord::TestCase - # Are decimal, not integer. coerce_tests! :test_should_return_decimal_average_of_integer_field def test_should_return_decimal_average_of_integer_field_coerced @@ -124,16 +96,15 @@ def test_should_return_decimal_average_of_integer_field_coerced def test_limit_is_kept_coerced queries = assert_sql { Account.limit(1).count } assert_equal 1, queries.length - queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY} + queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1} end coerce_tests! :test_limit_with_offset_is_kept def test_limit_with_offset_is_kept_coerced queries = assert_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length - queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY} + queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} end - end @@ -142,18 +113,13 @@ def test_limit_with_offset_is_kept_coerced module ActiveRecord class Migration class ChangeSchemaTest < ActiveRecord::TestCase - # We test these. coerce_tests! :test_create_table_with_bigint, :test_create_table_with_defaults - - end class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase - # In SQL Server you have to delete the tables yourself in the right order. coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects - end end end @@ -164,7 +130,6 @@ class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase module ActiveRecord class Migration class ColumnAttributesTest < ActiveRecord::TestCase - # We have a default 4000 varying character limit. coerce_tests! :test_add_column_without_limit def test_add_column_without_limit_coerced @@ -172,7 +137,6 @@ def test_add_column_without_limit_coerced TestModel.reset_column_information TestModel.columns_hash["description"].limit.must_equal 4000 end - end end end @@ -183,14 +147,14 @@ def test_add_column_without_limit_coerced module ActiveRecord class Migration class ColumnsTest - - # Our defaults are reall 70000 integers vs '70000' strings. + # 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 default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default assert_equal 70000, default_before rename_column "test_models", "salary", "annual_salary" + TestModel.reset_column_information assert TestModel.column_names.include?("annual_salary") default_after = connection.columns("test_models").find { |c| c.name == "annual_salary" }.default assert_equal 70000, default_after @@ -206,7 +170,6 @@ def test_remove_column_with_multi_column_index_coerced remove_column("test_models", "hat_size") assert_equal [], connection.indexes('test_models').map(&:name) end - end end end @@ -215,12 +178,10 @@ def test_remove_column_with_multi_column_index_coerced class CoreTest < ActiveRecord::TestCase - # I think fixtures are useing the wrong time zone and the `:first` # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is # getting local EST time for me and set to "09:28:00.0000000". coerce_tests! :test_pretty_print_persisted - end @@ -228,7 +189,6 @@ class CoreTest < ActiveRecord::TestCase module ActiveRecord module ConnectionAdapters - # Just like PostgreSQLAdapter does. TypeLookupTest.coerce_all_tests! if defined?(TypeLookupTest) @@ -236,7 +196,6 @@ module ConnectionAdapters # a value of 'default_env' will still show tests failing. Just ignoring all # of them since we have no monkey in this circus. MergeAndResolveDefaultUrlConfigTest.coerce_all_tests! if defined?(MergeAndResolveDefaultUrlConfigTest) - end end @@ -258,10 +217,8 @@ class DatabaseTasksDropAllTest < ActiveRecord::TestCase class DefaultScopingTest < ActiveRecord::TestCase - # We are not doing order duplicate removal anymore. coerce_tests! :test_order_in_default_scope_should_not_prevail - end @@ -270,7 +227,6 @@ class DefaultScopingTest < ActiveRecord::TestCase require 'models/post' require 'models/subscriber' class EachTest < ActiveRecord::TestCase - coerce_tests! :test_find_in_batches_should_quote_batch_order def test_find_in_batches_should_quote_batch_order_coerced c = Post.connection @@ -281,7 +237,6 @@ def test_find_in_batches_should_quote_batch_order_coerced end end end - end @@ -295,7 +250,6 @@ class Owner < ActiveRecord::Base } end class EagerAssociationTest < ActiveRecord::TestCase - # Use LEN() vs length() function. coerce_tests! :test_count_with_include def test_count_with_include_coerced @@ -307,7 +261,6 @@ def test_count_with_include_coerced it "including association based on sql condition and no database column coerced" do assert_equal pets(:parrot), Owner.including_last_pet.first.last_pet end - end @@ -315,13 +268,12 @@ def test_count_with_include_coerced require 'models/topic' class FinderTest < ActiveRecord::TestCase - coerce_tests! %r{doesn't have implicit ordering}, :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH. coerce_tests! :test_exists_does_not_select_columns_without_alias def test_exists_does_not_select_columns_without_alias_coerced - assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY/i) do + assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do Topic.exists? end end @@ -334,14 +286,13 @@ def test_string_sanitation_coerced coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced - assert_sql(/OFFSET 0 ROWS FETCH NEXT 3 ROWS ONLY/) { Topic.take(3).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY/) { Topic.first(2).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT 5 ROWS ONLY/) { Topic.last(5).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries } + assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries } end # This fails only when run in the full test suite task. Just taking it out of the mix. coerce_tests! :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct - end @@ -350,7 +301,6 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced module ActiveRecord class Migration class ForeignKeyTest < ActiveRecord::TestCase - # We do not support :restrict. coerce_tests! :test_add_on_delete_restrict_foreign_key def test_add_on_delete_restrict_foreign_key_coerced @@ -361,7 +311,6 @@ def test_add_on_delete_restrict_foreign_key_coerced @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :restrict end end - end end end @@ -370,10 +319,8 @@ def test_add_on_delete_restrict_foreign_key_coerced class HasOneAssociationsTest < ActiveRecord::TestCase - # We use OFFSET/FETCH vs TOP. So we always have an order. coerce_tests! :test_has_one_does_not_use_order_by - end @@ -381,7 +328,6 @@ class HasOneAssociationsTest < ActiveRecord::TestCase require 'models/company' class InheritanceTest < ActiveRecord::TestCase - coerce_tests! :test_a_bad_type_column def test_a_bad_type_column_coerced Company.connection.with_identity_insert_enabled('companies') do @@ -393,11 +339,10 @@ def test_a_bad_type_column_coerced coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced con = Account.connection - assert_sql(/\[companies\]\.\[id\] IN \(1\)/) do + assert_sql(/\[companies\]\.\[id\] = 1/) do Account.all.merge!(:includes => :firm).find(1) end end - end @@ -405,7 +350,6 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced class NamedScopingTest < ActiveRecord::TestCase - # This works now because we add an `order(:id)` sort to break the order tie for deterministic results. coerce_tests! :test_scopes_honor_current_scopes_from_when_defined def test_scopes_honor_current_scopes_from_when_defined_coerced @@ -417,7 +361,6 @@ def test_scopes_honor_current_scopes_from_when_defined_coerced assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id) assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5) end - end @@ -426,7 +369,6 @@ def test_scopes_honor_current_scopes_from_when_defined_coerced require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase - coerce_tests! :test_merge_options def test_merge_options_coerced Developer.where('salary = 80000').scoping do @@ -438,7 +380,6 @@ def test_merge_options_coerced end end end - end @@ -446,7 +387,6 @@ def test_merge_options_coerced require 'models/topic' class PersistenceTest < ActiveRecord::TestCase - # We can not UPDATE identity columns. coerce_tests! :test_update_columns_changing_id @@ -487,7 +427,6 @@ def test_update_attributes_coerced # assert_nothing_raised { topic.reload } # assert_equal topic.title, Topic.find(1234).title end - end @@ -496,15 +435,15 @@ def test_update_attributes_coerced require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase - coerce_tests! :test_registering_new_handlers def test_registering_new_handlers_coerced - PredicateBuilder.register_handler(Regexp, proc do |column, value| + Topic.predicate_builder.register_handler(Regexp, proc do |column, value| Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source)) end) - assert_match %r{\[topics\]\.\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql + assert_match %r{\[topics\].\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql + ensure + Topic.reset_column_information end - end end @@ -513,14 +452,12 @@ def test_registering_new_handlers_coerced require 'models/task' class QueryCacheTest < ActiveRecord::TestCase - coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays def test_cache_does_not_wrap_string_results_in_arrays_coerced Task.cache do assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end end - end @@ -528,25 +465,14 @@ def test_cache_does_not_wrap_string_results_in_arrays_coerced require 'models/post' class RelationTest < ActiveRecord::TestCase - # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering} # We are not doing order duplicate removal anymore. coerce_tests! :test_order_using_scoping - # Account for our `EXEC sp_executesql...` statements. - coerce_tests! :test_to_sql_on_eager_join - def test_to_sql_on_eager_join_coerced - expected = assert_sql { Post.eager_load(:last_comment).order('comments.id DESC').to_a }.first - actual = Post.eager_load(:last_comment).order('comments.id DESC').to_sql - actual = "EXEC sp_executesql N'#{ActiveRecord::ConnectionAdapters::SQLServer::Utils.quote_string(actual)}'" - assert_equal expected, actual - end - # We are not doing order duplicate removal anymore. coerce_tests! :test_default_scope_order_with_scope_order - end @@ -554,7 +480,6 @@ def test_to_sql_on_eager_join_coerced require 'models/post' class SanitizeTest < ActiveRecord::TestCase - coerce_tests! :test_sanitize_sql_like_example_use_case def test_sanitize_sql_like_example_use_case_coerced searchable_post = Class.new(Post) do @@ -562,18 +487,16 @@ def self.search(term) where("title LIKE ?", sanitize_sql_like(term, '!')) end end - assert_sql(/\(title LIKE N''20!% !_reduction!_!!''\)/) do + assert_sql(/\(title LIKE N'20!% !_reduction!_!!'\)/) do searchable_post.search("20% _reduction_!").to_a end end - end class SchemaDumperTest < ActiveRecord::TestCase - # We have precision to 38. coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced @@ -590,27 +513,22 @@ 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(/add_index.*company_partial_index/).first.strip - assert_equal 'add_index "companies", ["firm_id", "type"], name: "company_partial_index", where: "([rating]>(10))"', index_definition + 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 - end class SchemaDumperDefaultsTest < ActiveRecord::TestCase - # These date formats do not match ours. We got these covered in our dumper tests. coerce_tests! :test_schema_dump_defaults_with_universally_supported_types - end class TestAdapterWithInvalidConnection < ActiveRecord::TestCase - # We trust Rails on this since we do not want to install mysql. coerce_tests! %r{inspect on Model class does not raise} - end @@ -618,7 +536,6 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase require 'models/topic' class TransactionTest < ActiveRecord::TestCase - coerce_tests! :test_releasing_named_savepoints def test_releasing_named_savepoints_coerced Topic.transaction do @@ -628,7 +545,6 @@ def test_releasing_named_savepoints_coerced Topic.connection.release_savepoint("another") end end - end @@ -636,7 +552,6 @@ def test_releasing_named_savepoints_coerced 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`. coerce_tests! %r{read committed} @@ -653,19 +568,38 @@ class TransactionIsolationTest < ActiveRecord::TestCase # I really need some help understanding this one. coerce_tests! %r{repeatable read} - end + + require 'models/book' -class Paperback < ActiveRecord::Base; end class ViewWithPrimaryKeyTest < ActiveRecord::TestCase - # We have a few fews and test is poor equality based. + # We have a few view tables. use includes vs equality. coerce_tests! :test_views - # This is deprecated and we dont care in thsi version to pass it. + def test_views_coerced + assert_includes @connection.views, Ebook.table_name + end + + # This is deprecated and we dont care in this version to pass it. coerce_tests! :test_table_exists + # We do better than ActiveRecord and find the views PK. coerce_tests! :test_does_not_assume_id_column_as_primary_key + def test_does_not_assume_id_column_as_primary_key_coerced + model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" } + assert_equal 'id', model.primary_key + end +end +class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase + # We have a few view tables. use includes vs equality. + coerce_tests! :test_views + def test_views_coerced + assert_includes @connection.views, Paperback.table_name + end + + # This is deprecated and we dont care in this version to pass it. + coerce_tests! :test_table_exists end @@ -673,7 +607,6 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase 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 author = Author.select('authors.*, 5 as posts_count').first @@ -681,6 +614,4 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced assert_equal 5, author.posts_count assert_equal 5, dumped.posts_count end - end - diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 042bba2e2..c4d0b081d 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,6 +1,7 @@ require 'support/paths_sqlserver' require 'bundler/setup' Bundler.require :default, :development +require 'pry' require 'support/minitest_sqlserver' require 'cases/helper' require 'support/load_schema_sqlserver' diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 346321539..03ea893e1 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -25,7 +25,7 @@ def coerce_all_tests! undef_method(method) once = true end - STDOUT.puts "Info: Undefined all tests: #{self.name}" + STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{self.name}" end private @@ -35,9 +35,9 @@ def coerced_test_warning(method) Array(method).each do |m| result = undef_method(m) if m && method_defined?(m) if result.blank? - STDOUT.puts "Warning: Unfound coerced test: #{self.name}##{m}" + STDOUT.puts "🐳 Unfound coerced test: #{self.name}##{m}" else - STDOUT.puts "Info: Undefined coerced test: #{self.name}##{m}" + STDOUT.puts "🐵 Undefined coerced test: #{self.name}##{m}" end end end From a74f7892f2680f688bd322cddaf5b242632b1d7b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Jan 2017 13:11:59 -0500 Subject: [PATCH 0539/1412] [Rails5] Fix `test_column_names_are_escaped_coerced` case. --- test/cases/coerced_tests.rb | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f0247cdd3..85bfd871c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -35,11 +35,8 @@ def test_typecast_attribute_from_select_to_true_coerced class BasicsTest < ActiveRecord::TestCase coerce_tests! :test_column_names_are_escaped def test_column_names_are_escaped_coerced - conn = ActiveRecord::Base.connection - classname = conn.class.name[/[^:]*$/] - badchar = "'" - quoted = conn.quote_column_name "foo#{badchar}bar" - assert_equal "[foo'bar]", quoted + conn = ActiveRecord::Base.connection + assert_equal '[t]]]', conn.quote_column_name('t]') end # PENDING: [Rails5.x] Remove coerced tests and use simple symbol types.. From 61636a38e24e56bd726a209e6447270e1c8dfed9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 3 Jan 2017 07:17:56 -0500 Subject: [PATCH 0540/1412] TODO - BIGINT PK support. [ci skip] --- TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/TODO.md b/TODO.md index f62834fc3..9b5bcbf43 100644 --- a/TODO.md +++ b/TODO.md @@ -17,6 +17,7 @@ After we get some tests passing http://michaeljswart.com/2014/12/materialized-views-in-sql-server/ https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server +* BIGINT PK support. https://github.com/rails/rails/pull/26266 #### Does Find By SQL Work? From ef279ce87462aa226e1147eff0057008a6b3dce5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 3 Jan 2017 09:47:40 -0500 Subject: [PATCH 0541/1412] TODO - Can we use `OPTIMIZE FOR UNKNOWN` [ci skip] --- TODO.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/TODO.md b/TODO.md index 9b5bcbf43..867f4b132 100644 --- a/TODO.md +++ b/TODO.md @@ -18,6 +18,10 @@ After we get some tests passing https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server * BIGINT PK support. https://github.com/rails/rails/pull/26266 +* Can we use `OPTIMIZE FOR UNKNOWN` + http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx + http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast + https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ #### Does Find By SQL Work? From 4b341efce76c03af56b785a690f1ab6d59ec6127 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 2 Jan 2017 13:15:56 -0500 Subject: [PATCH 0542/1412] [Rails5] Fix arity of all type initialize methods. --- .../connection_adapters/sqlserver/type/datetime.rb | 5 +++++ .../connection_adapters/sqlserver/type/money.rb | 2 +- .../connection_adapters/sqlserver/type/small_money.rb | 2 +- .../connection_adapters/sqlserver/type/unicode_varchar.rb | 2 +- .../sqlserver/type/unicode_varchar_max.rb | 2 +- .../connection_adapters/sqlserver/type/varbinary.rb | 2 +- .../connection_adapters/sqlserver/type/varbinary_max.rb | 2 +- .../connection_adapters/sqlserver/type/varchar.rb | 2 +- .../connection_adapters/sqlserver/type/varchar_max.rb | 2 +- 9 files changed, 13 insertions(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index b6cd2f208..eaef14901 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,6 +6,11 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional + def initialize(*args) + super + @precision = nil if self.class == DateTime + end + def sqlserver_type 'datetime'.freeze end diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index e0c93d02a..8d6eed882 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Money < Decimal - def initialize(options = {}) + def initialize(*args) super @precision = 19 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 0ba487895..5c4dfd49d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class SmallMoney < Money - def initialize(options = {}) + def initialize(*args) super @precision = 10 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 008320c79..8bcaab4b5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarchar < UnicodeChar - def initialize(options = {}) + def initialize(*args) super @limit = 4000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index 5f6990a0b..df6b455a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarcharMax < UnicodeVarchar - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index 265af265b..dffe7ee61 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varbinary < Binary - def initialize(options = {}) + def initialize(*args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index a251c268a..01087c616 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarbinaryMax < Varbinary - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index b3091718a..0e50a17e1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varchar < Char - def initialize(options = {}) + def initialize(*args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 79fa56464..6d063b5f8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarcharMax < Varchar - def initialize(options = {}) + def initialize(*args) super @limit = 2_147_483_647 end From 2578f0b3659c2e7162670bc31539bc3afedcdc19 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 4 Jan 2017 21:23:06 -0500 Subject: [PATCH 0543/1412] [Rails5] Support for `supports_datetime_with_precision`. --- CHANGELOG.md | 2 +- .../sqlserver/schema_statements.rb | 14 +++ .../sqlserver/table_definition.rb | 86 ++++++++++++------- .../sqlserver/type/datetime.rb | 5 -- .../sqlserver/type/time_value_fractional.rb | 33 +++++-- .../connection_adapters/sqlserver_adapter.rb | 11 ++- test/cases/coerced_tests.rb | 26 ++++++ test/cases/column_test_sqlserver.rb | 71 +++++++++++---- 8 files changed, 179 insertions(+), 69 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a421fb6cd..7f1d0578c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ #### Added -* ... +* Support for `supports_datetime_with_precision`. #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 492345221..a05d17581 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -188,6 +188,16 @@ def type_to_sql(type, limit = nil, precision = nil, scale = nil) when 5..8 then 'bigint' else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.") end + when 'datetime2' + column_type_sql = super + if precision + if (0..7) === precision + column_type_sql << "(#{precision})" + else + raise(ActiveRecordError, "The dattime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7") + end + end + column_type_sql else super end @@ -202,6 +212,10 @@ def columns_for_distinct(columns, orders) [super, *order_columns].join(', ') end + def update_table_definition(table_name, base) + SQLServer::Table.new(table_name, base) + end + def change_column_null(table_name, column_name, allow_null, default = nil) table_id = SQLServer::Utils.extract_identifiers(table_name) column_id = SQLServer::Utils.extract_identifiers(column_name) diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index fd2f6eb1d..730e48c40 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -1,7 +1,8 @@ module ActiveRecord module ConnectionAdapters module SQLServer - class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + + module ColumnMethods def primary_key(name, type = :primary_key, **options) return super unless type == :uuid @@ -10,67 +11,90 @@ def primary_key(name, type = :primary_key, **options) column name, type, options end - def real(name, options = {}) - column(name, :real, options) + def real(*args, **options) + args.each { |name| column(name, :real, options) } + end + + def money(*args, **options) + args.each { |name| column(name, :money, options) } end - def money(name, options = {}) - column(name, :money, options) + def datetime(*args, **options) + args.each do |name| + if options[:precision] + datetime2(name, options) + else + column(name, :datetime, options) + end + end end - def datetime2(name, options = {}) - column(name, :datetime2, options) + def datetime2(*args, **options) + args.each { |name| column(name, :datetime2, options) } end - def datetimeoffset(name, options = {}) - column(name, :datetimeoffset, options) + def datetimeoffset(*args, **options) + args.each { |name| column(name, :datetimeoffset, options) } end - def smallmoney(name, options = {}) - column(name, :smallmoney, options) + def smallmoney(*args, **options) + args.each { |name| column(name, :smallmoney, options) } end - def char(name, options = {}) - column(name, :char, options) + def char(*args, **options) + args.each { |name| column(name, :char, options) } end - def varchar(name, options = {}) - column(name, :varchar, options) + def varchar(*args, **options) + args.each { |name| column(name, :varchar, options) } end - def varchar_max(name, options = {}) - column(name, :varchar_max, options) + def varchar_max(*args, **options) + args.each { |name| column(name, :varchar_max, options) } end - def text_basic(name, options = {}) - column(name, :text_basic, options) + def text_basic(*args, **options) + args.each { |name| column(name, :text_basic, options) } end - def nchar(name, options = {}) - column(name, :nchar, options) + def nchar(*args, **options) + args.each { |name| column(name, :nchar, options) } end - def ntext(name, options = {}) - column(name, :ntext, options) + def ntext(*args, **options) + args.each { |name| column(name, :ntext, options) } end - def binary_basic(name, options = {}) - column(name, :binary_basic, options) + def binary_basic(*args, **options) + args.each { |name| column(name, :binary_basic, options) } end - def varbinary(name, options = {}) - column(name, :varbinary, options) + def varbinary(*args, **options) + args.each { |name| column(name, :varbinary, options) } end - def uuid(name, options = {}) - column(name, :uniqueidentifier, options) + def uuid(*args, **options) + args.each { |name| column(name, :uniqueidentifier, options) } end - def ss_timestamp(name, options = {}) - column(name, :ss_timestamp, options) + def ss_timestamp(*args, **options) + args.each { |name| column(name, :ss_timestamp, options) } end end + + class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition + include ColumnMethods + + def new_column_definition(name, type, options) + type = :datetime2 if type == :datetime && options[:precision] + super name, type, options + end + end + + class Table < ActiveRecord::ConnectionAdapters::Table + include ColumnMethods + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index eaef14901..b6cd2f208 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -6,11 +6,6 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional - def initialize(*args) - super - @precision = nil if self.class == DateTime - end - def sqlserver_type 'datetime'.freeze 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 d8a2ff1cd..412958795 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 @@ -9,18 +9,20 @@ module TimeValueFractional def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? - frac_seconds = if fractional_scale == 0 - 0 - else - seconds = value.send(fractional_property).to_f / fractional_operator.to_f - seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) - (seconds * fractional_operator).to_i - end - value.change fractional_property => frac_seconds + value.change fractional_property => seconds_precision(value) + end + + def seconds_precision(value) + return 0 if fractional_scale == 0 + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + (seconds * fractional_operator).round(0).to_i end def quote_fractional(value) - seconds = (value.send(fractional_property).to_f / fractional_operator.to_f).round(fractional_scale) + return 0 if fractional_scale == 0 + frac_seconds = seconds_precision(value) + seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) seconds.to_d.to_s.split('.').last.to(fractional_scale-1) end @@ -52,6 +54,11 @@ module TimeValueFractional2 private + def seconds_precision(value) + seconds = super + seconds > fractional_max ? fractional_scale_max : seconds + end + def fractional_property :nsec end @@ -68,6 +75,14 @@ def fractional_scale precision end + def fractional_max + 999999999 + end + + def fractional_scale_max + ('9' * fractional_scale) + ('0' * (fractional_digits - fractional_scale)) + end + end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index aa3171a72..e7f0944a8 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -128,7 +128,7 @@ def supports_views? end def supports_datetime_with_precision? - false + true end def supports_json? @@ -268,10 +268,13 @@ def initialize_type_map(m) m.register_type 'real', SQLServer::Type::Real.new # Date and Time m.register_type 'date', SQLServer::Type::Date.new - m.register_type 'datetime', SQLServer::Type::DateTime.new - m.register_type %r{\Adatetime2}i do |sql_type| + m.register_type %r{\Adatetime} do |sql_type| precision = extract_precision(sql_type) - SQLServer::Type::DateTime2.new precision: precision + if precision + SQLServer::Type::DateTime2.new precision: precision + else + SQLServer::Type::DateTime.new + end end m.register_type %r{\Adatetimeoffset}i do |sql_type| precision = extract_precision(sql_type) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 85bfd871c..b63826da0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -612,3 +612,29 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced assert_equal 5, dumped.posts_count end end + + + + +class DateTimePrecisionTest < ActiveRecord::TestCase + # Original test had `7` which we support vs `8` which we use. + coerce_tests! :test_invalid_datetime_precision_raises_error + def test_invalid_datetime_precision_raises_error_coerced + assert_raises ActiveRecord::ActiveRecordError do + @connection.create_table(:foos, force: true) do |t| + t.timestamps precision: 8 + end + end + end + + # Original test uses `datetime` vs `datetime2` type which we dynamically use. + coerce_tests! :test_schema_dump_includes_datetime_precision + def test_schema_dump_includes_datetime_precision_coerced + @connection.create_table(:foos, force: true) do |t| + t.timestamps precision: 6 + end + output = dump_table_schema("foos") + assert_match %r{t\.datetime2\s+"created_at",\s+precision: 6,\s+null: false$}, output + assert_match %r{t\.datetime2\s+"updated_at",\s+precision: 6,\s+null: false$}, output + end +end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index d3a2e217e..661288029 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -309,26 +309,43 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'datetime' col.type.must_equal :datetime col.null.must_equal true - col.default.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{col.default.usec}> vs <123000>" - obj.datetime.must_equal Time.utc(1753, 01, 01, 00, 00, 00, 123000), "Microseconds were <#{obj.datetime.usec}> vs <123000>" + time = Time.utc 1753, 01, 01, 00, 00, 00, 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 = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime type.limit.must_be_nil type.precision.must_be_nil type.scale.must_be_nil + obj.save! + obj.must_equal obj.class.where(datetime: time).first # Can save to proper accuracy and return again. - obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 3000) - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.datetime.usec}> vs <3000>" + time = Time.utc 2010, 04, 01, 12, 34, 56, 3000 + obj.datetime = time + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.reload - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 3000), "Microseconds were <#{obj.reload.datetime.usec}> vs <3000>" + obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + obj.must_equal obj.class.where(datetime: time).first # Will cast to true DB value on attribute write, save and return again. - obj.datetime = Time.utc(2010, 04, 01, 12, 34, 56, 234567) - obj.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.datetime.usec}> vs <233000>" + time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 + time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 + obj.datetime = time + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.save! + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.reload + obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + obj.must_equal obj.class.where(datetime: time).first + obj.must_equal obj.class.where(datetime: time2).first + # Set and find nil. + obj.datetime = nil + obj.datetime.must_be_nil obj.save! - obj.reload.datetime.must_equal Time.utc(2010, 04, 01, 12, 34, 56, 233000), "Microseconds were <#{obj.reload.datetime.usec}> vs <233000>" + obj.datetime.must_be_nil + obj.must_equal obj.class.where(datetime: nil).first end it 'datetime2' do @@ -337,27 +354,38 @@ def assert_obj_set_and_save(attribute, value) col.sql_type.must_equal 'datetime2(7)' col.type.must_equal :datetime2 col.null.must_equal true - col.default.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <999999900>" - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(999999900, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" + 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_function.must_be_nil type = connection.lookup_cast_type_from_column(col) type.must_be_instance_of Type::DateTime2 type.limit.must_be_nil type.precision.must_equal 7 type.scale.must_be_nil + obj.save! + obj.must_equal obj.class.where(datetime2_7: time).first # Can save 100 nanosecond precisoins and return again. - obj.datetime2_7 = Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456755, 1000)) - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) + time2 = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456800, 1000) + obj.datetime2_7 = time + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.reload - obj.datetime2_7.must_equal Time.utc(9999, 12, 31, 23, 59, 59, Rational(123456800, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + obj.must_equal obj.class.where(datetime2_7: time).first + obj.must_equal obj.class.where(datetime2_7: time2).first # Can save small fraction nanosecond precisoins and return again. - obj.datetime2_7 = Time.utc(2008, 6, 21, 13, 30, 0, Rational(15020, 1000)) - obj.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) + time2 = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15000, 1000) + obj.datetime2_7 = time + obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" obj.save! - obj.reload.datetime2_7.must_equal Time.utc(2008, 6, 21, 13, 30, 0, Rational(15000, 1000)), "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" - # With other precisions. + obj.reload.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + obj.must_equal obj.class.where(datetime2_7: time).first + obj.must_equal obj.class.where(datetime2_7: time2).first + # datetime2_3 time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column('datetime2_3') connection.lookup_cast_type_from_column(col).precision.must_equal 3 @@ -365,12 +393,16 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" 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 col = column('datetime2_1') connection.lookup_cast_type_from_column(col).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.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 col = column('datetime2_0') connection.lookup_cast_type_from_column(col).precision.must_equal 0 time = Time.utc 2016, 4, 19, 16, 45, 40, 771036 @@ -378,6 +410,7 @@ def assert_obj_set_and_save(attribute, value) obj.datetime2_0.must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" 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 From 3298a811c2de58b3ce967abd545cef1580c70997 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 5 Jan 2017 08:00:18 -0500 Subject: [PATCH 0544/1412] [Rails5] Notes and stuff. [ci skip] --- RAILS5-FAILS.txt | 1346 ++++++++++++++++++++++++++++++++++++++++++++++ RAILS5-TODO.md | 64 +++ README.md | 15 +- TODO.md | 37 -- 4 files changed, 1420 insertions(+), 42 deletions(-) create mode 100644 RAILS5-FAILS.txt create mode 100644 RAILS5-TODO.md delete mode 100644 TODO.md diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt new file mode 100644 index 000000000..ef5698413 --- /dev/null +++ b/RAILS5-FAILS.txt @@ -0,0 +1,1346 @@ +Run options: --seed=44527 + +# Running: + +..........................................................................................................................................................................................................FF......................................................................................................................................................................................................................F....F...........E..EF.....S..E....................................................................................................................E................................................................................................................................................................F................................................................................................................................................................................................................................................................E.........................................................F.................................................................................................................F.........................................................................E.......................................................................................................................................................................................................................................................................................................E...E...................................................F.........................................................................................................................................F......F....F...........F................F....F...................................................................................................................................................................................................................................................................................................................................E.........................................................S..................................................F...................................................................................................................................................................................................................................................E......E......E..........................................................................................................................E.............E......................F...........................................................................................................................E......F...............................................................................................................................................................E..................F...............F.............F..E...........F....F...FE.....F.......E..................................................................................................................................................................................................................................................................................................................................E........................E.................................E........................................F...E..............E............F..F..........F........................................................................................................E...................................................................................................................................................................................................................................................................................................................................................................F.................................................................................................E.......................................E.......E........................................................................................E...........................................................................................................................................................................E................................................................................E.................................E...................................E..............................................................................................................S.........................................E........................E.............................................................................................................................................................................................E.. + +Finished in 442.623076s, 10.9664 runs/s, 31.5641 assertions/s. + + 1) Failure: +DefaultNumbersTest#test_default_positive_integer [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/defaults_test.rb:43]: +Expected: "7" + Actual: 7 + + + 2) Failure: +DefaultNumbersTest#test_default_negative_integer [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/defaults_test.rb:49]: +Expected: "-5" + Actual: -5 + + + 3) Failure: +UniquenessValidationTest#test_validate_uniqueness_with_limit [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:376]: +ActiveRecord::ValueTooLong expected but nothing was raised. + + + 4) Failure: +UniquenessValidationTest#test_validate_uniqueness_with_limit_and_utf8 [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:395]: +ActiveRecord::ValueTooLong expected but nothing was raised. + + + 5) Error: +UniquenessValidationTest#test_validates_uniqueness_with_newline_chars: +ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''new +line'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'new +line', @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:135:in `test_validates_uniqueness_with_newline_chars' + + + 6) Error: +UniquenessValidationTest#test_validate_case_insensitive_uniqueness_with_special_sql_like_chars: +ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:299:in `test_validate_case_insensitive_uniqueness_with_special_sql_like_chars' + + + 7) Failure: +UniquenessValidationTest#test_validate_case_sensitive_uniqueness [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:318]: +Should be valid + + + 8) Error: +UniquenessValidationTest#test_validate_case_insensitive_uniqueness: +ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:245:in `test_validate_case_insensitive_uniqueness' + + + 9) Error: +ActiveRecord::ConnectionAdapters::ConnectionHandlerTest#test_establish_connection_uses_spec_name: +Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_adapters/connection_handler_test.rb:16:in `test_establish_connection_uses_spec_name' + + + 10) Failure: +DirtyTest#test_time_attributes_changes_with_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:80]: +Expected 2017-01-05 01:55:43 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. + + + 11) Error: +DefaultScopingTest#test_unscope_string_where_clauses_involved: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-05-2016 01:56:15.689'') ORDER BY salary DESC + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/delegation.rb:38:in `collect' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:178:in `test_unscope_string_where_clauses_involved' + + + 12) Failure: +DefaultScopingTest#test_with_abstract_class_scope_should_be_executed_in_correct_context [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:497]: +Expected /"lions"."is_vegetarian"/ to match "SELECT [lions].* FROM [lions] WHERE [lions].[is_vegetarian] = 0". + + + 13) Failure: +CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:95]: +Expected: 1 + Actual: 3 + + + 14) Error: +CalculationsTest#test_having_with_strong_parameters: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [accounts].* FROM [accounts] GROUP BY [accounts].[id] HAVING [accounts].[credit_limit] = @0', N'@0 int', @0 = 50 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/delegation.rb:38:in `[]' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/calculations_test.rb:789:in `test_having_with_strong_parameters' + + + 15) Error: +MultipleDbTest#test_associations_should_work_when_model_has_no_connection: +ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:80:in `visit_Arel_Table' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:94:in `visit_Arel_Nodes_JoinSource' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:67:in `block in visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `inject' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `visit_Arel_Nodes_SelectStatement' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:3:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:115:in `block in test_associations_should_work_when_model_has_no_connection' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/test_case.rb:83:in `assert_nothing_raised' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:114:in `test_associations_should_work_when_model_has_no_connection' + + + 16) Error: +MultipleDbTest#test_count_on_custom_connection: +ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:80:in `visit_Arel_Table' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:94:in `visit_Arel_Nodes_JoinSource' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:67:in `block in visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `inject' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `visit_Arel_Nodes_SelectStatement' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:248:in `execute_simple_calculation' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:207:in `perform_calculation' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:121:in `calculate' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:40:in `count' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:13:in `count' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:106:in `test_count_on_custom_connection' + + + 17) Failure: +EachTest#test_in_batches_should_quote_batch_order [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/batches_test.rb:375]: +Query pattern(s) /ORDER BY [posts].[id]/ not found. +Queries: +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 1) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 2 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 2) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 3 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 3) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 4 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 4) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 5 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 5) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 6 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 6) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 7 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 7) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 8 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 8) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 9 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 9) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 10 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 10) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 11 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 +EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 11) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + + + 18) Failure: +MigrationTest#test_add_table_with_decimals [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:181]: +Expected # to be a kind of Integer, not BigDecimal. + + + 19) Failure: +MigrationTest#test_migration_sets_internal_metadata_even_when_fully_migrated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:419]: +Expected "development" to not be equal to "development". + + + 20) Failure: +MigrationTest#test_internal_metadata_stores_environment [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:393]: +Expected "development" to not be equal to "development". + + + 21) Failure: +MigrationTest#test_filtering_migrations [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223]: +[ActiveRecord::StatementInvalid] exception expected, not +Class: +Message: <"undefined method `second' for nil:NilClass"> +---Backtrace--- +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:193:in `primary_Key_From_Table' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:161:in `make_Fetch_Possible_And_Deterministic' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:123:in `visit_Orders_And_Let_Fetch_Happen' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:69:in `visit_Arel_Nodes_SelectStatement' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:3:in `first' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223:in `block in test_filtering_migrations' +--------------- + + + 22) Failure: +SchemaDumperTest#test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:320]: +non-primary key id column not preserved. +Expected /t\.string\s+"id",.*?null: false$/ to match " t.string \"id\", null: false, collation: \"SQL_Latin1_General_CP1_CI_AS\"". + + + 23) Failure: +SchemaDumperTest#test_schema_dump_includes_decimal_options [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:262]: +Expected /precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"/ to match "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 0) do\n\n create_table \"nep_ar_internal_metadata\", primary_key: \"key\", id: :string, collation: \"SQL_Latin1_General_CP1_CI_AS\", force: :cascade do |t|\n t.string \"value\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"created_at\", null: false\n t.datetime \"updated_at\", null: false\n end\n\n create_table \"nodes\", force: :cascade do |t|\n t.integer \"tree_id\"\n t.integer \"parent_id\"\n t.string \"name\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"updated_at\"\n end\n\n create_table \"non_poly_ones\", force: :cascade do |t|\n end\n\n create_table \"non_poly_twos\", force: :cascade do |t|\n end\n\n create_table \"notifications\", force: :cascade do |t|\n t.string \"message\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n end\n\n create_table \"numeric_data\", force: :cascade do |t|\n t.decimal \"bank_balance\", precision: 10, scale: 2\n t.decimal \"big_bank_balance\", precision: 15, scale: 2\n t.decimal \"world_population\", precision: 10, scale: 0\n t.decimal \"my_house_population\", precision: 2, scale: 0\n t.decimal \"decimal_number_with_default\", precision: 3, scale: 2, default: 2.78\n t.float \"temperature\"\n t.decimal \"atoms_in_universe\", precision: 38, scale: 0\n end\n\nend\n". + + + 24) Error: +BasicsTest#test_create_without_prepared_statement: +ActiveRecord::StatementInvalid: TinyTds::Error: Must declare the scalar variable "@0".: SELECT DISTINCT columns.TABLE_NAME AS table_name, columns.COLUMN_NAME AS name, columns.DATA_TYPE AS type, columns.COLUMN_DEFAULT AS default_value, columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, columns.DATETIME_PRECISION AS datetime_precision, columns.COLLATION_NAME AS collation, columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH ELSE COL_LENGTH('.'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) END AS [length], CASE WHEN columns.IS_NULLABLE = 'YES' THEN 1 ELSE NULL END AS [is_nullable], CASE WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1 ELSE NULL END AS [is_primary], c.is_identity AS [is_identity] FROM .INFORMATION_SCHEMA.COLUMNS columns LEFT OUTER JOIN .INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' LEFT OUTER JOIN .INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.COLUMN_NAME = columns.COLUMN_NAME AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA INNER JOIN .sys.schemas AS s ON s.name = columns.TABLE_SCHEMA AND s.schema_id = s.schema_id INNER JOIN .sys.objects AS o ON s.schema_id = o.schema_id AND o.is_ms_shipped = 0 AND o.type IN ('U', 'V') AND o.name = columns.TABLE_NAME INNER JOIN .sys.columns AS c ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME WHERE columns.TABLE_NAME = @0 AND columns.TABLE_SCHEMA = schema_name() ORDER BY columns.ordinal_position + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:331:in `column_definitions' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:57:in `columns' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:489:in `identity_columns' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:477:in `query_requires_identity_insert?' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:23:in `exec_insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:125:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:34:in `create' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:616:in `block in test_create_without_prepared_statement' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:215:in `unprepared_statement' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:615:in `test_create_without_prepared_statement' + + + 25) Failure: +MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: +Expected: 2004-06-24 23:24:00 UTC + Actual: 2004-06-24 16:24:00 UTC + + + 26) Error: +TransactionCallbacksTest#test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record: +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.044915', @3 = '01-05-2017 02:00:21.044915' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:230:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:220:in `block in test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:219:in `test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record' + + + 27) Error: +TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association: +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.183741', @3 = '01-05-2017 02:00:21.183741' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:51:in `create!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:134:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association' + + + 28) Error: +TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record: +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.328166', @3 = '01-05-2017 02:00:21.328166' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:129:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record' + + + 29) Error: +ActiveRecord::ConnectionAdapters::ConnectionSpecification::ResolverTest#test_spec_name_with_inline_config: +Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:12:in `spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:125:in `test_spec_name_with_inline_config' + + + 30) Error: +ActiveRecord::ConnectionAdapters::ConnectionSpecification::ResolverTest#test_spec_name_on_key_lookup: +Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:12:in `spec' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:120:in `test_spec_name_on_key_lookup' + + + 31) Failure: +ActiveRecord::ConnectionAdapters::QuoteBooleanTest#test_quote_returns_frozen_string [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/quoting_test.rb:159]: +Expected "1" to be frozen?. + + + 32) Error: +ActiveRecord::Migration::ColumnsTest#test_rename_column_using_symbol_arguments: +ActiveRecord::ActiveRecordError: No such column: test_models.first_name + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:432:in `detect_column_for!' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:139:in `rename_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `rename_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:33:in `test_rename_column_using_symbol_arguments' + + + 33) Failure: +ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: +Expected false to be truthy. + + + 34) Error: +LeftOuterJoinAssociationTest#test_does_not_override_select: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC sp_executesql N'SELECT authors.name, (authors.author_address_id || '' '' || authors.author_address_extra_id) as addr_id FROM [authors] LEFT OUTER JOIN [posts] ON [posts].[author_id] = [authors].[id] ORDER BY [authors].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/left_outer_join_association_test.rb:78:in `test_does_not_override_select' + + + 35) Failure: +AttributeMethodsTest#test_read_attributes_after_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:235]: +--- expected ++++ actual +@@ -1 +1 @@ +-Thu, 24 Mar 2011 00:00:00 PDT -07:00 ++2011-03-24 00:00:00 UTC + + + + 36) Failure: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_in_other_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:614]: +--- expected ++++ actual +@@ -1 +1 @@ +-#> ++#> + + + + 37) Failure: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:669]: +--- expected ++++ actual +@@ -1 +1 @@ +-Tue, 01 Jan 2008 00:00:00 SST -11:00 ++2008-01-01 00:00:00 UTC + + + + 38) Error: +AttributeMethodsTest#test_setting_time_zone_aware_read_attribute: +NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:625:in `block in test_setting_time_zone_aware_read_attribute' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:622:in `test_setting_time_zone_aware_read_attribute' + + + 39) Failure: +AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: +--- expected ++++ actual +@@ -1 +1 @@ +-Sat, 01 Jan 2000 10:00:00 PST -08:00 ++Sat, 01 Jan 2000 02:00:00 PST -08:00 + + + + 40) Failure: +AttributeMethodsTest#test_time_attributes_are_retrieved_in_current_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:590]: +Expected 2008-01-01 00:00:00 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. + + + 41) Failure: +AttributeMethodsTest#test_time_zone_aware_attribute_saved [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:650]: +--- expected ++++ actual +@@ -1 +1 @@ +-Mon, 20 Feb 2012 09:00:00 CET +01:00 ++2012-02-20 09:00:00 UTC + + + + 42) Error: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_with_string: +NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:638:in `block (2 levels) in test_setting_time_zone_aware_attribute_with_string' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:634:in `block in test_setting_time_zone_aware_attribute_with_string' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:632:in `test_setting_time_zone_aware_attribute_with_string' + + + 43) Failure: +AttributeMethodsTest#test_read_attributes_before_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:219]: +--- expected ++++ actual +@@ -1 +1 @@ +-Sun, 11 Oct 2009 12:13:14 PDT -07:00 ++2009-10-11 12:13:14 UTC + + + + 44) Error: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_to_utc: +NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:602:in `block in test_setting_time_zone_aware_attribute_to_utc' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:597:in `test_setting_time_zone_aware_attribute_to_utc' + + + 45) Error: +FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE (written_on = ''''07-16-2003 14:28:11.223'''') ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:838:in `block (2 levels) in test_condition_local_time_interpolation_with_default_timezone_utc' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:82:in `with_timezone_config' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:836:in `block in test_condition_local_time_interpolation_with_default_timezone_utc' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:61:in `with_env_tz' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:835:in `test_condition_local_time_interpolation_with_default_timezone_utc' + + + 46) Error: +FinderTest#test_exists_with_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' + + + 47) Error: +FinderTest#test_exists_with_distinct_association_includes_limit_and_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' + + + 48) Failure: +FinderTest#test_hash_condition_find_malformed [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:752]: +[ActiveRecord::StatementInvalid] exception expected, not +Class: +Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> +---Backtrace--- +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:254:in `sp_executesql_sql_type' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:240:in `block in sp_executesql_types_and_parameters' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each_with_index' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `sp_executesql_types_and_parameters' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' +/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:753:in `block in test_hash_condition_find_malformed' +--------------- + + + 49) Error: +FinderTest#test_condition_utc_time_interpolation_with_default_timezone_local: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE (written_on = ''''07-16-2003 14:28:11.223'''') ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:820:in `block (2 levels) in test_condition_utc_time_interpolation_with_default_timezone_local' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:82:in `with_timezone_config' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:818:in `block in test_condition_utc_time_interpolation_with_default_timezone_local' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:61:in `with_env_tz' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:817:in `test_condition_utc_time_interpolation_with_default_timezone_local' + + + 50) Error: +FinderTest#test_exists_with_distinct_association_includes_and_limit: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:220:in `test_exists_with_distinct_association_includes_and_limit' + + + 51) Failure: +ActiveRecord::AdapterTest#test_value_limit_violations_are_translated_to_specific_exception [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/adapter_test.rb:217]: +ActiveRecord::ValueTooLong expected but nothing was raised. + + + 52) Failure: +ActiveRecord::AdapterTest#test_table_exists_checking_both_tables_and_views_is_deprecated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/adapter_test.rb:51]: +Expected a deprecation warning within the block but received none + + + 53) Failure: +ActiveRecord::AdapterTest#test_passing_arguments_to_tables_is_deprecated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/adapter_test.rb:291]: +Expected a deprecation warning within the block but received none + + + 54) Error: +EagerAssociationTest#test_0024_including association based on sql condition and no database column coerced: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near 'limit'.: EXEC sp_executesql N'SELECT + owners.*, ( + select p.pet_id from pets p + where p.owner_id = owners.owner_id + order by p.name desc + limit 1 + ) as last_pet_id + FROM [owners] ORDER BY [owners].[owner_id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:259:in `block in ' + + + 55) Failure: +HasManyAssociationsTest#test_do_not_call_callbacks_for_delete_all [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_many_associations_test.rb:248]: +bulbs should have been deleted using :delete_all strategy. +Expected: 0 + Actual: 1 + + + 56) Error: +HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 54, @2 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' + + + 57) Error: +HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' + + + 58) Error: +HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' + + + 59) Error: +DateTest#test_date_with_string_value: +ArgumentError: wrong number of arguments (given 1, expected 0) + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `to_s' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `serialize' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute.rb:51:in `value_for_database' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/query_attribute.rb:11:in `value_for_database' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:259:in `sp_executesql_sql_param' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:241:in `block in sp_executesql_types_and_parameters' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each_with_index' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `sp_executesql_types_and_parameters' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/statement_cache.rb:109:in `execute' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/core.rb:203:in `find_by' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/date_test.rb:14:in `test_date_with_string_value' + + + 60) Error: +RelationTest#test_reverse_order_with_function: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' + + + 61) Error: +RelationTest#test_multiple_where_and_having_clauses: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + + + 62) Error: +RelationTest#test_having_with_binds_for_both_where_and_having: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + + + 63) Error: +RelationTest#test_reverse_order_with_function_other_predicates: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' + + + 64) Error: +NamedScopingTest#test_method_missing_priority_when_delegating: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-05-2017 02:02:11.789'') AND (written_on >= ''01-04-2017 02:02:11.799'') + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' + + + 65) Error: +NamedScopingTest#test_nested_scopes_queries_size: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-05-2017 02:02:12.476'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' + + + 66) Error: +ActiveRecord::CollectionCacheKeyTest#test_0011_cache_key for queries with offset which return 0 rows: +ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT COUNT(*) AS [size], MAX([developers].[updated_at]) AS timestamp FROM [developers] ORDER BY [developers].[id] ASC OFFSET @0 ROWS', N'@0 int', @0 = 20 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:48:in `select_one' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/collection_cache_key.rb:21:in `collection_cache_key' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:337:in `cache_key' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/collection_cache_key_test.rb:80:in `block in ' + +4854 runs, 13971 assertions, 30 failures, 36 errors, 3 skips + +* 10 - AttributeMethodsTest +* 6 - FinderTest +* 6 - Gem `sqlite3` load error. +* 6 - UniquenessValidationTest +* 4 - RelationTest +* 4 - MigrationTest +* 4 - (HasMany|HasAndBelongs)AssociationsTest +* 3 - AdapterTest +* 3 - TransactionCallbacksTest +* 2 - NamedScopingTest +* 2 - DefaultNumbersTest +* 2 - DefaultScopingTest +* 2 - CalculationsTest +* 2 - MultipleDbTest +* 2 - EachTest +* 2 - ColumnsTest +* 2 - SchemaDumperTest +* 1 - BasicsTest +* 1 - MultiParameterAttributeTest +* 1 - QuoteBooleanTest +* 1 - LeftOuterJoinAssociationTest +* 1 - EagerAssociationTest +* 1 - DateTest + diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md new file mode 100644 index 000000000..ee36dc72c --- /dev/null +++ b/RAILS5-TODO.md @@ -0,0 +1,64 @@ + +## SHORT TERM + +Misc remidners while in the heat of adapting the adpater. + + +## TEST FAILURE SUMMARY + +I am currently using `TESTOPTS="--seed=44527"` to get a consistant order for now. Also, the CI suite is locked down to using `ONLY_SQLSERVER=1` to make PRs feel good. Soon we will want to change this. + +* 10 - AttributeMethodsTest +* 6 - FinderTest +* 6 - Gem `sqlite3` load error. +* 6 - UniquenessValidationTest +* 4 - RelationTest +* 4 - MigrationTest +* 4 - (HasMany|HasAndBelongs)AssociationsTest +* 3 - AdapterTest +* 3 - TransactionCallbacksTest +* 2 - NamedScopingTest +* 2 - DefaultNumbersTest +* 2 - DefaultScopingTest +* 2 - CalculationsTest +* 2 - MultipleDbTest +* 2 - EachTest +* 2 - ColumnsTest +* 2 - SchemaDumperTest +* 1 - BasicsTest +* 1 - MultiParameterAttributeTest +* 1 - QuoteBooleanTest +* 1 - LeftOuterJoinAssociationTest +* 1 - EagerAssociationTest +* 1 - DateTest + + +## LONG TERM + +After we get some tests passing + +* Check `sql_for_insert` can do without the table regular expresion. +* Do we need the `query_requires_identity_insert` check in `execute`? +* Does the schema cache serialize properly since we conform to that now? +* What does `supports_materialized_views?` means for SQL Server + - http://michaeljswart.com/2014/12/materialized-views-in-sql-server/ + - https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ + - http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server +* BIGINT PK support. https://github.com/rails/rails/pull/26266 +* Can we use `OPTIMIZE FOR UNKNOWN` + - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx + - http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast + - https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ + + +#### Does Find By SQL Work? + +With binds and prepareable? + +```ruby +# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] +# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }] +# +def find_by_sql(sql, binds = [], preparable: nil) + result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable) +``` diff --git a/README.md b/README.md index c85cadce5..630d9f223 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,18 @@ ## RAILS v5 COMING!!! -The work for Rails v5 started a on 2016-07-03 and none of the work landed in master yet. The changes for adapters from v4.2 to v5.0 is one of the most dramatic I have seen and Rails 5 compatibility will take several more weeks till it is ready. +2017-01-04 - All core adapter tests are passing on Rails v5. We have ~60 failures under the full AcitveRecord suite. At this time, the foundation of the adapter is considered to be solid and the remaining tests will not likely require any refactors of the adapter internals. -2016-07-14 UPDATE: https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/rails5 +This means you can technically bundle to the adapter's master branch and work with Rails 5. However, but reports should not be given till we have a release and pass all ActiveRecord tests. Please follow the `RAILS5-TODO.md` and `RAILS5-FAILS.txt` notes before filing issues. + +* [RAILS5-TODO.md](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/RAILS5-TODO.md) +* [RAILS5-FAILS.txt](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/RAILS5-FAILS.txt) +* [All Rails 5.0 Issues - Closed](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues?q=label%3A%22Rails+5.0%22+is%3Aclosed) +* [All Rails 5.0 Issues - Open](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/labels/Rails%205.0) + +* **Can I help?** - Thanks so much! Feel free to jump in. I suggest chatting on Gitter or GitHub to make sure we are not dupliating work. +* **What is the ETA of an initial release?** - My guess is that we can have an initial release by mid January easily. -* **Can I help?** - Thanks so much! But no, not yet. There are some foundational changes coming to master in the next few weeks. Having multiple people involved at this stage is counter productive. Please stay tuned here for when that may change. -* **Why is it taking so long?** - I spent the last several months trying to make TinyTDS/FreeTDS strong vs working on the adapter. If you did not know, the FreeTDS finally hit a v1.0 release which has been in the works for several years. It is a major achievement by that team. I thought it was more important to get the low level connection strong before doing the adapter work. We will get there soon. -* **What branch you working on?** - Right now I am on the [rails5](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/rails5) branch. #### Using Rails v4 diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 867f4b132..000000000 --- a/TODO.md +++ /dev/null @@ -1,37 +0,0 @@ - -## SHORT TERM - -Misc remidners while in the heat of adapting the adpater. - -* Review coerced tests. - -## LONG TERM - -After we get some tests passing - -* Is `primary_keys(table_name)` performant? Contribute to rails for abstract adapter. -* Check `sql_for_insert` can do without the table regular expresion. -* Do we need the `query_requires_identity_insert` check in `execute`? -* Does the schema cache serialize properly since we conform to that now? -* What does `supports_materialized_views?` means for SQL Server - http://michaeljswart.com/2014/12/materialized-views-in-sql-server/ - https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ - http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server -* BIGINT PK support. https://github.com/rails/rails/pull/26266 -* Can we use `OPTIMIZE FOR UNKNOWN` - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx - http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast - https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ - - -#### Does Find By SQL Work? - -With binds and prepareable? - -```ruby -# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] -# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }] -# -def find_by_sql(sql, binds = [], preparable: nil) - result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable) -``` From 9cb96d781b2c201d3f9dca4c495bd22158b7612f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 18:16:34 -0500 Subject: [PATCH 0545/1412] Fix tests which require sqlite3. --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 41fc1e351..06bbd62b6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,6 +1,7 @@ source 'https://rubygems.org' gemspec +gem 'sqlite3' gem 'minitest', '~> 5.9.0' gem 'bcrypt', platforms: [:mri] gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] From dc44fa24d138a56c856ca0c03013c4a1dffb516a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 18:30:04 -0500 Subject: [PATCH 0546/1412] [Rails5] Coerce DefaultNumbersTest for before type cast. --- test/cases/coerced_tests.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b63826da0..573207bb4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -638,3 +638,22 @@ def test_schema_dump_includes_datetime_precision_coerced assert_match %r{t\.datetime2\s+"updated_at",\s+precision: 6,\s+null: false$}, output end end + + + + +class DefaultNumbersTest < ActiveRecord::TestCase + # We do better with native types and do not return strings for everything. + coerce_tests! :test_default_positive_integer + def test_default_positive_integer_coerced + record = DefaultNumber.new + assert_equal 7, record.positive_integer + assert_equal 7, record.positive_integer_before_type_cast + end + 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 + end +end From 18e4f2049de2f7efc7b445e7ddfdfd48029442d9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 20:35:51 -0500 Subject: [PATCH 0547/1412] [Rails5] Conform to table_exists decprecations. --- .../connection_adapters/sqlserver/schema_statements.rb | 9 +++++++++ test/cases/coerced_tests.rb | 6 ------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a05d17581..9afee4877 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -11,6 +11,15 @@ def tables(table_type = 'BASE TABLE') select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME", 'SCHEMA' end + def table_exists?(table_name) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + #table_exists? currently checks both tables and views. + This behavior is deprecated and will be changed with Rails 5.1 to only check tables. + Use #data_source_exists? instead. + MSG + data_source_exists?(table_name) + end + def data_source_exists?(table_name) return false if table_name.blank? unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 573207bb4..a96accdc2 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -578,9 +578,6 @@ def test_views_coerced assert_includes @connection.views, Ebook.table_name end - # This is deprecated and we dont care in this version to pass it. - coerce_tests! :test_table_exists - # We do better than ActiveRecord and find the views PK. coerce_tests! :test_does_not_assume_id_column_as_primary_key def test_does_not_assume_id_column_as_primary_key_coerced @@ -594,9 +591,6 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase def test_views_coerced assert_includes @connection.views, Paperback.table_name end - - # This is deprecated and we dont care in this version to pass it. - coerce_tests! :test_table_exists end From b485c2adb4f89bf4c82ecf919ca9e2437d28d000 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 20:48:43 -0500 Subject: [PATCH 0548/1412] [Rails5] Notes [ci skip] --- RAILS5-FAILS.txt | 513 +++++++++++++++++++---------------------------- RAILS5-TODO.md | 1 + 2 files changed, 204 insertions(+), 310 deletions(-) diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt index ef5698413..2cfea5a61 100644 --- a/RAILS5-FAILS.txt +++ b/RAILS5-FAILS.txt @@ -2,33 +2,21 @@ Run options: --seed=44527 # Running: -..........................................................................................................................................................................................................FF......................................................................................................................................................................................................................F....F...........E..EF.....S..E....................................................................................................................E................................................................................................................................................................F................................................................................................................................................................................................................................................................E.........................................................F.................................................................................................................F.........................................................................E.......................................................................................................................................................................................................................................................................................................E...E...................................................F.........................................................................................................................................F......F....F...........F................F....F...................................................................................................................................................................................................................................................................................................................................E.........................................................S..................................................F...................................................................................................................................................................................................................................................E......E......E..........................................................................................................................E.............E......................F...........................................................................................................................E......F...............................................................................................................................................................E..................F...............F.............F..E...........F....F...FE.....F.......E..................................................................................................................................................................................................................................................................................................................................E........................E.................................E........................................F...E..............E............F..F..........F........................................................................................................E...................................................................................................................................................................................................................................................................................................................................................................F.................................................................................................E.......................................E.......E........................................................................................E...........................................................................................................................................................................E................................................................................E.................................E...................................E..............................................................................................................S.........................................E........................E.............................................................................................................................................................................................E.. +..................................................................................................................................................................................................................................................................................................................................................................................................................................F....F...........E..EF.....S..E.....................................................................................................................................................................................................................................................................................F................................................................................................................................................................................................................................................................E.........................................................F.................................................................................................................F.........................................................................E.......................................................................................................................................................................................................................................................................................................E...E...................................................F.........................................................................................................................................F......F....F...........F................F....F.............................................................................................................................................................................................................................................................................................................................................................................................S..................................................F...................................................................................................................................................................................................................................................E......E......E...............................................................................................................................................................F.........................................................................................................................F............E..................................................................................................................................................E...............EE.......F......................F..............................F.E....F.F......FF..................................................................................................................................................................................................................................................................................E..............E.....................................................................E...................E..........F.........................................E............................F...........................................................E.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F....E.................................E..................................................E..............................................................................E.................................................................................................................................................................................E..............E.................................................E.............E.................................................................................................................................................................................S.........................E.............................E.................................................................................................................................................................................................E........... -Finished in 442.623076s, 10.9664 runs/s, 31.5641 assertions/s. +Finished in 462.990190s, 10.4883 runs/s, 30.1907 assertions/s. 1) Failure: -DefaultNumbersTest#test_default_positive_integer [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/defaults_test.rb:43]: -Expected: "7" - Actual: 7 - - - 2) Failure: -DefaultNumbersTest#test_default_negative_integer [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/defaults_test.rb:49]: -Expected: "-5" - Actual: -5 - - - 3) Failure: UniquenessValidationTest#test_validate_uniqueness_with_limit [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:376]: ActiveRecord::ValueTooLong expected but nothing was raised. - 4) Failure: + 2) Failure: UniquenessValidationTest#test_validate_uniqueness_with_limit_and_utf8 [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:395]: ActiveRecord::ValueTooLong expected but nothing was raised. - 5) Error: + 3) Error: UniquenessValidationTest#test_validates_uniqueness_with_newline_chars: ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''new line'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'new @@ -89,7 +77,7 @@ line', @1 = 1 /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:135:in `test_validates_uniqueness_with_newline_chars' - 6) Error: + 4) Error: UniquenessValidationTest#test_validate_case_insensitive_uniqueness_with_special_sql_like_chars: ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -148,12 +136,12 @@ ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:299:in `test_validate_case_insensitive_uniqueness_with_special_sql_like_chars' - 7) Failure: + 5) Failure: UniquenessValidationTest#test_validate_case_sensitive_uniqueness [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:318]: Should be valid - 8) Error: + 6) Error: UniquenessValidationTest#test_validate_case_insensitive_uniqueness: ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -212,22 +200,14 @@ ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:245:in `test_validate_case_insensitive_uniqueness' - 9) Error: -ActiveRecord::ConnectionAdapters::ConnectionHandlerTest#test_establish_connection_uses_spec_name: -Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_adapters/connection_handler_test.rb:16:in `test_establish_connection_uses_spec_name' - - - 10) Failure: + 7) Failure: DirtyTest#test_time_attributes_changes_with_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:80]: -Expected 2017-01-05 01:55:43 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. +Expected 2017-01-07 01:30:47 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. - 11) Error: + 8) Error: DefaultScopingTest#test_unscope_string_where_clauses_involved: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-05-2016 01:56:15.689'') ORDER BY salary DESC +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-07-2016 01:31:21.06'') ORDER BY salary DESC /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -251,18 +231,18 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SEL /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:178:in `test_unscope_string_where_clauses_involved' - 12) Failure: + 9) Failure: DefaultScopingTest#test_with_abstract_class_scope_should_be_executed_in_correct_context [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:497]: Expected /"lions"."is_vegetarian"/ to match "SELECT [lions].* FROM [lions] WHERE [lions].[is_vegetarian] = 0". - 13) Failure: -CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:95]: + 10) Failure: +CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:107]: Expected: 1 Actual: 3 - 14) Error: + 11) Error: CalculationsTest#test_having_with_strong_parameters: ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [accounts].* FROM [accounts] GROUP BY [accounts].[id] HAVING [accounts].[credit_limit] = @0', N'@0 int', @0 = 50 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -288,7 +268,7 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' i /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/calculations_test.rb:789:in `test_having_with_strong_parameters' - 15) Error: + 12) Error: MultipleDbTest#test_associations_should_work_when_model_has_no_connection: ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' @@ -325,7 +305,7 @@ ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:114:in `test_associations_should_work_when_model_has_no_connection' - 16) Error: + 13) Error: MultipleDbTest#test_count_on_custom_connection: ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' @@ -354,7 +334,7 @@ ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:106:in `test_count_on_custom_connection' - 17) Failure: + 14) Failure: EachTest#test_in_batches_should_quote_batch_order [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/batches_test.rb:375]: Query pattern(s) /ORDER BY [posts].[id]/ not found. Queries: @@ -383,22 +363,22 @@ EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 11 ORD EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 11) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - 18) Failure: + 15) Failure: MigrationTest#test_add_table_with_decimals [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:181]: -Expected # to be a kind of Integer, not BigDecimal. +Expected # to be a kind of Integer, not BigDecimal. - 19) Failure: + 16) Failure: MigrationTest#test_migration_sets_internal_metadata_even_when_fully_migrated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:419]: Expected "development" to not be equal to "development". - 20) Failure: + 17) Failure: MigrationTest#test_internal_metadata_stores_environment [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:393]: Expected "development" to not be equal to "development". - 21) Failure: + 18) Failure: MigrationTest#test_filtering_migrations [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223]: [ActiveRecord::StatementInvalid] exception expected, not Class: @@ -429,87 +409,26 @@ Message: <"undefined method `second' for nil:NilClass"> --------------- - 22) Failure: + 19) Failure: SchemaDumperTest#test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:320]: non-primary key id column not preserved. Expected /t\.string\s+"id",.*?null: false$/ to match " t.string \"id\", null: false, collation: \"SQL_Latin1_General_CP1_CI_AS\"". - 23) Failure: + 20) Failure: SchemaDumperTest#test_schema_dump_includes_decimal_options [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:262]: Expected /precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"/ to match "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 0) do\n\n create_table \"nep_ar_internal_metadata\", primary_key: \"key\", id: :string, collation: \"SQL_Latin1_General_CP1_CI_AS\", force: :cascade do |t|\n t.string \"value\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"created_at\", null: false\n t.datetime \"updated_at\", null: false\n end\n\n create_table \"nodes\", force: :cascade do |t|\n t.integer \"tree_id\"\n t.integer \"parent_id\"\n t.string \"name\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"updated_at\"\n end\n\n create_table \"non_poly_ones\", force: :cascade do |t|\n end\n\n create_table \"non_poly_twos\", force: :cascade do |t|\n end\n\n create_table \"notifications\", force: :cascade do |t|\n t.string \"message\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n end\n\n create_table \"numeric_data\", force: :cascade do |t|\n t.decimal \"bank_balance\", precision: 10, scale: 2\n t.decimal \"big_bank_balance\", precision: 15, scale: 2\n t.decimal \"world_population\", precision: 10, scale: 0\n t.decimal \"my_house_population\", precision: 2, scale: 0\n t.decimal \"decimal_number_with_default\", precision: 3, scale: 2, default: 2.78\n t.float \"temperature\"\n t.decimal \"atoms_in_universe\", precision: 38, scale: 0\n end\n\nend\n". - 24) Error: -BasicsTest#test_create_without_prepared_statement: -ActiveRecord::StatementInvalid: TinyTds::Error: Must declare the scalar variable "@0".: SELECT DISTINCT columns.TABLE_NAME AS table_name, columns.COLUMN_NAME AS name, columns.DATA_TYPE AS type, columns.COLUMN_DEFAULT AS default_value, columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, columns.DATETIME_PRECISION AS datetime_precision, columns.COLLATION_NAME AS collation, columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH ELSE COL_LENGTH('.'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) END AS [length], CASE WHEN columns.IS_NULLABLE = 'YES' THEN 1 ELSE NULL END AS [is_nullable], CASE WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1 ELSE NULL END AS [is_primary], c.is_identity AS [is_identity] FROM .INFORMATION_SCHEMA.COLUMNS columns LEFT OUTER JOIN .INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' LEFT OUTER JOIN .INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.COLUMN_NAME = columns.COLUMN_NAME AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA INNER JOIN .sys.schemas AS s ON s.name = columns.TABLE_SCHEMA AND s.schema_id = s.schema_id INNER JOIN .sys.objects AS o ON s.schema_id = o.schema_id AND o.is_ms_shipped = 0 AND o.type IN ('U', 'V') AND o.name = columns.TABLE_NAME INNER JOIN .sys.columns AS c ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME WHERE columns.TABLE_NAME = @0 AND columns.TABLE_SCHEMA = schema_name() ORDER BY columns.ordinal_position - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:331:in `column_definitions' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:57:in `columns' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:489:in `identity_columns' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:477:in `query_requires_identity_insert?' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:23:in `exec_insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:125:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:34:in `create' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:616:in `block in test_create_without_prepared_statement' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:215:in `unprepared_statement' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:615:in `test_create_without_prepared_statement' - - - 25) Failure: + 21) Failure: MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: Expected: 2004-06-24 23:24:00 UTC Actual: 2004-06-24 16:24:00 UTC - 26) Error: + 22) Error: TransactionCallbacksTest#test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.044915', @3 = '01-05-2017 02:00:21.044915' +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.028116', @3 = '01-07-2017 01:35:39.028116' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -563,9 +482,9 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarc /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:219:in `test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record' - 27) Error: + 23) Error: TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.183741', @3 = '01-05-2017 02:00:21.183741' +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.161145', @3 = '01-07-2017 01:35:39.161145' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -617,9 +536,9 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarc /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:134:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association' - 28) Error: + 24) Error: TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-04-2017''', @2 = '01-05-2017 02:00:21.328166', @3 = '01-05-2017 02:00:21.328166' +ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.319981', @3 = '01-07-2017 01:35:39.319981' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -670,44 +589,26 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarc /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:129:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record' - 29) Error: -ActiveRecord::ConnectionAdapters::ConnectionSpecification::ResolverTest#test_spec_name_with_inline_config: -Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:12:in `spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:125:in `test_spec_name_with_inline_config' - - - 30) Error: -ActiveRecord::ConnectionAdapters::ConnectionSpecification::ResolverTest#test_spec_name_on_key_lookup: -Gem::LoadError: Specified 'sqlite3' for database adapter, but the gem is not loaded. Add `gem 'sqlite3'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord). - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:176:in `rescue in spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/connection_specification.rb:173:in `spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:12:in `spec' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/connection_specification/resolver_test.rb:120:in `test_spec_name_on_key_lookup' - - - 31) Failure: + 25) Failure: ActiveRecord::ConnectionAdapters::QuoteBooleanTest#test_quote_returns_frozen_string [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/quoting_test.rb:159]: Expected "1" to be frozen?. - 32) Error: -ActiveRecord::Migration::ColumnsTest#test_rename_column_using_symbol_arguments: -ActiveRecord::ActiveRecordError: No such column: test_models.first_name - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:432:in `detect_column_for!' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:139:in `rename_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `rename_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:33:in `test_rename_column_using_symbol_arguments' - - - 33) Failure: + 26) Failure: ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: Expected false to be truthy. - 34) Error: + 27) Error: +ActiveRecord::Migration::ColumnsTest#test_change_column: +NoMethodError: undefined method `sql_type' for nil:NilClass + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/quoting.rb:39:in `quote_default_expression' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:127:in `change_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `change_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:203:in `test_change_column' + + + 28) Error: LeftOuterJoinAssociationTest#test_does_not_override_select: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC sp_executesql N'SELECT authors.name, (authors.author_address_id || '' '' || authors.author_address_extra_id) as addr_id FROM [authors] LEFT OUTER JOIN [posts] ON [posts].[author_id] = [authors].[id] ORDER BY [authors].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -737,17 +638,23 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/left_outer_join_association_test.rb:78:in `test_does_not_override_select' - 35) Failure: -AttributeMethodsTest#test_read_attributes_after_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:235]: ---- expected -+++ actual -@@ -1 +1 @@ --Thu, 24 Mar 2011 00:00:00 PDT -07:00 -+2011-03-24 00:00:00 UTC + 29) Error: +AttributeMethodsTest#test_setting_time_zone_aware_read_attribute: +NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:625:in `block in test_setting_time_zone_aware_read_attribute' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:622:in `test_setting_time_zone_aware_read_attribute' + 30) Error: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_to_utc: +NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:602:in `block in test_setting_time_zone_aware_attribute_to_utc' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:597:in `test_setting_time_zone_aware_attribute_to_utc' - 36) Failure: + + 31) Failure: AttributeMethodsTest#test_setting_time_zone_aware_attribute_in_other_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:614]: --- expected +++ actual @@ -757,40 +664,43 @@ AttributeMethodsTest#test_setting_time_zone_aware_attribute_in_other_time_zone [ - 37) Failure: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:669]: + 32) Failure: +AttributeMethodsTest#test_time_attributes_are_retrieved_in_current_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:590]: +Expected 2008-01-01 00:00:00 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. + + + 33) Failure: +AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: --- expected +++ actual @@ -1 +1 @@ --Tue, 01 Jan 2008 00:00:00 SST -11:00 -+2008-01-01 00:00:00 UTC +-Sat, 01 Jan 2000 10:00:00 PST -08:00 ++Sat, 01 Jan 2000 02:00:00 PST -08:00 - 38) Error: -AttributeMethodsTest#test_setting_time_zone_aware_read_attribute: + 34) Error: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_with_string: NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:625:in `block in test_setting_time_zone_aware_read_attribute' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:638:in `block (2 levels) in test_setting_time_zone_aware_attribute_with_string' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:622:in `test_setting_time_zone_aware_read_attribute' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:634:in `block in test_setting_time_zone_aware_attribute_with_string' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:632:in `test_setting_time_zone_aware_attribute_with_string' - 39) Failure: -AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: + 35) Failure: +AttributeMethodsTest#test_read_attributes_before_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:219]: --- expected +++ actual @@ -1 +1 @@ --Sat, 01 Jan 2000 10:00:00 PST -08:00 -+Sat, 01 Jan 2000 02:00:00 PST -08:00 - - +-Sun, 11 Oct 2009 12:13:14 PDT -07:00 ++2009-10-11 12:13:14 UTC - 40) Failure: -AttributeMethodsTest#test_time_attributes_are_retrieved_in_current_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:590]: -Expected 2008-01-01 00:00:00 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. - 41) Failure: + 36) Failure: AttributeMethodsTest#test_time_zone_aware_attribute_saved [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:650]: --- expected +++ actual @@ -800,36 +710,65 @@ AttributeMethodsTest#test_time_zone_aware_attribute_saved [/Users/kencollins/.rb - 42) Error: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_with_string: -NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:638:in `block (2 levels) in test_setting_time_zone_aware_attribute_with_string' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:634:in `block in test_setting_time_zone_aware_attribute_with_string' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:632:in `test_setting_time_zone_aware_attribute_with_string' + 37) Failure: +AttributeMethodsTest#test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:669]: +--- expected ++++ actual +@@ -1 +1 @@ +-Tue, 01 Jan 2008 00:00:00 SST -11:00 ++2008-01-01 00:00:00 UTC - 43) Failure: -AttributeMethodsTest#test_read_attributes_before_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:219]: + + 38) Failure: +AttributeMethodsTest#test_read_attributes_after_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:235]: --- expected +++ actual @@ -1 +1 @@ --Sun, 11 Oct 2009 12:13:14 PDT -07:00 -+2009-10-11 12:13:14 UTC +-Thu, 24 Mar 2011 00:00:00 PDT -07:00 ++2011-03-24 00:00:00 UTC - 44) Error: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_to_utc: -NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:602:in `block in test_setting_time_zone_aware_attribute_to_utc' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:597:in `test_setting_time_zone_aware_attribute_to_utc' + 39) Error: +FinderTest#test_exists_with_distinct_association_includes_limit_and_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' - 45) Error: + 40) Error: +FinderTest#test_exists_with_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' + + + 41) Error: FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE (written_on = ''''07-16-2003 14:28:11.223'''') ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -863,27 +802,8 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXE /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:835:in `test_condition_local_time_interpolation_with_default_timezone_utc' - 46) Error: -FinderTest#test_exists_with_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' - - - 47) Error: -FinderTest#test_exists_with_distinct_association_includes_limit_and_order: + 42) Error: +FinderTest#test_exists_with_distinct_association_includes_and_limit: ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' @@ -898,14 +818,14 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:220:in `test_exists_with_distinct_association_includes_and_limit' - 48) Failure: + 43) Failure: FinderTest#test_hash_condition_find_malformed [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:752]: [ActiveRecord::StatementInvalid] exception expected, not Class: -Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> +Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> ---Backtrace--- /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:254:in `sp_executesql_sql_type' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:240:in `block in sp_executesql_types_and_parameters' @@ -931,7 +851,7 @@ Message: <"sp_executesql_sql_type can not find sql type for attr #' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:271:in `block in ' - 55) Failure: + 47) Failure: HasManyAssociationsTest#test_do_not_call_callbacks_for_delete_all [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_many_associations_test.rb:248]: bulbs should have been deleted using :delete_all strategy. Expected: 0 Actual: 1 - 56) Error: -HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 54, @2 = 1 + 48) Error: +HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1059,14 +950,15 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' - 57) Error: -HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + 49) Error: +HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 52, @2 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1080,13 +972,12 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' - 58) Error: + 50) Error: HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -1109,7 +1000,7 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' - 59) Error: + 51) Error: DateTest#test_date_with_string_value: ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `to_s' @@ -1132,9 +1023,9 @@ ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/date_test.rb:14:in `test_date_with_string_value' - 60) Error: -RelationTest#test_reverse_order_with_function: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + 52) Error: +RelationTest#test_multiple_where_and_having_clauses: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1146,25 +1037,20 @@ ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized bui /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 61) Error: -RelationTest#test_multiple_where_and_having_clauses: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 + 53) Error: +RelationTest#test_reverse_order_with_function_other_predicates: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1176,20 +1062,25 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is inva /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' - 62) Error: -RelationTest#test_having_with_binds_for_both_where_and_having: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 + 54) Error: +RelationTest#test_reverse_order_with_function: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1201,20 +1092,25 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is inva /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' - 63) Error: -RelationTest#test_reverse_order_with_function_other_predicates: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + 55) Error: +RelationTest#test_having_with_binds_for_both_where_and_having: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1226,25 +1122,20 @@ ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized bui /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 64) Error: -NamedScopingTest#test_method_missing_priority_when_delegating: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-05-2017 02:02:11.789'') AND (written_on >= ''01-04-2017 02:02:11.799'') + 56) Error: +NamedScopingTest#test_nested_scopes_queries_size: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-07-2017 01:37:31.536'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1265,12 +1156,14 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SEL /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' - 65) Error: -NamedScopingTest#test_nested_scopes_queries_size: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-05-2017 02:02:12.476'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' + 57) Error: +NamedScopingTest#test_method_missing_priority_when_delegating: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-07-2017 01:37:32.753'') AND (written_on >= ''01-06-2017 01:37:32.766'') /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1291,12 +1184,10 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXE /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' - 66) Error: + 58) Error: ActiveRecord::CollectionCacheKeyTest#test_0011_cache_key for queries with offset which return 0 rows: ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT COUNT(*) AS [size], MAX([developers].[updated_at]) AS timestamp FROM [developers] ORDER BY [developers].[id] ASC OFFSET @0 ROWS', N'@0 int', @0 = 20 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -1319,18 +1210,20 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invali /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/collection_cache_key_test.rb:80:in `block in ' 4854 runs, 13971 assertions, 30 failures, 36 errors, 3 skips +√ 3 - Gem `sqlite3` load error. +4854 runs, 13974 assertions, 30 failures, 33 errors, 3 skips +√ 2 - DefaultNumbersTest +√ 3 - AdapterTest +4856 runs, 13978 assertions, 26 failures, 32 errors, 3 skips * 10 - AttributeMethodsTest * 6 - FinderTest -* 6 - Gem `sqlite3` load error. * 6 - UniquenessValidationTest * 4 - RelationTest * 4 - MigrationTest * 4 - (HasMany|HasAndBelongs)AssociationsTest -* 3 - AdapterTest * 3 - TransactionCallbacksTest * 2 - NamedScopingTest -* 2 - DefaultNumbersTest * 2 - DefaultScopingTest * 2 - CalculationsTest * 2 - MultipleDbTest diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index ee36dc72c..640978099 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -49,6 +49,7 @@ After we get some tests passing - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx - http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast - https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ +* Re-visit all `current_adapter?(:PostgreSQLAdapter)` checks and find ones we can play in. #### Does Find By SQL Work? From 928febf7ff5afd0209e91ff749368b9f2e1b7c4d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 20:48:03 -0500 Subject: [PATCH 0549/1412] [Rails5] Support `unprepared_statement` blocks & ValueTooLong excp. --- .../sqlserver/schema_statements.rb | 6 +++--- .../connection_adapters/sqlserver_adapter.rb | 2 ++ test/cases/coerced_tests.rb | 12 ++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9afee4877..25f7d581c 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -329,15 +329,15 @@ def column_definitions(table_name) INNER JOIN #{database}.sys.columns AS c ON o.object_id = c.object_id AND c.name = columns.COLUMN_NAME - WHERE columns.TABLE_NAME = @0 - AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : '@1'} + WHERE columns.TABLE_NAME = #{prepared_statements ? '@0' : quote(identifier.object)} + AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))} ORDER BY columns.ordinal_position }.gsub(/[ \t\r\n]+/, ' ').strip binds = [] nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? - results = sp_executesql(sql, 'SCHEMA', binds, prepare: true) + results = sp_executesql(sql, 'SCHEMA', binds) results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index e7f0944a8..3ebc05841 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -316,6 +316,8 @@ def translate_exception(e, message) DeadlockVictim.new(message) when /database .* does not exist/i NoDatabaseError.new(message) + when /data would be truncated/ + ValueTooLong.new(message) else super end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a96accdc2..2b1d25b44 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2,10 +2,22 @@ +require 'models/event' module ActiveRecord class AdapterTest < ActiveRecord::TestCase # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement + + # So sp_executesql swallows this exception. Run without prpared to see it. + coerce_tests! :test_value_limit_violations_are_translated_to_specific_exception + def test_value_limit_violations_are_translated_to_specific_exception_coerced + connection.unprepared_statement do + error = assert_raises(ActiveRecord::ValueTooLong) do + Event.create(title: 'abcdefgh') + end + assert_not_nil error.cause + end + end end end From ff924f938521efc049b4fabf530b3ade83640c6f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 21:04:41 -0500 Subject: [PATCH 0550/1412] [Rails5] Add note about unprepared_statement support. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1d0578c..ecc7447ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ #### Added * Support for `supports_datetime_with_precision`. +* Support for `unprepared_statement` blocks on the connection. #### Changed From f0ff483005d5d20e8b8ddbc8a897d03bd0acb21a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 21:06:29 -0500 Subject: [PATCH 0551/1412] [Rails5] Support deprecations on `tables` with name arg. --- .../sqlserver/schema_statements.rb | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 25f7d581c..dde358cf5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,8 +7,9 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end - def tables(table_type = 'BASE TABLE') - select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME", 'SCHEMA' + def tables(name = nil) + ActiveSupport::Deprecation.warn 'Passing arguments to #tables is deprecated without replacement.' if name + tables_sql('BASE TABLE') end def table_exists?(table_name) @@ -27,7 +28,7 @@ def data_source_exists?(table_name) end def views - tables('VIEW') + tables_sql('VIEW') end def view_exists?(table_name) @@ -276,6 +277,12 @@ def initialize_native_database_types } end + def tables_sql(type) + tn = lowercase_schema_reflection_sql 'TABLE_NAME' + sql = "SELECT #{tn} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '#{type}' ORDER BY TABLE_NAME" + select_values sql, 'SCHEMA' + end + def column_definitions(table_name) identifier = if database_prefix_remote_server? SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") From 95ceea493b33d508e1e48723d89569482dbfcc7e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 6 Jan 2017 21:08:46 -0500 Subject: [PATCH 0552/1412] [Rails5] More notes [ci skip] --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ecc7447ab..c6aa66523 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ #### Changed -* ... +* Major refactoring of all type objects. Especially time types. #### Deprecated -* ... +* Support for a handful of standard Rails deprecations in 5-0-stable suite. #### Removed From 1c537ed895acee9f61d8a296544a1903072e5513 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 17:33:09 -0500 Subject: [PATCH 0553/1412] [Rails5] Update notes. [ci skip] --- RAILS5-FAILS.txt | 534 ++++++++++++++--------------------------------- 1 file changed, 155 insertions(+), 379 deletions(-) diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt index 2cfea5a61..1cec1f51e 100644 --- a/RAILS5-FAILS.txt +++ b/RAILS5-FAILS.txt @@ -1,10 +1,5 @@ Run options: --seed=44527 -# Running: - -..................................................................................................................................................................................................................................................................................................................................................................................................................................F....F...........E..EF.....S..E.....................................................................................................................................................................................................................................................................................F................................................................................................................................................................................................................................................................E.........................................................F.................................................................................................................F.........................................................................E.......................................................................................................................................................................................................................................................................................................E...E...................................................F.........................................................................................................................................F......F....F...........F................F....F.............................................................................................................................................................................................................................................................................................................................................................................................S..................................................F...................................................................................................................................................................................................................................................E......E......E...............................................................................................................................................................F.........................................................................................................................F............E..................................................................................................................................................E...............EE.......F......................F..............................F.E....F.F......FF..................................................................................................................................................................................................................................................................................E..............E.....................................................................E...................E..........F.........................................E............................F...........................................................E.........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................F....E.................................E..................................................E..............................................................................E.................................................................................................................................................................................E..............E.................................................E.............E.................................................................................................................................................................................S.........................E.............................E.................................................................................................................................................................................................E........... - -Finished in 462.990190s, 10.4883 runs/s, 30.1907 assertions/s. 1) Failure: UniquenessValidationTest#test_validate_uniqueness_with_limit [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:376]: @@ -201,13 +196,13 @@ ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for 7) Failure: -DirtyTest#test_time_attributes_changes_with_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:80]: -Expected 2017-01-07 01:30:47 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. +DirtyTest#test_time_attributes_changes_without_time_zone_by_skip [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:122]: +Expected Sat, 07 Jan 2017 23:06:30 CET +01:00 to be an instance of Time, not ActiveSupport::TimeWithZone. 8) Error: DefaultScopingTest#test_unscope_string_where_clauses_involved: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-07-2016 01:31:21.06'') ORDER BY salary DESC +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-07-2016 22:07:01.676'') ORDER BY salary DESC /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -365,7 +360,7 @@ EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 11) 15) Failure: MigrationTest#test_add_table_with_decimals [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:181]: -Expected # to be a kind of Integer, not BigDecimal. +Expected # to be a kind of Integer, not BigDecimal. 16) Failure: @@ -421,194 +416,51 @@ Expected /precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"/ to mat 21) Failure: -MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: -Expected: 2004-06-24 23:24:00 UTC - Actual: 2004-06-24 16:24:00 UTC +BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:269]: +--- expected ++++ actual +@@ -1 +1 @@ +-[0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"] ++[0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"] - 22) Error: -TransactionCallbacksTest#test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.028116', @3 = '01-07-2017 01:35:39.028116' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:230:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:220:in `block in test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:219:in `test_only_call_after_rollback_on_create_after_transaction_rollsback_for_new_record' + 22) Failure: +BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:241]: +--- expected ++++ actual +@@ -1 +1 @@ +-[0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"] ++[0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"] - 23) Error: -TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.161145', @3 = '01-07-2017 01:35:39.161145' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:51:in `create!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:134:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record_if_create_succeeds_creating_through_association' - 24) Error: -TransactionCallbacksTest#test_only_call_after_commit_on_create_after_transaction_commits_for_new_record: -ActiveRecord::StatementInvalid: TinyTds::Error: Error converting data type nvarchar to datetime2.: EXEC sp_executesql N'INSERT INTO [topics] ([title], [written_on], [created_at], [updated_at]) OUTPUT INSERTED.[id] VALUES (@0, @1, @2, @3)', N'@0 nvarchar(250), @1 datetime2(6), @2 datetime2(6), @3 datetime2(6)', @0 = N'New topic', @1 = N'''01-06-2017''', @2 = '01-07-2017 01:35:39.319981', @3 = '01-07-2017 01:35:39.319981' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:26:in `exec_insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:124:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:17:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:65:in `insert' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:560:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/counter_cache.rb:128:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/locking/optimistic.rb:75:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:123:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `block in _create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_create_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:302:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/timestamp.rb:68:in `_create_record' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:540:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `block in create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_save_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/callbacks.rb:298:in `create_or_update' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/persistence.rb:152:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:50:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:30:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `block in save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:324:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:45:in `save!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/transaction_callbacks_test.rb:129:in `test_only_call_after_commit_on_create_after_transaction_commits_for_new_record' + 23) Failure: +MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: +Expected: 2004-06-24 23:24:00 UTC + Actual: 2004-06-24 16:24:00 UTC - 25) Failure: + 24) Failure: ActiveRecord::ConnectionAdapters::QuoteBooleanTest#test_quote_returns_frozen_string [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/quoting_test.rb:159]: Expected "1" to be frozen?. - 26) Failure: -ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: -Expected false to be truthy. - - - 27) Error: + 25) Error: ActiveRecord::Migration::ColumnsTest#test_change_column: NoMethodError: undefined method `sql_type' for nil:NilClass /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/quoting.rb:39:in `quote_default_expression' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:127:in `change_column' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:128:in `change_column' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `change_column' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:203:in `test_change_column' - 28) Error: + 26) Failure: +ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: +Expected false to be truthy. + + + 27) Error: LeftOuterJoinAssociationTest#test_does_not_override_select: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC sp_executesql N'SELECT authors.name, (authors.author_address_id || '' '' || authors.author_address_extra_id) as addr_id FROM [authors] LEFT OUTER JOIN [posts] ON [posts].[author_id] = [authors].[id] ORDER BY [authors].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -638,38 +490,7 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/left_outer_join_association_test.rb:78:in `test_does_not_override_select' - 29) Error: -AttributeMethodsTest#test_setting_time_zone_aware_read_attribute: -NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:625:in `block in test_setting_time_zone_aware_read_attribute' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:622:in `test_setting_time_zone_aware_read_attribute' - - - 30) Error: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_to_utc: -NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:602:in `block in test_setting_time_zone_aware_attribute_to_utc' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:597:in `test_setting_time_zone_aware_attribute_to_utc' - - - 31) Failure: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_in_other_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:614]: ---- expected -+++ actual -@@ -1 +1 @@ --#> -+#> - - - - 32) Failure: -AttributeMethodsTest#test_time_attributes_are_retrieved_in_current_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:590]: -Expected 2008-01-01 00:00:00 UTC to be a kind of ActiveSupport::TimeWithZone, not Time. - - - 33) Failure: + 28) Failure: AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: --- expected +++ actual @@ -679,96 +500,7 @@ AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencolli - 34) Error: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_with_string: -NoMethodError: undefined method `time_zone' for 2008-01-01 00:00:00 UTC:Time - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:638:in `block (2 levels) in test_setting_time_zone_aware_attribute_with_string' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:197:in `in_time_zone' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:634:in `block in test_setting_time_zone_aware_attribute_with_string' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/core_ext/range/each.rb:5:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:632:in `test_setting_time_zone_aware_attribute_with_string' - - - 35) Failure: -AttributeMethodsTest#test_read_attributes_before_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:219]: ---- expected -+++ actual -@@ -1 +1 @@ --Sun, 11 Oct 2009 12:13:14 PDT -07:00 -+2009-10-11 12:13:14 UTC - - - - 36) Failure: -AttributeMethodsTest#test_time_zone_aware_attribute_saved [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:650]: ---- expected -+++ actual -@@ -1 +1 @@ --Mon, 20 Feb 2012 09:00:00 CET +01:00 -+2012-02-20 09:00:00 UTC - - - - 37) Failure: -AttributeMethodsTest#test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:669]: ---- expected -+++ actual -@@ -1 +1 @@ --Tue, 01 Jan 2008 00:00:00 SST -11:00 -+2008-01-01 00:00:00 UTC - - - - 38) Failure: -AttributeMethodsTest#test_read_attributes_after_type_cast_on_datetime [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:235]: ---- expected -+++ actual -@@ -1 +1 @@ --Thu, 24 Mar 2011 00:00:00 PDT -07:00 -+2011-03-24 00:00:00 UTC - - - - 39) Error: -FinderTest#test_exists_with_distinct_association_includes_limit_and_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' - - - 40) Error: -FinderTest#test_exists_with_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' - - - 41) Error: + 29) Error: FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE (written_on = ''''07-16-2003 14:28:11.223'''') ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -802,30 +534,11 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXE /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:835:in `test_condition_local_time_interpolation_with_default_timezone_utc' - 42) Error: -FinderTest#test_exists_with_distinct_association_includes_and_limit: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:220:in `test_exists_with_distinct_association_includes_and_limit' - - - 43) Failure: + 30) Failure: FinderTest#test_hash_condition_find_malformed [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:752]: [ActiveRecord::StatementInvalid] exception expected, not Class: -Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> +Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> ---Backtrace--- /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:254:in `sp_executesql_sql_type' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:240:in `block in sp_executesql_types_and_parameters' @@ -851,7 +564,7 @@ Message: <"sp_executesql_sql_type can not find sql type for attr #' - 47) Failure: + 36) Failure: HasManyAssociationsTest#test_do_not_call_callbacks_for_delete_all [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_many_associations_test.rb:248]: bulbs should have been deleted using :delete_all strategy. Expected: 0 Actual: 1 - 48) Error: -HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + 37) Error: +HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -950,15 +715,16 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' - 49) Error: + 38) Error: HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 52, @2 = 1 +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 61, @2 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -977,9 +743,9 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' - 50) Error: -HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 + 39) Error: +HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -993,14 +759,13 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' - 51) Error: + 40) Error: DateTest#test_date_with_string_value: ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `to_s' @@ -1023,9 +788,9 @@ ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/date_test.rb:14:in `test_date_with_string_value' - 52) Error: -RelationTest#test_multiple_where_and_having_clauses: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 + 41) Error: +RelationTest#test_having_with_binds_for_both_where_and_having: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1048,9 +813,9 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is inva /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 53) Error: -RelationTest#test_reverse_order_with_function_other_predicates: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + 42) Error: +RelationTest#test_multiple_where_and_having_clauses: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1062,23 +827,18 @@ ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized bui /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 54) Error: + 43) Error: RelationTest#test_reverse_order_with_function: ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -1108,9 +868,9 @@ ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized bui /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' - 55) Error: -RelationTest#test_having_with_binds_for_both_where_and_having: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 + 44) Error: +RelationTest#test_reverse_order_with_function_other_predicates: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1122,20 +882,25 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is inva /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' - 56) Error: -NamedScopingTest#test_nested_scopes_queries_size: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-07-2017 01:37:31.536'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' + 45) Error: +NamedScopingTest#test_method_missing_priority_when_delegating: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-07-2017 22:28:17.729'') AND (written_on >= ''01-06-2017 22:28:17.739'') /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1156,14 +921,12 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXE /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' - 57) Error: -NamedScopingTest#test_method_missing_priority_when_delegating: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-07-2017 01:37:32.753'') AND (written_on >= ''01-06-2017 01:37:32.766'') + 46) Error: +NamedScopingTest#test_nested_scopes_queries_size: +ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-07-2017 22:28:19.127'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' @@ -1184,10 +947,12 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SEL /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' - 58) Error: + 47) Error: ActiveRecord::CollectionCacheKeyTest#test_0011_cache_key for queries with offset which return 0 rows: ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT COUNT(*) AS [size], MAX([developers].[updated_at]) AS timestamp FROM [developers] ORDER BY [developers].[id] ASC OFFSET @0 ROWS', N'@0 int', @0 = 20 /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' @@ -1209,15 +974,26 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invali /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:337:in `cache_key' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/collection_cache_key_test.rb:80:in `block in ' + 4854 runs, 13971 assertions, 30 failures, 36 errors, 3 skips √ 3 - Gem `sqlite3` load error. 4854 runs, 13974 assertions, 30 failures, 33 errors, 3 skips √ 2 - DefaultNumbersTest √ 3 - AdapterTest 4856 runs, 13978 assertions, 26 failures, 32 errors, 3 skips +4856 runs, 13978 assertions, 25 failures, 32 errors, 3 skips +√ 4 - AttributeMethodsTest +4856 runs, 14063 assertions, 25 failures, 26 errors, 3 skips +4856 runs, 14140 assertions, 21 failures, 26 errors, 3 skips + + +* 6 - ORDER BY items +* 4 - The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer. +* 2 - ActiveRecord::ValueTooLong expected + -* 10 - AttributeMethodsTest -* 6 - FinderTest +* 6 - AttributeMethodsTest +* 7 - FinderTest * 6 - UniquenessValidationTest * 4 - RelationTest * 4 - MigrationTest From 9faa34058d98d963e90e29b8f66cf05c530ae4c8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 17:34:38 -0500 Subject: [PATCH 0554/1412] [Rails5] Pass more attribute tests for date/time/zones. --- .../sqlserver/type/datetime.rb | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index b6cd2f208..c0a71159a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -11,8 +11,9 @@ def sqlserver_type end def serialize(value) - return super unless value.acts_like?(:time) - datetime = super.to_s(:_sqlserver_datetime).tap do |v| + value = super + return value unless value.acts_like?(:time) + datetime = value.to_s(:_sqlserver_datetime).tap do |v| fraction = quote_fractional(value) v << ".#{fraction}" end @@ -34,9 +35,7 @@ def quoted(value) private def cast_value(value) - value = value.acts_like?(:time) ? value : super - return unless value - apply_seconds_precision(value) + super.try(:in_time_zone) end def fast_string_to_time(string) @@ -53,6 +52,20 @@ def fast_string_to_time_zone ::Time.zone || ActiveSupport::TimeZone.new('UTC') end + # Copy of module ActiveModel::Type::Helpers::TimeValue with + # else condition using a zone for local parsing. + def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) + return if year.nil? || (year == 0 && mon == 0 && mday == 0) + if offset + time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil + return unless time + time -= offset + is_utc? ? time : time.getlocal + else + fast_string_to_time_zone.local(year, mon, mday, hour, min, sec, microsec) rescue nil + end + end + end end end From 79f062497002ff3b240958e671ba04d86278791d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 19:19:41 -0500 Subject: [PATCH 0555/1412] [Rails5] Update notes. [ci skip] --- RAILS5-FAILS.txt | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt index 1cec1f51e..b6616ad24 100644 --- a/RAILS5-FAILS.txt +++ b/RAILS5-FAILS.txt @@ -987,29 +987,18 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invali 4856 runs, 14140 assertions, 21 failures, 26 errors, 3 skips +* 7 - Incorrect syntax + 3 - Incorrect syntax near '01' + 2 - Incorrect syntax near '07' + 1 - Incorrect syntax near '|' + 1 - Incorrect syntax near 'limit' * 6 - ORDER BY items -* 4 - The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer. +* 3 - The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer. * 2 - ActiveRecord::ValueTooLong expected - - -* 6 - AttributeMethodsTest -* 7 - FinderTest -* 6 - UniquenessValidationTest -* 4 - RelationTest -* 4 - MigrationTest -* 4 - (HasMany|HasAndBelongs)AssociationsTest -* 3 - TransactionCallbacksTest -* 2 - NamedScopingTest -* 2 - DefaultScopingTest -* 2 - CalculationsTest -* 2 - MultipleDbTest -* 2 - EachTest -* 2 - ColumnsTest -* 2 - SchemaDumperTest -* 1 - BasicsTest -* 1 - MultiParameterAttributeTest -* 1 - QuoteBooleanTest -* 1 - LeftOuterJoinAssociationTest -* 1 - EagerAssociationTest -* 1 - DateTest +* 1 - to be an instance of Time, not ActiveSupport::TimeWithZone +* 4 - is invalid in + 2 - is invalid in the select list + 1 - is invalid in the HAVING + 1 - is invalid in the ORDER BY clause +* 2 - No connection pool with id primary found From 030312fdd5a5fdbd1dc9ebeb7c7703366e0fa128 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 19:15:47 -0500 Subject: [PATCH 0556/1412] [Rails5] Coerce ValueTooLong exceptions. Another sp_executesql failure. --- test/cases/coerced_tests.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2b1d25b44..a47cd12da 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2,6 +2,32 @@ +require 'models/event' +class UniquenessValidationTest < ActiveRecord::TestCase + # So sp_executesql swallows this exception. Run without prpared to see it. + coerce_tests! :test_validate_uniqueness_with_limit + def test_validate_uniqueness_with_limit_coerced + connection.unprepared_statement do + assert_raise(ActiveRecord::ValueTooLong) do + Event.create(title: "abcdefgh") + end + end + end + + # So sp_executesql swallows this exception. Run without prpared to see it. + coerce_tests! :test_validate_uniqueness_with_limit_and_utf8 + def test_validate_uniqueness_with_limit_and_utf8_coerced + connection.unprepared_statement do + assert_raise(ActiveRecord::ValueTooLong) do + Event.create(title: "一二三四五六七八") + end + end + end +end + + + + require 'models/event' module ActiveRecord class AdapterTest < ActiveRecord::TestCase From 0443443291bfba04069bbb3caca1ad3a6568211b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 19:34:08 -0500 Subject: [PATCH 0557/1412] Avoid CircleCI doing SQLite and database.yml install. --- circle.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/circle.yml b/circle.yml index dd4e3bc22..ae5da887c 100644 --- a/circle.yml +++ b/circle.yml @@ -24,6 +24,8 @@ dependencies: - rvm-exec 2.3.1 bundle install database: + override: + - echo "Hello" post: - docker info - ./test/bin/setup.sh From bfae5816e0fb343543ae7ab72fbf90128904cae9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 19:49:25 -0500 Subject: [PATCH 0558/1412] [Rails5] Fix Uniqueness tests. * Return bind params vs arel equality nodes. * Conform to `can_perform_case_insensitive_comparison_for?` method. --- .../sqlserver/database_statements.rb | 22 +++---------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 4f3602b8f..53cb7903f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -79,26 +79,10 @@ def exec_rollback_to_savepoint(name = current_savepoint_name) def release_savepoint(name = current_savepoint_name) end - def case_sensitive_modifier(node, table_attribute) - node = Arel::Nodes.build_quoted node, table_attribute - Arel::Nodes::Bin.new(node) - end - - def case_sensitive_comparison(table, attribute, column, value) - if column.case_sensitive? - table[attribute].eq(value) - else - super - end - end - - def case_insensitive_comparison(table, attribute, column, value) - if column.case_sensitive? - super - else - table[attribute].eq(value) - end + def can_perform_case_insensitive_comparison_for?(column) + column.case_sensitive? end + private :can_perform_case_insensitive_comparison_for? # === SQLServer Specific ======================================== # From 649a05b4b19ca794935ba323fd7711ac57f7847e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 20:47:49 -0500 Subject: [PATCH 0559/1412] [Rails5] Fix case_sensitive_comparison. --- .../connection_adapters/sqlserver/database_statements.rb | 8 ++++++++ lib/arel/visitors/sqlserver.rb | 6 +----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 53cb7903f..2a6cbbde5 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -79,6 +79,14 @@ def exec_rollback_to_savepoint(name = current_savepoint_name) def release_savepoint(name = current_savepoint_name) end + def case_sensitive_comparison(table, attribute, column, value) + if !value.nil? && column.collation && !column.case_sensitive? + table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new)) + else + super + end + end + def can_perform_case_insensitive_comparison_for?(column) column.case_sensitive? end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index a88b34d40..dda5a511e 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -19,11 +19,7 @@ def visit_Arel_Nodes_BindParam o, collector def visit_Arel_Nodes_Bin o, collector visit o.expr, collector - if o.expr.val.is_a? Numeric - collector - else - collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} " - end + collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} " end def visit_Arel_Nodes_UpdateStatement(o, a) From 8b07807b44836148aee1cec135a730ae6102f05d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 21:11:36 -0500 Subject: [PATCH 0560/1412] [Rails5] Update notes. --- RAILS5-FAILS.txt | 812 ++++++++++++++--------------------------------- 1 file changed, 240 insertions(+), 572 deletions(-) diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt index b6616ad24..09df787b5 100644 --- a/RAILS5-FAILS.txt +++ b/RAILS5-FAILS.txt @@ -1,255 +1,34 @@ -Run options: --seed=44527 - +Finished in 508.626312s, 9.5473 runs/s, 27.8692 assertions/s. 1) Failure: -UniquenessValidationTest#test_validate_uniqueness_with_limit [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:376]: -ActiveRecord::ValueTooLong expected but nothing was raised. - - - 2) Failure: -UniquenessValidationTest#test_validate_uniqueness_with_limit_and_utf8 [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:395]: -ActiveRecord::ValueTooLong expected but nothing was raised. - - - 3) Error: -UniquenessValidationTest#test_validates_uniqueness_with_newline_chars: -ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''new -line'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'new -line', @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:135:in `test_validates_uniqueness_with_newline_chars' - - - 4) Error: -UniquenessValidationTest#test_validate_case_insensitive_uniqueness_with_special_sql_like_chars: -ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:299:in `test_validate_case_insensitive_uniqueness_with_special_sql_like_chars' - - - 5) Failure: -UniquenessValidationTest#test_validate_case_sensitive_uniqueness [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:318]: -Should be valid - - - 6) Error: -UniquenessValidationTest#test_validate_case_insensitive_uniqueness: -ActiveRecord::StatementInvalid: TinyTds::Error: The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer.: EXEC sp_executesql N'SELECT 1 AS one FROM [topics] WHERE [topics].[title] = N''I''''m unique!'' ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 nvarchar(max), @1 int', @0 = N'I''m unique!', @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations/uniqueness.rb:29:in `validate_each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:151:in `block in validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validator.rb:148:in `validate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `public_send' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:405:in `block in make_lambda' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `catch' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:546:in `block in default_terminator' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:170:in `block in halting' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `block in call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `each' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:454:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:408:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:126:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:455:in `call' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:101:in `__run_callbacks__' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations/callbacks.rb:113:in `run_validations!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activemodel/lib/active_model/validations.rb:338:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:65:in `valid?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:82:in `perform_validations' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/validations.rb:44:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute_methods/dirty.rb:22:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block (2 levels) in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `block in transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb:189:in `within_new_transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:232:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:211:in `transaction' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:392:in `with_transaction_returning_status' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:319:in `block in save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:334:in `rollback_active_record_state!' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/transactions.rb:318:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/suppressor.rb:41:in `save' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/validations/uniqueness_validation_test.rb:245:in `test_validate_case_insensitive_uniqueness' - - - 7) Failure: DirtyTest#test_time_attributes_changes_without_time_zone_by_skip [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:122]: -Expected Sat, 07 Jan 2017 23:06:30 CET +01:00 to be an instance of Time, not ActiveSupport::TimeWithZone. - +Expected Sun, 08 Jan 2017 03:02:16 CET +01:00 to be an instance of Time, not ActiveSupport::TimeWithZone. - 8) Error: -DefaultScopingTest#test_unscope_string_where_clauses_involved: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [developers].* FROM [developers] WHERE (created_at > ''01-07-2016 22:07:01.676'') ORDER BY salary DESC - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/delegation.rb:38:in `collect' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:178:in `test_unscope_string_where_clauses_involved' - - 9) Failure: + 2) Failure: DefaultScopingTest#test_with_abstract_class_scope_should_be_executed_in_correct_context [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:497]: Expected /"lions"."is_vegetarian"/ to match "SELECT [lions].* FROM [lions] WHERE [lions].[is_vegetarian] = 0". - 10) Failure: -CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:107]: + 3) Failure: +CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:133]: Expected: 1 Actual: 3 - 11) Error: + 4) Error: CalculationsTest#test_having_with_strong_parameters: ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [accounts].* FROM [accounts] GROUP BY [accounts].[id] HAVING [accounts].[credit_limit] = @0', N'@0 int', @0 = 50 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' @@ -263,21 +42,21 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' i /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/calculations_test.rb:789:in `test_having_with_strong_parameters' - 12) Error: + 5) Error: MultipleDbTest#test_associations_should_work_when_model_has_no_connection: ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:80:in `visit_Arel_Table' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:76:in `visit_Arel_Table' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:94:in `visit_Arel_Nodes_JoinSource' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:90:in `visit_Arel_Nodes_JoinSource' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:67:in `block in visit_Arel_Nodes_SelectStatement' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `inject' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:63:in `block in visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `inject' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `visit_Arel_Nodes_SelectStatement' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' @@ -300,21 +79,21 @@ ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:114:in `test_associations_should_work_when_model_has_no_connection' - 13) Error: + 6) Error: MultipleDbTest#test_count_on_custom_connection: ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:80:in `visit_Arel_Table' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:76:in `visit_Arel_Table' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:94:in `visit_Arel_Nodes_JoinSource' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:90:in `visit_Arel_Nodes_JoinSource' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:67:in `block in visit_Arel_Nodes_SelectStatement' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `inject' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:66:in `visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:63:in `block in visit_Arel_Nodes_SelectStatement' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `inject' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `visit_Arel_Nodes_SelectStatement' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' @@ -329,7 +108,7 @@ ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:106:in `test_count_on_custom_connection' - 14) Failure: + 7) Failure: EachTest#test_in_batches_should_quote_batch_order [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/batches_test.rb:375]: Query pattern(s) /ORDER BY [posts].[id]/ not found. Queries: @@ -358,31 +137,31 @@ EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 11 ORD EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 11) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - 15) Failure: + 8) Failure: MigrationTest#test_add_table_with_decimals [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:181]: -Expected # to be a kind of Integer, not BigDecimal. +Expected # to be a kind of Integer, not BigDecimal. - 16) Failure: + 9) Failure: MigrationTest#test_migration_sets_internal_metadata_even_when_fully_migrated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:419]: Expected "development" to not be equal to "development". - 17) Failure: + 10) Failure: MigrationTest#test_internal_metadata_stores_environment [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:393]: Expected "development" to not be equal to "development". - 18) Failure: + 11) Failure: MigrationTest#test_filtering_migrations [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223]: [ActiveRecord::StatementInvalid] exception expected, not Class: Message: <"undefined method `second' for nil:NilClass"> ---Backtrace--- -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:193:in `primary_Key_From_Table' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:161:in `make_Fetch_Possible_And_Deterministic' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:123:in `visit_Orders_And_Let_Fetch_Happen' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:69:in `visit_Arel_Nodes_SelectStatement' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:189:in `primary_Key_From_Table' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:157:in `make_Fetch_Possible_And_Deterministic' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:119:in `visit_Orders_And_Let_Fetch_Happen' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:65:in `visit_Arel_Nodes_SelectStatement' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' @@ -404,18 +183,18 @@ Message: <"undefined method `second' for nil:NilClass"> --------------- - 19) Failure: + 12) Failure: SchemaDumperTest#test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:320]: non-primary key id column not preserved. Expected /t\.string\s+"id",.*?null: false$/ to match " t.string \"id\", null: false, collation: \"SQL_Latin1_General_CP1_CI_AS\"". - 20) Failure: + 13) Failure: SchemaDumperTest#test_schema_dump_includes_decimal_options [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:262]: Expected /precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"/ to match "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 0) do\n\n create_table \"nep_ar_internal_metadata\", primary_key: \"key\", id: :string, collation: \"SQL_Latin1_General_CP1_CI_AS\", force: :cascade do |t|\n t.string \"value\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"created_at\", null: false\n t.datetime \"updated_at\", null: false\n end\n\n create_table \"nodes\", force: :cascade do |t|\n t.integer \"tree_id\"\n t.integer \"parent_id\"\n t.string \"name\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"updated_at\"\n end\n\n create_table \"non_poly_ones\", force: :cascade do |t|\n end\n\n create_table \"non_poly_twos\", force: :cascade do |t|\n end\n\n create_table \"notifications\", force: :cascade do |t|\n t.string \"message\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n end\n\n create_table \"numeric_data\", force: :cascade do |t|\n t.decimal \"bank_balance\", precision: 10, scale: 2\n t.decimal \"big_bank_balance\", precision: 15, scale: 2\n t.decimal \"world_population\", precision: 10, scale: 0\n t.decimal \"my_house_population\", precision: 2, scale: 0\n t.decimal \"decimal_number_with_default\", precision: 3, scale: 2, default: 2.78\n t.float \"temperature\"\n t.decimal \"atoms_in_universe\", precision: 38, scale: 0\n end\n\nend\n". - 21) Failure: + 14) Failure: BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:269]: --- expected +++ actual @@ -425,7 +204,7 @@ BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_defaul - 22) Failure: + 15) Failure: BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:241]: --- expected +++ actual @@ -435,44 +214,44 @@ BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_defaul - 23) Failure: + 16) Failure: MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: Expected: 2004-06-24 23:24:00 UTC Actual: 2004-06-24 16:24:00 UTC - 24) Failure: + 17) Failure: ActiveRecord::ConnectionAdapters::QuoteBooleanTest#test_quote_returns_frozen_string [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/quoting_test.rb:159]: Expected "1" to be frozen?. - 25) Error: -ActiveRecord::Migration::ColumnsTest#test_change_column: -NoMethodError: undefined method `sql_type' for nil:NilClass - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/quoting.rb:39:in `quote_default_expression' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:128:in `change_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `change_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:203:in `test_change_column' + 18) Error: +ActiveRecord::Migration::ColumnsTest#test_rename_column_using_symbol_arguments: +ActiveRecord::ActiveRecordError: No such column: test_models.first_name + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:448:in `detect_column_for!' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:149:in `rename_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `rename_column' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:33:in `test_rename_column_using_symbol_arguments' - 26) Failure: + 19) Failure: ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: Expected false to be truthy. - 27) Error: + 20) Error: LeftOuterJoinAssociationTest#test_does_not_override_select: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC sp_executesql N'SELECT authors.name, (authors.author_address_id || '' '' || authors.author_address_extra_id) as addr_id FROM [authors] LEFT OUTER JOIN [posts] ON [posts].[author_id] = [authors].[id] ORDER BY [authors].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' @@ -490,7 +269,7 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/left_outer_join_association_test.rb:78:in `test_does_not_override_select' - 28) Failure: + 21) Failure: AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: --- expected +++ actual @@ -500,52 +279,66 @@ AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencolli - 29) Error: -FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '07'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE (written_on = ''''07-16-2003 14:28:11.223'''') ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + 22) Failure: +FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:838]: +--- expected ++++ actual +@@ -1 +1 @@ +-# ++nil + + + + 23) Error: +FinderTest#test_exists_with_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:838:in `block (2 levels) in test_condition_local_time_interpolation_with_default_timezone_utc' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:82:in `with_timezone_config' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:836:in `block in test_condition_local_time_interpolation_with_default_timezone_utc' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/helper.rb:61:in `with_env_tz' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:835:in `test_condition_local_time_interpolation_with_default_timezone_utc' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' + + + 24) Error: +FinderTest#test_exists_with_distinct_association_includes_limit_and_order: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' - 30) Failure: + 25) Failure: FinderTest#test_hash_condition_find_malformed [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:752]: [ActiveRecord::StatementInvalid] exception expected, not Class: -Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> +Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> ---Backtrace--- -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:254:in `sp_executesql_sql_type' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:240:in `block in sp_executesql_types_and_parameters' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each_with_index' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `sp_executesql_types_and_parameters' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:246:in `sp_executesql_sql_type' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:232:in `block in sp_executesql_types_and_parameters' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each_with_index' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql_types_and_parameters' +/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:223:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' @@ -564,98 +357,36 @@ Message: <"sp_executesql_sql_type can not find sql type for attr # ++nil - 32) Error: -FinderTest#test_exists_with_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' - 33) Error: + 27) Error: FinderTest#test_exists_with_distinct_association_includes_and_limit: ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:220:in `test_exists_with_distinct_association_includes_and_limit' - 34) Error: -FinderTest#test_exists_with_distinct_association_includes_limit_and_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' - - - 35) Error: + 28) Error: EagerAssociationTest#test_0024_including association based on sql condition and no database column coerced: ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near 'limit'.: EXEC sp_executesql N'SELECT owners.*, ( @@ -665,16 +396,16 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near 'limit'.: limit 1 ) as last_pet_id FROM [owners] ORDER BY [owners].[owner_id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' @@ -689,52 +420,29 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near 'limit'.: /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:271:in `block in ' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:297:in `block in ' - 36) Failure: + 29) Failure: HasManyAssociationsTest#test_do_not_call_callbacks_for_delete_all [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_many_associations_test.rb:248]: bulbs should have been deleted using :delete_all strategy. Expected: 0 Actual: 1 - 37) Error: -HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' - - - 38) Error: + 30) Error: HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 61, @2 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 54, @2 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' @@ -743,19 +451,19 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' - 39) Error: + 31) Error: HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' @@ -765,19 +473,42 @@ ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in th /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' - 40) Error: + 32) Error: +HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: +ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' + + + 33) Error: DateTest#test_date_with_string_value: ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `to_s' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `serialize' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute.rb:51:in `value_for_database' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/query_attribute.rb:11:in `value_for_database' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:259:in `sp_executesql_sql_param' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:241:in `block in sp_executesql_types_and_parameters' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `each_with_index' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:239:in `sp_executesql_types_and_parameters' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:251:in `sp_executesql_sql_param' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:233:in `block in sp_executesql_types_and_parameters' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each_with_index' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql_types_and_parameters' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:223:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' @@ -788,44 +519,49 @@ ArgumentError: wrong number of arguments (given 1, expected 0) /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/date_test.rb:14:in `test_date_with_string_value' - 41) Error: -RelationTest#test_having_with_binds_for_both_where_and_having: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + 34) Error: +RelationTest#test_reverse_order_with_function: +ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' - 42) Error: + 35) Error: RelationTest#test_multiple_where_and_having_clauses: ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' @@ -838,49 +574,44 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is inva /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 43) Error: -RelationTest#test_reverse_order_with_function: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + 36) Error: +RelationTest#test_having_with_binds_for_both_where_and_having: +ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' + /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - 44) Error: + 37) Error: RelationTest#test_reverse_order_with_function_other_predicates: ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' @@ -898,73 +629,19 @@ ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized bui /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' - 45) Error: -NamedScopingTest#test_method_missing_priority_when_delegating: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: SELECT [topics].* FROM [topics] WHERE (written_on <= ''01-07-2017 22:28:17.729'') AND (written_on >= ''01-06-2017 22:28:17.739'') - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:55:in `test_method_missing_priority_when_delegating' - - - 46) Error: -NamedScopingTest#test_nested_scopes_queries_size: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '01'.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] WHERE [topics].[approved] = @0 AND [topics].[author_name] = @1 AND (replies_count > 0) AND (written_on < ''''01-07-2017 22:28:19.127'''')', N'@0 bit, @1 nvarchar(4000)', @0 = 1, @1 = N'lifo' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:479:in `block in test_nested_scopes_queries_size' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/named_scoping_test.rb:478:in `test_nested_scopes_queries_size' - - - 47) Error: + 38) Error: ActiveRecord::CollectionCacheKeyTest#test_0011_cache_key for queries with offset which return 0 rows: ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT COUNT(*) AS [size], MAX([developers].[updated_at]) AS timestamp FROM [developers] ORDER BY [developers].[id] ASC OFFSET @0 ROWS', N'@0 int', @0 = 20 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:330:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:321:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:300:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `block in raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:295:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:234:in `sp_executesql' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' + /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' @@ -975,17 +652,8 @@ ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invali /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/collection_cache_key_test.rb:80:in `block in ' -4854 runs, 13971 assertions, 30 failures, 36 errors, 3 skips -√ 3 - Gem `sqlite3` load error. -4854 runs, 13974 assertions, 30 failures, 33 errors, 3 skips -√ 2 - DefaultNumbersTest -√ 3 - AdapterTest -4856 runs, 13978 assertions, 26 failures, 32 errors, 3 skips -4856 runs, 13978 assertions, 25 failures, 32 errors, 3 skips -√ 4 - AttributeMethodsTest -4856 runs, 14063 assertions, 25 failures, 26 errors, 3 skips -4856 runs, 14140 assertions, 21 failures, 26 errors, 3 skips - +4856 runs, 14170 assertions, 18 failures, 23 errors, 3 skips +4856 runs, 14175 assertions, 20 failures, 18 errors, 3 skips * 7 - Incorrect syntax 3 - Incorrect syntax near '01' From 5d30181b2bfdf9f0d006013332b991eb58692d70 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 7 Jan 2017 21:11:46 -0500 Subject: [PATCH 0561/1412] [Rails5] Allow simple "created_at > ?" conditions. --- lib/active_record/connection_adapters/sqlserver/quoting.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 95d26ffa8..44631c951 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -62,9 +62,9 @@ def unquoted_false def quoted_date(value) if value.acts_like?(:date) - Type::Date.new.serialize(value).quoted + Type::Date.new.serialize(value) else value.acts_like?(:time) - Type::DateTime.new.serialize(value).quoted + Type::DateTime.new.serialize(value) end end From cef7c28b51ff65b1dd59ae154fac60898a366afd Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 09:29:07 -0500 Subject: [PATCH 0562/1412] [Rails5] Coerice quoting in test. --- test/cases/coerced_tests.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a47cd12da..3ba063c27 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -254,6 +254,14 @@ class DatabaseTasksDropAllTest < ActiveRecord::TestCase class DefaultScopingTest < ActiveRecord::TestCase # We are not doing order duplicate removal anymore. coerce_tests! :test_order_in_default_scope_should_not_prevail + + # Use our escaped format in assertion. + coerce_tests! :test_with_abstract_class_scope_should_be_executed_in_correct_context + def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced + vegetarian_pattern, gender_pattern = [/[lions].[is_vegetarian]/, /[lions].[gender]/] + assert_match vegetarian_pattern, Lion.all.to_sql + assert_match gender_pattern, Lion.female.to_sql + end end From d07ea03270c8e64e1848ed835bbbf01e3c85af96 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 09:54:45 -0500 Subject: [PATCH 0563/1412] [Rails5] Remove failure tracking notes. Numbers getting low. --- RAILS5-FAILS.txt | 672 ----------------------------------------------- RAILS5-TODO.md | 29 -- 2 files changed, 701 deletions(-) delete mode 100644 RAILS5-FAILS.txt diff --git a/RAILS5-FAILS.txt b/RAILS5-FAILS.txt deleted file mode 100644 index 09df787b5..000000000 --- a/RAILS5-FAILS.txt +++ /dev/null @@ -1,672 +0,0 @@ -Finished in 508.626312s, 9.5473 runs/s, 27.8692 assertions/s. - - 1) Failure: -DirtyTest#test_time_attributes_changes_without_time_zone_by_skip [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/dirty_test.rb:122]: -Expected Sun, 08 Jan 2017 03:02:16 CET +01:00 to be an instance of Time, not ActiveSupport::TimeWithZone. - - - 2) Failure: -DefaultScopingTest#test_with_abstract_class_scope_should_be_executed_in_correct_context [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/scoping/default_scoping_test.rb:497]: -Expected /"lions"."is_vegetarian"/ to match "SELECT [lions].* FROM [lions] WHERE [lions].[is_vegetarian] = 0". - - - 3) Failure: -CalculationsTest#test_limit_is_kept_coerced [/Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:133]: -Expected: 1 - Actual: 3 - - - 4) Error: -CalculationsTest#test_having_with_strong_parameters: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'accounts.credit_limit' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [accounts].* FROM [accounts] GROUP BY [accounts].[id] HAVING [accounts].[credit_limit] = @0', N'@0 int', @0 = 50 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/delegation.rb:38:in `[]' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/calculations_test.rb:789:in `test_having_with_strong_parameters' - - - 5) Error: -MultipleDbTest#test_associations_should_work_when_model_has_no_connection: -ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:76:in `visit_Arel_Table' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:90:in `visit_Arel_Nodes_JoinSource' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:63:in `block in visit_Arel_Nodes_SelectStatement' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `inject' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `visit_Arel_Nodes_SelectStatement' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:3:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:115:in `block in test_associations_should_work_when_model_has_no_connection' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/test_case.rb:83:in `assert_nothing_raised' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:114:in `test_associations_should_work_when_model_has_no_connection' - - - 6) Error: -MultipleDbTest#test_count_on_custom_connection: -ActiveRecord::ConnectionNotEstablished: No connection pool with id primary found. - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb:882:in `retrieve_connection' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:128:in `retrieve_connection' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_handling.rb:91:in `connection' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:76:in `visit_Arel_Table' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:90:in `visit_Arel_Nodes_JoinSource' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/to_sql.rb:250:in `visit_Arel_Nodes_SelectCore' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:63:in `block in visit_Arel_Nodes_SelectStatement' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `inject' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:62:in `visit_Arel_Nodes_SelectStatement' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:248:in `execute_simple_calculation' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:207:in `perform_calculation' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:121:in `calculate' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/calculations.rb:40:in `count' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:13:in `count' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiple_db_test.rb:106:in `test_count_on_custom_connection' - - - 7) Failure: -EachTest#test_in_batches_should_quote_batch_order [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/batches_test.rb:375]: -Query pattern(s) /ORDER BY [posts].[id]/ not found. -Queries: -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 1) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 2 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 2) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 3 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 3) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 4 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 4) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 5 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 5) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 6 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 6) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 7 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 7) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 8 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 8) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 9 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 9) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 10 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 10) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[id] = 11 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 -EXEC sp_executesql N'SELECT [posts].[id] FROM [posts] WHERE ([posts].[id] > 11) ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - - - 8) Failure: -MigrationTest#test_add_table_with_decimals [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:181]: -Expected # to be a kind of Integer, not BigDecimal. - - - 9) Failure: -MigrationTest#test_migration_sets_internal_metadata_even_when_fully_migrated [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:419]: -Expected "development" to not be equal to "development". - - - 10) Failure: -MigrationTest#test_internal_metadata_stores_environment [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:393]: -Expected "development" to not be equal to "development". - - - 11) Failure: -MigrationTest#test_filtering_migrations [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223]: -[ActiveRecord::StatementInvalid] exception expected, not -Class: -Message: <"undefined method `second' for nil:NilClass"> ----Backtrace--- -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:189:in `primary_Key_From_Table' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:157:in `make_Fetch_Possible_And_Deterministic' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:119:in `visit_Orders_And_Let_Fetch_Happen' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/arel/visitors/sqlserver.rb:65:in `visit_Arel_Nodes_SelectStatement' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:13:in `visit' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/arel-7.1.4/lib/arel/visitors/reduce.rb:7:in `accept' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb:8:in `accept' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:12:in `to_sql' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:32:in `select_all' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:3:in `first' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration_test.rb:223:in `block in test_filtering_migrations' ---------------- - - - 12) Failure: -SchemaDumperTest#test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:320]: -non-primary key id column not preserved. -Expected /t\.string\s+"id",.*?null: false$/ to match " t.string \"id\", null: false, collation: \"SQL_Latin1_General_CP1_CI_AS\"". - - - 13) Failure: -SchemaDumperTest#test_schema_dump_includes_decimal_options [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/schema_dumper_test.rb:262]: -Expected /precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"/ to match "# This file is auto-generated from the current state of the database. Instead\n# of editing this file, please use the migrations feature of Active Record to\n# incrementally modify your database, and then regenerate this schema definition.\n#\n# Note that this schema.rb definition is the authoritative source for your\n# database schema. If you need to create the application database on another\n# system, you should be using db:schema:load, not running all the migrations\n# from scratch. The latter is a flawed and unsustainable approach (the more migrations\n# you'll amass, the slower it'll run and the greater likelihood for issues).\n#\n# It's strongly recommended that you check this file into your version control system.\n\nActiveRecord::Schema.define(version: 0) do\n\n create_table \"nep_ar_internal_metadata\", primary_key: \"key\", id: :string, collation: \"SQL_Latin1_General_CP1_CI_AS\", force: :cascade do |t|\n t.string \"value\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"created_at\", null: false\n t.datetime \"updated_at\", null: false\n end\n\n create_table \"nodes\", force: :cascade do |t|\n t.integer \"tree_id\"\n t.integer \"parent_id\"\n t.string \"name\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n t.datetime \"updated_at\"\n end\n\n create_table \"non_poly_ones\", force: :cascade do |t|\n end\n\n create_table \"non_poly_twos\", force: :cascade do |t|\n end\n\n create_table \"notifications\", force: :cascade do |t|\n t.string \"message\", collation: \"SQL_Latin1_General_CP1_CI_AS\"\n end\n\n create_table \"numeric_data\", force: :cascade do |t|\n t.decimal \"bank_balance\", precision: 10, scale: 2\n t.decimal \"big_bank_balance\", precision: 15, scale: 2\n t.decimal \"world_population\", precision: 10, scale: 0\n t.decimal \"my_house_population\", precision: 2, scale: 0\n t.decimal \"decimal_number_with_default\", precision: 3, scale: 2, default: 2.78\n t.float \"temperature\"\n t.decimal \"atoms_in_universe\", precision: 38, scale: 0\n end\n\nend\n". - - - 14) Failure: -BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:269]: ---- expected -+++ actual -@@ -1 +1 @@ --[0, 0, 1, 1, 1, 2000, 6, 1, false, "EST"] -+[0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"] - - - - 15) Failure: -BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/base_test.rb:241]: ---- expected -+++ actual -@@ -1 +1 @@ --[0, 0, 6, 1, 1, 2000, 6, 1, false, "UTC"] -+[0, 0, 0, 1, 1, 2000, 6, 1, false, "CST"] - - - - 16) Failure: -MultiParameterAttributeTest#test_multiparameter_attributes_on_time_with_time_zone_aware_attributes [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/multiparameter_attributes_test.rb:209]: -Expected: 2004-06-24 23:24:00 UTC - Actual: 2004-06-24 16:24:00 UTC - - - 17) Failure: -ActiveRecord::ConnectionAdapters::QuoteBooleanTest#test_quote_returns_frozen_string [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/quoting_test.rb:159]: -Expected "1" to be frozen?. - - - 18) Error: -ActiveRecord::Migration::ColumnsTest#test_rename_column_using_symbol_arguments: -ActiveRecord::ActiveRecordError: No such column: test_models.first_name - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:448:in `detect_column_for!' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/schema_statements.rb:149:in `rename_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/helper.rb:36:in `rename_column' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:33:in `test_rename_column_using_symbol_arguments' - - - 19) Failure: -ActiveRecord::Migration::ColumnsTest#test_rename_column_with_sql_reserved_word [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/migration/columns_test.rb:90]: -Expected false to be truthy. - - - 20) Error: -LeftOuterJoinAssociationTest#test_does_not_override_select: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near '|'.: EXEC sp_executesql N'SELECT authors.name, (authors.author_address_id || '' '' || authors.author_address_extra_id) as addr_id FROM [authors] LEFT OUTER JOIN [posts] ON [posts].[author_id] = [authors].[id] ORDER BY [authors].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/left_outer_join_association_test.rb:78:in `test_does_not_override_select' - - - 21) Failure: -AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/attribute_methods_test.rb:719]: ---- expected -+++ actual -@@ -1 +1 @@ --Sat, 01 Jan 2000 10:00:00 PST -08:00 -+Sat, 01 Jan 2000 02:00:00 PST -08:00 - - - - 22) Failure: -FinderTest#test_condition_local_time_interpolation_with_default_timezone_utc [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:838]: ---- expected -+++ actual -@@ -1 +1 @@ --# -+nil - - - - 23) Error: -FinderTest#test_exists_with_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [topics] ORDER BY [topics].[id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:209:in `test_exists_with_order' - - - 24) Error: -FinderTest#test_exists_with_distinct_association_includes_limit_and_order: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:226:in `test_exists_with_distinct_association_includes_limit_and_order' - - - 25) Failure: -FinderTest#test_hash_condition_find_malformed [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:752]: -[ActiveRecord::StatementInvalid] exception expected, not -Class: -Message: <"sp_executesql_sql_type can not find sql type for attr #, @original_attribute=nil, @value=true, @value_for_database=true>"> ----Backtrace--- -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:246:in `sp_executesql_sql_type' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:232:in `block in sp_executesql_types_and_parameters' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each_with_index' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql_types_and_parameters' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:223:in `sp_executesql' -/Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' -/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:753:in `block in test_hash_condition_find_malformed' ---------------- - - - 26) Failure: -FinderTest#test_condition_utc_time_interpolation_with_default_timezone_local [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:820]: ---- expected -+++ actual -@@ -1 +1 @@ --# -+nil - - - - 27) Error: -FinderTest#test_exists_with_distinct_association_includes_and_limit: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [posts] LEFT OUTER JOIN [comments] ON [comments].[post_id] = [posts].[id] AND [comments].[type] IN (N''SpecialComment'', N''SubSpecialComment'') INNER JOIN [categorizations] ON [posts].[id] = [categorizations].[post_id] WHERE [categorizations].[author_id] = @0 AND [posts].[id] = 1 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/finder_test.rb:220:in `test_exists_with_distinct_association_includes_and_limit' - - - 28) Error: -EagerAssociationTest#test_0024_including association based on sql condition and no database column coerced: -ActiveRecord::StatementInvalid: TinyTds::Error: Incorrect syntax near 'limit'.: EXEC sp_executesql N'SELECT - owners.*, ( - select p.pet_id from pets p - where p.owner_id = owners.owner_id - order by p.name desc - limit 1 - ) as last_pet_id - FROM [owners] ORDER BY [owners].[owner_id] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/test/cases/coerced_tests.rb:297:in `block in ' - - - 29) Failure: -HasManyAssociationsTest#test_do_not_call_callbacks_for_delete_all [/Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_many_associations_test.rb:248]: -bulbs should have been deleted using :delete_all strategy. -Expected: 0 - Actual: 1 - - - 30) Error: -HasAndBelongsToManyAssociationsTest#test_include_returns_false_for_non_matching_record_to_verify_scoping: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 54, @2 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:569:in `test_include_returns_false_for_non_matching_record_to_verify_scoping' - - - 31) Error: -HasAndBelongsToManyAssociationsTest#test_has_and_belongs_to_many: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @1 ROWS ONLY', N'@0 int, @1 int', @0 = 1, @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:350:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/has_many_association.rb:56:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:813:in `empty?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:170:in `test_has_and_belongs_to_many' - - - 32) Error: -HasAndBelongsToManyAssociationsTest#test_include_checks_if_record_exists_if_target_not_loaded: -ActiveRecord::StatementInvalid: TinyTds::Error: ORDER BY items must appear in the select list if SELECT DISTINCT is specified.: EXEC sp_executesql N'SELECT DISTINCT 1 AS one FROM [developers] INNER JOIN [developers_projects] ON [developers].[id] = [developers_projects].[developer_id] WHERE [developers_projects].[project_id] = @0 AND [developers].[id] = @1 ORDER BY [developers].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 int, @1 int, @2 int', @0 = 1, @1 = 11, @2 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:7:in `select_rows' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:54:in `select_value' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:335:in `exists?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_association.rb:407:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/associations/collection_proxy.rb:898:in `include?' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:559:in `block in test_include_checks_if_record_exists_if_target_not_loaded' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/test_case.rb:38:in `assert_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb:558:in `test_include_checks_if_record_exists_if_target_not_loaded' - - - 33) Error: -DateTest#test_date_with_string_value: -ArgumentError: wrong number of arguments (given 1, expected 0) - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `to_s' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/date.rb:13:in `serialize' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/attribute.rb:51:in `value_for_database' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/query_attribute.rb:11:in `value_for_database' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:251:in `sp_executesql_sql_param' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:233:in `block in sp_executesql_types_and_parameters' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `each_with_index' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:231:in `sp_executesql_types_and_parameters' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:223:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/statement_cache.rb:109:in `execute' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/core.rb:203:in `find_by' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/date_test.rb:14:in `test_date_with_string_value' - - - 34) Error: -RelationTest#test_reverse_order_with_function: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY length(title) DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:229:in `test_reverse_order_with_function' - - - 35) Error: -RelationTest#test_multiple_where_and_having_clauses: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 AND [posts].[title] = @1 GROUP BY [posts].[id] HAVING [posts].[id] = @2 AND [posts].[id] = @3', N'@0 nvarchar(4000), @1 nvarchar(4000), @2 int, @3 int', @0 = N'Welcome to the weblog', @1 = N'Welcome to the weblog', @2 = 1, @3 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - - - 36) Error: -RelationTest#test_having_with_binds_for_both_where_and_having: -ActiveRecord::StatementInvalid: TinyTds::Error: Column 'posts.author_id' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT [posts].* FROM [posts] WHERE [posts].[title] = @0 GROUP BY [posts].[id] HAVING [posts].[id] = @1', N'@0 nvarchar(4000), @1 int', @0 = N'Welcome to the weblog', @1 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:377:in `select_prepared' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:39:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:668:in `==' - - - 37) Error: -RelationTest#test_reverse_order_with_function_other_predicates: -ActiveRecord::StatementInvalid: TinyTds::Error: 'length' is not a recognized built-in function name.: EXEC sp_executesql N'SELECT [topics].* FROM [topics] ORDER BY author_name DESC, length(title) DESC, id DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY', N'@0 int', @0 = 1 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/querying.rb:39:in `find_by_sql' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:702:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/record_fetch_warning.rb:17:in `exec_queries' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:583:in `load' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:260:in `records' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:256:in `to_a' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:563:in `find_nth_with_limit' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:592:in `find_nth_with_limit_and_offset' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:545:in `find_nth' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation/finder_methods.rb:122:in `first' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/relations_test.rb:234:in `test_reverse_order_with_function_other_predicates' - - - 38) Error: -ActiveRecord::CollectionCacheKeyTest#test_0011_cache_key for queries with offset which return 0 rows: -ActiveRecord::StatementInvalid: TinyTds::Error: Column "developers.id" is invalid in the ORDER BY clause because it is not contained in either an aggregate function or the GROUP BY clause.: EXEC sp_executesql N'SELECT COUNT(*) AS [size], MAX([developers].[updated_at]) AS timestamp FROM [developers] ORDER BY [developers].[id] ASC OFFSET @0 ROWS', N'@0 int', @0 = 20 - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `each' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:322:in `handle_to_names_and_values_dblib' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:313:in `handle_to_names_and_values' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:292:in `_raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `block in raw_select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:589:in `block in log' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activesupport/lib/active_support/notifications/instrumenter.rb:21:in `instrument' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:583:in `log' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:287:in `raw_select' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:226:in `sp_executesql' - /Users/kencollins/Repositories/activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/database_statements.rb:19:in `exec_query' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:373:in `select' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:41:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb:95:in `select_all' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb:48:in `select_one' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/collection_cache_key.rb:21:in `collection_cache_key' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/lib/active_record/relation.rb:337:in `cache_key' - /Users/kencollins/.rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/bundler/gems/rails-3843ee5ac37f/activerecord/test/cases/collection_cache_key_test.rb:80:in `block in ' - - -4856 runs, 14170 assertions, 18 failures, 23 errors, 3 skips -4856 runs, 14175 assertions, 20 failures, 18 errors, 3 skips - -* 7 - Incorrect syntax - 3 - Incorrect syntax near '01' - 2 - Incorrect syntax near '07' - 1 - Incorrect syntax near '|' - 1 - Incorrect syntax near 'limit' -* 6 - ORDER BY items -* 3 - The number of rows provided for a TOP or FETCH clauses row count parameter must be an integer. -* 2 - ActiveRecord::ValueTooLong expected -* 1 - to be an instance of Time, not ActiveSupport::TimeWithZone -* 4 - is invalid in - 2 - is invalid in the select list - 1 - is invalid in the HAVING - 1 - is invalid in the ORDER BY clause -* 2 - No connection pool with id primary found - diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 640978099..984b4b7e7 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -4,35 +4,6 @@ Misc remidners while in the heat of adapting the adpater. -## TEST FAILURE SUMMARY - -I am currently using `TESTOPTS="--seed=44527"` to get a consistant order for now. Also, the CI suite is locked down to using `ONLY_SQLSERVER=1` to make PRs feel good. Soon we will want to change this. - -* 10 - AttributeMethodsTest -* 6 - FinderTest -* 6 - Gem `sqlite3` load error. -* 6 - UniquenessValidationTest -* 4 - RelationTest -* 4 - MigrationTest -* 4 - (HasMany|HasAndBelongs)AssociationsTest -* 3 - AdapterTest -* 3 - TransactionCallbacksTest -* 2 - NamedScopingTest -* 2 - DefaultNumbersTest -* 2 - DefaultScopingTest -* 2 - CalculationsTest -* 2 - MultipleDbTest -* 2 - EachTest -* 2 - ColumnsTest -* 2 - SchemaDumperTest -* 1 - BasicsTest -* 1 - MultiParameterAttributeTest -* 1 - QuoteBooleanTest -* 1 - LeftOuterJoinAssociationTest -* 1 - EagerAssociationTest -* 1 - DateTest - - ## LONG TERM After we get some tests passing From 50153abc74e91b1f2ab914c2cf6d639f5e813087 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 12:40:47 -0500 Subject: [PATCH 0564/1412] [Rails5] New SQL counter hacks and helper. --- test/cases/coerced_tests.rb | 2 +- test/cases/helper_sqlserver.rb | 1 + test/support/sql_counter_sqlserver.rb | 37 ++++++++++++--------------- 3 files changed, 18 insertions(+), 22 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3ba063c27..8d6f6e010 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -129,7 +129,7 @@ def test_should_return_decimal_average_of_integer_field_coerced coerce_tests! :test_limit_is_kept def test_limit_is_kept_coerced - queries = assert_sql { Account.limit(1).count } + queries = capture_sql_ss { Account.limit(1).count } assert_equal 1, queries.length queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1} end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index c4d0b081d..454b6b559 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -17,6 +17,7 @@ class TestCase < ActiveSupport::TestCase include ARTest::SQLServer::CoerceableTest, ARTest::SQLServer::ConnectionReflection, + ARTest::SQLServer::SqlCounterSqlserver, ActiveSupport::Testing::Stream let(:logger) { ActiveRecord::Base.logger } diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index c230f5ee3..756d7e7a1 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -1,32 +1,27 @@ module ARTest module SQLServer - extend self + module SqlCounterSqlserver - attr_accessor :sql_counter_listenter + # Only return the log vs. log_all + def capture_sql_ss + ActiveRecord::SQLCounter.clear_log + yield + ActiveRecord::SQLCounter.log.dup + end - def ignored_sql - [ /SELECT SCOPE_IDENTITY/, - /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/, - /SELECT @@version/, - /SELECT @@TRANCOUNT/, - /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, - /SELECT CAST\(.* AS .*\) AS value/ ] end - def sql_counter_listenters - ActiveSupport::Notifications.notifier.listeners_for('sql.active_record').select do |listener| - listener.inspect =~ /ActiveRecord::SQLCounter/ - end - end + ignored_sql = [ + /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/im, + /SELECT @@version/, + /SELECT @@TRANCOUNT/, + /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, + /SELECT CAST\(.* AS .*\) AS value/ + ] - def sql_counter_listenters_unsubscribe - sql_counter_listenters.each { |listener| ActiveSupport::Notifications.unsubscribe(listener) } - end + sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first + sqlcounter.instance_variable_set :@ignore, Regexp.union(ignored_sql.push(sqlcounter.ignore)) end end - -ActiveRecord::SQLCounter.ignored_sql.concat ARTest::SQLServer.ignored_sql -ARTest::SQLServer.sql_counter_listenters_unsubscribe -ARTest::SQLServer.sql_counter_listenter = ActiveSupport::Notifications.subscribe 'sql.active_record', ActiveRecord::SQLCounter.new From 7334e44c339cd18bb88c73046c8a3cd7189c1855 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 16:02:16 -0500 Subject: [PATCH 0565/1412] [Rails5] Pass MultipleDbTest with removed connections. --- lib/arel/visitors/sqlserver.rb | 10 +++++++--- test/config.yml | 1 - 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index dda5a511e..aead069c7 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -73,9 +73,13 @@ def visit_Arel_Table o, collector # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 - table_name = if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? - remote_server_table_name(o) - else + table_name = begin + if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? + remote_server_table_name(o) + else + quote_table_name(o.name) + end + rescue Exception => e quote_table_name(o.name) end if o.table_alias diff --git a/test/config.yml b/test/config.yml index fb507593a..4f4e2bf43 100644 --- a/test/config.yml +++ b/test/config.yml @@ -1,4 +1,3 @@ -default_connection: dblib default_connection_info: &default_connection_info adapter: sqlserver From c3ead9f5f89fb20141b123798dc9274d5658d008 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 16:07:25 -0500 Subject: [PATCH 0566/1412] [Rails5] Coerce EachTest for in batches. --- test/cases/coerced_tests.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8d6f6e010..ace9f9bef 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -270,6 +270,7 @@ def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced require 'models/post' require 'models/subscriber' class EachTest < ActiveRecord::TestCase + # Quoting in tests does not cope with bracket quoting. coerce_tests! :test_find_in_batches_should_quote_batch_order def test_find_in_batches_should_quote_batch_order_coerced c = Post.connection @@ -280,6 +281,18 @@ def test_find_in_batches_should_quote_batch_order_coerced end end end + + # Quoting in tests does not cope with bracket quoting. + coerce_tests! :test_in_batches_should_quote_batch_order + def test_in_batches_should_quote_batch_order_coerced + c = Post.connection + assert_sql(/ORDER BY \[posts\]\.\[id\]/) do + Post.in_batches(of: 1) do |relation| + assert_kind_of ActiveRecord::Relation, relation + assert_kind_of Post, relation.first + end + end + end end From 25baf891dce5c8f13e211cd7e2af4304964a3270 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 16:11:28 -0500 Subject: [PATCH 0567/1412] [Rails5] Coerce LENGTH function-based test to LEN. --- test/cases/coerced_tests.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index ace9f9bef..39304c6ae 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -521,6 +521,22 @@ def test_cache_does_not_wrap_string_results_in_arrays_coerced require 'models/post' class RelationTest < ActiveRecord::TestCase + # Use LEN vs LENGTH function. + coerce_tests! :test_reverse_order_with_function + def test_reverse_order_with_function_coerced + topics = Topic.order("LEN(title)").reverse_order + assert_equal topics(:second).title, topics.first.title + end + + # Use LEN vs LENGTH function. + coerce_tests! :test_reverse_order_with_function_other_predicates + def test_reverse_order_with_function_other_predicates_coerced + topics = Topic.order("author_name, LEN(title), id").reverse_order + assert_equal topics(:second).title, topics.first.title + topics = Topic.order("LEN(author_name), id, LEN(title)").reverse_order + assert_equal topics(:fifth).title, topics.first.title + end + # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering} From ab3413e710c9f1ac37c95b3b1b38fd367a44332b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 16:40:48 -0500 Subject: [PATCH 0568/1412] [Rails5] Fix `test_date_with_string_value` test. --- lib/active_record/connection_adapters/sqlserver/type/date.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 492377ef7..87fa8333a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -10,7 +10,7 @@ def sqlserver_type def serialize(value) return unless value.present? - date = value.to_s(:_sqlserver_dateformat) + date = super(value).to_s(:_sqlserver_dateformat) Data.new date, self end From 56683dd2cf345c08772b9299bd51a76aafa7f5a9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 18:25:28 -0500 Subject: [PATCH 0569/1412] [Rails5] SQL Server does decimal sans scale vs BigInt. cc @sgrif --- test/cases/coerced_tests.rb | 42 ++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 39304c6ae..f16ba3757 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -77,9 +77,7 @@ def test_column_names_are_escaped_coerced assert_equal '[t]]]', conn.quote_column_name('t]') end - # PENDING: [Rails5.x] Remove coerced tests and use simple symbol types.. - # This test has a few problems. First, it would require that we use the - # `Type::SQLServer::BigInteger.new(limit: 8)` for the `world_population` attribute. + # We do not have do the DecimalWithoutScale type. coerce_tests! :test_numeric_fields coerce_tests! :test_numeric_fields_with_scale @@ -212,6 +210,44 @@ def test_remove_column_with_multi_column_index_coerced +class MigrationTest < ActiveRecord::TestCase + # We do not have do the DecimalWithoutScale type. + coerce_tests! :test_add_table_with_decimals + def test_add_table_with_decimals_coerced + Person.connection.drop_table :big_numbers rescue nil + assert !BigNumber.table_exists? + GiveMeBigNumbers.up + BigNumber.reset_column_information + assert BigNumber.create( + :bank_balance => 1586.43, + :big_bank_balance => BigDecimal("1000234000567.95"), + :world_population => 6000000000, + :my_house_population => 3, + :value_of_e => BigDecimal("2.7182818284590452353602875") + ) + b = BigNumber.first + assert_not_nil b + assert_not_nil b.bank_balance + assert_not_nil b.big_bank_balance + assert_not_nil b.world_population + assert_not_nil b.my_house_population + assert_not_nil b.value_of_e + assert_kind_of BigDecimal, b.world_population + assert_equal '6000000000.0', b.world_population.to_s + assert_kind_of Integer, b.my_house_population + assert_equal 3, b.my_house_population + assert_kind_of BigDecimal, b.bank_balance + assert_equal BigDecimal("1586.43"), b.bank_balance + assert_kind_of BigDecimal, b.big_bank_balance + assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance + GiveMeBigNumbers.down + assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first } + end +end + + + + class CoreTest < ActiveRecord::TestCase # I think fixtures are useing the wrong time zone and the `:first` # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is From 524e8aea88a40c1ca68c3bb07459b1058f4ca463 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 18:25:28 -0500 Subject: [PATCH 0570/1412] [Rails5] SQL Server does decimal sans scale vs BigInt. cc @sgrif --- test/cases/coerced_tests.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f16ba3757..23df6fc4d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -243,6 +243,10 @@ def test_add_table_with_decimals_coerced GiveMeBigNumbers.down assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first } end + + # For some reason our tests set Rails.@_env which breaks test env switching. + coerce_tests! :test_migration_sets_internal_metadata_even_when_fully_migrated + coerce_tests! :test_internal_metadata_stores_environment end From 0c403d6fb05f7e3ebb56cff9604316b6334eeefe Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 19:09:39 -0500 Subject: [PATCH 0571/1412] [Rails5] Pass MigrationTest#test_filtering_migrations. --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index aead069c7..1a8ffdd5b 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -190,7 +190,7 @@ def table_From_Statement o def primary_Key_From_Table t return unless t - column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.second.try(:name) + column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.try(:second).try(:name) column_name ? t[column_name] : nil end From fdaa64c8b1e87e503f0037965692dabbe6148862 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 8 Jan 2017 20:08:38 -0500 Subject: [PATCH 0572/1412] [Rails5] Do not output column collation in schema when same as database. --- CHANGELOG.md | 2 +- .../sqlserver/schema_statements.rb | 5 ++++- test/cases/schema_dumper_test_sqlserver.rb | 20 +++++++++---------- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6aa66523..743d78fa8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,4 +21,4 @@ #### Fixed -* ... +* Do not output column collation in schema when same as database. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index dde358cf5..8fce1eba7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -301,7 +301,10 @@ def column_definitions(table_name) columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, columns.DATETIME_PRECISION AS datetime_precision, - columns.COLLATION_NAME AS collation, + CASE + WHEN columns.COLLATION_NAME <> DATABASEPROPERTYEX(DB_NAME(), 'Collation') THEN columns.COLLATION_NAME + ELSE NULL + END AS [collation], columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index ee52d1e02..edd26f1d9 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -36,15 +36,15 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil end # Character Strings - assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :varchar_max, type: 'varchar_max', limit: 2147483647, precision: nil, scale: nil, default: "test varchar_max", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :text, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: "test text", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil + assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil + assert_line :varchar_max, type: 'varchar_max', limit: 2147483647, precision: nil, scale: nil, default: "test varchar_max", collation: nil + assert_line :text, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: "test text", collation: nil # Unicode Character Strings - assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :nvarchar_max, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: "SQL_Latin1_General_CP1_CI_AS" - assert_line :ntext, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: "test ntext åå", collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil + assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil + assert_line :nvarchar_max, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil + assert_line :ntext, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: "test ntext åå", collation: nil # Binary Strings assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil @@ -125,7 +125,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'no id with model driven primary key' do output = generate_schema_for_table 'sst_no_pk_data' output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} - assert_line :name, type: 'string', limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CI_AS" + assert_line :name, type: 'string', limit: nil, default: nil, collation: nil end @@ -154,7 +154,7 @@ def line(column_name) def assert_line(column_name, options={}) line = line(column_name) assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" - [:type, :limit, :precision, :scale, :default].each do |key| + [:type, :limit, :precision, :scale, :collation, :default].each do |key| next unless options.key?(key) actual = key == :type ? line.send(:type_method) : line.send(key) expected = options[key] From 969c18fe054faead6cf84e67762041f910917968 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 19:06:27 -0500 Subject: [PATCH 0573/1412] [Rails5] Avoid un-needed collation dumps. --- .../sqlserver/database_statements.rb | 4 ++-- .../sqlserver/database_tasks.rb | 3 +-- .../sqlserver/schema_dumper.rb | 16 ++++++++++++++++ .../sqlserver/schema_statements.rb | 5 +---- .../connection_adapters/sqlserver_adapter.rb | 9 ++++++++- .../connection_adapters/sqlserver_column.rb | 2 +- test/support/sql_counter_sqlserver.rb | 3 ++- 7 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/schema_dumper.rb diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 2a6cbbde5..bc7a58d91 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -80,7 +80,7 @@ def release_savepoint(name = current_savepoint_name) end def case_sensitive_comparison(table, attribute, column, value) - if !value.nil? && column.collation && !column.case_sensitive? + if value && value.acts_like?(:string) table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new)) else super @@ -88,7 +88,7 @@ def case_sensitive_comparison(table, attribute, column, value) end def can_perform_case_insensitive_comparison_for?(column) - column.case_sensitive? + column.type == :string end private :can_perform_case_insensitive_comparison_for? diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 7c86443b5..8e24234a3 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -24,10 +24,9 @@ def charset end def collation - select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')" + @collation ||= select_value "SELECT DATABASEPROPERTYEX(DB_NAME(), 'Collation')" end - private def create_database_options(options={}) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb new file mode 100644 index 000000000..637632f4a --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -0,0 +1,16 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module SchemaDumper + + private + + def schema_collation(column) + return unless column.collation + column.collation if column.collation != collation + end + + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 8fce1eba7..afeb1fccf 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -301,10 +301,7 @@ def column_definitions(table_name) columns.NUMERIC_SCALE AS numeric_scale, columns.NUMERIC_PRECISION AS numeric_precision, columns.DATETIME_PRECISION AS datetime_precision, - CASE - WHEN columns.COLLATION_NAME <> DATABASEPROPERTYEX(DB_NAME(), 'Collation') THEN columns.COLLATION_NAME - ELSE NULL - END AS [collation], + columns.COLLATION_NAME AS [collation], columns.ordinal_position, CASE WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 3ebc05841..2d1e1b3da 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -14,6 +14,7 @@ require 'active_record/connection_adapters/sqlserver/transaction' require 'active_record/connection_adapters/sqlserver/errors' require 'active_record/connection_adapters/sqlserver/schema_creation' +require 'active_record/connection_adapters/sqlserver/schema_dumper' require 'active_record/connection_adapters/sqlserver/schema_statements' require 'active_record/connection_adapters/sqlserver/sql_type_metadata' require 'active_record/connection_adapters/sqlserver/showplan' @@ -32,6 +33,7 @@ class SQLServerAdapter < AbstractAdapter SQLServer::Quoting, SQLServer::DatabaseStatements, SQLServer::Showplan, + SQLServer::SchemaDumper, SQLServer::SchemaStatements, SQLServer::DatabaseLimits, SQLServer::DatabaseTasks @@ -169,7 +171,6 @@ def reconnect! def disconnect! super - @spid = nil case @connection_options[:mode] when :dblib @connection.close rescue nil @@ -177,6 +178,12 @@ def disconnect! @connection = nil end + def clear_cache! + super + @spid = nil + @collation = nil + end + def reset! reset_transaction do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index db683ba84..6644a0ca1 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -20,7 +20,7 @@ def is_utf8? end def case_sensitive? - collation && !collation.match(/_CI/) + collation && collation.match(/_CS/) end end diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 756d7e7a1..72946735c 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -17,7 +17,8 @@ def capture_sql_ss /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, - /SELECT CAST\(.* AS .*\) AS value/ + /SELECT CAST\(.* AS .*\) AS value/, + /SELECT DATABASEPROPERTYEX/im ] sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first From a7860ccfbc17848e948a9e9dff21ec7c388db3bb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 19:28:32 -0500 Subject: [PATCH 0574/1412] [Rails5] Coerce HAVING queries. --- test/cases/coerced_tests.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 23df6fc4d..2fd946649 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -138,8 +138,10 @@ def test_limit_with_offset_is_kept_coerced assert_equal 1, queries.length queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} end -end + # Leave it up to users to format selects/functions so HAVING works correctly. + coerce_tests! :test_having_with_strong_parameters +end @@ -585,6 +587,10 @@ def test_reverse_order_with_function_other_predicates_coerced # We are not doing order duplicate removal anymore. coerce_tests! :test_default_scope_order_with_scope_order + + # Leave it up to users to format selects/functions so HAVING works correctly. + coerce_tests! :test_multiple_where_and_having_clauses + coerce_tests! :test_having_with_binds_for_both_where_and_having end From b689b03780f5b35969c3696813905eb4c798b8f2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 20:37:55 -0500 Subject: [PATCH 0575/1412] [Rails5] Patch up exists? distinct 1 as one queries. --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 1a8ffdd5b..56b7d6d54 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -170,7 +170,7 @@ def distinct_One_As_One_Is_So_Not_Fetch o core = o.cores.first distinct = Nodes::Distinct === core.set_quantifier oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE } - limitone = node_value(o.limit) == 1 + limitone = [nil, 0, 1].include? node_value(o.limit) if distinct && oneasone && limitone && !o.offset core.projections = [Arel.sql("TOP(1) 1 AS [one]")] o.limit = nil From dda13b91fbe8bc315d6a01d3d511403f7f41036c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 21:07:28 -0500 Subject: [PATCH 0576/1412] [Rails5] Coerce schema dump for unquoted default. --- test/cases/coerced_tests.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2fd946649..325a9f380 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -634,6 +634,13 @@ def test_schema_dumps_partial_indices_coerced 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 + + # We do not quote the 2.78 string default. + coerce_tests! :test_schema_dump_includes_decimal_options + def test_schema_dump_includes_decimal_options_coerced + output = dump_all_table_schema([/^[^n]/]) + assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output + end end class SchemaDumperDefaultsTest < ActiveRecord::TestCase From 9c0600554037088ab3c662cf558301ce1bec81e9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 21:14:10 -0500 Subject: [PATCH 0577/1412] [Rails5] Coerce test with non-SQLServer || operator. --- test/cases/coerced_tests.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 325a9f380..f2cbc4e3f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -446,6 +446,13 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced +class LeftOuterJoinAssociationTest < ActiveRecord::TestCase + # Uses || operator in SQL. Just trust core gets value out of this test. + coerce_tests! :test_does_not_override_select +end + + + class NamedScopingTest < ActiveRecord::TestCase # This works now because we add an `order(:id)` sort to break the order tie for deterministic results. From 66b7f1e7faee5b1eba53a30e1d3ab0c822819dc6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 9 Jan 2017 21:20:09 -0500 Subject: [PATCH 0578/1412] [Rails5] Ignore EagerAssociationTest with specific SQL Server condition. --- test/cases/coerced_tests.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f2cbc4e3f..5a76d1358 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -340,13 +340,6 @@ def test_in_batches_should_quote_batch_order_coerced -require 'models/owner' -class Owner < ActiveRecord::Base - scope :including_last_pet, -> { - select('owners.*, (select TOP (1) p.pet_id from pets p where p.owner_id = owners.owner_id order by p.name desc ) as last_pet_id'). - includes(:last_pet) - } -end class EagerAssociationTest < ActiveRecord::TestCase # Use LEN() vs length() function. coerce_tests! :test_count_with_include @@ -356,9 +349,6 @@ def test_count_with_include_coerced # Use TOP (1) in scope vs limit 1. coerce_tests! %r{including association based on sql condition and no database column} - it "including association based on sql condition and no database column coerced" do - assert_equal pets(:parrot), Owner.including_last_pet.first.last_pet - end end From 95358964201f655545c15fae86047cff14f2177a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 10 Jan 2017 22:50:46 -0500 Subject: [PATCH 0579/1412] [Rails5] BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc --- .../connection_adapters/sqlserver/type/datetime.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index c0a71159a..8d990794c 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -35,7 +35,7 @@ def quoted(value) private def cast_value(value) - super.try(:in_time_zone) + is_utc? ? super.try(:in_time_zone) : super end def fast_string_to_time(string) From fb99e74942f9ac4af5342305a59742ad4d94e247 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 10 Jan 2017 22:51:23 -0500 Subject: [PATCH 0580/1412] [Rails5] Better @spid handling? --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 2d1e1b3da..5d3517bfc 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -179,9 +179,9 @@ def disconnect! end def clear_cache! - super @spid = nil @collation = nil + super end def reset! From 22ed6150b1bf0e49e942b6b822e04e293d514146 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 16:53:44 -0500 Subject: [PATCH 0581/1412] [Rails5] Simplify Date/Time Types. * Use simple :datetime type name so we are included in `time_zone_aware_attributes` checks. * Pass: BasicsTest#test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc * Pass: AttributeMethodsTest#test_setting_time_zone_aware_time_with_dst --- .../sqlserver/type/datetime.rb | 20 +------------------ .../sqlserver/type/datetime2.rb | 4 ---- .../sqlserver/type/datetimeoffset.rb | 4 ---- .../sqlserver/type/time.rb | 5 +++-- test/cases/coerced_tests.rb | 11 ---------- test/cases/column_test_sqlserver.rb | 4 ++-- test/cases/schema_dumper_test_sqlserver.rb | 8 ++++---- 7 files changed, 10 insertions(+), 46 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 8d990794c..1aefb34aa 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -34,10 +34,6 @@ def quoted(value) private - def cast_value(value) - is_utc? ? super.try(:in_time_zone) : super - end - def fast_string_to_time(string) fast_string_to_time_zone.strptime(string, fast_string_to_time_format).time rescue ArgumentError @@ -49,21 +45,7 @@ def fast_string_to_time_format end def fast_string_to_time_zone - ::Time.zone || ActiveSupport::TimeZone.new('UTC') - end - - # Copy of module ActiveModel::Type::Helpers::TimeValue with - # else condition using a zone for local parsing. - def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil) - return if year.nil? || (year == 0 && mon == 0 && mday == 0) - if offset - time = ::Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil - return unless time - time -= offset - is_utc? ? time : time.getlocal - else - fast_string_to_time_zone.local(year, mon, mday, hour, min, sec, microsec) rescue nil - end + ::Time.zone || ActiveSupport::TimeZone['UTC'] end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb index 946418821..510e831c3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -6,10 +6,6 @@ class DateTime2 < DateTime include TimeValueFractional2 - def type - :datetime2 - end - def sqlserver_type "datetime2(#{precision.to_i})" end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 9564dadda..63219de85 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -4,10 +4,6 @@ module SQLServer module Type class DateTimeOffset < DateTime2 - def type - :datetimeoffset - end - def sqlserver_type "datetimeoffset(#{precision.to_i})" end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 3ca7348fd..f8bc0dec8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -7,7 +7,8 @@ class Time < ActiveRecord::Type::Time include TimeValueFractional2 def serialize(value) - return super unless value.acts_like?(:time) + value = super + return value unless value.acts_like?(:time) time = value.to_s(:_sqlserver_time).tap do |v| fraction = quote_fractional(value) v << ".#{fraction}" @@ -34,7 +35,7 @@ def quoted(value) private def cast_value(value) - value = value.acts_like?(:time) ? value : super + value = super return if value.blank? value = value.change year: 2000, month: 01, day: 01 apply_seconds_precision(value) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5a76d1358..3ae8ce9a5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -745,17 +745,6 @@ def test_invalid_datetime_precision_raises_error_coerced end end end - - # Original test uses `datetime` vs `datetime2` type which we dynamically use. - coerce_tests! :test_schema_dump_includes_datetime_precision - def test_schema_dump_includes_datetime_precision_coerced - @connection.create_table(:foos, force: true) do |t| - t.timestamps precision: 6 - end - output = dump_table_schema("foos") - assert_match %r{t\.datetime2\s+"created_at",\s+precision: 6,\s+null: false$}, output - assert_match %r{t\.datetime2\s+"updated_at",\s+precision: 6,\s+null: false$}, output - end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 661288029..8492d8120 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -352,7 +352,7 @@ def assert_obj_set_and_save(attribute, value) skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? col = column('datetime2_7') col.sql_type.must_equal 'datetime2(7)' - col.type.must_equal :datetime2 + 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>" @@ -417,7 +417,7 @@ def assert_obj_set_and_save(attribute, value) skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? col = column('datetimeoffset_7') col.sql_type.must_equal 'datetimeoffset(7)' - col.type.must_equal :datetimeoffset + col.type.must_equal :datetime col.null.must_equal true col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index edd26f1d9..fe4abbdcb 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -26,9 +26,9 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "01-01-0001" assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? - assert_line :datetime2_7, type: 'datetime2', limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" - assert_line :datetime2_3, type: 'datetime2', limit: nil, precision: 3, scale: nil, default: nil - assert_line :datetime2_1, type: 'datetime2', limit: nil, precision: 1, scale: nil, default: nil + assert_line :datetime2_7, type: 'datetime', limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" + assert_line :datetime2_3, type: 'datetime', limit: nil, precision: 3, scale: nil, default: nil + assert_line :datetime2_1, type: 'datetime', limit: nil, precision: 1, scale: nil, default: nil end assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? @@ -99,7 +99,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['sstimestamp_col'].sql_type.must_equal 'timestamp' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil - assert_line :datetime2_col, type: 'datetime2', limit: nil, precision: 7, scale: nil, default: nil + assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil From a33caff9949696c9adffd9108d6ee45eeb91a33f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 17:22:47 -0500 Subject: [PATCH 0582/1412] [Rails5] Fix coerced test. --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3ae8ce9a5..5e64b0a4e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -134,7 +134,7 @@ def test_limit_is_kept_coerced coerce_tests! :test_limit_with_offset_is_kept def test_limit_with_offset_is_kept_coerced - queries = assert_sql { Account.limit(1).offset(1).count } + queries = capture_sql_ss { Account.limit(1).offset(1).count } assert_equal 1, queries.length queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} end From dfc1f71efac8603ab541c9f5fe9bbf609e1b4cfe Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 18:26:19 -0500 Subject: [PATCH 0583/1412] [Rails5] Pass a few `Migration::ColumnsTest` examples. * Pass: test_rename_column_with_sql_reserved_word * Pass: test_rename_column_using_symbol_arguments --- .../connection_adapters/sqlserver/schema_statements.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index afeb1fccf..fe7502f30 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -146,7 +146,6 @@ def change_column_default(table_name, column_name, default_or_changes) def rename_column(table_name, column_name, new_column_name) clear_cache! - detect_column_for! table_name, column_name identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}") execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN' rename_column_indexes(table_name, column_name, new_column_name) From 9914ef1325b3393cede30d79b54fba0c3369671d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 18:40:40 -0500 Subject: [PATCH 0584/1412] [Rails5] Coerce test with offset and no limit. --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5e64b0a4e..51a040f7a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -765,3 +765,13 @@ def test_default_negative_integer_coerced assert_equal -5, record.negative_integer_before_type_cast end end + + + + +module ActiveRecord + class CollectionCacheKeyTest < ActiveRecord::TestCase + # Will trust rails has this sorted since you cant offset without a limit. + coerce_tests! %r{with offset which return 0 rows} + end +end From 57358e18d51a9909fffbbb28102ad11001e33898 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 20:28:40 -0500 Subject: [PATCH 0585/1412] [Rails5] Coerce a few tests with no change due to ambigious datetime nsec quoting. --- test/cases/coerced_tests.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 51a040f7a..6d8787a54 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -381,6 +381,28 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced # This fails only when run in the full test suite task. Just taking it out of the mix. coerce_tests! :test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct + + # Can not use array condition due to not finding right type and hence fractional second quoting. + coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local + def test_condition_utc_time_interpolation_with_default_timezone_local_coerced + with_env_tz 'America/New_York' do + with_timezone_config default: :local do + topic = Topic.first + assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first + end + end + end + + # Can not use array condition due to not finding right type and hence fractional second quoting. + coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc + def test_condition_local_time_interpolation_with_default_timezone_utc_coerced + with_env_tz 'America/New_York' do + with_timezone_config default: :utc do + topic = Topic.first + assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first + end + end + end end From 08d596b29903954c4d6fd631625dce739d7f1549 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 20:47:42 -0500 Subject: [PATCH 0586/1412] [Rails5] Fall back to nvarchar for sp_executesql. --- .../connection_adapters/sqlserver/database_statements.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index bc7a58d91..6cadfd542 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -240,10 +240,8 @@ def sp_executesql_sql_type(attr) case value = attr.value_for_database when Numeric 'int'.freeze - when String - 'nvarchar(max)'.freeze else - raise TypeError, "sp_executesql_sql_type can not find sql type for attr #{attr.inspect}" + 'nvarchar(max)'.freeze end end From 4f493b693fda0927f6ac1847551f0911a9ff33be Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 20:53:00 -0500 Subject: [PATCH 0587/1412] [Rails5] Fix connection spid test. --- test/cases/connection_test_sqlserver.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 9a36120af..b51b65e3f 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -8,8 +8,10 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase fixtures :topics, :accounts - before { assert connection.active? } - after { connection.reconnect! } + before do + connection.reconnect! + assert connection.active? + end it 'affect rows' do topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } From ca1babf48cc211826fe608b7f12ab785411a24b5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 20:54:39 -0500 Subject: [PATCH 0588/1412] [Rails5] Ensure quoted literals are frozen. --- lib/active_record/connection_adapters/sqlserver/quoting.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 44631c951..e577c69ee 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -3,9 +3,9 @@ module ConnectionAdapters module SQLServer module Quoting - QUOTED_TRUE = '1' - QUOTED_FALSE = '0' - QUOTED_STRING_PREFIX = 'N' + QUOTED_TRUE = '1'.freeze + QUOTED_FALSE = '0'.freeze + QUOTED_STRING_PREFIX = 'N'.freeze def fetch_type_metadata(sql_type, sqlserver_options = {}) cast_type = lookup_cast_type(sql_type) From 147c42c39c0eca170904ba38903a6ebc1e9e11ff Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 21:13:52 -0500 Subject: [PATCH 0589/1412] [Rails5] Open up full test suite. --- .travis.yml | 1 - appveyor.yml | 1 - circle.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index f85ee8fc5..c8779141a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,6 @@ env: - TINYTDS_VERSION=1.1.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost - - ONLY_SQLSERVER=1 rvm: - 2.2.5 - 2.3.1 diff --git a/appveyor.yml b/appveyor.yml index d75979e2f..58815883c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,6 @@ init: - SET RAKEOPT=-rdevkit - SET TESTOPTS='-v' - SET TINYTDS_VERSION=1.1.0 - - SET ONLY_SQLSERVER=1 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index ae5da887c..a19016590 100644 --- a/circle.yml +++ b/circle.yml @@ -10,7 +10,6 @@ machine: TINYTDS_VERSION: 1.1.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost - ONLY_SQLSERVER: 1 services: - docker From 4ae77fec8dc895af136ac1974e50299f76bb2ef9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 15 Jan 2017 21:42:57 -0500 Subject: [PATCH 0590/1412] [Rails5] Do not use verbose TESTOPTS --- .travis.yml | 1 - appveyor.yml | 1 - circle.yml | 1 - 3 files changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index c8779141a..55e1094a0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ services: - docker env: global: - - TESTOPTS="-v" - TINYTDS_VERSION=1.1.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost diff --git a/appveyor.yml b/appveyor.yml index 58815883c..592d73bf9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,6 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TESTOPTS='-v' - SET TINYTDS_VERSION=1.1.0 clone_depth: 5 skip_tags: true diff --git a/circle.yml b/circle.yml index a19016590..cc4670556 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,6 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TESTOPTS: -v TINYTDS_VERSION: 1.1.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost From ffa4ff38f7cfd7f79199d1be18c7213035c4c479 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 13:12:18 -0500 Subject: [PATCH 0591/1412] Use non-random Minitest just like Rails core. --- Gemfile | 2 +- test/cases/coerced_tests.rb | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 06bbd62b6..e45f80828 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' gemspec gem 'sqlite3' -gem 'minitest', '~> 5.9.0' +gem 'minitest', '< 5.3.4' gem 'bcrypt', platforms: [:mri] gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6d8787a54..9eaea87dc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -206,6 +206,15 @@ def test_remove_column_with_multi_column_index_coerced assert_equal [], connection.indexes('test_models').map(&:name) end end + + # Choose `StatementInvalid` vs `ActiveRecordError`. + coerce_tests! :test_rename_nonexistent_column + def test_rename_nonexistent_column + exception = ActiveRecord::StatementInvalid + assert_raise(exception) do + rename_column "test_models", "nonexistent", "should_fail" + end + end end end From 18f2381ff3ea751de3420dd232f6064dedc4f7c0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 13:27:30 -0500 Subject: [PATCH 0592/1412] [Rails5] Fix coerced test. --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9eaea87dc..448a9f5eb 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -209,7 +209,7 @@ def test_remove_column_with_multi_column_index_coerced # Choose `StatementInvalid` vs `ActiveRecordError`. coerce_tests! :test_rename_nonexistent_column - def test_rename_nonexistent_column + def test_rename_nonexistent_column_coerced exception = ActiveRecord::StatementInvalid assert_raise(exception) do rename_column "test_models", "nonexistent", "should_fail" From 6a18ffd578038c10c6f08b4140bf36a936fdb315 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 13:53:14 -0500 Subject: [PATCH 0593/1412] [Rails5] Put coerced test in right place. --- test/cases/coerced_tests.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 448a9f5eb..ddf3ff2c1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -205,14 +205,14 @@ def test_remove_column_with_multi_column_index_coerced remove_column("test_models", "hat_size") assert_equal [], connection.indexes('test_models').map(&:name) end - end - # Choose `StatementInvalid` vs `ActiveRecordError`. - coerce_tests! :test_rename_nonexistent_column - def test_rename_nonexistent_column_coerced - exception = ActiveRecord::StatementInvalid - assert_raise(exception) do - rename_column "test_models", "nonexistent", "should_fail" + # Choose `StatementInvalid` vs `ActiveRecordError`. + coerce_tests! :test_rename_nonexistent_column + def test_rename_nonexistent_column_coerced + exception = ActiveRecord::StatementInvalid + assert_raise(exception) do + rename_column "test_models", "nonexistent", "should_fail" + end end end end From b50dfb746e54222d82b75ef6bca988c81e3efb4b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 16:30:40 -0500 Subject: [PATCH 0594/1412] [Rails5] Remove notes [ci skip] --- README.md | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 630d9f223..388f01304 100644 --- a/README.md +++ b/README.md @@ -7,37 +7,10 @@ * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community -## RAILS v5 COMING!!! -2017-01-04 - All core adapter tests are passing on Rails v5. We have ~60 failures under the full AcitveRecord suite. At this time, the foundation of the adapter is considered to be solid and the remaining tests will not likely require any refactors of the adapter internals. +The SQL Server adapter for ActiveRecord v5.0 using SQL Server 2012 or higher. -This means you can technically bundle to the adapter's master branch and work with Rails 5. However, but reports should not be given till we have a release and pass all ActiveRecord tests. Please follow the `RAILS5-TODO.md` and `RAILS5-FAILS.txt` notes before filing issues. - -* [RAILS5-TODO.md](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/RAILS5-TODO.md) -* [RAILS5-FAILS.txt](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/RAILS5-FAILS.txt) -* [All Rails 5.0 Issues - Closed](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues?q=label%3A%22Rails+5.0%22+is%3Aclosed) -* [All Rails 5.0 Issues - Open](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/labels/Rails%205.0) - -* **Can I help?** - Thanks so much! Feel free to jump in. I suggest chatting on Gitter or GitHub to make sure we are not dupliating work. -* **What is the ETA of an initial release?** - My guess is that we can have an initial release by mid January easily. - - -#### Using Rails v4 - -Use these commands to use the latest of Rails v4 till our v5 release is ready. - -```shell -$ gem install rails -v 4.2.7.1 -$ rails _4.2.7.1_ new MYAPP --database=sqlserver -``` - -![kantishna-wide](https://cloud.githubusercontent.com/assets/2381/5895051/aa6a57e0-a4e1-11e4-95b9-23627af5876a.jpg) - -## Code Name Kantishna - -The SQL Server adapter for ActiveRecord v4.2 using SQL Server 2012 or higher. - -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 4.2.x version of the adapter is only for the latest 4.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 4.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. #### Executing Stored Procedures From d972584b304d77a0be6426f9cef20ba0f5a29603 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 19:33:09 -0500 Subject: [PATCH 0595/1412] [Rails5] Update docs and TDSVER --- CHANGELOG.md | 6 ++ README.md | 77 ++++--------------- VERSION | 2 +- .../connection_adapters/sqlserver_adapter.rb | 2 +- 4 files changed, 25 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 743d78fa8..9e37dc02a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v5.0.1 + +#### Changed + +* Set `tds_version` fallback to `7.3`. + ## v5.0.0 diff --git a/README.md b/README.md index 388f01304..725cef26f 100644 --- a/README.md +++ b/README.md @@ -10,65 +10,16 @@ The SQL Server adapter for ActiveRecord v5.0 using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 4.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. - - -#### Executing Stored Procedures - -Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: - -```ruby -Account.execute_procedure :update_totals, 'admin', nil, true -# Or with named parameters. -Account.execute_procedure :update_totals, named: 'params' -``` +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 5.0 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. #### Native Data Type Support -We support every data type supported by FreeTDS and then a few more. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Here is a basic chart. Always check the `initialize_native_database_types` method for an updated list. - -```ruby -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', precision: 7 } -datetimeoffset: { name: 'datetimeoffset', precision: 7 } -smalldatetime: { name: 'smalldatetime' } -timestamp: { name: 'datetime' } -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' } -``` - -The following types require TDS version 7.3 with TinyTDS. This requires the latest FreeTDS v0.95 or higher. +We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb#L243) for an updated list. -* date -* datetime2 -* datetimeoffset -* time +The following types (date, datetime2, datetimeoffset, time) all require TDS version 7.3 with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to "7.3". The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. -Set `tds_version` in your database.yml or the `TDSVER` environment variable to `7.3` to ensure you are using the proper protocol version till 7.3 becomes the default. - -**Zone Conversion** - The `[datetimeoffset]` type is the only ActiveRecord time based datatype that does not cast the zone to ActiveRecord's default - typically UTC. As intended, this datatype is meant to maintain the zone you pass to it and/or retreived from the database. +The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a pecision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. #### Force Schema To Lowercase @@ -111,6 +62,16 @@ module ActiveRecord end ``` +#### Executing Stored Procedures + +Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: + +```ruby +Account.execute_procedure :update_totals, 'admin', nil, true +# Or with named parameters. +Account.execute_procedure :update_totals, named: 'params' +``` + #### Explain Support (SHOWPLAN) The 3.2 version of the adapter support ActiveRecord's explain features. In SQL Server, this is called the showplan. By default we use the `SHOWPLAN_ALL` option and format it using a simple table printer. So the following ruby would log the plan table below it. @@ -143,18 +104,13 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. -## Versions - -The adapter follows a rational versioning policy that also tracks ActiveRecord's major and minor version. That means the latest 3.1.x version of the adapter will always work for the latest 3.1.x version of ActiveRecord. - - ## Installation The adapter has no strict gem dependencies outside of ActiveRecord. You will have to pick a connection mode, the default is dblib which uses the TinyTDS gem. Just bundle the gem and the adapter will use it. ```ruby gem 'tiny_tds' -gem 'activerecord-sqlserver-adapter', '~> 4.2.0' +gem 'activerecord-sqlserver-adapter' ``` @@ -172,6 +128,7 @@ Many many people have contributed. If you do not see your name here and it shoul ## Contributers + Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors * metaskills (Ken Collins) @@ -194,5 +151,5 @@ Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord- ## License -Copyright © 2008-2016. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. +Copyright © 2008-2017. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. diff --git a/VERSION b/VERSION index 0062ac971..6b244dcd6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.0 +5.0.1 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5d3517bfc..f823772cf 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -356,7 +356,7 @@ def dblib_connect(config) username: config[:username], password: config[:password], database: config[:database], - tds_version: config[:tds_version], + tds_version: config[:tds_version] || '7.3', appname: config_appname(config), login_timeout: config_login_timeout(config), timeout: config_timeout(config), From 46cd30d4615238a5949a00a9d9055497cb7691fe Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 19:36:15 -0500 Subject: [PATCH 0596/1412] [Rails5] Install bcrypt for all platforms. --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index e45f80828..351fa48ef 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ gemspec gem 'sqlite3' gem 'minitest', '< 5.3.4' -gem 'bcrypt', platforms: [:mri] +gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] if RbConfig::CONFIG["host_os"] =~ /darwin/ From 7ea658d8066b5b4a68591c949d3bd0c8b26d638e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 19:51:09 -0500 Subject: [PATCH 0597/1412] Appveyor test for bcrypt. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 592d73bf9..47b22e479 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,6 +11,7 @@ install: - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version - gem --version + - gem install bcrypt --platform=ruby - bundle install build: off test_script: From 8d61d9d1b165cb9abb98d102b0e77598bfa14724 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 19:55:41 -0500 Subject: [PATCH 0598/1412] Appveyor bcrypt test. --- appveyor.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 47b22e479..ce117dd0d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,8 +11,9 @@ install: - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version - gem --version - - gem install bcrypt --platform=ruby - bundle install + - gem uninstall bcrypt + - gem install bcrypt --platform=ruby build: off test_script: - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" From f1065a5fa3ce277b822a2d20b11bcda304169abc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 21:02:20 -0500 Subject: [PATCH 0599/1412] [Rails5] Notes [ci skip] --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e37dc02a..1d0972947 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ * Set `tds_version` fallback to `7.3`. +#### Fixed + +* Support 2014, 2012 drop table statement. + ## v5.0.0 From 3a68ac378ed03aaf3a761ef01f8622810b005f75 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Jan 2017 20:47:12 -0500 Subject: [PATCH 0600/1412] [Rails5] Support 2014, 2012 drop table statement. --- .../connection_adapters/sqlserver/schema_statements.rb | 8 ++++++++ .../connection_adapters/sqlserver_adapter.rb | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index fe7502f30..2c43d20c3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -42,6 +42,14 @@ def create_table(table_name, comment: nil, **options) res end + def drop_table(table_name, options = {}) + if options[:if_exists] && @version_year != 2016 + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}" + else + super + end + end + def indexes(table_name, name = nil) data = select("EXEC sp_helpindex #{quote(table_name)}", name) rescue [] data.reduce([]) do |indexes, index| diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f823772cf..03a0ba0cb 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -339,6 +339,7 @@ def connect dblib_connect(config) end @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first + @version_year = version_year configure_connection end @@ -415,6 +416,13 @@ def initialize_dateformatter } end + def version_year + vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s + /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i + rescue Exception => e + 2016 + end + end end end From f76df0b86670f69c73429eb5b297548bc72d6602 Mon Sep 17 00:00:00 2001 From: Rails SQL Server Date: Tue, 17 Jan 2017 07:45:49 -0500 Subject: [PATCH 0601/1412] Add Dependency Status Badge [ci skip] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 725cef26f..bbce43212 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ * [![CircleCI](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master.svg?style=svg)](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master) - CircleCI * [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version +* [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community From 6e067c6ba6ea458295d0aa15090961a6607c5e75 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 18 Jan 2017 19:34:08 -0500 Subject: [PATCH 0602/1412] Use "Microsoft SQL Server vNext (CTP1.1) - 14.0.100.187 (X64)"" --- test/bin/setup.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/bin/setup.sh b/test/bin/setup.sh index 4ad03244c..aa4fec7c2 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,15 +3,17 @@ set -x set -e -docker pull metaskills/mssql-server-linux-rails +tag=1.1 -container=$(docker ps -a -q --filter ancestor=metaskills/mssql-server-linux-rails) +docker pull metaskills/mssql-server-linux-rails:$tag + +container=$(docker ps -a -q --filter ancestor=metaskills/mssql-server-linux-rails:$tag) if [[ -z $container ]]; then - docker run -p 1433:1433 -d metaskills/mssql-server-linux-rails && sleep 10 + docker run -p 1433:1433 -d metaskills/mssql-server-linux-rails:$tag && sleep 10 exit fi -container=$(docker ps -q --filter ancestor=metaskills/mssql-server-linux-rails) +container=$(docker ps -q --filter ancestor=metaskills/mssql-server-linux-rails:$tag) if [[ -z $container ]]; then docker start $container && sleep 10 fi From b4d59924704ad6f8fe868ec57568e68edb6672e9 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 20 Jan 2017 19:17:47 -0500 Subject: [PATCH 0603/1412] Microsoft SQL Server vNext (CTP1.2) --- test/bin/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/setup.sh b/test/bin/setup.sh index aa4fec7c2..84bd6c4f6 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,7 +3,7 @@ set -x set -e -tag=1.1 +tag=1.2 docker pull metaskills/mssql-server-linux-rails:$tag From 6f9d63e1bb9e9f0fefbe40717879c66bf8632b82 Mon Sep 17 00:00:00 2001 From: jonbell Date: Wed, 1 Jun 2016 00:06:46 -0500 Subject: [PATCH 0604/1412] Filter table constraints with matching table schema to column. --- .../sqlserver/schema_statements.rb | 1 + test/cases/specific_schema_test_sqlserver.rb | 8 ++++++++ test/schema/sqlserver_specific_schema.rb | 16 ++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2c43d20c3..2f128abcb 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -326,6 +326,7 @@ def column_definitions(table_name) FROM #{database}.INFORMATION_SCHEMA.COLUMNS columns LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC ON TC.TABLE_NAME = columns.TABLE_NAME + AND TC.TABLE_SCHEMA = columns.TABLE_SCHEMA AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU ON KCU.COLUMN_NAME = columns.COLUMN_NAME diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 59df21bb6..e6d2cabc5 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -165,4 +165,12 @@ def quoted_id db_uuid.must_match(acceptable_uuid) end + # with similar table definition in two schemas + + it 'returns the correct primary columns' do + connection = ActiveRecord::Base.connection + assert_equal 'field_1', connection.columns('test.sst_schema_test_mulitple_schema').detect(&:is_primary?).name + assert_equal 'field_2', connection.columns('test2.sst_schema_test_mulitple_schema').detect(&:is_primary?).name + end + end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index a4b542768..756a6b5db 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -204,4 +204,20 @@ ) NATURALPKTABLESQLINOTHERSCHEMA + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_mulitple_schema' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_test_mulitple_schema" + execute <<-SCHEMATESTMULTIPLESCHEMA + CREATE TABLE test.sst_schema_test_mulitple_schema( + field_1 int NOT NULL PRIMARY KEY, + field_2 int, + ) + SCHEMATESTMULTIPLESCHEMA + execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test2') EXEC sp_executesql N'CREATE SCHEMA test2'" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_mulitple_schema' and TABLE_SCHEMA = 'test2') DROP TABLE test2.sst_schema_test_mulitple_schema" + execute <<-SCHEMATESTMULTIPLESCHEMA + CREATE TABLE test2.sst_schema_test_mulitple_schema( + field_1 int, + field_2 int NOT NULL PRIMARY KEY, + ) + SCHEMATESTMULTIPLESCHEMA + end From 73b4e09a3bec9fd069d6fe87a4f67730549378b1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 23 Jan 2017 23:15:16 -0500 Subject: [PATCH 0605/1412] Prepare for v5.0.2 --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d0972947..cf2a9606e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.0.2 + +#### Fixed + +* Filter table constraints with matching table schema to column. Fixes #478 + + ## v5.0.1 #### Changed diff --git a/VERSION b/VERSION index 6b244dcd6..a1ef0cae1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.1 +5.0.2 From 6c82adb73951c773e5a2f799636331ccc9625621 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 28 Jan 2017 10:27:08 -0500 Subject: [PATCH 0606/1412] Test Ruby 2.4.0. --- .travis.yml | 1 + circle.yml | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 55e1094a0..761886cd5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: rvm: - 2.2.5 - 2.3.1 + - 2.4.0 before_install: - export PATH=/opt/local/bin:$PATH - docker info diff --git a/circle.yml b/circle.yml index cc4670556..eec43677a 100644 --- a/circle.yml +++ b/circle.yml @@ -20,6 +20,7 @@ dependencies: - tsql -C - rvm-exec 2.2.5 bundle install - rvm-exec 2.3.1 bundle install + - rvm-exec 2.4.0 bundle install database: override: @@ -32,3 +33,4 @@ test: override: - rvm-exec 2.2.5 bundle exec rake test - rvm-exec 2.3.1 bundle exec rake test + - rvm-exec 2.4.0 bundle exec rake test From dc526c116fccf1d5dc1815bfd0a0a535a0b592ce Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 Jan 2017 20:14:05 -0500 Subject: [PATCH 0607/1412] Reduce view information reflection to per table vs. column. Fixes #552 --- CHANGELOG.md | 7 ++++++ .../sqlserver/schema_statements.rb | 25 +++++++++++-------- .../connection_adapters/sqlserver_adapter.rb | 5 ++-- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf2a9606e..e4d83abef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.0.3 + +#### Changed + +* Reduce view information reflection to per table vs. column. Fixes #552 + + ## v5.0.2 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2f128abcb..6d4ca40a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -470,20 +470,23 @@ def view_table_name(table_name) end def view_information(table_name) - identifier = SQLServer::Utils.extract_identifiers(table_name) - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA' - if view_info - view_info = view_info.with_indifferent_access - 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 + @view_information ||= {} + @view_information[table_name] ||= begin + identifier = SQLServer::Utils.extract_identifiers(table_name) + view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA' + if view_info + view_info = view_info.with_indifferent_access + 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 + view_info end - view_info end def views_real_column_name(table_name, column_name) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 03a0ba0cb..8369fe2c2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -176,11 +176,12 @@ def disconnect! @connection.close rescue nil end @connection = nil + @spid = nil + @collation = nil end def clear_cache! - @spid = nil - @collation = nil + @view_information = nil super end From 9bdc6b716a0b8d8eb5bee5967d7ee35535ccdb5b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 Jan 2017 21:11:09 -0500 Subject: [PATCH 0608/1412] The `user_options` parsing. Works for hash/array. Fixes #535 --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4d83abef..7c29466f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Changed * Reduce view information reflection to per table vs. column. Fixes #552 +* The `user_options` parsing. Works for hash/array. Fixes #535 ## v5.0.2 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6cadfd542..5a98e4ce2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -132,7 +132,9 @@ def use_database(database = nil) def user_options return {} if sqlserver_azure? - select_rows('dbcc useroptions', 'SCHEMA').reduce(HashWithIndifferentAccess.new) do |values, row| + rows = select_rows('dbcc useroptions', 'SCHEMA') + rows = rows.first if rows.size == 2 && rows.last.empty? + rows.reduce(HashWithIndifferentAccess.new) do |values, row| if row.instance_of? Hash set_option = row.values[0].gsub(/\s+/, '_') user_value = row.values[1] From 6f4b3917364a8d64c67e672eb2117e3dc0aca4bb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 Jan 2017 21:14:34 -0500 Subject: [PATCH 0609/1412] Prepare for v5.0.3. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a1ef0cae1..50e2274e6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.2 +5.0.3 From 3cee83caa631870f5dcc8dbd89f08da8c30013bc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 30 Jan 2017 21:52:31 -0500 Subject: [PATCH 0610/1412] Pass the `:contained` option to TinyTDS. Fixes #527 --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver_adapter.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c29466f7..6cd962656 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Reduce view information reflection to per table vs. column. Fixes #552 * The `user_options` parsing. Works for hash/array. Fixes #535 +* Pass the `:contained` option to TinyTDS. Fixes #527 ## v5.0.2 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 8369fe2c2..26bcc301c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -363,7 +363,8 @@ def dblib_connect(config) login_timeout: config_login_timeout(config), timeout: config_timeout(config), encoding: config_encoding(config), - azure: config[:azure] + azure: config[:azure], + contained: config[:contained] ).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do From 8d1da5bc597730e7673f6aad70584836e4ee4d97 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 15 Feb 2017 20:20:39 -0500 Subject: [PATCH 0611/1412] Fix Misc `datetimeoffset` Bugs (#559) * Allow datetimeoffset to be represented in schema.rb. * CHANGELOG notes. [CI SKIP] --- CHANGELOG.md | 7 +++++ VERSION | 2 +- .../sqlserver/type/datetimeoffset.rb | 4 +++ test/cases/column_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 27 ++++++++++--------- 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6cd962656..8612f931f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.0.4 + +#### Fixed + +* Allow `datetimeoffset` to be used in migrations and represented in schema. + + ## v5.0.3 #### Changed diff --git a/VERSION b/VERSION index 50e2274e6..2d6c0bcf1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.3 +5.0.4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 63219de85..9564dadda 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -4,6 +4,10 @@ module SQLServer module Type class DateTimeOffset < DateTime2 + def type + :datetimeoffset + end + def sqlserver_type "datetimeoffset(#{precision.to_i})" end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 8492d8120..6f6e515e2 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -417,7 +417,7 @@ def assert_obj_set_and_save(attribute, value) skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? col = column('datetimeoffset_7') col.sql_type.must_equal 'datetimeoffset(7)' - col.type.must_equal :datetime + col.type.must_equal :datetimeoffset col.null.must_equal true col.default.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index fe4abbdcb..3e27285a7 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -97,19 +97,20 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase 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' - assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil - assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil - assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil - assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil - assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil - assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_basic_col, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: nil - assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil - assert_line :ntext_col, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: nil - assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil - assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil - assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil - assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil + assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil + assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil + assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil + assert_line :datetimeoffset, type: 'datetimeoffset', limit: nil, precision: 7, scale: nil, default: nil + assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil + assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil + assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil + assert_line :text_basic_col, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil + assert_line :ntext_col, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil + assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil + assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil + assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil end # Special Cases From 36dafe08e2c3ee3d88237ebc070dd97bfe58bafc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 15 Feb 2017 20:42:17 -0500 Subject: [PATCH 0612/1412] Ensure datetimeoffset maintains offset. --- test/cases/column_test_sqlserver.rb | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 6f6e515e2..d7fb0476e 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -434,20 +434,26 @@ def assert_obj_set_and_save(attribute, value) obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + # Maintains the timezone + time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) + obj.datetimeoffset_7 = time + obj.datetimeoffset_7.must_equal time + obj.save! + obj.datetimeoffset_7.must_equal time + obj.reload.datetimeoffset_7.must_equal time # With other precisions. time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column('datetimeoffset_3') connection.lookup_cast_type_from_column(col).precision.must_equal 3 obj.datetimeoffset_3 = time obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" - # TODO: FreeTDS date bug fixed: https://github.com/FreeTDS/freetds/issues/44 - obj.save! ; obj.reload + obj.save! obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column('datetime2_1') connection.lookup_cast_type_from_column(col).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.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" end From 1a248d8bec1b64fccdd12b391357cb528b1c8afc Mon Sep 17 00:00:00 2001 From: Michael Morris Date: Thu, 16 Feb 2017 13:03:26 +1100 Subject: [PATCH 0613/1412] Handle using transactions and resetting isolation level correctly when `READ_COMMITTED_SNAPSHOT` is set to `ON` (#520) --- .../sqlserver/transaction.rb | 10 +++++++- test/cases/transaction_test_sqlserver.rb | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 81a58e968..3d5052ab0 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -14,7 +14,15 @@ def sqlserver? def current_isolation_level return unless sqlserver? level = connection.user_options_isolation_level - level.blank? ? 'READ COMMITTED' : level.upcase + + # When READ_COMMITTED_SNAPSHOT is set to ON, + # user_options_isolation_level will be equal to 'read committed + # snapshot' which is not a valid isolation level + if level.blank? || level == 'read committed snapshot' + 'READ COMMITTED' + else + level.upcase + end end end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 6db739bfb..e16a63a53 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -52,6 +52,31 @@ class TransactionTestSQLServer < ActiveRecord::TestCase connection.user_options_isolation_level.must_match %r{read committed}i end + describe 'when READ_COMMITTED_SNAPSHOT is set' do + before do + connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON" + connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE" + end + + after do + connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF" + connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" + end + + it 'should use READ COMMITTED as an isolation level' do + connection.user_options_isolation_level.must_match "read committed snapshot" + + Ship.transaction(isolation: :serializable) do + Ship.create! name: 'Black Pearl' + end + + # We're actually testing that the isolation level was correctly reset to + # "READ COMMITTED", and that no exception was raised (it's reported back + # by SQL Server as "read committed snapshot"). + connection.user_options_isolation_level.must_match "read committed snapshot" + end + end + protected From 35fffa36b394689813ca3851f3ae7b6b0682297e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 15 Feb 2017 21:06:36 -0500 Subject: [PATCH 0614/1412] Update CHANGELOG. Prepare for v5.0.4 --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver/transaction.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8612f931f..452f927c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed * Allow `datetimeoffset` to be used in migrations and represented in schema. +* Using transactions and resetting isolation level correctly when `READ_COMMITTED_SNAPSHOT` is set to `ON` Fixes #520 ## v5.0.3 diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 3d5052ab0..591613f1e 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -14,7 +14,6 @@ def sqlserver? def current_isolation_level return unless sqlserver? level = connection.user_options_isolation_level - # When READ_COMMITTED_SNAPSHOT is set to ON, # user_options_isolation_level will be equal to 'read committed # snapshot' which is not a valid isolation level From a0dbdc7c8800cdba35b97f65982ba5e2bb5aeb1b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 19 Feb 2017 08:57:48 -0500 Subject: [PATCH 0615/1412] Use SQL Server vNext CTP 1.3 (#565) --- test/bin/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/setup.sh b/test/bin/setup.sh index 84bd6c4f6..4713d308e 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,7 +3,7 @@ set -x set -e -tag=1.2 +tag=1.3 docker pull metaskills/mssql-server-linux-rails:$tag From 277c65608bfb17fecf48da1dc182fcd76001cf75 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 23 Feb 2017 07:10:50 -0500 Subject: [PATCH 0616/1412] Add TinyTDS as a runtime dependency. (#566) --- CHANGELOG.md | 7 +++++++ Gemfile | 1 + VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 1 + 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 452f927c7..4da89f24e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.0.5 + +#### Changed + +* Add TinyTDS as a runtime dependency. + + ## v5.0.4 #### Fixed diff --git a/Gemfile b/Gemfile index 351fa48ef..9a5d6af1f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,4 @@ +require 'openssl' source 'https://rubygems.org' gemspec diff --git a/VERSION b/VERSION index 2d6c0bcf1..ab0fa336d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.4 +5.0.5 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 9001c00dc..34eed07aa 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,4 +17,5 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.add_dependency 'activerecord', '~> 5.0.0' + spec.add_dependency 'tiny_tds' end From 5986c0f20f7a36961e5401403d4051c54ea4029e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 23 Feb 2017 21:39:01 -0500 Subject: [PATCH 0617/1412] Add Gratipay [ci skip] --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index bbce43212..4156974b2 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +[![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.svg)](https://gratipay.com/metaskills/) # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. From 62b1a6f57f8b233bc14893cc83d24b6d0fe3a439 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 26 Feb 2017 21:48:28 -0500 Subject: [PATCH 0618/1412] Enable `supports_index_sort_order?` and test it. --- .../connection_adapters/sqlserver_adapter.rb | 4 --- test/cases/adapter_test_sqlserver.rb | 15 -------- test/cases/index_test_sqlserver.rb | 35 +++++++++++++++++++ 3 files changed, 35 insertions(+), 19 deletions(-) create mode 100644 test/cases/index_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 26bcc301c..3874b7342 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -97,10 +97,6 @@ def supports_index_sort_order? true end - def supports_index_sort_order? - false - end - def supports_partial_index? true end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 5ecb077ad..8522d8623 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -290,21 +290,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'indexes' do - - let(:desc_index_name) { 'idx_credit_limit_test_desc' } - - it 'have indexes with descending order' do - begin - connection.execute "CREATE INDEX [#{desc_index_name}] ON [accounts] (credit_limit DESC)" - assert connection.indexes('accounts').find { |i| i.name == desc_index_name } - ensure - connection.execute "DROP INDEX [#{desc_index_name}] ON [accounts]" - end - end - - end - describe 'views' do # Using connection.views diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb new file mode 100644 index 000000000..94c00cf38 --- /dev/null +++ b/test/cases/index_test_sqlserver.rb @@ -0,0 +1,35 @@ +require 'cases/helper_sqlserver' + +class IndexTestSQLServer < ActiveRecord::TestCase + + before do + connection.create_table(:testings) do |t| + 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.boolean :administrator + end + end + + after do + connection.drop_table :testings rescue nil + end + + it 'add index with order' do + assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do + connection.add_index 'testings', ['last_name'], order: { last_name: :desc } + connection.remove_index 'testings', ['last_name'] + end + assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do + connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc } + connection.remove_index 'testings', ['last_name', 'first_name'] + end + assert_sql(/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.remove_index 'testings', ['last_name', 'first_name'] + end + end + +end From e3aa7f7bb673c816cdb72391f73f8b273f613ac7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 27 Feb 2017 18:29:51 -0500 Subject: [PATCH 0619/1412] Test `supports_partial_index?`. --- test/cases/index_test_sqlserver.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index 94c00cf38..5204188bc 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -32,4 +32,11 @@ class IndexTestSQLServer < ActiveRecord::TestCase end end + it 'add index with where' do + assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do + connection.add_index 'testings', 'last_name', where: "[first_name] = N'john doe'" + connection.remove_index 'testings', 'last_name' + end + end + end From f333449e0a23222626076087466bb7a2d0cecfbf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 27 Feb 2017 18:49:15 -0500 Subject: [PATCH 0620/1412] Demonstrate and test how expression indexes work. --- test/cases/index_test_sqlserver.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index 5204188bc..3a073e572 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -39,4 +39,9 @@ class IndexTestSQLServer < ActiveRecord::TestCase end end + it 'add index with expression' do + connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])" + connection.add_index 'testings', 'first_name_upper' + end + end From f56b1fb8797e2d6abcad87aac6ec82e70a75c038 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 27 Feb 2017 19:17:10 -0500 Subject: [PATCH 0621/1412] CHANGELOG for work and v5.0.6 new. --- CHANGELOG.md | 8 ++++++++ VERSION | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4da89f24e..69a0faa9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,11 @@ +## v5.0.6 + +#### Changed + +* Misc index enhancements or testing. Fixes #570 + Enable `supports_index_sort_order?`, test `supports_partial_index?`, test how expression indexes work. + + ## v5.0.5 #### Changed diff --git a/VERSION b/VERSION index ab0fa336d..c20c645d7 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.5 +5.0.6 From 93874c16a8f2efe15eba4e7183529be1a19ca9f5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 3 Mar 2017 17:29:09 -0500 Subject: [PATCH 0622/1412] Example for In-Memory db/table. --- CHANGELOG.md | 14 ++++ .../sqlserver/schema_statements.rb | 1 + .../sqlserver/table_definition.rb | 4 + .../connection_adapters/sqlserver_adapter.rb | 5 ++ test/cases/adapter_test_sqlserver.rb | 9 +++ test/cases/helper_sqlserver.rb | 1 + test/models/sqlserver/sst_memory.rb | 3 + test/schema/enable-in-memory-oltp.sql | 81 +++++++++++++++++++ test/schema/sqlserver_specific_schema.rb | 9 +++ test/support/test_in_memory_oltp.rb | 15 ++++ 10 files changed, 142 insertions(+) create mode 100644 test/models/sqlserver/sst_memory.rb create mode 100644 test/schema/enable-in-memory-oltp.sql create mode 100644 test/support/test_in_memory_oltp.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a0faa9d..1506961a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,20 @@ * Misc index enhancements or testing. Fixes #570 Enable `supports_index_sort_order?`, test `supports_partial_index?`, test how expression indexes work. +#### Added + +* New `primary_key_nonclustered` type for easy In-Memory table creation. +* Examples for an In-Memory table. + +```ruby +create_table :in_memory_table, id: false, + options: 'WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)' do |t| + t.primary_key_nonclustered :id + t.string :name + t.timestamps +end +``` + ## v5.0.5 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 6d4ca40a8..c94b58b19 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -253,6 +253,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) def initialize_native_database_types { primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', + primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED', integer: { name: 'int', limit: 4 }, bigint: { name: 'bigint' }, boolean: { name: 'bit' }, diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 730e48c40..f578dc11c 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -11,6 +11,10 @@ def primary_key(name, type = :primary_key, **options) column name, type, options end + def primary_key_nonclustered(*args, **options) + args.each { |name| column(name, :primary_key_nonclustered, options) } + end + def real(*args, **options) args.each { |name| column(name, :real, options) } end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 3874b7342..1fb3fda07 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -141,6 +141,10 @@ def supports_comments_in_create? false end + def supports_in_memory_oltp? + @version_year >= 2014 + end + def disable_referential_integrity tables = tables_with_referential_integrity tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" } @@ -416,6 +420,7 @@ def initialize_dateformatter def version_year vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s + return 2016 if vstring =~ /vNext/ /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i rescue Exception => e 2016 diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 8522d8623..a8b42a59d 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -416,5 +416,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end + it 'in_memory_oltp' do + if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp? + SSTMemory.primary_key.must_equal 'id' + SSTMemory.columns_hash['id'].must_be :is_identity? + else + skip 'supports_in_memory_oltp? => false' + end + end + end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 454b6b559..59391055f 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -3,6 +3,7 @@ Bundler.require :default, :development require 'pry' require 'support/minitest_sqlserver' +require 'support/test_in_memory_oltp' require 'cases/helper' require 'support/load_schema_sqlserver' require 'support/coerceable_test_sqlserver' diff --git a/test/models/sqlserver/sst_memory.rb b/test/models/sqlserver/sst_memory.rb new file mode 100644 index 000000000..c1767c5f8 --- /dev/null +++ b/test/models/sqlserver/sst_memory.rb @@ -0,0 +1,3 @@ +class SSTMemory < ActiveRecord::Base + self.table_name = 'sst_memory' +end diff --git a/test/schema/enable-in-memory-oltp.sql b/test/schema/enable-in-memory-oltp.sql new file mode 100644 index 000000000..fd5dee1b9 --- /dev/null +++ b/test/schema/enable-in-memory-oltp.sql @@ -0,0 +1,81 @@ +-- https://msdn.microsoft.com/en-us/library/mt694156.aspx +-- https://raw.githubusercontent.com/Microsoft/sql-server-samples/master/samples/features/in-memory/t-sql-scripts/enable-in-memory-oltp.sql +-- +-- The below scipt enables the use of In-Memory OLTP in the current database, +-- provided it is supported in the edition / pricing tier of the database. +-- It does the following: +-- 1. Validate that In-Memory OLTP is supported. +-- 2. In SQL Server, it will add a MEMORY_OPTIMIZED_DATA filegroup to the database +-- and create a container within the filegroup in the default data folder. +-- 3. Change the database compatibility level to 130 (needed for parallel queries +-- and auto-update of statistics). +-- 4. Enables the database option MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT to avoid the +-- need to use the WITH (SNAPSHOT) hint for ad hoc queries accessing memory-optimized +-- tables. +-- +-- Applies To: SQL Server 2016 (or higher); Azure SQL Database +-- Author: Jos de Bruijn (Microsoft) +-- Last Updated: 2016-05-02 + +SET NOCOUNT ON; +SET XACT_ABORT ON; + +-- 1. validate that In-Memory OLTP is supported +IF SERVERPROPERTY(N'IsXTPSupported') = 0 +BEGIN + PRINT N'Error: In-Memory OLTP is not supported for this server edition or database pricing tier.'; +END +IF DB_ID() < 5 +BEGIN + PRINT N'Error: In-Memory OLTP is not supported in system databases. Connect to a user database.'; +END +ELSE +BEGIN + BEGIN TRY; +-- 2. add MEMORY_OPTIMIZED_DATA filegroup when not using Azure SQL DB + IF SERVERPROPERTY('EngineEdition') != 5 + BEGIN + DECLARE @SQLDataFolder nvarchar(max) = cast(SERVERPROPERTY('InstanceDefaultDataPath') as nvarchar(max)) + DECLARE @MODName nvarchar(max) = DB_NAME() + N'_mod'; + DECLARE @MemoryOptimizedFilegroupFolder nvarchar(max) = @SQLDataFolder + @MODName; + + DECLARE @SQL nvarchar(max) = N''; + + -- add filegroup + IF NOT EXISTS (SELECT 1 FROM sys.filegroups WHERE type = N'FX') + BEGIN + SET @SQL = N' +ALTER DATABASE CURRENT +ADD FILEGROUP ' + QUOTENAME(@MODName) + N' CONTAINS MEMORY_OPTIMIZED_DATA;'; + EXECUTE (@SQL); + + END; + + -- add container in the filegroup + IF NOT EXISTS (SELECT * FROM sys.database_files WHERE data_space_id IN (SELECT data_space_id FROM sys.filegroups WHERE type = N'FX')) + BEGIN + SET @SQL = N' +ALTER DATABASE CURRENT +ADD FILE (name = N''' + @MODName + ''', filename = ''' + + @MemoryOptimizedFilegroupFolder + N''') +TO FILEGROUP ' + QUOTENAME(@MODName); + EXECUTE (@SQL); + END + END + + -- 3. set compat level to 130 if it is lower + IF (SELECT compatibility_level FROM sys.databases WHERE database_id=DB_ID()) < 130 + ALTER DATABASE CURRENT SET COMPATIBILITY_LEVEL = 130 + + -- 4. enable MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT for the database + ALTER DATABASE CURRENT SET MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT = ON; + + + END TRY + BEGIN CATCH + PRINT N'Error enabling In-Memory OLTP'; + IF XACT_STATE() != 0 + ROLLBACK; + THROW; + END CATCH; +END; diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 756a6b5db..72bdaab96 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -37,6 +37,15 @@ # Edge Cases + 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| + t.primary_key_nonclustered :id + t.string :name + t.timestamps + end + end + create_table 'sst_bookings', force: true do |t| t.string :name t.datetime2 :created_at, null: false diff --git a/test/support/test_in_memory_oltp.rb b/test/support/test_in_memory_oltp.rb new file mode 100644 index 000000000..5db693f0f --- /dev/null +++ b/test/support/test_in_memory_oltp.rb @@ -0,0 +1,15 @@ +if ENV['IN_MEMORY_OLTP'] + require 'config' + require 'active_record' + require 'support/config' + require 'support/connection' + + ARTest.connect + + if ActiveRecord::Base.connection.supports_in_memory_oltp? + puts 'Configuring In-Memory OLTP...' + inmem_file = ARTest::SQLServer.test_root_sqlserver, 'schema', 'enable-in-memory-oltp.sql' + inmem_sql = File.read File.join(inmem_file) + ActiveRecord::Base.connection.execute(inmem_sql) + end +end From 388cdf7d00fac523471e52d88176f3173b00c79b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 3 Mar 2017 21:46:03 -0500 Subject: [PATCH 0623/1412] Rails v5.0.2 coerce random test. --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index ddf3ff2c1..498f4a1dd 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -806,3 +806,13 @@ class CollectionCacheKeyTest < ActiveRecord::TestCase coerce_tests! %r{with offset which return 0 rows} end end + + + + +module ActiveRecord + class StatementCacheTest < ActiveRecord::TestCase + # Getting random failures. + coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed + end +end From 116be8a6cd420645e5fc33373a7065a9d856cd8d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 4 Mar 2017 08:53:46 -0500 Subject: [PATCH 0624/1412] Perf w/inserts. Check binds & use schema cache. Fixes #572. Thanks @noelr. --- CHANGELOG.md | 5 ++++ .../sqlserver/database_statements.rb | 26 ++++++++++++++++++- .../sqlserver/schema_statements.rb | 21 --------------- test/cases/scratchpad_test_sqlserver.rb | 4 ++- 4 files changed, 33 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1506961a4..6fb56f5fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## v5.0.6 +#### Fixed + +* Performance w/inserts. Check binds & use schema cache for id inserts. + Fixes #572. Thanks @noelr. + #### Changed * Misc index enhancements or testing. Fixes #570 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 5a98e4ce2..0899d889d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -20,7 +20,7 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) end def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil) - if pk && id_insert_table_name = query_requires_identity_insert?(sql) + if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) with_identity_insert_enabled(id_insert_table_name) { exec_query(sql, name, binds) } else exec_query(sql, name, binds) @@ -281,6 +281,30 @@ def raw_connection_do(sql) @update_sql = false end + # === SQLServer Specific (Identity Inserts) ===================== # + + def exec_insert_requires_identity?(sql, pk, binds) + query_requires_identity_insert?(sql) if pk && binds.map(&:name).include?(pk) + end + + def query_requires_identity_insert?(sql) + if insert_sql?(sql) + table_name = get_table_name(sql) + id_column = identity_columns(table_name).first + id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false + else + false + end + end + + def insert_sql?(sql) + !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? + end + + def identity_columns(table_name) + schema_cache.columns(table_name).select(&:is_identity?) + end + # === SQLServer Specific (Selecting) ============================ # def raw_select(sql, name = 'SQL', binds = [], options = {}) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c94b58b19..b03e1aee7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -497,27 +497,6 @@ def views_real_column_name(table_name, column_name) match_data ? match_data[1] : column_name end - # === SQLServer Specific (Identity Inserts) ===================== # - - def query_requires_identity_insert?(sql) - if insert_sql?(sql) - table_name = get_table_name(sql) - id_column = identity_columns(table_name).first - id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false - else - false - end - end - - def insert_sql?(sql) - !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? - end - - def identity_columns(table_name) - columns(table_name).select(&:is_identity?) - end - - private def create_table_definition(*args) diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb index 3cc9010e6..1b12c9f45 100644 --- a/test/cases/scratchpad_test_sqlserver.rb +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -1,9 +1,11 @@ require 'cases/helper_sqlserver' +require 'models/book' class ScratchpadTestSQLServer < ActiveRecord::TestCase it 'helps debug things' do - # + $FOO = true + 1000.times { Book.create! name: 'test' } end end From ac6468333a74341b52fada24a328e0a47756099f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 4 Mar 2017 10:02:18 -0500 Subject: [PATCH 0625/1412] Better abstract SET defaults. --- .../connection_adapters/sqlserver_adapter.rb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1fb3fda07..a5ff31f1c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -368,17 +368,15 @@ def dblib_connect(config) ).tap do |client| if config[:azure] client.execute('SET ANSI_NULLS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do client.execute('SET ANSI_NULL_DFLT_ON ON').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do client.execute('SET ANSI_PADDING ON').do - client.execute('SET QUOTED_IDENTIFIER ON').do client.execute('SET ANSI_WARNINGS ON').do else client.execute('SET ANSI_DEFAULTS ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do end + client.execute('SET QUOTED_IDENTIFIER ON').do + client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do + client.execute('SET IMPLICIT_TRANSACTIONS OFF').do client.execute('SET TEXTSIZE 2147483647').do client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do end From 6197f0ba4c9e5c125071df03fd6c44d6d90f8119 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 4 Mar 2017 11:23:12 -0500 Subject: [PATCH 0626/1412] TODO NOTES [ci skip] --- RAILS5-TODO.md | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 984b4b7e7..164aca28d 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -1,36 +1,21 @@ -## SHORT TERM - -Misc remidners while in the heat of adapting the adpater. - - -## LONG TERM - -After we get some tests passing - -* Check `sql_for_insert` can do without the table regular expresion. -* Do we need the `query_requires_identity_insert` check in `execute`? +## Rails v5.0 + +* Docs on Docker Usage & Testing - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/547 +* Supports JSON - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/485 +* Supports Comments - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/486 +* Supports Indexed in Create - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/483 + https://blog.dbi-services.com/sql-server-2014-tips-create-indexes-directly-via-create-table/ + Column Store https://msdn.microsoft.com/en-us/library/gg492153.aspx + https://www.microsoft.com/en-us/sql-server/developer-get-started/node-mac * Does the schema cache serialize properly since we conform to that now? -* What does `supports_materialized_views?` means for SQL Server - - http://michaeljswart.com/2014/12/materialized-views-in-sql-server/ - - https://blogs.msdn.microsoft.com/ssma/2011/06/20/migrating-oracle-materialized-view-to-sql-server/ - - http://stackoverflow.com/questions/3986366/how-to-create-materialized-views-in-sql-server -* BIGINT PK support. https://github.com/rails/rails/pull/26266 * Can we use `OPTIMIZE FOR UNKNOWN` - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx - http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast - https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ -* Re-visit all `current_adapter?(:PostgreSQLAdapter)` checks and find ones we can play in. -#### Does Find By SQL Work? +## Rails v5.1 -With binds and prepareable? +* BIGINT PK support. https://github.com/rails/rails/pull/26266 -```ruby -# Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date] -# Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }] -# -def find_by_sql(sql, binds = [], preparable: nil) - result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable) -``` From 01a7de4465c6609687dd59ce1e5846907942de9d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 12 Mar 2017 13:17:27 -0400 Subject: [PATCH 0627/1412] Use TINYTDS_VERSION=1.2.0. --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 761886cd5..2d448caaf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=1.1.0 + - TINYTDS_VERSION=1.2.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index ce117dd0d..84d0643f9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=1.1.0 + - SET TINYTDS_VERSION=1.2.0 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index eec43677a..3f11dfeb6 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 1.1.0 + TINYTDS_VERSION: 1.2.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: From 83f6d57483e1e64453468d04612bdeade904e384 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 13 Mar 2017 22:33:04 -0400 Subject: [PATCH 0628/1412] Enable supports_json? for 2016 and up with a little bit of sugar. (#577) Adds a simple JSON type for creating an underlying `nvarchar(max)` column to add JSON too. Since SQL Server does not have a true JSON type. This means supporting JSON from a Rails perspective is a simple as adding the proper SQL Server type to your model's column which has some form of underlying string type. ```ruby create_table :users do |t| t.string :name, :email t.json :data # Creates a nvarchar(max) column. end class Users < ActiveRecord::Base attribute :data, ActiveRecord::Type::SQLServer::Json.new end ``` This allows you to save and return JSON data and query it as needed. ```ruby User.create! name: 'DJ Ruby Rhod', data: nil User.create! name: 'Ken Collins', data: { 'admin' => true, 'foo' => 'bar' } admin = User.where("JSON_VALUE(data, '$.admin') = CAST(1 AS BIT)").first admin.data['foo'] # => "bar" ``` Fixes #485. --- CHANGELOG.md | 18 +++++++++++ RAILS5-TODO.md | 7 ---- .../sqlserver/schema_statements.rb | 3 +- .../sqlserver/table_definition.rb | 4 +++ .../connection_adapters/sqlserver/type.rb | 1 + .../sqlserver/type/json.rb | 11 +++++++ .../connection_adapters/sqlserver_adapter.rb | 4 ++- test/cases/json_test_sqlserver.rb | 32 +++++++++++++++++++ test/cases/schema_dumper_test_sqlserver.rb | 2 ++ test/models/sqlserver/datatype_migration.rb | 5 +++ test/schema/sqlserver_specific_schema.rb | 5 +++ 11 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/json.rb create mode 100644 test/cases/json_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 6fb56f5fe..3e49c7d9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,24 @@ create_table :in_memory_table, id: false, end ``` +* Enable supports_json? Fixes #577. + +```ruby +create_table :users do |t| + t.string :name, :email + t.json :data # Creates a nvarchar(max) column. + end + +class Users < ActiveRecord::Base + attribute :data, ActiveRecord::Type::SQLServer::Json.new +end + +User.create! name: 'Ken Collins', data: { 'admin' => true, 'foo' => 'bar' } + +admin = User.where("JSON_VALUE(data, '$.admin') = CAST(1 AS BIT)").first +admin.data['foo'] # => "bar" +``` + ## v5.0.5 diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 164aca28d..6ed122f7d 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -1,13 +1,6 @@ ## Rails v5.0 -* Docs on Docker Usage & Testing - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/547 -* Supports JSON - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/485 -* Supports Comments - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/486 -* Supports Indexed in Create - https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/483 - https://blog.dbi-services.com/sql-server-2014-tips-create-indexes-directly-via-create-table/ - Column Store https://msdn.microsoft.com/en-us/library/gg492153.aspx - https://www.microsoft.com/en-us/sql-server/developer-get-started/node-mac * Does the schema cache serialize properly since we conform to that now? * Can we use `OPTIMIZE FOR UNKNOWN` - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b03e1aee7..f15f66cd2 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -281,7 +281,8 @@ def initialize_native_database_types varbinary: { name: 'varbinary', limit: 8000 }, binary: { name: 'varbinary(max)' }, uuid: { name: 'uniqueidentifier' }, - ss_timestamp: { name: 'timestamp' } + ss_timestamp: { name: 'timestamp' }, + json: { name: 'nvarchar(max)' } } end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index f578dc11c..ce7c9e2f2 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -85,6 +85,10 @@ def ss_timestamp(*args, **options) args.each { |name| column(name, :ss_timestamp, options) } end + def json(*args, **options) + args.each { |name| column(name, :text, options) } + end + end class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index c8291e433..bbb74266a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -40,6 +40,7 @@ # Other Data Types require 'active_record/connection_adapters/sqlserver/type/uuid' require 'active_record/connection_adapters/sqlserver/type/timestamp' +require 'active_record/connection_adapters/sqlserver/type/json' module ActiveRecord module Type diff --git a/lib/active_record/connection_adapters/sqlserver/type/json.rb b/lib/active_record/connection_adapters/sqlserver/type/json.rb new file mode 100644 index 000000000..3e01977bc --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/json.rb @@ -0,0 +1,11 @@ +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class Json < ActiveRecord::Type::Internal::AbstractJson + + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a5ff31f1c..abc4bf66c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -130,7 +130,7 @@ def supports_datetime_with_precision? end def supports_json? - true + @version_year >= 2016 end def supports_comments? @@ -304,6 +304,8 @@ def initialize_type_map(m) 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.alias_type 'json', 'nvarchar(max)' m.register_type 'ntext', SQLServer::Type::UnicodeText.new # Binary Strings register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb new file mode 100644 index 000000000..37168e785 --- /dev/null +++ b/test/cases/json_test_sqlserver.rb @@ -0,0 +1,32 @@ +require 'cases/helper_sqlserver' + +if ActiveRecord::Base.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] } + @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' }) + @o1.save! + @o1.reload.json_col.must_equal({ 'a' => 'a' }) + end + + it 'can use ISJSON function' do + SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count.must_equal 4 + SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count.must_equal 1 + end + + it 'can use JSON_VALUE function' do + SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count.must_equal 2 + end + +end +end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 3e27285a7..79bb0da7f 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -97,6 +97,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase 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', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil @@ -111,6 +112,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil + assert_line :json_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil end # Special Cases diff --git a/test/models/sqlserver/datatype_migration.rb b/test/models/sqlserver/datatype_migration.rb index e3752a3f3..79ee8492d 100644 --- a/test/models/sqlserver/datatype_migration.rb +++ b/test/models/sqlserver/datatype_migration.rb @@ -1,3 +1,8 @@ class SSTestDatatypeMigration < ActiveRecord::Base self.table_name = :sst_datatypes_migration end + +class SSTestDatatypeMigrationJson < ActiveRecord::Base + self.table_name = :sst_datatypes_migration + attribute :json_col, ActiveRecord::Type::SQLServer::Json.new +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 72bdaab96..1ba8d47aa 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -33,6 +33,11 @@ t.varbinary :varbinary_col t.uuid :uuid_col t.ss_timestamp :sstimestamp_col + if supports_json? + t.json :json_col + else + t.text :json_col + end end # Edge Cases From 1fc921c321cae437ac7614843074655646cbd344 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 13 Mar 2017 22:43:00 -0400 Subject: [PATCH 0629/1412] Add smalldatetime type for migrations. Fixes #507 --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/table_definition.rb | 4 ++++ .../connection_adapters/sqlserver/type/smalldatetime.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 2 ++ test/schema/sqlserver_specific_schema.rb | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e49c7d9e..b49faa346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Performance w/inserts. Check binds & use schema cache for id inserts. Fixes #572. Thanks @noelr. +* Add smalldatetime type for migrations. Fixes #507 #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index ce7c9e2f2..c09c65c28 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -23,6 +23,10 @@ def money(*args, **options) args.each { |name| column(name, :money, options) } end + def smalldatetime(*args, **options) + args.each { |name| column(name, :smalldatetime, options) } + end + def datetime(*args, **options) args.each do |name| if options[:precision] diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 22c6c1856..427809fcf 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -19,7 +19,7 @@ def fast_string_to_time_format end def apply_seconds_precision(value) - value.change usec: 0 + value.change usec: 0 if value end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 79bb0da7f..4ba681ec8 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -85,6 +85,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase # 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' @@ -100,6 +101,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns['json_col'].sql_type.must_equal 'nvarchar(max)' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil + assert_line :smalldatetime_col, type: 'smalldatetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil assert_line :datetimeoffset, type: 'datetimeoffset', limit: nil, precision: 7, scale: nil, default: nil assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 1ba8d47aa..e18cb9973 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -21,6 +21,7 @@ # Our type methods. t.real :real_col t.money :money_col + t.smalldatetime :smalldatetime_col t.datetime2 :datetime2_col t.datetimeoffset :datetimeoffset t.smallmoney :smallmoney_col From 256ffe953b05b3ea0f3e6ec6f917663893cb9e75 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 13 Mar 2017 23:18:25 -0400 Subject: [PATCH 0630/1412] Update TODO for Rails v5 [ci skip] --- RAILS5-TODO.md | 9 --------- 1 file changed, 9 deletions(-) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 6ed122f7d..5fd8e2068 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -1,13 +1,4 @@ -## Rails v5.0 - -* Does the schema cache serialize properly since we conform to that now? -* Can we use `OPTIMIZE FOR UNKNOWN` - - http://sqlblog.com/blogs/aaron_bertrand/archive/2011/09/17/bad-habits-to-kick-using-exec-instead-of-sp-executesql.aspx - - http://stackoverflow.com/questions/24016199/sql-server-stored-procedure-become-very-slow-raw-sql-query-is-still-very-fast - - https://blogs.msdn.microsoft.com/sqlprogrammability/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature/ - - ## Rails v5.1 * BIGINT PK support. https://github.com/rails/rails/pull/26266 From db038d33cdbd1dac5a8482edc604b50ac967dbaf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 13 Mar 2017 23:18:49 -0400 Subject: [PATCH 0631/1412] Fix up scratch file [ci skip] --- test/cases/scratchpad_test_sqlserver.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb index 1b12c9f45..0d77122c0 100644 --- a/test/cases/scratchpad_test_sqlserver.rb +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -1,11 +1,8 @@ require 'cases/helper_sqlserver' -require 'models/book' class ScratchpadTestSQLServer < ActiveRecord::TestCase it 'helps debug things' do - $FOO = true - 1000.times { Book.create! name: 'test' } end end From 959c569be9ff82832cbeb673b6497d905974fde6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 16 Mar 2017 07:10:35 -0400 Subject: [PATCH 0632/1412] Remove superfluous DB json type alias. --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index abc4bf66c..5713d0432 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -305,7 +305,6 @@ def initialize_type_map(m) 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.alias_type 'json', 'nvarchar(max)' m.register_type 'ntext', SQLServer::Type::UnicodeText.new # Binary Strings register_class_with_limit m, %r{\Abinary}i, SQLServer::Type::Binary From 1b94978d3997a6af98504ef9046802ffd3a40307 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 18 Mar 2017 18:07:46 -0400 Subject: [PATCH 0633/1412] Use TinyTDS v1.3.0 & vNext SQL Server Docker v1.4 --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- test/bin/setup.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2d448caaf..3832ffb40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=1.2.0 + - TINYTDS_VERSION=1.3.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index 84d0643f9..08c4e1eaf 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=1.2.0 + - SET TINYTDS_VERSION=1.3.0 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index 3f11dfeb6..7e55d1d86 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 1.2.0 + TINYTDS_VERSION: 1.3.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: diff --git a/test/bin/setup.sh b/test/bin/setup.sh index 4713d308e..70b93acbd 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,7 +3,7 @@ set -x set -e -tag=1.3 +tag=1.4 docker pull metaskills/mssql-server-linux-rails:$tag From 8c4884b26837106f885222a8fd898375df73308f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 21 Mar 2017 20:22:58 -0400 Subject: [PATCH 0634/1412] Remove deprecations for Fixnum in 2.4. --- test/cases/connection_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index b51b65e3f..656c42fd4 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -36,7 +36,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase describe 'Connection management' do it 'set spid on connect' do - assert_instance_of Fixnum, connection.spid + ['Fixnum', 'Integer'].must_include connection.spid.class.name end it 'reset spid on disconnect!' do diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index e6d2cabc5..e70c86b55 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -17,7 +17,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'models can use tinyint pk tables' do obj = SSTestTinyintPk.create! name: '1' - obj.id.is_a? Fixnum + ['Fixnum', 'Integer'].must_include obj.id.class.name SSTestTinyintPk.find(obj.id).must_equal obj end From b67a0dbbfefae8e597b68f62077bafecee90e17f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 18 Apr 2017 07:40:38 -0400 Subject: [PATCH 0635/1412] Use Patreon Button --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4156974b2..09b0d9f50 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Support via Gratipay](https://cdn.rawgit.com/gratipay/gratipay-badge/2.3.0/dist/gratipay.svg)](https://gratipay.com/metaskills/) +Become a Patron! # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. From 6359c374f19167d5a717681512aa2b94af7d5331 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 18 Apr 2017 08:37:47 -0400 Subject: [PATCH 0636/1412] [CI SKIP] Create BACKERS.md --- BACKERS.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 BACKERS.md diff --git a/BACKERS.md b/BACKERS.md new file mode 100644 index 000000000..5797c4bc8 --- /dev/null +++ b/BACKERS.md @@ -0,0 +1,32 @@ +# Backers + +You can join in supporting TinyTDS and the Rails SQL Server Adapter development by [pledging on Patreon](https://www.patreon.com/metaskills)! Backers in the same pledge level appear in the order of pledge date. + +### $2000 + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611218) + + +### $500 + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611209) + + +### $250 + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611199) + + +### $100 + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611196) + + +### $50+ + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611186) + + +### $10+ + +[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611149) From 666c6ab40e04ca69f5cb6be2c2c6b3d8ac4dd74d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 18 Apr 2017 08:38:38 -0400 Subject: [PATCH 0637/1412] [CI SKIP] Add sponsor links. --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 09b0d9f50..fd4a2e434 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -Become a Patron! - # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. * [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI @@ -9,6 +7,12 @@ * [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community +## Supporting TinyTDS/Adapter + +Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source projects. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rails-sqlserver/tiny_tds/blob/master/BACKERS.md). If you'd like to join them, check out [MetaSkills Patreon Campaign](https://www.patreon.com/metaskills). + + +## About The Adapter The SQL Server adapter for ActiveRecord v5.0 using SQL Server 2012 or higher. From 2c486e37ebfa837a8774c4d8248187d035866d83 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 18 Apr 2017 15:38:47 -0400 Subject: [PATCH 0638/1412] [CI SKIP] Update patreon campaign info. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fd4a2e434..c219b257a 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ ## Supporting TinyTDS/Adapter -Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source projects. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rails-sqlserver/tiny_tds/blob/master/BACKERS.md). If you'd like to join them, check out [MetaSkills Patreon Campaign](https://www.patreon.com/metaskills). +Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source projects. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rails-sqlserver/tiny_tds/blob/master/BACKERS.md). If you'd like to join them, check out our [Patreon Campaign](https://www.patreon.com/metaskills). ## About The Adapter From 233a053beb44604151dbfd69123a8ef1ee974428 Mon Sep 17 00:00:00 2001 From: Kunihiko Ito Date: Fri, 21 Apr 2017 11:40:38 +0900 Subject: [PATCH 0639/1412] Fix typo of README (#581) miss-spelling of "Contributors" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c219b257a..7a2cff22c 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,7 @@ If you would like to contribute a feature or bugfix, thanks! To make sure your f Many many people have contributed. If you do not see your name here and it should be let us know. Also, many thanks go out to those that have pledged financial contributions. -## Contributers +## Contributors Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors From 2ae162dd5a5e6c056c7c0d5a1cacba33f00f01d0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 21 Mar 2017 22:45:22 -0400 Subject: [PATCH 0640/1412] Start Rails v5.1 with notes. --- RAILS5-TODO.md | 21 ++++++++++++++++++++- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 5fd8e2068..2e3a27340 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -2,4 +2,23 @@ ## Rails v5.1 * BIGINT PK support. https://github.com/rails/rails/pull/26266 - +* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted + or updated because it would violate a not null constraint. +* Raise `ActiveRecord::RangeError` when values that executed are out of range. +* Allow passing extra flags to `db:structure:load` and `db:structure:dump` + Introduces `ActiveRecord::Tasks::DatabaseTasks.structure_(load|dump)_flags` to customize the + eventual commands run against the database, e.g. mysqldump/pg_dump. +* Set `:time` as a timezone aware type and remove deprecation when + `config.active_record.time_zone_aware_types` is not explicitly set. +* Remove deprecated support to passing a column to `#quote`. +* `#tables` and `#table_exists?` return only tables and not views. + All the deprecations on those methods were removed. +* Remove deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize` + and `ActiveRecord::StatementInvalid#original_exception`. +* Remove deprecated tasks: `db:test:clone`, `db:test:clone_schema`, `db:test:clone_structure`. +* Make `table_name=` reset current statement cache, + so queries are not run against the previous table name. +* Deprecate `supports_primary_key?` on connection adapters since it's + been long unused and unsupported. +* Deprecate using `#quoted_id` in quoting. +* Deprecate `supports_migrations?` on connection adapters. diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 34eed07aa..ad261b7fd 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 5.0.0' + spec.add_dependency 'activerecord', '5.1.0.rc1' spec.add_dependency 'tiny_tds' end From d9c7f812ada59e6475db27dfdce1f5ca8cea2b0f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 26 Mar 2017 16:37:06 -0400 Subject: [PATCH 0641/1412] Ensure 5-0-stable `WITH NO_INFOMSGS` for user options query. --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 0899d889d..be1a8d446 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -132,7 +132,7 @@ def use_database(database = nil) def user_options return {} if sqlserver_azure? - rows = select_rows('dbcc useroptions', 'SCHEMA') + 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| if row.instance_of? Hash From da91218146a02bc74e1aaeadc6652c436e33ccc5 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 28 Mar 2017 20:53:59 -0400 Subject: [PATCH 0642/1412] Support new type_to_sql arity. --- .../sqlserver/schema_statements.rb | 6 +++--- test/cases/adapter_test_sqlserver.rb | 16 ++++++++-------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f15f66cd2..30ea38bf9 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -130,7 +130,7 @@ def change_column(table_name, column_name, type, options = {}) remove_indexes(table_name, column_name) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false if options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}" @@ -194,7 +194,7 @@ def extract_foreign_key_action(action, fk_name) end end - def type_to_sql(type, limit = nil, precision = nil, scale = nil) + def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable case type.to_s @@ -240,7 +240,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) if !allow_null.nil? && allow_null == false && !default.nil? do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end - sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, column.limit, column.precision, column.scale}" + sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}" sql << ' NOT NULL' if !allow_null.nil? && allow_null == false do_execute sql end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a8b42a59d..eb81136c8 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -265,23 +265,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'create integers when limit is 4' do - assert_equal 'integer', connection.type_to_sql(:integer, 4) + assert_equal 'integer', connection.type_to_sql(:integer, limit: 4) end it 'create integers when limit is 3' do - assert_equal 'integer', connection.type_to_sql(:integer, 3) + assert_equal 'integer', connection.type_to_sql(:integer, limit: 3) end it 'create smallints when limit is less than 3' do - assert_equal 'smallint', connection.type_to_sql(:integer, 2) - assert_equal 'smallint', connection.type_to_sql(:integer, 1) + assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2) + assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1) end it 'create bigints when limit is greateer than 4' do - assert_equal 'bigint', connection.type_to_sql(:integer, 5) - assert_equal 'bigint', connection.type_to_sql(:integer, 6) - assert_equal 'bigint', connection.type_to_sql(:integer, 7) - assert_equal 'bigint', connection.type_to_sql(:integer, 8) + assert_equal 'bigint', connection.type_to_sql(:integer, limit: 5) + assert_equal 'bigint', connection.type_to_sql(:integer, limit: 6) + assert_equal 'bigint', connection.type_to_sql(:integer, limit: 7) + assert_equal 'bigint', connection.type_to_sql(:integer, limit: 8) end it 'create floats when no limit supplied' do From 0f2b9552b4ef384e2dc6e9efb242206e8a8f19d4 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 10 Apr 2017 21:59:17 -0400 Subject: [PATCH 0643/1412] Prepare CHANGELOG for 5.1 entries. --- CHANGELOG.md | 104 +-------------------------------------------------- VERSION | 2 +- 2 files changed, 2 insertions(+), 104 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b49faa346..92875a71d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,111 +1,9 @@ -## v5.0.6 +## v5.1.0 #### Fixed -* Performance w/inserts. Check binds & use schema cache for id inserts. - Fixes #572. Thanks @noelr. -* Add smalldatetime type for migrations. Fixes #507 - -#### Changed - -* Misc index enhancements or testing. Fixes #570 - Enable `supports_index_sort_order?`, test `supports_partial_index?`, test how expression indexes work. - -#### Added - -* New `primary_key_nonclustered` type for easy In-Memory table creation. -* Examples for an In-Memory table. - -```ruby -create_table :in_memory_table, id: false, - options: 'WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)' do |t| - t.primary_key_nonclustered :id - t.string :name - t.timestamps -end -``` - -* Enable supports_json? Fixes #577. - -```ruby -create_table :users do |t| - t.string :name, :email - t.json :data # Creates a nvarchar(max) column. - end - -class Users < ActiveRecord::Base - attribute :data, ActiveRecord::Type::SQLServer::Json.new -end - -User.create! name: 'Ken Collins', data: { 'admin' => true, 'foo' => 'bar' } - -admin = User.where("JSON_VALUE(data, '$.admin') = CAST(1 AS BIT)").first -admin.data['foo'] # => "bar" -``` - - -## v5.0.5 - -#### Changed - -* Add TinyTDS as a runtime dependency. - - -## v5.0.4 - -#### Fixed - -* Allow `datetimeoffset` to be used in migrations and represented in schema. -* Using transactions and resetting isolation level correctly when `READ_COMMITTED_SNAPSHOT` is set to `ON` Fixes #520 - - -## v5.0.3 - #### Changed -* Reduce view information reflection to per table vs. column. Fixes #552 -* The `user_options` parsing. Works for hash/array. Fixes #535 -* Pass the `:contained` option to TinyTDS. Fixes #527 - - -## v5.0.2 - -#### Fixed - -* Filter table constraints with matching table schema to column. Fixes #478 - - -## v5.0.1 - -#### Changed - -* Set `tds_version` fallback to `7.3`. - -#### Fixed - -* Support 2014, 2012 drop table statement. - - -## v5.0.0 - #### Added -* Support for `supports_datetime_with_precision`. -* Support for `unprepared_statement` blocks on the connection. - -#### Changed - -* Major refactoring of all type objects. Especially time types. - -#### Deprecated - -* Support for a handful of standard Rails deprecations in 5-0-stable suite. - -#### Removed - -* ODBC connection mode. Not been maintained since Rails 4.0. -* View table name detection in `with_identity_insert_enabled` method for fixtures. Perf hit. - -#### Fixed -* Do not output column collation in schema when same as database. diff --git a/VERSION b/VERSION index c20c645d7..831446cbd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.0.6 +5.1.0 From 6f3373ab7e4f6c02dc463a92d76852ce8cfc02cb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 20 Apr 2017 22:26:52 -0400 Subject: [PATCH 0644/1412] Test Rails v5.1.0.rc2. --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index ad261b7fd..dea97a442 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '5.1.0.rc1' + spec.add_dependency 'activerecord', '5.1.0.rc2' spec.add_dependency 'tiny_tds' end From 620baf2be175cfdb5333aa11c69f8efafb65fff6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 20 Apr 2017 22:30:23 -0400 Subject: [PATCH 0645/1412] [Rails51] Base adapter tests passing. * Fix #exec_insert to super up. * Dup frozen strings passed into database statements. * Remove tables/table_exists? deprecations. * Switch to bigint primary keys. * The `drop_table` with force cascade option now mimics in via pure SQL for us. * Support MismatchedForeignKey exception. --- CHANGELOG.md | 3 +++ .../sqlserver/database_statements.rb | 18 +++++++++--------- .../sqlserver/schema_statements.rb | 19 ++++++++++++------- .../connection_adapters/sqlserver_adapter.rb | 14 ++++++++++++-- .../tasks/sqlserver_database_tasks.rb | 4 ++-- lib/arel/visitors/sqlserver.rb | 3 ++- .../pessimistic_locking_test_sqlserver.rb | 11 ----------- test/schema/sqlserver_specific_schema.rb | 2 +- 8 files changed, 41 insertions(+), 33 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92875a71d..ab6577e8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ #### Changed +* The `drop_table` with force cascade option now mimics in via pure SQL for us. + #### Added +* Support MismatchedForeignKey exception. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index be1a8d446..c30108730 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -19,22 +19,22 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) sp_executesql(sql, name, binds, prepare: prepare) end - def exec_insert(sql, name, binds, pk = nil, _sequence_name = nil) + def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil) if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) - with_identity_insert_enabled(id_insert_table_name) { exec_query(sql, name, binds) } + with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk) } else - exec_query(sql, name, binds) + super(sql, name, binds, pk) end end def exec_delete(sql, name, binds) - sql << '; SELECT @@ROWCOUNT AS AffectedRows' - super.rows.first.first + sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows' + super(sql, name, binds).rows.first.first end def exec_update(sql, name, binds) - sql << '; SELECT @@ROWCOUNT AS AffectedRows' - super.rows.first.first + sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows' + super(sql, name, binds).rows.first.first end def supports_statement_cache? @@ -198,7 +198,7 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds) end sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted - sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" end @@ -261,7 +261,7 @@ def sp_executesql_sql(sql, types, params, name) if name == 'EXPLAIN' params.each.with_index do |param, index| substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values. - sql.sub! substitute_at_finder, param.to_s + sql = sql.sub substitute_at_finder, param.to_s end else types = quote(types.join(', ')) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 30ea38bf9..c45e1446a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -8,16 +8,10 @@ def native_database_types end def tables(name = nil) - ActiveSupport::Deprecation.warn 'Passing arguments to #tables is deprecated without replacement.' if name tables_sql('BASE TABLE') end def table_exists?(table_name) - ActiveSupport::Deprecation.warn(<<-MSG.squish) - #table_exists? currently checks both tables and views. - This behavior is deprecated and will be changed with Rails 5.1 to only check tables. - Use #data_source_exists? instead. - MSG data_source_exists?(table_name) end @@ -43,6 +37,17 @@ def create_table(table_name, comment: nil, **options) end def drop_table(table_name, options = {}) + # Mimic CASCADE option as best we can. + if options[:force] == :cascade + execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata| + fktable = fkdata['FKTABLE_NAME'] + fkcolmn = fkdata['FKCOLUMN_NAME'] + pktable = fkdata['PKTABLE_NAME'] + pkcolmn = fkdata['PKCOLUMN_NAME'] + remove_foreign_key fktable, name: fkdata['FK_NAME'] + do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" + end + end if options[:if_exists] && @version_year != 2016 execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}" else @@ -252,7 +257,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) def initialize_native_database_types { - primary_key: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY', + primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY', primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED', integer: { name: 'int', limit: 4 }, bigint: { name: 'bigint' }, diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5713d0432..edf6343a2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -147,10 +147,10 @@ def supports_in_memory_oltp? def disable_referential_integrity tables = tables_with_referential_integrity - tables.each { |t| do_execute "ALTER TABLE #{t} NOCHECK CONSTRAINT ALL" } + tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" } yield ensure - tables.each { |t| do_execute "ALTER TABLE #{t} CHECK CONSTRAINT ALL" } + tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" } end # === Abstract Adapter (Connection Management) ================== # @@ -327,6 +327,16 @@ def translate_exception(e, message) NoDatabaseError.new(message) when /data would be truncated/ ValueTooLong.new(message) + when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/ + pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2) + MismatchedForeignKey.new( + self, + message: message, + table: fk_id.schema, + foreign_key: fk_id.object, + target_table: pk_id.schema, + primary_key: pk_id.object + ) else super end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 57dedbd50..9bf106a7c 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -48,7 +48,7 @@ def purge create true end - def structure_dump(filename) + def structure_dump(filename, extra_flags) command = [ "defncopy", "-S #{Shellwords.escape(configuration['host'])}", @@ -71,7 +71,7 @@ def structure_dump(filename) File.open(filename, "w") { |file| file.puts dump } end - def structure_load(filename) + def structure_load(filename, extra_flags) connection.execute File.read(filename) end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 56b7d6d54..4352da53e 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -190,7 +190,8 @@ def table_From_Statement o def primary_Key_From_Table t return unless t - column_name = schema_cache.primary_keys(t.name) || column_cache(t.name).first.try(:second).try(:name) + column_name = @connection.schema_cache.primary_keys(t.name) || + @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) column_name ? t[column_name] : nil end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 1a4b8940e..9dd789360 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -46,17 +46,6 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - it 'reload with lock when #lock! called' do - assert_nothing_raised do - Person.transaction do - person = Person.find 1 - old, person.first_name = person.first_name, 'fooman' - person.lock! - assert_equal old, person.first_name - end - end - end - it 'can add a custom lock directive' do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do Person.lock('WITH(HOLDLOCK, ROWLOCK)').load diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index e18cb9973..8d66e60cc 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -148,7 +148,7 @@ # Constraints - create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :integer, null: false) } + create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :bigint, null: false) } create_table(:sst_has_pks, force: true) { } execute <<-ADDFKSQL ALTER TABLE sst_has_fks From 004b97f2c08e3eedb9515c9285132477f8f9de09 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 24 Apr 2017 20:41:44 -0400 Subject: [PATCH 0646/1412] [Fix] TypeError: can't quote ActiveRecord::Relation 5027 runs, 14504 assertions, 33 failures, 179 errors, 7 skips 5027 runs, 14941 assertions, 30 failures, 14 errors, 7 skips By removing this we were able to allow `select_all` to do the work for us to convert the realation. --- .../connection_adapters/sqlserver/database_statements.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index c30108730..267e4f6fe 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -3,10 +3,6 @@ module ConnectionAdapters module SQLServer module DatabaseStatements - def select_rows(sql, name = nil, binds = []) - sp_executesql sql, name, binds, fetch: :rows - end - def execute(sql, name = nil) if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } From 3f66c43e090801cad714ef485b1e164e9bf7d04a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 25 Apr 2017 22:14:13 -0400 Subject: [PATCH 0647/1412] [Rails51] Fix coerced tests. --- test/cases/coerced_tests.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 498f4a1dd..026544fad 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -52,14 +52,14 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced require 'models/topic' class AttributeMethodsTest < ActiveRecord::TestCase - coerce_tests! :test_typecast_attribute_from_select_to_false + 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 assert !topic.is_test? end - coerce_tests! :test_typecast_attribute_from_select_to_true + 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 @@ -375,12 +375,6 @@ def test_exists_does_not_select_columns_without_alias_coerced end end - coerce_tests! :test_string_sanitation - def test_string_sanitation_coerced - assert_not_equal "'something ' 1=1'", ActiveRecord::Base.sanitize("something ' 1=1") - assert_equal "N'something; select table'", ActiveRecord::Base.sanitize("something; select table") - end - coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } @@ -650,9 +644,6 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output end - # This accidently returns the wrong number because of our tables too. - coerce_tests! :test_types_line_up - # This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off. coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues From 89ccbe40c611128d9524a11054001eb431c2c1f0 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 17 May 2017 20:13:06 -0400 Subject: [PATCH 0648/1412] Pass `PrimaryKeyIntegerNilDefaultTest` tests. * Allow create tables with PKs like `[id] integer DEFAULT NUL NOT NULL PRIMARY KEY` * Note how IDENTITY(1,1) incrementing is not present. Not allowed with columns that have defaults. --- .../sqlserver/schema_creation.rb | 8 ++++++++ .../sqlserver/schema_dumper.rb | 8 ++++++++ .../sqlserver/table_definition.rb | 18 +++++++++++------- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index b43354c91..7af9be47c 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -28,6 +28,14 @@ def action_sql(action, dependency) end end + def options_include_default?(options) + super || options_primary_key_with_nil_default?(options) + end + + def options_primary_key_with_nil_default?(options) + options[:primary_key] && options.include?(:default) && options[:default].nil? + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 637632f4a..6acba7639 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -5,11 +5,19 @@ module SchemaDumper private + def explicit_primary_key_default?(column) + column.is_primary? && !column.is_identity? + end + def schema_collation(column) return unless column.collation column.collation if column.collation != collation end + def default_primary_key?(column) + super && column.is_primary? && column.is_identity? + end + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index c09c65c28..adaa4a4b2 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -5,10 +5,11 @@ module SQLServer module ColumnMethods def primary_key(name, type = :primary_key, **options) - return super unless type == :uuid - options[:default] = options.fetch(:default, 'NEWID()') - options[:primary_key] = true - column name, type, options + if type == :uuid + options[:default] = options.fetch(:default, 'NEWID()') + options[:primary_key] = true + end + super end def primary_key_nonclustered(*args, **options) @@ -98,9 +99,12 @@ def json(*args, **options) class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition include ColumnMethods - def new_column_definition(name, type, options) - type = :datetime2 if type == :datetime && options[:precision] - super name, type, options + def new_column_definition(name, type, **options) + case type + when :datetime + type = :datetime2 if options[:precision] + end + super end end From babdf59057185902dbaf943eff6aba4af38627e8 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 17 May 2017 21:12:15 -0400 Subject: [PATCH 0649/1412] [Rails51] Pass the following tests. * CoreTest#test_inspect_class * ReflectionTest#test_association_primary_key_type * ReflectionTest#test_integer_columns * ActiveRecord::Migration::ReferencesStatementsTest#test_add_belongs_to_alias * ActiveRecord::Migration::ReferencesStatementsTest#test_creates_reference_id_column --- .../connection_adapters/sqlserver/type/big_integer.rb | 4 ---- test/cases/column_test_sqlserver.rb | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb index 5b786aa24..18f1207af 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -4,10 +4,6 @@ module SQLServer module Type class BigInteger < Integer - def type - :bigint - end - def sqlserver_type 'bigint'.freeze end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index d7fb0476e..42acb08b4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -36,7 +36,7 @@ def assert_obj_set_and_save(attribute, value) it 'bigint(8)' do col = column('bigint') col.sql_type.must_equal 'bigint(8)' - col.type.must_equal :bigint + col.type.must_equal :integer col.null.must_equal true col.default.must_equal 42 obj.bigint.must_equal 42 From b7b3aa8e153725e9e5b4f7f0370bf9fc587bbe81 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 21 May 2017 21:58:38 -0400 Subject: [PATCH 0650/1412] Few Rails v5.1 TODOs. --- RAILS5-TODO.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 2e3a27340..d130567d9 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -22,3 +22,5 @@ been long unused and unsupported. * Deprecate using `#quoted_id` in quoting. * Deprecate `supports_migrations?` on connection adapters. +* Dig moving `Column#sqlserver_options` to `sql_type_metadata` delegate. +* Should we do like PG and add `options[:collation]` before `#add_column_options!`? From a62f7b90aa62c5e78f358bb03a2912ae0a5ef2cb Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 21 May 2017 21:58:59 -0400 Subject: [PATCH 0651/1412] New #add_column_options wiht identity support. Support NotNullViolation error. Fixes the following tests. * ActiveRecord::AdapterTest#test_not_null_violations_are_translated_to_specific_exception * ActiveRecord::Migration::ChangeSchemaTest#test_create_table_with_not_null_column * ActiveRecord::Migration::ChangeSchemaTest#test_add_column_not_null_with_default * ActiveRecord::Migration::ChangeSchemaTest#test_add_column_not_null_without_default * LegacyPrimaryKeyTest#test_legacy_bigint_primary_key_should_not_be_auto_incremented * LegacyPrimaryKeyTest#test_legacy_primary_key_should_be_auto_incremented * LegacyPrimaryKeyTest#test_legacy_integer_primary_key_should_not_be_auto_incremented * PrimaryKeyWithAutoIncrementTest#test_primary_key_with_bigint * PrimaryKeyWithAutoIncrementTest#test_primary_key_with_integer --- .../sqlserver/schema_creation.rb | 14 ++++++++++++++ .../sqlserver/table_definition.rb | 6 +++++- .../connection_adapters/sqlserver_adapter.rb | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 7af9be47c..382216573 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -16,6 +16,20 @@ def visit_TableDefinition(o) end end + def add_column_options!(sql, options) + sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) + if options[:null] == false + sql << " NOT NULL" + end + if options[:is_identity] == true + sql << " IDENTITY(1,1)" + end + if options[:primary_key] == true + sql << " PRIMARY KEY" + end + sql + end + def action_sql(action, dependency) case dependency when :restrict diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index adaa4a4b2..de66ea79c 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -5,7 +5,9 @@ module SQLServer module ColumnMethods def primary_key(name, type = :primary_key, **options) - if type == :uuid + if [:integer, :bigint].include?(type) + options[:is_identity] = true unless options.key?(:default) + elsif type == :uuid options[:default] = options.fetch(:default, 'NEWID()') options[:primary_key] = true end @@ -103,6 +105,8 @@ def new_column_definition(name, type, **options) case type when :datetime type = :datetime2 if options[:precision] + when :primary_key + options[:is_identity] = true end super end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index edf6343a2..46c742c81 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -337,6 +337,8 @@ def translate_exception(e, message) target_table: pk_id.schema, primary_key: pk_id.object ) + when /Cannot insert the value NULL into column.*does not allow nulls/ + NotNullViolation.new(message) else super end From 3cd0527d62786b2b27e6668558eb82a0a9ff795c Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 21 May 2017 22:06:40 -0400 Subject: [PATCH 0652/1412] Rails v5.1 TODO. --- RAILS5-TODO.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index d130567d9..6c3d27706 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -24,3 +24,4 @@ * Deprecate `supports_migrations?` on connection adapters. * Dig moving `Column#sqlserver_options` to `sql_type_metadata` delegate. * Should we do like PG and add `options[:collation]` before `#add_column_options!`? +* Translated exceptions: `SerializationFailure` and `RangeError`. From 00c88445ca93a7af6082ca55e560617017552262 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 22 May 2017 21:34:36 -0400 Subject: [PATCH 0653/1412] [Rails51] Conform to new #data_source_sql and #quoted_scope Rocking support from Rails for proper multi DB schema reflection!!! Fixes the following tests: * ViewWithPrimaryKeyTest#test_column_definitions * ViewWithPrimaryKeyTest#test_attributes * ViewWithPrimaryKeyTest#test_reading * ViewWithPrimaryKeyTest#test_table_exists * ViewWithoutPrimaryKeyTest#test_table_exists --- .../sqlserver/schema_statements.rb | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c45e1446a..e28235eb7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,28 +7,16 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end - def tables(name = nil) - tables_sql('BASE TABLE') - end - - def table_exists?(table_name) - data_source_exists?(table_name) - end - - def data_source_exists?(table_name) - return false if table_name.blank? - unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object - super(unquoted_table_name) - end - - def views - tables_sql('VIEW') - end + # def data_source_exists?(table_name) + # return false if table_name.blank? + # unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object + # super(unquoted_table_name) + # end - def view_exists?(table_name) - identifier = SQLServer::Utils.extract_identifiers(table_name) - super(identifier.object) - end + # def view_exists?(table_name) + # identifier = SQLServer::Utils.extract_identifiers(table_name) + # super(identifier.object) + # end def create_table(table_name, comment: nil, **options) res = super @@ -250,8 +238,29 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute sql end + private - protected + def data_source_sql(name = nil, type: nil) + scope = quoted_scope name, type: type + table_name = lowercase_schema_reflection_sql 'TABLE_NAME' + sql = "SELECT #{table_name}" + sql << ' FROM INFORMATION_SCHEMA.TABLES' + sql << ' WHERE TABLE_CATALOG = DB_NAME()' + sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}" + sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] + sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] + sql << " ORDER BY #{table_name}" + sql + end + + def quoted_scope(name = nil, type: nil) + identifier = SQLServer::Utils.extract_identifiers(name) + {}.tap do |scope| + scope[:schema] = identifier.schema || 'dbo' + scope[:name] = identifier.object if identifier.object + scope[:type] = type if type + end + end # === SQLServer Specific ======================================== # @@ -291,12 +300,6 @@ def initialize_native_database_types } end - def tables_sql(type) - tn = lowercase_schema_reflection_sql 'TABLE_NAME' - sql = "SELECT #{tn} FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = '#{type}' ORDER BY TABLE_NAME" - select_values sql, 'SCHEMA' - end - def column_definitions(table_name) identifier = if database_prefix_remote_server? SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") @@ -480,7 +483,7 @@ def view_information(table_name) @view_information ||= {} @view_information[table_name] ||= begin identifier = SQLServer::Utils.extract_identifiers(table_name) - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{identifier.object}'", 'SCHEMA' + view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA' if view_info view_info = view_info.with_indifferent_access if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000 @@ -503,8 +506,6 @@ def views_real_column_name(table_name, column_name) match_data ? match_data[1] : column_name end - private - def create_table_definition(*args) SQLServer::TableDefinition.new(*args) end From 8b576877e7efeb29dc451b71a9f53161e93ed832 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 22 May 2017 21:37:16 -0400 Subject: [PATCH 0654/1412] Remove commented out code. --- .../sqlserver/schema_statements.rb | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e28235eb7..40c8ad712 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,17 +7,6 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end - # def data_source_exists?(table_name) - # return false if table_name.blank? - # unquoted_table_name = SQLServer::Utils.extract_identifiers(table_name).object - # super(unquoted_table_name) - # end - - # def view_exists?(table_name) - # identifier = SQLServer::Utils.extract_identifiers(table_name) - # super(identifier.object) - # end - def create_table(table_name, comment: nil, **options) res = super clear_cache! From 61cea36ba7ddfe83efab74146d900f1bfdf9a53b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 24 May 2017 16:35:28 -0400 Subject: [PATCH 0655/1412] [Rails51] Coerce a few tests. * RelationTest#test_reverse_arel_assoc_order_with_function * RelationTest#test_0020_relations * RelationTest#test_empty_complex_chained_relations IMPORTANT: https://github.com/rails/rails/pull/28699 --- test/cases/coerced_tests.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 026544fad..1cbc39ae0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -613,6 +613,26 @@ def test_reverse_order_with_function_other_predicates_coerced # Leave it up to users to format selects/functions so HAVING works correctly. coerce_tests! :test_multiple_where_and_having_clauses coerce_tests! :test_having_with_binds_for_both_where_and_having + + # Find any limit via our expression. + coerce_tests! %r{relations don't load all records in #inspect} + def test_relations_dont_load_all_records_in_inspect_coerced + assert_sql(/NEXT @0 ROWS.*@0 = \d+/) do + Post.all.inspect + end + end + + # I wanted to add `.order("author_id")` scope to avoid error: Column "posts.id" is invalid in the ORDER BY + # However, this pull request on Rails core drops order on exists relation. https://github.com/rails/rails/pull/28699 + # so we are skipping all together. + coerce_tests! :test_empty_complex_chained_relations + + # Use LEN() vs length() function. + coerce_tests! :test_reverse_arel_assoc_order_with_function + def test_reverse_arel_assoc_order_with_function_coerced + topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order + assert_equal topics(:second).title, topics.first.title + end end From 21c39e89472523ec22add585b3a6ee07e8cf168a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 24 May 2017 21:14:53 -0400 Subject: [PATCH 0656/1412] [Rails51] Fix a few more tests: * ActiveRecord::ConnectionAdapters::QuoteARBaseTest#test_type_cast_ar_object * ActiveRecord::ConnectionAdapters::QuoteARBaseTest#test_quote_ar_object * ActiveRecord::ConnectionAdapters::TypeCastingTest * ActiveRecord::ConnectionAdapters::TypeCastingTest#test_type_cast_time --- .../sqlserver/type/data.rb | 5 ++++ test/cases/coerced_tests.rb | 23 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index a96d54cd1..683e0f7c8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -23,6 +23,11 @@ def inspect @value.inspect end + def eql?(other) + self.class == other.class && self.value == other.value + end + alias :== :eql? + end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1cbc39ae0..d6ea36754 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -161,6 +161,29 @@ class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase +module ActiveRecord + module ConnectionAdapters + class QuoteARBaseTest < ActiveRecord::TestCase + + # Use our date format. + coerce_tests! :test_quote_ar_object + def test_quote_ar_object_coerced + value = DatetimePrimaryKey.new(id: @time) + assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) + end + + # Use our date format. + coerce_tests! :test_type_cast_ar_object + def test_type_cast_ar_object_coerced + value = DatetimePrimaryKey.new(id: @time) + assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) + end + + end + end +end + + module ActiveRecord class Migration From 1faa85512b53b7ed47fc2445f20475de9fafaffe Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 24 May 2017 21:28:07 -0400 Subject: [PATCH 0657/1412] [Rails51] Remove supports_primary_key and coerce no key test. * PrimaryKeysTest#test_create_without_primary_key_no_extra_query * PrimaryKeysTest#test_deprecate_supports_primary_key --- RAILS5-TODO.md | 2 -- .../connection_adapters/sqlserver_adapter.rb | 4 ---- test/cases/coerced_tests.rb | 9 +++++++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md index 6c3d27706..d0bf9dc4b 100644 --- a/RAILS5-TODO.md +++ b/RAILS5-TODO.md @@ -18,8 +18,6 @@ * Remove deprecated tasks: `db:test:clone`, `db:test:clone_schema`, `db:test:clone_structure`. * Make `table_name=` reset current statement cache, so queries are not run against the previous table name. -* Deprecate `supports_primary_key?` on connection adapters since it's - been long unused and unsupported. * Deprecate using `#quoted_id` in quoting. * Deprecate `supports_migrations?` on connection adapters. * Dig moving `Column#sqlserver_options` to `sql_type_metadata` delegate. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 46c742c81..00783ecd0 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -77,10 +77,6 @@ def supports_migrations? true end - def supports_primary_key? - true - end - def supports_ddl_transactions? true end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d6ea36754..e0f66a377 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -593,6 +593,15 @@ def test_registering_new_handlers_coerced +class PrimaryKeysTest < ActiveRecord::TestCase + # Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted + # but as far as I can tell, this is only one for us anyway. + coerce_tests! :test_create_without_primary_key_no_extra_query +end + + + + require 'models/task' class QueryCacheTest < ActiveRecord::TestCase coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays From 42c46c0d617555864fe86e1cf328312114efa999 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 25 May 2017 21:27:19 -0400 Subject: [PATCH 0658/1412] [Rails51] Find #primary_keys propertly. * Support proper ordinal order. * Bake in schema and full DB identifer support like #column_definitions. --- .../sqlserver/schema_statements.rb | 32 +++++++++++++++---- .../connection_adapters/sqlserver_adapter.rb | 8 +++++ test/support/sql_counter_sqlserver.rb | 2 +- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 40c8ad712..0058dfec0 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -85,10 +85,34 @@ def new_column(name, default, sql_type_metadata, null, table_name, default_funct end def primary_keys(table_name) - primaries = schema_cache.columns(table_name).select(&:is_primary?).map(&:name) + primaries = primary_keys_select(table_name) primaries.present? ? primaries : identity_columns(table_name).map(&:name) end + def primary_keys_select(table_name) + identifier = database_prefix_identifier(table_name) + database = identifier.fully_qualified_database_quoted + sql = %{ + SELECT 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 + AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_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))} + AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' + ORDER BY KCU.ORDINAL_POSITION ASC + }.gsub(/[[:space:]]/, ' ') + binds = [] + nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 + binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) + binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? + sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] } + end + def rename_table(table_name, new_name) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" rename_table_indexes(table_name, new_name) @@ -290,11 +314,7 @@ def initialize_native_database_types end def column_definitions(table_name) - identifier = if database_prefix_remote_server? - SQLServer::Utils.extract_identifiers("#{database_prefix}#{table_name}") - else - SQLServer::Utils.extract_identifiers(table_name) - end + identifier = database_prefix_identifier(table_name) database = identifier.fully_qualified_database_quoted view_exists = view_exists?(table_name) view_tblnm = view_table_name(table_name) if view_exists diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 00783ecd0..b3ce3f7ad 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -226,6 +226,14 @@ def database_prefix @connection_options[:database_prefix] end + def database_prefix_identifier(name) + if database_prefix_remote_server? + SQLServer::Utils.extract_identifiers("#{database_prefix}#{name}") + else + SQLServer::Utils.extract_identifiers(name) + end + end + def version self.class::VERSION end diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 72946735c..a3130695e 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -13,7 +13,7 @@ def capture_sql_ss end ignored_sql = [ - /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS)/im, + /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, From 72f371b551a49acfb2459a225b833aec5381ac81 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 26 May 2017 20:42:01 -0400 Subject: [PATCH 0659/1412] [Rails51] Pass UniquenessValidationTest#test_validate_case_sensitive_uniqueness --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 267e4f6fe..88a67440f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -76,7 +76,7 @@ def release_savepoint(name = current_savepoint_name) end def case_sensitive_comparison(table, attribute, column, value) - if value && value.acts_like?(:string) + if column.collation && !column.case_sensitive? table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new)) else super From 15253f58856ccae0b1d823ce0cc3a49184e3915f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 26 May 2017 20:51:23 -0400 Subject: [PATCH 0660/1412] [Rails51] AdapterTest#test_numeric_value_out_of_ranges_are_translated_to_specific_exception --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b3ce3f7ad..5f5176d6b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -343,6 +343,8 @@ def translate_exception(e, message) ) when /Cannot insert the value NULL into column.*does not allow nulls/ NotNullViolation.new(message) + when /Arithmetic overflow error/ + RangeError.new(message) else super end From b7084aa5a04592c95b5a0ac64b7c52cfdf94145f Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 26 May 2017 21:38:17 -0400 Subject: [PATCH 0661/1412] [Rails51] Ignore AdapterTest#test_select_all_with_legacy_binds --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e0f66a377..126bd943f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -31,6 +31,9 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced require 'models/event' module ActiveRecord class AdapterTest < ActiveRecord::TestCase + # I really dont think we can support legacy binds. + coerce_tests! :test_select_all_with_legacy_binds + # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement From 27520c9a97e7e47eada50402b1929db4a4ac8f01 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 26 May 2017 22:37:50 -0400 Subject: [PATCH 0662/1412] [Rails51] Ensure clean Rails env. Pass /test_establish_connection_using_3_levels_config/ --- test/cases/helper_sqlserver.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 59391055f..74e364d8f 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -23,9 +23,14 @@ class TestCase < ActiveSupport::TestCase let(:logger) { ActiveRecord::Base.logger } + setup :ensure_clean_rails_env private + def ensure_clean_rails_env + Rails.instance_variable_set(:@_env, nil) if defined?(::Rails) + end + def host_windows? RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end From 35c3ac3c750e2be981502835b055cf0aecd2fa02 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 26 May 2017 22:52:40 -0400 Subject: [PATCH 0663/1412] [Rail51] ActiveRecord::ConnectionAdapters::SchemaCacheTest#test_yaml_loads_5_1_dump --- test/cases/coerced_tests.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 126bd943f..d2a784970 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -862,3 +862,19 @@ class StatementCacheTest < ActiveRecord::TestCase coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed end end + + + + +module ActiveRecord + module ConnectionAdapters + class SchemaCacheTest < ActiveRecord::TestCase + private + # We need to give the full path for this to work. + def schema_dump_path + File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml' + end + end + end +end + From e1e3bd02f59aea815675c927e8c76008b9e70f0a Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 27 May 2017 20:17:00 -0400 Subject: [PATCH 0664/1412] [Rails51] ActiveRecord::Migration::ColumnsTest#test_change_column_to_drop_default_with_null_false --- .../connection_adapters/sqlserver/schema_statements.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 0058dfec0..986c904c3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -137,8 +137,10 @@ def change_column(table_name, column_name, type, options = {}) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" - sql_commands[-1] << ' NOT NULL' if !options[:null].nil? && options[:null] == false - if options_include_default?(options) + sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false + if options.key?(:default) && default_constraint_name(table_name, column_name).present? + change_column_default(table_name, column_name, options[:default]) + elsif options_include_default?(options) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}" end # Add any removed indexes back @@ -146,6 +148,7 @@ def change_column(table_name, column_name, type, options = {}) 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| do_execute(c) } + clear_cache! end def change_column_default(table_name, column_name, default_or_changes) From b6f8a7795c2f9fde127ccc0ab6166b6dced2ac43 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 27 May 2017 20:26:07 -0400 Subject: [PATCH 0665/1412] [Rails51] CopyMigrationsTest#test_deprecate_supports_migrations --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ---- test/cases/adapter_test_sqlserver.rb | 4 ---- 2 files changed, 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5f5176d6b..7f164a001 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -73,10 +73,6 @@ def schema_creation SQLServer::SchemaCreation.new self end - def supports_migrations? - true - end - def supports_ddl_transactions? true end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index eb81136c8..aeeaff700 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -37,10 +37,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'SQLServer', connection.adapter_name end - it 'supports migrations' do - assert connection.supports_migrations? - end - it 'support DDL in transactions' do assert connection.supports_ddl_transactions? end From 73878d521cf41aaadb5f83b70511086f07660b55 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sat, 27 May 2017 21:27:50 -0400 Subject: [PATCH 0666/1412] [Rails51] Do not schema dump limits for certain types. Passes: * SchemaDumperTest#test_schema_dump_does_not_include_limit_for_text_field * SchemaDumperTest#test_schema_dump_does_not_include_limit_for_binary_field --- .../sqlserver/schema_dumper.rb | 13 ++++++++++++ test/cases/schema_dumper_test_sqlserver.rb | 20 +++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 6acba7639..708238d42 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -3,12 +3,25 @@ module ConnectionAdapters module SQLServer module SchemaDumper + SQLSEVER_NO_LIMIT_TYPES = [ + 'text', + 'ntext', + 'varchar(max)', + 'nvarchar(max)', + 'varbinary(max)' + ].freeze + private def explicit_primary_key_default?(column) column.is_primary? && !column.is_identity? end + def schema_limit(column) + return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type) + super + end + def schema_collation(column) return unless column.collation column.collation if column.collation != collation diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 4ba681ec8..950c85b62 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -38,17 +38,17 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase # Character Strings assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil - assert_line :varchar_max, type: 'varchar_max', limit: 2147483647, precision: nil, scale: nil, default: "test varchar_max", collation: nil - assert_line :text, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: "test text", collation: nil + assert_line :varchar_max, type: 'varchar_max', limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil + assert_line :text, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: "test text", collation: nil # Unicode Character Strings assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil - assert_line :nvarchar_max, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil - assert_line :ntext, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: "test ntext åå", collation: nil + assert_line :nvarchar_max, type: 'text', limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil + assert_line :ntext, type: 'ntext', limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil # Binary Strings assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil - assert_line :varbinary_max, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :varbinary_max, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil # Other Data Types assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: -> { "newid()" } assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil @@ -76,12 +76,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :decimal_col, type: 'decimal', limit: nil, precision: 18, scale: 0, default: nil assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil assert_line :string_col, type: 'string', limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :text_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil assert_line :time_col, type: 'time', limit: nil, precision: 7, scale: nil, default: nil assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_col, type: 'binary', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :binary_col, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil # Our type methods. columns['real_col'].sql_type.must_equal 'real' columns['money_col'].sql_type.must_equal 'money' @@ -107,14 +107,14 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_basic_col, type: 'text_basic', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :text_basic_col, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: nil assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil - assert_line :ntext_col, type: 'ntext', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :ntext_col, type: 'ntext', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil - assert_line :json_col, type: 'text', limit: 2147483647, precision: nil, scale: nil, default: nil + assert_line :json_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil end # Special Cases From 0cb2732372c64f547457aa14297868f09b8cb80e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 28 May 2017 14:22:31 -0400 Subject: [PATCH 0667/1412] [Rails51] Pass failing test on Windows. --- test/cases/coerced_tests.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d2a784970..bfcd1b2c6 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -315,6 +315,10 @@ module ConnectionAdapters module ActiveRecord + class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase + # Skip this test with /tmp/my_schema_cache.yml path on Windows. + coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + end class DatabaseTasksCreateAllTest < ActiveRecord::TestCase # We extend `local_database?` so that common VM IPs can be used. coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases From d0b5c3484679fabf246e83fa5ab2608d79825b45 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 28 May 2017 15:38:53 -0400 Subject: [PATCH 0668/1412] [Rails51] Exempt test on windows. --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bfcd1b2c6..1bc1de16c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -121,6 +121,9 @@ class BindParameterTest < ActiveRecord::TestCase class CalculationsTest < ActiveRecord::TestCase + # I have no idea why 2 queries happen on windows vs 1. + coerce_tests! :test_offset_is_kept if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + # Are decimal, not integer. coerce_tests! :test_should_return_decimal_average_of_integer_field def test_should_return_decimal_average_of_integer_field_coerced From 495ae43834556ff539840b78be89790f94b54326 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 28 May 2017 19:23:18 -0400 Subject: [PATCH 0669/1412] [Rails51] Fix random failing test. --- test/cases/coerced_tests.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1bc1de16c..af1ebb0d1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -121,8 +121,14 @@ class BindParameterTest < ActiveRecord::TestCase class CalculationsTest < ActiveRecord::TestCase - # I have no idea why 2 queries happen on windows vs 1. - coerce_tests! :test_offset_is_kept if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + # This fails randomly due to schema cache being lost? + coerce_tests! :test_offset_is_kept + def test_offset_is_kept_coerced + Account.first + queries = assert_sql { Account.offset(1).count } + assert_equal 1, queries.length + assert_match(/OFFSET/, queries.first) + end # Are decimal, not integer. coerce_tests! :test_should_return_decimal_average_of_integer_field From 049ace1b9bdf7d60de4fa64e66088dd2408ab3a2 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 28 May 2017 20:56:50 -0400 Subject: [PATCH 0670/1412] Release Rails v5.1 support. --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index dea97a442..c7d7463d6 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '5.1.0.rc2' + spec.add_dependency 'activerecord', '~> 5.1.0' spec.add_dependency 'tiny_tds' end From 2f5882f24788d01555725194f8a2c0fd263738bf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 28 May 2017 23:21:38 -0400 Subject: [PATCH 0671/1412] Test against CTP v2.0 http://bit.ly/2oPlP7E SQL Server 2017 Community Technology Preview 2.0 now available https://blogs.technet.microsoft.com/dataplatforminsider/2017/04/19/sql-server-2017-community-technology-preview-2-0-now-available/ --- test/bin/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/setup.sh b/test/bin/setup.sh index 70b93acbd..d2d19e67d 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,7 +3,7 @@ set -x set -e -tag=1.4 +tag=2.0 docker pull metaskills/mssql-server-linux-rails:$tag From e2c3c5fffa8104e7979d8209d13e27f3b548e17e Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 29 May 2017 18:08:55 -0400 Subject: [PATCH 0672/1412] Remove pre-quoted table name. Dont do this. --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 6 ------ test/models/sqlserver/dot_table_name.rb | 3 --- test/schema/sqlserver_specific_schema.rb | 4 ---- 4 files changed, 1 insertion(+), 14 deletions(-) delete mode 100644 test/models/sqlserver/dot_table_name.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 986c904c3..3800f4d22 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -25,7 +25,7 @@ def drop_table(table_name, options = {}) do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" end end - if options[:if_exists] && @version_year != 2016 + if options[:if_exists] && @version_year < 2016 execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}" else super diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index e70c86b55..4f106385d 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -9,12 +9,6 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase SSTestDollarTableName.limit(20).offset(1) end - it 'handle dot table names' do - SSTestDotTableName.create! name: 'test' - SSTestDotTableName.limit(20).offset(1) - SSTestDotTableName.where(name: 'test').first.must_be :present? - end - it 'models can use tinyint pk tables' do obj = SSTestTinyintPk.create! name: '1' ['Fixnum', 'Integer'].must_include obj.id.class.name diff --git a/test/models/sqlserver/dot_table_name.rb b/test/models/sqlserver/dot_table_name.rb deleted file mode 100644 index 13799ab72..000000000 --- a/test/models/sqlserver/dot_table_name.rb +++ /dev/null @@ -1,3 +0,0 @@ -class SSTestDotTableName < ActiveRecord::Base - self.table_name = '[dbo].[some.Name]' -end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 8d66e60cc..98df36468 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -64,10 +64,6 @@ t.uuid :uuid_nil_default, default: nil end - create_table '[some.Name]', force: true do |t| - t.varchar :name - end - create_table 'sst_my$strange_table', force: true do |t| t.string :name end From 95f12e079de8eabedc42735ba87266ab49f95e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Rigart?= Date: Wed, 31 May 2017 13:14:59 +0200 Subject: [PATCH 0673/1412] Bugfix: Use `ActiveSupport.on_load` to hook into ActiveRecord (#598) * Use `ActiveSupport.on_load` to hook into ActiveRecord #588 --- .../sqlserver/core_ext/active_record.rb | 4 +++- .../sqlserver/core_ext/attribute_methods.rb | 4 +++- .../connection_adapters/sqlserver/core_ext/explain.rb | 6 ++++-- .../sqlserver/core_ext/explain_subscriber.rb | 8 +++++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index b1019e34e..ae8359686 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -24,4 +24,6 @@ def execute_procedure(proc_name, *variables) end end -ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ActiveRecord +ActiveSupport.on_load(:active_record) do + include ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ActiveRecord +end 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 eecb5e34e..6687ac36a 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 @@ -22,4 +22,6 @@ def attributes_for_update(attribute_names) end end -ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AttributeMethods +ActiveSupport.on_load(:active_record) do + include ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AttributeMethods +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 bdacc573e..b12502197 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -43,5 +43,7 @@ def unprepare_sqlserver_statement(sql) end end -ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain -ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain +ActiveSupport.on_load(:active_record) do + extend ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain + ActiveRecord::Relation.include(ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain) +end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index d96f8ac2c..45c56cd78 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1,4 +1,6 @@ -silence_warnings do - # Already defined in Rails - ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i +ActiveSupport.on_load(:active_record) do + silence_warnings do + # Already defined in Rails + ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i + end end From 4a4ce1e37e7ef4ea6d388342e9bfbbc22c6fe734 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 31 May 2017 07:16:13 -0400 Subject: [PATCH 0674/1412] [CI SKIP] Update CHANGELOG --- CHANGELOG.md | 7 ++++++- VERSION | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6577e8e..2986bcffd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ -## v5.1.0 +## v5.1.1 #### Fixed +* Use `ActiveSupport.on_load` to hook into ActiveRecord Fixes #588 #598 + + +## v5.1.0 + #### Changed * The `drop_table` with force cascade option now mimics in via pure SQL for us. diff --git a/VERSION b/VERSION index 831446cbd..ac14c3dfa 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.0 +5.1.1 From d8d53035c9055fce675280a8db9acf317e3142f1 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 2 Jul 2017 21:18:14 -0400 Subject: [PATCH 0675/1412] Use TinyTDS v2.0.0.pre1 --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3832ffb40..29a251738 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=1.3.0 + - TINYTDS_VERSION=2.0.0.pre1 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index 08c4e1eaf..92e66b154 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=1.3.0 + - SET TINYTDS_VERSION=2.0.0.pre1 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index 7e55d1d86..8cf316e2d 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 1.3.0 + TINYTDS_VERSION: 2.0.0.pre1 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: From 4a8368589dcf36ea8ee0f71d7bcb975523361e15 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 30 Jul 2017 11:58:21 -0400 Subject: [PATCH 0676/1412] Use TinyTDS v2.1.0.pre1 gems. (#611) --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 29a251738..23688e751 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=2.0.0.pre1 + - TINYTDS_VERSION=2.1.0.pre1 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index 92e66b154..cb6fb53eb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.0.0.pre1 + - SET TINYTDS_VERSION=2.1.0.pre1 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index 8cf316e2d..26c470f11 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.0.0.pre1 + TINYTDS_VERSION: 2.1.0.pre1 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: From b183dcdcbc850e55dcc56978d3d67597a521dde9 Mon Sep 17 00:00:00 2001 From: Michael Dotterer Date: Mon, 4 Sep 2017 19:43:44 -0400 Subject: [PATCH 0677/1412] Fix mangled times after save when default_timezone is set to local (#609) --- .../sqlserver/type/datetime.rb | 9 +++----- test/cases/coerced_tests.rb | 22 +++++++++++++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 1aefb34aa..700c99e82 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -35,7 +35,9 @@ def quoted(value) private def fast_string_to_time(string) - fast_string_to_time_zone.strptime(string, fast_string_to_time_format).time + 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)) rescue ArgumentError super end @@ -43,11 +45,6 @@ def fast_string_to_time(string) def fast_string_to_time_format "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N".freeze end - - def fast_string_to_time_zone - ::Time.zone || ActiveSupport::TimeZone['UTC'] - end - end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index af1ebb0d1..553d76e78 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -586,6 +586,28 @@ def test_update_attributes_coerced # assert_nothing_raised { topic.reload } # assert_equal topic.title, Topic.find(1234).title end + + def test_update_date_time_attributes + Time.use_zone("Eastern Time (US & Canada)") do + topic = Topic.find(1) + time = Time.zone.parse("2017-07-17 10:56") + topic.update_attributes!(written_on: time) + assert_equal(time, topic.written_on) + end + end + + def test_update_date_time_attributes_with_default_timezone_local + with_env_tz 'America/New_York' do + with_timezone_config default: :local do + Time.use_zone("Eastern Time (US & Canada)") do + topic = Topic.find(1) + time = Time.zone.parse("2017-07-17 10:56") + topic.update_attributes!(written_on: time) + assert_equal(time, topic.written_on) + end + end + end + end end From f95fcdd0dddcc3d80abc1c8ec36fdf2857610c63 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 4 Sep 2017 19:45:56 -0400 Subject: [PATCH 0678/1412] Update changelog. --- CHANGELOG.md | 7 ++++++ test/cases/coerced_tests.rb | 44 ++++++++++++++++++------------------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2986bcffd..21c51cf5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.1.2 + +#### Fixed + +* The `fast_string_to_time` method when zone local. Fixes #609 #614 #620 + + ## v5.1.1 #### Fixed diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 553d76e78..7b7bf5074 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -90,6 +90,28 @@ def test_column_names_are_escaped_coerced # Caused in Rails v4.2.5 by adding `firm_id` column in this http://git.io/vBfMs # commit. Trust Rails has this covered. coerce_tests! :test_find_keeps_multiple_group_values + + def test_update_date_time_attributes + Time.use_zone("Eastern Time (US & Canada)") do + topic = Topic.find(1) + time = Time.zone.parse("2017-07-17 10:56") + topic.update_attributes!(written_on: time) + assert_equal(time, topic.written_on) + end + end + + def test_update_date_time_attributes_with_default_timezone_local + with_env_tz 'America/New_York' do + with_timezone_config default: :local do + Time.use_zone("Eastern Time (US & Canada)") do + topic = Topic.find(1) + time = Time.zone.parse("2017-07-17 10:56") + topic.update_attributes!(written_on: time) + assert_equal(time, topic.written_on) + end + end + end + end end @@ -586,28 +608,6 @@ def test_update_attributes_coerced # assert_nothing_raised { topic.reload } # assert_equal topic.title, Topic.find(1234).title end - - def test_update_date_time_attributes - Time.use_zone("Eastern Time (US & Canada)") do - topic = Topic.find(1) - time = Time.zone.parse("2017-07-17 10:56") - topic.update_attributes!(written_on: time) - assert_equal(time, topic.written_on) - end - end - - def test_update_date_time_attributes_with_default_timezone_local - with_env_tz 'America/New_York' do - with_timezone_config default: :local do - Time.use_zone("Eastern Time (US & Canada)") do - topic = Topic.find(1) - time = Time.zone.parse("2017-07-17 10:56") - topic.update_attributes!(written_on: time) - assert_equal(time, topic.written_on) - end - end - end - end end From 5c0d5a7929b448841c4b0622fc4f425ece425dd6 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 4 Sep 2017 19:46:19 -0400 Subject: [PATCH 0679/1412] Start v5.1.2 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index ac14c3dfa..61fcc8735 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.1 +5.1.2 From e1d1d16b975f43e90f9287bd8dd4bbc291833f8b Mon Sep 17 00:00:00 2001 From: Joe Bauser Date: Tue, 5 Sep 2017 21:07:21 -0400 Subject: [PATCH 0680/1412] Use tiny-tds 2.1.0.pre2 with defncopy wrapper (#619) --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- lib/active_record/tasks/sqlserver_database_tasks.rb | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 23688e751..db6c50639 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=2.1.0.pre1 + - TINYTDS_VERSION=2.1.0.pre2 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index cb6fb53eb..9024e320c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.1.0.pre1 + - SET TINYTDS_VERSION=2.1.0.pre2 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index 26c470f11..b1b4aacdf 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.1.0.pre1 + TINYTDS_VERSION: 2.1.0.pre2 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 9bf106a7c..0c62d58e4 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -50,7 +50,7 @@ def purge def structure_dump(filename, extra_flags) command = [ - "defncopy", + "defncopy-ttds", "-S #{Shellwords.escape(configuration['host'])}", "-D #{Shellwords.escape(configuration['database'])}", "-U #{Shellwords.escape(configuration['username'])}", From da4ca140c9df1bb2ffd3ed9aafa193f3deea6a01 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 6 Sep 2017 18:58:16 -0400 Subject: [PATCH 0681/1412] Use TinyTDS-v2.1.0.pre3 (#621) --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index db6c50639..8c9c3feff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=2.1.0.pre2 + - TINYTDS_VERSION=2.1.0.pre3 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index 9024e320c..adcf3e390 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.1.0.pre2 + - SET TINYTDS_VERSION=2.1.0.pre3 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index b1b4aacdf..c7900f60b 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.1.0.pre2 + TINYTDS_VERSION: 2.1.0.pre3 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: From b2a99961a74f4dcec4cc0fcc320073c5fdbe8354 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Thu, 21 Sep 2017 23:04:14 -0400 Subject: [PATCH 0682/1412] Use TinyTDS v2.1.0.pre4. --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8c9c3feff..93420a71e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=2.1.0.pre3 + - TINYTDS_VERSION=2.1.0.pre4 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index adcf3e390..af4179a1c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.1.0.pre3 + - SET TINYTDS_VERSION=2.1.0.pre4 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index c7900f60b..9b4f1eb8a 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.1.0.pre3 + TINYTDS_VERSION: 2.1.0.pre4 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: From 628390933464247846491408323eaccdf806b43b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 22 Sep 2017 22:15:29 -0400 Subject: [PATCH 0683/1412] Patched `Relation#build_count_subquery`. Fixes #613. --- CHANGELOG.md | 7 ++++ .../sqlserver/core_ext/calculations.rb | 39 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 1 + 3 files changed, 47 insertions(+) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 21c51cf5c..8726d5288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.1.3 + +#### Fixed + +* Patched `Relation#build_count_subquery`. Fixes #613. + + ## v5.1.2 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb new file mode 100644 index 000000000..e6478df3e --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -0,0 +1,39 @@ +require 'active_record/relation' +require 'active_record/version' + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module Calculations + private + + def build_count_subquery(relation, column_name, distinct) + relation.select_values = [ + if column_name == :all + distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE) + else + column_alias = Arel.sql("count_column") + aggregate_column(column_name).as(column_alias) + end + ] + + subquery = relation.arel.as(Arel.sql("subquery_for_count")) + select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false) + + Arel::SelectManager.new(subquery).project(select_value) + end + end + end + end + end +end + +ActiveSupport.on_load(:active_record) do + if ActiveRecord::VERSION::MAJOR == 5 && + ActiveRecord::VERSION::MINOR == 1 && + ActiveRecord::VERSION::TINY >= 4 + mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations + ActiveRecord::Relation.prepend(mod) + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 7f164a001..17c0377cd 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -3,6 +3,7 @@ require 'arel_sqlserver' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' +require 'active_record/connection_adapters/sqlserver/core_ext/calculations' require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' From 3a2e404b0870a2f5bd6ee0f869acc2cb428ca309 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Oct 2017 18:01:14 -0400 Subject: [PATCH 0684/1412] For 5.1 in docs. [CI SKIP] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7a2cff22c..b75c7c24b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source proje ## About The Adapter -The SQL Server adapter for ActiveRecord v5.0 using SQL Server 2012 or higher. +The SQL Server adapter for ActiveRecord v5.1 using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 5.0 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. From 7fa5930e85e685cf03de71c7438f55face60cebc Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Oct 2017 18:36:01 -0400 Subject: [PATCH 0685/1412] Identity Inserts with Triggers The adapter uses `OUTPUT INSERTED` so that we can select any data type key, for example UUID tables. However, this poses a problem with tables that use triggers. The solution requires that we use a more complex insert statement which uses a temporary table to select the inserted identity. To use this format you must declare your table exempt from the simple output inserted style with the table name into a concurrent hash. Optionally, you can set the data type of the table's primary key to return. ```ruby adapter = ActiveRecord::ConnectionAdapters::SQLServerAdapter adapter.exclude_output_inserted_table_names['my_table_name'] = true adapter.exclude_output_inserted_table_names['my_uuid_table_name'] = 'uniqueidentifier' ``` --- CHANGELOG.md | 1 + README.md | 16 +++++++- .../sqlserver/database_statements.rb | 32 ++++++++++++++-- .../connection_adapters/sqlserver_adapter.rb | 2 + test/cases/adapter_test_sqlserver.rb | 12 +++--- test/cases/trigger_test_sqlserver.rb | 30 +++++++++++++++ test/models/sqlserver/trigger.rb | 7 ++++ test/models/sqlserver/trigger_history.rb | 3 ++ test/schema/sqlserver_specific_schema.rb | 38 +++++++++++++++++++ 9 files changed, 131 insertions(+), 10 deletions(-) create mode 100644 test/cases/trigger_test_sqlserver.rb create mode 100644 test/models/sqlserver/trigger.rb create mode 100644 test/models/sqlserver/trigger_history.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8726d5288..2ed450d9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed * Patched `Relation#build_count_subquery`. Fixes #613. +* Inserts to tables with triggers using default `OUTPUT INSERTED` style. Fixes #595. ## v5.1.2 diff --git a/README.md b/README.md index b75c7c24b..a8c06b03c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,6 @@ The SQL Server adapter for ActiveRecord v5.1 using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 5.0 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. - #### Native Data Type Support We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb#L243) for an updated list. @@ -28,6 +27,21 @@ The following types (date, datetime2, datetimeoffset, time) all require TDS vers The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a pecision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. +#### Identity Inserts with Triggers + +The adapter uses `OUTPUT INSERTED` so that we can select any data type key, for example UUID tables. However, this poses a problem with tables that use triggers. The solution requires that we use a more complex insert statement which uses a temporary table to select the inserted identity. To use this format you must declare your table exempt from the simple output inserted style with the table name into a concurrent hash. Optionally, you can set the data type of the table's primary key to return. + +```ruby +adapter = ActiveRecord::ConnectionAdapters::SQLServerAdapter + +# Will assume `bigint` as the id key temp table type. +adapter.exclude_output_inserted_table_names['my_table_name'] = true + +# Explicitly set the data type for the temporary key table. +adapter.exclude_output_inserted_table_names['my_uuid_table_name'] = 'uniqueidentifier' +``` + + #### Force Schema To Lowercase Although it is not necessary, the Ruby convention is to use lowercase method names. If your database schema is in upper or mixed case, we can force all table and column names during the schema reflection process to be lowercase. Add this to your config/initializers file for the adapter. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 88a67440f..28729e9df 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -192,9 +192,19 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds) table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) end - sql = if pk && self.class.use_output_inserted && !database_prefix_remote_server? + sql = if pk && use_output_inserted? && !database_prefix_remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted - sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) + if exclude_output_inserted + id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted + <<-SQL.strip_heredoc + DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); + #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} + SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable + SQL + else + sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + end else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" end @@ -279,6 +289,21 @@ def raw_connection_do(sql) # === SQLServer Specific (Identity Inserts) ===================== # + def use_output_inserted? + self.class.use_output_inserted + end + + def exclude_output_inserted_table_names? + !self.class.exclude_output_inserted_table_names.empty? + end + + def exclude_output_inserted_table_name?(table_name, sql) + return false unless exclude_output_inserted_table_names? + table_name ||= get_table_name(sql) + return false unless table_name + self.class.exclude_output_inserted_table_names[table_name] + end + def exec_insert_requires_identity?(sql, pk, binds) query_requires_identity_insert?(sql) if pk && binds.map(&:name).include?(pk) end @@ -287,7 +312,8 @@ def query_requires_identity_insert?(sql) if insert_sql?(sql) table_name = get_table_name(sql) id_column = identity_columns(table_name).first - id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false + # id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false + id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? table_name : false else false end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 17c0377cd..975317e72 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -45,11 +45,13 @@ class SQLServerAdapter < AbstractAdapter cattr_accessor :cs_equality_operator, instance_accessor: false cattr_accessor :use_output_inserted, instance_accessor: false + cattr_accessor :exclude_output_inserted_table_names, instance_accessor: false cattr_accessor :showplan_option, instance_accessor: false cattr_accessor :lowercase_schema_reflection self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS' self.use_output_inserted = true + self.exclude_output_inserted_table_names = Concurrent::Map.new { false } def initialize(connection, logger = nil, config = {}) super(connection, logger, config) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index aeeaff700..1b7b46635 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -140,12 +140,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) end it 'return false to #query_requires_identity_insert? for normal SQL' do diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb new file mode 100644 index 000000000..2c77546dc --- /dev/null +++ b/test/cases/trigger_test_sqlserver.rb @@ -0,0 +1,30 @@ +# encoding: UTF-8 +require 'cases/helper_sqlserver' + +class SQLServerTriggerTest < ActiveRecord::TestCase + after { exclude_output_inserted_table_names.clear } + + let(:exclude_output_inserted_table_names) do + ActiveRecord::ConnectionAdapters::SQLServerAdapter.exclude_output_inserted_table_names + end + + it 'can insert into a table with output inserted - with a true setting for table name' do + exclude_output_inserted_table_names['sst_table_with_trigger'] = true + assert SSTestTriggerHistory.all.empty? + obj = SSTestTrigger.create! event_name: 'test trigger' + ['Fixnum', 'Integer'].must_include obj.id.class.name + obj.event_name.must_equal 'test trigger' + obj.id.must_be :present? + obj.id.to_s.must_equal SSTestTriggerHistory.first.id_source + end + + it 'can insert into a table with output inserted - with a uniqueidentifier value' do + exclude_output_inserted_table_names['sst_table_with_uuid_trigger'] = 'uniqueidentifier' + assert SSTestTriggerHistory.all.empty? + obj = SSTestTriggerUuid.create! event_name: 'test uuid trigger' + obj.id.class.name.must_equal 'String' + obj.event_name.must_equal 'test uuid trigger' + obj.id.must_be :present? + obj.id.to_s.must_equal SSTestTriggerHistory.first.id_source + end +end diff --git a/test/models/sqlserver/trigger.rb b/test/models/sqlserver/trigger.rb new file mode 100644 index 000000000..cf1c45f7c --- /dev/null +++ b/test/models/sqlserver/trigger.rb @@ -0,0 +1,7 @@ +class SSTestTrigger < ActiveRecord::Base + self.table_name = 'sst_table_with_trigger' +end + +class SSTestTriggerUuid < ActiveRecord::Base + self.table_name = 'sst_table_with_uuid_trigger' +end diff --git a/test/models/sqlserver/trigger_history.rb b/test/models/sqlserver/trigger_history.rb new file mode 100644 index 000000000..35fd98016 --- /dev/null +++ b/test/models/sqlserver/trigger_history.rb @@ -0,0 +1,3 @@ +class SSTestTriggerHistory < ActiveRecord::Base + self.table_name = 'sst_table_with_trigger_history' +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 98df36468..52329387f 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -177,6 +177,44 @@ FROM sst_string_defaults STRINGDEFAULTSBIGVIEW + # Trigger + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_trigger') DROP TABLE sst_table_with_trigger" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_trigger_history') DROP TABLE sst_table_with_trigger_history" + execute <<-SQL + CREATE TABLE sst_table_with_trigger( + id bigint IDENTITY NOT NULL PRIMARY KEY, + event_name nvarchar(255) + ) + CREATE TABLE sst_table_with_trigger_history( + id bigint IDENTITY NOT NULL PRIMARY KEY, + id_source nvarchar(36), + event_name nvarchar(255) + ) + SQL + execute <<-SQL + CREATE TRIGGER sst_table_with_trigger_t ON sst_table_with_trigger + FOR INSERT + AS + INSERT INTO sst_table_with_trigger_history (id_source, event_name) + SELECT id AS id_source, event_name FROM INSERTED + SQL + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_uuid_trigger') DROP TABLE sst_table_with_uuid_trigger" + execute <<-SQL + CREATE TABLE sst_table_with_uuid_trigger( + id uniqueidentifier DEFAULT NEWID() PRIMARY KEY, + event_name nvarchar(255) + ) + SQL + execute <<-SQL + CREATE TRIGGER sst_table_with_uuid_trigger_t ON sst_table_with_uuid_trigger + FOR INSERT + AS + INSERT INTO sst_table_with_trigger_history (id_source, event_name) + SELECT id AS id_source, event_name FROM INSERTED + SQL + # Another schema. create_table :sst_schema_columns, force: true do |t| From a02faa59bd4316276a5994665e2a5e006887c362 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Oct 2017 20:40:07 -0400 Subject: [PATCH 0686/1412] Prepare for 5.1.3 [CI SKIP] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 61fcc8735..cdb98d26e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.2 +5.1.3 From 99f1fafeaea2e4982510291025b9b559265534ee Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Sun, 1 Oct 2017 20:44:27 -0400 Subject: [PATCH 0687/1412] Prepare for v5.1.2 --- CHANGELOG.md | 10 ++-------- VERSION | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ed450d9a..764b2008f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,10 @@ -## v5.1.3 - -#### Fixed - -* Patched `Relation#build_count_subquery`. Fixes #613. -* Inserts to tables with triggers using default `OUTPUT INSERTED` style. Fixes #595. - - ## v5.1.2 #### Fixed * The `fast_string_to_time` method when zone local. Fixes #609 #614 #620 +* Patched `Relation#build_count_subquery`. Fixes #613. +* Inserts to tables with triggers using default `OUTPUT INSERTED` style. Fixes #595. ## v5.1.1 diff --git a/VERSION b/VERSION index cdb98d26e..61fcc8735 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.3 +5.1.2 From c100d3188739e7941fc0c1b3dea94abad24ddb94 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Oct 2017 21:40:09 -0400 Subject: [PATCH 0688/1412] Use bigint type in sqlserver_type when needed. Fixes #616 --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 28729e9df..f52611d0c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -247,7 +247,7 @@ def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) case value = attr.value_for_database when Numeric - 'int'.freeze + value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze else 'nvarchar(max)'.freeze end From 8742e793056d394eadb9ee338e52de440040602b Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Mon, 16 Oct 2017 21:42:54 -0400 Subject: [PATCH 0689/1412] Start change log for v5.1.3. --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 764b2008f..af593216f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.1.3 + +#### Fixed + +* Use bigint type in sqlserver_type when needed. Fixes #616 + + ## v5.1.2 #### Fixed diff --git a/VERSION b/VERSION index 61fcc8735..cdb98d26e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.2 +5.1.3 From 67aa5e5a63758f6f67ba2b75e3b5e7ce87392d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20Horv=C3=A1th?= Date: Tue, 12 Sep 2017 16:17:01 +0100 Subject: [PATCH 0690/1412] Add case insensitive comparison for better performance with CI collations --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f52611d0c..9df2422e8 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -84,7 +84,7 @@ def case_sensitive_comparison(table, attribute, column, value) end def can_perform_case_insensitive_comparison_for?(column) - column.type == :string + column.type == :string && (!column.collation || column.case_sensitive?) end private :can_perform_case_insensitive_comparison_for? From 0920a1b949d3bb261b02a34469896aa50ced1caf Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 10 Nov 2017 07:34:04 -0500 Subject: [PATCH 0691/1412] Delete RAILS5-TODO.md --- RAILS5-TODO.md | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 RAILS5-TODO.md diff --git a/RAILS5-TODO.md b/RAILS5-TODO.md deleted file mode 100644 index d0bf9dc4b..000000000 --- a/RAILS5-TODO.md +++ /dev/null @@ -1,25 +0,0 @@ - -## Rails v5.1 - -* BIGINT PK support. https://github.com/rails/rails/pull/26266 -* Raise `ActiveRecord::NotNullViolation` when a record cannot be inserted - or updated because it would violate a not null constraint. -* Raise `ActiveRecord::RangeError` when values that executed are out of range. -* Allow passing extra flags to `db:structure:load` and `db:structure:dump` - Introduces `ActiveRecord::Tasks::DatabaseTasks.structure_(load|dump)_flags` to customize the - eventual commands run against the database, e.g. mysqldump/pg_dump. -* Set `:time` as a timezone aware type and remove deprecation when - `config.active_record.time_zone_aware_types` is not explicitly set. -* Remove deprecated support to passing a column to `#quote`. -* `#tables` and `#table_exists?` return only tables and not views. - All the deprecations on those methods were removed. -* Remove deprecated `original_exception` argument in `ActiveRecord::StatementInvalid#initialize` - and `ActiveRecord::StatementInvalid#original_exception`. -* Remove deprecated tasks: `db:test:clone`, `db:test:clone_schema`, `db:test:clone_structure`. -* Make `table_name=` reset current statement cache, - so queries are not run against the previous table name. -* Deprecate using `#quoted_id` in quoting. -* Deprecate `supports_migrations?` on connection adapters. -* Dig moving `Column#sqlserver_options` to `sql_type_metadata` delegate. -* Should we do like PG and add `options[:collation]` before `#add_column_options!`? -* Translated exceptions: `SerializationFailure` and `RangeError`. From aefb17b96e08cf34b0d77eff37fe03c42fe1e4d7 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 24 Oct 2017 21:25:16 -0400 Subject: [PATCH 0692/1412] Start v5.1.4. --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af593216f..6da7509f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.1.4 + +#### Fixed + +* Add case insensitive comparison for better performance with CI collations. Fixes #624 + + ## v5.1.3 #### Fixed diff --git a/VERSION b/VERSION index cdb98d26e..76e9e619d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.3 +5.1.4 From 6fa8a24885d9c1631cdf4021c85bcc05c06bf8da Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 22 Nov 2017 11:32:18 -0500 Subject: [PATCH 0693/1412] Use TinyTDS v2.1 and SS 2017-GA. --- .travis.yml | 2 +- appveyor.yml | 2 +- circle.yml | 2 +- test/bin/setup.sh | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93420a71e..efc76819a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ services: - docker env: global: - - TINYTDS_VERSION=2.1.0.pre4 + - TINYTDS_VERSION=2.1.0 - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: diff --git a/appveyor.yml b/appveyor.yml index af4179a1c..a4db20e3b 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.1.0.pre4 + - SET TINYTDS_VERSION=2.1.0 clone_depth: 5 skip_tags: true matrix: diff --git a/circle.yml b/circle.yml index 9b4f1eb8a..d13053f64 100644 --- a/circle.yml +++ b/circle.yml @@ -6,7 +6,7 @@ general: machine: environment: PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.1.0.pre4 + TINYTDS_VERSION: 2.1.0 ACTIVERECORD_UNITTEST_HOST: localhost ACTIVERECORD_UNITTEST_DATASERVER: localhost services: diff --git a/test/bin/setup.sh b/test/bin/setup.sh index d2d19e67d..b312d0cf3 100755 --- a/test/bin/setup.sh +++ b/test/bin/setup.sh @@ -3,7 +3,7 @@ set -x set -e -tag=2.0 +tag=2017-GA docker pull metaskills/mssql-server-linux-rails:$tag From ddb3ac728dd30b8d852eba80bb1b8ea4484f7111 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Wed, 22 Nov 2017 16:04:55 -0500 Subject: [PATCH 0694/1412] [CI] Different timeout. --- test/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/config.yml b/test/config.yml index 4f4e2bf43..9b30ae6be 100644 --- a/test/config.yml +++ b/test/config.yml @@ -19,7 +19,7 @@ connections: dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> - timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : nil %> + timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : 10 %> arunit2: <<: *default_connection_info database: activerecord_unittest2 @@ -27,5 +27,5 @@ connections: dataserver: <%= ENV['ACTIVERECORD_UNITTEST_DATASERVER'] %> tds_version: <%= ENV['ACTIVERECORD_UNITTEST_TDSVERSION'] %> azure: <%= !ENV['ACTIVERECORD_UNITTEST_AZURE'].nil? %> - timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : nil %> + timeout: <%= ENV['ACTIVERECORD_UNITTEST_AZURE'].present? ? 20 : 10 %> From f5eee6996503d31edf1a6fde8aa205eab5dfce2d Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 29 Dec 2017 07:51:09 -0500 Subject: [PATCH 0695/1412] Memoize #version_year --- .../connection_adapters/sqlserver_adapter.rb | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 975317e72..6dbed485e 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -435,11 +435,14 @@ def initialize_dateformatter end def version_year - vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s - return 2016 if vstring =~ /vNext/ - /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i - rescue Exception => e - 2016 + return @version_year if defined?(@version_year) + @version_year = begin + vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s + return 2016 if vstring =~ /vNext/ + /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i + rescue Exception => e + 2016 + end end end From 1f870c5c654ad338ad297b7ed7b70c42ecc75f96 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 29 Dec 2017 10:20:27 -0500 Subject: [PATCH 0696/1412] [CI SKIP] Prepare for v5.1.5. --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da7509f8..40e0151bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.1.5 + +#### Fixed + +* Memoize `@@version` queries. Fixes #632 + + ## v5.1.4 #### Fixed diff --git a/VERSION b/VERSION index 76e9e619d..220d8e0a4 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.4 +5.1.5 From bc758171ed43c75c8ffa00afec3893b58dc264a7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 23 Jan 2018 11:30:09 +0000 Subject: [PATCH 0697/1412] Use the query lock hint when joining tables --- CHANGELOG.md | 4 +++ lib/arel/visitors/sqlserver.rb | 16 +++++++++-- .../pessimistic_locking_test_sqlserver.rb | 28 +++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 40e0151bc..f7d6b46be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## v5.1.5 +#### Added + +* Use lock hint when joining table in query. + #### Fixed * Memoize `@@version` queries. Fixes #632 diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 4352da53e..6f66ef507 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -95,17 +95,29 @@ def visit_Arel_Nodes_JoinSource o, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector end if o.right.any? - collector << " " if o.left + collector << SPACE if o.left collector = inject_join o.right, collector, ' ' end collector end + def visit_Arel_Nodes_InnerJoin o, collector + collector << "INNER JOIN " + collector = visit o.left, collector + collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true + if o.right + collector << SPACE + visit(o.right, collector) + else + collector + end + end + def visit_Arel_Nodes_OuterJoin o, collector collector << "LEFT OUTER JOIN " collector = visit o.left, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true - collector << " " + collector << SPACE visit o.right, collector end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 9dd789360..96649e087 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -52,6 +52,34 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end + describe 'joining tables' do + + it 'joined tables use updlock by default' do + assert_sql %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_sql %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_sql %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_sql %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 + + end + end describe 'For paginated finds' do From d0af37b23f3f31f3163c3e2fdaa194d4fd79607c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 23 Jan 2018 12:38:12 +0000 Subject: [PATCH 0698/1412] Fixed changelog --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f7d6b46be..ca28fdc9f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ -## v5.1.5 +## v5.1.6 #### Added * Use lock hint when joining table in query. + +## v5.1.5 + #### Fixed * Memoize `@@version` queries. Fixes #632 From ed8b1700e4404a529b59f55d6bd76397ee9df737 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Tue, 23 Jan 2018 12:57:04 -0500 Subject: [PATCH 0699/1412] Prepare for v5.1.6 [CI SKIP] --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 220d8e0a4..8710cfdff 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.5 +5.1.6 From b0f54488a4a9b56d9510be53f713bdb88275570b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20B=C3=BChmann?= Date: Thu, 22 Feb 2018 13:29:27 +0100 Subject: [PATCH 0700/1412] Don't disable referential integrity for the same table twice --- .../connection_adapters/sqlserver_adapter.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 5 +++++ test/schema/sqlserver_specific_schema.rb | 10 +++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6dbed485e..82f32a690 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -189,7 +189,7 @@ def reset! def tables_with_referential_integrity schemas_and_tables = select_rows <<-SQL.strip_heredoc - SELECT s.name, o.name + SELECT DISTINCT s.name, o.name FROM sys.foreign_keys i INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID INNER JOIN sys.schemas s ON o.schema_id = s.schema_id diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 1b7b46635..16cb96b97 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -231,6 +231,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end + it 'not disable referential integrity for the same table twice' do + tables = SSTestHasPk.connection.tables_with_referential_integrity + assert_equal tables.size, tables.uniq.size + end + end describe 'database statements' do diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 52329387f..00fef941e 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -144,12 +144,20 @@ # Constraints - create_table(:sst_has_fks, force: true) { |t| t.column(:fk_id, :bigint, null: false) } + create_table(:sst_has_fks, force: true) do |t| + t.column(:fk_id, :bigint, null: false) + t.column(:fk_id2, :bigint) + end + create_table(:sst_has_pks, force: true) { } execute <<-ADDFKSQL ALTER TABLE sst_has_fks ADD CONSTRAINT FK__sst_has_fks_id FOREIGN KEY ([fk_id]) + REFERENCES [sst_has_pks] ([id]), + + CONSTRAINT FK__sst_has_fks_id2 + FOREIGN KEY ([fk_id2]) REFERENCES [sst_has_pks] ([id]) ADDFKSQL From 5f1a18cd939d3eefde4e3ce53192317364247b06 Mon Sep 17 00:00:00 2001 From: Alexandr Panko Date: Mon, 26 Feb 2018 00:15:08 +0300 Subject: [PATCH 0701/1412] Primary key should be lowercase if schema forced to lowercase --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3800f4d22..4c5a179e9 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -93,7 +93,7 @@ def primary_keys_select(table_name) identifier = database_prefix_identifier(table_name) database = identifier.fully_qualified_database_quoted sql = %{ - SELECT KCU.COLUMN_NAME AS [name] + 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 From 9e2cc92fc96422d5661202235dce247ba7db47f0 Mon Sep 17 00:00:00 2001 From: "A.B" Date: Tue, 13 Mar 2018 08:30:51 -0400 Subject: [PATCH 0702/1412] added with (nolock) hint to information_schema.views call in schema_statements --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3800f4d22..c0bd72cee 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -495,7 +495,7 @@ def view_information(table_name) @view_information ||= {} @view_information[table_name] ||= begin identifier = SQLServer::Utils.extract_identifiers(table_name) - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA' + view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA' if view_info view_info = view_info.with_indifferent_access if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000 From ab137b8300070a0b08944567eb6127407641f56e Mon Sep 17 00:00:00 2001 From: Alex Baranov Date: Tue, 20 Mar 2018 07:30:34 -0500 Subject: [PATCH 0703/1412] added nolock hint to schema_statements --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c0bd72cee..2c7bf5e89 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -260,7 +260,7 @@ def data_source_sql(name = nil, type: nil) scope = quoted_scope name, type: type table_name = lowercase_schema_reflection_sql 'TABLE_NAME' sql = "SELECT #{table_name}" - sql << ' FROM INFORMATION_SCHEMA.TABLES' + sql << ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)' sql << ' WHERE TABLE_CATALOG = DB_NAME()' sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}" sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] From 0ca37c18a830d9f3f017ab681e8aa3d666f85925 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 24 May 2018 08:34:29 -0400 Subject: [PATCH 0704/1412] Version bump to 5.2.0 --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 8710cfdff..91ff57278 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.1.6 +5.2.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index c7d7463d6..b560cf30a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 5.1.0' + spec.add_dependency 'activerecord', '~> 5.2.0' spec.add_dependency 'tiny_tds' end From ea8da70824e1087a9d717d0913f3f3816cac4bea Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 15:39:44 -0400 Subject: [PATCH 0705/1412] Add Dockerfile for testing --- Dockerfile | 67 ++++++++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 25 +++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..3ec420e88 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +FROM ruby:2.6-rc + +RUN apt-get update && apt-get install -y \ + build-essential \ + imagemagick \ + libfontconfig1-dev \ + libssl-dev \ + libxml2-dev \ + libxslt-dev \ + pkg-config \ + less \ + wget \ + unzip + +# Node.js +RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \ + && apt-get install -y nodejs + +# yarn +# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -\ +# && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ +# && apt-get update \ +# && apt-get install -y yarn +# +# RUN wget -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ +# && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ +# && apt-get update -y \ +# && apt-get -y install google-chrome-stable \ +# && rm /etc/apt/sources.list.d/google-chrome.list \ +# && rm -rf /var/lib/apt/lists/* /var/cache/apt/* \ +# && google-chrome --version +# +# RUN wget --no-check-certificate https://chromedriver.storage.googleapis.com/2.38/chromedriver_linux64.zip \ +# && unzip chromedriver_linux64.zip \ +# && rm chromedriver_linux64.zip \ +# && mv -f chromedriver /usr/local/share/ \ +# && chmod +x /usr/local/share/chromedriver \ +# && ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver \ +# && chromedriver -v + + +# ADD ./package.json /wordsearch/package.json +# ADD ./yarn.lock /wordsearch/yarn.lock +# RUN yarn install + +RUN cd /tmp + +ADD ./test/bin /src/test/bin + +RUN /src/test/bin/install-openssl.sh +# RUN /src/test/bin/install-freetds.sh + +RUN wget http://www.freetds.org/files/stable/freetds-1.00.27.tar.gz +RUN tar -xzf freetds-1.00.27.tar.gz +RUN cd freetds-1.00.27 +RUN ls +RUN ./freetds-1.00.27/configure --prefix=/usr/local --with-tdsver=7.3 +RUN make install ./freetds-1.00.27 + + +# RUN mkdir /src +WORKDIR /src +ADD ./Gemfile /src/Gemfile +ADD ./activerecord-sqlserver-adapter.gemspec /src/activerecord-sqlserver-adapter.gemspec +ADD ./lib/active_record/connection_adapters/sqlserver/version.rb /src/lib/active_record/connection_adapters/sqlserver/version.rb +ADD ./VERSION /src/VERSION +RUN bundle install --jobs=7 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..b824589a1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,25 @@ +version: '3' +services: + rails: + build: + context: . + dockerfile: ./Dockerfile + command: rails s -p 3000 -b '0.0.0.0' + stdin_open: true + tty: true + ports: + - '3000:3000' + volumes: + - .:/src:cached + depends_on: + - db + privileged: true + environment: + - ACTIVERECORD_UNITTEST_HOST=db + db: + image: microsoft/mssql-server-linux + ports: + - '1433:1433' + environment: + - ACCEPT_EULA=Y + - SA_PASSWORD=Passw0rd! From 5bdbd77508de354ffe269a48ba66c26bfe24a15d Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 15:41:38 -0400 Subject: [PATCH 0706/1412] Fix require statements --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 + lib/arel_sqlserver.rb | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6dbed485e..e7beee144 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,6 +1,7 @@ require 'base64' require 'active_record' require 'arel_sqlserver' +require 'arel/visitors/sqlserver' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/calculations' diff --git a/lib/arel_sqlserver.rb b/lib/arel_sqlserver.rb index 3fa9d949b..d437be596 100644 --- a/lib/arel_sqlserver.rb +++ b/lib/arel_sqlserver.rb @@ -1,3 +1,2 @@ require 'arel' -require 'arel/visitors/bind_visitor' require 'arel/visitors/sqlserver' From 9a6f5b9bbf3020e761b837e7fa6ff0494b502d1c Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 15:44:20 -0400 Subject: [PATCH 0707/1412] Update JSON base class --- lib/active_record/connection_adapters/sqlserver/type/json.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/json.rb b/lib/active_record/connection_adapters/sqlserver/type/json.rb index 3e01977bc..403261b5b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/json.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/json.rb @@ -2,7 +2,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Type - class Json < ActiveRecord::Type::Internal::AbstractJson + class Json < ActiveRecord::Type::Json end end From 8978e0ea13e3957860f5cbe2e5ac036d22ee258d Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 15:45:48 -0400 Subject: [PATCH 0708/1412] Update IndexDefinition initialization --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3800f4d22..92eb02125 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -47,7 +47,7 @@ def indexes(table_name, name = nil) column.gsub! '(-)', '' if column.ends_with?('(-)') column end - indexes << IndexDefinition.new(table_name, name, unique, columns, nil, nil, where) + indexes << IndexDefinition.new(table_name, name, unique, columns, where: where) end end end From 3dec3c8449db9edcdfeb2c4ecb8921f7515ff20c Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 15:48:17 -0400 Subject: [PATCH 0709/1412] Check if Arel BindParam is passed --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9df2422e8..e35c6268c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -237,6 +237,10 @@ def sp_executesql(sql, name, binds, options = {}) def sp_executesql_types_and_parameters(binds) types, params = [], [] binds.each_with_index do |attr, index| + if attr.is_a?(Arel::Nodes::BindParam) + attr = attr.value + end + types << "@#{index} #{sp_executesql_sql_type(attr)}" params << sp_executesql_sql_param(attr) end From 8ab734d8fb3c851ca4a07fb1655e5d450e2dd10e Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Wed, 27 Jun 2018 17:16:13 -0400 Subject: [PATCH 0710/1412] Run fixture statements seperately so identity insert can be turned on properly --- .../sqlserver/database_statements.rb | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e35c6268c..fd3215872 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -90,6 +90,27 @@ def can_perform_case_insensitive_comparison_for?(column) # === SQLServer Specific ======================================== # + + def insert_fixtures_set(fixture_set, tables_to_delete = []) + fixture_inserts = fixture_set.map do |table_name, fixtures| + next if fixtures.empty? + + build_fixture_sql(fixtures, table_name) + end.compact + + table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup } + sql_statements = table_deletes + fixture_inserts + + disable_referential_integrity do + transaction(requires_new: true) do + sql_statements.each do |sql| + execute sql, "Fixtures Load" + yield if block_given? + end + end + end + end + def execute_procedure(proc_name, *variables) vars = if variables.any? && variables.first.is_a?(Hash) variables.first.map { |k, v| "@#{k} = #{quote(v)}" } From 9f4f3f5bd65c90c70ebb40459790868bb22444da Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 28 Jun 2018 00:38:05 -0400 Subject: [PATCH 0711/1412] Fix pessimistic locking tests --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 6f66ef507..e17726d23 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -14,7 +14,7 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) def visit_Arel_Nodes_BindParam o, collector - collector.add_bind(o) { |i| "@#{i-1}" } + collector.add_bind(o.value) { |i| "@#{i-1}" } end def visit_Arel_Nodes_Bin o, collector From fae4540cac0f332dd802f1d643228f63bdba704f Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 28 Jun 2018 10:20:40 -0400 Subject: [PATCH 0712/1412] Remove order from count subquery --- .../connection_adapters/sqlserver/core_ext/calculations.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index e6478df3e..bd4556ff1 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -18,6 +18,7 @@ def build_count_subquery(relation, column_name, distinct) end ] + relation = relation.unscope(:order) subquery = relation.arel.as(Arel.sql("subquery_for_count")) select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false) @@ -31,8 +32,8 @@ def build_count_subquery(relation, column_name, distinct) ActiveSupport.on_load(:active_record) do if ActiveRecord::VERSION::MAJOR == 5 && - ActiveRecord::VERSION::MINOR == 1 && - ActiveRecord::VERSION::TINY >= 4 + ActiveRecord::VERSION::MINOR == 2 && + ActiveRecord::VERSION::TINY >= 0 mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations ActiveRecord::Relation.prepend(mod) end From e5092df77559c0ffe4c197f724dc410a44a0d89a Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 28 Jun 2018 11:20:09 -0400 Subject: [PATCH 0713/1412] Don't know what these are for --- .../sqlserver/database_statements.rb | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fd3215872..3da39e6c7 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -77,7 +77,7 @@ def release_savepoint(name = current_savepoint_name) def case_sensitive_comparison(table, attribute, column, value) if column.collation && !column.case_sensitive? - table[attribute].eq(Arel::Nodes::Bin.new(Arel::Nodes::BindParam.new)) + table[attribute].eq(Arel::Nodes::Bin.new(value)) else super end @@ -270,7 +270,7 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) - case value = attr.value_for_database + case value = attr.try(:value_for_database) || attr.try(:value_before_type_cast) when Numeric value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze else @@ -279,12 +279,17 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - case attr.value_for_database + value = attr.try(:value_for_database) || attr.try(:value_before_type_cast) + if value.is_a?(ActiveRecord::StatementCache::Substitute) + return quote('') + end + + case value when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data - quote(attr.value_for_database) + quote(value) else - quote(type_cast(attr.value_for_database)) + quote(type_cast(value)) end end From 98d6c4e1e56ff19e17b7f64269a49eb7311a76a8 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 15:42:30 -0400 Subject: [PATCH 0714/1412] Remove dockerfiles since setup.sh sets everything up --- Dockerfile | 67 ---------------------------------------------- docker-compose.yml | 25 ----------------- 2 files changed, 92 deletions(-) delete mode 100644 Dockerfile delete mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 3ec420e88..000000000 --- a/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -FROM ruby:2.6-rc - -RUN apt-get update && apt-get install -y \ - build-essential \ - imagemagick \ - libfontconfig1-dev \ - libssl-dev \ - libxml2-dev \ - libxslt-dev \ - pkg-config \ - less \ - wget \ - unzip - -# Node.js -RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \ - && apt-get install -y nodejs - -# yarn -# RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -\ -# && echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ -# && apt-get update \ -# && apt-get install -y yarn -# -# RUN wget -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ -# && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list \ -# && apt-get update -y \ -# && apt-get -y install google-chrome-stable \ -# && rm /etc/apt/sources.list.d/google-chrome.list \ -# && rm -rf /var/lib/apt/lists/* /var/cache/apt/* \ -# && google-chrome --version -# -# RUN wget --no-check-certificate https://chromedriver.storage.googleapis.com/2.38/chromedriver_linux64.zip \ -# && unzip chromedriver_linux64.zip \ -# && rm chromedriver_linux64.zip \ -# && mv -f chromedriver /usr/local/share/ \ -# && chmod +x /usr/local/share/chromedriver \ -# && ln -s /usr/local/share/chromedriver /usr/local/bin/chromedriver \ -# && chromedriver -v - - -# ADD ./package.json /wordsearch/package.json -# ADD ./yarn.lock /wordsearch/yarn.lock -# RUN yarn install - -RUN cd /tmp - -ADD ./test/bin /src/test/bin - -RUN /src/test/bin/install-openssl.sh -# RUN /src/test/bin/install-freetds.sh - -RUN wget http://www.freetds.org/files/stable/freetds-1.00.27.tar.gz -RUN tar -xzf freetds-1.00.27.tar.gz -RUN cd freetds-1.00.27 -RUN ls -RUN ./freetds-1.00.27/configure --prefix=/usr/local --with-tdsver=7.3 -RUN make install ./freetds-1.00.27 - - -# RUN mkdir /src -WORKDIR /src -ADD ./Gemfile /src/Gemfile -ADD ./activerecord-sqlserver-adapter.gemspec /src/activerecord-sqlserver-adapter.gemspec -ADD ./lib/active_record/connection_adapters/sqlserver/version.rb /src/lib/active_record/connection_adapters/sqlserver/version.rb -ADD ./VERSION /src/VERSION -RUN bundle install --jobs=7 diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index b824589a1..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: '3' -services: - rails: - build: - context: . - dockerfile: ./Dockerfile - command: rails s -p 3000 -b '0.0.0.0' - stdin_open: true - tty: true - ports: - - '3000:3000' - volumes: - - .:/src:cached - depends_on: - - db - privileged: true - environment: - - ACTIVERECORD_UNITTEST_HOST=db - db: - image: microsoft/mssql-server-linux - ports: - - '1433:1433' - environment: - - ACCEPT_EULA=Y - - SA_PASSWORD=Passw0rd! From 876576f627823a263c28826c8767465a0f57405d Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 15:58:06 -0400 Subject: [PATCH 0715/1412] Use new migration context to run migration --- test/cases/migration_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index d880222c9..73bf98399 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -20,7 +20,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it 'not create a tables if error in migrations' do begin migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table' - quietly { ActiveRecord::Migrator.up(migrations_dir) } + quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message end From 954324f841c581ac1cb6f0c240d2a7152b7e1f2d Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 17:14:29 -0400 Subject: [PATCH 0716/1412] Coerce tests, but still failing for some reason --- test/cases/coerced_tests.rb | 83 +++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7b7bf5074..1dc9677f3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -33,6 +33,7 @@ module ActiveRecord class AdapterTest < ActiveRecord::TestCase # I really dont think we can support legacy binds. coerce_tests! :test_select_all_with_legacy_binds + coerce_tests! :test_insert_update_delete_with_legacy_binds # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement @@ -913,3 +914,85 @@ def schema_dump_path end end +class UnsafeRawSqlTest < ActiveRecord::TestCase + coerce_tests! %r{always allows Arel} + test 'order: always allows Arel' do + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) } + + assert_equal ids_depr, ids_disabled + end + + test "pluck: always allows Arel" do + values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } + values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } + + assert_equal values_depr, values_disabled + end + + + coerce_tests! %r{order: disallows invalid Array arguments} + test "order: disallows invalid Array arguments" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.order(["author_id", "len(title)"]).pluck(:id) + end + end + end + + coerce_tests! %r{order: allows valid Array arguments} + test "order: allows valid Array arguments" do + ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + coerce_tests! %r{order: logs deprecation warning for unrecognized column} + test "order: logs deprecation warning for unrecognized column" do + with_unsafe_raw_sql_deprecated do + assert_deprecated(/Dangerous query method/) do + Post.order("len(title)") + end + end + end + + coerce_tests! %r{pluck: disallows invalid column name} + test "pluck: disallows invalid column name" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.pluck("len(title)") + end + end + end + + coerce_tests! %r{pluck: disallows invalid column name amongst valid names} + test "pluck: disallows invalid column name amongst valid names" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.pluck(:title, "len(title)") + end + end + end + + coerce_tests! %r{pluck: disallows invalid column names with includes} + test "pluck: disallows invalid column names with includes" do + with_unsafe_raw_sql_disabled do + assert_raises(ActiveRecord::UnknownAttributeReference) do + Post.includes(:comments).pluck(:title, "len(title)") + end + end + end + + coerce_tests! %r{pluck: logs deprecation warning} + test "pluck: logs deprecation warning" do + with_unsafe_raw_sql_deprecated do + assert_deprecated(/Dangerous query method/) do + Post.includes(:comments).pluck(:title, "len(title)") + end + end + end +end From eb968f4479f83abb47e2a1427f9d22adf337a7fd Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 18:05:58 -0400 Subject: [PATCH 0717/1412] Enable insert of primary key --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3da39e6c7..71eb18bea 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -335,7 +335,7 @@ def exclude_output_inserted_table_name?(table_name, sql) end def exec_insert_requires_identity?(sql, pk, binds) - query_requires_identity_insert?(sql) if pk && binds.map(&:name).include?(pk) + query_requires_identity_insert?(sql) end def query_requires_identity_insert?(sql) From 445837c22ed2ce24462a548e23ff0d8b59cac7fb Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 19:29:48 -0400 Subject: [PATCH 0718/1412] Enable ordering count subquery --- .../connection_adapters/sqlserver/core_ext/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index bd4556ff1..e984d918f 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -18,7 +18,7 @@ def build_count_subquery(relation, column_name, distinct) end ] - relation = relation.unscope(:order) + relation = relation.offset(0) subquery = relation.arel.as(Arel.sql("subquery_for_count")) select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false) From 551cedd84f2c4d1154e24d1457a59d68abe805ee Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 6 Jul 2018 19:30:01 -0400 Subject: [PATCH 0719/1412] Rename coerced tests who's name changed --- test/cases/coerced_tests.rb | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1dc9677f3..0d1394509 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -72,7 +72,11 @@ def test_typecast_attribute_from_select_to_true_coerced end - +class NumericDataTest < ActiveRecord::TestCase + # We do not have do the DecimalWithoutScale type. + coerce_tests! :test_numeric_fields + coerce_tests! :test_numeric_fields_with_scale +end class BasicsTest < ActiveRecord::TestCase coerce_tests! :test_column_names_are_escaped @@ -81,9 +85,7 @@ def test_column_names_are_escaped_coerced assert_equal '[t]]]', conn.quote_column_name('t]') end - # We do not have do the DecimalWithoutScale type. - coerce_tests! :test_numeric_fields - coerce_tests! :test_numeric_fields_with_scale + # Just like PostgreSQLAdapter does. coerce_tests! :test_respect_internal_encoding @@ -314,7 +316,7 @@ def test_add_table_with_decimals_coerced end # For some reason our tests set Rails.@_env which breaks test env switching. - coerce_tests! :test_migration_sets_internal_metadata_even_when_fully_migrated + coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists coerce_tests! :test_internal_metadata_stores_environment end @@ -643,7 +645,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase require 'models/task' class QueryCacheTest < ActiveRecord::TestCase - coerce_tests! :test_cache_does_not_wrap_string_results_in_arrays + coerce_tests! :test_cache_does_not_wrap_results_in_arrays def test_cache_does_not_wrap_string_results_in_arrays_coerced Task.cache do assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") From 01ae4cf3b748512032d1d39bdde6e1a04ffd7565 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Mon, 9 Jul 2018 12:35:56 -0400 Subject: [PATCH 0720/1412] Remove monkey patch conditional as its needed in every supported version --- .../sqlserver/core_ext/calculations.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index e984d918f..7a3ab6760 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -31,10 +31,6 @@ def build_count_subquery(relation, column_name, distinct) end ActiveSupport.on_load(:active_record) do - if ActiveRecord::VERSION::MAJOR == 5 && - ActiveRecord::VERSION::MINOR == 2 && - ActiveRecord::VERSION::TINY >= 0 - mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations - ActiveRecord::Relation.prepend(mod) - end + mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations + ActiveRecord::Relation.prepend(mod) end From 434182d8bf3470ef8d13297871e2046d88296933 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Mon, 9 Jul 2018 12:37:55 -0400 Subject: [PATCH 0721/1412] Update mocha minitest require --- test/cases/helper_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 74e364d8f..607d5a5f7 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -9,7 +9,7 @@ require 'support/coerceable_test_sqlserver' require 'support/sql_counter_sqlserver' require 'support/connection_reflection' -require 'mocha/mini_test' +require 'mocha/minitest' module ActiveRecord class TestCase < ActiveSupport::TestCase From cad7c044054357b5ab59b4460d51f97b09a3ffc6 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Mon, 9 Jul 2018 13:01:50 -0400 Subject: [PATCH 0722/1412] Remove un-necessary adapter override --- .../connection_adapters/sqlserver/database_statements.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 71eb18bea..e2e22dad0 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -33,10 +33,6 @@ def exec_update(sql, name, binds) super(sql, name, binds).rows.first.first end - def supports_statement_cache? - true - end - def begin_db_transaction do_execute 'BEGIN TRANSACTION' end From cc86d04cfba70bf2a7ece34854672b6a8f1d9df5 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Mon, 9 Jul 2018 13:09:53 -0400 Subject: [PATCH 0723/1412] Remove special StatementCache Substitute case --- .../connection_adapters/sqlserver/database_statements.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e2e22dad0..753883cbe 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -276,9 +276,6 @@ def sp_executesql_sql_type(attr) def sp_executesql_sql_param(attr) value = attr.try(:value_for_database) || attr.try(:value_before_type_cast) - if value.is_a?(ActiveRecord::StatementCache::Substitute) - return quote('') - end case value when Type::Binary::Data, From 111d8dfd5e65671caefaec113af09429470676ba Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 10 Jul 2018 16:27:35 +0100 Subject: [PATCH 0724/1412] Fix SchemaDumper usage --- .../connection_adapters/sqlserver/schema_dumper.rb | 4 ++-- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++++ lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 - lib/active_record/tasks/sqlserver_database_tasks.rb | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 708238d42..d801d1d86 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -1,7 +1,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer - module SchemaDumper + class SchemaDumper < ConnectionAdapters::SchemaDumper SQLSEVER_NO_LIMIT_TYPES = [ 'text', @@ -24,7 +24,7 @@ def schema_limit(column) def schema_collation(column) return unless column.collation - column.collation if column.collation != collation + column.collation if column.collation != @connection.collation end def default_primary_key?(column) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3800f4d22..4152ac582 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -254,6 +254,10 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute sql end + def create_schema_dumper(options) + SQLServer::SchemaDumper.create(self, options) + end + private def data_source_sql(name = nil, type: nil) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6dbed485e..42eebd68e 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -34,7 +34,6 @@ class SQLServerAdapter < AbstractAdapter SQLServer::Quoting, SQLServer::DatabaseStatements, SQLServer::Showplan, - SQLServer::SchemaDumper, SQLServer::SchemaStatements, SQLServer::DatabaseLimits, SQLServer::DatabaseTasks diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 0c62d58e4..23d499f9b 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -51,7 +51,7 @@ def purge def structure_dump(filename, extra_flags) command = [ "defncopy-ttds", - "-S #{Shellwords.escape(configuration['host'])}", + "-S #{Shellwords.escape(configuration['host'])}:#{Shellwords.escape(configuration['port'])}", "-D #{Shellwords.escape(configuration['database'])}", "-U #{Shellwords.escape(configuration['username'])}", "-P #{Shellwords.escape(configuration['password'])}", From 25af4ec3b531dfe16d789d67addb8be9027166e6 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Jul 2018 13:50:01 +0100 Subject: [PATCH 0725/1412] Fix QueryCache tests --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- test/cases/coerced_tests.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index e7beee144..86b13be4c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -254,7 +254,7 @@ def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], # === Abstract Adapter (Misc Support) =========================== # - def initialize_type_map(m) + def initialize_type_map(m = type_map) m.register_type %r{.*}, SQLServer::Type::UnicodeString.new # Exact Numerics register_class_with_limit m, 'bigint(8)', SQLServer::Type::BigInteger diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0d1394509..9ef26fb75 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -646,7 +646,7 @@ class PrimaryKeysTest < ActiveRecord::TestCase require 'models/task' class QueryCacheTest < ActiveRecord::TestCase coerce_tests! :test_cache_does_not_wrap_results_in_arrays - def test_cache_does_not_wrap_string_results_in_arrays_coerced + def test_cache_does_not_wrap_results_in_arrays_coerced Task.cache do assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end From 1d57a1c9b20e1c850259da349642a095de6721c8 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Jul 2018 14:18:26 +0100 Subject: [PATCH 0726/1412] Coerce ConnectionHandler tests --- test/cases/coerced_tests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0d1394509..ee3e9359d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -342,6 +342,7 @@ module ConnectionAdapters # a value of 'default_env' will still show tests failing. Just ignoring all # of them since we have no monkey in this circus. MergeAndResolveDefaultUrlConfigTest.coerce_all_tests! if defined?(MergeAndResolveDefaultUrlConfigTest) + ConnectionHandlerTest.coerce_all_tests! if defined?(ConnectionHandlerTest) end end From 5a52d49ae3f2e1cdf8b8b4f360b0de94975246c2 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 12 Jul 2018 12:08:35 +0100 Subject: [PATCH 0727/1412] Fix SerializedAttribute tests --- .../sqlserver/database_statements.rb | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 753883cbe..8323da816 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -254,9 +254,7 @@ def sp_executesql(sql, name, binds, options = {}) def sp_executesql_types_and_parameters(binds) types, params = [], [] binds.each_with_index do |attr, index| - if attr.is_a?(Arel::Nodes::BindParam) - attr = attr.value - end + attr = attr.value if attr.is_a?(Arel::Nodes::BindParam) types << "@#{index} #{sp_executesql_sql_type(attr)}" params << sp_executesql_sql_param(attr) @@ -266,7 +264,7 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) - case value = attr.try(:value_for_database) || attr.try(:value_before_type_cast) + case value = attr.value_for_database when Numeric value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze else @@ -275,9 +273,7 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - value = attr.try(:value_for_database) || attr.try(:value_before_type_cast) - - case value + case value = attr.value_for_database when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data quote(value) From 372bc7899ece96aee01ca82ea553064834a2e1a4 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 12 Jul 2018 14:35:17 +0100 Subject: [PATCH 0728/1412] Fix ReservedWord tests --- test/cases/coerced_tests.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0d1394509..335223573 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -998,3 +998,14 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end end end + + +class ReservedWordTest < ActiveRecord::TestCase + coerce_tests! :test_change_columns + def test_change_columns_coerced + assert_nothing_raised { @connection.change_column_default(:group, :order, "whatever") } + assert_nothing_raised { @connection.change_column("group", "order", :text) } + assert_nothing_raised { @connection.change_column_null("group", "order", true) } + assert_nothing_raised { @connection.rename_column(:group, :order, :values) } + end +end From 17db67c3ac563ee79746770f7691cf347b72f902 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 12 Jul 2018 16:53:01 +0100 Subject: [PATCH 0729/1412] Relax minitest version --- Gemfile | 1 - 1 file changed, 1 deletion(-) diff --git a/Gemfile b/Gemfile index 9a5d6af1f..75a716cfd 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,6 @@ source 'https://rubygems.org' gemspec gem 'sqlite3' -gem 'minitest', '< 5.3.4' gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] From 359a90c729a151e5982417f1b9986c332b4679e9 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 13 Jul 2018 13:59:26 +0100 Subject: [PATCH 0730/1412] Fix SchemaDumper tests --- .../sqlserver/schema_statements.rb | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 92eb02125..e0941f86e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -32,22 +32,33 @@ def drop_table(table_name, options = {}) end end - def indexes(table_name, name = nil) - data = select("EXEC sp_helpindex #{quote(table_name)}", name) rescue [] + def indexes(table_name) + data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue [] + data.reduce([]) do |indexes, index| index = index.with_indifferent_access + if index[:index_description] =~ /primary key/ indexes else name = index[:index_name] unique = index[:index_description] =~ /unique/ where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}") - columns = index[:index_keys].split(',').map do |column| + orders = {} + columns = [] + + index[:index_keys].split(',').each do |column| column.strip! - column.gsub! '(-)', '' if column.ends_with?('(-)') - column + + if column.ends_with?('(-)') + column.gsub! '(-)', '' + orders[column] = :desc + end + + columns << column end - indexes << IndexDefinition.new(table_name, name, unique, columns, where: where) + + indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders) end end end From b03eb977c6186faa0e8446f149b442feb05628e7 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 16 Jul 2018 11:44:50 +0100 Subject: [PATCH 0731/1412] Fix Fixtures tests --- .../sqlserver/database_statements.rb | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 8323da816..e4e866812 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -84,28 +84,22 @@ def can_perform_case_insensitive_comparison_for?(column) end private :can_perform_case_insensitive_comparison_for? - # === SQLServer Specific ======================================== # - - - def insert_fixtures_set(fixture_set, tables_to_delete = []) - fixture_inserts = fixture_set.map do |table_name, fixtures| - next if fixtures.empty? - - build_fixture_sql(fixtures, table_name) - end.compact - - table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup } - sql_statements = table_deletes + fixture_inserts + def combine_multi_statements(total_sql) + total_sql + end + private :combine_multi_statements - disable_referential_integrity do - transaction(requires_new: true) do - sql_statements.each do |sql| - execute sql, "Fixtures Load" - yield if block_given? - end - end + def default_insert_value(column) + if column.is_identity? + table_name = quote(quote_table_name(column.table_name)) + Arel.sql("IDENT_CURRENT(#{table_name}) + IDENT_INCR(#{table_name})") + else + super end end + private :default_insert_value + + # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) vars = if variables.any? && variables.first.is_a?(Hash) From c91af083e68179ffefd776bbfed83fb199f6e226 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Jul 2018 13:27:49 +0100 Subject: [PATCH 0732/1412] Fix Calculations tests --- .../sqlserver/core_ext/calculations.rb | 15 +---------- test/cases/coerced_tests.rb | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 7a3ab6760..61b5057df 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -9,20 +9,7 @@ module Calculations private def build_count_subquery(relation, column_name, distinct) - relation.select_values = [ - if column_name == :all - distinct ? table[Arel.star] : Arel.sql(FinderMethods::ONE_AS_ONE) - else - column_alias = Arel.sql("count_column") - aggregate_column(column_name).as(column_alias) - end - ] - - relation = relation.offset(0) - subquery = relation.arel.as(Arel.sql("subquery_for_count")) - select_value = operation_over_aggregate_column(column_alias || Arel.star, "count", false) - - Arel::SelectManager.new(subquery).project(select_value) + super(relation.unscope(:order), column_name, distinct) end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 225442797..c2fcd6100 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -176,6 +176,14 @@ def test_limit_with_offset_is_kept_coerced queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} end + # SQL Server needs an alias for the calculated column + coerce_tests! :test_distinct_count_all_with_custom_select_and_order + def test_distinct_count_all_with_custom_select_and_order_coerced + accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10")) + assert_queries(1) { assert_equal 3, accounts.count(:all) } + assert_queries(1) { assert_equal 3, accounts.load.size } + end + # Leave it up to users to format selects/functions so HAVING works correctly. coerce_tests! :test_having_with_strong_parameters end @@ -189,6 +197,7 @@ class ChangeSchemaTest < ActiveRecord::TestCase coerce_tests! :test_create_table_with_bigint, :test_create_table_with_defaults end + class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase # In SQL Server you have to delete the tables yourself in the right order. coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects @@ -701,6 +710,22 @@ def test_relations_dont_load_all_records_in_inspect_coerced # so we are skipping all together. coerce_tests! :test_empty_complex_chained_relations + # Can't apply offset withour ORDER + coerce_tests! %r{using a custom table affects the wheres} + test 'using a custom table affects the wheres coerced' do + post = posts(:welcome) + + assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take + end + + # Can't apply offset withour ORDER + coerce_tests! %r{using a custom table with joins affects the joins} + test 'using a custom table with joins affects the joins coerced' do + post = posts(:welcome) + + assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take + end + # Use LEN() vs length() function. coerce_tests! :test_reverse_arel_assoc_order_with_function def test_reverse_arel_assoc_order_with_function_coerced From 72416d27090b617c474ab0cddc3db3e7af379381 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Mon, 16 Jul 2018 09:02:52 -0400 Subject: [PATCH 0733/1412] Don't pass unsafe SQL to .order --- test/cases/coerced_tests.rb | 6 ++--- test/cases/order_test_sqlserver.rb | 38 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c2fcd6100..c6eba4aeb 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -671,16 +671,16 @@ class RelationTest < ActiveRecord::TestCase # Use LEN vs LENGTH function. coerce_tests! :test_reverse_order_with_function def test_reverse_order_with_function_coerced - topics = Topic.order("LEN(title)").reverse_order + topics = Topic.order(Arel.sql("LEN(title)")).reverse_order assert_equal topics(:second).title, topics.first.title end # Use LEN vs LENGTH function. coerce_tests! :test_reverse_order_with_function_other_predicates def test_reverse_order_with_function_other_predicates_coerced - topics = Topic.order("author_name, LEN(title), id").reverse_order + topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order assert_equal topics(:second).title, topics.first.title - topics = Topic.order("LEN(author_name), id, LEN(title)").reverse_order + topics = Topic.order(Arel.sql("LEN(author_name), id, LEN(title)")).reverse_order assert_equal topics(:fifth).title, topics.first.title end diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index 839a4e1a9..d0707eede 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -44,13 +44,13 @@ class OrderTestSQLServer < ActiveRecord::TestCase it 'support quoted column' do order = "[title]" post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first + assert_equal post1, Post.order(Arel.sql(order)).first end it 'support quoted table and column' do order = "[posts].[title]" post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first + assert_equal post1, Post.order(Arel.sql(order)).first end it 'support primary: column, secondary: column' do @@ -73,74 +73,74 @@ class OrderTestSQLServer < ActiveRecord::TestCase order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body" post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support primary: quoted table and column, secondary: case expresion' do order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC" post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support inline function' do order = "LEN(title)" post1 = Post.create title: 'A', body: 'AAA Test cased orders.' - assert_equal post1, Post.order(order).first + assert_equal post1, Post.order(Arel.sql(order)).first end it 'support inline function with parameters' do order = "SUBSTRING(title, 1, 3)" post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first + assert_equal post1, Post.order(Arel.sql(order)).first end it 'support inline function with parameters DESC' do order = "SUBSTRING(title, 1, 3) DESC" post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first + assert_equal post1, Post.order(Arel.sql(order)).first end it 'support primary: inline function, secondary: column' do order = "LEN(title), body" post1 = Post.create title: 'A', body: 'AAA Test cased orders.' post2 = Post.create title: 'A', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support primary: inline function, secondary: column with direction' do order = "LEN(title) ASC, body DESC" post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.' post2 = Post.create title: 'A', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support primary: column, secondary: inline function' do order = "body DESC, LEN(title)" post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.' post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support primary: case expression, secondary: inline function' do order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC" post1 = Post.create title: 'ZZZ Post', body: 'Z' post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end it 'support primary: inline function, secondary: case expression' do order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC" post1 = Post.create title: 'ZZZ Post', body: 'Z' post2 = Post.create title: 'Post', body: 'Z' - assert_equal post1, Post.order(order).first - assert_equal post2, Post.order(order).second + assert_equal post1, Post.order(Arel.sql(order)).first + assert_equal post2, Post.order(Arel.sql(order)).second end From 237a0a884cf34d976d47a721b6847cd43f300f04 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 16 Jul 2018 16:43:57 +0100 Subject: [PATCH 0734/1412] Fix Migration tests --- .../connection_adapters/sqlserver/schema_creation.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 382216573..31875a13b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -8,7 +8,8 @@ class SchemaCreation < AbstractAdapter::SchemaCreation def visit_TableDefinition(o) if o.as table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name) - projections, source = @conn.to_sql(o.as).match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures + query = o.as.respond_to?(:to_sql) ? o.as.to_sql : o.as + projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}" else o.instance_variable_set :@as, nil From 2cf7426e12f34ddb0fe3f07312ef98c3bb7484b8 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 16 Jul 2018 17:43:46 +0100 Subject: [PATCH 0735/1412] Fix OptimisticLocking tests --- test/cases/coerced_tests.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c6eba4aeb..7952bf4fc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1035,3 +1035,26 @@ def test_change_columns_coerced assert_nothing_raised { @connection.rename_column(:group, :order, :values) } end end + + + +class OptimisticLockingTest < ActiveRecord::TestCase + # We do not allow updating identities, but we can test using a non-identity key + coerce_tests! :test_update_with_dirty_primary_key + def test_update_with_dirty_primary_key_coerced + assert_raises(ActiveRecord::RecordNotUnique) do + record = StringKeyObject.find('record1') + record.id = 'record2' + record.save! + end + + record = StringKeyObject.find('record1') + record.id = 'record42' + record.save! + + assert StringKeyObject.find('record42') + assert_raises(ActiveRecord::RecordNotFound) do + StringKeyObject.find('record1') + end + end +end From 23aa0a4fe304041cd40bf66f4cf28f9d5bb39280 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 16 Jul 2018 17:55:32 +0100 Subject: [PATCH 0736/1412] Fix Basic tests --- test/cases/coerced_tests.rb | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c6eba4aeb..190e7f51b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -85,8 +85,6 @@ def test_column_names_are_escaped_coerced assert_equal '[t]]]', conn.quote_column_name('t]') end - - # Just like PostgreSQLAdapter does. coerce_tests! :test_respect_internal_encoding @@ -115,6 +113,16 @@ def test_update_date_time_attributes_with_default_timezone_local end end end + + # Need to escape `quoted_id` once it contains brackets + coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns} + test "column names are quoted when using #from clause and model has ignored columns coerced" do + refute_empty Developer.ignored_columns + query = Developer.from("developers").to_sql + quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}" + + assert_match(/SELECT #{Regexp.escape(quoted_id)}.* FROM developers/, query) + end end From 6e82dc8b1531c314605f5f8ee13ff22c86c28509 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 16 Jul 2018 18:01:17 +0100 Subject: [PATCH 0737/1412] Update ruby versions to be tested --- .travis.yml | 7 ++++--- circle.yml | 14 ++++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index efc76819a..e8ec0ad6c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,9 +8,10 @@ env: - ACTIVERECORD_UNITTEST_HOST=localhost - ACTIVERECORD_UNITTEST_DATASERVER=localhost rvm: - - 2.2.5 - - 2.3.1 - - 2.4.0 + - 2.2.10 + - 2.3.7 + - 2.4.4 + - 2.5.1 before_install: - export PATH=/opt/local/bin:$PATH - docker info diff --git a/circle.yml b/circle.yml index d13053f64..d62c92952 100644 --- a/circle.yml +++ b/circle.yml @@ -18,9 +18,10 @@ dependencies: - openssl version - sudo ./test/bin/install-freetds.sh - tsql -C - - rvm-exec 2.2.5 bundle install - - rvm-exec 2.3.1 bundle install - - rvm-exec 2.4.0 bundle install + - rvm-exec 2.2.10 bundle install + - rvm-exec 2.3.7 bundle install + - rvm-exec 2.4.4 bundle install + - rvm-exec 2.5.1 bundle install database: override: @@ -31,6 +32,7 @@ database: test: override: - - rvm-exec 2.2.5 bundle exec rake test - - rvm-exec 2.3.1 bundle exec rake test - - rvm-exec 2.4.0 bundle exec rake test + - rvm-exec 2.2.10 bundle exec rake test + - rvm-exec 2.3.7 bundle exec rake test + - rvm-exec 2.4.4 bundle exec rake test + - rvm-exec 2.5.1 bundle exec rake test From 67317b316506388d17ec51e56528430323c3f822 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 17 Jul 2018 12:10:11 +0100 Subject: [PATCH 0738/1412] Fix DateTime tests --- .../sqlserver/type/time_value_fractional.rb | 10 +++++----- test/cases/coerced_tests.rb | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) 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 412958795..38862473d 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 @@ -14,15 +14,15 @@ def apply_seconds_precision(value) def seconds_precision(value) return 0 if fractional_scale == 0 - seconds = value.send(fractional_property).to_f / fractional_operator.to_f - seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) + seconds = value.send(fractional_property).to_d / fractional_operator.to_d + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).truncate(fractional_scale) (seconds * fractional_operator).round(0).to_i end def quote_fractional(value) return 0 if fractional_scale == 0 frac_seconds = seconds_precision(value) - seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) + seconds = (frac_seconds.to_d / fractional_operator.to_d).round(fractional_scale) seconds.to_d.to_s.split('.').last.to(fractional_scale-1) end @@ -39,7 +39,7 @@ def fractional_operator end def fractional_precision - 0.00333 + 0.00333.to_d end def fractional_scale @@ -68,7 +68,7 @@ def fractional_digits end def fractional_precision - 0.0000001 + 0.0000001.to_d end def fractional_scale diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bd2dbcd84..7cc094d28 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -223,14 +223,14 @@ class QuoteARBaseTest < ActiveRecord::TestCase coerce_tests! :test_quote_ar_object def test_quote_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) + assert_equal "'02-14-2017 12:34:56.789'", @connection.quote(value) end # Use our date format. coerce_tests! :test_type_cast_ar_object def test_type_cast_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) + assert_equal "02-14-2017 12:34:56.789", @connection.type_cast(value) end end From dd35b0693e1cdd5ea3e94a7895ba5d728238b017 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 17 Jul 2018 14:51:20 +0100 Subject: [PATCH 0739/1412] Fix eager loading tests --- .../connection_adapters/sqlserver/schema_statements.rb | 3 ++- test/cases/coerced_tests.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 68b6b03c2..a1940088e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -246,7 +246,8 @@ def columns_for_distinct(columns, orders) s.gsub(/\s+(?:ASC|DESC)\b/i, '') .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '') }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } - [super, *order_columns].join(', ') + + (order_columns << super).join(", ") end def update_table_definition(table_name, base) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7cc094d28..db7aff98f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -534,7 +534,7 @@ def test_a_bad_type_column_coerced coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced con = Account.connection - assert_sql(/\[companies\]\.\[id\] = 1/) do + assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do Account.all.merge!(:includes => :firm).find(1) end end From 02e9f6f63c1d79ccb9c153bc51d2603676884f34 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 19 Jul 2018 18:04:41 -0400 Subject: [PATCH 0740/1412] Fix structure dump tests --- lib/active_record/tasks/sqlserver_database_tasks.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 23d499f9b..ae5a6f3df 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -49,9 +49,11 @@ def purge end def structure_dump(filename, extra_flags) + server_arg = "-S #{Shellwords.escape(configuration['host'])}" + server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration['port'] command = [ "defncopy-ttds", - "-S #{Shellwords.escape(configuration['host'])}:#{Shellwords.escape(configuration['port'])}", + host_parameter, "-D #{Shellwords.escape(configuration['database'])}", "-U #{Shellwords.escape(configuration['username'])}", "-P #{Shellwords.escape(configuration['password'])}", From 4f44552ecfb11cf289709106510741e245f16995 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Fri, 20 Jul 2018 09:16:53 -0400 Subject: [PATCH 0741/1412] Fix incorrect command name related to commit from yesterday --- lib/active_record/tasks/sqlserver_database_tasks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index ae5a6f3df..cd10b87c2 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -53,7 +53,7 @@ def structure_dump(filename, extra_flags) server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration['port'] command = [ "defncopy-ttds", - host_parameter, + server_arg, "-D #{Shellwords.escape(configuration['database'])}", "-U #{Shellwords.escape(configuration['username'])}", "-P #{Shellwords.escape(configuration['password'])}", From cf0b175eee8caf34afe45315d661d31641decb9e Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 27 Sep 2018 14:50:00 -0400 Subject: [PATCH 0742/1412] Fix BigDecimal usage --- .../sqlserver/type/time_value_fractional.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 38862473d..290045a9e 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 @@ -39,7 +39,7 @@ def fractional_operator end def fractional_precision - 0.00333.to_d + BigDecimal('0.00333') end def fractional_scale @@ -68,7 +68,7 @@ def fractional_digits end def fractional_precision - 0.0000001.to_d + BigDecimal('0.0000001') end def fractional_scale From 2cf2e7c063d884880067dfc9e37608d6117c266d Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 27 Sep 2018 15:24:53 -0400 Subject: [PATCH 0743/1412] Remove unneeded require --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 71db36e88..f37ee8cae 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,7 +1,6 @@ require 'base64' require 'active_record' require 'arel_sqlserver' -require 'arel/visitors/sqlserver' require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/sqlserver/core_ext/active_record' require 'active_record/connection_adapters/sqlserver/core_ext/calculations' From 42bed222d1d56238bcab051fb6dacd984d2a845b Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 27 Sep 2018 15:38:57 -0400 Subject: [PATCH 0744/1412] Use alternative mirror to download Free TDS --- test/bin/install-freetds.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/install-freetds.sh b/test/bin/install-freetds.sh index 16e9f66fe..5c8bcb705 100755 --- a/test/bin/install-freetds.sh +++ b/test/bin/install-freetds.sh @@ -5,7 +5,7 @@ set -e FREETDS_VERSION=1.00.21 -wget ftp://ftp.freetds.org/pub/freetds/stable/freetds-$FREETDS_VERSION.tar.gz +wget https://fossies.org/linux/privat/freetds-FREETDS_VERSION.tar.gz tar -xzf freetds-$FREETDS_VERSION.tar.gz cd freetds-$FREETDS_VERSION ./configure --prefix=/opt/local \ From d325d77b164711b6d9031fad82d03d50fbaed220 Mon Sep 17 00:00:00 2001 From: Matthew Dunbar Date: Thu, 27 Sep 2018 15:47:16 -0400 Subject: [PATCH 0745/1412] Switch back to official FTP server --- test/bin/install-freetds.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/install-freetds.sh b/test/bin/install-freetds.sh index 5c8bcb705..16e9f66fe 100755 --- a/test/bin/install-freetds.sh +++ b/test/bin/install-freetds.sh @@ -5,7 +5,7 @@ set -e FREETDS_VERSION=1.00.21 -wget https://fossies.org/linux/privat/freetds-FREETDS_VERSION.tar.gz +wget ftp://ftp.freetds.org/pub/freetds/stable/freetds-$FREETDS_VERSION.tar.gz tar -xzf freetds-$FREETDS_VERSION.tar.gz cd freetds-$FREETDS_VERSION ./configure --prefix=/opt/local \ From a225513740630b19f3ff3e03aa064adf2f3b8912 Mon Sep 17 00:00:00 2001 From: Rhuan Date: Tue, 4 Dec 2018 08:59:53 +0100 Subject: [PATCH 0746/1412] Fix build problems for testing. Pointing the file download to http will fix the problems with Job #40.3 in travis ci. FTP download is unstable. --- test/bin/install-freetds.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/bin/install-freetds.sh b/test/bin/install-freetds.sh index 16e9f66fe..755ff928a 100755 --- a/test/bin/install-freetds.sh +++ b/test/bin/install-freetds.sh @@ -5,7 +5,7 @@ set -e FREETDS_VERSION=1.00.21 -wget ftp://ftp.freetds.org/pub/freetds/stable/freetds-$FREETDS_VERSION.tar.gz +wget http://www.freetds.org/files/stable/freetds-$FREETDS_VERSION.tar.gz tar -xzf freetds-$FREETDS_VERSION.tar.gz cd freetds-$FREETDS_VERSION ./configure --prefix=/opt/local \ From 02729b234d4fae80bf898c9b785aae239a1ffe41 Mon Sep 17 00:00:00 2001 From: Rodrigo Azevedo Date: Fri, 14 Dec 2018 16:26:17 +0000 Subject: [PATCH 0747/1412] Fix Time value precision calculation --- .../sqlserver/type/time_value_fractional.rb | 8 ++++++-- test/cases/coerced_tests.rb | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) 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 290045a9e..1a3de73d9 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 @@ -10,12 +10,16 @@ module TimeValueFractional def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? value.change fractional_property => seconds_precision(value) + + millis = seconds_precision(value).to_i + value = value.change fractional_property => millis % fractional_operator + value + millis / fractional_operator end def seconds_precision(value) return 0 if fractional_scale == 0 seconds = value.send(fractional_property).to_d / fractional_operator.to_d - seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).truncate(fractional_scale) + seconds = ((seconds / fractional_precision).round * fractional_precision).round(fractional_scale) (seconds * fractional_operator).round(0).to_i end @@ -39,7 +43,7 @@ def fractional_operator end def fractional_precision - BigDecimal('0.00333') + BigDecimal('0.003333') end def fractional_scale diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index db7aff98f..207d0ff04 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -223,14 +223,14 @@ class QuoteARBaseTest < ActiveRecord::TestCase coerce_tests! :test_quote_ar_object def test_quote_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "'02-14-2017 12:34:56.789'", @connection.quote(value) + assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) end # Use our date format. coerce_tests! :test_type_cast_ar_object def test_type_cast_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "02-14-2017 12:34:56.789", @connection.type_cast(value) + assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) end end From 594195b24007161be093bed5ad6a69fa2dc3ffc4 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 7 Nov 2018 00:00:23 +0000 Subject: [PATCH 0748/1412] Configure tests to run inside a Docker container --- .gitignore | 4 +++- .travis.yml | 33 +++++++++++---------------------- Dockerfile | 14 ++++++++++++++ docker-compose.yml | 11 +++++++++++ test/bin/.keep | 0 5 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml delete mode 100644 test/bin/.keep diff --git a/.gitignore b/.gitignore index 511d7d68d..ff20d0deb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,9 @@ Gemfile.lock test/profile/output/* .rvmrc .rbenv-version +.tool-versions .idea coverage/* .flooignore -.floo \ No newline at end of file +.floo +.byebug_history diff --git a/.travis.yml b/.travis.yml index e8ec0ad6c..e6db413aa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,28 +2,17 @@ sudo: required cache: bundler services: - docker -env: - global: - - TINYTDS_VERSION=2.1.0 - - ACTIVERECORD_UNITTEST_HOST=localhost - - ACTIVERECORD_UNITTEST_DATASERVER=localhost -rvm: - - 2.2.10 - - 2.3.7 - - 2.4.4 - - 2.5.1 before_install: - - export PATH=/opt/local/bin:$PATH - - docker info - - sudo ./test/bin/setup.sh - - sudo ./test/bin/install-openssl.sh - - openssl version - - sudo ./test/bin/install-freetds.sh - - tsql -C + - sudo rm /usr/local/bin/docker-compose + - sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose + - sudo chmod +x /usr/local/bin/docker-compose install: - - export PATH=/opt/local/bin:$PATH - - gem install bundler - - bundle --version - - bundle install + - docker-compose build --build-arg TARGET_VERSION=$TARGET_VERSION script: - - bundle exec rake + - docker-compose run tests +matrix: + include: + - env: TARGET_VERSION=2.3.8 + - env: TARGET_VERSION=2.4.5 + - env: TARGET_VERSION=2.5.3 + - env: TARGET_VERSION=2.6.0 diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..74718bab2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,14 @@ +ARG TARGET_VERSION=2.5.3 + +FROM wpolicarpo/activerecord-sqlserver-adapter:${TARGET_VERSION} + +ENV WORKDIR /activerecord-sqlserver-adapter + +RUN mkdir -p $WORKDIR +WORKDIR $WORKDIR + +COPY . $WORKDIR + +RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 + +CMD ["sh"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 000000000..556a68f7c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,11 @@ +version: "2.2" +services: + database: + image: metaskills/mssql-server-linux-rails + tests: + environment: + - ACTIVERECORD_UNITTEST_HOST=database + build: . + command: wait-for database:1433 -- bundle exec rake test + depends_on: + - "database" diff --git a/test/bin/.keep b/test/bin/.keep deleted file mode 100644 index e69de29bb..000000000 From d92c6a298a768c4e84845e2bff78867642bf974b Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 16 Jan 2019 22:03:46 +0000 Subject: [PATCH 0749/1412] Rename CI docker-compose file --- .travis.yml | 5 ++++- docker-compose.yml => docker-compose.ci.yml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) rename docker-compose.yml => docker-compose.ci.yml (96%) diff --git a/.travis.yml b/.travis.yml index e6db413aa..9cc49dc18 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,9 @@ sudo: required cache: bundler services: - docker +env: + global: + - COMPOSE_FILE: docker-compose.ci.yml before_install: - sudo rm /usr/local/bin/docker-compose - sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose @@ -9,7 +12,7 @@ before_install: install: - docker-compose build --build-arg TARGET_VERSION=$TARGET_VERSION script: - - docker-compose run tests + - docker-compose run ci matrix: include: - env: TARGET_VERSION=2.3.8 diff --git a/docker-compose.yml b/docker-compose.ci.yml similarity index 96% rename from docker-compose.yml rename to docker-compose.ci.yml index 556a68f7c..a80798097 100644 --- a/docker-compose.yml +++ b/docker-compose.ci.yml @@ -2,7 +2,7 @@ version: "2.2" services: database: image: metaskills/mssql-server-linux-rails - tests: + ci: environment: - ACTIVERECORD_UNITTEST_HOST=database build: . From 8b97fef78bef1086aee05555600f45ebc369f790 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 1 Oct 2018 23:07:28 +0100 Subject: [PATCH 0750/1412] Fix isolation level reset --- .../connection_adapters/sqlserver/transaction.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 591613f1e..0e9f759f6 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -26,13 +26,13 @@ def current_isolation_level end - Transaction.send :include, SQLServerTransaction + Transaction.send :prepend, SQLServerTransaction module SQLServerRealTransaction attr_reader :starting_isolation_level - def initialize(connection, options, run_commit_callbacks: false) + def initialize(connection, options, *args) @connection = connection @starting_isolation_level = current_isolation_level if options[:isolation] super @@ -58,7 +58,6 @@ def reset_starting_isolation_level end - RealTransaction.send :include, SQLServerRealTransaction - + RealTransaction.send :prepend, SQLServerRealTransaction end end From 716bbb4d7d3469a586ccd24f50b743a17d741f18 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 2 Oct 2018 00:39:50 +0100 Subject: [PATCH 0751/1412] Fix failing ActiveRecord::RelationTest tests --- test/cases/coerced_tests.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 207d0ff04..8746c4623 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -742,6 +742,24 @@ def test_reverse_arel_assoc_order_with_function_coerced end end +class ActiveRecord::RelationTest < ActiveRecord::TestCase + coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased + def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced + categorizations_with_authors = Categorization.joins(:author) + queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a } + + nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size } + assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query" + + # using `\W` as the column separator + query_matches = queries.any? do |sql| + %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql) + end + + assert query_matches, "Should be aliasing the child INNER JOINs in query" + end +end + From 3a98b473dc9068bd5df84a3f23129561b6aad9cc Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 18 Oct 2018 23:16:07 +0100 Subject: [PATCH 0752/1412] Update supported ruby versions --- circle.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/circle.yml b/circle.yml index d62c92952..def09edb8 100644 --- a/circle.yml +++ b/circle.yml @@ -19,9 +19,9 @@ dependencies: - sudo ./test/bin/install-freetds.sh - tsql -C - rvm-exec 2.2.10 bundle install - - rvm-exec 2.3.7 bundle install - - rvm-exec 2.4.4 bundle install - - rvm-exec 2.5.1 bundle install + - rvm-exec 2.3.8 bundle install + - rvm-exec 2.4.5 bundle install + - rvm-exec 2.5.3 bundle install database: override: @@ -33,6 +33,6 @@ database: test: override: - rvm-exec 2.2.10 bundle exec rake test - - rvm-exec 2.3.7 bundle exec rake test - - rvm-exec 2.4.4 bundle exec rake test - - rvm-exec 2.5.1 bundle exec rake test + - rvm-exec 2.3.8 bundle exec rake test + - rvm-exec 2.4.5 bundle exec rake test + - rvm-exec 2.5.3 bundle exec rake test From 2df96a0cd2f6e2ce0e867c85ccbd4bb4fc07e51f Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 19 Oct 2018 01:16:49 +0100 Subject: [PATCH 0753/1412] Fix flaky InstrumentationTest --- test/cases/coerced_tests.rb | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8746c4623..9b90f9a6b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -151,7 +151,25 @@ class BindParameterTest < ActiveRecord::TestCase end - +module ActiveRecord + class InstrumentationTest < ActiveRecord::TestCase + # This fails randomly due to schema cache being lost? + coerce_tests! :test_payload_name_on_load + def test_payload_name_on_load_coerced + Book.create(name: "test book") + Book.first + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "SELECT" + assert_equal "Book Load", event.payload[:name] + end + end + Book.first + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + end +end class CalculationsTest < ActiveRecord::TestCase # This fails randomly due to schema cache being lost? From 0ecb374e391cad64c697d476ee58f491f36c5541 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 19:19:12 +0000 Subject: [PATCH 0754/1412] Set Connection#bind_params_length to 2100 --- .../connection_adapters/sqlserver/database_limits.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index e822ca96a..42bf1e97c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -2,7 +2,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseLimits - def table_alias_length 128 end @@ -43,6 +42,11 @@ def joins_per_query 256 end + private + + def bind_params_length + 2_100 + end end end end From 702ac5387f57a9a972be3804376d52b4c3875890 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 20:04:21 +0000 Subject: [PATCH 0755/1412] Decrease the max number of binds to 2098 --- .../connection_adapters/sqlserver/database_limits.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 42bf1e97c..78014aa01 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -44,8 +44,11 @@ def joins_per_query private + # The max number of binds is 2100, but because sp_executesql takes + # the first 2 params as the query string and the list of types, + # we have only 2098 spaces left def bind_params_length - 2_100 + 2_098 end end end From 521d48049da3a4f2ac135f23a8a61a1a1b6444f6 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 21:37:27 +0000 Subject: [PATCH 0756/1412] Limit fixture insert to 1000 rows SQL Server has a limit of 1000 rows per insert statement. ActiveRecord does not expose a configuration so adapters can set this limit by themselves. This change creates a new config entry and apply the limit fixtures creation. --- .../sqlserver/database_limits.rb | 4 ++++ .../sqlserver/database_statements.rb | 23 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 78014aa01..f6e77a80e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -50,6 +50,10 @@ def joins_per_query def bind_params_length 2_098 end + + def insert_rows_length + 1_000 + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e4e866812..3d428b6a6 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -79,6 +79,29 @@ def case_sensitive_comparison(table, attribute, column, value) end end + # We should propose this change to Rails team + def insert_fixtures_set(fixture_set, tables_to_delete = []) + fixture_inserts = [] + + fixture_set.each do |table_name, fixtures| + fixtures.each_slice(insert_rows_length) do |batch| + fixture_inserts << build_fixture_sql(batch, table_name) + end + end + + table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup } + total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts)) + + disable_referential_integrity do + transaction(requires_new: true) do + total_sql.each do |sql| + execute sql, "Fixtures Load" + yield if block_given? + end + end + end + end + def can_perform_case_insensitive_comparison_for?(column) column.type == :string && (!column.collation || column.case_sensitive?) end From 2877bc803fbe1621d2764742fdf5feb2f88ee8ac Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 22:10:38 +0000 Subject: [PATCH 0757/1412] Fix datetime precision tests --- test/cases/coerced_tests.rb | 43 +++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9b90f9a6b..e8fceb05c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -930,10 +930,53 @@ def test_invalid_datetime_precision_raises_error_coerced end end end + + # datetime is rounded to increments of .000, .003, or .007 seconds + coerce_tests! :test_datetime_precision_is_truncated_on_assignment + def test_datetime_precision_is_truncated_on_assignment_coerced + @connection.create_table(:foos, force: true) + @connection.add_column :foos, :created_at, :datetime, precision: 0 + @connection.add_column :foos, :updated_at, :datetime, precision: 6 + + time = ::Time.now.change(nsec: 123456789) + foo = Foo.new(created_at: time, updated_at: time) + + assert_equal 0, foo.created_at.nsec + assert_equal 123457000, foo.updated_at.nsec + + foo.save! + foo.reload + + assert_equal 0, foo.created_at.nsec + assert_equal 123457000, foo.updated_at.nsec + end end +class TimePrecisionTest < ActiveRecord::TestCase + # datetime is rounded to increments of .000, .003, or .007 seconds + 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, :finish, :time, precision: 6 + + time = ::Time.now.change(nsec: 123456789) + foo = Foo.new(start: time, finish: time) + + assert_equal 0, foo.start.nsec + assert_equal 123457000, foo.finish.nsec + + foo.save! + foo.reload + + assert_equal 0, foo.start.nsec + assert_equal 123457000, foo.finish.nsec + end +end + + class DefaultNumbersTest < ActiveRecord::TestCase # We do better with native types and do not return strings for everything. From ba5893b1e25aa45a6ee23894132c658a6eecee9d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 22:17:56 +0000 Subject: [PATCH 0758/1412] Remove ruby 2.2 in favour of 2.6 --- circle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/circle.yml b/circle.yml index def09edb8..ce0dfafc6 100644 --- a/circle.yml +++ b/circle.yml @@ -18,10 +18,10 @@ dependencies: - openssl version - sudo ./test/bin/install-freetds.sh - tsql -C - - rvm-exec 2.2.10 bundle install - rvm-exec 2.3.8 bundle install - rvm-exec 2.4.5 bundle install - rvm-exec 2.5.3 bundle install + - rvm-exec 2.6.0 bundle install database: override: @@ -32,7 +32,7 @@ database: test: override: - - rvm-exec 2.2.10 bundle exec rake test - rvm-exec 2.3.8 bundle exec rake test - rvm-exec 2.4.5 bundle exec rake test - rvm-exec 2.5.3 bundle exec rake test + - rvm-exec 2.6.0 bundle exec rake test From ae2ceadc1e0e47c08e406e9ee214e03af1ae6240 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 14 Jan 2019 22:40:29 +0000 Subject: [PATCH 0759/1412] Fix RelationMerging test case --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e8fceb05c..047966262 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1145,3 +1145,13 @@ def test_update_with_dirty_primary_key_coerced end end end + + + +class RelationMergingTest < ActiveRecord::TestCase + coerce_tests! :test_merging_with_order_with_binds + def test_merging_with_order_with_binds_coerced + relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"])) + assert_equal ["title LIKE N'%suffix'"], relation.order_values + end +end From c0b36331c53b83973f4b73c9537825960f4d87a4 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 16 Jan 2019 01:29:30 +0000 Subject: [PATCH 0760/1412] Decrease in_clause_length to 10k --- .../connection_adapters/sqlserver/database_limits.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index f6e77a80e..c5f419094 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -31,7 +31,7 @@ def columns_per_multicolumn_index end def in_clause_length - 65_536 + 10_000 end def sql_query_length From 965aa346327f344be021cea4a37bda800ca1939c Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 16 Jan 2019 21:15:14 +0000 Subject: [PATCH 0761/1412] Coerce test_eager_loading_too_may_ids due to rails issue --- test/cases/coerced_tests.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 047966262..5607f0058 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1155,3 +1155,9 @@ def test_merging_with_order_with_binds_coerced assert_equal ["title LIKE N'%suffix'"], relation.order_values end end + + +class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase + # Temporarily coerce this test due to https://github.com/rails/rails/issues/34945 + coerce_tests! :test_eager_loading_too_may_ids +end From 1bd52f193d06f024093a322bb30cdbba06d4b096 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 16 Jan 2019 23:48:42 +0000 Subject: [PATCH 0762/1412] Fix datetime precision regression --- .../sqlserver/type/time_value_fractional.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) 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 1a3de73d9..412958795 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 @@ -10,23 +10,19 @@ module TimeValueFractional def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? value.change fractional_property => seconds_precision(value) - - millis = seconds_precision(value).to_i - value = value.change fractional_property => millis % fractional_operator - value + millis / fractional_operator end def seconds_precision(value) return 0 if fractional_scale == 0 - seconds = value.send(fractional_property).to_d / fractional_operator.to_d - seconds = ((seconds / fractional_precision).round * fractional_precision).round(fractional_scale) + seconds = value.send(fractional_property).to_f / fractional_operator.to_f + seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) (seconds * fractional_operator).round(0).to_i end def quote_fractional(value) return 0 if fractional_scale == 0 frac_seconds = seconds_precision(value) - seconds = (frac_seconds.to_d / fractional_operator.to_d).round(fractional_scale) + seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) seconds.to_d.to_s.split('.').last.to(fractional_scale-1) end @@ -43,7 +39,7 @@ def fractional_operator end def fractional_precision - BigDecimal('0.003333') + 0.00333 end def fractional_scale @@ -72,7 +68,7 @@ def fractional_digits end def fractional_precision - BigDecimal('0.0000001') + 0.0000001 end def fractional_scale From bd6eacb806fde37ab45bd87855c9a2bbf3607966 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 17 Jan 2019 15:09:22 +0000 Subject: [PATCH 0763/1412] Fix average calculation in ruby 2.6 Ruby 2.6 added `to_d` to BigDecimal and it was causing some tests to fail because of that. See https://github.com/rails/rails/pull/34858 --- .../sqlserver/core_ext/calculations.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 61b5057df..cfdb65453 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -11,6 +11,15 @@ module Calculations def build_count_subquery(relation, column_name, distinct) super(relation.unscope(:order), column_name, distinct) end + + def type_cast_calculated_value(value, type, operation = nil) + case operation + when "count" then value.to_i + when "sum" then type.deserialize(value || 0) + when "average" then value&.respond_to?(:to_d) ? value.to_d : value + else type.deserialize(value) + end + end end end end From c07ecc0331c804afd815e71e12d87e4b4d2112eb Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 17 Jan 2019 16:38:43 +0000 Subject: [PATCH 0764/1412] Add back supports_savepoints? returning true --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d7ffd0f49..f49f7fdc2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -135,6 +135,10 @@ def supports_comments_in_create? false end + def supports_savepoints? + true + end + def supports_in_memory_oltp? @version_year >= 2014 end From 235765e24ef409d06bb066e1c6ce2931be6b3185 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 17 Jan 2019 23:21:48 +0000 Subject: [PATCH 0765/1412] Add label to travis matrix --- .travis.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9cc49dc18..35c82b95c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,11 @@ script: - docker-compose run ci matrix: include: - - env: TARGET_VERSION=2.3.8 - - env: TARGET_VERSION=2.4.5 - - env: TARGET_VERSION=2.5.3 - - env: TARGET_VERSION=2.6.0 + - name: 2.3.8 + env: TARGET_VERSION=2.3.8 + - name: 2.4.5 + env: TARGET_VERSION=2.4.5 + - name: 2.5.3 + env: TARGET_VERSION=2.5.3 + - name: 2.6.0 + env: TARGET_VERSION=2.6.0 From c6eaa01415eea245fd48e7c9efa1ae67e440b399 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 18 Jan 2019 11:45:58 +0000 Subject: [PATCH 0766/1412] Better sqlserver version memoization --- .../connection_adapters/sqlserver_adapter.rb | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f49f7fdc2..b3f745ac6 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -215,7 +215,7 @@ def sqlserver? end def sqlserver_azure? - @sqlserver_azure ||= !!(select_value('SELECT @@version', 'SCHEMA') =~ /Azure/i) + !!(sqlserver_version =~ /Azure/i) end def database_prefix_remote_server? @@ -438,16 +438,15 @@ def initialize_dateformatter end def version_year - return @version_year if defined?(@version_year) - @version_year = begin - vstring = _raw_select('SELECT @@version', fetch: :rows).first.first.to_s - return 2016 if vstring =~ /vNext/ - /SQL Server (\d+)/.match(vstring).to_a.last.to_s.to_i - rescue Exception => e - 2016 - end + return 2016 if sqlserver_version =~ /vNext/ + /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i + rescue StandardError => e + 2016 end + def sqlserver_version + @sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s + end end end end From 2641dc016a6357c802b1a34e832b84ba687e4fac Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 18 Jan 2019 14:26:56 +0000 Subject: [PATCH 0767/1412] Fix flaky migration test --- test/cases/migration_test_sqlserver.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 73bf98399..04bee8d40 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -41,6 +41,8 @@ class MigrationTestSQLServer < ActiveRecord::TestCase lock_version_column = Person.columns_hash['lock_version'] assert_equal :string, lock_version_column.type assert lock_version_column.default.nil? + assert_nothing_raised { connection.change_column 'people', 'lock_version', :integer } + Person.reset_column_information end it 'not drop the default contraint if just renaming' do From 78ac7a18c940228940c9747eae990d6f9a9b89f2 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 18 Jan 2019 16:20:10 +0000 Subject: [PATCH 0768/1412] Do not cache internal introspection queries --- .../sqlserver/schema_statements.rb | 16 ++++++++++------ test/cases/coerced_tests.rb | 1 + 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9186b8e7d..68761c8b5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -415,11 +415,15 @@ def column_definitions(table_name) ci[:default_function] = begin default = ci[:default_value] if default.nil? && view_exists - default = select_value " - SELECT c.COLUMN_DEFAULT - FROM #{database}.INFORMATION_SCHEMA.COLUMNS c - WHERE c.TABLE_NAME = '#{view_tblnm}' - AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'".squish, 'SCHEMA' + default = uncached do + select_value %{ + SELECT c.COLUMN_DEFAULT + FROM #{database}.INFORMATION_SCHEMA.COLUMNS c + WHERE + c.TABLE_NAME = '#{view_tblnm}' + AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}' + }.squish, 'SCHEMA' + end end case default when nil @@ -438,7 +442,7 @@ def column_definitions(table_name) else ci[:type] end value = default.match(/\A\((.*)\)\Z/m)[1] - value = select_value "SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA' + value = uncached { select_value("SELECT CAST(#{value} AS #{type}) AS value3", 'SCHEMA') } [value, nil] end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5607f0058..6bcf406b6 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -605,6 +605,7 @@ def test_merge_options_coerced +require 'models/parrot' require 'models/topic' class PersistenceTest < ActiveRecord::TestCase # We can not UPDATE identity columns. From 37e19256e94a22f4a758c6a63e908517de4619e1 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 23 Jan 2019 14:14:42 +0000 Subject: [PATCH 0769/1412] Add back cache for internal queries --- .../sqlserver/schema_statements.rb | 18 +++++------- test/cases/helper_sqlserver.rb | 1 + test/support/core_ext/query_cache.rb | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 10 deletions(-) create mode 100644 test/support/core_ext/query_cache.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 68761c8b5..1debbb3fa 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -415,15 +415,13 @@ def column_definitions(table_name) ci[:default_function] = begin default = ci[:default_value] if default.nil? && view_exists - default = uncached do - select_value %{ - SELECT c.COLUMN_DEFAULT - FROM #{database}.INFORMATION_SCHEMA.COLUMNS c - WHERE - c.TABLE_NAME = '#{view_tblnm}' - AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}' - }.squish, 'SCHEMA' - end + default = select_value %{ + SELECT c.COLUMN_DEFAULT + FROM #{database}.INFORMATION_SCHEMA.COLUMNS c + WHERE + c.TABLE_NAME = '#{view_tblnm}' + AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}' + }.squish, 'SCHEMA' end case default when nil @@ -442,7 +440,7 @@ def column_definitions(table_name) else ci[:type] end value = default.match(/\A\((.*)\)\Z/m)[1] - value = uncached { select_value("SELECT CAST(#{value} AS #{type}) AS value3", 'SCHEMA') } + value = select_value("SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA') [value, nil] end end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 607d5a5f7..677c40bf7 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -2,6 +2,7 @@ require 'bundler/setup' Bundler.require :default, :development require 'pry' +require 'support/core_ext/query_cache' require 'support/minitest_sqlserver' require 'support/test_in_memory_oltp' require 'cases/helper' diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb new file mode 100644 index 000000000..6661696e4 --- /dev/null +++ b/test/support/core_ext/query_cache.rb @@ -0,0 +1,29 @@ +require 'active_record/connection_adapters/sqlserver_adapter' + +module SqlIgnoredCache + extend ActiveSupport::Concern + + IGNORED_SQL = [ + /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, + /SELECT @@version/, + /SELECT @@TRANCOUNT/, + /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, + /SELECT CAST\(.* AS .*\) AS value/, + /SELECT DATABASEPROPERTYEX/im + ] + + # We don't want to coerce every ActiveRecord test that relies on `query_cache` + # just because we do more queries than the other adapters. + # + # Removing internal queries from the cache will make AR tests pass without + # compromising cache outside tests. + def cache_sql(sql, name, binds) + result = super + @query_cache.delete_if { |k, v| k =~ Regexp.union(IGNORED_SQL) } + result + end +end + +ActiveSupport.on_load(:active_record) do + ActiveRecord::ConnectionAdapters::SQLServerAdapter.prepend(SqlIgnoredCache) +end From 0dd5e98dadc0e81e4ceb724cb5323ad465f4e107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Attila=20Horv=C3=A1th?= Date: Thu, 5 Apr 2018 13:33:18 +0100 Subject: [PATCH 0770/1412] Make String equality check work for Type::Data values --- .../connection_adapters/sqlserver/type/string.rb | 7 +++++++ test/cases/column_test_sqlserver.rb | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/string.rb b/lib/active_record/connection_adapters/sqlserver/type/string.rb index e6b12b3bb..a1439f45b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/string.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/string.rb @@ -4,6 +4,13 @@ module SQLServer module Type class String < ActiveRecord::Type::String + def changed_in_place?(raw_old_value, new_value) + if raw_old_value.is_a?(Data) + raw_old_value.value != new_value + else + super + end + end end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 42acb08b4..e3f869135 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -798,6 +798,12 @@ def assert_obj_set_and_save(attribute, value) obj.save! end + it 'does not mark object as changed after save' do + obj.save! + obj.attributes + obj.changed?.must_equal false + end + end end From 9040c247a3fd6809fc98c71e4e8f3b5e1f14c564 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 21 Jan 2019 10:59:53 +0000 Subject: [PATCH 0771/1412] Fix tinyint columns schema migration --- .../connection_adapters/sqlserver/schema_statements.rb | 3 ++- test/cases/adapter_test_sqlserver.rb | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 1debbb3fa..a57a37c4b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -220,7 +220,8 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) case type.to_s when 'integer' case limit - when 1..2 then 'smallint' + 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.") diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 16cb96b97..34d75bce7 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -273,9 +273,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'integer', connection.type_to_sql(:integer, limit: 3) end - it 'create smallints when limit is less than 3' do + it 'create smallints when limit is 2' do assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2) - assert_equal 'smallint', connection.type_to_sql(:integer, limit: 1) + end + + it 'create tinyints when limit is 1' do + assert_equal 'tinyint', connection.type_to_sql(:integer, limit: 1) end it 'create bigints when limit is greateer than 4' do From 229939b83f1e7bc58556bf4b15b46514efb3de1d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 6 Feb 2019 19:54:19 +0000 Subject: [PATCH 0772/1412] Prepare for v5.2.0.rc1 --- CHANGELOG.md | 52 ++++++---------------------------------------------- Gemfile | 2 +- README.md | 8 ++++---- VERSION | 2 +- 4 files changed, 12 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca28fdc9f..09af9eb7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,54 +1,14 @@ -## v5.1.6 - -#### Added - -* Use lock hint when joining table in query. - - -## v5.1.5 - -#### Fixed - -* Memoize `@@version` queries. Fixes #632 - - -## v5.1.4 - -#### Fixed - -* Add case insensitive comparison for better performance with CI collations. Fixes #624 - - -## v5.1.3 +## v5.2.0.rc1 #### Fixed -* Use bigint type in sqlserver_type when needed. Fixes #616 - - -## v5.1.2 - -#### Fixed - -* The `fast_string_to_time` method when zone local. Fixes #609 #614 #620 -* Patched `Relation#build_count_subquery`. Fixes #613. -* Inserts to tables with triggers using default `OUTPUT INSERTED` style. Fixes #595. - - -## v5.1.1 - -#### Fixed - -* Use `ActiveSupport.on_load` to hook into ActiveRecord Fixes #588 #598 - - -## v5.1.0 +- #638 Don't disable referential integrity for the same table twice. +- #646 Make String equality check work for Type::Data values. Fixes #645. +- #671 Fix tinyint columns schema migration. Fixes #670. #### Changed -* The `drop_table` with force cascade option now mimics in via pure SQL for us. - -#### Added +- #642 Added with (nolock) hint to information_schema.views. -* Support MismatchedForeignKey exception. +Please check [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/5-1-stable/CHANGELOG.md) for previous changes. diff --git a/Gemfile b/Gemfile index 75a716cfd..f27143d4d 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ require 'openssl' source 'https://rubygems.org' gemspec -gem 'sqlite3' +gem 'sqlite3', '~> 1.3.6' gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/README.md b/README.md index a8c06b03c..c83a7f849 100644 --- a/README.md +++ b/README.md @@ -14,13 +14,13 @@ Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source proje ## About The Adapter -The SQL Server adapter for ActiveRecord v5.1 using SQL Server 2012 or higher. +The SQL Server adapter for ActiveRecord v5.2 using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.0.x version of the adapter is only for the latest 5.0 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.1.x version of the adapter is only for the latest 5.1 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. #### Native Data Type Support -We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb#L243) for an updated list. +We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. The following types (date, datetime2, datetimeoffset, time) all require TDS version 7.3 with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to "7.3". The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. @@ -136,7 +136,7 @@ gem 'activerecord-sqlserver-adapter' ## Contributing -If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Gitter, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the `RUNNING_UNIT_TESTS` file for the details of how to run the unit tests. +If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Gitter, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the [`RUNNING_UNIT_TESTS`](RUNNING_UNIT_TESTS.md) file for the details of how to run the unit tests. * Github: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter * Gitter: https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter diff --git a/VERSION b/VERSION index 91ff57278..9f1dc6661 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.2.0 +5.2.0.rc1 From 3fdaa107122064d809975f42a6e8af68ca0b3424 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lairton=20L=C3=A9lis?= Date: Fri, 22 Feb 2019 19:52:48 -0300 Subject: [PATCH 0773/1412] Make change_column_null use column_for instead of schema_cache --- .../connection_adapters/sqlserver/schema_statements.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a57a37c4b..a6c201025 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -258,7 +258,7 @@ def update_table_definition(table_name, base) def change_column_null(table_name, column_name, allow_null, default = nil) table_id = SQLServer::Utils.extract_identifiers(table_name) column_id = SQLServer::Utils.extract_identifiers(column_name) - column = detect_column_for! table_name, column_name + column = column_for(table_name, column_name) if !allow_null.nil? && allow_null == false && !default.nil? do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end @@ -492,13 +492,6 @@ def default_constraint_name(table_name, column_name) "DF_#{table_name}_#{column_name}" end - def detect_column_for!(table_name, column_name) - unless column = schema_cache.columns(table_name).find { |c| c.name == column_name.to_s } - raise ActiveRecordError, "No such column: #{table_name}.#{column_name}" - end - column - end - def lowercase_schema_reflection_sql(node) lowercase_schema_reflection ? "LOWER(#{node})" : node end From dc4ca14e3d25d6f05b7eb32595c31fe05cd6776c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lairton=20L=C3=A9lis?= Date: Fri, 22 Feb 2019 19:54:49 -0300 Subject: [PATCH 0774/1412] Add test cases for change_column_null bug --- .../change_column_null_test_sqlserver.rb | 42 +++++++++++++++++++ .../create_clients_and_change_column_null.rb | 23 ++++++++++ 2 files changed, 65 insertions(+) create mode 100644 test/cases/change_column_null_test_sqlserver.rb create mode 100644 test/migrations/create_clients_and_change_column_null.rb diff --git a/test/cases/change_column_null_test_sqlserver.rb b/test/cases/change_column_null_test_sqlserver.rb new file mode 100644 index 000000000..60fcf1077 --- /dev/null +++ b/test/cases/change_column_null_test_sqlserver.rb @@ -0,0 +1,42 @@ +require 'cases/helper_sqlserver' +require 'migrations/create_clients_and_change_column_null' + +class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase + before do + @old_verbose = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + CreateClientsAndChangeColumnNull.new.up + end + + after do + CreateClientsAndChangeColumnNull.new.down + ActiveRecord::Migration.verbose = @old_verbose + end + + def find_column(table, name) + table.find { |column| column.name == name } + end + + let(:clients_table) { connection.columns('clients') } + let(:name_column) { find_column(clients_table, 'name') } + let(:code_column) { find_column(clients_table, 'code') } + let(:value_column) { find_column(clients_table, 'value') } + + describe '#change_column_null' do + it 'does not change the column limit' do + name_column.limit.must_equal 15 + end + + it 'does not change the column default' do + code_column.default.must_equal 'n/a' + end + + it 'does not change the column precision' do + value_column.precision.must_equal 32 + end + + it 'does not change the column scale' do + value_column.scale.must_equal 8 + end + end +end diff --git a/test/migrations/create_clients_and_change_column_null.rb b/test/migrations/create_clients_and_change_column_null.rb new file mode 100644 index 000000000..96e459455 --- /dev/null +++ b/test/migrations/create_clients_and_change_column_null.rb @@ -0,0 +1,23 @@ +class CreateClientsAndChangeColumnNull < ActiveRecord::Migration[5.2] + def up + create_table :clients do |t| + t.string :name + t.string :code + t.decimal :value + + t.timestamps + end + + change_column :clients, :name, :string, limit: 15 + change_column :clients, :code, :string, default: 'n/a' + change_column :clients, :value, :decimal, precision: 32, scale: 8 + + change_column_null :clients, :name, false + change_column_null :clients, :code, false + change_column_null :clients, :value, false + end + + def down + drop_table :clients + end +end From 9f9946742ea2df2d9d168cf7f66872a8585eb57c Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sun, 10 Mar 2019 21:29:33 +0000 Subject: [PATCH 0775/1412] Fix explain with array conditions --- .../sqlserver/core_ext/explain.rb | 32 ++++++++----------- test/cases/showplan_test_sqlserver.rb | 12 +++++++ 2 files changed, 26 insertions(+), 18 deletions(-) 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 b12502197..baa8d736c 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -5,12 +5,11 @@ module CoreExt module Explain SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '.freeze - SQLSERVER_PARAM_MATCHER = /@\d+ = (.*)/ - SQLSERVER_NATIONAL_STRING_MATCHER = /N'(.*)'/m + SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries) unprepared_queries = queries.map do |(sql, binds)| - [unprepare_sqlserver_statement(sql), binds] + [unprepare_sqlserver_statement(sql, binds), binds] end super(unprepared_queries) end @@ -19,22 +18,19 @@ def exec_explain(queries) # This is somewhat hacky, but it should reliably reformat our prepared sql statment # which uses sp_executesql to just the first argument, then unquote it. Likewise our - # `sp_executesql` method should substitude the @n args withe the quoted values. - def unprepare_sqlserver_statement(sql) - if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) - executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) - args = executesql.split(', ') - unprepared_sql = args.shift.strip.match(SQLSERVER_NATIONAL_STRING_MATCHER)[1] - unprepared_sql = Utils.unquote_string(unprepared_sql) - args = args.from(args.length / 2) - args.each_with_index do |arg, index| - value = arg.match(SQLSERVER_PARAM_MATCHER)[1] - unprepared_sql.sub! "@#{index}", value - end - unprepared_sql - else - sql + # `sp_executesql` method should substitude the @n args with the quoted values. + def unprepare_sqlserver_statement(sql, binds) + return sql unless sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) + + executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) + executesql = executesql.match(SQLSERVER_STATEMENT_REGEXP).to_a[1] + + binds.each_with_index do |bind, index| + value = connection.quote(bind) + executesql = executesql.sub("@#{index}", value) end + + executesql end end diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index a140f8e32..9e4afb3cd 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -26,6 +26,18 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' end + it 'from array condition using index' do + plan = Car.where(id: [1, 2]).explain + plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" + plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + end + + it 'from array condition' do + plan = Car.where(name: ['honda', 'zyke']).explain + plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" + plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' + end + end describe 'With SHOWPLAN_TEXT option' do From c4aeddb55ee6dbb09a9659399cdad1395a3e5575 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sun, 10 Mar 2019 22:16:35 +0000 Subject: [PATCH 0776/1412] Release v5.2.0.rc2 --- CHANGELOG.md | 7 +++++++ VERSION | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09af9eb7e..5935b233a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## v5.2.0.rc2 + +#### Fixed + +- #681 change_column_null should not clear other column attributes. Fixes #582. +- #684 Fix explain with array conditions. Fixes #673. + ## v5.2.0.rc1 #### Fixed diff --git a/VERSION b/VERSION index 9f1dc6661..793e1f9d1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.2.0.rc1 +5.2.0.rc2 From 9bbd035cc42354b6225049ce86030c89d57cdd6f Mon Sep 17 00:00:00 2001 From: X1ting Date: Mon, 18 Mar 2019 08:58:46 +0300 Subject: [PATCH 0777/1412] set table name in case when pk is not nil --- .../connection_adapters/sqlserver/database_statements.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3d428b6a6..6f543a59a 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -228,6 +228,7 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds) end sql = if pk && use_output_inserted? && !database_prefix_remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted + table_name ||= get_table_name(sql) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted From bbb331055bba67fc4dfdaaa0fbe88834e273d5e7 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 19 Mar 2019 20:40:52 +0000 Subject: [PATCH 0778/1412] Release 5.2.0 --- CHANGELOG.md | 4 ++++ VERSION | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5935b233a..873505b5c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v5.2.0 + +- #686 sql_for_insert set table name in case when pk is not nil + ## v5.2.0.rc2 #### Fixed diff --git a/VERSION b/VERSION index 793e1f9d1..91ff57278 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.2.0.rc2 +5.2.0 From b29603b62baf8d771cc83da07c8ee2d81c8640db Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 12 Feb 2019 22:12:55 +0000 Subject: [PATCH 0779/1412] [skip ci] Remove circle ci --- README.md | 1 - circle.yml | 38 -------------------------------------- 2 files changed, 39 deletions(-) delete mode 100644 circle.yml diff --git a/README.md b/README.md index c83a7f849..8eb5ef7e5 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. * [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI -* [![CircleCI](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master.svg?style=svg)](https://circleci.com/gh/rails-sqlserver/activerecord-sqlserver-adapter/tree/master) - CircleCI * [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version * [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status diff --git a/circle.yml b/circle.yml deleted file mode 100644 index ce0dfafc6..000000000 --- a/circle.yml +++ /dev/null @@ -1,38 +0,0 @@ -general: - branches: - ignore: - - /dev.*/ - -machine: - environment: - PATH: /opt/local/bin:${PATH} - TINYTDS_VERSION: 2.1.0 - ACTIVERECORD_UNITTEST_HOST: localhost - ACTIVERECORD_UNITTEST_DATASERVER: localhost - services: - - docker - -dependencies: - override: - - sudo ./test/bin/install-openssl.sh - - openssl version - - sudo ./test/bin/install-freetds.sh - - tsql -C - - rvm-exec 2.3.8 bundle install - - rvm-exec 2.4.5 bundle install - - rvm-exec 2.5.3 bundle install - - rvm-exec 2.6.0 bundle install - -database: - override: - - echo "Hello" - post: - - docker info - - ./test/bin/setup.sh - -test: - override: - - rvm-exec 2.3.8 bundle exec rake test - - rvm-exec 2.4.5 bundle exec rake test - - rvm-exec 2.5.3 bundle exec rake test - - rvm-exec 2.6.0 bundle exec rake test From 2cea8a9b3854915fb5d56630e98bacc95da1a92a Mon Sep 17 00:00:00 2001 From: pavel Date: Wed, 20 Mar 2019 22:35:14 +0100 Subject: [PATCH 0780/1412] constrains bug --- .../sqlserver/schema_statements.rb | 15 ++++++++++----- test/cases/migration_test_sqlserver.rb | 8 ++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a6c201025..185cc2154 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -141,7 +141,13 @@ def change_column(table_name, column_name, type, options = {}) sql_commands = [] indexes = [] column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s } - if options_include_default?(options) || (column_object && column_object.type != type.to_sym) + without_constraints = options.key?(:default) || options.key?(:limit) + default = if !options.key?(:default) && column_object + 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) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } remove_indexes(table_name, column_name) @@ -149,10 +155,9 @@ def change_column(table_name, column_name, type, options = {}) sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false - if options.key?(:default) && default_constraint_name(table_name, column_name).present? - change_column_default(table_name, column_name, options[:default]) - elsif options_include_default?(options) - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(options[:default], column_object)} FOR #{quote_column_name(column_name)}" + if without_constraints + default = quote_default_expression(default, column_object || column_for(table_name, column_name)) + sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}" end # Add any removed indexes back indexes.each do |index| diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 04bee8d40..fb059ddee 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -57,6 +57,14 @@ class MigrationTestSQLServer < ActiveRecord::TestCase assert default_after assert_equal default_before['constraint_keys'], default_after['constraint_keys'] end + + it 'change limit' do + assert_nothing_raised { connection.change_column :people, :lock_version, :integer, limit: 8 } + end + + it 'change null and default' do + assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } + end end From 8a03c39673f21658eac842d569779c0a36929154 Mon Sep 17 00:00:00 2001 From: Ken Collins Date: Fri, 24 May 2019 18:08:29 -0400 Subject: [PATCH 0781/1412] [Patreon] Remove all info. --- BACKERS.md | 32 -------------------------------- README.md | 5 ----- 2 files changed, 37 deletions(-) delete mode 100644 BACKERS.md diff --git a/BACKERS.md b/BACKERS.md deleted file mode 100644 index 5797c4bc8..000000000 --- a/BACKERS.md +++ /dev/null @@ -1,32 +0,0 @@ -# Backers - -You can join in supporting TinyTDS and the Rails SQL Server Adapter development by [pledging on Patreon](https://www.patreon.com/metaskills)! Backers in the same pledge level appear in the order of pledge date. - -### $2000 - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611218) - - -### $500 - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611209) - - -### $250 - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611199) - - -### $100 - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611196) - - -### $50+ - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611186) - - -### $10+ - -[It could be you!](https://www.patreon.com/bePatron?c=765225&rid=1611149) diff --git a/README.md b/README.md index 8eb5ef7e5..137418527 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,6 @@ * [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community -## Supporting TinyTDS/Adapter - -Both TinyTDS and the Rails SQL Server Adapter are MIT-licensed open source projects. Its ongoing development is made possible thanks to the support by these awesome [backers](https://github.com/rails-sqlserver/tiny_tds/blob/master/BACKERS.md). If you'd like to join them, check out our [Patreon Campaign](https://www.patreon.com/metaskills). - - ## About The Adapter The SQL Server adapter for ActiveRecord v5.2 using SQL Server 2012 or higher. From 41d0675cdf63a12d12dad209d6a20733bf33a0ed Mon Sep 17 00:00:00 2001 From: Akira Matsuda Date: Fri, 7 Jun 2019 18:22:27 +0900 Subject: [PATCH 0782/1412] SET SINGLE_USER before dropping the database to make sure no other connections are alive --- .../connection_adapters/sqlserver/database_tasks.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 8e24234a3..e1ab94871 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -12,6 +12,7 @@ def create_database(database, options = {}) def drop_database(database) name = SQLServer::Utils.extract_identifiers(database) + do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" do_execute "DROP DATABASE #{name}" end From 3ae684d8c1c1a81f94942cbc15c37bdf648e0a13 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 24 Jun 2019 22:22:21 +0100 Subject: [PATCH 0783/1412] [CI] Update ruby matrix --- .travis.yml | 12 ++++++------ Dockerfile | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 35c82b95c..eee1f33f7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,9 @@ matrix: include: - name: 2.3.8 env: TARGET_VERSION=2.3.8 - - name: 2.4.5 - env: TARGET_VERSION=2.4.5 - - name: 2.5.3 - env: TARGET_VERSION=2.5.3 - - name: 2.6.0 - env: TARGET_VERSION=2.6.0 + - name: 2.4.6 + env: TARGET_VERSION=2.4.6 + - name: 2.5.5 + env: TARGET_VERSION=2.5.5 + - name: 2.6.3 + env: TARGET_VERSION=2.6.3 diff --git a/Dockerfile b/Dockerfile index 74718bab2..8b18d5c6a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG TARGET_VERSION=2.5.3 +ARG TARGET_VERSION=2.6.3 FROM wpolicarpo/activerecord-sqlserver-adapter:${TARGET_VERSION} From f2233d71d18b9a73e28450270daec6fdb5fce2fe Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 19 Mar 2019 22:36:29 +0000 Subject: [PATCH 0784/1412] Update ruby versions supported by Rails 6 --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index eee1f33f7..b50730303 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,10 +15,6 @@ script: - docker-compose run ci matrix: include: - - name: 2.3.8 - env: TARGET_VERSION=2.3.8 - - name: 2.4.6 - env: TARGET_VERSION=2.4.6 - name: 2.5.5 env: TARGET_VERSION=2.5.5 - name: 2.6.3 From c43f5a996f7ba9bb064ef43126e48845abcda6c9 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 19 Mar 2019 22:41:59 +0000 Subject: [PATCH 0785/1412] Update activerecord version to 6 --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 91ff57278..248da9b7b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -5.2.0 +6.0.0.beta1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index b560cf30a..c71c1167d 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 5.2.0' + spec.add_dependency 'activerecord', '~> 6.0.0.beta3' spec.add_dependency 'tiny_tds' end From 027ecb3ee431d39f0782d09d3c54305aeb414113 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 20 Mar 2019 20:53:38 +0000 Subject: [PATCH 0786/1412] Fix Rails 6 migrations --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- .../connection_adapters/sqlserver/schema_creation.rb | 2 +- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++-- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6f543a59a..6872a9952 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -232,7 +232,7 @@ def sql_for_insert(sql, pk, id_value, sequence_name, binds) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted - <<-SQL.strip_heredoc + <<~SQL.squish DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 31875a13b..711ec3aca 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -34,7 +34,7 @@ def add_column_options!(sql, options) def action_sql(action, dependency) case dependency when :restrict - raise ArgumentError, <<-MSG.strip_heredoc + raise ArgumentError, <<~MSG.squish '#{dependency}' is not supported for :on_update or :on_delete. Supported values are: :nullify, :cascade MSG diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 185cc2154..617008268 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -7,7 +7,7 @@ def native_database_types @native_database_types ||= initialize_native_database_types.freeze end - def create_table(table_name, comment: nil, **options) + def create_table(table_name, **options) res = super clear_cache! res @@ -536,7 +536,7 @@ def views_real_column_name(table_name, column_name) end def create_table_definition(*args) - SQLServer::TableDefinition.new(*args) + SQLServer::TableDefinition.new(self, *args) end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b3f745ac6..603abc99b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -191,7 +191,7 @@ def reset! # === Abstract Adapter (Misc Support) =========================== # def tables_with_referential_integrity - schemas_and_tables = select_rows <<-SQL.strip_heredoc + schemas_and_tables = select_rows <<~SQL.squish SELECT DISTINCT s.name, o.name FROM sys.foreign_keys i INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID From 6ead58b821e2e797e24e7ae2af1e4660b87f95fa Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 12 Jan 2019 03:16:50 +0900 Subject: [PATCH 0787/1412] Remove `table` and `column` which are no longer passed to `case_sensitive_comparison` Follow up to https://github.com/rails/rails/commit/eb5fef554fde84d36b45191182ed98bd344dc967. --- .../connection_adapters/sqlserver/database_statements.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6872a9952..a87cb29fb 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -71,9 +71,11 @@ def exec_rollback_to_savepoint(name = current_savepoint_name) def release_savepoint(name = current_savepoint_name) end - def case_sensitive_comparison(table, attribute, column, value) + def case_sensitive_comparison(attribute, value) + column = column_for_attribute(attribute) + if column.collation && !column.case_sensitive? - table[attribute].eq(Arel::Nodes::Bin.new(value)) + attribute.eq(Arel::Nodes::Bin.new(value)) else super end From 2dc497b468efac44e86da97c7a4b847708ebe094 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Sat, 12 Jan 2019 03:19:26 +0900 Subject: [PATCH 0788/1412] Remove `id_value` argument which is no longer passed to `sql_for_insert` Follow up to https://github.com/rails/rails/commit/5f9e0f848e83688a7f7b5c0b2285144563d84f36. --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index a87cb29fb..b01f8bf69 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -223,7 +223,7 @@ def newsequentialid_function protected - def sql_for_insert(sql, pk, id_value, sequence_name, binds) + def sql_for_insert(sql, pk, sequence_name, binds) if pk.nil? table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) From 1057a814264bd7aff094fb574a6c09adc4a0bc65 Mon Sep 17 00:00:00 2001 From: Jens Nockert Date: Fri, 24 May 2019 17:57:13 +0200 Subject: [PATCH 0789/1412] Fix constants missing in 6.0.0.rc1 --- lib/arel/visitors/sqlserver.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index e17726d23..84c0e601c 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -31,7 +31,7 @@ def visit_Arel_Nodes_UpdateStatement(o, a) def visit_Arel_Nodes_Lock o, collector o.expr = Arel.sql('WITH(UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/ - collector << SPACE + collector << " " visit o.expr, collector end @@ -57,7 +57,7 @@ def visit_Arel_Nodes_SelectStatement o, collector distinct_One_As_One_Is_So_Not_Fetch o if o.with collector = visit o.with, collector - collector << SPACE + collector << " " end collector = o.cores.inject(collector) { |c,x| visit_Arel_Nodes_SelectCore(x, c) @@ -95,7 +95,7 @@ def visit_Arel_Nodes_JoinSource o, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector end if o.right.any? - collector << SPACE if o.left + collector << " " if o.left collector = inject_join o.right, collector, ' ' end collector @@ -106,7 +106,7 @@ def visit_Arel_Nodes_InnerJoin o, collector collector = visit o.left, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true if o.right - collector << SPACE + collector << " " visit(o.right, collector) else collector @@ -117,7 +117,7 @@ def visit_Arel_Nodes_OuterJoin o, collector collector << "LEFT OUTER JOIN " collector = visit o.left, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true - collector << SPACE + collector << " " visit o.right, collector end @@ -126,7 +126,7 @@ def visit_Arel_Nodes_OuterJoin o, collector def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} if select_statement_lock? collector = visit @select_statement.lock, collector - collector << SPACE if options[:space] + collector << " " if options[:space] end collector end @@ -134,12 +134,12 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} def visit_Orders_And_Let_Fetch_Happen o, collector make_Fetch_Possible_And_Deterministic o unless o.orders.empty? - collector << SPACE - collector << ORDER_BY + collector << " " + collector << " ORDER BY " len = o.orders.length - 1 o.orders.each_with_index { |x, i| collector = visit(x, collector) - collector << COMMA unless len == i + collector << ", " unless len == i } end collector From 0f76d923c8aec6dc4682a4bf01be603b272091cf Mon Sep 17 00:00:00 2001 From: Jens Nockert Date: Fri, 5 Jul 2019 10:57:19 +0200 Subject: [PATCH 0790/1412] Fix concatenate syntax --- lib/arel/visitors/sqlserver.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 84c0e601c..822231262 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -22,6 +22,12 @@ def visit_Arel_Nodes_Bin o, collector collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} " end + def visit_Arel_Nodes_Concat(o, collector) + visit o.left, collector + collector << " + " + visit o.right, collector + end + def visit_Arel_Nodes_UpdateStatement(o, a) if o.orders.any? && o.limit.nil? o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) From 3be1cb8d781772d8c22b092aa95733579055609e Mon Sep 17 00:00:00 2001 From: Roberto Vasquez Angel Date: Mon, 4 Nov 2019 10:05:34 +0100 Subject: [PATCH 0791/1412] Adapt to changed method signature in Rails 6.0. --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- lib/active_record/connection_adapters/sqlserver_column.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b01f8bf69..6ad269b4c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -223,7 +223,7 @@ def newsequentialid_function protected - def sql_for_insert(sql, pk, sequence_name, binds) + def sql_for_insert(sql, pk, binds) if pk.nil? table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 6644a0ca1..07a2eb8d0 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -4,7 +4,7 @@ class SQLServerColumn < Column def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) @sqlserver_options = sqlserver_options || {} - super(name, default, sql_type_metadata, null, table_name, default_function, collation, comment: comment) + super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment) end def is_identity? From ec9d711531bf601a7ebda486cbb8c77117790d6e Mon Sep 17 00:00:00 2001 From: takanamito Date: Sun, 12 Jan 2020 17:54:15 +0900 Subject: [PATCH 0792/1412] Use rails backtrace in tests --- test/cases/helper_sqlserver.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 677c40bf7..028648c37 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -25,6 +25,7 @@ class TestCase < ActiveSupport::TestCase let(:logger) { ActiveRecord::Base.logger } setup :ensure_clean_rails_env + setup :remove_backtrace_silencers private @@ -32,6 +33,10 @@ def ensure_clean_rails_env Rails.instance_variable_set(:@_env, nil) if defined?(::Rails) end + def remove_backtrace_silencers + Rails.backtrace_cleaner.remove_silencers! + end + def host_windows? RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end From 046f820b0e226f088cd537f788c0da668b5c6dcd Mon Sep 17 00:00:00 2001 From: takanamito Date: Sun, 12 Jan 2020 18:14:51 +0900 Subject: [PATCH 0793/1412] Prepare for Minitest6 --- test/cases/adapter_test_sqlserver.rb | 36 +- .../change_column_null_test_sqlserver.rb | 8 +- test/cases/column_test_sqlserver.rb | 908 +++++++++--------- test/cases/connection_test_sqlserver.rb | 4 +- test/cases/fetch_test_sqlserver.rb | 10 +- test/cases/json_test_sqlserver.rb | 12 +- test/cases/migration_test_sqlserver.rb | 4 +- .../pessimistic_locking_test_sqlserver.rb | 18 +- test/cases/rake_test_sqlserver.rb | 40 +- test/cases/schema_dumper_test_sqlserver.rb | 66 +- test/cases/schema_test_sqlserver.rb | 4 +- test/cases/showplan_test_sqlserver.rb | 28 +- test/cases/specific_schema_test_sqlserver.rb | 22 +- test/cases/transaction_test_sqlserver.rb | 18 +- test/cases/trigger_test_sqlserver.rb | 16 +- test/cases/utils_test_sqlserver.rb | 72 +- test/cases/uuid_test_sqlserver.rb | 16 +- 17 files changed, 641 insertions(+), 641 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 34d75bce7..628a54140 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -15,14 +15,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it 'has basic and non-senstive information in the adpaters inspect method' do string = connection.inspect - string.must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} - string.must_match %r{version\: \d.\d} - string.must_match %r{mode: dblib} - string.must_match %r{azure: (true|false)} - string.wont_match %r{host} - string.wont_match %r{password} - string.wont_match %r{username} - string.wont_match %r{port} + _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} + _(string).must_match %r{version\: \d.\d} + _(string).must_match %r{mode: dblib} + _(string).must_match %r{azure: (true|false)} + _(string).wont_match %r{host} + _(string).wont_match %r{password} + _(string).wont_match %r{username} + _(string).wont_match %r{port} end it 'has a 128 max #table_alias_length' do @@ -161,7 +161,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'return an empty array when calling #identity_columns for a table_name with no identity' do - connection.send(:identity_columns, Subscriber.table_name).must_equal [] + _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end end @@ -303,7 +303,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'find SSTestCustomersView table name' do - connection.views.must_include 'sst_customers_view' + _(connection.views).must_include 'sst_customers_view' end it 'work with dynamic finders' do @@ -344,9 +344,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'find identity column' do - SSTestCustomersView.primary_key.must_equal 'id' - connection.primary_key(SSTestCustomersView.table_name).must_equal 'id' - SSTestCustomersView.columns_hash['id'].must_be :is_identity? + _(SSTestCustomersView.primary_key).must_equal 'id' + _(connection.primary_key(SSTestCustomersView.table_name)).must_equal 'id' + _(SSTestCustomersView.columns_hash['id']).must_be :is_identity? end it 'find default values' do @@ -371,9 +371,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it 'find identity column' do - SSTestStringDefaultsView.primary_key.must_equal 'id' - connection.primary_key(SSTestStringDefaultsView.table_name).must_equal 'id' - SSTestStringDefaultsView.columns_hash['id'].must_be :is_identity? + _(SSTestStringDefaultsView.primary_key).must_equal 'id' + _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal 'id' + _(SSTestStringDefaultsView.columns_hash['id']).must_be :is_identity? end it 'find default values' do @@ -422,8 +422,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it 'in_memory_oltp' do if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp? - SSTMemory.primary_key.must_equal 'id' - SSTMemory.columns_hash['id'].must_be :is_identity? + _(SSTMemory.primary_key).must_equal 'id' + _(SSTMemory.columns_hash['id']).must_be :is_identity? else skip 'supports_in_memory_oltp? => false' end diff --git a/test/cases/change_column_null_test_sqlserver.rb b/test/cases/change_column_null_test_sqlserver.rb index 60fcf1077..32ef027f9 100644 --- a/test/cases/change_column_null_test_sqlserver.rb +++ b/test/cases/change_column_null_test_sqlserver.rb @@ -24,19 +24,19 @@ def find_column(table, name) describe '#change_column_null' do it 'does not change the column limit' do - name_column.limit.must_equal 15 + _(name_column.limit).must_equal 15 end it 'does not change the column default' do - code_column.default.must_equal 'n/a' + _(code_column.default).must_equal 'n/a' end it 'does not change the column precision' do - value_column.precision.must_equal 32 + _(value_column.precision).must_equal 32 end it 'does not change the column scale' do - value_column.scale.must_equal 8 + _(value_column.scale).must_equal 8 end end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index e3f869135..796470436 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -18,9 +18,9 @@ def new_obj ; SSTestDatatype.new ; end def column(name) ; SSTestDatatype.columns_hash[name] ; end def assert_obj_set_and_save(attribute, value) obj.send :"#{attribute}=", value - obj.send(attribute).must_equal value + _(obj.send(attribute)).must_equal value obj.save! - obj.reload.send(attribute).must_equal value + _(obj.reload.send(attribute)).must_equal value end # http://msdn.microsoft.com/en-us/library/ms187752.aspx @@ -29,199 +29,199 @@ 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::BigInteger - type.limit.must_equal 8 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Integer - type.limit.must_equal 4 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::SmallInteger - type.limit.must_equal 2 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::TinyInteger - type.limit.must_equal 1 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Boolean - type.limit.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Boolean + _(type.limit).must_be_nil obj.bit = 0 - obj.bit.must_equal false + _(obj.bit).must_equal false obj.save! - obj.reload.bit.must_equal false + _(obj.reload.bit).must_equal false obj.bit = '1' - obj.bit.must_equal true + _(obj.bit).must_equal true obj.save! - obj.reload.bit.must_equal true + _(obj.reload.bit).must_equal true end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Decimal - type.limit.must_be_nil - type.precision.must_equal 9 - type.scale.must_equal 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.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Decimal + _(type.limit).must_be_nil + _(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.decimal_9_2).must_equal BigDecimal('1234567.89') obj.save! - obj.reload.decimal_9_2.must_equal BigDecimal('1234567.89') + _(obj.reload.decimal_9_2).must_equal BigDecimal('1234567.89') end 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.default_function.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) - 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.decimal_16_4).must_equal BigDecimal('1234567.8901') obj.save! - obj.reload.decimal_16_4.must_equal BigDecimal('1234567.8901') + _(obj.reload.decimal_16_4).must_equal BigDecimal('1234567.8901') end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Decimal - type.limit.must_be_nil - type.precision.must_equal 18 - type.scale.must_equal 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.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Decimal + _(type.limit).must_be_nil + _(type.precision).must_equal 18 + _(type.scale).must_equal 0 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Decimal - type.limit.must_be_nil - type.precision.must_equal 36 - type.scale.must_equal 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.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Decimal + _(type.limit).must_be_nil + _(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.numeric_36_2).must_equal BigDecimal('192.12') obj.save! - obj.reload.numeric_36_2.must_equal BigDecimal('192.12') + _(obj.reload.numeric_36_2).must_equal BigDecimal('192.12') end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Money - type.limit.must_be_nil - type.precision.must_equal 19 - type.scale.must_equal 4 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Money + _(type.limit).must_be_nil + _(type.precision).must_equal 19 + _(type.scale).must_equal 4 obj.money = '922337203685477.58061' - obj.money.must_equal BigDecimal('922337203685477.5806') + _(obj.money).must_equal BigDecimal('922337203685477.5806') obj.save! - obj.reload.money.must_equal BigDecimal('922337203685477.5806') + _(obj.reload.money).must_equal BigDecimal('922337203685477.5806') end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::SmallMoney - type.limit.must_be_nil - type.precision.must_equal 10 - type.scale.must_equal 4 + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::SmallMoney + _(type.limit).must_be_nil + _(type.precision).must_equal 10 + _(type.scale).must_equal 4 obj.smallmoney = '214748.36461' - obj.smallmoney.must_equal BigDecimal('214748.3646') + _(obj.smallmoney).must_equal BigDecimal('214748.3646') obj.save! - obj.reload.smallmoney.must_equal BigDecimal('214748.3646') + _(obj.reload.smallmoney).must_equal BigDecimal('214748.3646') end # Approximate Numerics @@ -230,386 +230,386 @@ 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Float - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Float + _(type.limit).must_be_nil + _(type.precision).must_be_nil + _(type.scale).must_be_nil obj.float = '214748.36461' - obj.float.must_equal 214748.36461 + _(obj.float).must_equal 214748.36461 obj.save! - obj.reload.float.must_equal 214748.36461 + _(obj.reload.float).must_equal 214748.36461 end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Real - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Real + _(type.limit).must_be_nil + _(type.precision).must_be_nil + _(type.scale).must_be_nil obj.real = '214748.36461' - obj.real.must_be_close_to 214748.36461, 0.01 + _(obj.real).must_be_close_to 214748.36461, 0.01 obj.save! - obj.reload.real.must_be_close_to 214748.36461, 0.01 + _(obj.reload.real).must_be_close_to 214748.36461, 0.01 end # Date and Time 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 connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' - obj.date.must_equal Date.civil(0001, 1, 1) - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Date - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(col.sql_type).must_equal 'date' + _(col.type).must_equal :date + _(col.null).must_equal true + _(col.default).must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' + _(obj.date).must_equal Date.civil(0001, 1, 1) + _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Date + _(type.limit).must_be_nil + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Can cast strings. SQL Server format. obj.date = '04-01-0001' - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) # Can cast strings. ISO format. obj.date = '0001-04-01' - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload - obj.date.must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) # Can keep and return assigned date. assert_obj_set_and_save :date, Date.civil(1972, 04, 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) + _(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, 01, 01, 00, 00, 00, 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 + _(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 = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::DateTime - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(type).must_be_instance_of Type::DateTime + _(type.limit).must_be_nil + _(type.precision).must_be_nil + _(type.scale).must_be_nil obj.save! - obj.must_equal obj.class.where(datetime: time).first + _(obj).must_equal obj.class.where(datetime: time).first # Can save to proper accuracy and return again. time = Time.utc 2010, 04, 01, 12, 34, 56, 3000 obj.datetime = time - obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.reload - obj.datetime.must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" - obj.must_equal obj.class.where(datetime: time).first + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj).must_equal obj.class.where(datetime: time).first # Will cast to true DB value on attribute write, save and return again. time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 obj.datetime = time - obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.save! - obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.reload - obj.datetime.must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" - obj.must_equal obj.class.where(datetime: time).first - obj.must_equal obj.class.where(datetime: time2).first + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj).must_equal obj.class.where(datetime: time).first + _(obj).must_equal obj.class.where(datetime: time2).first # Set and find nil. obj.datetime = nil - obj.datetime.must_be_nil + _(obj.datetime).must_be_nil obj.save! - obj.datetime.must_be_nil - obj.must_equal obj.class.where(datetime: nil).first + _(obj.datetime).must_be_nil + _(obj).must_equal obj.class.where(datetime: nil).first end it 'datetime2' do skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? 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_function.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::DateTime2 - type.limit.must_be_nil - type.precision.must_equal 7 - type.scale.must_be_nil + _(type).must_be_instance_of Type::DateTime2 + _(type.limit).must_be_nil + _(type.precision).must_equal 7 + _(type.scale).must_be_nil obj.save! - obj.must_equal obj.class.where(datetime2_7: time).first + _(obj).must_equal obj.class.where(datetime2_7: time).first # Can save 100 nanosecond precisoins and return again. time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) time2 = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetime2_7 = time - obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! - obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.reload - obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" - obj.must_equal obj.class.where(datetime2_7: time).first - obj.must_equal obj.class.where(datetime2_7: time2).first + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj).must_equal obj.class.where(datetime2_7: time).first + _(obj).must_equal obj.class.where(datetime2_7: time2).first # Can save small fraction nanosecond precisoins and return again. time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) time2 = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15000, 1000) obj.datetime2_7 = time - obj.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" obj.save! - obj.reload.datetime2_7.must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" - obj.must_equal obj.class.where(datetime2_7: time).first - obj.must_equal obj.class.where(datetime2_7: time2).first + _(obj.reload.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + _(obj).must_equal obj.class.where(datetime2_7: time).first + _(obj).must_equal obj.class.where(datetime2_7: time2).first # datetime2_3 time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column('datetime2_3') - connection.lookup_cast_type_from_column(col).precision.must_equal 3 + _(connection.lookup_cast_type_from_column(col).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.datetime2_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetime2_3.nsec}> vs <123000000>" 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 + _(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 col = column('datetime2_1') - connection.lookup_cast_type_from_column(col).precision.must_equal 1 + _(connection.lookup_cast_type_from_column(col).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.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" 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 + _(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 col = column('datetime2_0') - connection.lookup_cast_type_from_column(col).precision.must_equal 0 + _(connection.lookup_cast_type_from_column(col).precision).must_equal 0 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.datetime2_0).must_equal time.change(nsec: 0), "Nanoseconds were <#{obj.datetime2_0.nsec}> vs <0>" 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 + _(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 skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? 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, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" - obj.datetimeoffset_7.must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::DateTimeOffset - type.limit.must_be_nil - type.precision.must_equal 7 - type.scale.must_be_nil + _(col.sql_type).must_equal 'datetimeoffset(7)' + _(col.type).must_equal :datetimeoffset + _(col.null).must_equal true + _(col.default).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" + _(obj.datetimeoffset_7).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" + _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::DateTimeOffset + _(type.limit).must_be_nil + _(type.precision).must_equal 7 + _(type.scale).must_be_nil # Can save 100 nanosecond precisoins and return again. obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755) - obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.save! - obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload - obj.datetimeoffset_7.must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # Maintains the timezone time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetimeoffset_7 = time - obj.datetimeoffset_7.must_equal time + _(obj.datetimeoffset_7).must_equal time obj.save! - obj.datetimeoffset_7.must_equal time - obj.reload.datetimeoffset_7.must_equal time + _(obj.datetimeoffset_7).must_equal time + _(obj.reload.datetimeoffset_7).must_equal time # With other precisions. time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column('datetimeoffset_3') - connection.lookup_cast_type_from_column(col).precision.must_equal 3 + _(connection.lookup_cast_type_from_column(col).precision).must_equal 3 obj.datetimeoffset_3 = time - obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" obj.save! - obj.datetimeoffset_3.must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" + _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column('datetime2_1') - connection.lookup_cast_type_from_column(col).precision.must_equal 1 + _(connection.lookup_cast_type_from_column(col).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.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" obj.save! - obj.datetime2_1.must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" + _(obj.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" end 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, 01, 01, 15, 45, 00, 000) - obj.smalldatetime.must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::SmallDateTime - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(col.sql_type).must_equal 'smalldatetime' + _(col.type).must_equal :smalldatetime + _(col.null).must_equal true + _(col.default).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) + _(obj.smalldatetime).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) + _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::SmallDateTime + _(type.limit).must_be_nil + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Will remove fractional seconds and return again. obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) - obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" obj.save! - obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" obj.reload - obj.smalldatetime.must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end it 'time(7)' do skip 'time() not supported in this protocal version' unless connection_dblib_73? 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, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Time - type.limit.must_be_nil - type.precision.must_equal 7 - type.scale.must_be_nil + _(col.sql_type).must_equal 'time(7)' + _(col.type).must_equal :time + _(col.null).must_equal true + _(col.default).must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" + _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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, 01, 01, 15, 45, 00, 300) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" obj.save! ; obj.reload - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" # Time's #usec precision (high micro) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" obj.save! ; obj.reload - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" # Time's #usec precision (high nano rounded) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" obj.save! ; obj.reload - obj.time_7.must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" end it 'time(2)' do skip 'time() not supported in this protocal version' unless connection_dblib_73? col = column('time_2') - 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 = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Time - type.limit.must_be_nil - type.precision.must_equal 2 - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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, 01, 10, 15, 45, 00, 0) - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) obj.save! ; obj.reload - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000) - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" obj.save! ; obj.reload - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" # Time's #usec precision (below 2 precision) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000) - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" obj.save! ; obj.reload - obj.time_2.must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end # Character Strings 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Char - type.limit.must_equal 10 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Char + _(type.limit).must_equal 10 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. obj.char_10 = '012345' - obj.char_10.strip.must_equal '012345' + _(obj.char_10.strip).must_equal '012345' obj.save! - obj.reload.char_10.strip.must_equal '012345' + _(obj.reload.char_10.strip).must_equal '012345' end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Varchar - type.limit.must_equal 50 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Varchar + _(type.limit).must_equal 50 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :varchar_50, 'Hello World' end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::VarcharMax - type.limit.must_equal 2_147_483_647 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::VarcharMax + _(type.limit).must_equal 2_147_483_647 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :varchar_max, 'Hello World' end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Text - type.limit.must_equal 2_147_483_647 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Text + _(type.limit).must_equal 2_147_483_647 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :text, 'Hello World' end @@ -618,71 +618,71 @@ 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::UnicodeChar - type.limit.must_equal 10 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::UnicodeChar + _(type.limit).must_equal 10 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. obj.nchar_10 = "五六" - obj.nchar_10.strip.must_equal "五六" + _(obj.nchar_10.strip).must_equal "五六" obj.save! - obj.reload.nchar_10.strip.must_equal "五六" + _(obj.reload.nchar_10.strip).must_equal "五六" end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::UnicodeVarchar - type.limit.must_equal 50 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::UnicodeVarchar + _(type.limit).must_equal 50 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :nvarchar_50, "一二34五六" end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::UnicodeVarcharMax - type.limit.must_equal 2_147_483_647 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::UnicodeVarcharMax + _(type.limit).must_equal 2_147_483_647 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :nvarchar_max, "一二34五六" end 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.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::UnicodeText - type.limit.must_equal 2_147_483_647 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::UnicodeText + _(type.limit).must_equal 2_147_483_647 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. assert_obj_set_and_save :ntext, "一二34五六" end @@ -694,60 +694,60 @@ def assert_obj_set_and_save(attribute, value) 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.default.must_be_nil - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Binary - type.limit.must_equal 49 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Binary + _(type.limit).must_equal 49 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. - binary_data.encoding.must_equal Encoding::BINARY - binary_data.length.must_equal 49 + _(binary_data.encoding).must_equal Encoding::BINARY + _(binary_data.length).must_equal 49 obj.binary_49 = binary_data - obj.binary_49.must_equal binary_data + _(obj.binary_49).must_equal binary_data obj.save! - obj.reload.binary_49.must_equal binary_data + _(obj.reload.binary_49).must_equal binary_data end 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.default.must_be_nil - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Varbinary - type.limit.must_equal 49 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::Varbinary + _(type.limit).must_equal 49 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. binary_data_20 = binary_data.to(20) - binary_data_20.encoding.must_equal Encoding::BINARY + _(binary_data_20.encoding).must_equal Encoding::BINARY obj.varbinary_49 = binary_data_20 - obj.varbinary_49.must_equal binary_data_20 + _(obj.varbinary_49).must_equal binary_data_20 obj.save! - obj.reload.varbinary_49.must_equal binary_data_20 + _(obj.reload.varbinary_49).must_equal binary_data_20 end 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.default.must_be_nil - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::VarbinaryMax - type.limit.must_equal 2_147_483_647 - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of Type::VarbinaryMax + _(type.limit).must_equal 2_147_483_647 + _(type.precision).must_be_nil + _(type.scale).must_be_nil # Basic set and save. - binary_data.encoding.must_equal Encoding::BINARY + _(binary_data.encoding).must_equal Encoding::BINARY assert_obj_set_and_save :varbinary_max, binary_data end @@ -755,43 +755,43 @@ 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.default.must_be_nil - col.default_function.must_equal 'newid()' - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Uuid - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.uniqueidentifier).must_be_nil obj.save! ; obj.reload - obj.uniqueidentifier.must_match Type::Uuid::ACCEPTABLE_UUID + _(obj.uniqueidentifier).must_match Type::Uuid::ACCEPTABLE_UUID obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" - obj.uniqueidentifier.must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" + _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" obj.save! ; obj.reload - obj.uniqueidentifier.must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" + _(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.default.must_be_nil - col.default_function.must_be_nil - type = connection.lookup_cast_type_from_column(col) - type.must_be_instance_of Type::Timestamp - type.limit.must_be_nil - type.precision.must_be_nil - type.scale.must_be_nil + _(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 = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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.timestamp).must_be_nil obj.save! ; obj.reload - obj.timestamp.must_match %r|\000| + _(obj.timestamp).must_match %r|\000| obj.timestamp # Can set another attribute obj.uniqueidentifier = "6F9619FF-8B86-D011-B42D-00C04FC964FF" @@ -801,7 +801,7 @@ def assert_obj_set_and_save(attribute, value) it 'does not mark object as changed after save' do obj.save! obj.attributes - obj.changed?.must_equal false + _(obj.changed?).must_equal false end end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 656c42fd4..e4c1ed55b 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -36,7 +36,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase describe 'Connection management' do it 'set spid on connect' do - ['Fixnum', 'Integer'].must_include connection.spid.class.name + _(['Fixnum', 'Integer']).must_include connection.spid.class.name end it 'reset spid on disconnect!' do @@ -46,7 +46,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase it 'reset the connection' do connection.disconnect! - connection.raw_connection.must_be_nil + _(connection.raw_connection).must_be_nil end it 'be able to disconnect and reconnect at will' do diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 787185d22..48e7ba2e5 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -36,11 +36,11 @@ class FetchTestSqlserver < ActiveRecord::TestCase it 'gauntlet' do Book.where(name:'Name-10').delete_all - Book.order(:name).limit(1).offset(1).map(&:name).must_equal ['Name-2'] - Book.order(:name).limit(2).offset(2).map(&:name).must_equal ['Name-3', 'Name-4'] - Book.order(:name).limit(2).offset(7).map(&:name).must_equal ['Name-8', 'Name-9'] - Book.order(:name).limit(3).offset(7).map(&:name).must_equal ['Name-8', 'Name-9'] - Book.order(:name).limit(3).offset(9).map(&:name).must_equal [] + _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ['Name-2'] + _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ['Name-3', 'Name-4'] + _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9'] + _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9'] + _(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal [] end end diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index 37168e785..664c8327c 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -12,20 +12,20 @@ class JsonTestSQLServer < ActiveRecord::TestCase end it 'can return and save JSON data' do - SSTestDatatypeMigrationJson.find(@o1.id).json_col.must_equal({ 'a' => 'a', 'b' => 'b', 'c' => 'c' }) + _(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.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 - SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count.must_equal 4 - SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count.must_equal 1 + _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count).must_equal 4 + _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count).must_equal 1 end it 'can use JSON_VALUE function' do - SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count.must_equal 2 + _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2 end end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index fb059ddee..088024df3 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -24,8 +24,8 @@ class MigrationTestSQLServer < ActiveRecord::TestCase rescue Exception => 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 + _(connection.tables).wont_include @trans_test_table1 + _(connection.tables).wont_include @trans_test_table2 end end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 96649e087..0e3e44fd4 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 it 'uses with updlock by default' do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do - Person.lock(true).to_a.must_equal Person.all.to_a + _(Person.lock(true).to_a).must_equal Person.all.to_a end end @@ -22,7 +22,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase it 'lock with simple find' do assert_nothing_raised do Person.transaction do - Person.lock(true).find(1).must_equal Person.find(1) + _(Person.lock(true).find(1)).must_equal Person.find(1) end end end @@ -31,7 +31,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase assert_nothing_raised do Person.transaction do Person.lock(true).scoping do - Person.find(1).must_equal Person.find(1) + _(Person.find(1)).must_equal Person.find(1) end end end @@ -41,7 +41,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase assert_nothing_raised do Person.transaction do person = Person.lock(true).includes(:readers).find(1) - person.must_equal Person.find(1) + _(person).must_equal Person.find(1) end end end @@ -94,11 +94,11 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ assert_sql(eager_ids_sql, loader_sql) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a - people[0].first_name.must_equal 'Thing_10' - people[1].first_name.must_equal 'Thing_11' - people[2].first_name.must_equal 'Thing_12' - people[3].first_name.must_equal 'Thing_13' - people[4].first_name.must_equal 'Thing_14' + _(people[0].first_name).must_equal 'Thing_10' + _(people[1].first_name).must_equal 'Thing_11' + _(people[2].first_name).must_equal 'Thing_12' + _(people[3].first_name).must_equal 'Thing_13' + _(people[4].first_name).must_equal 'Thing_14' end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 09b2bf664..366ece3e4 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -43,23 +43,23 @@ class SQLServerRakeCreateTest < SQLServerRakeTest it 'establishes connection to database after create ' do quietly { db_tasks.create configuration } - connection.current_database.must_equal(new_database) + _(connection.current_database).must_equal(new_database) end it 'creates database with default collation' do quietly { db_tasks.create configuration } - connection.collation.must_equal 'SQL_Latin1_General_CP1_CI_AS' + _(connection.collation).must_equal 'SQL_Latin1_General_CP1_CI_AS' end it 'creates database with given collation' do quietly { db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') } - connection.collation.must_equal 'Latin1_General_CI_AS' + _(connection.collation).must_equal 'Latin1_General_CI_AS' end it 'prints error message when database exists' do quietly { db_tasks.create configuration } message = capture(:stderr) { db_tasks.create configuration } - message.must_match %r{activerecord_unittest_tasks.*already exists} + _(message).must_match %r{activerecord_unittest_tasks.*already exists} end end @@ -73,12 +73,12 @@ class SQLServerRakeDropTest < SQLServerRakeTest db_tasks.create configuration db_tasks.drop configuration end - connection.current_database.must_equal 'master' + _(connection.current_database).must_equal 'master' end it 'prints error message when database does not exist' do message = capture(:stderr) { db_tasks.drop configuration.merge('database' => 'doesnotexist') } - message.must_match %r{'doesnotexist' does not exist} + _(message).must_match %r{'doesnotexist' does not exist} end end @@ -94,11 +94,11 @@ class SQLServerRakePurgeTest < SQLServerRakeTest end it 'clears active connections, drops database, and recreates with established connection' do - connection.current_database.must_equal(new_database) - connection.tables.must_include 'users' + _(connection.current_database).must_equal(new_database) + _(connection.tables).must_include 'users' quietly { db_tasks.purge(configuration) } - connection.current_database.must_equal(new_database) - connection.tables.wont_include 'users' + _(connection.current_database).must_equal(new_database) + _(connection.tables).wont_include 'users' end end @@ -110,7 +110,7 @@ class SQLServerRakeCharsetTest < SQLServerRakeTest end it 'retrieves charset' do - db_tasks.charset(configuration).must_equal 'iso_1' + _(db_tasks.charset(configuration)).must_equal 'iso_1' end end @@ -122,7 +122,7 @@ class SQLServerRakeCollationTest < SQLServerRakeTest end it 'retrieves collation' do - db_tasks.collation(configuration).must_equal 'SQL_Latin1_General_CP1_CI_AS' + _(db_tasks.collation(configuration)).must_equal 'SQL_Latin1_General_CP1_CI_AS' end end @@ -149,21 +149,21 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it 'dumps structure and accounts for defncopy oddities' do skip 'debug defncopy on windows later' if host_windows? quietly { db_tasks.structure_dump configuration, filename } - filedata.wont_match %r{\AUSE.*\z} - filedata.wont_match %r{\AGO.*\z} - filedata.must_match %r{email\s+nvarchar\(4000\)} - filedata.must_match %r{background1\s+nvarchar\(max\)} - filedata.must_match %r{background2\s+text\s+} + _(filedata).wont_match %r{\AUSE.*\z} + _(filedata).wont_match %r{\AGO.*\z} + _(filedata).must_match %r{email\s+nvarchar\(4000\)} + _(filedata).must_match %r{background1\s+nvarchar\(max\)} + _(filedata).must_match %r{background2\s+text\s+} end it 'can load dumped structure' do skip 'debug defncopy on windows later' if host_windows? quietly { db_tasks.structure_dump configuration, filename } - filedata.must_match %r{CREATE TABLE dbo\.users} + _(filedata).must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) - connection.tables.wont_include 'users' + _(connection.tables).wont_include 'users' db_tasks.load_schema configuration, :sql, filename - connection.tables.must_include 'users' + _(connection.tables).must_include 'users' end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 950c85b62..3da17ca1c 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -58,18 +58,18 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase columns = SSTestDatatypeMigration.columns_hash 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['datetime_col'].sql_type.must_equal 'datetime' - columns['timestamp_col'].sql_type.must_equal 'datetime' - 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)' + _(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_col'].sql_type).must_equal 'datetime' + _(columns['timestamp_col'].sql_type).must_equal 'datetime' + _(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', limit: nil, precision: nil, scale: nil, default: nil assert_line :bigint_col, type: 'bigint', limit: nil, precision: nil, scale: nil, default: nil assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil @@ -83,22 +83,22 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil assert_line :binary_col, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil # Our type methods. - columns['real_col'].sql_type.must_equal 'real' - columns['money_col'].sql_type.must_equal 'money' - columns['smalldatetime_col'].sql_type.must_equal 'smalldatetime' - columns['datetime2_col'].sql_type.must_equal 'datetime2(7)' - columns['datetimeoffset'].sql_type.must_equal 'datetimeoffset(7)' - columns['smallmoney_col'].sql_type.must_equal 'smallmoney' - columns['char_col'].sql_type.must_equal 'char(1)' - columns['varchar_col'].sql_type.must_equal 'varchar(8000)' - columns['text_basic_col'].sql_type.must_equal 'text' - columns['nchar_col'].sql_type.must_equal 'nchar(1)' - columns['ntext_col'].sql_type.must_equal 'ntext' - columns['binary_basic_col'].sql_type.must_equal 'binary(1)' - columns['varbinary_col'].sql_type.must_equal 'varbinary(8000)' - columns['uuid_col'].sql_type.must_equal 'uniqueidentifier' - columns['sstimestamp_col'].sql_type.must_equal 'timestamp' - columns['json_col'].sql_type.must_equal 'nvarchar(max)' + _(columns['real_col'].sql_type).must_equal 'real' + _(columns['money_col'].sql_type).must_equal 'money' + _(columns['smalldatetime_col'].sql_type).must_equal 'smalldatetime' + _(columns['datetime2_col'].sql_type).must_equal 'datetime2(7)' + _(columns['datetimeoffset'].sql_type).must_equal 'datetimeoffset(7)' + _(columns['smallmoney_col'].sql_type).must_equal 'smallmoney' + _(columns['char_col'].sql_type).must_equal 'char(1)' + _(columns['varchar_col'].sql_type).must_equal 'varchar(8000)' + _(columns['text_basic_col'].sql_type).must_equal 'text' + _(columns['nchar_col'].sql_type).must_equal 'nchar(1)' + _(columns['ntext_col'].sql_type).must_equal 'ntext' + _(columns['binary_basic_col'].sql_type).must_equal 'binary(1)' + _(columns['varbinary_col'].sql_type).must_equal 'varbinary(8000)' + _(columns['uuid_col'].sql_type).must_equal 'uniqueidentifier' + _(columns['sstimestamp_col'].sql_type).must_equal 'timestamp' + _(columns['json_col'].sql_type).must_equal 'nvarchar(max)' assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil assert_line :smalldatetime_col, type: 'smalldatetime', limit: nil, precision: nil, scale: nil, default: nil @@ -129,7 +129,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it 'no id with model driven primary key' do output = generate_schema_for_table 'sst_no_pk_data' - output.must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} + _(output).must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} assert_line :name, type: 'string', limit: nil, default: nil, collation: nil end @@ -165,15 +165,15 @@ def assert_line(column_name, options={}) expected = options[key] message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" if expected.nil? - actual.must_be_nil message + _(actual).must_be_nil message elsif expected.is_a?(Array) actual.must_include expected, message elsif expected.is_a?(Float) - actual.must_be_close_to expected, 0.001 + _(actual).must_be_close_to expected, 0.001 elsif expected.is_a?(Proc) - actual.call.must_equal(expected.call) + _(actual.call).must_equal(expected.call) else - actual.must_equal expected, message + _(actual).must_equal expected, message end end end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index a2887ca2e..5986b5b06 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -5,7 +5,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase describe 'When table is dbo schema' do it 'find primary key for tables with odd schema' do - connection.primary_key('sst_natural_pk_data').must_equal 'legacy_id' + _(connection.primary_key('sst_natural_pk_data')).must_equal 'legacy_id' end end @@ -18,7 +18,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end it 'find primary key for tables with odd schema' do - connection.primary_key('test.sst_schema_natural_id').must_equal 'legacy_id' + _(connection.primary_key('test.sst_schema_natural_id')).must_equal 'legacy_id' end it "have only one identity column" do diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 9e4afb3cd..848f6f151 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -9,33 +9,33 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it 'from simple statement' do plan = Car.where(id: 1).explain - plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" - plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" + _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end it 'from multiline statement' do plan = Car.where("\n id = 1 \n").explain - plan.must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)" - plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)" + _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end it 'from prepared statement' do plan = Car.where(name: ',').limit(1).explain - plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" - plan.must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' - plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' + _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" + _(plan).must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' end it 'from array condition using index' do plan = Car.where(id: [1, 2]).explain - plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" - plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" + _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end it 'from array condition' do plan = Car.where(name: ['honda', 'zyke']).explain - plan.must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" - plan.must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' + _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" + _(plan).must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' end end @@ -45,8 +45,8 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it 'use simple table printer' do with_showplan_option('SHOWPLAN_TEXT') do plan = Car.where(id: 1).explain - plan.must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" - plan.must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" + _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end end @@ -57,7 +57,7 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it 'show formatted xml' do with_showplan_option('SHOWPLAN_XML') do plan = Car.where(id: 1).explain - plan.must_include 'ShowPlanXML' + _(plan).must_include 'ShowPlanXML' end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 4f106385d..9e0659227 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -11,8 +11,8 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'models can use tinyint pk tables' do obj = SSTestTinyintPk.create! name: '1' - ['Fixnum', 'Integer'].must_include obj.id.class.name - SSTestTinyintPk.find(obj.id).must_equal obj + _(['Fixnum', 'Integer']).must_include obj.id.class.name + _(SSTestTinyintPk.find(obj.id)).must_equal obj end it 'be able to complex count tables with no primary key' do @@ -58,17 +58,17 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it 'default objects work' do obj = SSTestObjectDefault.create! name: 'MetaSkills' - obj.date.must_be_nil 'since this is set on insert' - obj.reload.date.must_be_instance_of Date + _(obj.date).must_be_nil 'since this is set on insert' + _(obj.reload.date).must_be_instance_of Date end it 'allows datetime2 as timestamps' do - SSTestBooking.columns_hash['created_at'].sql_type.must_equal 'datetime2(7)' - SSTestBooking.columns_hash['updated_at'].sql_type.must_equal 'datetime2(7)' + _(SSTestBooking.columns_hash['created_at'].sql_type).must_equal 'datetime2(7)' + _(SSTestBooking.columns_hash['updated_at'].sql_type).must_equal 'datetime2(7)' obj1 = SSTestBooking.new name: 'test1' obj1.save! - obj1.created_at.must_be_instance_of Time - obj1.updated_at.must_be_instance_of Time + _(obj1.created_at).must_be_instance_of Time + _(obj1.updated_at).must_be_instance_of Time end # Natural primary keys. @@ -124,10 +124,10 @@ def quoted_id o = SSTestDatatypeMigration.create! o.varchar_col = "O'Reilly" o.save! - o.reload.varchar_col.must_equal "O'Reilly" + _(o.reload.varchar_col).must_equal "O'Reilly" o.varchar_col = nil o.save! - o.reload.varchar_col.must_be_nil + _(o.reload.varchar_col).must_be_nil end # With column names that have spaces @@ -156,7 +156,7 @@ def quoted_id it 'returns a new id via connection newid_function' do acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID db_uuid = ActiveRecord::Base.connection.newid_function - db_uuid.must_match(acceptable_uuid) + _(db_uuid).must_match(acceptable_uuid) end # with similar table definition in two schemas diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index e16a63a53..bca3c071e 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -34,22 +34,22 @@ class TransactionTestSQLServer < ActiveRecord::TestCase it 'can use an isolation level and reverts back to starting isolation level' do in_level = nil begin_level = connection.user_options_isolation_level - begin_level.must_match %r{read committed}i + _(begin_level).must_match %r{read committed}i Ship.transaction(isolation: :serializable) do Ship.create! name: 'Black Pearl' in_level = connection.user_options_isolation_level end after_level = connection.user_options_isolation_level - in_level.must_match %r{serializable}i - after_level.must_match %r{read committed}i + _(in_level).must_match %r{serializable}i + _(after_level).must_match %r{read committed}i end it 'can use an isolation level and reverts back to starting isolation level under exceptions' do - connection.user_options_isolation_level.must_match %r{read committed}i - lambda { + _(connection.user_options_isolation_level).must_match %r{read committed}i + _(lambda { Ship.transaction(isolation: :serializable) { Ship.create! } - }.must_raise(ActiveRecord::RecordInvalid) - connection.user_options_isolation_level.must_match %r{read committed}i + }).must_raise(ActiveRecord::RecordInvalid) + _(connection.user_options_isolation_level).must_match %r{read committed}i end describe 'when READ_COMMITTED_SNAPSHOT is set' do @@ -64,7 +64,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end it 'should use READ COMMITTED as an isolation level' do - connection.user_options_isolation_level.must_match "read committed snapshot" + _(connection.user_options_isolation_level).must_match "read committed snapshot" Ship.transaction(isolation: :serializable) do Ship.create! name: 'Black Pearl' @@ -73,7 +73,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase # We're actually testing that the isolation level was correctly reset to # "READ COMMITTED", and that no exception was raised (it's reported back # by SQL Server as "read committed snapshot"). - connection.user_options_isolation_level.must_match "read committed snapshot" + _(connection.user_options_isolation_level).must_match "read committed snapshot" end end diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index 2c77546dc..c762c709e 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -12,19 +12,19 @@ class SQLServerTriggerTest < ActiveRecord::TestCase exclude_output_inserted_table_names['sst_table_with_trigger'] = true assert SSTestTriggerHistory.all.empty? obj = SSTestTrigger.create! event_name: 'test trigger' - ['Fixnum', 'Integer'].must_include obj.id.class.name - obj.event_name.must_equal 'test trigger' - obj.id.must_be :present? - obj.id.to_s.must_equal SSTestTriggerHistory.first.id_source + _(['Fixnum', 'Integer']).must_include obj.id.class.name + _(obj.event_name).must_equal 'test trigger' + _(obj.id).must_be :present? + _(obj.id.to_s).must_equal SSTestTriggerHistory.first.id_source end it 'can insert into a table with output inserted - with a uniqueidentifier value' do exclude_output_inserted_table_names['sst_table_with_uuid_trigger'] = 'uniqueidentifier' assert SSTestTriggerHistory.all.empty? obj = SSTestTriggerUuid.create! event_name: 'test uuid trigger' - obj.id.class.name.must_equal 'String' - obj.event_name.must_equal 'test uuid trigger' - obj.id.must_be :present? - obj.id.to_s.must_equal SSTestTriggerHistory.first.id_source + _(obj.id.class.name).must_equal 'String' + _(obj.event_name).must_equal 'test uuid trigger' + _(obj.id).must_be :present? + _(obj.id.to_s).must_equal SSTestTriggerHistory.first.id_source end end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 492fdabf0..c64939384 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -3,15 +3,15 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it '.quote_string' do - SQLServer::Utils.quote_string("I'll store this in C:\\Users").must_equal "I''ll store this in C:\\Users" + _(SQLServer::Utils.quote_string("I'll store this in C:\\Users")).must_equal "I''ll store this in C:\\Users" end it '.unquote_string' do - SQLServer::Utils.unquote_string("I''ll store this in C:\\Users").must_equal "I'll store this in C:\\Users" + _(SQLServer::Utils.unquote_string("I''ll store this in C:\\Users")).must_equal "I'll store this in C:\\Users" end it '.quoted_raw' do - SQLServer::Utils.quoted_raw("some.Name").must_equal "[some.Name]" + _(SQLServer::Utils.quoted_raw("some.Name")).must_equal "[some.Name]" end describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do @@ -47,8 +47,8 @@ class UtilsTestSQLServer < ActiveRecord::TestCase it 'extracts and returns #object identifier unquoted by default or quoted as needed' do valid_names.each do |n| name = extract_identifiers(n) - name.object.must_equal 'object', "With #{n.inspect} for #object" - name.object_quoted.must_equal '[object]', "With #{n.inspect} for #object_quoted" + _(name.object).must_equal 'object', "With #{n.inspect} for #object" + _(name.object_quoted).must_equal '[object]', "With #{n.inspect} for #object_quoted" end end @@ -58,64 +58,64 @@ 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}_quoted").must_equal "[#{part}]", "With #{n.inspect} for ##{part}_quoted method" + _(name.send(:"#{part}")).must_equal "#{part}", "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| name = extract_identifiers(n) - name.send(:"#{part}").must_be_nil "With #{n.inspect} for ##{part} method" - name.send(:"#{part}_quoted").must_be_nil "With #{n.inspect} for ##{part}_quoted method" + _(name.send(:"#{part}")).must_be_nil "With #{n.inspect} for ##{part} method" + _(name.send(:"#{part}_quoted")).must_be_nil "With #{n.inspect} for ##{part}_quoted method" end end end it 'does not blow up on nil or blank string name' do - extract_identifiers(nil).object.must_be_nil - extract_identifiers(' ').object.must_be_nil + _(extract_identifiers(nil).object).must_be_nil + _(extract_identifiers(' ').object).must_be_nil end it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do - extract_identifiers('object').quoted.must_equal '[object]' - extract_identifiers('server.database..object').quoted.must_equal '[server].[database]..[object]' - extract_identifiers('[server]...[object]').quoted.must_equal '[server]...[object]' + _(extract_identifiers('object').quoted).must_equal '[object]' + _(extract_identifiers('server.database..object').quoted).must_equal '[server].[database]..[object]' + _(extract_identifiers('[server]...[object]').quoted).must_equal '[server]...[object]' end it 'can take a symbol argument' do - extract_identifiers(:object).object.must_equal 'object' + _(extract_identifiers(:object).object).must_equal 'object' end it 'allows identifiers with periods to work' do - extract_identifiers('[obj.name]').quoted.must_equal '[obj.name]' - extract_identifiers('[obj.name].[foo]').quoted.must_equal '[obj.name].[foo]' + _(extract_identifiers('[obj.name]').quoted).must_equal '[obj.name]' + _(extract_identifiers('[obj.name].[foo]').quoted).must_equal '[obj.name].[foo]' end it 'should indicate if a name is fully qualitified' do - extract_identifiers('object').fully_qualified?.must_equal false - extract_identifiers('schema.object').fully_qualified?.must_equal false - extract_identifiers('database.schema.object').fully_qualified?.must_equal false - extract_identifiers('database.object').fully_qualified?.must_equal false - extract_identifiers('server...object').fully_qualified?.must_equal false - extract_identifiers('server.database..object').fully_qualified?.must_equal false - extract_identifiers('server.database.schema.object').fully_qualified?.must_equal true - extract_identifiers('server.database.schema.').fully_qualified?.must_equal true - extract_identifiers('[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[schema].[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[database].[schema].[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[database].[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[server.name]...[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?.must_equal false - extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?.must_equal true - extract_identifiers('[server.name].[database].[schema].').fully_qualified?.must_equal true + _(extract_identifiers('object').fully_qualified?).must_equal false + _(extract_identifiers('schema.object').fully_qualified?).must_equal false + _(extract_identifiers('database.schema.object').fully_qualified?).must_equal false + _(extract_identifiers('database.object').fully_qualified?).must_equal false + _(extract_identifiers('server...object').fully_qualified?).must_equal false + _(extract_identifiers('server.database..object').fully_qualified?).must_equal false + _(extract_identifiers('server.database.schema.object').fully_qualified?).must_equal true + _(extract_identifiers('server.database.schema.').fully_qualified?).must_equal true + _(extract_identifiers('[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[schema].[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[database].[schema].[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[database].[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[server.name]...[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?).must_equal false + _(extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?).must_equal true + _(extract_identifiers('[server.name].[database].[schema].').fully_qualified?).must_equal true end it 'can return fully qualified quoted table name' do name = extract_identifiers('[my.server].db.schema.') - name.fully_qualified_database_quoted.must_equal '[my.server].[db]' + _(name.fully_qualified_database_quoted).must_equal '[my.server].[db]' name = extract_identifiers('[server.name].[database].[schema].[object]') - name.fully_qualified_database_quoted.must_equal '[server.name].[database]' + _(name.fully_qualified_database_quoted).must_equal '[server.name].[database]' name = extract_identifiers('server.database.schema.object') - name.fully_qualified_database_quoted.must_equal '[server].[database]' + _(name.fully_qualified_database_quoted).must_equal '[server].[database]' end end diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index cf99f0dd7..c97319230 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -6,24 +6,24 @@ class SQLServerUuidTest < ActiveRecord::TestCase let(:acceptable_uuid) { ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID } it 'has a uuid primary key' do - SSTestUuid.columns_hash['id'].type.must_equal :uuid + _(SSTestUuid.columns_hash['id'].type).must_equal :uuid assert SSTestUuid.primary_key end it 'can create with a new pk' do obj = SSTestUuid.create! - obj.id.must_be :present? - obj.id.must_match acceptable_uuid + _(obj.id).must_be :present? + _(obj.id).must_match acceptable_uuid end it 'can create other uuid column on reload' do obj = SSTestUuid.create! obj.reload - obj.other_uuid.must_match acceptable_uuid + _(obj.other_uuid).must_match acceptable_uuid end it 'can find uuid pk via connection' do - connection.primary_key(SSTestUuid.table_name).must_equal 'id' + _(connection.primary_key(SSTestUuid.table_name)).must_equal 'id' end it 'changing column default' do @@ -31,17 +31,17 @@ class SQLServerUuidTest < ActiveRecord::TestCase connection.add_column table_name, :thingy, :uuid, null: false, default: "NEWSEQUENTIALID()" SSTestUuid.reset_column_information column = SSTestUuid.columns_hash['thingy'] - column.default_function.must_equal "newsequentialid()" + _(column.default_function).must_equal "newsequentialid()" # Now to a different function. connection.change_column table_name, :thingy, :uuid, null: false, default: "NEWID()" SSTestUuid.reset_column_information column = SSTestUuid.columns_hash['thingy'] - column.default_function.must_equal "newid()" + _(column.default_function).must_equal "newid()" end it 'can insert even when use_output_inserted to false ' do obj = with_use_output_inserted_disabled { SSTestUuid.create!(name: "😢") } - obj.id.must_be :nil? + _(obj.id).must_be :nil? end end From 1aafcc65cf82bbca5806b9cdf3ea6b42cd7d80a1 Mon Sep 17 00:00:00 2001 From: Andrew Blignaut Date: Mon, 13 Jan 2020 15:23:30 +0200 Subject: [PATCH 0794/1412] DateTime also returns true for .acts_like?(:date) so it must be checked first --- .../connection_adapters/sqlserver/quoting.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index e577c69ee..aac075f14 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -61,14 +61,15 @@ def unquoted_false end def quoted_date(value) - if value.acts_like?(:date) - Type::Date.new.serialize(value) - else value.acts_like?(:time) + if value.acts_like?(:time) Type::DateTime.new.serialize(value) + elsif value.acts_like?(:date) + Type::Date.new.serialize(value) + else + value end end - private def _quote(value) From 339a255f9890e319a8c50552d91f46329449936d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 25 Feb 2020 11:29:27 +0000 Subject: [PATCH 0795/1412] Fix minitest expection calls --- test/cases/coerced_tests.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6bcf406b6..a75c0171c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -192,14 +192,14 @@ def test_should_return_decimal_average_of_integer_field_coerced def test_limit_is_kept_coerced queries = capture_sql_ss { Account.limit(1).count } assert_equal 1, queries.length - queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1} + _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1} end coerce_tests! :test_limit_with_offset_is_kept def test_limit_with_offset_is_kept_coerced queries = capture_sql_ss { Account.limit(1).offset(1).count } assert_equal 1, queries.length - queries.first.must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} + _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} end # SQL Server needs an alias for the calculated column @@ -265,7 +265,7 @@ class ColumnAttributesTest < ActiveRecord::TestCase def test_add_column_without_limit_coerced add_column :test_models, :description, :string, limit: nil TestModel.reset_column_information - TestModel.columns_hash["description"].limit.must_equal 4000 + _(TestModel.columns_hash["description"].limit).must_equal 4000 end end end @@ -615,14 +615,14 @@ class PersistenceTest < ActiveRecord::TestCase coerce_tests! :test_update_all_doesnt_ignore_order def test_update_all_doesnt_ignore_order_coerced david, mary = authors(:david), authors(:mary) - david.id.must_equal 1 - mary.id.must_equal 2 - david.name.wont_equal mary.name + _(david.id).must_equal 1 + _(mary.id).must_equal 2 + _(david.name).wont_equal mary.name assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do Author.where('[id] > 1').order(:id).update_all(name: 'Test') end - david.reload.name.must_equal 'David' - mary.reload.name.must_equal 'Test' + _(david.reload.name).must_equal 'David' + _(mary.reload.name).must_equal 'Test' end # We can not UPDATE identity columns. From ff79a68c5a778b0de16057bb2fd89f178056ee95 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 26 Feb 2020 14:42:06 +0000 Subject: [PATCH 0796/1412] Calculate should not remove ordering for MSSQL --- .../sqlserver/core_ext/calculations.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index cfdb65453..d2b7096c7 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -6,6 +6,25 @@ module ConnectionAdapters module SQLServer module CoreExt module Calculations + + # Same as original except we don't perform PostgreSQL hack that removes ordering. + def calculate(operation, column_name) + if has_include?(column_name) + relation = apply_join_dependency + + if operation.to_s.downcase == "count" + unless distinct_value || distinct_select?(column_name || select_for_count) + relation.distinct! + relation.select_values = [ klass.primary_key || table[Arel.star] ] + end + end + + relation.calculate(operation, column_name) + else + perform_calculation(operation, column_name) + end + end + private def build_count_subquery(relation, column_name, distinct) From c57ab31fd734477eaf0e4781fdfd4150d2a26de4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 27 Feb 2020 11:34:31 +0000 Subject: [PATCH 0797/1412] Order by selected items when using distinct exists --- .../sqlserver/core_ext/finder_methods.rb | 43 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 1 + 2 files changed, 44 insertions(+) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb 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 new file mode 100644 index 000000000..fc9ff9db3 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb @@ -0,0 +1,43 @@ +require 'active_record/relation' +require 'active_record/version' + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module FinderMethods + + private + + # Same as original except we order by values in distinct select if present. + def construct_relation_for_exists(conditions) + if distinct_value && offset_value + relation = limit!(1) + + if select_values.present? + relation = relation.order(*select_values) + else + relation = relation.except(:order) + end + else + relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1) + end + + case conditions + when Array, Hash + relation.where!(conditions) unless conditions.empty? + else + relation.where!(primary_key => conditions) unless conditions == :none + end + + relation + end + end + end + end + end +end + +ActiveSupport.on_load(:active_record) do + ActiveRecord::Relation.include(ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::FinderMethods) +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b3f745ac6..cc8564320 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -7,6 +7,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/explain' require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' +require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods' require 'active_record/connection_adapters/sqlserver/version' require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' From 5cbc494d2eee0e9f647f6f30d36280ea2bd4777a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 27 Feb 2020 15:16:35 +0000 Subject: [PATCH 0798/1412] Default precision for 'time' column type is 7 --- .../sqlserver/type/time.rb | 5 ++- test/cases/column_test_sqlserver.rb | 40 +++++++++++++++++-- test/cases/schema_dumper_test_sqlserver.rb | 1 + test/schema/datatypes/2012.sql | 1 + 4 files changed, 42 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index f8bc0dec8..6a7354f33 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -4,6 +4,9 @@ module SQLServer module Type class Time < ActiveRecord::Type::Time + # Default fractional scale for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) + DEFAULT_FRACTIONAL_SCALE = 7 + include TimeValueFractional2 def serialize(value) @@ -42,7 +45,7 @@ def cast_value(value) end def fractional_scale - precision + precision || DEFAULT_FRACTIONAL_SCALE end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 796470436..9e41e81e3 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -349,7 +349,7 @@ def assert_obj_set_and_save(attribute, value) end it 'datetime2' do - skip 'datetime2 not supported in this protocal version' unless connection_dblib_73? + skip 'datetime2 not supported in this protocol version' unless connection_dblib_73? col = column('datetime2_7') _(col.sql_type).must_equal 'datetime2(7)' _(col.type).must_equal :datetime @@ -414,7 +414,7 @@ def assert_obj_set_and_save(attribute, value) end it 'datetimeoffset' do - skip 'datetimeoffset not supported in this protocal version' unless connection_dblib_73? + skip 'datetimeoffset not supported in this protocol version' unless connection_dblib_73? col = column('datetimeoffset_7') _(col.sql_type).must_equal 'datetimeoffset(7)' _(col.type).must_equal :datetimeoffset @@ -480,7 +480,7 @@ def assert_obj_set_and_save(attribute, value) end it 'time(7)' do - skip 'time() not supported in this protocal version' unless connection_dblib_73? + skip 'time() not supported in this protocol version' unless connection_dblib_73? col = column('time_7') _(col.sql_type).must_equal 'time(7)' _(col.type).must_equal :time @@ -512,7 +512,7 @@ def assert_obj_set_and_save(attribute, value) end it 'time(2)' do - skip 'time() not supported in this protocal version' unless connection_dblib_73? + skip 'time() not supported in this protocol version' unless connection_dblib_73? col = column('time_2') _(col.sql_type).must_equal 'time(2)' _(col.type).must_equal :time @@ -541,6 +541,38 @@ def assert_obj_set_and_save(attribute, value) _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end + it 'time using default precision' do + skip 'time() not supported in this protocol version' unless connection_dblib_73? + 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, 01, 01, 15, 03, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" + _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) + _(type).must_be_instance_of 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, 01, 01, 15, 45, 00, 300) + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" + obj.save! ; obj.reload + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" + # Time's #usec precision (high micro) + obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 234567) + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + obj.save! ; obj.reload + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + # Time's #usec precision (high nano rounded) + obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + obj.save! ; obj.reload + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + end + # Character Strings it 'char(10)' do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 3da17ca1c..47b96cff3 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -34,6 +34,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase if connection_dblib_73? assert_line :time_7, type: 'time', limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil + assert_line :time_default, type: 'time', limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" end # Character Strings assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil diff --git a/test/schema/datatypes/2012.sql b/test/schema/datatypes/2012.sql index 044b78c97..77b14807d 100644 --- a/test/schema/datatypes/2012.sql +++ b/test/schema/datatypes/2012.sql @@ -35,6 +35,7 @@ CREATE TABLE [sst_datatypes] ( [smalldatetime] [smalldatetime] NULL DEFAULT '1901-01-01T15:45:00.000Z', [time_7] [time](7) NULL DEFAULT '04:20:00.2883215', [time_2] [time](2) NULL, + [time_default] [time] NULL DEFAULT '15:03:42.0621978', -- Character Strings [char_10] [char](10) NULL DEFAULT '1234567890', [varchar_50] [varchar](50) NULL DEFAULT 'test varchar_50', From d846bc12a0247de8ce203676dbd59b51bcfebbb4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Mar 2020 15:55:39 +0000 Subject: [PATCH 0799/1412] Set default time precision when registering time type --- .../connection_adapters/sqlserver/type/time.rb | 5 +---- lib/active_record/connection_adapters/sqlserver_adapter.rb | 6 ++++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 6a7354f33..f8bc0dec8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -4,9 +4,6 @@ module SQLServer module Type class Time < ActiveRecord::Type::Time - # Default fractional scale for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) - DEFAULT_FRACTIONAL_SCALE = 7 - include TimeValueFractional2 def serialize(value) @@ -45,7 +42,7 @@ def cast_value(value) end def fractional_scale - precision || DEFAULT_FRACTIONAL_SCALE + precision end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index cc8564320..0b875cb10 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -41,6 +41,9 @@ class SQLServerAdapter < AbstractAdapter ADAPTER_NAME = 'SQLServer'.freeze + # Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) + DEFAULT_TIME_PRECISION = 7 + attr_reader :spid cattr_accessor :cs_equality_operator, instance_accessor: false @@ -297,8 +300,7 @@ def initialize_type_map(m = type_map) end m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new m.register_type %r{\Atime}i do |sql_type| - scale = extract_scale(sql_type) - precision = extract_precision(sql_type) + precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION SQLServer::Type::Time.new precision: precision end # Character Strings From 8747adc7031788fcc8fa0ae3f723488eb2bb0442 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 23 Mar 2020 16:48:49 +0000 Subject: [PATCH 0800/1412] Adapter does not use prepared statement cache --- test/cases/coerced_tests.rb | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a75c0171c..6dff353b7 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -145,8 +145,27 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced module ActiveRecord class BindParameterTest < ActiveRecord::TestCase - # Never finds `sql` since we use `EXEC sp_executesql` wrappers. + # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper. coerce_tests! :test_binds_are_logged + def test_binds_are_logged_coerced + sub = Arel::Nodes::BindParam.new(1) + binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] + sql = "select * from topics where id = #{sub.to_sql}" + + @connection.exec_query(sql, "SQL", binds) + + logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1" + message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql } + + assert_equal binds, message[4][:binds] + end + + # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`. + coerce_tests! :test_statement_cache + coerce_tests! :test_statement_cache_with_query_cache + coerce_tests! :test_statement_cache_with_find_by + coerce_tests! :test_statement_cache_with_in_clause + coerce_tests! :test_statement_cache_with_sql_string_literal end end From 72737e8c07d84675559f215ec10c74684d08ec96 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 24 Mar 2020 17:08:19 +0000 Subject: [PATCH 0801/1412] Quoted table names containing square brackets need to be regex escaped Use method from Rails master --- .../sqlserver/core_ext/query_methods.rb | 26 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 1 + 2 files changed, 27 insertions(+) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb new file mode 100644 index 000000000..b9f4529dd --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb @@ -0,0 +1,26 @@ +require 'active_record/relation' +require 'active_record/version' + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module QueryMethods + + private + + # Copy of original from Rails master. This patch can be removed when adapter supports Rails 6. + def table_name_matches?(from) + table_name = Regexp.escape(table.name) + quoted_table_name = Regexp.escape(connection.quote_table_name(table.name)) + /(?:\A|(? Date: Wed, 25 Mar 2020 16:48:03 +0000 Subject: [PATCH 0802/1412] Update version Rails 6.0.2.2 Remove SQLite versioning Set to use Rails 6.0.0 --- Gemfile | 2 +- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index f27143d4d..75a716cfd 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ require 'openssl' source 'https://rubygems.org' gemspec -gem 'sqlite3', '~> 1.3.6' +gem 'sqlite3' gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/VERSION b/VERSION index 248da9b7b..09b254e90 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0.beta1 +6.0.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index c71c1167d..783e7742e 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -16,6 +16,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 6.0.0.beta3' + spec.add_dependency 'activerecord', '~> 6.0.0' spec.add_dependency 'tiny_tds' end From e24ec67be14d8093cc5566f993429101f2a892d4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 26 Mar 2020 16:11:13 +0000 Subject: [PATCH 0803/1412] Fix method signature --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 11c53871c..c5a7aeb12 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -325,7 +325,7 @@ def initialize_type_map(m = type_map) m.register_type 'timestamp', SQLServer::Type::Timestamp.new end - def translate_exception(e, message) + def translate_exception(e, message:, sql:, binds:) case message when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i RecordNotUnique.new(message) From 7a1552d5aa41ed64bdac0380c582177ffd6355c1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 26 Mar 2020 16:11:20 +0000 Subject: [PATCH 0804/1412] Updated comment --- .../connection_adapters/sqlserver/core_ext/query_methods.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb index b9f4529dd..91d17721f 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb @@ -9,7 +9,8 @@ module QueryMethods private - # Copy of original from Rails master. This patch can be removed when adapter supports Rails 6. + # Copy of original from Rails master. + # This patch can be removed when adapter supports Rails version greater than 6.0.2.2 def table_name_matches?(from) table_name = Regexp.escape(table.name) quoted_table_name = Regexp.escape(connection.quote_table_name(table.name)) From b8333933fe549dabb5a0e2941821401482fa2130 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 30 Mar 2020 16:03:47 +0100 Subject: [PATCH 0805/1412] Remove dependency status badge --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 137418527..55dd629f2 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,6 @@ * [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI * [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version -* [![Dependency Status](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter/badge)](https://dependencyci.com/github/rails-sqlserver/activerecord-sqlserver-adapter) - Dependency Status * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community ## About The Adapter From ee65b455a725af1c44a14d79ea3533a39dd11e86 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 30 Mar 2020 17:03:02 +0100 Subject: [PATCH 0806/1412] Fix for table from statement join --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 822231262..c5f2d6913 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -202,7 +202,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 + Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left.left end end From a25eddbf1ec54a5c92bdaeacfc31b42912b8e51d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 31 Mar 2020 15:49:24 +0100 Subject: [PATCH 0807/1412] Sanitize forbidden attributes for exists relation --- .../sqlserver/core_ext/finder_methods.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 fc9ff9db3..519628a22 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 @@ -11,13 +11,13 @@ module FinderMethods # Same as original except we order by values in distinct select if present. def construct_relation_for_exists(conditions) - if distinct_value && offset_value - relation = limit!(1) + conditions = sanitize_forbidden_attributes(conditions) + if distinct_value && offset_value if select_values.present? - relation = relation.order(*select_values) + relation = order(*select_values).limit!(1) else - relation = relation.except(:order) + relation = except(:order).limit!(1) end else relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1) From 2e00595804efe5f200380c42c5c3fe6b77cd7a60 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 1 Apr 2020 11:19:23 +0100 Subject: [PATCH 0808/1412] Added method to get database version Method should be public --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c5a7aeb12..af5b624d5 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -256,6 +256,9 @@ def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], result end + def get_database_version # :nodoc: + version_year + end protected From 400314a331328db73d112dcf6f34421a51161a88 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 1 Apr 2020 15:36:41 +0100 Subject: [PATCH 0809/1412] Use abstract controllers combine multi statements method --- .../sqlserver/database_statements.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 6ad269b4c..3d9e3ae0b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -91,12 +91,12 @@ def insert_fixtures_set(fixture_set, tables_to_delete = []) end end - table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup } - total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts)) + table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}" } + total_sqls = Array.wrap(table_deletes + fixture_inserts) disable_referential_integrity do transaction(requires_new: true) do - total_sql.each do |sql| + total_sqls.each do |sql| execute sql, "Fixtures Load" yield if block_given? end @@ -109,11 +109,6 @@ def can_perform_case_insensitive_comparison_for?(column) end private :can_perform_case_insensitive_comparison_for? - def combine_multi_statements(total_sql) - total_sql - end - private :combine_multi_statements - def default_insert_value(column) if column.is_identity? table_name = quote(quote_table_name(column.table_name)) From ebfd1213e75d0fa98bacac00a501c9f7f2bbb12b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 2 Apr 2020 11:46:40 +0100 Subject: [PATCH 0810/1412] Add/remove foreign keys when truncating during tests Fixed namespacing Fixed calls to coerce tests Fix tests --- test/cases/coerced_tests.rb | 60 +++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6dff353b7..0725f222b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -51,6 +51,56 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced end end +module ActiveRecord + class AdapterTestWithoutTransaction < ActiveRecord::TestCase + # SQL Server does not allow truncation of tables that are referenced by foreign key + # constraints. So manually remove/add foreign keys in test. + coerce_tests! :test_truncate_tables, :test_truncate_tables_with_query_cache + def test_truncate_tables_coerced + # Remove foreign key constraint to allow truncation. + @connection.remove_foreign_key :authors, :author_addresses + + assert_operator Post.count, :>, 0 + assert_operator Author.count, :>, 0 + assert_operator AuthorAddress.count, :>, 0 + + @connection.truncate_tables("author_addresses", "authors", "posts") + + assert_equal 0, Post.count + assert_equal 0, Author.count + assert_equal 0, AuthorAddress.count + ensure + reset_fixtures("posts", "authors", "author_addresses") + + # Restore foreign key constraint. + @connection.add_foreign_key :authors, :author_addresses + end + + def test_truncate_tables_with_query_cache + # Remove foreign key constraint to allow truncation. + @connection.remove_foreign_key :authors, :author_addresses + + @connection.enable_query_cache! + + assert_operator Post.count, :>, 0 + assert_operator Author.count, :>, 0 + assert_operator AuthorAddress.count, :>, 0 + + @connection.truncate_tables("author_addresses", "authors", "posts") + + assert_equal 0, Post.count + assert_equal 0, Author.count + assert_equal 0, AuthorAddress.count + ensure + reset_fixtures("posts", "authors", "author_addresses") + @connection.disable_query_cache! + + # Restore foreign key constraint. + @connection.add_foreign_key :authors, :author_addresses + end + end +end + @@ -1181,3 +1231,13 @@ class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase # Temporarily coerce this test due to https://github.com/rails/rails/issues/34945 coerce_tests! :test_eager_loading_too_may_ids end + + +module ActiveRecord + class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase + # SQL Server does not allow truncation of tables that are referenced by foreign key + # constraints. As this test truncates all tables we would need to remove all foreign + # key constraints and then restore them afterwards to get this test to pass. + coerce_tests! :test_truncate_tables + end +end From 694c1e579c676433a6fad08f58e38329f21c4012 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 2 Apr 2020 15:23:33 +0100 Subject: [PATCH 0811/1412] Use https protocol for Github --- Gemfile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 75a716cfd..10c9bccf8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,10 @@ +# frozen_string_literal: true + require 'openssl' source 'https://rubygems.org' + +git_source(:github) { |repo| "https://github.com/#{repo}.git" } + gemspec gem 'sqlite3' @@ -33,7 +38,7 @@ else ver end end - gem 'rails', git: "git://github.com/rails/rails.git", tag: "v#{version}" + gem 'rails', github: "rails/rails", tag: "v#{version}" end if ENV['AREL'] From 3d3faf8dde9309307aa3818609f5706e5f8bbc8a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Apr 2020 15:46:49 +0100 Subject: [PATCH 0812/1412] Subqueries cannot include ordering unless TOP/LIMIT also specified --- lib/arel/visitors/sqlserver.rb | 18 ++++++++++++++ test/cases/in_clause_test_sqlserver.rb | 34 ++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 test/cases/in_clause_test_sqlserver.rb diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index c5f2d6913..29dd5daba 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -127,6 +127,19 @@ def visit_Arel_Nodes_OuterJoin o, collector visit o.right, collector end + # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer + # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables, + # subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified." + def collect_in_clause(left, right, collector) + if Array === right + right.each { |node| remove_invalid_ordering_from_in_clause(node) } + else + remove_invalid_ordering_from_in_clause(right) + end + + super + end + # SQLServer ToSql/Visitor (Additions) def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} @@ -219,6 +232,11 @@ def remote_server_table_name o ).quoted end + def remove_invalid_ordering_from_in_clause(node) + return unless Arel::Nodes::SelectStatement === node + + node.orders = [] unless node.offset || node.limit + end end end end diff --git a/test/cases/in_clause_test_sqlserver.rb b/test/cases/in_clause_test_sqlserver.rb new file mode 100644 index 000000000..6ec295130 --- /dev/null +++ b/test/cases/in_clause_test_sqlserver.rb @@ -0,0 +1,34 @@ +require 'cases/helper_sqlserver' +require 'models/post' +require 'models/author' + +class InClauseTestSQLServer < ActiveRecord::TestCase + fixtures :posts, :authors + + it 'removes ordering from subqueries' do + authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_not_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 10, posts.length + end + + it 'does not remove ordering from subquery that includes a limit' do + authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name).limit(2) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 7, posts.length + end + + it 'does not remove ordering from subquery that includes an offset' do + authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name).offset(1) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 8, posts.length + end +end From 23607417abb0cede7ab7cef5bacaf848d3d8c2e3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 7 Apr 2020 14:53:43 +0100 Subject: [PATCH 0813/1412] Store table name on columns Match SQLServerColumn initializer with Column's --- .../sqlserver/schema_statements.rb | 13 ++++++------- .../connection_adapters/sqlserver_column.rb | 10 +++++++--- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 617008268..b8a5ea7a4 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -66,14 +66,13 @@ def indexes(table_name) def columns(table_name) return [] if table_name.blank? column_definitions(table_name).map do |ci| - sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity + sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options new_column( ci[:name], ci[:default_value], sql_type_metadata, ci[:null], - ci[:table_name], ci[:default_function], ci[:collation], nil, @@ -82,16 +81,16 @@ def columns(table_name) end end - def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) SQLServerColumn.new( name, default, sql_type_metadata, - null, table_name, + null, default_function, - collation, - comment, - sqlserver_options + collation: collation, + comment: comment, + **sqlserver_options ) end diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 07a2eb8d0..7d0547344 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -2,9 +2,9 @@ module ActiveRecord module ConnectionAdapters class SQLServerColumn < Column - def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) - @sqlserver_options = sqlserver_options || {} - super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment) + def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **sqlserver_options) + @sqlserver_options = sqlserver_options + super end def is_identity? @@ -15,6 +15,10 @@ def is_primary? @sqlserver_options[:is_primary] end + def table_name + @sqlserver_options[:table_name] + end + def is_utf8? sql_type =~ /nvarchar|ntext|nchar/i end From ca78a173dff230d4b95248d76e453f24f7ba717d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Apr 2020 14:53:42 +0100 Subject: [PATCH 0814/1412] Fixed migration test --- test/cases/migration_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 088024df3..a687ce6b4 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -20,7 +20,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it 'not create a tables if error in migrations' do begin migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table' - quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir, ActiveRecord::SchemaMigration).up } rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message end From b73de76cc8cd6200ebc57a41bc16706f321cf510 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Apr 2020 16:14:22 +0100 Subject: [PATCH 0815/1412] Updated unsafe raw sql tests to match tests in Rails 6 --- test/cases/coerced_tests.rb | 63 ++++--------------------------------- 1 file changed, 6 insertions(+), 57 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0725f222b..53541e531 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1100,7 +1100,9 @@ def schema_dump_path end class UnsafeRawSqlTest < ActiveRecord::TestCase - coerce_tests! %r{always allows Arel} + # Coerce the originals as they use 'LENGTH' instead of SQL Servers 'LEN' function. + + coerce_tests! %r{order: always allows Arel} test 'order: always allows Arel' do ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) } @@ -1108,6 +1110,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_depr, ids_disabled end + coerce_tests! %r{pluck: always allows Arel} test "pluck: always allows Arel" do values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } @@ -1115,71 +1118,17 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal values_depr, values_disabled end - - coerce_tests! %r{order: disallows invalid Array arguments} - test "order: disallows invalid Array arguments" do - with_unsafe_raw_sql_disabled do - assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.order(["author_id", "len(title)"]).pluck(:id) - end - end - end - coerce_tests! %r{order: allows valid Array arguments} test "order: allows valid Array arguments" do ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", Arel.sql("len(title)")]).pluck(:id) } + ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "len(title)"]).pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "len(title)"]).pluck(:id) } assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end - coerce_tests! %r{order: logs deprecation warning for unrecognized column} - test "order: logs deprecation warning for unrecognized column" do - with_unsafe_raw_sql_deprecated do - assert_deprecated(/Dangerous query method/) do - Post.order("len(title)") - end - end - end - - coerce_tests! %r{pluck: disallows invalid column name} - test "pluck: disallows invalid column name" do - with_unsafe_raw_sql_disabled do - assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.pluck("len(title)") - end - end - end - - coerce_tests! %r{pluck: disallows invalid column name amongst valid names} - test "pluck: disallows invalid column name amongst valid names" do - with_unsafe_raw_sql_disabled do - assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.pluck(:title, "len(title)") - end - end - end - - coerce_tests! %r{pluck: disallows invalid column names with includes} - test "pluck: disallows invalid column names with includes" do - with_unsafe_raw_sql_disabled do - assert_raises(ActiveRecord::UnknownAttributeReference) do - Post.includes(:comments).pluck(:title, "len(title)") - end - end - end - - coerce_tests! %r{pluck: logs deprecation warning} - test "pluck: logs deprecation warning" do - with_unsafe_raw_sql_deprecated do - assert_deprecated(/Dangerous query method/) do - Post.includes(:comments).pluck(:title, "len(title)") - end - end - end end From 92ff32a9c28a1b9d0643b9c2ee272263f7fdd604 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 7 Apr 2020 17:11:31 +0100 Subject: [PATCH 0816/1412] Refactor columns introspection query to make it faster --- .../sqlserver/schema_statements.rb | 119 ++++++++++-------- test/support/core_ext/query_cache.rb | 1 + test/support/sql_counter_sqlserver.rb | 1 + 3 files changed, 72 insertions(+), 49 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 185cc2154..38872848f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -343,55 +343,9 @@ def column_definitions(table_name) database = identifier.fully_qualified_database_quoted view_exists = view_exists?(table_name) view_tblnm = view_table_name(table_name) if view_exists - sql = %{ - SELECT DISTINCT - #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name, - #{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name, - columns.DATA_TYPE AS type, - columns.COLUMN_DEFAULT AS default_value, - columns.NUMERIC_SCALE AS numeric_scale, - columns.NUMERIC_PRECISION AS numeric_precision, - columns.DATETIME_PRECISION AS datetime_precision, - columns.COLLATION_NAME AS [collation], - columns.ordinal_position, - CASE - WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH - ELSE COL_LENGTH('#{database}.'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME) - END AS [length], - CASE - WHEN columns.IS_NULLABLE = 'YES' THEN 1 - ELSE NULL - END AS [is_nullable], - CASE - WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1 - ELSE NULL - END AS [is_primary], - c.is_identity AS [is_identity] - FROM #{database}.INFORMATION_SCHEMA.COLUMNS columns - LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC - ON TC.TABLE_NAME = columns.TABLE_NAME - AND TC.TABLE_SCHEMA = columns.TABLE_SCHEMA - AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' - LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU - ON KCU.COLUMN_NAME = columns.COLUMN_NAME - AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME - AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG - AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA - INNER JOIN #{database}.sys.schemas AS s - ON s.name = columns.TABLE_SCHEMA - AND s.schema_id = s.schema_id - INNER JOIN #{database}.sys.objects AS o - ON s.schema_id = o.schema_id - AND o.is_ms_shipped = 0 - AND o.type IN ('U', 'V') - AND o.name = columns.TABLE_NAME - INNER JOIN #{database}.sys.columns AS c - ON o.object_id = c.object_id - AND c.name = columns.COLUMN_NAME - WHERE columns.TABLE_NAME = #{prepared_statements ? '@0' : quote(identifier.object)} - AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))} - ORDER BY columns.ordinal_position - }.gsub(/[ \t\r\n]+/, ' ').strip + + sql = column_definitions_sql(database, identifier) + binds = [] nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) @@ -458,6 +412,73 @@ def column_definitions(table_name) 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 + + %{ + SELECT + #{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 + WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint') + THEN c.scale + END AS [numeric_scale], + CASE + WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint', 'real', 'float') + THEN c.precision + END AS [numeric_precision], + CASE + WHEN t.name IN ('date', 'datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'time') + THEN c.scale + END AS [datetime_precision], + c.collation_name AS [collation], + ROW_NUMBER() OVER (ORDER BY c.column_id) AS [ordinal_position], + CASE + WHEN t.name IN ('nchar', 'nvarchar') AND c.max_length > 0 + THEN c.max_length / 2 + ELSE c.max_length + END AS [length], + CASE c.is_nullable + WHEN 1 + THEN 1 + END AS [is_nullable], + CASE + WHEN ic.object_id IS NOT NULL + THEN 1 + END AS [is_primary], + c.is_identity AS [is_identity] + FROM #{database}.sys.columns c + INNER JOIN #{database}.sys.objects o + ON c.object_id = o.object_id + INNER JOIN #{database}.sys.schemas s + ON o.schema_id = s.schema_id + INNER JOIN #{database}.sys.types t + ON c.system_type_id = t.system_type_id + AND c.user_type_id = t.user_type_id + LEFT OUTER JOIN #{database}.sys.default_constraints d + ON c.object_id = d.parent_object_id + AND c.default_object_id = d.object_id + LEFT OUTER JOIN #{database}.sys.key_constraints k + ON c.object_id = k.parent_object_id + LEFT OUTER JOIN #{database}.sys.index_columns ic + ON k.parent_object_id = ic.object_id + AND k.unique_index_id = ic.index_id + AND c.column_id = ic.column_id + WHERE + o.name = #{object_name} + AND s.name = #{schema_name} + ORDER BY + c.column_id + }.gsub(/[ \t\r\n]+/, ' ').strip + end + def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA' constraints.each do |constraint| diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb index 6661696e4..01601f695 100644 --- a/test/support/core_ext/query_cache.rb +++ b/test/support/core_ext/query_cache.rb @@ -5,6 +5,7 @@ module SqlIgnoredCache IGNORED_SQL = [ /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, + /sys.columns/i, /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index a3130695e..04a814dc6 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -14,6 +14,7 @@ def capture_sql_ss ignored_sql = [ /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, + /sys.columns/i, /SELECT @@version/, /SELECT @@TRANCOUNT/, /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, From 6ea5654231af9aea36bd27b434ec845cc375ad78 Mon Sep 17 00:00:00 2001 From: David McDonald Date: Wed, 8 Apr 2020 19:04:26 -0400 Subject: [PATCH 0817/1412] Correcting tests (#725) * corrected syntax for DEPRECATED warnings in test/cases * corrected syntax for DEPRECATED warnings in test/cases --- Gemfile | 2 +- test/cases/coerced_tests.rb | 6 +++--- test/cases/schema_dumper_test_sqlserver.rb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index f27143d4d..ceaca8990 100644 --- a/Gemfile +++ b/Gemfile @@ -51,7 +51,7 @@ group :tinytds do end group :development do - gem 'byebug' + gem 'byebug', platform: [:mri, :mingw, :x64_mingw] gem 'mocha' gem 'minitest-spec-rails' end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6dff353b7..ae41d4962 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -295,7 +295,7 @@ def test_add_column_without_limit_coerced module ActiveRecord class Migration - class ColumnsTest + 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 @@ -525,9 +525,8 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced end end end -end - +end module ActiveRecord @@ -778,6 +777,7 @@ def test_reverse_arel_assoc_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order assert_equal topics(:second).title, topics.first.title end + end class ActiveRecord::RelationTest < ActiveRecord::TestCase diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 47b96cff3..a72438e2a 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -168,7 +168,7 @@ def assert_line(column_name, options={}) if expected.nil? _(actual).must_be_nil message elsif expected.is_a?(Array) - actual.must_include expected, message + _(actual).must_include expected, message elsif expected.is_a?(Float) _(actual).must_be_close_to expected, 0.001 elsif expected.is_a?(Proc) From 9200fa9a841d485f9caba5ce12a34da8eb5ae907 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Apr 2020 10:11:41 +0100 Subject: [PATCH 0818/1412] Fix missing parent class --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0725f222b..8e3edad6d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -345,7 +345,7 @@ def test_add_column_without_limit_coerced module ActiveRecord class Migration - class ColumnsTest + 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 From 7ee21228416a3baf20a6f749871d314c955ec207 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Apr 2020 11:35:49 +0100 Subject: [PATCH 0819/1412] Fix sanitization matchers --- .../connection_adapters/sqlserver/quoting.rb | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index e577c69ee..0d95202c8 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -68,6 +68,42 @@ def quoted_date(value) end end + def column_name_matcher + COLUMN_NAME + end + + def column_name_with_order_matcher + COLUMN_NAME_WITH_ORDER + end + + COLUMN_NAME = / + \A + ( + (?: + # [table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + ) + (?:\s+AS\s+(?:\w+|\[\w+\]))? + ) + (?:\s*,\s*\g<1>)* + \z + /ix + + COLUMN_NAME_WITH_ORDER = / + \A + ( + (?: + # [table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + ) + (?:\s+ASC|\s+DESC)? + (?:\s+NULLS\s+(?:FIRST|LAST))? + ) + (?:\s*,\s*\g<1>)* + \z + /ix + + private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER private From e872d91ef0250de20f297db6c5563a7136e0c10e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Apr 2020 11:35:37 +0100 Subject: [PATCH 0820/1412] Changed query cached test Space already being added by ORDER BY string Changed test to include resetting column information --- lib/arel/visitors/sqlserver.rb | 1 - test/cases/coerced_tests.rb | 27 +++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 29dd5daba..449165f91 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -153,7 +153,6 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} def visit_Orders_And_Let_Fetch_Happen o, collector make_Fetch_Possible_And_Deterministic o unless o.orders.empty? - collector << " " collector << " ORDER BY " len = o.orders.length - 1 o.orders.each_with_index { |x, i| diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3523d69ac..8ba7640e9 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -756,6 +756,33 @@ def test_cache_does_not_wrap_results_in_arrays_coerced assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end end + + + # Same as original test except that we expect one query to be performed to retrieve the table's primary key. + # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column + # information then the primary key needs to be retrieved from the database again to generate the SQL causing the + # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key. + coerce_tests! :test_query_cached_even_when_types_are_reset + def test_query_cached_even_when_types_are_reset_coerced + Task.cache do + # Warm the cache + Task.find(1) + + # Preload the type cache again (so we don't have those queries issued during our assertions) + Task.connection.send(:reload_type_map) + + # Clear places where type information is cached + Task.reset_column_information + Task.initialize_find_by_cache + Task.define_attribute_methods + + assert_queries(1, ignore_none: true) do + Task.find(1) + end + + assert_includes ActiveRecord::SQLCounter.log_all.first , "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''" + end + end end From d4340fac5daaa90d4b93e52a1c73da65c44ab190 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Apr 2020 14:15:23 +0100 Subject: [PATCH 0821/1412] Added method deprecations --- .../connection_adapters/sqlserver/database_limits.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index c5f419094..f52ffdf39 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -9,10 +9,12 @@ def table_alias_length def column_name_length 128 end + deprecate :column_name_length def table_name_length 128 end + deprecate :table_name_length def index_name_length 128 @@ -21,14 +23,17 @@ def index_name_length def columns_per_table 1024 end + deprecate :columns_per_table def indexes_per_table 999 end + deprecate :indexes_per_table def columns_per_multicolumn_index 16 end + deprecate :columns_per_multicolumn_index def in_clause_length 10_000 @@ -37,10 +42,12 @@ def in_clause_length def sql_query_length 65_536 * 4_096 end + deprecate :sql_query_length def joins_per_query 256 end + deprecate :joins_per_query private From b3492a89a3c233b6118c40b274affd25a3fb639a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Apr 2020 10:13:33 +0100 Subject: [PATCH 0822/1412] Fixed tests --- test/cases/fully_qualified_identifier_test_sqlserver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index a454d4baf..18b028b2d 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -42,14 +42,14 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase it 'should not use fully qualified table name in order clause' do table = Arel::Table.new(:table) - expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" + expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql end it 'should use fully qualified table name in insert statement' do manager = Arel::InsertManager.new manager.into Arel::Table.new(:table) - manager.values = manager.create_values [Arel.sql('*')], %w{ a } + manager.values = manager.create_values [Arel.sql('*')] expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" quietly { assert_equal expected_sql, manager.to_sql } end From b9b5178cbf6c7b1df52cad25256cea10098cf2c9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Apr 2020 15:57:32 +0100 Subject: [PATCH 0823/1412] Add ability to block writes to a database Prevent writes on exec_query https://github.com/rails/rails/commit/bde898c1df17722a83e5e8866b2009273c5190c6 Block writes to a database if using execute --- .../sqlserver/database_statements.rb | 14 ++++++ test/cases/adapter_test_sqlserver.rb | 45 ++++++++++++++++++- 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3d9e3ae0b..ebea06705 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -2,8 +2,18 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseStatements + READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback) # :nodoc: + private_constant :READ_QUERY + + def write_query?(sql) # :nodoc: + !READ_QUERY.match?(sql) + end def execute(sql, name = nil) + if preventing_writes? && write_query?(sql) + raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" + end + if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } else @@ -12,6 +22,10 @@ def execute(sql, name = nil) end def exec_query(sql, name = 'SQL', binds = [], prepare: false) + if preventing_writes? && write_query?(sql) + raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" + end + sp_executesql(sql, name, binds, prepare: prepare) end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 628a54140..fcad2b494 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -13,7 +13,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } - it 'has basic and non-senstive information in the adpaters inspect method' do + it 'has basic and non-sensitive information in the adapters inspect method' do string = connection.inspect _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} _(string).must_match %r{version\: \d.\d} @@ -429,5 +429,48 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end + describe 'block writes to a database' do + def setup + @conn = ActiveRecord::Base.connection + @connection_handler = ActiveRecord::Base.connection_handler + end + + def test_errors_when_an_insert_query_is_called_while_preventing_writes + assert_raises(ActiveRecord::ReadOnlyError) do + @connection_handler.while_preventing_writes do + @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") + end + end + end + + def test_errors_when_an_update_query_is_called_while_preventing_writes + @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") + + assert_raises(ActiveRecord::ReadOnlyError) do + @connection_handler.while_preventing_writes do + @conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'") + end + end + end + + def test_errors_when_a_delete_query_is_called_while_preventing_writes + @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") + + assert_raises(ActiveRecord::ReadOnlyError) do + @connection_handler.while_preventing_writes do + @conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'") + end + end + end + + def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes + @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") + + @connection_handler.while_preventing_writes do + assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'") + end + end + end + end From 355fb342fe7e3679f51be0b6dbba19d91d0d3f91 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Apr 2020 11:47:49 +0100 Subject: [PATCH 0824/1412] Moved coerced tests to correct classes --- test/cases/coerced_tests.rb | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8ba7640e9..242cc8a90 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -673,12 +673,15 @@ def test_merge_options_coerced -require 'models/parrot' -require 'models/topic' class PersistenceTest < ActiveRecord::TestCase # We can not UPDATE identity columns. coerce_tests! :test_update_columns_changing_id +end + + +require 'models/author' +class UpdateAllTest < ActiveRecord::TestCase # Previous test required updating a identity column. coerce_tests! :test_update_all_doesnt_ignore_order def test_update_all_doesnt_ignore_order_coerced @@ -692,30 +695,6 @@ def test_update_all_doesnt_ignore_order_coerced _(david.reload.name).must_equal 'David' _(mary.reload.name).must_equal 'Test' end - - # We can not UPDATE identity columns. - coerce_tests! :test_update_attributes - def test_update_attributes_coerced - topic = Topic.find(1) - assert !topic.approved? - assert_equal "The First Topic", topic.title - topic.update_attributes("approved" => true, "title" => "The First Topic Updated") - topic.reload - assert topic.approved? - assert_equal "The First Topic Updated", topic.title - topic.update_attributes(approved: false, title: "The First Topic") - topic.reload - assert !topic.approved? - assert_equal "The First Topic", topic.title - # SQLServer: Here is where it breaks down. No exceptions. - # assert_raise(ActiveRecord::RecordNotUnique, ActiveRecord::StatementInvalid) do - # topic.update_attributes(id: 3, title: "Hm is it possible?") - # end - # assert_not_equal "Hm is it possible?", Topic.find(3).title - # topic.update_attributes(id: 1234) - # assert_nothing_raised { topic.reload } - # assert_equal topic.title, Topic.find(1234).title - end end From 88596fc04a041aec689a4869222dc6c684ddd7d0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Apr 2020 14:21:11 +0100 Subject: [PATCH 0825/1412] Coerce tests that require changing identity column --- test/cases/coerced_tests.rb | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 242cc8a90..075e821e6 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -672,17 +672,35 @@ def test_merge_options_coerced - +require 'models/topic' class PersistenceTest < ActiveRecord::TestCase - # We can not UPDATE identity columns. + # Rails test required updating a identity column. coerce_tests! :test_update_columns_changing_id + + # Rails test required updating a identity column. + coerce_tests! :test_update + def test_update_coerced + topic = Topic.find(1) + assert_not_predicate topic, :approved? + assert_equal "The First Topic", topic.title + + topic.update("approved" => true, "title" => "The First Topic Updated") + topic.reload + assert_predicate topic, :approved? + assert_equal "The First Topic Updated", topic.title + + topic.update(approved: false, title: "The First Topic") + topic.reload + assert_not_predicate topic, :approved? + assert_equal "The First Topic", topic.title + end end require 'models/author' class UpdateAllTest < ActiveRecord::TestCase - # Previous test required updating a identity column. + # Rails test required updating a identity column. coerce_tests! :test_update_all_doesnt_ignore_order def test_update_all_doesnt_ignore_order_coerced david, mary = authors(:david), authors(:mary) From 7589e9081468e25e67745f241c0b36116adbd7fa Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Apr 2020 14:27:56 +0100 Subject: [PATCH 0826/1412] Fix showplan test --- test/cases/showplan_test_sqlserver.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 848f6f151..f54a37a88 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -21,14 +21,14 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it 'from prepared statement' do plan = Car.where(name: ',').limit(1).explain - _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name]" + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[name]" _(plan).must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' _(plan).must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' end it 'from array condition using index' do plan = Car.where(id: [1, 2]).explain - _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' end From 86506cf8f38508619104f59da114fa4162a1b7de Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Apr 2020 15:19:13 +0100 Subject: [PATCH 0827/1412] Install PostgreSQL gem --- Gemfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index c3f233f01..43208ba7e 100644 --- a/Gemfile +++ b/Gemfile @@ -8,6 +8,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec gem 'sqlite3' +gem 'pg' gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] @@ -18,7 +19,7 @@ end if ENV['RAILS_SOURCE'] gemspec path: ENV['RAILS_SOURCE'] else - # Need to get rails source beacause the gem doesn't include tests + # Need to get rails source because the gem doesn't include tests version = ENV['RAILS_VERSION'] || begin require 'net/http' require 'yaml' From 3aa7abb1d82b3f9d1dd6c5d022d683048decfe38 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 Apr 2020 15:39:07 +0100 Subject: [PATCH 0828/1412] Skip test if database case insensitive --- test/cases/coerced_tests.rb | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 075e821e6..e62c57256 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -4,7 +4,7 @@ require 'models/event' class UniquenessValidationTest < ActiveRecord::TestCase - # So sp_executesql swallows this exception. Run without prpared to see it. + # So sp_executesql swallows this exception. Run without prepared to see it. coerce_tests! :test_validate_uniqueness_with_limit def test_validate_uniqueness_with_limit_coerced connection.unprepared_statement do @@ -14,7 +14,7 @@ def test_validate_uniqueness_with_limit_coerced end end - # So sp_executesql swallows this exception. Run without prpared to see it. + # So sp_executesql swallows this exception. Run without prepared to see it. coerce_tests! :test_validate_uniqueness_with_limit_and_utf8 def test_validate_uniqueness_with_limit_and_utf8_coerced connection.unprepared_statement do @@ -23,6 +23,15 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced end end end + + # Skip the test if database is case-insensitive. + coerce_tests! :test_validate_case_sensitive_uniqueness_by_default + def test_validate_case_sensitive_uniqueness_by_default_coerced + database_collation = connection.select_one("SELECT collation_name FROM sys.databases WHERE name = 'activerecord_unittest'").values.first + skip if database_collation.include?('_CI_') + + original_test_validate_case_sensitive_uniqueness_by_default_coerced + end end From 54ce1b636dae990e85c68bbee7b0311083e03600 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 Apr 2020 17:03:38 +0100 Subject: [PATCH 0829/1412] Fix asserted SQL --- test/cases/coerced_tests.rb | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e62c57256..b36d42332 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -611,6 +611,47 @@ def test_add_on_delete_restrict_foreign_key_coerced class HasOneAssociationsTest < ActiveRecord::TestCase # We use OFFSET/FETCH vs TOP. So we always have an order. coerce_tests! :test_has_one_does_not_use_order_by + + # Asserted SQL to get one row different from original test. + coerce_tests! :test_has_one + def test_has_one_coerced + firm = companies(:first_firm) + first_account = Account.find(1) + assert_sql(/FETCH NEXT @1 ROWS ONLY(.)*@1 = 1/) do + assert_equal first_account, firm.account + assert_equal first_account.credit_limit, firm.account.credit_limit + end + end +end + + + + +class HasOneThroughAssociationsTest < ActiveRecord::TestCase + # Asserted SQL to get one row different from original test. + coerce_tests! :test_has_one_through_executes_limited_query + def test_has_one_through_executes_limited_query_coerced + boring_club = clubs(:boring_club) + assert_sql(/FETCH NEXT @3 ROWS ONLY(.)*@3 = 1/) do + assert_equal boring_club, @member.general_club + end + end +end + + + + +class BelongsToAssociationsTest < ActiveRecord::TestCase + # Asserted SQL to get one row different from original test. + coerce_tests! :test_belongs_to + def test_belongs_to_coerced + client = Client.find(3) + first_firm = companies(:first_firm) + assert_sql(/FETCH NEXT @3 ROWS ONLY(.)*@3 = 1/) do + assert_equal first_firm, client.firm + assert_equal first_firm.name, client.firm.name + end + end end From c256c339a25738fa49a5ba9d9b3673b6fc6fdff4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 Apr 2020 16:29:43 +0100 Subject: [PATCH 0830/1412] Wrap original test with code to add/remove index --- test/cases/coerced_tests.rb | 59 ++++++++++++++++++++++- test/support/coerceable_test_sqlserver.rb | 17 +++++-- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b36d42332..204b40215 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -879,7 +879,7 @@ def test_relations_dont_load_all_records_in_inspect_coerced # so we are skipping all together. coerce_tests! :test_empty_complex_chained_relations - # Can't apply offset withour ORDER + # Can't apply offset without ORDER coerce_tests! %r{using a custom table affects the wheres} test 'using a custom table affects the wheres coerced' do post = posts(:welcome) @@ -887,7 +887,7 @@ def test_relations_dont_load_all_records_in_inspect_coerced assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take end - # Can't apply offset withour ORDER + # Can't apply offset without ORDER coerce_tests! %r{using a custom table with joins affects the joins} test 'using a custom table with joins affects the joins coerced' do post = posts(:welcome) @@ -1151,10 +1151,21 @@ class CollectionCacheKeyTest < ActiveRecord::TestCase +require "models/book" module ActiveRecord class StatementCacheTest < ActiveRecord::TestCase # Getting random failures. coerce_tests! :test_find_does_not_use_statement_cache_if_table_name_is_changed + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! :test_statement_cache_values_differ + def test_statement_cache_values_differ_coerced + Book.connection.remove_index(:books, column: [:author_id, :name]) + + original_test_statement_cache_values_differ + ensure + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end end end @@ -1264,3 +1275,47 @@ class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase coerce_tests! :test_truncate_tables 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.connection.remove_index(:books, column: [:author_id, :name]) + + send(:'original_enums are distinct per class') + ensure + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! %r{creating new objects with enum scopes} + test "creating new objects with enum scopes coerced" do + Book.connection.remove_index(:books, column: [:author_id, :name]) + + send(:'original_creating new objects with enum scopes') + ensure + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end + + # 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 inheritable} + test "enums are inheritable coerced" do + Book.connection.remove_index(:books, column: [:author_id, :name]) + + send(:'original_enums are inheritable') + ensure + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! %r{declare multiple enums at a time} + test "declare multiple enums at a time coerced" do + Book.connection.remove_index(:books, column: [:author_id, :name]) + + send(:'original_declare multiple enums at a time') + ensure + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end +end diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 03ea893e1..3a7d38387 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -19,21 +19,28 @@ def coerce_tests!(*methods) end def coerce_all_tests! - once = false instance_methods(false).each do |method| next unless method.to_s =~ /\Atest/ undef_method(method) - once = true end STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{self.name}" end private - def coerced_test_warning(method) - method = instance_methods(false).select { |m| m =~ method } if method.is_a?(Regexp) + def coerced_test_warning(test_to_coerce) + if test_to_coerce.is_a?(Regexp) + method = instance_methods(false).select { |m| m =~ test_to_coerce } + else + method = test_to_coerce + end + Array(method).each do |m| - result = undef_method(m) if m && method_defined?(m) + result = if m && method_defined?(m) + alias_method("original_#{test_to_coerce.inspect.tr('/\:"', '')}", m) + undef_method(m) + end + if result.blank? STDOUT.puts "🐳 Unfound coerced test: #{self.name}##{m}" else From 420153a2017fde9f4065a6c3fc0dcce2eecb3016 Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Wed, 22 Apr 2020 13:33:19 +0300 Subject: [PATCH 0831/1412] added if_not_exists option with workaround --- .../sqlserver/schema_creation.rb | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 711ec3aca..670d77379 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -6,15 +6,26 @@ class SchemaCreation < AbstractAdapter::SchemaCreation private def visit_TableDefinition(o) + if_not_exists = o.if_not_exists + if o.as table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name) query = o.as.respond_to?(:to_sql) ? o.as.to_sql : o.as projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures - select_into = "SELECT #{projections} INTO #{table_name} FROM #{source}" + sql = "SELECT #{projections} INTO #{table_name} FROM #{source}" else o.instance_variable_set :@as, nil - super + o.instance_variable_set :@if_not_exists, false + sql = super + end + + if if_not_exists + o.instance_variable_set :@if_not_exists, true + table_name = o.temporary ? "##{o.name}" : o.name + sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='#{table_name}' and xtype='U') #{sql}" end + + sql end def add_column_options!(sql, options) From c96d91ded7fb9e9e51246d3e8c158c921559318e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Apr 2020 15:48:44 +0100 Subject: [PATCH 0832/1412] Coerce test as adapter doesnt use statement cache --- test/cases/coerced_tests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 204b40215..cf2eb239e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -222,6 +222,7 @@ def test_binds_are_logged_coerced # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`. coerce_tests! :test_statement_cache coerce_tests! :test_statement_cache_with_query_cache + coerce_tests! :test_statement_cache_with_find coerce_tests! :test_statement_cache_with_find_by coerce_tests! :test_statement_cache_with_in_clause coerce_tests! :test_statement_cache_with_sql_string_literal From 764aa3d1520055e172eccf525fd7d271755d9fa5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Apr 2020 16:14:43 +0100 Subject: [PATCH 0833/1412] Updated test to match test in Rails --- test/cases/coerced_tests.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 204b40215..7fab2287e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -797,10 +797,11 @@ class PrimaryKeysTest < ActiveRecord::TestCase 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 def test_cache_does_not_wrap_results_in_arrays_coerced Task.cache do - assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end end From ba4dfc9360df920e24f62bd4fffa29fb021fe5e2 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 21 Apr 2020 14:42:09 +0100 Subject: [PATCH 0834/1412] Update test matrix --- .travis.yml | 14 ++++++++------ Dockerfile => Dockerfile.ci | 2 +- docker-compose.ci.yml | 12 +++++++----- 3 files changed, 16 insertions(+), 12 deletions(-) rename Dockerfile => Dockerfile.ci (77%) diff --git a/.travis.yml b/.travis.yml index eee1f33f7..7cec17ae0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,9 +17,11 @@ matrix: include: - name: 2.3.8 env: TARGET_VERSION=2.3.8 - - name: 2.4.6 - env: TARGET_VERSION=2.4.6 - - name: 2.5.5 - env: TARGET_VERSION=2.5.5 - - name: 2.6.3 - env: TARGET_VERSION=2.6.3 + - name: 2.4.10 + env: TARGET_VERSION=2.4.10 + - name: 2.5.8 + env: TARGET_VERSION=2.5.8 + - name: 2.6.6 + env: TARGET_VERSION=2.6.6 + - name: 2.7.1 + env: TARGET_VERSION=2.7.1 diff --git a/Dockerfile b/Dockerfile.ci similarity index 77% rename from Dockerfile rename to Dockerfile.ci index 8b18d5c6a..e616cff26 100644 --- a/Dockerfile +++ b/Dockerfile.ci @@ -1,6 +1,6 @@ ARG TARGET_VERSION=2.6.3 -FROM wpolicarpo/activerecord-sqlserver-adapter:${TARGET_VERSION} +FROM railssqlserver/activerecord-sqlserver-adapter:${TARGET_VERSION} ENV WORKDIR /activerecord-sqlserver-adapter diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index a80798097..25c81cbd9 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,11 +1,13 @@ version: "2.2" services: - database: + sqlserver: image: metaskills/mssql-server-linux-rails ci: environment: - - ACTIVERECORD_UNITTEST_HOST=database - build: . - command: wait-for database:1433 -- bundle exec rake test + - ACTIVERECORD_UNITTEST_HOST=sqlserver + build: + context: . + dockerfile: Dockerfile.ci + command: wait-for sqlserver:1433 -- bundle exec rake test depends_on: - - "database" + - "sqlserver" From 4b30617143e6f34dcb7d3c8f576a3114c988fe9f Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Fri, 24 Apr 2020 16:37:01 +0300 Subject: [PATCH 0835/1412] fix update_attributes deprecated method usage --- test/cases/coerced_tests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cf2eb239e..0dfc6f7b0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -155,7 +155,7 @@ def test_update_date_time_attributes Time.use_zone("Eastern Time (US & Canada)") do topic = Topic.find(1) time = Time.zone.parse("2017-07-17 10:56") - topic.update_attributes!(written_on: time) + topic.update!(written_on: time) assert_equal(time, topic.written_on) end end @@ -166,7 +166,7 @@ def test_update_date_time_attributes_with_default_timezone_local Time.use_zone("Eastern Time (US & Canada)") do topic = Topic.find(1) time = Time.zone.parse("2017-07-17 10:56") - topic.update_attributes!(written_on: time) + topic.update!(written_on: time) assert_equal(time, topic.written_on) end end From 3706bc53b21697694a1809f9e815fc0459f16241 Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Sun, 26 Apr 2020 13:13:21 +0300 Subject: [PATCH 0836/1412] fixes test_default_decimal_number to assert against number.like others test_default_integer. --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cf2eb239e..cb52b6ea9 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1131,12 +1131,22 @@ def test_default_positive_integer_coerced assert_equal 7, record.positive_integer assert_equal 7, record.positive_integer_before_type_cast end + + # We do better with native types and do not return strings for everything. 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 end + + # We do better with native types and do not return strings for everything. + coerce_tests! :test_default_decimal_number + def test_default_decimal_number_coerced + record = DefaultNumber.new + assert_equal BigDecimal("2.78"), record.decimal_number + assert_equal 2.78, record.decimal_number_before_type_cast + end end From 0f92db25a442e4075bf9389080f86829bc161fe9 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Mon, 27 Apr 2020 14:48:40 +0900 Subject: [PATCH 0837/1412] Fix `EagerLoadingTooManyIdsTest#test_preloading_too_many_ids` Since https://github.com/rails/rails/pull/36074, `in_clause_length` will no longer help the adapter to avoid complex query limitation, and will be removed from Rails code base (https://github.com/rails/rails/pull/39057). --- .../sqlserver/core_ext/preloader.rb | 34 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 1 + 2 files changed, 35 insertions(+) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb new file mode 100644 index 000000000..5fca92532 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -0,0 +1,34 @@ +require "active_record/associations/preloader" + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module Preloader + private + + def records_for(ids) + ids.each_slice(in_clause_length).flat_map do |slice| + scope.where(association_key_name => slice).load do |record| + # Processing only the first owner + # because the record is modified but not an owner + owner = owners_by_key[convert_key(record[association_key_name])].first + association = owner.association(reflection.name) + association.set_inverse_instance(record) + end.records + end + end + + def in_clause_length + 10_000 + end + end + end + end + end +end + +ActiveSupport.on_load(:active_record) do + mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Preloader + ActiveRecord::Associations::Preloader::Association.prepend(mod) +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index af5b624d5..716662851 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -9,6 +9,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods' require 'active_record/connection_adapters/sqlserver/core_ext/query_methods' +require 'active_record/connection_adapters/sqlserver/core_ext/preloader' require 'active_record/connection_adapters/sqlserver/version' require 'active_record/connection_adapters/sqlserver/type' require 'active_record/connection_adapters/sqlserver/database_limits' From 9061a7a5673885d38255983e910e1e54d36b4cbe Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 27 Apr 2020 14:40:41 +0100 Subject: [PATCH 0838/1412] Raise ActiveRecord::InvalidForeignKey for foreign-key constraint violations on delete --- .../connection_adapters/sqlserver_adapter.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 716662851..145940063 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -332,15 +332,15 @@ def initialize_type_map(m = type_map) def translate_exception(e, message:, sql:, binds:) case message when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i - RecordNotUnique.new(message) - when /conflicted with the foreign key constraint/i - InvalidForeignKey.new(message) + RecordNotUnique.new(message, sql: sql, binds: binds) + when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i + InvalidForeignKey.new(message, sql: sql, binds: binds) when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message) + DeadlockVictim.new(message, sql: sql, binds: binds) when /database .* does not exist/i - NoDatabaseError.new(message) + NoDatabaseError.new(message, sql: sql, binds: binds) when /data would be truncated/ - ValueTooLong.new(message) + ValueTooLong.new(message, sql: sql, binds: binds) when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/ pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2) MismatchedForeignKey.new( @@ -352,9 +352,9 @@ def translate_exception(e, message:, sql:, binds:) primary_key: pk_id.object ) when /Cannot insert the value NULL into column.*does not allow nulls/ - NotNullViolation.new(message) + NotNullViolation.new(message, sql: sql, binds: binds) when /Arithmetic overflow error/ - RangeError.new(message) + RangeError.new(message, sql: sql, binds: binds) else super end From 6aa992d889f0cce5a156df574e5c67a8227bccbb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 28 Apr 2020 15:14:52 +0100 Subject: [PATCH 0839/1412] Coerce tests as SQL Server does not return string value for updated_at --- test/cases/coerced_tests.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 567357fff..e372f48da 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1163,6 +1163,19 @@ class CollectionCacheKeyTest < ActiveRecord::TestCase +module ActiveRecord + class CacheKeyTest < ActiveRecord::TestCase + # Like Mysql2 and PostgreSQL, SQL Server doesn't return a string value for updated_at. In the Rails tests + # the tests are skipped if adapter is Mysql2 or PostgreSQL. + coerce_tests! %r{cache_version is the same when it comes from the DB or from the user} + coerce_tests! %r{cache_version does NOT call updated_at when value is from the database} + coerce_tests! %r{cache_version does not truncate zeros when timestamp ends in zeros} + end +end + + + + require "models/book" module ActiveRecord class StatementCacheTest < ActiveRecord::TestCase From 14d461916055840252a24e18d35a49ce6bc92a8a Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Wed, 29 Apr 2020 12:09:33 +0300 Subject: [PATCH 0840/1412] Rails 6: Clean coerced tests (#793) * update AttributeMethodsTest tests * clean AdapterTestWithoutTransaction coerce_tests! statement * update CalculationsTest tests * add coerced test for test_create_without_primary_key_no_extra_query * remove test_relation_merging_with_merged_symbol_joins_is_aliased__coerced fixed in rails/50684d15c6546d3050d93896612edd9c5a0fca62 * update test_sanitize_sql_like_example_use_case_coerced updated in rails/de2f1e073b37f98a99334a909b8bc29f36ad506b * remove coerce test_eager_loading_too_may_ids fixed in https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/786 * remove fixed coerce test test "column names are quoted when using #from clause and model has ignored columns" fixed in rails/d53b259a29e77a60a02e9c219661e19ef62ef9aa * remove coerce NumericDataTest tests tests been fiexd in active recorde * updete ChangeSchemaTest coerce tests * remove fixed test_scopes_honor_current_scopes_from_when_defined * add tests comment * clean lines spacing * fix test_typecast_attribute_from_select_to_true_coerced --- test/cases/coerced_tests.rb | 195 ++++++++++++++++++++---------------- 1 file changed, 111 insertions(+), 84 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 567357fff..189a102d6 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -40,14 +40,14 @@ def test_validate_case_sensitive_uniqueness_by_default_coerced require 'models/event' module ActiveRecord class AdapterTest < ActiveRecord::TestCase - # I really dont think we can support legacy binds. + # I really don`t think we can support legacy binds. coerce_tests! :test_select_all_with_legacy_binds coerce_tests! :test_insert_update_delete_with_legacy_binds # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement - # So sp_executesql swallows this exception. Run without prpared to see it. + # So sp_executesql swallows this exception. Run without prepared to see it. coerce_tests! :test_value_limit_violations_are_translated_to_specific_exception def test_value_limit_violations_are_translated_to_specific_exception_coerced connection.unprepared_statement do @@ -60,11 +60,14 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced end end + + + module ActiveRecord class AdapterTestWithoutTransaction < ActiveRecord::TestCase # SQL Server does not allow truncation of tables that are referenced by foreign key # constraints. So manually remove/add foreign keys in test. - coerce_tests! :test_truncate_tables, :test_truncate_tables_with_query_cache + coerce_tests! :test_truncate_tables def test_truncate_tables_coerced # Remove foreign key constraint to allow truncation. @connection.remove_foreign_key :authors, :author_addresses @@ -85,6 +88,9 @@ def test_truncate_tables_coerced @connection.add_foreign_key :authors, :author_addresses end + # SQL Server does not allow truncation of tables that are referenced by foreign key + # constraints. So manually remove/add foreign keys in test. + coerce_tests! :test_truncate_tables_with_query_cache def test_truncate_tables_with_query_cache # Remove foreign key constraint to allow truncation. @connection.remove_foreign_key :authors, :author_addresses @@ -115,29 +121,28 @@ def test_truncate_tables_with_query_cache 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 - assert !topic.is_test? + 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 - assert topic.is_test? + assert_predicate topic, :is_test? end end -class NumericDataTest < ActiveRecord::TestCase - # We do not have do the DecimalWithoutScale type. - coerce_tests! :test_numeric_fields - coerce_tests! :test_numeric_fields_with_scale -end + class BasicsTest < ActiveRecord::TestCase + # Use square brackets as SQL Server escaped character coerce_tests! :test_column_names_are_escaped def test_column_names_are_escaped_coerced conn = ActiveRecord::Base.connection @@ -172,16 +177,6 @@ def test_update_date_time_attributes_with_default_timezone_local end end end - - # Need to escape `quoted_id` once it contains brackets - coerce_tests! %r{column names are quoted when using #from clause and model has ignored columns} - test "column names are quoted when using #from clause and model has ignored columns coerced" do - refute_empty Developer.ignored_columns - query = Developer.from("developers").to_sql - quoted_id = "#{Developer.quoted_table_name}.#{Developer.quoted_primary_key}" - - assert_match(/SELECT #{Regexp.escape(quoted_id)}.* FROM developers/, query) - end end @@ -191,6 +186,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase # Since @client.firm is a single first/top, and we use FETCH the order clause is used. coerce_tests! :test_belongs_to_does_not_use_order_by + # Square brackets around column name coerce_tests! :test_belongs_to_with_primary_key_joins_on_correct_column def test_belongs_to_with_primary_key_joins_on_correct_column_coerced sql = Client.joins(:firm_with_primary_key).to_sql @@ -230,6 +226,8 @@ def test_binds_are_logged_coerced end + + module ActiveRecord class InstrumentationTest < ActiveRecord::TestCase # This fails randomly due to schema cache being lost? @@ -250,6 +248,9 @@ def test_payload_name_on_load_coerced end end + + + class CalculationsTest < ActiveRecord::TestCase # This fails randomly due to schema cache being lost? coerce_tests! :test_offset_is_kept @@ -267,18 +268,20 @@ def test_should_return_decimal_average_of_integer_field_coerced assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s end + # Match SQL Server limit implementation coerce_tests! :test_limit_is_kept def test_limit_is_kept_coerced queries = capture_sql_ss { Account.limit(1).count } assert_equal 1, queries.length - _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1} + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first) end + # Match SQL Server limit implementation coerce_tests! :test_limit_with_offset_is_kept def test_limit_with_offset_is_kept_coerced queries = capture_sql_ss { Account.limit(1).offset(1).count } assert_equal 1, queries.length - _(queries.first).must_match %r{ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1} + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first) end # SQL Server needs an alias for the calculated column @@ -295,27 +298,44 @@ def test_distinct_count_all_with_custom_select_and_order_coerced + module ActiveRecord class Migration class ChangeSchemaTest < ActiveRecord::TestCase - # We test these. - coerce_tests! :test_create_table_with_bigint, - :test_create_table_with_defaults - end + # Integer.default is a number and not a string + coerce_tests! :test_create_table_with_defaults + def test_create_table_with_defaults_coerce + connection.create_table :testings do |t| + t.column :one, :string, default: "hello" + t.column :two, :boolean, default: true + t.column :three, :boolean, default: false + t.column :four, :integer, default: 1 + t.column :five, :text, default: "hello" + end - class ChangeSchemaWithDependentObjectsTest < ActiveRecord::TestCase - # In SQL Server you have to delete the tables yourself in the right order. - coerce_tests! :test_create_table_with_force_cascade_drops_dependent_objects + columns = connection.columns(:testings) + one = columns.detect { |c| c.name == "one" } + two = columns.detect { |c| c.name == "two" } + three = columns.detect { |c| c.name == "three" } + four = columns.detect { |c| c.name == "four" } + five = columns.detect { |c| c.name == "five" } + + assert_equal "hello", one.default + assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default) + assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default) + assert_equal 1, four.default + assert_equal "hello", five.default + end end end end + module ActiveRecord module ConnectionAdapters class QuoteARBaseTest < ActiveRecord::TestCase - # Use our date format. coerce_tests! :test_quote_ar_object def test_quote_ar_object_coerced @@ -329,13 +349,13 @@ def test_type_cast_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) end - end end end + module ActiveRecord class Migration class ColumnAttributesTest < ActiveRecord::TestCase @@ -438,7 +458,7 @@ def test_add_table_with_decimals_coerced class CoreTest < ActiveRecord::TestCase - # I think fixtures are useing the wrong time zone and the `:first` + # I think fixtures are using the wrong time zone and the `:first` # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is # getting local EST time for me and set to "09:28:00.0000000". coerce_tests! :test_pretty_print_persisted @@ -468,10 +488,12 @@ class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase # Skip this test with /tmp/my_schema_cache.yml path on Windows. coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end + class DatabaseTasksCreateAllTest < ActiveRecord::TestCase # We extend `local_database?` so that common VM IPs can be used. coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases end + class DatabaseTasksDropAllTest < ActiveRecord::TestCase # We extend `local_database?` so that common VM IPs can be used. coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases @@ -544,9 +566,11 @@ def test_count_with_include_coerced require 'models/topic' class FinderTest < ActiveRecord::TestCase + # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering}, - :test_find_doesnt_have_implicit_ordering # We have implicit ordering, via FETCH. + :test_find_doesnt_have_implicit_ordering + # Square brackets around column name coerce_tests! :test_exists_does_not_select_columns_without_alias def test_exists_does_not_select_columns_without_alias_coerced assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do @@ -554,6 +578,7 @@ def test_exists_does_not_select_columns_without_alias_coerced end end + # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } @@ -585,10 +610,11 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced end end end - end + + module ActiveRecord class Migration class ForeignKeyTest < ActiveRecord::TestCase @@ -660,6 +686,7 @@ def test_belongs_to_coerced require 'models/company' class InheritanceTest < ActiveRecord::TestCase + # Rails test required inserting to a identity column. coerce_tests! :test_a_bad_type_column def test_a_bad_type_column_coerced Company.connection.with_identity_insert_enabled('companies') do @@ -668,6 +695,7 @@ def test_a_bad_type_column_coerced assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } end + # Use Square brackets around column name coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced con = Account.connection @@ -688,26 +716,10 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase -class NamedScopingTest < ActiveRecord::TestCase - # This works now because we add an `order(:id)` sort to break the order tie for deterministic results. - coerce_tests! :test_scopes_honor_current_scopes_from_when_defined - def test_scopes_honor_current_scopes_from_when_defined_coerced - assert !Post.ranked_by_comments.order(:id).limit_by(5).empty? - assert !authors(:david).posts.ranked_by_comments.order(:id).limit_by(5).empty? - assert_not_equal Post.ranked_by_comments.order(:id).limit_by(5), authors(:david).posts.ranked_by_comments.order(:id).limit_by(5) - assert_not_equal Post.order(:id).top(5), authors(:david).posts.order(:id).top(5) - # Oracle sometimes sorts differently if WHERE condition is changed - assert_equal authors(:david).posts.ranked_by_comments.limit_by(5).to_a.sort_by(&:id), authors(:david).posts.top(5).to_a.sort_by(&:id) - assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5) - end -end - - - - require 'models/developer' require 'models/computer' class NestedRelationScopingTest < ActiveRecord::TestCase + # Assert SQL Server limit implementation coerce_tests! :test_merge_options def test_merge_options_coerced Developer.where('salary = 80000').scoping do @@ -723,6 +735,7 @@ def test_merge_options_coerced + require 'models/topic' class PersistenceTest < ActiveRecord::TestCase # Rails test required updating a identity column. @@ -749,6 +762,7 @@ def test_update_coerced + require 'models/author' class UpdateAllTest < ActiveRecord::TestCase # Rails test required updating a identity column. @@ -772,6 +786,7 @@ def test_update_all_doesnt_ignore_order_coerced require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase + # Use Square brackets around column name coerce_tests! :test_registering_new_handlers def test_registering_new_handlers_coerced Topic.predicate_builder.register_handler(Regexp, proc do |column, value| @@ -788,9 +803,15 @@ def test_registering_new_handlers_coerced class PrimaryKeysTest < ActiveRecord::TestCase - # Gonna trust Rails core for this. We end up with 2 querys vs 3 asserted - # but as far as I can tell, this is only one for us anyway. + # SQL Server does not have query for release_savepoint coerce_tests! :test_create_without_primary_key_no_extra_query + def test_create_without_primary_key_no_extra_query_coerced + klass = Class.new(ActiveRecord::Base) do + self.table_name = "dashboards" + end + klass.create! # warmup schema cache + assert_queries(2, ignore_none: true) { klass.create! } + end end @@ -806,7 +827,6 @@ def test_cache_does_not_wrap_results_in_arrays_coerced end end - # Same as original test except that we expect one query to be performed to retrieve the table's primary key. # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column # information then the primary key needs to be retrieved from the database again to generate the SQL causing the @@ -903,25 +923,6 @@ def test_reverse_arel_assoc_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order assert_equal topics(:second).title, topics.first.title end - -end - -class ActiveRecord::RelationTest < ActiveRecord::TestCase - coerce_tests! :test_relation_merging_with_merged_symbol_joins_is_aliased - def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced - categorizations_with_authors = Categorization.joins(:author) - queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a } - - nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size } - assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query" - - # using `\W` as the column separator - query_matches = queries.any? do |sql| - %r[INNER\s+JOIN\s+#{Regexp.escape(Author.quoted_table_name)}\s+\Wauthors_categorizations\W]i.match?(sql) - end - - assert query_matches, "Should be aliasing the child INNER JOINs in query" - end end @@ -929,15 +930,25 @@ def test_relation_merging_with_merged_symbol_joins_is_aliased__coerced require 'models/post' class SanitizeTest < ActiveRecord::TestCase + # Use nvarchar string (N'') in assert coerce_tests! :test_sanitize_sql_like_example_use_case def test_sanitize_sql_like_example_use_case_coerced searchable_post = Class.new(Post) do - def self.search(term) - where("title LIKE ?", sanitize_sql_like(term, '!')) + def self.search_as_method(term) + where("title LIKE ?", sanitize_sql_like(term, "!")) end + + scope :search_as_scope, -> (term) { + where("title LIKE ?", sanitize_sql_like(term, "!")) + } end - assert_sql(/\(title LIKE N'20!% !_reduction!_!!'\)/) do - searchable_post.search("20% _reduction_!").to_a + + assert_sql(/LIKE N'20!% !_reduction!_!!'/) do + searchable_post.search_as_method("20% _reduction_!").to_a + end + + assert_sql(/LIKE N'20!% !_reduction!_!!'/) do + searchable_post.search_as_scope("20% _reduction_!").to_a end end end @@ -971,6 +982,9 @@ def test_schema_dump_includes_decimal_options_coerced end end + + + class SchemaDumperDefaultsTest < ActiveRecord::TestCase # These date formats do not match ours. We got these covered in our dumper tests. coerce_tests! :test_schema_dump_defaults_with_universally_supported_types @@ -989,6 +1003,7 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase require 'models/topic' class TransactionTest < ActiveRecord::TestCase + # SQL Server does not have query for release_savepoint coerce_tests! :test_releasing_named_savepoints def test_releasing_named_savepoints_coerced Topic.transaction do @@ -1041,6 +1056,10 @@ def test_does_not_assume_id_column_as_primary_key_coerced assert_equal 'id', model.primary_key end end + + + + class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase # We have a few view tables. use includes vs equality. coerce_tests! :test_views @@ -1100,6 +1119,7 @@ def test_datetime_precision_is_truncated_on_assignment_coerced + class TimePrecisionTest < ActiveRecord::TestCase # datetime is rounded to increments of .000, .003, or .007 seconds coerce_tests! :test_time_precision_is_truncated_on_assignment @@ -1124,6 +1144,7 @@ def test_time_precision_is_truncated_on_assignment_coerced + class DefaultNumbersTest < ActiveRecord::TestCase # We do better with native types and do not return strings for everything. coerce_tests! :test_default_positive_integer @@ -1196,9 +1217,11 @@ def schema_dump_path end end -class UnsafeRawSqlTest < ActiveRecord::TestCase - # Coerce the originals as they use 'LENGTH' instead of SQL Servers 'LEN' function. + + +class UnsafeRawSqlTest < ActiveRecord::TestCase + # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} test 'order: always allows Arel' do ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } @@ -1207,6 +1230,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_depr, ids_disabled end + # Use LEN() vs length() function. coerce_tests! %r{pluck: always allows Arel} test "pluck: always allows Arel" do values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } @@ -1215,6 +1239,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal values_depr, values_disabled end + # Use LEN() vs length() function. coerce_tests! %r{order: allows valid Array arguments} test "order: allows valid Array arguments" do ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id) @@ -1225,10 +1250,11 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end - end + + class ReservedWordTest < ActiveRecord::TestCase coerce_tests! :test_change_columns def test_change_columns_coerced @@ -1241,6 +1267,7 @@ def test_change_columns_coerced + class OptimisticLockingTest < ActiveRecord::TestCase # We do not allow updating identities, but we can test using a non-identity key coerce_tests! :test_update_with_dirty_primary_key @@ -1264,7 +1291,9 @@ def test_update_with_dirty_primary_key_coerced + class RelationMergingTest < ActiveRecord::TestCase + # Use nvarchar string (N'') in assert coerce_tests! :test_merging_with_order_with_binds def test_merging_with_order_with_binds_coerced relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"])) @@ -1273,10 +1302,6 @@ def test_merging_with_order_with_binds_coerced end -class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase - # Temporarily coerce this test due to https://github.com/rails/rails/issues/34945 - coerce_tests! :test_eager_loading_too_may_ids -end module ActiveRecord @@ -1289,6 +1314,8 @@ class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase 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. From 0f9779e6dd01bf6afb7c3742fa92bc8e16c3d8da Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 29 Apr 2020 10:10:37 +0100 Subject: [PATCH 0841/1412] Rails 6: Coerce time no precision test (#791) * Coerce test as time has default precision * Fixed typo --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/coerced_tests.rb | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a359d86a2..66ca32a47 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -236,7 +236,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) if (0..7) === precision column_type_sql << "(#{precision})" else - raise(ActiveRecordError, "The dattime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7") + raise(ActiveRecordError, "The datetime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7") end end column_type_sql diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 628b120d4..c36ebeb2c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1140,6 +1140,9 @@ def test_time_precision_is_truncated_on_assignment_coerced assert_equal 0, foo.start.nsec assert_equal 123457000, foo.finish.nsec end + + # SQL Server uses default precision for time. + coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment end From 967c93e8ecd1fc9ad2868ead48b6bd663e75a267 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 29 Apr 2020 10:10:55 +0100 Subject: [PATCH 0842/1412] Take and first use implicit ordering on identifier column (#789) --- test/cases/coerced_tests.rb | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c36ebeb2c..fe56eb762 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -878,6 +878,27 @@ def test_reverse_order_with_function_other_predicates_coerced # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering} + # We have implicit ordering, via FETCH. + coerce_tests! :test_reorder_with_take + def test_reorder_with_take_coerced + sql_log = capture_sql do + 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}" + end + + # We have implicit ordering, via FETCH. + coerce_tests! :test_reorder_with_first + def test_reorder_with_first_coerced + sql_log = capture_sql do + assert Post.order(:title).reorder(nil).first + 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}" + end + + # We are not doing order duplicate removal anymore. coerce_tests! :test_order_using_scoping From a07552c26fc90d992c872c62163089ee623d5b58 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 29 Apr 2020 10:12:03 +0100 Subject: [PATCH 0843/1412] Rails 6: Support lazy transactions (#780) * Support lazy transactions * aaded materialize_transactions guard to connection uses. for implementing supports for lazy transactions https://github.com/rails/rails/pull/32647#issue-182891795 * Moved materialize_transactions calls * Fix typo * Materialize transaction in do_execute Co-authored-by: Yehuda Goldberg --- .../connection_adapters/sqlserver/database_statements.rb | 8 ++++++++ .../connection_adapters/sqlserver_adapter.rb | 4 ++++ test/cases/migration_test_sqlserver.rb | 2 +- 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index ebea06705..e5643940b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,6 +14,8 @@ def execute(sql, name = nil) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end + materialize_transactions + if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } else @@ -26,6 +28,8 @@ def exec_query(sql, name = 'SQL', binds = [], prepare: false) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end + materialize_transactions + sp_executesql(sql, name, binds, prepare: prepare) end @@ -136,6 +140,8 @@ def default_insert_value(column) # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) + materialize_transactions + vars = if variables.any? && variables.first.is_a?(Hash) variables.first.map { |k, v| "@#{k} = #{quote(v)}" } else @@ -268,6 +274,8 @@ def set_identity_insert(table_name, enable = true) # === SQLServer Specific (Executing) ============================ # def do_execute(sql, name = 'SQL') + materialize_transactions + log(sql, name) { raw_connection_do(sql) } end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 145940063..546f6a8db 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -145,6 +145,10 @@ def supports_savepoints? true end + def supports_lazy_transactions? + true + end + def supports_in_memory_oltp? @version_year >= 2014 end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index a687ce6b4..7348bc15b 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -45,7 +45,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase Person.reset_column_information end - it 'not drop the default contraint if just renaming' do + 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| row['constraint_type'] == "DEFAULT on column string_with_pretend_paren_three" From 9b71375c25cd9cb7dfbced78632a32623f088fb8 Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Wed, 29 Apr 2020 15:51:04 +0300 Subject: [PATCH 0844/1412] Coerce test an empty transaction does not raise if preventing writes. (#788) The transaction in the test become SavepointTransaction because tests automatically wrapped in transaction, and SQL Server does not have query for release_savepoint --- test/cases/coerced_tests.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index fe56eb762..bbf62bc87 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -177,6 +177,18 @@ def test_update_date_time_attributes_with_default_timezone_local end end end + + # SQL Server does not have query for release_savepoint + coerce_tests! %r{an empty transaction does not raise if preventing writes} + test "an empty transaction does not raise if preventing writes coerced" do + ActiveRecord::Base.connection_handler.while_preventing_writes do + assert_queries(1, ignore_none: true) do + Bird.transaction do + ActiveRecord::Base.connection.materialize_transactions + end + end + end + end end From d36e5c4c108aefcc18bb84137ac603db0b0ce519 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 29 Apr 2020 15:17:13 +0100 Subject: [PATCH 0845/1412] Add issue template --- .github/issue_template.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/issue_template.md diff --git a/.github/issue_template.md b/.github/issue_template.md new file mode 100644 index 000000000..b51d61e15 --- /dev/null +++ b/.github/issue_template.md @@ -0,0 +1,23 @@ +## Issue + + +## Expected behavior + + +## Actual behavior + + +## How to reproduce + + +# Details + +- **Rails version**: `x.x.x` +- **SQL Server adapter version**: `x.x.x` +- **TinyTDS version**: `x.x.x` +- **FreeTDS details**: + + ``` + run `tsql -C` and paste here the output. + ``` + From e3871801d67221034e8fb2dff42d6c24945d9497 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 29 Apr 2020 15:25:36 +0100 Subject: [PATCH 0846/1412] Fix issue template --- .github/issue_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/issue_template.md b/.github/issue_template.md index b51d61e15..011951948 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -10,7 +10,7 @@ ## How to reproduce -# Details +## Details - **Rails version**: `x.x.x` - **SQL Server adapter version**: `x.x.x` From fe348fa163ced80e0531098b916e6f493d708998 Mon Sep 17 00:00:00 2001 From: Yehuda Goldberg Date: Thu, 30 Apr 2020 13:28:26 +0300 Subject: [PATCH 0847/1412] Rails 6: add database_exists? (#796) * add database_exists? https://github.com/rails/rails/pull/36471 * fix translate error to ActiveRecord::NoDatabaseError in connection creating add "test bad connection" like other adapters --- .../connection_adapters/sqlserver_adapter.rb | 6 ++++++ lib/active_record/sqlserver_base.rb | 6 ++++++ test/cases/adapter_test_sqlserver.rb | 19 +++++++++++++++++++ 3 files changed, 31 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 546f6a8db..bd1d972a9 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -81,6 +81,12 @@ def schema_creation SQLServer::SchemaCreation.new self end + def self.database_exists?(config) + !!ActiveRecord::Base.sqlserver_connection(config) + rescue ActiveRecord::NoDatabaseError + false + end + def supports_ddl_transactions? true end diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 4601b41d1..0b86358f4 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -11,6 +11,12 @@ def sqlserver_connection(config) #:nodoc: raise ArgumentError, "Unknown connection mode in #{config.inspect}." end ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode)) + rescue TinyTds::Error => e + if e.message.match(/database .* does not exist/i) + raise ActiveRecord::NoDatabaseError + else + raise + end end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index fcad2b494..e9f889ac9 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -65,6 +65,25 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal 'customers', connection.send(:get_table_name, basic_select_sql) end + it 'test bad connection' do + assert_raise ActiveRecord::NoDatabaseError do + config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + ActiveRecord::Base.sqlserver_connection config + end + end + + it 'test database exists returns false if database does not exist' do + config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), + 'expected database to not exist' + end + + it 'test database exists returns true when the database exists' do + config = ActiveRecord::Base.configurations['arunit'] + assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), + "expected database #{config[:database]} to exist" + end + describe 'with different language' do before do From d16d9582525430d2ab68eef71156bfc5ca57bbc6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 30 Apr 2020 11:35:09 +0100 Subject: [PATCH 0848/1412] Rails 6: Use same sqlite3 and pg versions as Rails 6.0.2.2 (#794) * Use same sqlite3 and pg versions as Rails 6.0.2.2 * Bump to version used by CI * Revert version --- Gemfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 43208ba7e..38e71be06 100644 --- a/Gemfile +++ b/Gemfile @@ -7,8 +7,9 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec -gem 'sqlite3' -gem 'pg' +gem "sqlite3", "~> 1.4" +gem "pg", ">= 0.18.0" + gem 'bcrypt' gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] From 3eba185da8eaeb020d2969854ebc2326e7da21f0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 30 Apr 2020 14:37:54 +0100 Subject: [PATCH 0849/1412] Updates for bulk insert/upsert (#795) Typo --- .../sqlserver/database_statements.rb | 13 +++++++ .../connection_adapters/sqlserver_adapter.rb | 16 +++++++++ test/cases/coerced_tests.rb | 36 +++++++++++++++++++ 3 files changed, 65 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e5643940b..68813c489 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -137,6 +137,17 @@ def default_insert_value(column) end private :default_insert_value + def build_insert_sql(insert) # :nodoc: + sql = +"INSERT #{insert.into}" + + if returning = insert.send(:insert_all).returning + sql << " OUTPUT " << returning.map {|column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + end + + sql << " #{insert.values_list}" + sql + end + # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) @@ -243,10 +254,12 @@ def sql_for_insert(sql, pk, binds) table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) end + sql = if pk && use_output_inserted? && !database_prefix_remote_server? quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted table_name ||= get_table_name(sql) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) + if exclude_output_inserted id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted <<~SQL.squish diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index bd1d972a9..86fc2c19a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -159,6 +159,22 @@ def supports_in_memory_oltp? @version_year >= 2014 end + def supports_insert_returning? + true + end + + def supports_insert_on_duplicate_skip? + false + end + + def supports_insert_on_duplicate_update? + false + end + + def supports_insert_conflict_target? + false + end + def disable_referential_integrity tables = tables_with_referential_integrity tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" } diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bbf62bc87..87d0b690c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1407,3 +1407,39 @@ class EnumTest < ActiveRecord::TestCase Book.connection.add_index(:books, [:author_id, :name], unique: true) end end + + + + +require 'models/task' +class QueryCacheExpiryTest < ActiveRecord::TestCase + + # SQL Server does not support skipping or upserting duplicates. + coerce_tests! :test_insert_all + def test_insert_all_coerced + assert_raises(ArgumentError, /does not support skipping duplicates/) do + Task.cache { Task.insert({ starting: Time.now }) } + end + + assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + Task.cache { Task.insert_all!([{ starting: Time.now }]) } + end + + assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + Task.cache { Task.insert!({ starting: Time.now }) } + end + + assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + Task.cache { Task.insert_all!([{ starting: Time.now }]) } + end + + assert_raises(ArgumentError, /does not support upsert/) do + Task.cache { Task.upsert({ starting: Time.now }) } + end + + assert_raises(ArgumentError, /does not support upsert/) do + Task.cache { Task.upsert_all([{ starting: Time.now }]) } + end + end + +end From 6bdfc09abf86cf1666aaa55b6e76646699f821cf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 30 Apr 2020 14:48:50 +0100 Subject: [PATCH 0850/1412] Fix randomly failing test (#797) --- test/cases/coerced_tests.rb | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 87d0b690c..bbe23431f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -205,6 +205,17 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced assert_no_match(/\[firm_with_primary_keys_companies\]\.\[id\]/, sql) assert_match(/\[firm_with_primary_keys_companies\]\.\[name\]/, sql) end + + # Asserted SQL to get one row different from original test. + coerce_tests! :test_belongs_to + def test_belongs_to_coerced + client = Client.find(3) + first_firm = companies(:first_firm) + assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_equal first_firm, client.firm + assert_equal first_firm.name, client.firm.name + end + end end @@ -656,7 +667,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_has_one_coerced firm = companies(:first_firm) first_account = Account.find(1) - assert_sql(/FETCH NEXT @1 ROWS ONLY(.)*@1 = 1/) do + assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do assert_equal first_account, firm.account assert_equal first_account.credit_limit, firm.account.credit_limit end @@ -671,7 +682,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase coerce_tests! :test_has_one_through_executes_limited_query def test_has_one_through_executes_limited_query_coerced boring_club = clubs(:boring_club) - assert_sql(/FETCH NEXT @3 ROWS ONLY(.)*@3 = 1/) do + assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do assert_equal boring_club, @member.general_club end end @@ -680,21 +691,6 @@ def test_has_one_through_executes_limited_query_coerced -class BelongsToAssociationsTest < ActiveRecord::TestCase - # Asserted SQL to get one row different from original test. - coerce_tests! :test_belongs_to - def test_belongs_to_coerced - client = Client.find(3) - first_firm = companies(:first_firm) - assert_sql(/FETCH NEXT @3 ROWS ONLY(.)*@3 = 1/) do - assert_equal first_firm, client.firm - assert_equal first_firm.name, client.firm.name - end - end -end - - - require 'models/company' class InheritanceTest < ActiveRecord::TestCase From 66db048dd4aec53de6edfbb961c7132e3a4faee8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 5 May 2020 17:22:17 +0100 Subject: [PATCH 0851/1412] Rails 6: Coerce eagerload too many IDs test (#798) * Coerce test to managable number of records * Assert that unprepared statement being used * Updated test * Fix test --- test/cases/coerced_tests.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bbe23431f..f5b386e48 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1407,6 +1407,7 @@ class EnumTest < ActiveRecord::TestCase + require 'models/task' class QueryCacheExpiryTest < ActiveRecord::TestCase @@ -1437,5 +1438,28 @@ def test_insert_all_coerced Task.cache { Task.upsert_all([{ starting: Time.now }]) } end end +end + + +require 'models/citation' +class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase + # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and + # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012) + # (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/). + # However, you cannot change the compatibility level during a test. The purpose of the test is to ensure that an + # unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test + # still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of + # adapter is 2,098. + coerce_tests! :test_eager_loading_too_may_ids + def test_eager_loading_too_may_ids_coerced + # Remove excess records. + Citation.limit(32768).order(id: :desc).delete_all + + # Perform test + citation_count = Citation.count + assert_sql(/WHERE \(\[citations\]\.\[id\] IN \(0, 1/) do + assert_equal citation_count, Citation.eager_load(:citations).offset(0).size + end + end end From ee8af236664c862beabfc14df70747c3e44a2850 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 6 May 2020 16:40:37 +0100 Subject: [PATCH 0852/1412] Fix randomly failing tests by loading models schema --- test/cases/coerced_tests.rb | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f5b386e48..772e359bc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -57,6 +57,13 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced assert_not_nil error.cause end end + + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes + def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema) + original_test_errors_when_an_insert_query_is_called_while_preventing_writes + end end end @@ -253,20 +260,11 @@ def test_binds_are_logged_coerced module ActiveRecord class InstrumentationTest < ActiveRecord::TestCase - # This fails randomly due to schema cache being lost? + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_payload_name_on_load def test_payload_name_on_load_coerced - Book.create(name: "test book") - Book.first - subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - if event.payload[:sql].match "SELECT" - assert_equal "Book Load", event.payload[:name] - end - end - Book.first - ensure - ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + Book.send(:load_schema) + original_test_payload_name_on_load end end end @@ -275,13 +273,11 @@ def test_payload_name_on_load_coerced class CalculationsTest < ActiveRecord::TestCase - # This fails randomly due to schema cache being lost? + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept def test_offset_is_kept_coerced - Account.first - queries = assert_sql { Account.offset(1).count } - assert_equal 1, queries.length - assert_match(/OFFSET/, queries.first) + Account.send(:load_schema) + original_test_offset_is_kept end # Are decimal, not integer. From b1e02f977e9873eacc755bd74a6d69ec2429ee86 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 6 May 2020 17:59:38 +0100 Subject: [PATCH 0853/1412] Rails 6: Log subscriber workaround (#799) * Coerce and call original * Added comment --- test/cases/coerced_tests.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f5b386e48..5f57ce05d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1463,3 +1463,14 @@ def test_eager_loading_too_may_ids_coerced end end end + + + + +class LogSubscriberTest < ActiveRecord::TestCase + # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. + coerce_tests! :test_vebose_query_logs + def test_vebose_query_logs_coerced + original_test_vebose_query_logs + end +end From 4712c928a7fbbf43558b34b0782f6b33aba482ba Mon Sep 17 00:00:00 2001 From: Larry Reid Date: Thu, 7 May 2020 01:51:55 -0700 Subject: [PATCH 0854/1412] How to Develop ActiveRecord SQL Server Adapter with Pre-Installed MS SQL (#726) * Document use of Vagrant box [ci skip] * Move vagrant document to docs directory [ci skip] * Update docs/DEVELOPING_WITH_VAGRANT.md Co-Authored-By: Aidan Haran * Use adapter-specific Vagrant box Co-authored-by: Aidan Haran --- RUNNING_UNIT_TESTS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 59ac591eb..4b06c391a 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -3,6 +3,9 @@ This process is much easier than it has been before! +## MS SQL SERVER + +If you don't have easy access to MS SQL Server, you can set up a Vagrant/VirtualBox virtual machine with MS SQL Server. [Here's how](/https://github.com/rails-sqlserver/activerecord-sqlserver-adapter-dev-box). ## TL;DR From a72acbc4dfea68d79976c21335ec94e8da71bd87 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 7 May 2020 12:07:32 +0100 Subject: [PATCH 0855/1412] Add metadata to the gemspec --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 39 +++++++++++++++++--------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/VERSION b/VERSION index 09b254e90..9746b9fe0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0 +6.0.0.rc1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 783e7742e..1b66fbf32 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -1,21 +1,32 @@ -# -*- encoding: utf-8 -*- -$:.push File.expand_path("../lib", __FILE__) -require "active_record/connection_adapters/sqlserver/version" +# frozen_string_literal: true + +version = File.read(File.expand_path("VERSION", __dir__)).strip Gem::Specification.new do |spec| - spec.name = 'activerecord-sqlserver-adapter' - spec.version = ActiveRecord::ConnectionAdapters::SQLServer::Version::VERSION + spec.name = "activerecord-sqlserver-adapter" spec.platform = Gem::Platform::RUBY - spec.license = 'MIT' - spec.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward'] - 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.version = version + + spec.required_ruby_version = ">= 2.5.0" + + spec.license = "MIT" + spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward"] + 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 = { + "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}", + } + 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.require_paths = ['lib'] - spec.add_dependency 'activerecord', '~> 6.0.0' - spec.add_dependency 'tiny_tds' + spec.require_paths = ["lib"] + + spec.add_dependency "activerecord", "~> 6.0.0" + spec.add_dependency "tiny_tds" end From 6a6650b2836761be27904aa8b53525964ae0bfc7 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 7 May 2020 12:07:59 +0100 Subject: [PATCH 0856/1412] Update README for Rails 6 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 55dd629f2..dee97c490 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,15 @@ ## About The Adapter -The SQL Server adapter for ActiveRecord v5.2 using SQL Server 2012 or higher. +The SQL Server adapter for ActiveRecord v6.0 using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.1.x version of the adapter is only for the latest 5.1 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.2.x version of the adapter is only for the latest 5.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. #### Native Data Type Support We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. -The following types (date, datetime2, datetimeoffset, time) all require TDS version 7.3 with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to "7.3". The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. +The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version 7.3 with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to "7.3". The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a pecision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. From 708570db19d9179bdf070769040624d5a9f597a0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 7 May 2020 14:17:58 +0100 Subject: [PATCH 0857/1412] Workaround for randomly failing test --- test/cases/coerced_tests.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 12279f93c..f3fc40323 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1470,3 +1470,15 @@ def test_vebose_query_logs_coerced original_test_vebose_query_logs end end + + + + +class ActiveRecordSchemaTest < ActiveRecord::TestCase + # Workaround for randomly failing test. + coerce_tests! :test_has_primary_key + def test_has_primary_key_coerced + @schema_migration.reset_column_information + original_test_has_primary_key + end +end From 0c8cdc877cd0721579f79cee1d28df8e13c421d0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 7 May 2020 15:00:06 +0100 Subject: [PATCH 0858/1412] Coerce predicate builder tests --- test/cases/coerced_tests.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 12279f93c..61234f3b4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -790,15 +790,16 @@ def test_update_all_doesnt_ignore_order_coerced require 'models/topic' module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase - # Use Square brackets around column name + # Same as original test except string has `N` prefix to indicate unicode string. coerce_tests! :test_registering_new_handlers def test_registering_new_handlers_coerced - Topic.predicate_builder.register_handler(Regexp, proc do |column, value| - Arel::Nodes::InfixOperation.new('~', column, Arel.sql(value.source)) - end) - assert_match %r{\[topics\].\[title\] ~ rails}i, Topic.where(title: /rails/).to_sql - ensure - Topic.reset_column_information + assert_match %r{#{Regexp.escape(topic_title)} ~ N'rails'}i, Topic.where(title: /rails/).to_sql + end + + # 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 end end end From 499a6c7ebea3c8e6447c4c6fe9f53f7344741145 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 7 May 2020 17:05:52 +0100 Subject: [PATCH 0859/1412] Remove invalid order from FROM subquery --- lib/arel/visitors/sqlserver.rb | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 449165f91..190239192 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -58,6 +58,11 @@ def visit_Arel_Nodes_Limit o, collector end end + def visit_Arel_Nodes_Grouping(o, collector) + remove_invalid_ordering_from_select_statement(o.expr) + super + end + def visit_Arel_Nodes_SelectStatement o, collector @select_statement = o distinct_One_As_One_Is_So_Not_Fetch o @@ -127,14 +132,11 @@ def visit_Arel_Nodes_OuterJoin o, collector visit o.right, collector end - # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer - # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables, - # subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified." def collect_in_clause(left, right, collector) if Array === right - right.each { |node| remove_invalid_ordering_from_in_clause(node) } + right.each { |node| remove_invalid_ordering_from_select_statement(node) } else - remove_invalid_ordering_from_in_clause(right) + remove_invalid_ordering_from_select_statement(right) end super @@ -231,7 +233,10 @@ def remote_server_table_name o ).quoted end - def remove_invalid_ordering_from_in_clause(node) + # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer + # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables, + # subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified." + def remove_invalid_ordering_from_select_statement(node) return unless Arel::Nodes::SelectStatement === node node.orders = [] unless node.offset || node.limit From 01a7946974c4205cc8014540b36b624b3f858592 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 7 May 2020 17:45:48 +0100 Subject: [PATCH 0860/1412] Update appveyor matrix --- appveyor.yml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index a4db20e3b..9e39b01b8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -33,7 +33,9 @@ environment: CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - - ruby_version: "23-x64" - - ruby_version: "23" - - ruby_version: "22-x64" - - ruby_version: "22" + - ruby_version: "25-x64" + - ruby_version: "25" + - ruby_version: "26-x64" + - ruby_version: "26" + - ruby_version: "27-x64" + - ruby_version: "27" From e72371f8f269cec0cf6f26c1ebfdd473a74794bd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 7 May 2020 17:52:06 +0100 Subject: [PATCH 0861/1412] Update tiny_tds version for appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 9e39b01b8..56d62daf4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -2,7 +2,7 @@ init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - - SET TINYTDS_VERSION=2.1.0 + - SET TINYTDS_VERSION=2.1.3.pre clone_depth: 5 skip_tags: true matrix: From 17b205367f8339991a89f8ae3ee0eb1b774c052e Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 7 May 2020 18:08:53 +0100 Subject: [PATCH 0862/1412] Cleanup appveyor configuration --- appveyor.yml | 25 +++++++++++++------------ test/appveyor/dbsetup.ps1 | 8 ++++---- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index 56d62daf4..bc3ffa6c9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,12 +1,19 @@ +image: Visual Studio 2017 +skip_tags: true +clone_depth: 5 +build: off +matrix: + fast_finish: true + +services: + - mssql2014 + init: - SET PATH=C:\Ruby%ruby_version%\bin;%PATH% - SET PATH=C:\MinGW\msys\1.0\bin;%PATH% - SET RAKEOPT=-rdevkit - SET TINYTDS_VERSION=2.1.3.pre -clone_depth: 5 -skip_tags: true -matrix: - fast_finish: true + install: - ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number" - ruby --version @@ -14,19 +21,13 @@ install: - bundle install - gem uninstall bcrypt - gem install bcrypt --platform=ruby -build: off + test_script: - powershell -File "%APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.ps1" - timeout /t 4 /nobreak > NUL - - ps: Start-Service 'MSSQL$SQL2014' - - timeout /t 4 /nobreak > NUL - sqlcmd -S ".\SQL2014" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2014" - - ps: Stop-Service 'MSSQL$SQL2014' - - ps: Start-Service 'MSSQL$SQL2012SP1' - - timeout /t 4 /nobreak > NUL - - sqlcmd -S ".\SQL2012SP1" -U sa -P Password12! -i %APPVEYOR_BUILD_FOLDER%\test\appveyor\dbsetup.sql - - bundle exec rake test ACTIVERECORD_UNITTEST_DATASERVER="localhost\SQL2012SP1" + environment: CI_AZURE_HOST: secure: VChrioaIWkf9iuuaSs4cryiA4honrADgZqNC0++begg= diff --git a/test/appveyor/dbsetup.ps1 b/test/appveyor/dbsetup.ps1 index 7890a86e6..d5d108432 100644 --- a/test/appveyor/dbsetup.ps1 +++ b/test/appveyor/dbsetup.ps1 @@ -5,15 +5,15 @@ Write-Output "Setting up..." Write-Output "Setting variables..." $serverName = $env:COMPUTERNAME -$instances = @('SQL2012SP1', 'SQL2014') +$instanceNames = @('SQL2014') $smo = 'Microsoft.SqlServer.Management.Smo.' $wmi = new-object ($smo + 'Wmi.ManagedComputer') Write-Output "Configure Instances..." -foreach ($instance in $instances) { - Write-Output "Instance $instance ..." +foreach ($instanceName in $instanceNames) { + Write-Output "Instance $instanceName ..." Write-Output "Enable TCP/IP and port 1433..." - $uri = "ManagedComputer[@Name='$serverName']/ServerInstance[@Name='$instance']/ServerProtocol[@Name='Tcp']" + $uri = "ManagedComputer[@Name='$serverName']/ServerInstance[@Name='$instanceName']/ServerProtocol[@Name='Tcp']" $tcp = $wmi.GetSmoObject($uri) $tcp.IsEnabled = $true foreach ($ipAddress in $Tcp.IPAddresses) { From cd98ba59e79e26d0333f8102b5d58525931e0552 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 11 May 2020 14:03:22 +0100 Subject: [PATCH 0863/1412] Fix database tasks tests for SQL Server --- test/cases/coerced_tests.rb | 124 ++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 96feb1556..2bae57df2 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -503,6 +503,130 @@ module ConnectionAdapters module ActiveRecord + # The original module is hardcoded for PostgreSQL/SQLite/MySQL tests. + module DatabaseTasksSetupper + def setup + @sqlserver_tasks = + Class.new do + def create; end + def drop; end + def purge; end + def charset; end + def collation; end + def structure_dump(*); end + def structure_load(*); end + end.new + + $stdout, @original_stdout = StringIO.new, $stdout + $stderr, @original_stderr = StringIO.new, $stderr + end + + def with_stubbed_new + ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do + yield + end + end + end + + class DatabaseTasksCreateTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_create + with_stubbed_new do + assert_called(eval("@sqlserver_tasks"), :create) do + ActiveRecord::Tasks::DatabaseTasks.create "adapter" => :sqlserver + end + end + end + end + + class DatabaseTasksDropTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_drop + with_stubbed_new do + assert_called(eval("@sqlserver_tasks"), :drop) do + ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => :sqlserver + end + end + end + end + + class DatabaseTasksPurgeTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_purge + with_stubbed_new do + assert_called(eval("@sqlserver_tasks"), :purge) do + ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => :sqlserver + end + end + end + end + + class DatabaseTasksCharsetTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_charset + with_stubbed_new do + assert_called(eval("@sqlserver_tasks"), :charset) do + ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => :sqlserver + end + end + end + + end + + class DatabaseTasksCollationTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_collation + with_stubbed_new do + assert_called(eval("@sqlserver_tasks"), :collation) do + ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => :sqlserver + end + end + end + end + + class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_structure_dump + with_stubbed_new do + assert_called_with( + eval("@sqlserver_tasks"), :structure_dump, + ["awesome-file.sql", nil] + ) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql") + end + end + end + end + + class DatabaseTasksStructureLoadTest < ActiveRecord::TestCase + # Coerce PostgreSQL/SQLite/MySQL tests. + coerce_all_tests! + + def test_sqlserver_structure_load + with_stubbed_new do + assert_called_with( + eval("@sqlserver_tasks"), + :structure_load, + ["awesome-file.sql", nil] + ) do + ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql") + end + end + end + end + class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase # Skip this test with /tmp/my_schema_cache.yml path on Windows. coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ From c6d99eea131c825651ce0428ad38bb97d07530c8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 12 May 2020 10:53:46 +0100 Subject: [PATCH 0864/1412] Force loading of schema --- test/cases/coerced_tests.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 96feb1556..efdf4dcc8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -61,7 +61,7 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema) + Subscriber.send(:load_schema!) original_test_errors_when_an_insert_query_is_called_while_preventing_writes end end @@ -263,7 +263,7 @@ class InstrumentationTest < ActiveRecord::TestCase # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_payload_name_on_load def test_payload_name_on_load_coerced - Book.send(:load_schema) + Book.send(:load_schema!) original_test_payload_name_on_load end end @@ -276,7 +276,7 @@ class CalculationsTest < ActiveRecord::TestCase # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept def test_offset_is_kept_coerced - Account.send(:load_schema) + Account.send(:load_schema!) original_test_offset_is_kept end From fcb3fdb867ea695c28d85002bb24f56418365c10 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 12 May 2020 11:25:50 +0100 Subject: [PATCH 0865/1412] Rails 6: Coerce reaper test using fork (#809) * Coerce reaper test using fork * Coerce test only if fork isn't supported --- test/cases/coerced_tests.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 96feb1556..5833018f4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1483,3 +1483,15 @@ def test_has_primary_key_coerced original_test_has_primary_key end end + + + + +module ActiveRecord + module ConnectionAdapters + class ReaperTest < ActiveRecord::TestCase + # Coerce can be removed if Rails version > 6.0.3 + coerce_tests! :test_connection_pool_starts_reaper_in_fork unless Process.respond_to?(:fork) + end + end +end From 99e711a8b4c6243f4a76697fa569ad6904e6ec04 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 11 May 2020 16:04:55 +0100 Subject: [PATCH 0866/1412] Skip binary fixtures test on Windows --- test/cases/coerced_tests.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 884a43b1c..9a82443ca 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1619,3 +1619,11 @@ class ReaperTest < ActiveRecord::TestCase end end end + + + + +class FixturesTest < ActiveRecord::TestCase + # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged. + coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ +end From 436fc423a79413dca21348c28c701282c9f5bc75 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 12 May 2020 16:26:07 +0100 Subject: [PATCH 0867/1412] Coerce test on Windows --- test/cases/coerced_tests.rb | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9a82443ca..5b3129f81 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1627,3 +1627,12 @@ class FixturesTest < ActiveRecord::TestCase # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged. coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ end + + + + +class ReloadModelsTest < ActiveRecord::TestCase + # Skip test on Windows. The number of arguements 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/ +end From 82217ab87f984b13897a3212d058f2d385abc035 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 15:52:48 +0100 Subject: [PATCH 0868/1412] Fix Ruby 2.7 kwargs warnings --- .../sqlserver/schema_statements.rb | 6 +- .../sqlserver/table_definition.rb | 80 +++++++++---------- .../sqlserver/transaction.rb | 2 +- .../sqlserver/type/money.rb | 2 +- .../sqlserver/type/small_money.rb | 2 +- .../sqlserver/type/unicode_varchar.rb | 2 +- .../sqlserver/type/unicode_varchar_max.rb | 2 +- .../sqlserver/type/varbinary.rb | 2 +- .../sqlserver/type/varbinary_max.rb | 2 +- .../sqlserver/type/varchar.rb | 2 +- .../sqlserver/type/varchar_max.rb | 2 +- 11 files changed, 52 insertions(+), 52 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 66ca32a47..94f7dcfdc 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -13,7 +13,7 @@ def create_table(table_name, **options) res end - def drop_table(table_name, options = {}) + def drop_table(table_name, **options) # Mimic CASCADE option as best we can. if options[:force] == :cascade execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata| @@ -555,8 +555,8 @@ def views_real_column_name(table_name, column_name) match_data ? match_data[1] : column_name end - def create_table_definition(*args) - SQLServer::TableDefinition.new(self, *args) + def create_table_definition(*args, **options) + SQLServer::TableDefinition.new(self, *args, **options) end end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index de66ea79c..878f09e4f 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -14,86 +14,86 @@ def primary_key(name, type = :primary_key, **options) super end - def primary_key_nonclustered(*args, **options) - args.each { |name| column(name, :primary_key_nonclustered, options) } + def primary_key_nonclustered(*names, **options) + names.each { |name| column(name, :primary_key_nonclustered, **options) } end - def real(*args, **options) - args.each { |name| column(name, :real, options) } + def real(*names, **options) + names.each { |name| column(name, :real, **options) } end - def money(*args, **options) - args.each { |name| column(name, :money, options) } + def money(*names, **options) + names.each { |name| column(name, :money, **options) } end - def smalldatetime(*args, **options) - args.each { |name| column(name, :smalldatetime, options) } + def smalldatetime(*names, **options) + names.each { |name| column(name, :smalldatetime, **options) } end - def datetime(*args, **options) - args.each do |name| + def datetime(*names, **options) + names.each do |name| if options[:precision] - datetime2(name, options) + datetime2(name, **options) else - column(name, :datetime, options) + column(name, :datetime, **options) end end end - def datetime2(*args, **options) - args.each { |name| column(name, :datetime2, options) } + def datetime2(*names, **options) + names.each { |name| column(name, :datetime2, **options) } end - def datetimeoffset(*args, **options) - args.each { |name| column(name, :datetimeoffset, options) } + def datetimeoffset(*names, **options) + names.each { |name| column(name, :datetimeoffset, **options) } end - def smallmoney(*args, **options) - args.each { |name| column(name, :smallmoney, options) } + def smallmoney(*names, **options) + names.each { |name| column(name, :smallmoney, **options) } end - def char(*args, **options) - args.each { |name| column(name, :char, options) } + def char(*names, **options) + names.each { |name| column(name, :char, **options) } end - def varchar(*args, **options) - args.each { |name| column(name, :varchar, options) } + def varchar(*names, **options) + names.each { |name| column(name, :varchar, **options) } end - def varchar_max(*args, **options) - args.each { |name| column(name, :varchar_max, options) } + def varchar_max(*names, **options) + names.each { |name| column(name, :varchar_max, **options) } end - def text_basic(*args, **options) - args.each { |name| column(name, :text_basic, options) } + def text_basic(*names, **options) + names.each { |name| column(name, :text_basic, **options) } end - def nchar(*args, **options) - args.each { |name| column(name, :nchar, options) } + def nchar(*names, **options) + names.each { |name| column(name, :nchar, **options) } end - def ntext(*args, **options) - args.each { |name| column(name, :ntext, options) } + def ntext(*names, **options) + names.each { |name| column(name, :ntext, **options) } end - def binary_basic(*args, **options) - args.each { |name| column(name, :binary_basic, options) } + def binary_basic(*names, **options) + names.each { |name| column(name, :binary_basic, **options) } end - def varbinary(*args, **options) - args.each { |name| column(name, :varbinary, options) } + def varbinary(*names, **options) + names.each { |name| column(name, :varbinary, **options) } end - def uuid(*args, **options) - args.each { |name| column(name, :uniqueidentifier, options) } + def uuid(*names, **options) + names.each { |name| column(name, :uniqueidentifier, **options) } end - def ss_timestamp(*args, **options) - args.each { |name| column(name, :ss_timestamp, options) } + def ss_timestamp(*names, **options) + names.each { |name| column(name, :ss_timestamp, **options) } end - def json(*args, **options) - args.each { |name| column(name, :text, options) } + def json(*names, **options) + names.each { |name| column(name, :text, **options) } end end diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 0e9f759f6..251193337 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -32,7 +32,7 @@ module SQLServerRealTransaction attr_reader :starting_isolation_level - def initialize(connection, options, *args) + def initialize(connection, options, **args) @connection = connection @starting_isolation_level = current_isolation_level if options[:isolation] super diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index 8d6eed882..516971f41 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Money < Decimal - def initialize(*args) + def initialize(**args) super @precision = 19 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 5c4dfd49d..d4a2e2375 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -4,7 +4,7 @@ module SQLServer module Type class SmallMoney < Money - def initialize(*args) + def initialize(**args) super @precision = 10 @scale = 4 diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 8bcaab4b5..4378e61ea 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarchar < UnicodeChar - def initialize(*args) + def initialize(**args) super @limit = 4000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index df6b455a8..f9cedeac3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class UnicodeVarcharMax < UnicodeVarchar - def initialize(*args) + def initialize(**args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index dffe7ee61..8e4e5571a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varbinary < Binary - def initialize(*args) + def initialize(**args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index 01087c616..cb1832210 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarbinaryMax < Varbinary - def initialize(*args) + def initialize(**args) super @limit = 2_147_483_647 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index 0e50a17e1..a386ec49c 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -4,7 +4,7 @@ module SQLServer module Type class Varchar < Char - def initialize(*args) + def initialize(**args) super @limit = 8000 if @limit.to_i == 0 end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 6d063b5f8..ecbb77a70 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -4,7 +4,7 @@ module SQLServer module Type class VarcharMax < Varchar - def initialize(*args) + def initialize(**args) super @limit = 2_147_483_647 end From 508334d4c2c2bed25f6b8b0118520a82224107bd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 21:20:13 +0100 Subject: [PATCH 0869/1412] Tidy up README --- README.md | 35 +++++++---------------------------- 1 file changed, 7 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index dee97c490..901381388 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ Interested in older versions? We follow a rational versioning policy that tracks We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. -The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version 7.3 with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to "7.3". The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. +The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version `7.3` with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to `7.3`. The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. -The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a pecision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. +The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a precision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. #### Identity Inserts with Triggers @@ -80,9 +80,9 @@ end Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: ```ruby -Account.execute_procedure :update_totals, 'admin', nil, true +Account.execute_procedure(:update_totals, 'admin', nil, true # Or with named parameters. -Account.execute_procedure :update_totals, named: 'params' +Account.execute_procedure(:update_totals, named: 'params') ``` #### Explain Support (SHOWPLAN) @@ -119,7 +119,7 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X ## Installation -The adapter has no strict gem dependencies outside of ActiveRecord. You will have to pick a connection mode, the default is dblib which uses the TinyTDS gem. Just bundle the gem and the adapter will use it. +The adapter has no strict gem dependencies outside of `ActiveRecord`. You will have to pick a connection mode, the default is dblib which uses the `TinyTDS` gem. Just bundle the gem and the adapter will use it. ```ruby gem 'tiny_tds' @@ -139,30 +139,9 @@ If you would like to contribute a feature or bugfix, thanks! To make sure your f Many many people have contributed. If you do not see your name here and it should be let us know. Also, many thanks go out to those that have pledged financial contributions. - -## Contributors - -Up-to-date list of contributors: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors - -* metaskills (Ken Collins) -* Annaswims (Annaswims) -* wbond (Will Bond) -* Thirdshift (Garrett Hart) -* h-lame (Murray Steele) -* vegantech -* cjheath (Clifford Heath) -* fryguy (Jason Frey) -* jrafanie (Joe Rafaniello) -* nerdrew (Andrew Ryan) -* snowblink (Jonathan Lim) -* koppen (Jakob Skjerning) -* ebryn (Erik Bryn) -* adzap (Adam Meehan) -* neomindryan (Ryan Findley) -* jeremydurham (Jeremy Durham) - +You can see an up-to-date list of contributors here: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors ## License -Copyright © 2008-2017. It is free software, and may be redistributed under the terms specified in the MIT-LICENSE file. +Copyright © 2008-2020. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file. From cd236680f6eb8b6aba208881952e1eb17268bfdd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 21:20:41 +0100 Subject: [PATCH 0870/1412] Prepare CHANGELOG for v6.0.0.rc1 --- CHANGELOG.md | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 873505b5c..de3d6b673 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,25 +1,22 @@ -## v5.2.0 - -- #686 sql_for_insert set table name in case when pk is not nil - -## v5.2.0.rc2 +## v6.0.0.rc1 (unreleased) #### Fixed -- #681 change_column_null should not clear other column attributes. Fixes #582. -- #684 Fix explain with array conditions. Fixes #673. - -## v5.2.0.rc1 - -#### Fixed - -- #638 Don't disable referential integrity for the same table twice. -- #646 Make String equality check work for Type::Data values. Fixes #645. -- #671 Fix tinyint columns schema migration. Fixes #670. +- [#690](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/690) Rails 6 support +- [#805](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/805) Rails 6: Fix database tasks tests for SQL Server +- [#807](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/807) Rails 6: Skip binary fixtures test on Windows +- [#809](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/809) Rails 6: Coerce reaper test using fork +- [#810](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/810) Rails 6: Fix randomly failing tests due to schema load +- [#812](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/812) Rails 6: Coerce ReloadModelsTest test on Windows +- [#819](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/819) Fix Ruby 2.7 kwargs warnings #### Changed -- #642 Added with (nolock) hint to information_schema.views. +- [#763](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/763) Refactor columns introspection query to make it faster +- [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix + +#### Added +- [#726](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/726) How to Develop ActiveRecord SQL Server Adapter with Pre-Installed MS SQL -Please check [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/5-1-stable/CHANGELOG.md) for previous changes. +Please check [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/5-2-stable/CHANGELOG.md) for previous changes. From 1c99b72ecf78a1f8b2e6ccef162f69c466eabd10 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:05:49 +0100 Subject: [PATCH 0871/1412] Enable frozen strings for tests --- CHANGELOG.md | 1 + test/cases/adapter_test_sqlserver.rb | 2 ++ test/cases/change_column_null_test_sqlserver.rb | 2 ++ test/cases/coerced_tests.rb | 2 ++ test/cases/column_test_sqlserver.rb | 2 ++ test/cases/connection_test_sqlserver.rb | 2 ++ test/cases/execute_procedure_test_sqlserver.rb | 2 ++ test/cases/fetch_test_sqlserver.rb | 2 ++ test/cases/fully_qualified_identifier_test_sqlserver.rb | 2 ++ test/cases/helper_sqlserver.rb | 2 ++ test/cases/in_clause_test_sqlserver.rb | 2 ++ test/cases/index_test_sqlserver.rb | 2 ++ test/cases/json_test_sqlserver.rb | 2 ++ test/cases/migration_test_sqlserver.rb | 2 ++ test/cases/order_test_sqlserver.rb | 2 ++ test/cases/pessimistic_locking_test_sqlserver.rb | 2 ++ test/cases/rake_test_sqlserver.rb | 2 ++ test/cases/schema_dumper_test_sqlserver.rb | 2 ++ test/cases/schema_test_sqlserver.rb | 2 ++ test/cases/scratchpad_test_sqlserver.rb | 2 ++ test/cases/showplan_test_sqlserver.rb | 2 ++ test/cases/specific_schema_test_sqlserver.rb | 2 ++ test/cases/transaction_test_sqlserver.rb | 2 ++ test/cases/trigger_test_sqlserver.rb | 2 ++ test/cases/utils_test_sqlserver.rb | 2 ++ test/cases/uuid_test_sqlserver.rb | 2 ++ test/debug.rb | 2 ++ test/migrations/create_clients_and_change_column_null.rb | 2 ++ .../transaction_table/1_table_will_never_be_created.rb | 2 ++ test/models/sqlserver/booking.rb | 2 ++ test/models/sqlserver/customers_view.rb | 2 ++ test/models/sqlserver/datatype.rb | 2 ++ test/models/sqlserver/datatype_migration.rb | 2 ++ test/models/sqlserver/dollar_table_name.rb | 2 ++ test/models/sqlserver/edge_schema.rb | 2 ++ test/models/sqlserver/fk_has_fk.rb | 2 ++ test/models/sqlserver/fk_has_pk.rb | 2 ++ test/models/sqlserver/natural_pk_data.rb | 2 ++ test/models/sqlserver/natural_pk_int_data.rb | 2 ++ test/models/sqlserver/no_pk_data.rb | 2 ++ test/models/sqlserver/object_default.rb | 2 ++ test/models/sqlserver/quoted_table.rb | 2 ++ test/models/sqlserver/quoted_view_1.rb | 2 ++ test/models/sqlserver/quoted_view_2.rb | 2 ++ test/models/sqlserver/sst_memory.rb | 2 ++ test/models/sqlserver/string_default.rb | 2 ++ test/models/sqlserver/string_defaults_big_view.rb | 2 ++ test/models/sqlserver/string_defaults_view.rb | 2 ++ test/models/sqlserver/tinyint_pk.rb | 2 ++ test/models/sqlserver/trigger.rb | 2 ++ test/models/sqlserver/trigger_history.rb | 2 ++ test/models/sqlserver/upper.rb | 2 ++ test/models/sqlserver/uppered.rb | 2 ++ test/models/sqlserver/uuid.rb | 2 ++ test/schema/sqlserver_specific_schema.rb | 2 ++ test/support/coerceable_test_sqlserver.rb | 2 ++ test/support/connection_reflection.rb | 2 ++ test/support/core_ext/query_cache.rb | 2 ++ test/support/load_schema_sqlserver.rb | 2 ++ test/support/minitest_sqlserver.rb | 2 ++ test/support/paths_sqlserver.rb | 2 ++ test/support/rake_helpers.rb | 1 + test/support/sql_counter_sqlserver.rb | 2 ++ test/support/test_in_memory_oltp.rb | 2 ++ 64 files changed, 126 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de3d6b673..423cb971a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [#763](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/763) Refactor columns introspection query to make it faster - [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix +- [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests #### Added diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index e9f889ac9..366c42302 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/topic' require 'models/task' diff --git a/test/cases/change_column_null_test_sqlserver.rb b/test/cases/change_column_null_test_sqlserver.rb index 32ef027f9..a4267f263 100644 --- a/test/cases/change_column_null_test_sqlserver.rb +++ b/test/cases/change_column_null_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'migrations/create_clients_and_change_column_null' diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5b3129f81..0e38cd068 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 9e41e81e3..c5b6ef0a1 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # encoding: UTF-8 require 'cases/helper_sqlserver' diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index e4c1ed55b..56e691ff4 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/reply' require 'models/topic' diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index d8731cd6b..7cdfa984f 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 48e7ba2e5..5650a5f55 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/book' diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index 18b028b2d..f3ecc2786 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 028648c37..41da2e52f 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'support/paths_sqlserver' require 'bundler/setup' Bundler.require :default, :development diff --git a/test/cases/in_clause_test_sqlserver.rb b/test/cases/in_clause_test_sqlserver.rb index 6ec295130..b32639bde 100644 --- a/test/cases/in_clause_test_sqlserver.rb +++ b/test/cases/in_clause_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/post' require 'models/author' diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index 3a073e572..a1cebaa9e 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class IndexTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index 664c8327c..c47b55794 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' if ActiveRecord::Base.connection.supports_json? diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 7348bc15b..3aa02406e 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/person' diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index d0707eede..f48a1fad1 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/post' diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 0e3e44fd4..45160f156 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/person' require 'models/reader' diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 366ece3e4..431497e00 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class SQLServerRakeTest < ActiveRecord::TestCase diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index a72438e2a..a5a5b6677 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class SchemaDumperTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 5986b5b06..a913923bc 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class SchemaTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb index 0d77122c0..f553170ca 100644 --- a/test/cases/scratchpad_test_sqlserver.rb +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class ScratchpadTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index f54a37a88..c43122610 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' require 'models/car' diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 9e0659227..21c75a1be 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class SpecificSchemaTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index bca3c071e..edac70ca3 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # encoding: UTF-8 require 'cases/helper_sqlserver' require 'models/ship' diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index c762c709e..1e9e0e979 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # encoding: UTF-8 require 'cases/helper_sqlserver' diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index c64939384..8274a93af 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'cases/helper_sqlserver' class UtilsTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index c97319230..3c4947f18 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # encoding: UTF-8 require 'cases/helper_sqlserver' diff --git a/test/debug.rb b/test/debug.rb index 99ef6950a..1b1f8faad 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # require 'rails/all' require 'tiny_tds' diff --git a/test/migrations/create_clients_and_change_column_null.rb b/test/migrations/create_clients_and_change_column_null.rb index 96e459455..18f74cdcf 100644 --- a/test/migrations/create_clients_and_change_column_null.rb +++ b/test/migrations/create_clients_and_change_column_null.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CreateClientsAndChangeColumnNull < ActiveRecord::Migration[5.2] def up create_table :clients do |t| diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index ffc29d634..dfcec8368 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TableWillNeverBeCreated < ActiveRecord::Migration def self.up diff --git a/test/models/sqlserver/booking.rb b/test/models/sqlserver/booking.rb index 8b38e4e52..4b70013d1 100644 --- a/test/models/sqlserver/booking.rb +++ b/test/models/sqlserver/booking.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestBooking < ActiveRecord::Base self.table_name = 'sst_bookings' end diff --git a/test/models/sqlserver/customers_view.rb b/test/models/sqlserver/customers_view.rb index 5c5a994e2..ef3ad2160 100644 --- a/test/models/sqlserver/customers_view.rb +++ b/test/models/sqlserver/customers_view.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestCustomersView < ActiveRecord::Base self.table_name = 'sst_customers_view' end diff --git a/test/models/sqlserver/datatype.rb b/test/models/sqlserver/datatype.rb index d9b24d830..3e3d61ed5 100644 --- a/test/models/sqlserver/datatype.rb +++ b/test/models/sqlserver/datatype.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestDatatype < ActiveRecord::Base self.table_name = :sst_datatypes end diff --git a/test/models/sqlserver/datatype_migration.rb b/test/models/sqlserver/datatype_migration.rb index 79ee8492d..2a77cce60 100644 --- a/test/models/sqlserver/datatype_migration.rb +++ b/test/models/sqlserver/datatype_migration.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestDatatypeMigration < ActiveRecord::Base self.table_name = :sst_datatypes_migration end diff --git a/test/models/sqlserver/dollar_table_name.rb b/test/models/sqlserver/dollar_table_name.rb index b43e89600..8b20e5275 100644 --- a/test/models/sqlserver/dollar_table_name.rb +++ b/test/models/sqlserver/dollar_table_name.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestDollarTableName < ActiveRecord::Base self.table_name = 'sst_my$strange_table' end diff --git a/test/models/sqlserver/edge_schema.rb b/test/models/sqlserver/edge_schema.rb index 4d14bf81d..30c79ad54 100644 --- a/test/models/sqlserver/edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestEdgeSchema < ActiveRecord::Base self.table_name = 'sst_edge_schemas' diff --git a/test/models/sqlserver/fk_has_fk.rb b/test/models/sqlserver/fk_has_fk.rb index 883c9dcee..95486c232 100644 --- a/test/models/sqlserver/fk_has_fk.rb +++ b/test/models/sqlserver/fk_has_fk.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestHasFk < ActiveRecord::Base self.table_name = 'sst_has_fks' end diff --git a/test/models/sqlserver/fk_has_pk.rb b/test/models/sqlserver/fk_has_pk.rb index bb3889bdd..1bd2506e8 100644 --- a/test/models/sqlserver/fk_has_pk.rb +++ b/test/models/sqlserver/fk_has_pk.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestHasPk < ActiveRecord::Base self.table_name = 'sst_has_pks' end diff --git a/test/models/sqlserver/natural_pk_data.rb b/test/models/sqlserver/natural_pk_data.rb index 0dbf67552..01cfdca06 100644 --- a/test/models/sqlserver/natural_pk_data.rb +++ b/test/models/sqlserver/natural_pk_data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestNaturalPkData < ActiveRecord::Base self.table_name = 'sst_natural_pk_data' self.primary_key = 'legacy_id' diff --git a/test/models/sqlserver/natural_pk_int_data.rb b/test/models/sqlserver/natural_pk_int_data.rb index 2a624f774..4eb770809 100644 --- a/test/models/sqlserver/natural_pk_int_data.rb +++ b/test/models/sqlserver/natural_pk_int_data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestNaturalPkIntData < ActiveRecord::Base self.table_name = 'sst_natural_pk_int_data' end diff --git a/test/models/sqlserver/no_pk_data.rb b/test/models/sqlserver/no_pk_data.rb index 65c7c1ab2..b9ef19ba3 100644 --- a/test/models/sqlserver/no_pk_data.rb +++ b/test/models/sqlserver/no_pk_data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestNoPkData < ActiveRecord::Base self.table_name = 'sst_no_pk_data' end diff --git a/test/models/sqlserver/object_default.rb b/test/models/sqlserver/object_default.rb index b3950166b..5ba7d4912 100644 --- a/test/models/sqlserver/object_default.rb +++ b/test/models/sqlserver/object_default.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestObjectDefault < ActiveRecord::Base self.table_name = 'sst_defaultobjects' end diff --git a/test/models/sqlserver/quoted_table.rb b/test/models/sqlserver/quoted_table.rb index ce9d283a8..bee44dd9a 100644 --- a/test/models/sqlserver/quoted_table.rb +++ b/test/models/sqlserver/quoted_table.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestQuotedTable < ActiveRecord::Base self.table_name = '[sst_quoted-table]' end diff --git a/test/models/sqlserver/quoted_view_1.rb b/test/models/sqlserver/quoted_view_1.rb index ff5d3495a..587f28691 100644 --- a/test/models/sqlserver/quoted_view_1.rb +++ b/test/models/sqlserver/quoted_view_1.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestQuotedView1 < ActiveRecord::Base self.table_name = 'sst_quoted-view1' end diff --git a/test/models/sqlserver/quoted_view_2.rb b/test/models/sqlserver/quoted_view_2.rb index bd03a9acd..d993c274c 100644 --- a/test/models/sqlserver/quoted_view_2.rb +++ b/test/models/sqlserver/quoted_view_2.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestQuotedView2 < ActiveRecord::Base self.table_name = 'sst_quoted-view2' end diff --git a/test/models/sqlserver/sst_memory.rb b/test/models/sqlserver/sst_memory.rb index c1767c5f8..5abbfd2e6 100644 --- a/test/models/sqlserver/sst_memory.rb +++ b/test/models/sqlserver/sst_memory.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTMemory < ActiveRecord::Base self.table_name = 'sst_memory' end diff --git a/test/models/sqlserver/string_default.rb b/test/models/sqlserver/string_default.rb index b8938e2e8..d797a401e 100644 --- a/test/models/sqlserver/string_default.rb +++ b/test/models/sqlserver/string_default.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestStringDefault < ActiveRecord::Base self.table_name = 'sst_string_defaults' end diff --git a/test/models/sqlserver/string_defaults_big_view.rb b/test/models/sqlserver/string_defaults_big_view.rb index a0626abc4..50bfcbb43 100644 --- a/test/models/sqlserver/string_defaults_big_view.rb +++ b/test/models/sqlserver/string_defaults_big_view.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestStringDefaultsBigView < ActiveRecord::Base self.table_name = 'sst_string_defaults_big_view' end diff --git a/test/models/sqlserver/string_defaults_view.rb b/test/models/sqlserver/string_defaults_view.rb index 01a83942f..6bf3c8b6b 100644 --- a/test/models/sqlserver/string_defaults_view.rb +++ b/test/models/sqlserver/string_defaults_view.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestStringDefaultsView < ActiveRecord::Base self.table_name = 'sst_string_defaults_view' end diff --git a/test/models/sqlserver/tinyint_pk.rb b/test/models/sqlserver/tinyint_pk.rb index 121a321c3..ca52f8de5 100644 --- a/test/models/sqlserver/tinyint_pk.rb +++ b/test/models/sqlserver/tinyint_pk.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestTinyintPk < ActiveRecord::Base self.table_name = 'sst_tinyint_pk' end diff --git a/test/models/sqlserver/trigger.rb b/test/models/sqlserver/trigger.rb index cf1c45f7c..50e4942be 100644 --- a/test/models/sqlserver/trigger.rb +++ b/test/models/sqlserver/trigger.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestTrigger < ActiveRecord::Base self.table_name = 'sst_table_with_trigger' end diff --git a/test/models/sqlserver/trigger_history.rb b/test/models/sqlserver/trigger_history.rb index 35fd98016..c358d935d 100644 --- a/test/models/sqlserver/trigger_history.rb +++ b/test/models/sqlserver/trigger_history.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestTriggerHistory < ActiveRecord::Base self.table_name = 'sst_table_with_trigger_history' end diff --git a/test/models/sqlserver/upper.rb b/test/models/sqlserver/upper.rb index 623f67b0f..05432b476 100644 --- a/test/models/sqlserver/upper.rb +++ b/test/models/sqlserver/upper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestUpper < ActiveRecord::Base self.table_name = 'sst_upper_tests' end diff --git a/test/models/sqlserver/uppered.rb b/test/models/sqlserver/uppered.rb index cb3989dc5..bb9dce777 100644 --- a/test/models/sqlserver/uppered.rb +++ b/test/models/sqlserver/uppered.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestUppered < ActiveRecord::Base self.table_name = 'SST_UPPER_TESTS' end diff --git a/test/models/sqlserver/uuid.rb b/test/models/sqlserver/uuid.rb index f03a7c373..e6eea62e3 100644 --- a/test/models/sqlserver/uuid.rb +++ b/test/models/sqlserver/uuid.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SSTestUuid < ActiveRecord::Base self.table_name = 'sst_uuids' end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 00fef941e..eefcb363d 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ActiveRecord::Schema.define do # Exhaustive Data Types diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 3a7d38387..6ecbe7953 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ARTest module SQLServer module CoerceableTest diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index 20ef1fd8a..777fae534 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ARTest module SQLServer module ConnectionReflection diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb index 01601f695..1debd57db 100644 --- a/test/support/core_ext/query_cache.rb +++ b/test/support/core_ext/query_cache.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/connection_adapters/sqlserver_adapter' module SqlIgnoredCache diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index 5e4eb8e1c..4b8a3c08b 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ARTest module SQLServer diff --git a/test/support/minitest_sqlserver.rb b/test/support/minitest_sqlserver.rb index c12a467ce..4e5574ef0 100644 --- a/test/support/minitest_sqlserver.rb +++ b/test/support/minitest_sqlserver.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require 'minitest-spec-rails/init/active_support' diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index c423d91a5..a495474f2 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ARTest module SQLServer diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index 58ff2303c..db04b5bba 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -1,3 +1,4 @@ +# frozen_string_literal: true SQLSERVER_HELPER = 'test/cases/helper_sqlserver.rb' SQLSERVER_COERCED = 'test/cases/coerced_tests.rb' diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 04a814dc6..6977c17d6 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ARTest module SQLServer diff --git a/test/support/test_in_memory_oltp.rb b/test/support/test_in_memory_oltp.rb index 5db693f0f..ba169951c 100644 --- a/test/support/test_in_memory_oltp.rb +++ b/test/support/test_in_memory_oltp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + if ENV['IN_MEMORY_OLTP'] require 'config' require 'active_record' From 74a532e1ffb039a4c234b9262ad255a0c7f906fb Mon Sep 17 00:00:00 2001 From: Sarun Rattanasiri Date: Sat, 14 Dec 2019 22:11:55 +0700 Subject: [PATCH 0872/1412] Translate the connection timed out error --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 86fc2c19a..c6fa1cfa9 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -154,7 +154,7 @@ def supports_savepoints? def supports_lazy_transactions? true end - + def supports_in_memory_oltp? @version_year >= 2014 end @@ -367,6 +367,8 @@ def translate_exception(e, message:, sql:, binds:) NoDatabaseError.new(message, sql: sql, binds: binds) when /data would be truncated/ ValueTooLong.new(message, sql: sql, binds: binds) + when /connection timed out/ + StatementTimeout.new(message, sql: sql, binds: binds) when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/ pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2) MismatchedForeignKey.new( From e16a0f990a300c8c0c8ad2bb49162c5c3ef58bbc Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:23:14 +0100 Subject: [PATCH 0873/1412] Remove encoding magic comment --- test/cases/column_test_sqlserver.rb | 1 - test/cases/transaction_test_sqlserver.rb | 1 - test/cases/trigger_test_sqlserver.rb | 1 - test/cases/uuid_test_sqlserver.rb | 1 - 4 files changed, 4 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index c5b6ef0a1..8d74c2908 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# encoding: UTF-8 require 'cases/helper_sqlserver' class ColumnTestSQLServer < ActiveRecord::TestCase diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index edac70ca3..f94694e36 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# encoding: UTF-8 require 'cases/helper_sqlserver' require 'models/ship' require 'models/developer' diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index 1e9e0e979..88ccf157e 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# encoding: UTF-8 require 'cases/helper_sqlserver' class SQLServerTriggerTest < ActiveRecord::TestCase diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index 3c4947f18..4a96c29ce 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,6 +1,5 @@ # frozen_string_literal: true -# encoding: UTF-8 require 'cases/helper_sqlserver' class SQLServerUuidTest < ActiveRecord::TestCase From cd648713fd45a39666867c8f962ebeb83f997742 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:26:01 +0100 Subject: [PATCH 0874/1412] Enable frozen strings in Rakefile --- Rakefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Rakefile b/Rakefile index 79a4a5885..1477ac6f5 100644 --- a/Rakefile +++ b/Rakefile @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'bundler/gem_tasks' require 'rake/testtask' require_relative 'test/support/paths_sqlserver' From cb2b972f37286942d5dcf2b4b4c993e25c038600 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:26:59 +0100 Subject: [PATCH 0875/1412] Enable frozen strings in core_ext --- .../connection_adapters/sqlserver/core_ext/active_record.rb | 2 ++ .../sqlserver/core_ext/attribute_methods.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/calculations.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/explain.rb | 4 +++- .../sqlserver/core_ext/explain_subscriber.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/finder_methods.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/preloader.rb | 2 ++ .../connection_adapters/sqlserver/core_ext/query_methods.rb | 2 ++ 8 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index ae8359686..03849a5cb 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer 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 6687ac36a..815da2914 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 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/attribute_methods' module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index d2b7096c7..c17d77876 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/relation' require 'active_record/version' 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 baa8d736c..76e1eb7a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -1,10 +1,12 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer module CoreExt module Explain - SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '.freeze + SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 45c56cd78..712465543 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + ActiveSupport.on_load(:active_record) do silence_warnings do # Already defined in Rails 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 519628a22..7db8aaecc 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 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/relation' require 'active_record/version' diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index 5fca92532..0171f4c65 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require "active_record/associations/preloader" module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb index 91d17721f..31ed4fa79 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/relation' require 'active_record/version' From dfed45aec7834b2ee73765057b54fbdfe91b5c03 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:41:14 +0100 Subject: [PATCH 0876/1412] Enable frozen strings in showplan --- .../connection_adapters/sqlserver/showplan/printer_table.rb | 2 ++ .../connection_adapters/sqlserver/showplan/printer_xml.rb | 2 ++ 2 files changed, 4 insertions(+) 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 70de897db..4885d74cc 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb index f620a6453..00c268143 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer From dc9a267938a84c4e8958b2518823ea6c481540b7 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 14 May 2020 10:52:57 +0100 Subject: [PATCH 0877/1412] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 423cb971a..cda67f6b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - [#763](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/763) Refactor columns introspection query to make it faster - [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix - [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests +- [#821](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/821) Enable frozen strings - part 1 #### Added From 1b01040ecb0b95bc21f9436dc04d0507dc44b251 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 13 May 2020 23:56:52 +0100 Subject: [PATCH 0878/1412] Enable frozen strings in type --- CHANGELOG.md | 1 + .../sqlserver/type/big_integer.rb | 4 +++- .../connection_adapters/sqlserver/type/binary.rb | 7 +++++-- .../connection_adapters/sqlserver/type/boolean.rb | 4 +++- .../connection_adapters/sqlserver/type/char.rb | 7 +++++-- .../connection_adapters/sqlserver/type/data.rb | 2 ++ .../connection_adapters/sqlserver/type/date.rb | 4 +++- .../connection_adapters/sqlserver/type/datetime.rb | 13 +++++++------ .../connection_adapters/sqlserver/type/datetime2.rb | 2 ++ .../sqlserver/type/datetimeoffset.rb | 2 ++ .../connection_adapters/sqlserver/type/decimal.rb | 7 +++++-- .../connection_adapters/sqlserver/type/float.rb | 4 +++- .../connection_adapters/sqlserver/type/integer.rb | 4 +++- .../connection_adapters/sqlserver/type/json.rb | 2 ++ .../connection_adapters/sqlserver/type/money.rb | 4 +++- .../connection_adapters/sqlserver/type/real.rb | 4 +++- .../sqlserver/type/small_integer.rb | 4 +++- .../sqlserver/type/small_money.rb | 4 +++- .../sqlserver/type/smalldatetime.rb | 4 +++- .../connection_adapters/sqlserver/type/string.rb | 2 ++ .../connection_adapters/sqlserver/type/text.rb | 4 +++- .../connection_adapters/sqlserver/type/time.rb | 9 +++++---- .../sqlserver/type/time_value_fractional.rb | 2 ++ .../connection_adapters/sqlserver/type/timestamp.rb | 4 +++- .../sqlserver/type/tiny_integer.rb | 4 +++- .../sqlserver/type/unicode_char.rb | 7 +++++-- .../sqlserver/type/unicode_string.rb | 2 ++ .../sqlserver/type/unicode_text.rb | 4 +++- .../sqlserver/type/unicode_varchar.rb | 7 +++++-- .../sqlserver/type/unicode_varchar_max.rb | 4 +++- .../connection_adapters/sqlserver/type/uuid.rb | 4 +++- .../connection_adapters/sqlserver/type/varbinary.rb | 7 +++++-- .../sqlserver/type/varbinary_max.rb | 4 +++- .../connection_adapters/sqlserver/type/varchar.rb | 7 +++++-- .../sqlserver/type/varchar_max.rb | 4 +++- 35 files changed, 116 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cda67f6b5..ef6c03e98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix - [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests - [#821](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/821) Enable frozen strings - part 1 +- [#822](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/822) Enable frozen strings - part 2 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb index 18f1207af..083b736be 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class BigInteger < Integer def sqlserver_type - 'bigint'.freeze + "bigint" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/binary.rb b/lib/active_record/connection_adapters/sqlserver/type/binary.rb index 06cb42b37..a700846a4 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/binary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/binary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,8 +11,9 @@ def type end def sqlserver_type - 'binary'.tap do |type| - type << "(#{limit})" if limit + "binary".yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb index e2b48e701..1b56ebc16 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class Boolean < ActiveRecord::Type::Boolean def sqlserver_type - 'bit'.freeze + "bit" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 47fb3e0d6..96832ad52 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -15,8 +17,9 @@ def serialize(value) end def sqlserver_type - 'char'.tap do |type| - type << "(#{limit})" if limit + "char".yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 683e0f7c8..599b49ee7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 87fa8333a..845f2b6db 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class Date < ActiveRecord::Type::Date def sqlserver_type - 'date'.freeze + 'date' end def serialize(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 700c99e82..59429cdc0 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -7,16 +9,15 @@ class DateTime < ActiveRecord::Type::DateTime include TimeValueFractional def sqlserver_type - 'datetime'.freeze + "datetime" end def serialize(value) value = super return value unless value.acts_like?(:time) - datetime = value.to_s(:_sqlserver_datetime).tap do |v| - fraction = quote_fractional(value) - v << ".#{fraction}" - end + + datetime = "#{value.to_s(:_sqlserver_datetime)}.#{quote_fractional(value)}" + Data.new datetime, self end @@ -43,7 +44,7 @@ def fast_string_to_time(string) end def fast_string_to_time_format - "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N".freeze + "#{::Time::DATE_FORMATS[:_sqlserver_datetime]}.%N" end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb index 510e831c3..77ab42f22 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 9564dadda..71f3d3529 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb index 1383de37e..7bf421f71 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,8 +7,9 @@ module Type class Decimal < ActiveRecord::Type::Decimal def sqlserver_type - 'decimal'.tap do |type| - type << "(#{precision.to_i},#{scale.to_i})" if precision || scale + "decimal".yield_self do |type| + type += "(#{precision.to_i},#{scale.to_i})" if precision || scale + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index d26433dd3..aecb7438d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'float'.freeze + "float" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/integer.rb b/lib/active_record/connection_adapters/sqlserver/type/integer.rb index 72f93e275..9b4d63725 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/integer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class Integer < ActiveRecord::Type::Integer def sqlserver_type - 'int'.freeze + "int" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/json.rb b/lib/active_record/connection_adapters/sqlserver/type/json.rb index 403261b5b..d942cd3a1 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/json.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/json.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index 516971f41..2906859e3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -15,7 +17,7 @@ def type end def sqlserver_type - 'money'.freeze + "money" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/real.rb b/lib/active_record/connection_adapters/sqlserver/type/real.rb index c0c143c52..7a5a36922 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/real.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/real.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'real'.freeze + "real" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb index ab2659455..1dff23244 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class SmallInteger < Integer def sqlserver_type - 'smallint'.freeze + "smallint" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index d4a2e2375..0ecfa00c8 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -15,7 +17,7 @@ def type end def sqlserver_type - 'smallmoney'.freeze + "smallmoney" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 427809fcf..99dc70a8a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'smalldatetime'.freeze + "smalldatetime" end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/string.rb b/lib/active_record/connection_adapters/sqlserver/type/string.rb index a1439f45b..110bcebde 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/string.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/string.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/text.rb b/lib/active_record/connection_adapters/sqlserver/type/text.rb index 8d49ff731..087b8fb85 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/text.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'text'.freeze + "text" 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 f8bc0dec8..38f2354c9 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,10 +11,9 @@ class Time < ActiveRecord::Type::Time def serialize(value) value = super return value unless value.acts_like?(:time) - time = value.to_s(:_sqlserver_time).tap do |v| - fraction = quote_fractional(value) - v << ".#{fraction}" - end + + time = "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}" + Data.new time, self 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 412958795..aa0b0ed73 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 @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb index b0e38a827..15125e849 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'timestamp'.freeze + "timestamp" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb index 739b51347..4370bc574 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -5,7 +7,7 @@ module Type class TinyInteger < Integer def sqlserver_type - 'tinyint'.freeze + "tinyint" end private diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb index 6934073e1..26ad415aa 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,8 +11,9 @@ def type end def sqlserver_type - 'nchar'.tap do |type| - type << "(#{limit})" if limit + 'nchar'.yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb index 351de8ec2..c2e376c89 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb index c39e18a11..38139f2b7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -9,7 +11,7 @@ def type end def sqlserver_type - 'ntext'.freeze + "ntext" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index 4378e61ea..a100b6baa 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,8 +16,9 @@ def type end def sqlserver_type - 'nvarchar'.tap do |type| - type << "(#{limit})" if limit + "nvarchar".yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index f9cedeac3..160a1f80a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,7 +16,7 @@ def type end def sqlserver_type - 'nvarchar(max)'.freeze + "nvarchar(max)" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index c5e479986..07b9682a7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -13,7 +15,7 @@ def type end def sqlserver_type - 'uniqueidentifier'.freeze + "uniqueidentifier" end def serialize(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index 8e4e5571a..6ae0af9cd 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,8 +16,9 @@ def type end def sqlserver_type - 'varbinary'.tap do |type| - type << "(#{limit})" if limit + 'varbinary'.yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index cb1832210..21476e544 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,7 +16,7 @@ def type end def sqlserver_type - 'varbinary(max)'.freeze + "varbinary(max)" end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index a386ec49c..a19216696 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,8 +16,9 @@ def type end def sqlserver_type - 'varchar'.tap do |type| - type << "(#{limit})" if limit + 'varchar'.yield_self do |type| + type += "(#{limit})" if limit + type end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index ecbb77a70..1582e500d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -14,7 +16,7 @@ def type end def sqlserver_type - 'varchar(max)'.freeze + "varchar(max)" end end From 7719b0d9cc93b115bd7cda28494f9b1fdc1b3522 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 13 May 2020 16:30:11 +0100 Subject: [PATCH 0879/1412] Updated Ruby AppVeyor matrix and handle TinyTDS false result --- appveyor.yml | 6 +++++- .../connection_adapters/sqlserver/database_statements.rb | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index bc3ffa6c9..7b4d943de 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -4,7 +4,11 @@ clone_depth: 5 build: off matrix: fast_finish: true - + allow_failures: + - ruby_version: "25" + - ruby_version: "26" + - ruby_version: "27" + - ruby_version: "27-x64" services: - mssql2014 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 68813c489..20e0df011 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -350,7 +350,12 @@ def sp_executesql_sql(sql, types, params, name) def raw_connection_do(sql) case @connection_options[:mode] when :dblib - @connection.execute(sql).do + result = @connection.execute(sql) + # If connection fails then TinyTDS returns false instead of an exception (see https://github.com/rails-sqlserver/tiny_tds/issues/464) + if result == false + raise TinyTds::Error, 'TinyTDS execute returned false instead of results' + end + result.do end ensure @update_sql = false From 3ca691490cb2247cabb2c4b8fe6e160d9d14fb3b Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 14 May 2020 16:41:49 +0100 Subject: [PATCH 0880/1412] Enable frozen string in all remaining files --- CHANGELOG.md | 1 + .../sqlserver/database_limits.rb | 2 ++ .../sqlserver/database_statements.rb | 4 +++- .../sqlserver/database_tasks.rb | 2 ++ .../connection_adapters/sqlserver/errors.rb | 2 ++ .../connection_adapters/sqlserver/quoting.rb | 2 ++ .../sqlserver/schema_creation.rb | 2 ++ .../sqlserver/schema_dumper.rb | 2 ++ .../sqlserver/schema_statements.rb | 21 +++++++++++-------- .../connection_adapters/sqlserver/showplan.rb | 2 ++ .../sqlserver/sql_type_metadata.rb | 2 ++ .../sqlserver/table_definition.rb | 2 ++ .../sqlserver/transaction.rb | 2 ++ .../connection_adapters/sqlserver/type.rb | 2 ++ .../connection_adapters/sqlserver/utils.rb | 2 ++ .../connection_adapters/sqlserver/version.rb | 2 ++ .../connection_adapters/sqlserver_adapter.rb | 2 ++ .../connection_adapters/sqlserver_column.rb | 2 ++ lib/active_record/sqlserver_base.rb | 2 ++ .../tasks/sqlserver_database_tasks.rb | 2 ++ lib/activerecord-sqlserver-adapter.rb | 2 ++ lib/arel/visitors/sqlserver.rb | 2 ++ lib/arel_sqlserver.rb | 2 ++ 23 files changed, 56 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef6c03e98..a51153243 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests - [#821](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/821) Enable frozen strings - part 1 - [#822](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/822) Enable frozen strings - part 2 +- [#823](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/823) Enable frozen strings - final #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index f52ffdf39..14ad94865 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 68813c489..0bcff16ed 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -342,7 +344,7 @@ def sp_executesql_sql(sql, types, params, name) types = quote(types.join(', ')) params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp. sql = "EXEC sp_executesql #{quote(sql)}" - sql << ", #{types}, #{params}" unless params.empty? + sql += ", #{types}, #{params}" unless params.empty? end sql end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index e1ab94871..687773806 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 70f64cefc..3ff5faec0 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord class DeadlockVictim < WrappedDatabaseException diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 0d95202c8..ad11c4900 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 670d77379..361e7bc53 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index d801d1d86..bd5d746c9 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 94f7dcfdc..cc0f87e1a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer @@ -152,8 +154,9 @@ def change_column(table_name, column_name, type, options = {}) remove_indexes(table_name, column_name) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? - sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" - sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false + alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" + alter_command += ' NOT NULL' if !options[:null].nil? && options[:null] == false + sql_commands << alter_command if without_constraints default = quote_default_expression(default, column_object || column_for(table_name, column_name)) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}" @@ -267,7 +270,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}" - sql << ' NOT NULL' if !allow_null.nil? && allow_null == false + sql += ' NOT NULL' if !allow_null.nil? && allow_null == false do_execute sql end @@ -281,12 +284,12 @@ def data_source_sql(name = nil, type: nil) scope = quoted_scope name, type: type table_name = lowercase_schema_reflection_sql 'TABLE_NAME' sql = "SELECT #{table_name}" - sql << ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)' - sql << ' WHERE TABLE_CATALOG = DB_NAME()' - sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}" - sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] - sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] - sql << " ORDER BY #{table_name}" + sql += ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)' + sql += ' WHERE TABLE_CATALOG = DB_NAME()' + sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" + sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] + sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] + sql += " ORDER BY #{table_name}" sql end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 33839ca1e..1891cb588 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/connection_adapters/sqlserver/showplan/printer_table' require 'active_record/connection_adapters/sqlserver/showplan/printer_xml' 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 f5640ca68..09b59ebd2 100644 --- a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 878f09e4f..244f33295 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 251193337..baba63859 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/connection_adapters/abstract/transaction' module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index bbb74266a..59121074a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/type' # Behaviors require 'active_record/connection_adapters/sqlserver/type/data' diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index e6eedc94e..bd2a6fdf9 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'strscan' module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 3692bdc20..4ab6f0bf4 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters module SQLServer diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 86fc2c19a..8c017ccc2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'base64' require 'active_record' require 'arel_sqlserver' diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 7d0547344..535a75967 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionAdapters class SQLServerColumn < Column diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 0b86358f4..7dcf2765d 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module ActiveRecord module ConnectionHandling def sqlserver_connection(config) #:nodoc: diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index cd10b87c2..0e7231dda 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'active_record/tasks/database_tasks' require 'shellwords' require 'ipaddr' diff --git a/lib/activerecord-sqlserver-adapter.rb b/lib/activerecord-sqlserver-adapter.rb index b420ca178..bfcdd0347 100644 --- a/lib/activerecord-sqlserver-adapter.rb +++ b/lib/activerecord-sqlserver-adapter.rb @@ -1 +1,3 @@ +# frozen_string_literal: true + require 'active_record/connection_adapters/sqlserver_adapter' diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 190239192..82b77d99e 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module Arel module Visitors class SQLServer < Arel::Visitors::ToSql diff --git a/lib/arel_sqlserver.rb b/lib/arel_sqlserver.rb index d437be596..f271dbb02 100644 --- a/lib/arel_sqlserver.rb +++ b/lib/arel_sqlserver.rb @@ -1,2 +1,4 @@ +# frozen_string_literal: true + require 'arel' require 'arel/visitors/sqlserver' From faa480b47037b907d7929230e1803da86f6dac1e Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 14 May 2020 17:01:21 +0100 Subject: [PATCH 0881/1412] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a51153243..1c592d5e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ #### Changed +- [#716](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/716) Translate the connection timed out error - [#763](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/763) Refactor columns introspection query to make it faster - [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix - [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests From e90bfd50cef5a8f72dda3f86a743bc8e292efb6f Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 14 May 2020 17:26:34 +0100 Subject: [PATCH 0882/1412] Tidy up Gemfile --- Gemfile | 76 ++++++++++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 41 deletions(-) diff --git a/Gemfile b/Gemfile index 38e71be06..2b094d3cc 100644 --- a/Gemfile +++ b/Gemfile @@ -1,69 +1,63 @@ # frozen_string_literal: true -require 'openssl' -source 'https://rubygems.org' +source "https://rubygems.org" git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec +gem "bcrypt" +gem "pg", ">= 0.18.0" gem "sqlite3", "~> 1.4" -gem "pg", ">= 0.18.0" +gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] -gem 'bcrypt' -gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] - -if RbConfig::CONFIG["host_os"] =~ /darwin/ - gem 'terminal-notifier-guard' -end - -if ENV['RAILS_SOURCE'] - gemspec path: ENV['RAILS_SOURCE'] +if ENV["RAILS_SOURCE"] + gemspec path: ENV["RAILS_SOURCE"] else # Need to get rails source because the gem doesn't include tests - version = ENV['RAILS_VERSION'] || begin - 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 - major, minor, tiny, pre = ver.split('.') - if !pre - uri = URI.parse "https://rubygems.org/api/v1/versions/activerecord.yaml" + version = ENV["RAILS_VERSION"] || begin + require "openssl" + 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 + major, minor, tiny, pre = ver.split(".") + + if pre + ver + else + 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 - YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).select do |data| - a, b, c = data['number'].split('.') - !data['prerelease'] && major == a && (minor.nil? || minor == b) - end.first['number'] - else - ver + YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).find do |data| + a, b, c = data["number"].split(".") + !data["prerelease"] && major == a && (minor.nil? || minor == b) + end["number"] end end - gem 'rails', github: "rails/rails", tag: "v#{version}" -end - -if ENV['AREL'] - gem 'arel', path: ENV['AREL'] + gem "rails", github: "rails/rails", tag: "v#{version}" end group :tinytds do - if ENV['TINYTDS_SOURCE'] - gem 'tiny_tds', path: ENV['TINYTDS_SOURCE'] - elsif ENV['TINYTDS_VERSION'] - gem 'tiny_tds', ENV['TINYTDS_VERSION'] + if ENV["TINYTDS_SOURCE"] + gem "tiny_tds", path: ENV["TINYTDS_SOURCE"] + elsif ENV["TINYTDS_VERSION"] + gem "tiny_tds", ENV["TINYTDS_VERSION"] else - gem 'tiny_tds' + gem "tiny_tds" end end group :development do - gem 'byebug', platform: [:mri, :mingw, :x64_mingw] - gem 'mocha' - gem 'minitest-spec-rails' + gem "pry-byebug", platform: [:mri, :mingw, :x64_mingw] + gem "mocha" + gem "minitest-spec-rails" end group :guard do - gem 'guard' - gem 'guard-minitest' + gem "guard" + gem "guard-minitest" + gem "terminal-notifier-guard" if RbConfig::CONFIG["host_os"] =~ /darwin/ end From 3a4231bf34a25d686d21244fc5cadd8cb07e7236 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 14 May 2020 17:26:54 +0100 Subject: [PATCH 0883/1412] Add .editorconfig --- .editorconfig | 9 +++++++++ CHANGELOG.md | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..c6c8b3621 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c592d5e4..21a74c0c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ #### Fixed -- [#690](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/690) Rails 6 support +- [#690](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/690) Rails 6 support - [#805](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/805) Rails 6: Fix database tasks tests for SQL Server - [#807](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/807) Rails 6: Skip binary fixtures test on Windows - [#809](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/809) Rails 6: Coerce reaper test using fork @@ -19,6 +19,7 @@ - [#821](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/821) Enable frozen strings - part 1 - [#822](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/822) Enable frozen strings - part 2 - [#823](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/823) Enable frozen strings - final +- [#824](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/824) Tidy up Gemfile #### Added From 60e2f0c6f2180aeba6bd931b16d924e053de81d4 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 12:47:55 +0100 Subject: [PATCH 0884/1412] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21a74c0c0..ccd45e76b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#809](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/809) Rails 6: Coerce reaper test using fork - [#810](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/810) Rails 6: Fix randomly failing tests due to schema load - [#812](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/812) Rails 6: Coerce ReloadModelsTest test on Windows +- [#818](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/818) Handle false return by TinyTDS if connection fails and fixed CI - [#819](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/819) Fix Ruby 2.7 kwargs warnings #### Changed From 7dabfb1723a524f26b08274eb53e5034d8e5ef37 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 13:35:26 +0100 Subject: [PATCH 0885/1412] Adjust error message when connection is dead TinyTDS returns false instead of raising an exception if connection fails while executing a query. Getting around this by raising an exception ourselves while this PR https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. The solution came from #717 and #818, I am just adjusting the error message to make it the same as that TinyTDS PR. --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ccd45e76b..983d1b304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - [#812](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/812) Rails 6: Coerce ReloadModelsTest test on Windows - [#818](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/818) Handle false return by TinyTDS if connection fails and fixed CI - [#819](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/819) Fix Ruby 2.7 kwargs warnings +- [#825](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/825) Adjust error message when connection is dead #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e68779ff8..b7bf24b18 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -353,10 +353,12 @@ def raw_connection_do(sql) case @connection_options[:mode] when :dblib result = @connection.execute(sql) - # If connection fails then TinyTDS returns false instead of an exception (see https://github.com/rails-sqlserver/tiny_tds/issues/464) - if result == false - raise TinyTds::Error, 'TinyTDS execute returned false instead of results' - end + + # TinyTDS returns false instead of raising an exception if connection fails. + # Getting around this by raising an exception ourselves while this PR + # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. + raise TinyTds::Error, 'failed to execute statement' if result.is_a?(FalseClass) + result.do end ensure From e11ec71ffa053bf5b96f673ce0e81145772422ef Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 15:36:51 +0100 Subject: [PATCH 0886/1412] Release v6.0.0.rc1 --- CHANGELOG.md | 2 +- guides/RELEASING.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 guides/RELEASING.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 983d1b304..ab300f12e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v6.0.0.rc1 (unreleased) +## v6.0.0.rc1 #### Fixed diff --git a/guides/RELEASING.md b/guides/RELEASING.md new file mode 100644 index 000000000..e41ef6c1a --- /dev/null +++ b/guides/RELEASING.md @@ -0,0 +1,11 @@ +# Releasing + +## Building locally + +If you want to build the gem to test it locally run `bundle exec rake build`. + +This command will build the gem in `pkg/activerecord-sqlserver-adapter-A.B.C.gem`, where `A.B.C` is the version in `VERSION` file. + +## Releasing to RubyGems + +Run `bundle exec rake release` to build the gem locally and push the `gem` file to RubyGems. From 747019ad1d0312b45dff2a5781f922837a6247f7 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 16:07:56 +0100 Subject: [PATCH 0887/1412] Rubocop: Enable Style/StringLiterals cop --- .gitignore | 1 + .rubocop.yml | 5 + Gemfile | 4 + Guardfile | 17 +- Rakefile | 24 +- .../sqlserver/core_ext/attribute_methods.rb | 2 +- .../sqlserver/core_ext/calculations.rb | 4 +- .../sqlserver/core_ext/explain.rb | 2 +- .../sqlserver/core_ext/finder_methods.rb | 4 +- .../sqlserver/core_ext/query_methods.rb | 4 +- .../sqlserver/database_statements.rb | 56 +-- .../sqlserver/database_tasks.rb | 6 +- .../connection_adapters/sqlserver/quoting.rb | 6 +- .../sqlserver/schema_dumper.rb | 10 +- .../sqlserver/schema_statements.rb | 166 ++++---- .../connection_adapters/sqlserver/showplan.rb | 12 +- .../sqlserver/showplan/printer_table.rb | 4 +- .../sqlserver/showplan/printer_xml.rb | 2 +- .../sqlserver/table_definition.rb | 2 +- .../sqlserver/transaction.rb | 6 +- .../connection_adapters/sqlserver/type.rb | 70 ++-- .../sqlserver/type/date.rb | 2 +- .../sqlserver/type/datetime.rb | 2 +- .../sqlserver/type/time_value_fractional.rb | 4 +- .../sqlserver/type/unicode_char.rb | 2 +- .../sqlserver/type/varbinary.rb | 2 +- .../sqlserver/type/varchar.rb | 2 +- .../connection_adapters/sqlserver/utils.rb | 8 +- .../connection_adapters/sqlserver_adapter.rb | 152 ++++---- lib/active_record/sqlserver_base.rb | 2 +- .../tasks/sqlserver_database_tasks.rb | 42 +- lib/activerecord-sqlserver-adapter.rb | 2 +- lib/arel/visitors/sqlserver.rb | 4 +- lib/arel_sqlserver.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 306 +++++++-------- .../change_column_null_test_sqlserver.rb | 24 +- test/cases/coerced_tests.rb | 116 +++--- test/cases/column_test_sqlserver.rb | 368 +++++++++--------- test/cases/connection_test_sqlserver.rb | 30 +- .../cases/execute_procedure_test_sqlserver.rb | 36 +- test/cases/fetch_test_sqlserver.rb | 26 +- ...lly_qualified_identifier_test_sqlserver.rb | 24 +- test/cases/helper_sqlserver.rb | 26 +- test/cases/in_clause_test_sqlserver.rb | 18 +- test/cases/index_test_sqlserver.rb | 26 +- test/cases/json_test_sqlserver.rb | 28 +- test/cases/migration_test_sqlserver.rb | 38 +- test/cases/order_test_sqlserver.rb | 102 ++--- .../pessimistic_locking_test_sqlserver.rb | 48 +-- test/cases/rake_test_sqlserver.rb | 62 +-- test/cases/schema_dumper_test_sqlserver.rb | 202 +++++----- test/cases/schema_test_sqlserver.rb | 28 +- test/cases/scratchpad_test_sqlserver.rb | 4 +- test/cases/showplan_test_sqlserver.rb | 48 +-- test/cases/specific_schema_test_sqlserver.rb | 118 +++--- test/cases/transaction_test_sqlserver.rb | 30 +- test/cases/trigger_test_sqlserver.rb | 22 +- test/cases/utils_test_sqlserver.rb | 118 +++--- test/cases/uuid_test_sqlserver.rb | 22 +- test/debug.rb | 12 +- .../create_clients_and_change_column_null.rb | 2 +- .../1_table_will_never_be_created.rb | 2 +- test/models/sqlserver/booking.rb | 2 +- test/models/sqlserver/customers_view.rb | 2 +- test/models/sqlserver/dollar_table_name.rb | 2 +- test/models/sqlserver/edge_schema.rb | 2 +- test/models/sqlserver/fk_has_fk.rb | 2 +- test/models/sqlserver/fk_has_pk.rb | 2 +- test/models/sqlserver/natural_pk_data.rb | 4 +- test/models/sqlserver/natural_pk_int_data.rb | 2 +- test/models/sqlserver/no_pk_data.rb | 2 +- test/models/sqlserver/object_default.rb | 2 +- test/models/sqlserver/quoted_table.rb | 4 +- test/models/sqlserver/quoted_view_1.rb | 2 +- test/models/sqlserver/quoted_view_2.rb | 2 +- test/models/sqlserver/sst_memory.rb | 2 +- test/models/sqlserver/string_default.rb | 2 +- .../sqlserver/string_defaults_big_view.rb | 2 +- test/models/sqlserver/string_defaults_view.rb | 2 +- test/models/sqlserver/tinyint_pk.rb | 2 +- test/models/sqlserver/trigger.rb | 4 +- test/models/sqlserver/trigger_history.rb | 2 +- test/models/sqlserver/upper.rb | 2 +- test/models/sqlserver/uppered.rb | 2 +- test/models/sqlserver/uuid.rb | 2 +- test/schema/sqlserver_specific_schema.rb | 32 +- test/support/core_ext/query_cache.rb | 2 +- test/support/load_schema_sqlserver.rb | 6 +- test/support/minitest_sqlserver.rb | 2 +- test/support/paths_sqlserver.rb | 18 +- test/support/rake_helpers.rb | 18 +- test/support/test_in_memory_oltp.rb | 14 +- 92 files changed, 1338 insertions(+), 1327 deletions(-) create mode 100644 .rubocop.yml diff --git a/.gitignore b/.gitignore index ff20d0deb..27b05d95f 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ debug.log .DS_Store pkg/ doc/ +db/ *.gem .bundle Gemfile.lock diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 000000000..55040ad53 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,5 @@ +AllCops: + TargetRubyVersion: 2.5 + +Style/StringLiterals: + EnforcedStyle: double_quotes diff --git a/Gemfile b/Gemfile index 2b094d3cc..a5b35593e 100644 --- a/Gemfile +++ b/Gemfile @@ -61,3 +61,7 @@ group :guard do gem "guard-minitest" gem "terminal-notifier-guard" if RbConfig::CONFIG["host_os"] =~ /darwin/ end + +group :rubocop do + gem "rubocop", require: false +end diff --git a/Guardfile b/Guardfile index 2f8a80820..ab781a114 100644 --- a/Guardfile +++ b/Guardfile @@ -1,29 +1,30 @@ +# frozen_string_literal: true -require_relative 'test/support/paths_sqlserver' +require_relative "test/support/paths_sqlserver" clearing :on notification :terminal_notifier if defined?(TerminalNotifier) ignore %r{debug\.log} -ar_lib = File.join ARTest::SQLServer.root_activerecord, 'lib' -ar_test = File.join ARTest::SQLServer.root_activerecord, 'test' +ar_lib = File.join ARTest::SQLServer.root_activerecord, "lib" +ar_test = File.join ARTest::SQLServer.root_activerecord, "test" guard :minitest, { all_on_start: false, autorun: false, - include: ['lib', 'test', ar_lib, ar_test], - test_folders: ['test'], + include: ["lib", "test", ar_lib, ar_test], + test_folders: ["test"], test_file_patterns: ["*_test.rb", "*_test_sqlserver.rb"] } do # Our project watchers. - if ENV['TEST_FILES'] - ENV['TEST_FILES'].split(',').map(&:strip).each do |file| + if ENV["TEST_FILES"] + ENV["TEST_FILES"].split(",").map(&:strip).each do |file| watch(%r{.*}) { file } end 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{^test/cases/helper_sqlserver\.rb$}) { 'test' } + watch(%r{^test/cases/helper_sqlserver\.rb$}) { "test" } end end diff --git a/Rakefile b/Rakefile index 1477ac6f5..d8174b1b2 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'bundler/gem_tasks' -require 'rake/testtask' -require_relative 'test/support/paths_sqlserver' -require_relative 'test/support/rake_helpers' +require "bundler/gem_tasks" +require "rake/testtask" +require_relative "test/support/paths_sqlserver" +require_relative "test/support/rake_helpers" -task test: ['test:dblib'] +task test: ["test:dblib"] task default: [:test] namespace :test do @@ -15,25 +15,25 @@ namespace :test do Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files - t.warning = !!ENV['WARNING'] + t.warning = !!ENV["WARNING"] t.verbose = false end end - task 'dblib:env' do - ENV['ARCONN'] = 'dblib' + task "dblib:env" do + ENV["ARCONN"] = "dblib" end end -task 'test:dblib' => 'test:dblib:env' +task "test:dblib" => "test:dblib:env" namespace :profile do - ['dblib'].each do |mode| + ["dblib"].each do |mode| namespace mode.to_sym do - Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file| - profile_case = File.basename(test_file).sub('_profile_case.rb', '') + Dir.glob("test/profile/*_profile_case.rb").sort.each do |test_file| + profile_case = File.basename(test_file).sub("_profile_case.rb", "") Rake::TestTask.new(profile_case) do |t| t.libs = ARTest::SQLServer.test_load_paths t.test_files = [test_file] 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 815da2914..715472c81 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 @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'active_record/attribute_methods' +require "active_record/attribute_methods" module ActiveRecord module ConnectionAdapters diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index c17d77876..dc6e73360 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'active_record/relation' -require 'active_record/version' +require "active_record/relation" +require "active_record/version" module ActiveRecord module ConnectionAdapters 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 76e1eb7a8..b71e7a0ad 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -6,7 +6,7 @@ module SQLServer module CoreExt module Explain - SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql ' + SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql " SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries) 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 7db8aaecc..8a37a27b6 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 @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'active_record/relation' -require 'active_record/version' +require "active_record/relation" +require "active_record/version" module ActiveRecord module ConnectionAdapters diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb index 31ed4fa79..ae89a12a0 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'active_record/relation' -require 'active_record/version' +require "active_record/relation" +require "active_record/version" module ActiveRecord module ConnectionAdapters diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b7bf24b18..98a2b9efe 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -25,7 +25,7 @@ def execute(sql, name = nil) end end - def exec_query(sql, name = 'SQL', binds = [], prepare: false) + def exec_query(sql, name = "SQL", binds = [], prepare: false) if preventing_writes? && write_query?(sql) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end @@ -44,17 +44,17 @@ def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil) end def exec_delete(sql, name, binds) - sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows' + sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" super(sql, name, binds).rows.first.first end def exec_update(sql, name, binds) - sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows' + sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" super(sql, name, binds).rows.first.first end def begin_db_transaction - do_execute 'BEGIN TRANSACTION' + do_execute "BEGIN TRANSACTION" end def transaction_isolation_levels @@ -71,11 +71,11 @@ def set_transaction_isolation_level(isolation_level) end def commit_db_transaction - do_execute 'COMMIT TRANSACTION' + do_execute "COMMIT TRANSACTION" end def exec_rollback_db_transaction - do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' + do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" end include Savepoints @@ -159,9 +159,9 @@ def execute_procedure(proc_name, *variables) variables.first.map { |k, v| "@#{k} = #{quote(v)}" } else variables.map { |v| quote(v) } - end.join(', ') + end.join(", ") sql = "EXEC #{proc_name} #{vars}".strip - name = 'Execute Procedure' + name = "Execute Procedure" log(sql, name) do case @connection_options[:mode] when :dblib @@ -192,14 +192,14 @@ def use_database(database = nil) def user_options return {} if sqlserver_azure? - rows = select_rows('DBCC USEROPTIONS WITH NO_INFOMSGS', 'SCHEMA') + 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| if row.instance_of? Hash - set_option = row.values[0].gsub(/\s+/, '_') + set_option = row.values[0].gsub(/\s+/, "_") user_value = row.values[1] elsif row.instance_of? Array - set_option = row[0].gsub(/\s+/, '_') + set_option = row[0].gsub(/\s+/, "_") user_value = row[1] end values[set_option] = user_value @@ -209,9 +209,9 @@ def user_options def user_options_dateformat if sqlserver_azure? - select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA' + select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID", "SCHEMA" else - user_options['dateformat'] + user_options["dateformat"] end end @@ -226,26 +226,26 @@ def user_options_isolation_level WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level] FROM [sys].[dm_exec_sessions] WHERE [session_id] = @@SPID).squish - select_value sql, 'SCHEMA' + select_value sql, "SCHEMA" else - user_options['isolation_level'] + user_options["isolation_level"] end end def user_options_language if sqlserver_azure? - select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA' + select_value "SELECT @@LANGUAGE AS [language]", "SCHEMA" else - user_options['language'] + user_options["language"] end end def newid_function - select_value 'SELECT NEWID()' + select_value "SELECT NEWID()" end def newsequentialid_function - select_value 'SELECT NEWSEQUENTIALID()' + select_value "SELECT NEWSEQUENTIALID()" end @@ -263,7 +263,7 @@ def sql_for_insert(sql, pk, binds) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted - id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted + id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted <<~SQL.squish DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} @@ -288,7 +288,7 @@ def set_identity_insert(table_name, enable = true) # === SQLServer Specific (Executing) ============================ # - def do_execute(sql, name = 'SQL') + def do_execute(sql, name = "SQL") materialize_transactions log(sql, name) { raw_connection_do(sql) } @@ -318,9 +318,9 @@ def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) case value = attr.value_for_database when Numeric - value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze + value > 2_147_483_647 ? "bigint".freeze : "int".freeze else - 'nvarchar(max)'.freeze + "nvarchar(max)".freeze end end @@ -335,14 +335,14 @@ def sp_executesql_sql_param(attr) end def sp_executesql_sql(sql, types, params, name) - if name == 'EXPLAIN' + if name == "EXPLAIN" params.each.with_index do |param, index| substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values. sql = sql.sub substitute_at_finder, param.to_s end else - types = quote(types.join(', ')) - params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp. + types = quote(types.join(", ")) + params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(", ") # Only p is needed, but with @i helps explain regexp. sql = "EXEC sp_executesql #{quote(sql)}" sql += ", #{types}, #{params}" unless params.empty? end @@ -357,7 +357,7 @@ def raw_connection_do(sql) # TinyTDS returns false instead of raising an exception if connection fails. # Getting around this by raising an exception ourselves while this PR # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. - raise TinyTds::Error, 'failed to execute statement' if result.is_a?(FalseClass) + raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) result.do end @@ -407,7 +407,7 @@ def identity_columns(table_name) # === SQLServer Specific (Selecting) ============================ # - def raw_select(sql, name = 'SQL', binds = [], options = {}) + def raw_select(sql, name = "SQL", binds = [], options = {}) log(sql, name, binds) { _raw_select(sql, options) } end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 687773806..3e6c47fdb 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -19,7 +19,7 @@ def drop_database(database) end def current_database - select_value 'SELECT DB_NAME()' + select_value "SELECT DB_NAME()" end def charset @@ -41,7 +41,7 @@ def create_database_options(options={}) v.present? }.slice(*keys).map { |k,v| "#{k.to_s.upcase} #{v}" - }.join(' ') + }.join(" ") options end @@ -56,7 +56,7 @@ def create_database_edition_options(options={}) v.present? }.slice(*keys).map { |k,v| "#{k.to_s.upcase} = #{v}" - }.join(', ') + }.join(", ") edition_options = "( #{edition_options} )" if edition_options.present? edition_options end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index ad11c4900..89583bbfb 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -5,9 +5,9 @@ module ConnectionAdapters module SQLServer module Quoting - QUOTED_TRUE = '1'.freeze - QUOTED_FALSE = '0'.freeze - QUOTED_STRING_PREFIX = 'N'.freeze + QUOTED_TRUE = "1".freeze + QUOTED_FALSE = "0".freeze + QUOTED_STRING_PREFIX = "N".freeze def fetch_type_metadata(sql_type, sqlserver_options = {}) cast_type = lookup_cast_type(sql_type) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index bd5d746c9..198bc293a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -6,11 +6,11 @@ module SQLServer class SchemaDumper < ConnectionAdapters::SchemaDumper SQLSEVER_NO_LIMIT_TYPES = [ - 'text', - 'ntext', - 'varchar(max)', - 'nvarchar(max)', - 'varbinary(max)' + "text", + "ntext", + "varchar(max)", + "nvarchar(max)", + "varbinary(max)" ].freeze private diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index cc0f87e1a..e50b21217 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -19,11 +19,11 @@ def drop_table(table_name, **options) # Mimic CASCADE option as best we can. if options[:force] == :cascade execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata| - fktable = fkdata['FKTABLE_NAME'] - fkcolmn = fkdata['FKCOLUMN_NAME'] - pktable = fkdata['PKTABLE_NAME'] - pkcolmn = fkdata['PKCOLUMN_NAME'] - remove_foreign_key fktable, name: fkdata['FK_NAME'] + fktable = fkdata["FKTABLE_NAME"] + fkcolmn = fkdata["FKCOLUMN_NAME"] + pktable = fkdata["PKTABLE_NAME"] + pkcolmn = fkdata["PKCOLUMN_NAME"] + remove_foreign_key fktable, name: fkdata["FK_NAME"] do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" end end @@ -49,11 +49,11 @@ def indexes(table_name) orders = {} columns = [] - index[:index_keys].split(',').each do |column| + index[:index_keys].split(",").each do |column| column.strip! - if column.ends_with?('(-)') - column.gsub! '(-)', '' + if column.ends_with?("(-)") + column.gsub! "(-)", "" orders[column] = :desc end @@ -117,12 +117,12 @@ def primary_keys_select(table_name) AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))} 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 - binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) - binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? - sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] } + binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) + binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? + sp_executesql(sql, "SCHEMA", binds).map { |r| r["name"] } end def rename_table(table_name, new_name) @@ -131,7 +131,7 @@ def rename_table(table_name, new_name) end def remove_column(table_name, column_name, type = nil, options = {}) - raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array + raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_name.is_a? Array remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) @@ -155,7 +155,7 @@ def change_column(table_name, column_name, type, options = {}) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" - alter_command += ' NOT NULL' if !options[:null].nil? && options[:null] == false + alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false sql_commands << alter_command if without_constraints default = quote_default_expression(default, column_object || column_for(table_name, column_name)) @@ -182,7 +182,7 @@ def change_column_default(table_name, column_name, default_or_changes) def rename_column(table_name, column_name, new_column_name) clear_cache! identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}") - execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN' + execute_procedure :sp_rename, identifier.quoted, new_column_name, "COLUMN" rename_column_indexes(table_name, column_name, new_column_name) clear_cache! end @@ -190,7 +190,7 @@ def rename_column(table_name, column_name, new_column_name) def rename_index(table_name, old_name, new_name) raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}") - execute_procedure :sp_rename, identifier.quoted, new_name, 'INDEX' + execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX" end def remove_index!(table_name, index_name) @@ -202,13 +202,13 @@ def foreign_keys(table_name) fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema fk_info.map do |row| from_table = identifier.object - to_table = row['PKTABLE_NAME'] + to_table = row["PKTABLE_NAME"] options = { - name: row['FK_NAME'], - column: row['FKCOLUMN_NAME'], - primary_key: row['PKCOLUMN_NAME'], - on_update: extract_foreign_key_action('update', row['FK_NAME']), - on_delete: extract_foreign_key_action('delete', row['FK_NAME']) + name: row["FK_NAME"], + column: row["FKCOLUMN_NAME"], + primary_key: row["PKCOLUMN_NAME"], + on_update: extract_foreign_key_action("update", row["FK_NAME"]), + on_delete: extract_foreign_key_action("delete", row["FK_NAME"]) } ForeignKeyDefinition.new from_table, to_table, options end @@ -216,8 +216,8 @@ def foreign_keys(table_name) def extract_foreign_key_action(action, fk_name) case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'") - when 'CASCADE' then :cascade - when 'SET_NULL' then :nullify + when "CASCADE" then :cascade + when "SET_NULL" then :nullify end end @@ -225,15 +225,15 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable case type.to_s - when 'integer' + 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 'datetime2' + when "datetime2" column_type_sql = super if precision if (0..7) === precision @@ -251,8 +251,8 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map{ |s| s = s.to_sql unless s.is_a?(String) - s.gsub(/\s+(?:ASC|DESC)\b/i, '') - .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '') + s.gsub(/\s+(?:ASC|DESC)\b/i, "") + .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } (order_columns << super).join(", ") @@ -270,7 +270,7 @@ def change_column_null(table_name, column_name, allow_null, default = nil) do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}" - sql += ' NOT NULL' if !allow_null.nil? && allow_null == false + sql += " NOT NULL" if !allow_null.nil? && allow_null == false do_execute sql end @@ -282,10 +282,10 @@ def create_schema_dumper(options) def data_source_sql(name = nil, type: nil) scope = quoted_scope name, type: type - table_name = lowercase_schema_reflection_sql 'TABLE_NAME' + table_name = lowercase_schema_reflection_sql "TABLE_NAME" sql = "SELECT #{table_name}" - sql += ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)' - sql += ' WHERE TABLE_CATALOG = DB_NAME()' + sql += " FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)" + sql += " WHERE TABLE_CATALOG = DB_NAME()" sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] @@ -296,7 +296,7 @@ def data_source_sql(name = nil, type: nil) def quoted_scope(name = nil, type: nil) identifier = SQLServer::Utils.extract_identifiers(name) {}.tap do |scope| - scope[:schema] = identifier.schema || 'dbo' + scope[:schema] = identifier.schema || "dbo" scope[:name] = identifier.object if identifier.object scope[:type] = type if type end @@ -306,37 +306,37 @@ def quoted_scope(name = nil, type: nil) def initialize_native_database_types { - primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY', - primary_key_nonclustered: 'int 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: 'datetime' }, - 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)' } + primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY", + primary_key_nonclustered: "int 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: "datetime" }, + 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 @@ -350,9 +350,9 @@ def column_definitions(table_name) binds = [] nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 - binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128) - binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank? - results = sp_executesql(sql, 'SCHEMA', binds) + binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) + binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? + results = sp_executesql(sql, "SCHEMA", binds) results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] @@ -383,7 +383,7 @@ def column_definitions(table_name) WHERE c.TABLE_NAME = '#{view_tblnm}' AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}' - }.squish, 'SCHEMA' + }.squish, "SCHEMA" end case default when nil @@ -402,7 +402,7 @@ def column_definitions(table_name) else ci[:type] end value = default.match(/\A\((.*)\)\Z/m)[1] - value = select_value("SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA') + value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA") [value, nil] end end @@ -415,11 +415,11 @@ def column_definitions(table_name) end def column_definitions_sql(database, identifier) - object_name = prepared_statements ? '@0' : quote(identifier.object) + object_name = prepared_statements ? "@0" : quote(identifier.object) schema_name = if identifier.schema.blank? - 'schema_name()' + "schema_name()" else - prepared_statements ? '@1' : quote(identifier.schema) + prepared_statements ? "@1" : quote(identifier.schema) end %{ @@ -478,11 +478,11 @@ def column_definitions_sql(database, identifier) AND s.name = #{schema_name} ORDER BY c.column_id - }.gsub(/[ \t\r\n]+/, ' ').strip + }.gsub(/[ \t\r\n]+/, " ").strip end def remove_check_constraints(table_name, column_name) - constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA' + constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", "SCHEMA" constraints.each do |constraint| do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}" end @@ -490,8 +490,8 @@ def remove_check_constraints(table_name, column_name) def remove_default_constraint(table_name, column_name) # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case. - execute_procedure(:sp_helpconstraint, table_name, 'nomsg').flatten.select do |row| - row['constraint_type'] == "DEFAULT on column #{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| do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}" end @@ -528,19 +528,19 @@ def lowercase_schema_reflection_sql(node) def view_table_name(table_name) view_info = view_information(table_name) - view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name + view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name end def view_information(table_name) @view_information ||= {} @view_information[table_name] ||= begin identifier = SQLServer::Utils.extract_identifiers(table_name) - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA' + view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA" if view_info view_info = view_info.with_indifferent_access 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 + 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 diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 1891cb588..9c296a8ee 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -1,21 +1,21 @@ # frozen_string_literal: true -require 'active_record/connection_adapters/sqlserver/showplan/printer_table' -require 'active_record/connection_adapters/sqlserver/showplan/printer_xml' +require "active_record/connection_adapters/sqlserver/showplan/printer_table" +require "active_record/connection_adapters/sqlserver/showplan/printer_xml" module ActiveRecord module ConnectionAdapters module SQLServer module Showplan - OPTION_ALL = 'SHOWPLAN_ALL' - OPTION_TEXT = 'SHOWPLAN_TEXT' - OPTION_XML = 'SHOWPLAN_XML' + OPTION_ALL = "SHOWPLAN_ALL" + OPTION_TEXT = "SHOWPLAN_TEXT" + OPTION_XML = "SHOWPLAN_XML" OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML] def explain(arel, binds = []) sql = to_sql(arel) - result = with_showplan_on { sp_executesql(sql, 'EXPLAIN', binds) } + result = with_showplan_on { sp_executesql(sql, "EXPLAIN", binds) } printer = showplan_printer.new(result) printer.pp end 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 4885d74cc..287e4efb7 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb @@ -43,7 +43,7 @@ def compute_column_widths end def build_separator - '+' + @widths.map { |w| '-' * (w + (cell_padding * 2)) }.join('+') + '+' + "+" + @widths.map { |w| "-" * (w + (cell_padding * 2)) }.join("+") + "+" end def build_cells(items) @@ -56,7 +56,7 @@ def build_cells(items) def cast_item(item) case item - when NilClass then 'NULL' + when NilClass then "NULL" when Float then item.to_s.to(9) else item.to_s.truncate(max_column_width) end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb index 00c268143..717eaf859 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb @@ -12,7 +12,7 @@ def initialize(result) def pp xml = @result.rows.first.first if defined?(Nokogiri) - Nokogiri::XML(xml).to_xml indent: 2, encoding: 'UTF-8' + Nokogiri::XML(xml).to_xml indent: 2, encoding: "UTF-8" else xml end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 244f33295..1027a6d97 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -10,7 +10,7 @@ def primary_key(name, type = :primary_key, **options) if [:integer, :bigint].include?(type) options[:is_identity] = true unless options.key?(:default) elsif type == :uuid - options[:default] = options.fetch(:default, 'NEWID()') + options[:default] = options.fetch(:default, "NEWID()") options[:primary_key] = true end super diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index baba63859..bdc8d78a2 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'active_record/connection_adapters/abstract/transaction' +require "active_record/connection_adapters/abstract/transaction" module ActiveRecord module ConnectionAdapters @@ -19,8 +19,8 @@ def current_isolation_level # When READ_COMMITTED_SNAPSHOT is set to ON, # user_options_isolation_level will be equal to 'read committed # snapshot' which is not a valid isolation level - if level.blank? || level == 'read committed snapshot' - 'READ COMMITTED' + if level.blank? || level == "read committed snapshot" + "READ COMMITTED" else level.upcase end diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 59121074a..6c5d82b4d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -1,48 +1,48 @@ # frozen_string_literal: true -require 'active_record/type' +require "active_record/type" # Behaviors -require 'active_record/connection_adapters/sqlserver/type/data' -require 'active_record/connection_adapters/sqlserver/type/time_value_fractional' +require "active_record/connection_adapters/sqlserver/type/data" +require "active_record/connection_adapters/sqlserver/type/time_value_fractional" # Exact Numerics -require 'active_record/connection_adapters/sqlserver/type/integer' -require 'active_record/connection_adapters/sqlserver/type/big_integer' -require 'active_record/connection_adapters/sqlserver/type/small_integer' -require 'active_record/connection_adapters/sqlserver/type/tiny_integer' -require 'active_record/connection_adapters/sqlserver/type/boolean' -require 'active_record/connection_adapters/sqlserver/type/decimal' -require 'active_record/connection_adapters/sqlserver/type/money' -require 'active_record/connection_adapters/sqlserver/type/small_money' +require "active_record/connection_adapters/sqlserver/type/integer" +require "active_record/connection_adapters/sqlserver/type/big_integer" +require "active_record/connection_adapters/sqlserver/type/small_integer" +require "active_record/connection_adapters/sqlserver/type/tiny_integer" +require "active_record/connection_adapters/sqlserver/type/boolean" +require "active_record/connection_adapters/sqlserver/type/decimal" +require "active_record/connection_adapters/sqlserver/type/money" +require "active_record/connection_adapters/sqlserver/type/small_money" # Approximate Numerics -require 'active_record/connection_adapters/sqlserver/type/float' -require 'active_record/connection_adapters/sqlserver/type/real' +require "active_record/connection_adapters/sqlserver/type/float" +require "active_record/connection_adapters/sqlserver/type/real" # Date and Time -require 'active_record/connection_adapters/sqlserver/type/date' -require 'active_record/connection_adapters/sqlserver/type/datetime' -require 'active_record/connection_adapters/sqlserver/type/datetime2' -require 'active_record/connection_adapters/sqlserver/type/datetimeoffset' -require 'active_record/connection_adapters/sqlserver/type/smalldatetime' -require 'active_record/connection_adapters/sqlserver/type/time' +require "active_record/connection_adapters/sqlserver/type/date" +require "active_record/connection_adapters/sqlserver/type/datetime" +require "active_record/connection_adapters/sqlserver/type/datetime2" +require "active_record/connection_adapters/sqlserver/type/datetimeoffset" +require "active_record/connection_adapters/sqlserver/type/smalldatetime" +require "active_record/connection_adapters/sqlserver/type/time" # Character Strings -require 'active_record/connection_adapters/sqlserver/type/string' -require 'active_record/connection_adapters/sqlserver/type/char' -require 'active_record/connection_adapters/sqlserver/type/varchar' -require 'active_record/connection_adapters/sqlserver/type/varchar_max' -require 'active_record/connection_adapters/sqlserver/type/text' +require "active_record/connection_adapters/sqlserver/type/string" +require "active_record/connection_adapters/sqlserver/type/char" +require "active_record/connection_adapters/sqlserver/type/varchar" +require "active_record/connection_adapters/sqlserver/type/varchar_max" +require "active_record/connection_adapters/sqlserver/type/text" # Unicode Character Strings -require 'active_record/connection_adapters/sqlserver/type/unicode_string' -require 'active_record/connection_adapters/sqlserver/type/unicode_char' -require 'active_record/connection_adapters/sqlserver/type/unicode_varchar' -require 'active_record/connection_adapters/sqlserver/type/unicode_varchar_max' -require 'active_record/connection_adapters/sqlserver/type/unicode_text' +require "active_record/connection_adapters/sqlserver/type/unicode_string" +require "active_record/connection_adapters/sqlserver/type/unicode_char" +require "active_record/connection_adapters/sqlserver/type/unicode_varchar" +require "active_record/connection_adapters/sqlserver/type/unicode_varchar_max" +require "active_record/connection_adapters/sqlserver/type/unicode_text" # Binary Strings -require 'active_record/connection_adapters/sqlserver/type/binary' -require 'active_record/connection_adapters/sqlserver/type/varbinary' -require 'active_record/connection_adapters/sqlserver/type/varbinary_max' +require "active_record/connection_adapters/sqlserver/type/binary" +require "active_record/connection_adapters/sqlserver/type/varbinary" +require "active_record/connection_adapters/sqlserver/type/varbinary_max" # Other Data Types -require 'active_record/connection_adapters/sqlserver/type/uuid' -require 'active_record/connection_adapters/sqlserver/type/timestamp' -require 'active_record/connection_adapters/sqlserver/type/json' +require "active_record/connection_adapters/sqlserver/type/uuid" +require "active_record/connection_adapters/sqlserver/type/timestamp" +require "active_record/connection_adapters/sqlserver/type/json" module ActiveRecord module Type diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 845f2b6db..43896458d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -7,7 +7,7 @@ module Type class Date < ActiveRecord::Type::Date def sqlserver_type - 'date' + "date" end def serialize(value) diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 59429cdc0..36c861f27 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -36,7 +36,7 @@ def quoted(value) private def fast_string_to_time(string) - time = ActiveSupport::TimeZone['UTC'].strptime(string, fast_string_to_time_format) + 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)) rescue ArgumentError 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 aa0b0ed73..45933248c 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 @@ -25,7 +25,7 @@ def quote_fractional(value) return 0 if fractional_scale == 0 frac_seconds = seconds_precision(value) seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) - seconds.to_d.to_s.split('.').last.to(fractional_scale-1) + seconds.to_d.to_s.split(".").last.to(fractional_scale-1) end def fractional_property @@ -82,7 +82,7 @@ def fractional_max end def fractional_scale_max - ('9' * fractional_scale) + ('0' * (fractional_digits - fractional_scale)) + ("9" * fractional_scale) + ("0" * (fractional_digits - fractional_scale)) end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb index 26ad415aa..0eb364234 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -11,7 +11,7 @@ def type end def sqlserver_type - 'nchar'.yield_self do |type| + "nchar".yield_self do |type| type += "(#{limit})" if limit type end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index 6ae0af9cd..c00473dd5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -16,7 +16,7 @@ def type end def sqlserver_type - 'varbinary'.yield_self do |type| + "varbinary".yield_self do |type| type += "(#{limit})" if limit type end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index a19216696..0e08c4e75 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -16,7 +16,7 @@ def type end def sqlserver_type - 'varchar'.yield_self do |type| + "varchar".yield_self do |type| type += "(#{limit})" if limit type end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index bd2a6fdf9..a5b74faa0 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -1,13 +1,13 @@ # frozen_string_literal: true -require 'strscan' +require "strscan" module ActiveRecord module ConnectionAdapters module SQLServer module Utils - QUOTED_STRING_PREFIX = 'N' + QUOTED_STRING_PREFIX = "N" # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils. @@ -93,7 +93,7 @@ def parse_raw_name @schema = @parts.first end rest = scanner.rest - rest = rest.starts_with?('.') ? rest[1..-1] : rest[0..-1] + rest = rest.starts_with?(".") ? rest[1..-1] : rest[0..-1] @object = unquote(rest) @parts << @object end @@ -103,7 +103,7 @@ def quote(part) end def unquote(part) - if part && part.start_with?('[') + if part && part.start_with?("[") part[1..-2] else part diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index d22cb238e..2f58eb5c2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,35 +1,35 @@ # frozen_string_literal: true -require 'base64' -require 'active_record' -require 'arel_sqlserver' -require 'active_record/connection_adapters/abstract_adapter' -require 'active_record/connection_adapters/sqlserver/core_ext/active_record' -require 'active_record/connection_adapters/sqlserver/core_ext/calculations' -require 'active_record/connection_adapters/sqlserver/core_ext/explain' -require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber' -require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods' -require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods' -require 'active_record/connection_adapters/sqlserver/core_ext/query_methods' -require 'active_record/connection_adapters/sqlserver/core_ext/preloader' -require 'active_record/connection_adapters/sqlserver/version' -require 'active_record/connection_adapters/sqlserver/type' -require 'active_record/connection_adapters/sqlserver/database_limits' -require 'active_record/connection_adapters/sqlserver/database_statements' -require 'active_record/connection_adapters/sqlserver/database_tasks' -require 'active_record/connection_adapters/sqlserver/transaction' -require 'active_record/connection_adapters/sqlserver/errors' -require 'active_record/connection_adapters/sqlserver/schema_creation' -require 'active_record/connection_adapters/sqlserver/schema_dumper' -require 'active_record/connection_adapters/sqlserver/schema_statements' -require 'active_record/connection_adapters/sqlserver/sql_type_metadata' -require 'active_record/connection_adapters/sqlserver/showplan' -require 'active_record/connection_adapters/sqlserver/table_definition' -require 'active_record/connection_adapters/sqlserver/quoting' -require 'active_record/connection_adapters/sqlserver/utils' -require 'active_record/sqlserver_base' -require 'active_record/connection_adapters/sqlserver_column' -require 'active_record/tasks/sqlserver_database_tasks' +require "base64" +require "active_record" +require "arel_sqlserver" +require "active_record/connection_adapters/abstract_adapter" +require "active_record/connection_adapters/sqlserver/core_ext/active_record" +require "active_record/connection_adapters/sqlserver/core_ext/calculations" +require "active_record/connection_adapters/sqlserver/core_ext/explain" +require "active_record/connection_adapters/sqlserver/core_ext/explain_subscriber" +require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods" +require "active_record/connection_adapters/sqlserver/core_ext/finder_methods" +require "active_record/connection_adapters/sqlserver/core_ext/query_methods" +require "active_record/connection_adapters/sqlserver/core_ext/preloader" +require "active_record/connection_adapters/sqlserver/version" +require "active_record/connection_adapters/sqlserver/type" +require "active_record/connection_adapters/sqlserver/database_limits" +require "active_record/connection_adapters/sqlserver/database_statements" +require "active_record/connection_adapters/sqlserver/database_tasks" +require "active_record/connection_adapters/sqlserver/transaction" +require "active_record/connection_adapters/sqlserver/errors" +require "active_record/connection_adapters/sqlserver/schema_creation" +require "active_record/connection_adapters/sqlserver/schema_dumper" +require "active_record/connection_adapters/sqlserver/schema_statements" +require "active_record/connection_adapters/sqlserver/sql_type_metadata" +require "active_record/connection_adapters/sqlserver/showplan" +require "active_record/connection_adapters/sqlserver/table_definition" +require "active_record/connection_adapters/sqlserver/quoting" +require "active_record/connection_adapters/sqlserver/utils" +require "active_record/sqlserver_base" +require "active_record/connection_adapters/sqlserver_column" +require "active_record/tasks/sqlserver_database_tasks" module ActiveRecord module ConnectionAdapters @@ -43,7 +43,7 @@ class SQLServerAdapter < AbstractAdapter SQLServer::DatabaseLimits, SQLServer::DatabaseTasks - ADAPTER_NAME = 'SQLServer'.freeze + ADAPTER_NAME = "SQLServer".freeze # Default precision for 'time' (See https://docs.microsoft.com/en-us/sql/t-sql/data-types/time-transact-sql) DEFAULT_TIME_PRECISION = 7 @@ -56,7 +56,7 @@ class SQLServerAdapter < AbstractAdapter cattr_accessor :showplan_option, instance_accessor: false cattr_accessor :lowercase_schema_reflection - self.cs_equality_operator = 'COLLATE Latin1_General_CS_AS_WS' + self.cs_equality_operator = "COLLATE Latin1_General_CS_AS_WS" self.use_output_inserted = true self.exclude_output_inserted_table_names = Concurrent::Map.new { false } @@ -189,7 +189,7 @@ def disable_referential_integrity def active? return false unless @connection - raw_connection_do 'SELECT 1' + raw_connection_do "SELECT 1" true rescue *connection_errors false @@ -219,7 +219,7 @@ def clear_cache! def reset! reset_transaction - do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION' + do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" end # === Abstract Adapter (Misc Support) =========================== # @@ -296,29 +296,29 @@ def get_database_version # :nodoc: def initialize_type_map(m = type_map) 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 + 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) SQLServer::Type::Decimal.new precision: precision, scale: scale 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 "date", SQLServer::Type::Date.new m.register_type %r{\Adatetime} do |sql_type| precision = extract_precision(sql_type) if precision @@ -331,7 +331,7 @@ def initialize_type_map(m = type_map) precision = extract_precision(sql_type) SQLServer::Type::DateTimeOffset.new precision: precision end - m.register_type 'smalldatetime', SQLServer::Type::SmallDateTime.new + 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 @@ -339,22 +339,22 @@ def initialize_type_map(m = type_map) # 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 + 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 + 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 + 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 def translate_exception(e, message:, sql:, binds:) @@ -398,7 +398,7 @@ def connect when :dblib dblib_connect(config) end - @spid = _raw_select('SELECT @@SPID', fetch: :rows).first.first + @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first @version_year = version_year configure_connection end @@ -417,7 +417,7 @@ def dblib_connect(config) username: config[:username], password: config[:password], database: config[:database], - tds_version: config[:tds_version] || '7.3', + tds_version: config[:tds_version] || "7.3", appname: config_appname(config), login_timeout: config_login_timeout(config), timeout: config_timeout(config), @@ -426,23 +426,23 @@ def dblib_connect(config) contained: config[:contained] ).tap do |client| if config[:azure] - client.execute('SET ANSI_NULLS ON').do - client.execute('SET ANSI_NULL_DFLT_ON ON').do - client.execute('SET ANSI_PADDING ON').do - client.execute('SET ANSI_WARNINGS ON').do + client.execute("SET ANSI_NULLS ON").do + client.execute("SET ANSI_NULL_DFLT_ON ON").do + client.execute("SET ANSI_PADDING ON").do + client.execute("SET ANSI_WARNINGS ON").do else - client.execute('SET ANSI_DEFAULTS ON').do + client.execute("SET ANSI_DEFAULTS ON").do end - client.execute('SET QUOTED_IDENTIFIER ON').do - client.execute('SET CURSOR_CLOSE_ON_COMMIT OFF').do - client.execute('SET IMPLICIT_TRANSACTIONS OFF').do - client.execute('SET TEXTSIZE 2147483647').do - client.execute('SET CONCAT_NULL_YIELDS_NULL ON').do + client.execute("SET QUOTED_IDENTIFIER ON").do + client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do + client.execute("SET IMPLICIT_TRANSACTIONS OFF").do + client.execute("SET TEXTSIZE 2147483647").do + client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do end end def config_appname(config) - config[:appname] || configure_application_name || Rails.application.class.name.split('::').first rescue nil + config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil end def config_login_timeout(config) @@ -464,11 +464,11 @@ def configure_application_name ; end def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a - [a, b, c].each { |f| f.upcase! if f == 'y' } + [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_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}" @@ -483,7 +483,7 @@ def version_year end def sqlserver_version - @sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s + @sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s end end end diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 7dcf2765d..8f2150ced 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -8,7 +8,7 @@ def sqlserver_connection(config) #:nodoc: mode = config[:mode].to_s.downcase.underscore.to_sym case mode when :dblib - require 'tiny_tds' + require "tiny_tds" else raise ArgumentError, "Unknown connection mode in #{config.inspect}." end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 0e7231dda..b95297b28 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true -require 'active_record/tasks/database_tasks' -require 'shellwords' -require 'ipaddr' -require 'socket' +require "active_record/tasks/database_tasks" +require "shellwords" +require "ipaddr" +require "socket" module ActiveRecord module Tasks class SQLServerDatabaseTasks - DEFAULT_COLLATION = 'SQL_Latin1_General_CP1_CI_AS' + DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" delegate :connection, :establish_connection, :clear_active_connections!, to: ActiveRecord::Base @@ -21,7 +21,7 @@ def initialize(configuration) def create(master_established = false) establish_master_connection unless master_established - connection.create_database configuration['database'], configuration.merge('collation' => default_collation) + connection.create_database configuration["database"], configuration.merge("collation" => default_collation) establish_connection configuration rescue ActiveRecord::StatementInvalid => error if /database .* already exists/i === error.message @@ -33,7 +33,7 @@ def create(master_established = false) def drop establish_master_connection - connection.drop_database configuration['database'] + connection.drop_database configuration["database"] end def charset @@ -52,7 +52,7 @@ def purge def structure_dump(filename, extra_flags) server_arg = "-S #{Shellwords.escape(configuration['host'])}" - server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration['port'] + server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration["port"] command = [ "defncopy-ttds", server_arg, @@ -65,13 +65,13 @@ def structure_dump(filename, extra_flags) command.concat(table_args) view_args = connection.views.map { |v| Shellwords.escape(v) } command.concat(view_args) - raise 'Error dumping database' unless Kernel.system(command.join(' ')) + raise "Error dumping database" unless Kernel.system(command.join(" ")) 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!(/text\(\d+\)/, 'text') # Fix text(16) column defs + 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!(/text\(\d+\)/, "text") # Fix text(16) column defs File.open(filename, "w") { |file| file.puts dump } end @@ -87,11 +87,11 @@ def configuration end def default_collation - configuration['collation'] || DEFAULT_COLLATION + configuration["collation"] || DEFAULT_COLLATION end def establish_master_connection - establish_connection configuration.merge('database' => 'master') + establish_connection configuration.merge("database" => "master") end end @@ -103,9 +103,9 @@ module DatabaseTasksSQLServer module ClassMethods LOCAL_IPADDR = [ - IPAddr.new('192.168.0.0/16'), - IPAddr.new('10.0.0.0/8'), - IPAddr.new('172.16.0.0/12') + IPAddr.new("192.168.0.0/16"), + IPAddr.new("10.0.0.0/8"), + IPAddr.new("172.16.0.0/12") ] private @@ -115,8 +115,8 @@ def local_database?(configuration) end def configuration_host_ip(configuration) - return nil unless configuration['host'] - Socket::getaddrinfo(configuration['host'], 'echo', Socket::AF_INET)[0][3] + return nil unless configuration["host"] + Socket::getaddrinfo(configuration["host"], "echo", Socket::AF_INET)[0][3] end def local_ipaddr?(host_ip) diff --git a/lib/activerecord-sqlserver-adapter.rb b/lib/activerecord-sqlserver-adapter.rb index bfcdd0347..ab2363641 100644 --- a/lib/activerecord-sqlserver-adapter.rb +++ b/lib/activerecord-sqlserver-adapter.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require 'active_record/connection_adapters/sqlserver_adapter' +require "active_record/connection_adapters/sqlserver_adapter" diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 82b77d99e..04daae5da 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -38,7 +38,7 @@ def visit_Arel_Nodes_UpdateStatement(o, a) 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 o.expr.to_s =~ /FOR UPDATE/ collector << " " visit o.expr, collector end @@ -109,7 +109,7 @@ def visit_Arel_Nodes_JoinSource o, collector end if o.right.any? collector << " " if o.left - collector = inject_join o.right, collector, ' ' + collector = inject_join o.right, collector, " " end collector end diff --git a/lib/arel_sqlserver.rb b/lib/arel_sqlserver.rb index f271dbb02..e6c7760ac 100644 --- a/lib/arel_sqlserver.rb +++ b/lib/arel_sqlserver.rb @@ -1,4 +1,4 @@ # frozen_string_literal: true -require 'arel' -require 'arel/visitors/sqlserver' +require "arel" +require "arel/visitors/sqlserver" diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 366c42302..7e2161513 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/topic' -require 'models/task' -require 'models/post' -require 'models/subscriber' -require 'models/minimalistic' +require "cases/helper_sqlserver" +require "models/topic" +require "models/task" +require "models/post" +require "models/subscriber" +require "models/minimalistic" class AdapterTestSQLServer < ActiveRecord::TestCase @@ -15,7 +15,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } - it 'has basic and non-sensitive information in the adapters inspect method' do + it "has basic and non-sensitive information in the adapters inspect method" do string = connection.inspect _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} _(string).must_match %r{version\: \d.\d} @@ -27,66 +27,66 @@ class AdapterTestSQLServer < ActiveRecord::TestCase _(string).wont_match %r{port} end - it 'has a 128 max #table_alias_length' do + it "has a 128 max #table_alias_length" do assert connection.table_alias_length <= 128 end - it 'raises invalid statement error for bad SQL' do + it "raises invalid statement error for bad SQL" do assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") } end - it 'is has our adapter_name' do - assert_equal 'SQLServer', connection.adapter_name + it "is has our adapter_name" do + assert_equal "SQLServer", connection.adapter_name end - it 'support DDL in transactions' do + it "support DDL in transactions" do assert connection.supports_ddl_transactions? end - it 'allow owner table name prefixs like dbo to still allow table exists to return true' do + it "allow owner table name prefixs like dbo to still allow table exists to return true" do begin - assert_equal 'topics', Topic.table_name + assert_equal "topics", Topic.table_name assert Topic.table_exists? - Topic.table_name = 'dbo.topics' - assert Topic.table_exists?, 'Tasks table name of dbo.topics should return true for exists.' + Topic.table_name = "dbo.topics" + assert Topic.table_exists?, "Tasks table name of dbo.topics should return true for exists." ensure - Topic.table_name = 'topics' + Topic.table_name = "topics" end end - it 'return true to insert sql query for inserts only' do - assert connection.send(:insert_sql?,'INSERT...') + it "return true to insert sql query for inserts only" do + assert connection.send(:insert_sql?,"INSERT...") assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") - assert !connection.send(:insert_sql?,'UPDATE...') - assert !connection.send(:insert_sql?,'SELECT...') + assert !connection.send(:insert_sql?,"UPDATE...") + assert !connection.send(:insert_sql?,"SELECT...") end - it 'return unquoted table name object from basic INSERT UPDATE and SELECT statements' do - assert_equal 'funny_jokes', connection.send(:get_table_name, basic_insert_sql) - assert_equal 'customers', connection.send(:get_table_name, basic_update_sql) - assert_equal 'customers', connection.send(:get_table_name, basic_select_sql) + it "return unquoted table name object from basic INSERT UPDATE and SELECT statements" do + assert_equal "funny_jokes", connection.send(:get_table_name, basic_insert_sql) + assert_equal "customers", connection.send(:get_table_name, basic_update_sql) + assert_equal "customers", connection.send(:get_table_name, basic_select_sql) end - it 'test bad connection' do + it "test bad connection" do assert_raise ActiveRecord::NoDatabaseError do - config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest") ActiveRecord::Base.sqlserver_connection config end end - it 'test database exists returns false if database does not exist' do - config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + it "test database exists returns false if database does not exist" do + config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest") assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), - 'expected database to not exist' + "expected database to not exist" end - it 'test database exists returns true when the database exists' do - config = ActiveRecord::Base.configurations['arunit'] + it "test database exists returns true when the database exists" do + config = ActiveRecord::Base.configurations["arunit"] assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), "expected database #{config[:database]} to exist" end - describe 'with different language' do + describe "with different language" do before do @default_language = connection.user_options_language @@ -97,18 +97,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase connection.send :initialize_dateformatter end - it 'memos users dateformat' do + it "memos users dateformat" do connection.execute("SET LANGUAGE us_english") rescue nil dateformat = connection.instance_variable_get(:@database_dateformat) - assert_equal 'mdy', dateformat + assert_equal "mdy", dateformat end - it 'has a dateformatter' do + it "has a dateformatter" do assert Date::DATE_FORMATS[:_sqlserver_dateformat] assert Time::DATE_FORMATS[:_sqlserver_dateformat] end - it 'does a datetime insertion when language is german' do + it "does a datetime insertion when language is german" do connection.execute("SET LANGUAGE deutsch") connection.send :initialize_dateformatter assert_nothing_raised do @@ -120,36 +120,36 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'testing #lowercase_schema_reflection' do + describe "testing #lowercase_schema_reflection" do before do SSTestUpper.delete_all - SSTestUpper.create COLUMN1: 'Got a minute?', COLUMN2: 419 - SSTestUpper.create COLUMN1: 'Favorite number?', COLUMN2: 69 + SSTestUpper.create COLUMN1: "Got a minute?", COLUMN2: 419 + SSTestUpper.create COLUMN1: "Favorite number?", COLUMN2: 69 end after do connection.lowercase_schema_reflection = false end - it 'not lowercase schema reflection by default' do - assert SSTestUpper.columns_hash['COLUMN1'] - assert_equal 'Got a minute?', SSTestUpper.first.COLUMN1 - assert_equal 'Favorite number?', SSTestUpper.last.COLUMN1 - assert SSTestUpper.columns_hash['COLUMN2'] + it "not lowercase schema reflection by default" do + assert SSTestUpper.columns_hash["COLUMN1"] + assert_equal "Got a minute?", SSTestUpper.first.COLUMN1 + assert_equal "Favorite number?", SSTestUpper.last.COLUMN1 + assert SSTestUpper.columns_hash["COLUMN2"] end - it 'lowercase schema reflection when set' do + it "lowercase schema reflection when set" do connection.lowercase_schema_reflection = true - assert SSTestUppered.columns_hash['column1'] - assert_equal 'Got a minute?', SSTestUppered.first.column1 - assert_equal 'Favorite number?', SSTestUppered.last.column1 - assert SSTestUppered.columns_hash['column2'] + assert SSTestUppered.columns_hash["column1"] + assert_equal "Got a minute?", SSTestUppered.first.column1 + assert_equal "Favorite number?", SSTestUppered.last.column1 + assert SSTestUppered.columns_hash["column2"] end end - describe 'identity inserts' do + describe "identity inserts" do before do @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')" @@ -160,62 +160,62 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" 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) + 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) end - it 'return false to #query_requires_identity_insert? for normal SQL' do + it "return false to #query_requires_identity_insert? for normal SQL" do [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| assert !connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" end end - it 'find identity column using #identity_columns' do - task_id_column = Task.columns_hash['id'] + it "find identity column using #identity_columns" do + task_id_column = Task.columns_hash["id"] assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type end - it 'return an empty array when calling #identity_columns for a table_name with no identity' do + it "return an empty array when calling #identity_columns for a table_name with no identity" do _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end end - describe 'quoting' do + describe "quoting" do - it 'return 1 for #quoted_true' do - assert_equal '1', connection.quoted_true + it "return 1 for #quoted_true" do + assert_equal "1", connection.quoted_true end - it 'return 0 for #quoted_false' do - assert_equal '0', connection.quoted_false + it "return 0 for #quoted_false" do + assert_equal "0", connection.quoted_false end - it 'not escape backslash characters like abstract adapter' do + it "not escape backslash characters like abstract adapter" do string_with_backslashs = "\\n" assert_equal string_with_backslashs, connection.quote_string(string_with_backslashs) end - it 'quote column names with brackets' do - assert_equal '[foo]', connection.quote_column_name(:foo) - assert_equal '[foo]', connection.quote_column_name('foo') - assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar') + it "quote column names with brackets" do + assert_equal "[foo]", connection.quote_column_name(:foo) + assert_equal "[foo]", connection.quote_column_name("foo") + assert_equal "[foo].[bar]", connection.quote_column_name("foo.bar") end - it 'not quote already quoted column names with brackets' do - assert_equal '[foo]', connection.quote_column_name('[foo]') - assert_equal '[foo].[bar]', connection.quote_column_name('[foo].[bar]') + it "not quote already quoted column names with brackets" do + assert_equal "[foo]", connection.quote_column_name("[foo]") + assert_equal "[foo].[bar]", connection.quote_column_name("[foo].[bar]") end - it 'quote table names like columns' do - assert_equal '[foo].[bar]', connection.quote_column_name('foo.bar') - assert_equal '[foo].[bar].[baz]', connection.quote_column_name('foo.bar.baz') + it "quote table names like columns" do + assert_equal "[foo].[bar]", connection.quote_column_name("foo.bar") + assert_equal "[foo].[bar].[baz]", connection.quote_column_name("foo.bar.baz") end it "surround string with national prefix" do @@ -228,7 +228,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end - describe 'disabling referential integrity' do + describe "disabling referential integrity" do before do connection.disable_referential_integrity { SSTestHasPk.delete_all; SSTestHasFk.delete_all } @@ -236,33 +236,33 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @member = SSTestHasFk.create!(fk_id: @parent.id) end - it 'NOT ALLOW by default the deletion of a referenced parent' do + it "NOT ALLOW by default the deletion of a referenced parent" do SSTestHasPk.connection.disable_referential_integrity { } assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end - it 'ALLOW deletion of referenced parent using #disable_referential_integrity block' do + it "ALLOW deletion of referenced parent using #disable_referential_integrity block" do SSTestHasPk.connection.disable_referential_integrity { @parent.destroy } end - it 'again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block' do + it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do assert_raise(ActiveRecord::StatementInvalid) do SSTestHasPk.connection.disable_referential_integrity { } @parent.destroy end end - it 'not disable referential integrity for the same table twice' do + it "not disable referential integrity for the same table twice" do tables = SSTestHasPk.connection.tables_with_referential_integrity assert_equal tables.size, tables.uniq.size end end - describe 'database statements' do + describe "database statements" do it "run the database consistency checker useroptions command" do - skip 'on azure' if connection_sqlserver_azure? + skip "on azure" if connection_sqlserver_azure? keys = [:textsize, :language, :isolation_level, :dateformat] user_options = connection.user_options keys.each do |key| @@ -272,89 +272,89 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return a underscored key hash with indifferent access of the results" do - skip 'on azure' if connection_sqlserver_azure? + skip "on azure" if connection_sqlserver_azure? user_options = connection.user_options - assert_equal 'read committed', user_options['isolation_level'] - assert_equal 'read committed', user_options[:isolation_level] + assert_equal "read committed", user_options["isolation_level"] + assert_equal "read committed", user_options[:isolation_level] end end - describe 'schema statements' do + describe "schema statements" do - it 'create integers when no limit supplied' do - assert_equal 'integer', connection.type_to_sql(:integer) + it "create integers when no limit supplied" do + assert_equal "integer", connection.type_to_sql(:integer) end - it 'create integers when limit is 4' do - assert_equal 'integer', connection.type_to_sql(:integer, limit: 4) + it "create integers when limit is 4" do + assert_equal "integer", connection.type_to_sql(:integer, limit: 4) end - it 'create integers when limit is 3' do - assert_equal 'integer', connection.type_to_sql(:integer, limit: 3) + it "create integers when limit is 3" do + assert_equal "integer", connection.type_to_sql(:integer, limit: 3) end - it 'create smallints when limit is 2' do - assert_equal 'smallint', connection.type_to_sql(:integer, limit: 2) + it "create smallints when limit is 2" do + assert_equal "smallint", connection.type_to_sql(:integer, limit: 2) end - it 'create tinyints when limit is 1' do - assert_equal 'tinyint', connection.type_to_sql(:integer, limit: 1) + it "create tinyints when limit is 1" do + assert_equal "tinyint", connection.type_to_sql(:integer, limit: 1) end - it 'create bigints when limit is greateer than 4' do - assert_equal 'bigint', connection.type_to_sql(:integer, limit: 5) - assert_equal 'bigint', connection.type_to_sql(:integer, limit: 6) - assert_equal 'bigint', connection.type_to_sql(:integer, limit: 7) - assert_equal 'bigint', connection.type_to_sql(:integer, limit: 8) + it "create bigints when limit is greateer than 4" do + assert_equal "bigint", connection.type_to_sql(:integer, limit: 5) + assert_equal "bigint", connection.type_to_sql(:integer, limit: 6) + assert_equal "bigint", connection.type_to_sql(:integer, limit: 7) + assert_equal "bigint", connection.type_to_sql(:integer, limit: 8) end - it 'create floats when no limit supplied' do - assert_equal 'float', connection.type_to_sql(:float) + it "create floats when no limit supplied" do + assert_equal "float", connection.type_to_sql(:float) end end - describe 'views' do + describe "views" do # Using connection.views - it 'return an array' do + it "return an array" do assert_instance_of Array, connection.views end - it 'find SSTestCustomersView table name' do - _(connection.views).must_include 'sst_customers_view' + it "find SSTestCustomersView table name" do + _(connection.views).must_include "sst_customers_view" end - it 'work with dynamic finders' do - name = 'MetaSkills' + it "work with dynamic finders" do + name = "MetaSkills" customer = SSTestCustomersView.create! name: name assert_equal customer, SSTestCustomersView.find_by_name(name) end - it 'not contain system views' do - systables = ['sysconstraints','syssegments'] + it "not contain system views" do + systables = ["sysconstraints","syssegments"] systables.each do |systable| assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array." end end - it 'allow the connection#view_information method to return meta data on the view' do - view_info = connection.send(:view_information,'sst_customers_view') - assert_equal('sst_customers_view', view_info['TABLE_NAME']) - assert_match(/CREATE VIEW sst_customers_view/, view_info['VIEW_DEFINITION']) + it "allow the connection#view_information method to return meta data on the view" do + view_info = connection.send(:view_information,"sst_customers_view") + assert_equal("sst_customers_view", view_info["TABLE_NAME"]) + assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) end - it 'allow the connection#view_table_name method to return true table_name for the view' do - assert_equal 'customers', connection.send(:view_table_name,'sst_customers_view') - assert_equal 'topics', connection.send(:view_table_name,'topics'), 'No view here, the same table name should come back.' + it "allow the connection#view_table_name method to return true table_name for the view" do + assert_equal "customers", connection.send(:view_table_name,"sst_customers_view") + assert_equal "topics", connection.send(:view_table_name,"topics"), "No view here, the same table name should come back." end # With same column names - it 'have matching column objects' do - columns = ['id','name','balance'] + it "have matching column objects" do + columns = ["id","name","balance"] assert !SSTestCustomersView.columns.blank? assert_equal columns.size, SSTestCustomersView.columns.size columns.each do |colname| @@ -364,24 +364,24 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - it 'find identity column' do - _(SSTestCustomersView.primary_key).must_equal 'id' - _(connection.primary_key(SSTestCustomersView.table_name)).must_equal 'id' - _(SSTestCustomersView.columns_hash['id']).must_be :is_identity? + it "find identity column" do + _(SSTestCustomersView.primary_key).must_equal "id" + _(connection.primary_key(SSTestCustomersView.table_name)).must_equal "id" + _(SSTestCustomersView.columns_hash["id"]).must_be :is_identity? end - it 'find default values' do + it "find default values" do assert_equal 0, SSTestCustomersView.new.balance end - it 'respond true to data_source_exists?' do + it "respond true to data_source_exists?" do assert SSTestCustomersView.connection.data_source_exists?(SSTestCustomersView.table_name) end # With aliased column names - it 'have matching column objects' do - columns = ['id','pretend_null'] + it "have matching column objects" do + columns = ["id","pretend_null"] assert !SSTestStringDefaultsView.columns.blank? assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| @@ -391,66 +391,66 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end end - it 'find identity column' do - _(SSTestStringDefaultsView.primary_key).must_equal 'id' - _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal 'id' - _(SSTestStringDefaultsView.columns_hash['id']).must_be :is_identity? + it "find identity column" do + _(SSTestStringDefaultsView.primary_key).must_equal "id" + _(connection.primary_key(SSTestStringDefaultsView.table_name)).must_equal "id" + _(SSTestStringDefaultsView.columns_hash["id"]).must_be :is_identity? end - it 'find default values' do - assert_equal 'null', SSTestStringDefaultsView.new.pretend_null, - SSTestStringDefaultsView.columns_hash['pretend_null'].inspect + it "find default values" do + assert_equal "null", SSTestStringDefaultsView.new.pretend_null, + SSTestStringDefaultsView.columns_hash["pretend_null"].inspect end - it 'respond true to data_source_exists?' do + it "respond true to data_source_exists?" do assert SSTestStringDefaultsView.connection.data_source_exists?(SSTestStringDefaultsView.table_name) end # That have more than 4000 chars for their defintion - it 'cope with null returned for the defintion' do + it "cope with null returned for the defintion" do assert_nothing_raised() { SSTestStringDefaultsBigView.columns } end - it 'using alternate view defintion still be able to find real default' do - assert_equal 'null', SSTestStringDefaultsBigView.new.pretend_null, - SSTestStringDefaultsBigView.columns_hash['pretend_null'].inspect + it "using alternate view defintion still be able to find real default" do + assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null, + SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect end end - describe 'database_prefix_remote_server?' do + describe "database_prefix_remote_server?" do after do connection_options.delete(:database_prefix) end - it 'returns false if database_prefix is not configured' do + it "returns false if database_prefix is not configured" do assert_equal false, connection.database_prefix_remote_server? end - it 'returns true if database_prefix has been set' do + it "returns true if database_prefix has been set" do connection_options[:database_prefix] = "server.database.schema." assert_equal true, connection.database_prefix_remote_server? end - it 'returns false if database_prefix has been set incorrectly' do + it "returns false if database_prefix has been set incorrectly" do connection_options[:database_prefix] = "server.database.schema" assert_equal false, connection.database_prefix_remote_server? end end - it 'in_memory_oltp' do - if ENV['IN_MEMORY_OLTP'] && connection.supports_in_memory_oltp? - _(SSTMemory.primary_key).must_equal 'id' - _(SSTMemory.columns_hash['id']).must_be :is_identity? + it "in_memory_oltp" do + if ENV["IN_MEMORY_OLTP"] && connection.supports_in_memory_oltp? + _(SSTMemory.primary_key).must_equal "id" + _(SSTMemory.columns_hash["id"]).must_be :is_identity? else - skip 'supports_in_memory_oltp? => false' + skip "supports_in_memory_oltp? => false" end end - describe 'block writes to a database' do + describe "block writes to a database" do def setup @conn = ActiveRecord::Base.connection @connection_handler = ActiveRecord::Base.connection_handler diff --git a/test/cases/change_column_null_test_sqlserver.rb b/test/cases/change_column_null_test_sqlserver.rb index a4267f263..1cd47de87 100644 --- a/test/cases/change_column_null_test_sqlserver.rb +++ b/test/cases/change_column_null_test_sqlserver.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'migrations/create_clients_and_change_column_null' +require "cases/helper_sqlserver" +require "migrations/create_clients_and_change_column_null" class ChangeColumnNullTestSqlServer < ActiveRecord::TestCase before do @@ -19,25 +19,25 @@ def find_column(table, name) table.find { |column| column.name == name } end - let(:clients_table) { connection.columns('clients') } - let(:name_column) { find_column(clients_table, 'name') } - let(:code_column) { find_column(clients_table, 'code') } - let(:value_column) { find_column(clients_table, 'value') } + let(:clients_table) { connection.columns("clients") } + let(:name_column) { find_column(clients_table, "name") } + let(:code_column) { find_column(clients_table, "code") } + let(:value_column) { find_column(clients_table, "value") } - describe '#change_column_null' do - it 'does not change the column limit' do + describe "#change_column_null" do + it "does not change the column limit" do _(name_column.limit).must_equal 15 end - it 'does not change the column default' do - _(code_column.default).must_equal 'n/a' + it "does not change the column default" do + _(code_column.default).must_equal "n/a" end - it 'does not change the column precision' do + it "does not change the column precision" do _(value_column.precision).must_equal 32 end - it 'does not change the column scale' do + it "does not change the column scale" do _(value_column.scale).must_equal 8 end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0e38cd068..0c52cd88b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" -require 'models/event' +require "models/event" class UniquenessValidationTest < ActiveRecord::TestCase # So sp_executesql swallows this exception. Run without prepared to see it. coerce_tests! :test_validate_uniqueness_with_limit @@ -30,7 +30,7 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced coerce_tests! :test_validate_case_sensitive_uniqueness_by_default def test_validate_case_sensitive_uniqueness_by_default_coerced database_collation = connection.select_one("SELECT collation_name FROM sys.databases WHERE name = 'activerecord_unittest'").values.first - skip if database_collation.include?('_CI_') + skip if database_collation.include?("_CI_") original_test_validate_case_sensitive_uniqueness_by_default_coerced end @@ -39,7 +39,7 @@ def test_validate_case_sensitive_uniqueness_by_default_coerced -require 'models/event' +require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase # I really don`t think we can support legacy binds. @@ -54,7 +54,7 @@ class AdapterTest < ActiveRecord::TestCase def test_value_limit_violations_are_translated_to_specific_exception_coerced connection.unprepared_statement do error = assert_raises(ActiveRecord::ValueTooLong) do - Event.create(title: 'abcdefgh') + Event.create(title: "abcdefgh") end assert_not_nil error.cause end @@ -128,12 +128,12 @@ def test_truncate_tables_with_query_cache -require 'models/topic' +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.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 @@ -141,7 +141,7 @@ def test_typecast_attribute_from_select_to_false_coerced # 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.create(:title => "Budget") topic = Topic.all.merge!(:select => "topics.*, IIF (1 = 1, 1, 0) as is_test").first assert_predicate topic, :is_test? end @@ -155,7 +155,7 @@ class BasicsTest < ActiveRecord::TestCase coerce_tests! :test_column_names_are_escaped def test_column_names_are_escaped_coerced conn = ActiveRecord::Base.connection - assert_equal '[t]]]', conn.quote_column_name('t]') + assert_equal "[t]]]", conn.quote_column_name("t]") end # Just like PostgreSQLAdapter does. @@ -175,7 +175,7 @@ def test_update_date_time_attributes end def test_update_date_time_attributes_with_default_timezone_local - with_env_tz 'America/New_York' do + with_env_tz "America/New_York" do with_timezone_config default: :local do Time.use_zone("Eastern Time (US & Canada)") do topic = Topic.find(1) @@ -286,7 +286,7 @@ def test_offset_is_kept_coerced coerce_tests! :test_should_return_decimal_average_of_integer_field def test_should_return_decimal_average_of_integer_field_coerced value = Account.average(:id) - assert_equal BigDecimal('3.0').to_s, BigDecimal(value).to_s + assert_equal BigDecimal("3.0").to_s, BigDecimal(value).to_s end # Match SQL Server limit implementation @@ -400,7 +400,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" @@ -416,9 +416,9 @@ 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 - assert_equal 1, connection.indexes('test_models').size + assert_equal 1, connection.indexes("test_models").size remove_column("test_models", "hat_size") - assert_equal [], connection.indexes('test_models').map(&:name) + assert_equal [], connection.indexes("test_models").map(&:name) end # Choose `StatementInvalid` vs `ActiveRecordError`. @@ -459,7 +459,7 @@ def test_add_table_with_decimals_coerced assert_not_nil b.my_house_population assert_not_nil b.value_of_e assert_kind_of BigDecimal, b.world_population - assert_equal '6000000000.0', b.world_population.to_s + assert_equal "6000000000.0", b.world_population.to_s assert_kind_of Integer, b.my_house_population assert_equal 3, b.my_house_population assert_kind_of BigDecimal, b.bank_balance @@ -631,7 +631,7 @@ def test_sqlserver_structure_load class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase # Skip this test with /tmp/my_schema_cache.yml path on Windows. - coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end class DatabaseTasksCreateAllTest < ActiveRecord::TestCase @@ -664,8 +664,8 @@ def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced -require 'models/post' -require 'models/subscriber' +require "models/post" +require "models/subscriber" class EachTest < ActiveRecord::TestCase # Quoting in tests does not cope with bracket quoting. coerce_tests! :test_find_in_batches_should_quote_batch_order @@ -709,7 +709,7 @@ def test_count_with_include_coerced -require 'models/topic' +require "models/topic" class FinderTest < ActiveRecord::TestCase # We have implicit ordering, via FETCH. coerce_tests! %r{doesn't have implicit ordering}, @@ -737,7 +737,7 @@ def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced # Can not use array condition due to not finding right type and hence fractional second quoting. coerce_tests! :test_condition_utc_time_interpolation_with_default_timezone_local def test_condition_utc_time_interpolation_with_default_timezone_local_coerced - with_env_tz 'America/New_York' do + with_env_tz "America/New_York" do with_timezone_config default: :local do topic = Topic.first assert_equal topic, Topic.where(written_on: topic.written_on.getutc).first @@ -748,7 +748,7 @@ def test_condition_utc_time_interpolation_with_default_timezone_local_coerced # Can not use array condition due to not finding right type and hence fractional second quoting. coerce_tests! :test_condition_local_time_interpolation_with_default_timezone_utc def test_condition_local_time_interpolation_with_default_timezone_utc_coerced - with_env_tz 'America/New_York' do + with_env_tz "America/New_York" do with_timezone_config default: :utc do topic = Topic.first assert_equal topic, Topic.where(written_on: topic.written_on.getlocal).first @@ -814,12 +814,12 @@ def test_has_one_through_executes_limited_query_coerced -require 'models/company' +require "models/company" class InheritanceTest < ActiveRecord::TestCase # Rails test required inserting to a identity column. coerce_tests! :test_a_bad_type_column def test_a_bad_type_column_coerced - Company.connection.with_identity_insert_enabled('companies') do + Company.connection.with_identity_insert_enabled("companies") do Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')" end assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } @@ -846,18 +846,18 @@ class LeftOuterJoinAssociationTest < ActiveRecord::TestCase -require 'models/developer' -require 'models/computer' +require "models/developer" +require "models/computer" class NestedRelationScopingTest < ActiveRecord::TestCase # Assert SQL Server limit implementation coerce_tests! :test_merge_options def test_merge_options_coerced - Developer.where('salary = 80000').scoping do + Developer.where("salary = 80000").scoping do Developer.limit(10).scoping do devs = Developer.all sql = devs.to_sql - assert_match '(salary = 80000)', sql - assert_match 'FETCH NEXT 10 ROWS ONLY', sql + assert_match "(salary = 80000)", sql + assert_match "FETCH NEXT 10 ROWS ONLY", sql end end end @@ -866,7 +866,7 @@ def test_merge_options_coerced -require 'models/topic' +require "models/topic" class PersistenceTest < ActiveRecord::TestCase # Rails test required updating a identity column. coerce_tests! :test_update_columns_changing_id @@ -893,7 +893,7 @@ def test_update_coerced -require 'models/author' +require "models/author" class UpdateAllTest < ActiveRecord::TestCase # Rails test required updating a identity column. coerce_tests! :test_update_all_doesnt_ignore_order @@ -903,17 +903,17 @@ def test_update_all_doesnt_ignore_order_coerced _(mary.id).must_equal 2 _(david.name).wont_equal mary.name assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do - Author.where('[id] > 1').order(:id).update_all(name: 'Test') + Author.where("[id] > 1").order(:id).update_all(name: "Test") end - _(david.reload.name).must_equal 'David' - _(mary.reload.name).must_equal 'Test' + _(david.reload.name).must_equal "David" + _(mary.reload.name).must_equal "Test" end end -require 'models/topic' +require "models/topic" module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase # Same as original test except string has `N` prefix to indicate unicode string. @@ -948,7 +948,7 @@ def test_create_without_primary_key_no_extra_query_coerced -require 'models/task' +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 @@ -988,7 +988,7 @@ def test_query_cached_even_when_types_are_reset_coerced -require 'models/post' +require "models/post" class RelationTest < ActiveRecord::TestCase # Use LEN vs LENGTH function. coerce_tests! :test_reverse_order_with_function @@ -1055,7 +1055,7 @@ def test_relations_dont_load_all_records_in_inspect_coerced # Can't apply offset without ORDER coerce_tests! %r{using a custom table affects the wheres} - test 'using a custom table affects the wheres coerced' do + test "using a custom table affects the wheres coerced" do post = posts(:welcome) assert_equal post, custom_post_relation.where!(title: post.title).order(:id).take @@ -1063,7 +1063,7 @@ def test_relations_dont_load_all_records_in_inspect_coerced # Can't apply offset without ORDER coerce_tests! %r{using a custom table with joins affects the joins} - test 'using a custom table with joins affects the joins coerced' do + test "using a custom table with joins affects the joins coerced" do post = posts(:welcome) assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take @@ -1080,7 +1080,7 @@ def test_reverse_arel_assoc_order_with_function_coerced -require 'models/post' +require "models/post" class SanitizeTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert coerce_tests! :test_sanitize_sql_like_example_use_case @@ -1153,7 +1153,7 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase -require 'models/topic' +require "models/topic" class TransactionTest < ActiveRecord::TestCase # SQL Server does not have query for release_savepoint coerce_tests! :test_releasing_named_savepoints @@ -1170,7 +1170,7 @@ def test_releasing_named_savepoints_coerced -require 'models/tag' +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`. @@ -1180,7 +1180,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase assert_equal 0, Tag.count Tag2.transaction do Tag2.create - assert_equal 0, Tag.lock('WITH(READPAST)').count + assert_equal 0, Tag.lock("WITH(READPAST)").count end end assert_equal 1, Tag.count @@ -1193,7 +1193,7 @@ class TransactionIsolationTest < ActiveRecord::TestCase -require 'models/book' +require "models/book" class ViewWithPrimaryKeyTest < ActiveRecord::TestCase # We have a few view tables. use includes vs equality. coerce_tests! :test_views @@ -1205,7 +1205,7 @@ def test_views_coerced coerce_tests! :test_does_not_assume_id_column_as_primary_key def test_does_not_assume_id_column_as_primary_key_coerced model = Class.new(ActiveRecord::Base) { self.table_name = "ebooks" } - assert_equal 'id', model.primary_key + assert_equal "id", model.primary_key end end @@ -1223,11 +1223,11 @@ def test_views_coerced -require 'models/author' +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 - author = Author.select('authors.*, 5 as posts_count').first + author = Author.select("authors.*, 5 as posts_count").first dumped = YAML.load(YAML.dump(author)) assert_equal 5, author.posts_count assert_equal 5, dumped.posts_count @@ -1379,7 +1379,7 @@ class SchemaCacheTest < ActiveRecord::TestCase private # We need to give the full path for this to work. def schema_dump_path - File.join ARTest::SQLServer.root_activerecord, 'test/assets/schema_dump_5_1.yml' + File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml" end end end @@ -1391,7 +1391,7 @@ def schema_dump_path class UnsafeRawSqlTest < ActiveRecord::TestCase # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} - test 'order: always allows Arel' do + test "order: always allows Arel" do ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) } @@ -1441,18 +1441,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase coerce_tests! :test_update_with_dirty_primary_key def test_update_with_dirty_primary_key_coerced assert_raises(ActiveRecord::RecordNotUnique) do - record = StringKeyObject.find('record1') - record.id = 'record2' + record = StringKeyObject.find("record1") + record.id = "record2" record.save! end - record = StringKeyObject.find('record1') - record.id = 'record42' + record = StringKeyObject.find("record1") + record.id = "record42" record.save! - assert StringKeyObject.find('record42') + assert StringKeyObject.find("record42") assert_raises(ActiveRecord::RecordNotFound) do - StringKeyObject.find('record1') + StringKeyObject.find("record1") end end end @@ -1531,7 +1531,7 @@ class EnumTest < ActiveRecord::TestCase -require 'models/task' +require "models/task" class QueryCacheExpiryTest < ActiveRecord::TestCase # SQL Server does not support skipping or upserting duplicates. @@ -1565,7 +1565,7 @@ def test_insert_all_coerced -require 'models/citation' +require "models/citation" class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012) @@ -1627,7 +1627,7 @@ class ReaperTest < ActiveRecord::TestCase class FixturesTest < ActiveRecord::TestCase # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged. - coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end @@ -1636,5 +1636,5 @@ class FixturesTest < ActiveRecord::TestCase class ReloadModelsTest < ActiveRecord::TestCase # Skip test on Windows. The number of arguements 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 RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 8d74c2908..6b57c7479 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class ColumnTestSQLServer < ActiveRecord::TestCase - it '#table_name' do - assert SSTestDatatype.columns.all? { |c| c.table_name == 'sst_datatypes' } - assert SSTestCustomersView.columns.all? { |c| c.table_name == 'customers' } + it "#table_name" do + assert SSTestDatatype.columns.all? { |c| c.table_name == "sst_datatypes" } + assert SSTestCustomersView.columns.all? { |c| c.table_name == "customers" } end - describe 'ActiveRecord::ConnectionAdapters::SQLServer::Type' do + describe "ActiveRecord::ConnectionAdapters::SQLServer::Type" do let(:obj) { SSTestDatatype.new } @@ -28,15 +28,15 @@ def assert_obj_set_and_save(attribute, value) # Exact Numerics - it 'int(4) PRIMARY KEY' do - col = column('id') - _(col.sql_type).must_equal 'int(4)' + it "int(4) PRIMARY KEY" do + col = column("id") + _(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)' + 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 @@ -49,9 +49,9 @@ def assert_obj_set_and_save(attribute, value) 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)' + 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 @@ -64,9 +64,9 @@ def assert_obj_set_and_save(attribute, value) 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)' + 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 @@ -79,9 +79,9 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :smallint, 32_767 end - it 'tinyint(1)' do - col = column('tinyint') - _(col.sql_type).must_equal 'tinyint(1)' + 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 @@ -94,9 +94,9 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :tinyint, 255 end - it 'bit' do - col = column('bit') - _(col.sql_type).must_equal 'bit' + 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 @@ -109,129 +109,129 @@ def assert_obj_set_and_save(attribute, value) _(obj.bit).must_equal false obj.save! _(obj.reload.bit).must_equal false - obj.bit = '1' + obj.bit = "1" _(obj.bit).must_equal true obj.save! _(obj.reload.bit).must_equal true end - it 'decimal(9,2)' do - col = column('decimal_9_2') - _(col.sql_type).must_equal 'decimal(9,2)' + 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.default).must_equal BigDecimal("12345.01") + _(obj.decimal_9_2).must_equal BigDecimal("12345.01") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(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.decimal_9_2 = "1234567.8901" + _(obj.decimal_9_2).must_equal BigDecimal("1234567.89") obj.save! - _(obj.reload.decimal_9_2).must_equal BigDecimal('1234567.89') + _(obj.reload.decimal_9_2).must_equal BigDecimal("1234567.89") end - 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') + 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.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(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.decimal_16_4 = "1234567.8901001" + _(obj.decimal_16_4).must_equal BigDecimal("1234567.8901") obj.save! - _(obj.reload.decimal_16_4).must_equal BigDecimal('1234567.8901') + _(obj.reload.decimal_16_4).must_equal BigDecimal("1234567.8901") end - it 'numeric(18,0)' do - col = column('numeric_18_0') - _(col.sql_type).must_equal 'numeric(18,0)' + 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.default).must_equal BigDecimal("191") + _(obj.numeric_18_0).must_equal BigDecimal("191") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 18 _(type.scale).must_equal 0 - obj.numeric_18_0 = '192.1' - _(obj.numeric_18_0).must_equal BigDecimal('192') + obj.numeric_18_0 = "192.1" + _(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)' + 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.default).must_equal BigDecimal("12345678901234567890.01") + _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(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.numeric_36_2 = "192.123" + _(obj.numeric_36_2).must_equal BigDecimal("192.12") obj.save! - _(obj.reload.numeric_36_2).must_equal BigDecimal('192.12') + _(obj.reload.numeric_36_2).must_equal BigDecimal("192.12") end - it 'money' do - col = column('money') - _(col.sql_type).must_equal 'money' + 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.default).must_equal BigDecimal("4.20") + _(obj.money).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Money _(type.limit).must_be_nil _(type.precision).must_equal 19 _(type.scale).must_equal 4 - obj.money = '922337203685477.58061' - _(obj.money).must_equal BigDecimal('922337203685477.5806') + obj.money = "922337203685477.58061" + _(obj.money).must_equal BigDecimal("922337203685477.5806") obj.save! - _(obj.reload.money).must_equal BigDecimal('922337203685477.5806') + _(obj.reload.money).must_equal BigDecimal("922337203685477.5806") end - it 'smallmoney' do - col = column('smallmoney') - _(col.sql_type).must_equal 'smallmoney' + 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.default).must_equal BigDecimal("4.20") + _(obj.smallmoney).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::SmallMoney _(type.limit).must_be_nil _(type.precision).must_equal 10 _(type.scale).must_equal 4 - obj.smallmoney = '214748.36461' - _(obj.smallmoney).must_equal BigDecimal('214748.3646') + obj.smallmoney = "214748.36461" + _(obj.smallmoney).must_equal BigDecimal("214748.3646") obj.save! - _(obj.reload.smallmoney).must_equal BigDecimal('214748.3646') + _(obj.reload.smallmoney).must_equal BigDecimal("214748.3646") end # Approximate Numerics # Float limits are adjusted to 24 or 53 by the database as per http://msdn.microsoft.com/en-us/library/ms173773.aspx # Floats with a limit of <= 24 are reduced to reals by sqlserver on creation. - it 'float' do - col = column('float') - _(col.sql_type).must_equal 'float' + 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 @@ -242,15 +242,15 @@ def assert_obj_set_and_save(attribute, value) _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil - obj.float = '214748.36461' + obj.float = "214748.36461" _(obj.float).must_equal 214748.36461 obj.save! _(obj.reload.float).must_equal 214748.36461 end - it 'real' do - col = column('real') - _(col.sql_type).must_equal 'real' + 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 @@ -261,7 +261,7 @@ def assert_obj_set_and_save(attribute, value) _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil - obj.real = '214748.36461' + obj.real = "214748.36461" _(obj.real).must_be_close_to 214748.36461, 0.01 obj.save! _(obj.reload.real).must_be_close_to 214748.36461, 0.01 @@ -269,12 +269,12 @@ def assert_obj_set_and_save(attribute, value) # Date and Time - it 'date' do - col = column('date') - _(col.sql_type).must_equal 'date' + 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 connection_dblib_73? ? Date.civil(0001, 1, 1) : '0001-01-01' + _(col.default).must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : "0001-01-01" _(obj.date).must_equal Date.civil(0001, 1, 1) _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) @@ -283,14 +283,14 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Can cast strings. SQL Server format. - obj.date = '04-01-0001' + obj.date = "04-01-0001" _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload _(obj.date).must_equal Date.civil(0001, 4, 1) # Can cast strings. ISO format. - obj.date = '0001-04-01' + obj.date = "0001-04-01" _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! _(obj.date).must_equal Date.civil(0001, 4, 1) @@ -305,9 +305,9 @@ def assert_obj_set_and_save(attribute, value) _(obj.reload.date).must_equal Date.civil(2010, 4, 14) end - it 'datetime' do - col = column('datetime') - _(col.sql_type).must_equal 'datetime' + it "datetime" do + col = column("datetime") + _(col.sql_type).must_equal "datetime" _(col.type).must_equal :datetime _(col.null).must_equal true time = Time.utc 1753, 01, 01, 00, 00, 00, 123000 @@ -349,10 +349,10 @@ def assert_obj_set_and_save(attribute, value) _(obj).must_equal obj.class.where(datetime: nil).first end - it 'datetime2' do - skip 'datetime2 not supported in this protocol version' unless connection_dblib_73? - col = column('datetime2_7') - _(col.sql_type).must_equal 'datetime2(7)' + it "datetime2" do + skip "datetime2 not supported in this protocol version" unless connection_dblib_73? + col = column("datetime2_7") + _(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) @@ -388,7 +388,7 @@ def assert_obj_set_and_save(attribute, value) _(obj).must_equal obj.class.where(datetime2_7: time2).first # datetime2_3 time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) - col = column('datetime2_3') + col = column("datetime2_3") _(connection.lookup_cast_type_from_column(col).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>" @@ -396,7 +396,7 @@ def assert_obj_set_and_save(attribute, value) _(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 - col = column('datetime2_1') + col = column("datetime2_1") _(connection.lookup_cast_type_from_column(col).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>" @@ -404,7 +404,7 @@ def assert_obj_set_and_save(attribute, value) _(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 - col = column('datetime2_0') + col = column("datetime2_0") _(connection.lookup_cast_type_from_column(col).precision).must_equal 0 time = Time.utc 2016, 4, 19, 16, 45, 40, 771036 obj.datetime2_0 = time @@ -414,10 +414,10 @@ def assert_obj_set_and_save(attribute, value) _(obj).must_equal obj.class.where(datetime2_0: time).first end - it 'datetimeoffset' do - skip 'datetimeoffset not supported in this protocol version' unless connection_dblib_73? - col = column('datetimeoffset_7') - _(col.sql_type).must_equal 'datetimeoffset(7)' + it "datetimeoffset" do + skip "datetimeoffset not supported in this protocol version" unless connection_dblib_73? + 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, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" @@ -436,21 +436,21 @@ def assert_obj_set_and_save(attribute, value) obj.reload _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # Maintains the timezone - time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) + time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetimeoffset_7 = time _(obj.datetimeoffset_7).must_equal time obj.save! _(obj.datetimeoffset_7).must_equal time _(obj.reload.datetimeoffset_7).must_equal time # With other precisions. - time = ActiveSupport::TimeZone['America/Los_Angeles'].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) - col = column('datetimeoffset_3') + time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) + col = column("datetimeoffset_3") _(connection.lookup_cast_type_from_column(col).precision).must_equal 3 obj.datetimeoffset_3 = time _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" obj.save! _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" - col = column('datetime2_1') + col = column("datetime2_1") _(connection.lookup_cast_type_from_column(col).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>" @@ -458,9 +458,9 @@ def assert_obj_set_and_save(attribute, value) _(obj.datetime2_1).must_equal time.change(nsec: 100000000), "Nanoseconds were <#{obj.datetime2_1.nsec}> vs <100000000>" end - it 'smalldatetime' do - col = column('smalldatetime') - _(col.sql_type).must_equal 'smalldatetime' + 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, 01, 01, 15, 45, 00, 000) @@ -480,10 +480,10 @@ def assert_obj_set_and_save(attribute, value) _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end - it 'time(7)' do - skip 'time() not supported in this protocol version' unless connection_dblib_73? - col = column('time_7') - _(col.sql_type).must_equal 'time(7)' + it "time(7)" do + skip "time() not supported in this protocol version" unless connection_dblib_73? + 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, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" @@ -512,10 +512,10 @@ def assert_obj_set_and_save(attribute, value) _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" end - it 'time(2)' do - skip 'time() not supported in this protocol version' unless connection_dblib_73? - col = column('time_2') - _(col.sql_type).must_equal 'time(2)' + it "time(2)" do + skip "time() not supported in this protocol version" unless connection_dblib_73? + col = column("time_2") + _(col.sql_type).must_equal "time(2)" _(col.type).must_equal :time _(col.null).must_equal true _(col.default).must_be_nil @@ -542,10 +542,10 @@ def assert_obj_set_and_save(attribute, value) _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end - it 'time using default precision' do - skip 'time() not supported in this protocol version' unless connection_dblib_73? - col = column('time_default') - _(col.sql_type).must_equal 'time(7)' + it "time using default precision" do + skip "time() not supported in this protocol version" unless connection_dblib_73? + 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, 01, 01, 15, 03, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" @@ -576,13 +576,13 @@ def assert_obj_set_and_save(attribute, value) # Character Strings - it 'char(10)' do - col = column('char_10') - _(col.sql_type).must_equal 'char(10)' + 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.default).must_equal "1234567890" + _(obj.char_10).must_equal "1234567890" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Char @@ -590,19 +590,19 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. - obj.char_10 = '012345' - _(obj.char_10.strip).must_equal '012345' + obj.char_10 = "012345" + _(obj.char_10.strip).must_equal "012345" obj.save! - _(obj.reload.char_10.strip).must_equal '012345' + _(obj.reload.char_10.strip).must_equal "012345" end - it 'varchar(50)' do - col = column('varchar_50') - _(col.sql_type).must_equal 'varchar(50)' + 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.default).must_equal "test varchar_50" + _(obj.varchar_50).must_equal "test varchar_50" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Varchar @@ -610,16 +610,16 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. - assert_obj_set_and_save :varchar_50, 'Hello World' + assert_obj_set_and_save :varchar_50, "Hello World" end - it 'varchar(max)' do - col = column('varchar_max') - _(col.sql_type).must_equal 'varchar(max)' + 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.default).must_equal "test varchar_max" + _(obj.varchar_max).must_equal "test varchar_max" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::VarcharMax @@ -627,16 +627,16 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. - assert_obj_set_and_save :varchar_max, 'Hello World' + assert_obj_set_and_save :varchar_max, "Hello World" end - it 'text' do - col = column('text') - _(col.sql_type).must_equal 'text' + 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.default).must_equal "test text" + _(obj.text).must_equal "test text" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Text @@ -644,18 +644,18 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. - assert_obj_set_and_save :text, 'Hello World' + assert_obj_set_and_save :text, "Hello World" end # Unicode Character Strings - it 'nchar(10)' do - col = column('nchar_10') - _(col.sql_type).must_equal 'nchar(10)' + 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.default).must_equal "12345678åå" + _(obj.nchar_10).must_equal "12345678åå" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::UnicodeChar @@ -669,13 +669,13 @@ def assert_obj_set_and_save(attribute, value) _(obj.reload.nchar_10.strip).must_equal "五六" end - it 'nvarchar(50)' do - col = column('nvarchar_50') - _(col.sql_type).must_equal 'nvarchar(50)' + 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.default).must_equal "test nvarchar_50 åå" + _(obj.nvarchar_50).must_equal "test nvarchar_50 åå" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::UnicodeVarchar @@ -686,13 +686,13 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :nvarchar_50, "一二34五六" end - it 'nvarchar(max)' do - col = column('nvarchar_max') - _(col.sql_type).must_equal 'nvarchar(max)' + 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.default).must_equal "test nvarchar_max åå" + _(obj.nvarchar_max).must_equal "test nvarchar_max åå" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::UnicodeVarcharMax @@ -703,13 +703,13 @@ def assert_obj_set_and_save(attribute, value) assert_obj_set_and_save :nvarchar_max, "一二34五六" end - it 'ntext' do - col = column('ntext') - _(col.sql_type).must_equal 'ntext' + 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.default).must_equal "test ntext åå" + _(obj.ntext).must_equal "test ntext åå" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::UnicodeText @@ -722,12 +722,12 @@ 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_file) { File.join ARTest::SQLServer.test_root_sqlserver, "fixtures", "1px.gif" } + let(:binary_data) { File.open(binary_file, "rb") { |f| f.read } } - it 'binary(49)' do - col = column('binary_49') - _(col.sql_type).must_equal 'binary(49)' + 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.default).must_be_nil @@ -746,9 +746,9 @@ def assert_obj_set_and_save(attribute, value) _(obj.reload.binary_49).must_equal binary_data end - it 'varbinary(49)' do - col = column('varbinary_49') - _(col.sql_type).must_equal 'varbinary(49)' + 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.default).must_be_nil @@ -767,9 +767,9 @@ def assert_obj_set_and_save(attribute, value) _(obj.reload.varbinary_49).must_equal binary_data_20 end - it 'varbinary(max)' do - col = column('varbinary_max') - _(col.sql_type).must_equal 'varbinary(max)' + 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.default).must_be_nil @@ -786,13 +786,13 @@ def assert_obj_set_and_save(attribute, value) # Other Data Types - it 'uniqueidentifier' do - col = column('uniqueidentifier') - _(col.sql_type).must_equal 'uniqueidentifier' + it "uniqueidentifier" do + col = column("uniqueidentifier") + _(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()' + _(col.default_function).must_equal "newid()" type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Uuid _(type.limit).must_be_nil @@ -809,9 +809,9 @@ def assert_obj_set_and_save(attribute, value) _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" end - it 'timestamp' do - col = column('timestamp') - _(col.sql_type).must_equal 'timestamp' + it "timestamp" do + col = column("timestamp") + _(col.sql_type).must_equal "timestamp" _(col.type).must_equal :ss_timestamp _(col.null).must_equal true _(col.default).must_be_nil @@ -831,7 +831,7 @@ def assert_obj_set_and_save(attribute, value) obj.save! end - it 'does not mark object as changed after save' do + it "does not mark object as changed after save" do obj.save! obj.attributes _(obj.changed?).must_equal false diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 56e691ff4..7309506c4 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/reply' -require 'models/topic' +require "cases/helper_sqlserver" +require "models/reply" +require "models/topic" class ConnectionTestSQLServer < ActiveRecord::TestCase @@ -15,7 +15,7 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert connection.active? end - it 'affect rows' do + it "affect rows" do topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } updated = Topic.update(topic_data.keys, topic_data.values) assert_equal 2, updated.size @@ -24,34 +24,34 @@ 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 + 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 + 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' + assert_equal "activerecord_unittest", connection.current_database, "Would default back to connection options" end end unless connection_sqlserver_azure? - describe 'Connection management' do + describe "Connection management" do - it 'set spid on connect' do - _(['Fixnum', 'Integer']).must_include connection.spid.class.name + it "set spid on connect" do + _(["Fixnum", "Integer"]).must_include connection.spid.class.name end - it 'reset spid on disconnect!' do + it "reset spid on disconnect!" do connection.disconnect! assert connection.spid.nil? end - it 'reset the connection' do + it "reset the connection" do connection.disconnect! _(connection.raw_connection).must_be_nil end - it 'be able to disconnect and reconnect at will' do + it "be able to disconnect and reconnect at will" do disconnect_raw_connection! assert !connection.active? connection.reconnect! diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 7cdfa984f..46d1774c3 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -1,45 +1,45 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase - it 'execute a simple procedure' do + it "execute a simple procedure" do tables = ActiveRecord::Base.execute_procedure :sp_tables assert_instance_of Array, tables assert tables.first.respond_to?(:keys) end - it 'take parameter arguments' do - tables = ActiveRecord::Base.execute_procedure :sp_tables, 'sst_datatypes' + it "take parameter arguments" do + tables = ActiveRecord::Base.execute_procedure :sp_tables, "sst_datatypes" table_info = tables.first assert_equal 1, tables.size - assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" - assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" + assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}" + assert_equal "TABLE", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}" end - it 'allow multiple result sets to be returned' do - results1, results2 = ActiveRecord::Base.execute_procedure('sp_helpconstraint','accounts') + it "allow multiple result sets to be returned" do + results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint","accounts") assert_instance_of Array, results1 assert results1.first.respond_to?(:keys) - assert results1.first['Object Name'] + assert results1.first["Object Name"] assert_instance_of Array, results2 assert results2.first.respond_to?(:keys) - assert results2.first['constraint_name'] - assert results2.first['constraint_type'] + assert results2.first["constraint_name"] + assert results2.first["constraint_type"] end - it 'take named parameter arguments' do - tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys' + it "take named parameter arguments" do + tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: "tables", table_owner: "sys" table_info = tables.first assert_equal 1, tables.size - assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}" - assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}" + assert_equal (ENV["ARUNIT_DB_NAME"] || "activerecord_unittest"), table_info["TABLE_QUALIFIER"], "Table Info: #{table_info.inspect}" + assert_equal "VIEW", table_info["TABLE_TYPE"], "Table Info: #{table_info.inspect}" end - it 'uses the proper timezone' do - date_proc = connection.execute_procedure('my_getutcdate').first['utcdate'] - date_base = connection.select_value('select GETUTCDATE()') + it "uses the proper timezone" do + date_proc = connection.execute_procedure("my_getutcdate").first["utcdate"] + date_base = connection.select_value("select GETUTCDATE()") assert_equal date_base.change(usec: 0), date_proc.change(usec: 0) end diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 5650a5f55..4f32b9db2 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/book' +require "cases/helper_sqlserver" +require "models/book" class FetchTestSqlserver < ActiveRecord::TestCase @@ -9,14 +9,14 @@ class FetchTestSqlserver < ActiveRecord::TestCase before { create_10_books } - it 'work with fully qualified table and columns in select' do - books = Book.select('books.id, books.name').limit(3).offset(5) + it "work with fully qualified table and columns in select" do + books = Book.select("books.id, books.name").limit(3).offset(5) assert_equal Book.all[5,3].map(&:id), books.map(&:id) end - describe 'count' do + describe "count" do - it 'gauntlet' do + it "gauntlet" do books[0].destroy books[1].destroy books[2].destroy @@ -34,14 +34,14 @@ class FetchTestSqlserver < ActiveRecord::TestCase end - describe 'order' do + describe "order" do - it 'gauntlet' do - Book.where(name:'Name-10').delete_all - _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ['Name-2'] - _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ['Name-3', 'Name-4'] - _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9'] - _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ['Name-8', 'Name-9'] + it "gauntlet" do + Book.where(name:"Name-10").delete_all + _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ["Name-2"] + _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ["Name-3", "Name-4"] + _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"] + _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"] _(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal [] end diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index f3ecc2786..33d042330 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase - describe 'local server' do + describe "local server" do - it 'should use table name in select projections' do + it "should use table name in select projections" do table = Arel::Table.new(:table) expected_sql = "SELECT [table].[name] FROM [table]" assert_equal expected_sql, table.project(table[:name]).to_sql @@ -14,7 +14,7 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase end - describe 'remote server' do + describe "remote server" do before do connection_options[:database_prefix] = "[my.server].db.schema." @@ -24,39 +24,39 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase connection_options.delete :database_prefix end - it 'should use fully qualified table name in select from clause' do + it "should use fully qualified table name in select from clause" do table = Arel::Table.new(:table) expected_sql = "SELECT * FROM [my.server].[db].[schema].[table]" assert_equal expected_sql, table.project(Arel.star).to_sql end - it 'should not use fully qualified table name in select projections' do + it "should not use fully qualified table name in select projections" do table = Arel::Table.new(:table) expected_sql = "SELECT [table].[name] FROM [my.server].[db].[schema].[table]" assert_equal expected_sql, table.project(table[:name]).to_sql end - it 'should not use fully qualified table name in where clause' do + it "should not use fully qualified table name in where clause" do table = Arel::Table.new(:table) expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" quietly { assert_equal expected_sql, table.project(Arel.star).where(table[:id].eq(42)).to_sql } end - it 'should not use fully qualified table name in order clause' do + it "should not use fully qualified table name in order clause" do table = Arel::Table.new(:table) expected_sql = "SELECT * FROM [my.server].[db].[schema].[table] ORDER BY [table].[name]" assert_equal expected_sql, table.project(Arel.star).order(table[:name]).to_sql end - it 'should use fully qualified table name in insert statement' do + it "should use fully qualified table name in insert statement" do manager = Arel::InsertManager.new manager.into Arel::Table.new(:table) - manager.values = manager.create_values [Arel.sql('*')] + manager.values = manager.create_values [Arel.sql("*")] expected_sql = "INSERT INTO [my.server].[db].[schema].[table] VALUES (*)" quietly { assert_equal expected_sql, manager.to_sql } end - it 'should use fully qualified table name in update statement' do + it "should use fully qualified table name in update statement" do table = Arel::Table.new(:table) manager = Arel::UpdateManager.new manager.table(table).where(table[:id].eq(42)) @@ -65,7 +65,7 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase quietly { assert_equal expected_sql, manager.to_sql } end - it 'should use fully qualified table name in delete statement' do + it "should use fully qualified table name in delete statement" do table = Arel::Table.new(:table) manager = Arel::DeleteManager.new manager.from(table).where(table[:id].eq(42)) diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 41da2e52f..4c1d3dcfd 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -1,18 +1,18 @@ # frozen_string_literal: true -require 'support/paths_sqlserver' -require 'bundler/setup' +require "support/paths_sqlserver" +require "bundler/setup" Bundler.require :default, :development -require 'pry' -require 'support/core_ext/query_cache' -require 'support/minitest_sqlserver' -require 'support/test_in_memory_oltp' -require 'cases/helper' -require 'support/load_schema_sqlserver' -require 'support/coerceable_test_sqlserver' -require 'support/sql_counter_sqlserver' -require 'support/connection_reflection' -require 'mocha/minitest' +require "pry" +require "support/core_ext/query_cache" +require "support/minitest_sqlserver" +require "support/test_in_memory_oltp" +require "cases/helper" +require "support/load_schema_sqlserver" +require "support/coerceable_test_sqlserver" +require "support/sql_counter_sqlserver" +require "support/connection_reflection" +require "mocha/minitest" module ActiveRecord class TestCase < ActiveSupport::TestCase @@ -40,7 +40,7 @@ def remove_backtrace_silencers end def host_windows? - RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end def with_use_output_inserted_disabled diff --git a/test/cases/in_clause_test_sqlserver.rb b/test/cases/in_clause_test_sqlserver.rb index b32639bde..360bf6003 100644 --- a/test/cases/in_clause_test_sqlserver.rb +++ b/test/cases/in_clause_test_sqlserver.rb @@ -1,14 +1,14 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/post' -require 'models/author' +require "cases/helper_sqlserver" +require "models/post" +require "models/author" class InClauseTestSQLServer < ActiveRecord::TestCase fixtures :posts, :authors - it 'removes ordering from subqueries' do - authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name) + it "removes ordering from subqueries" do + authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name) posts = Post.where(author: authors_subquery) assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" @@ -16,8 +16,8 @@ class InClauseTestSQLServer < ActiveRecord::TestCase assert_equal 10, posts.length end - it 'does not remove ordering from subquery that includes a limit' do - authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name).limit(2) + it "does not remove ordering from subquery that includes a limit" do + authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).limit(2) posts = Post.where(author: authors_subquery) assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" @@ -25,8 +25,8 @@ class InClauseTestSQLServer < ActiveRecord::TestCase assert_equal 7, posts.length end - it 'does not remove ordering from subquery that includes an offset' do - authors_subquery = Author.where(name: ['David', 'Mary', 'Bob']).order(:name).offset(1) + it "does not remove ordering from subquery that includes an offset" do + authors_subquery = Author.where(name: ["David", "Mary", "Bob"]).order(:name).offset(1) posts = Post.where(author: authors_subquery) assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index a1cebaa9e..fbea49b73 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class IndexTestSQLServer < ActiveRecord::TestCase @@ -19,31 +19,31 @@ class IndexTestSQLServer < ActiveRecord::TestCase connection.drop_table :testings rescue nil end - it 'add index with order' do + it "add index with order" do assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do - connection.add_index 'testings', ['last_name'], order: { last_name: :desc } - connection.remove_index 'testings', ['last_name'] + connection.add_index "testings", ["last_name"], order: { last_name: :desc } + connection.remove_index "testings", ["last_name"] end assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do - connection.add_index 'testings', ['last_name', 'first_name'], order: { last_name: :desc } - connection.remove_index 'testings', ['last_name', 'first_name'] + connection.add_index "testings", ["last_name", "first_name"], order: { last_name: :desc } + connection.remove_index "testings", ["last_name", "first_name"] end assert_sql(/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.remove_index 'testings', ['last_name', 'first_name'] + 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 - it 'add index with where' do + it "add index with where" do assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do - connection.add_index 'testings', 'last_name', where: "[first_name] = N'john doe'" - connection.remove_index 'testings', 'last_name' + connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'" + connection.remove_index "testings", "last_name" end end - it 'add index with expression' do + it "add index with expression" do connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])" - connection.add_index 'testings', 'first_name_upper' + connection.add_index "testings", "first_name_upper" end end diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index c47b55794..66ab4180d 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -1,32 +1,32 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" if ActiveRecord::Base.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' }) + 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" }) @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 - _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) > 0').count).must_equal 4 - _(SSTestDatatypeMigrationJson.where('ISJSON(json_col) IS NULL').count).must_equal 1 + it "can use ISJSON function" do + _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) > 0").count).must_equal 4 + _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) IS NULL").count).must_equal 1 end - it 'can use JSON_VALUE function' do + it "can use JSON_VALUE function" do _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2 end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 3aa02406e..1e54b831d 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/person' +require "cases/helper_sqlserver" +require "models/person" class MigrationTestSQLServer < ActiveRecord::TestCase - describe 'For transactions' do + describe "For transactions" do before do - @trans_test_table1 = 'sqlserver_trans_table1' - @trans_test_table2 = 'sqlserver_trans_table2' + @trans_test_table1 = "sqlserver_trans_table1" + @trans_test_table2 = "sqlserver_trans_table2" @trans_tables = [@trans_test_table1,@trans_test_table2] end @@ -19,9 +19,9 @@ class MigrationTestSQLServer < ActiveRecord::TestCase end end - it 'not create a tables if error in migrations' do + it "not create a tables if error in migrations" do begin - migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table' + migrations_dir = File.join ARTest::SQLServer.migrations_root, "transaction_table" quietly { ActiveRecord::MigrationContext.new(migrations_dir, ActiveRecord::SchemaMigration).up } rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message @@ -32,39 +32,39 @@ class MigrationTestSQLServer < ActiveRecord::TestCase end - describe 'For changing column' do + describe "For changing column" do - it 'not raise exception when column contains default constraint' do - lock_version_column = Person.columns_hash['lock_version'] + it "not raise exception when column contains default constraint" do + lock_version_column = Person.columns_hash["lock_version"] assert_equal :integer, lock_version_column.type assert lock_version_column.default.present? - assert_nothing_raised { connection.change_column 'people', 'lock_version', :string } + assert_nothing_raised { connection.change_column "people", "lock_version", :string } Person.reset_column_information - lock_version_column = Person.columns_hash['lock_version'] + lock_version_column = Person.columns_hash["lock_version"] assert_equal :string, lock_version_column.type assert lock_version_column.default.nil? - assert_nothing_raised { connection.change_column 'people', 'lock_version', :integer } + assert_nothing_raised { connection.change_column "people", "lock_version", :integer } Person.reset_column_information end - it 'not drop the default constraint if just renaming' do + 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| - row['constraint_type'] == "DEFAULT on column string_with_pretend_paren_three" + connection.execute_procedure(:sp_helpconstraint, "sst_string_defaults", "nomsg").select do |row| + row["constraint_type"] == "DEFAULT on column string_with_pretend_paren_three" end.last end default_before = find_default.call connection.change_column :sst_string_defaults, :string_with_pretend_paren_three, :string, limit: 255 default_after = find_default.call assert default_after - assert_equal default_before['constraint_keys'], default_after['constraint_keys'] + assert_equal default_before["constraint_keys"], default_after["constraint_keys"] end - it 'change limit' do + it "change limit" do assert_nothing_raised { connection.change_column :people, :lock_version, :integer, limit: 8 } end - it 'change null and default' do + it "change null and default" do assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } end diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index f48a1fad1..ae8bcd6ed 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -1,146 +1,146 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/post' +require "cases/helper_sqlserver" +require "models/post" class OrderTestSQLServer < ActiveRecord::TestCase fixtures :posts - it 'not mangel complex order clauses' do + it "not mangel complex order clauses" do xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END" - xyz_post = Post.create title: 'XYZ Post', body: 'Test cased orders.' + xyz_post = Post.create title: "XYZ Post", body: "Test cased orders." assert_equal xyz_post, Post.order(Arel.sql(xyz_order)).first end - it 'support column' do + it "support column" do order = "title" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(order).first end - it 'support column ASC' do + it "support column ASC" do order = "title ASC" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(order).first end - it 'support column DESC' do + it "support column DESC" do order = "title DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Test cased orders." assert_equal post1, Post.order(order).first end - it 'support column as symbol' do + it "support column as symbol" do order = :title - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(order).first end - it 'support table and column' do + it "support table and column" do order = "posts.title" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(order).first end - it 'support quoted column' do + it "support quoted column" do order = "[title]" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first end - it 'support quoted table and column' do + it "support quoted table and column" do order = "[posts].[title]" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first end - it 'support primary: column, secondary: column' do + it "support primary: column, secondary: column" do order = "title DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Test cased orders." + post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders." assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - it 'support primary: table and column, secondary: column' do + it "support primary: table and column, secondary: column" do order = "posts.title DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Test cased orders." + post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders." assert_equal post1, Post.order(order).first assert_equal post2, Post.order(order).second end - it 'support primary: case expression, secondary: column' do + it "support primary: case expression, secondary: column" do order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' - post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Test cased orders." + post2 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support primary: quoted table and column, secondary: case expresion' do + it "support primary: quoted table and column, secondary: case expresion" do order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC" - post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "ZZZ Test cased orders." + post2 = Post.create title: "ZZY Post", body: "ZZZ Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support inline function' do + it "support inline function" do order = "LEN(title)" - post1 = Post.create title: 'A', body: 'AAA Test cased orders.' + post1 = Post.create title: "A", body: "AAA Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first end - it 'support inline function with parameters' do + it "support inline function with parameters" do order = "SUBSTRING(title, 1, 3)" - post1 = Post.create title: 'AAA Post', body: 'Test cased orders.' + post1 = Post.create title: "AAA Post", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first end - it 'support inline function with parameters DESC' do + it "support inline function with parameters DESC" do order = "SUBSTRING(title, 1, 3) DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first end - it 'support primary: inline function, secondary: column' do + it "support primary: inline function, secondary: column" do order = "LEN(title), body" - post1 = Post.create title: 'A', body: 'AAA Test cased orders.' - post2 = Post.create title: 'A', body: 'Test cased orders.' + post1 = Post.create title: "A", body: "AAA Test cased orders." + post2 = Post.create title: "A", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support primary: inline function, secondary: column with direction' do + it "support primary: inline function, secondary: column with direction" do order = "LEN(title) ASC, body DESC" - post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'A', body: 'Test cased orders.' + post1 = Post.create title: "A", body: "ZZZ Test cased orders." + post2 = Post.create title: "A", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support primary: column, secondary: inline function' do + it "support primary: column, secondary: inline function" do order = "body DESC, LEN(title)" - post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.' - post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.' + post1 = Post.create title: "Post", body: "ZZZ Test cased orders." + post2 = Post.create title: "Longer Post", body: "ZZZ Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support primary: case expression, secondary: inline function' do + it "support primary: case expression, secondary: inline function" do order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC" - post1 = Post.create title: 'ZZZ Post', body: 'Z' - post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.' + post1 = Post.create title: "ZZZ Post", body: "Z" + post2 = Post.create title: "ZZZ Post", body: "Test cased orders." assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - it 'support primary: inline function, secondary: case expression' do + it "support primary: inline function, secondary: case expression" do order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC" - post1 = Post.create title: 'ZZZ Post', body: 'Z' - post2 = Post.create title: 'Post', body: 'Z' + post1 = Post.create title: "ZZZ Post", body: "Z" + post2 = Post.create title: "Post", body: "Z" assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 45160f156..b7bfadc8a 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/person' -require 'models/reader' +require "cases/helper_sqlserver" +require "models/person" +require "models/reader" class PessimisticLockingTestSQLServer < ActiveRecord::TestCase @@ -13,15 +13,15 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase Reader.columns end - it 'uses with updlock by default' do + it "uses with updlock by default" do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(UPDLOCK\)| do _(Person.lock(true).to_a).must_equal Person.all.to_a end end - describe 'For simple finds with default lock option' do + describe "For simple finds with default lock option" do - it 'lock with simple find' do + it "lock with simple find" do assert_nothing_raised do Person.transaction do _(Person.lock(true).find(1)).must_equal Person.find(1) @@ -29,7 +29,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - it 'lock with scoped find' do + it "lock with scoped find" do assert_nothing_raised do Person.transaction do Person.lock(true).scoping do @@ -39,7 +39,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - it 'lock with eager find' do + it "lock with eager find" do assert_nothing_raised do Person.transaction do person = Person.lock(true).includes(:readers).find(1) @@ -48,35 +48,35 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - it 'can add a custom lock directive' do + it "can add a custom lock directive" do assert_sql %r|SELECT \[people\]\.\* FROM \[people\] WITH\(HOLDLOCK, ROWLOCK\)| do - Person.lock('WITH(HOLDLOCK, ROWLOCK)').load + Person.lock("WITH(HOLDLOCK, ROWLOCK)").load end end - describe 'joining tables' do + describe "joining tables" do - it 'joined tables use updlock by default' do + it "joined tables use updlock by default" do assert_sql %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 + it "joined tables can use custom lock directive" do assert_sql %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 + Person.lock("WITH(NOLOCK)").joins(:readers).load end end - it 'left joined tables use updlock by default' do + it "left joined tables use updlock by default" do assert_sql %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 + it "left joined tables can use custom lock directive" do assert_sql %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 + Person.lock("WITH(NOLOCK)").left_joins(:readers).load end end @@ -84,23 +84,23 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end - describe 'For paginated finds' do + describe "For paginated finds" do before do Person.delete_all 20.times { |n| Person.create!(first_name: "Thing_#{n}") } end - it 'copes with eager loading un-locked paginated' do + it "copes with eager loading un-locked paginated" do eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ assert_sql(eager_ids_sql, loader_sql) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a - _(people[0].first_name).must_equal 'Thing_10' - _(people[1].first_name).must_equal 'Thing_11' - _(people[2].first_name).must_equal 'Thing_12' - _(people[3].first_name).must_equal 'Thing_13' - _(people[4].first_name).must_equal 'Thing_14' + _(people[0].first_name).must_equal "Thing_10" + _(people[1].first_name).must_equal "Thing_11" + _(people[2].first_name).must_equal "Thing_12" + _(people[3].first_name).must_equal "Thing_13" + _(people[4].first_name).must_equal "Thing_14" end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 431497e00..bb5fa8b1b 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SQLServerRakeTest < ActiveRecord::TestCase @@ -10,11 +10,11 @@ class SQLServerRakeTest < ActiveRecord::TestCase self.azure_skip = connection_sqlserver_azure? let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } - let(:new_database) { 'activerecord_unittest_tasks' } - let(:default_configuration) { ARTest.connection_config['arunit'] } - let(:configuration) { default_configuration.merge('database' => new_database) } + let(:new_database) { "activerecord_unittest_tasks" } + let(:default_configuration) { ARTest.connection_config["arunit"] } + let(:configuration) { default_configuration.merge("database" => new_database) } - before { skip 'on azure' if azure_skip } + before { skip "on azure" if azure_skip } before { disconnect! unless azure_skip } after { reconnect unless azure_skip } @@ -27,7 +27,7 @@ def disconnect! def reconnect config = default_configuration if connection_sqlserver_azure? - ActiveRecord::Base.establish_connection(config.merge('database' => 'master')) + ActiveRecord::Base.establish_connection(config.merge("database" => "master")) connection.drop_database(new_database) rescue nil disconnect! ActiveRecord::Base.establish_connection(config) @@ -43,22 +43,22 @@ class SQLServerRakeCreateTest < SQLServerRakeTest self.azure_skip = false - it 'establishes connection to database after create ' do + it "establishes connection to database after create " do quietly { db_tasks.create configuration } _(connection.current_database).must_equal(new_database) end - it 'creates database with default collation' do + it "creates database with default collation" do quietly { db_tasks.create configuration } - _(connection.collation).must_equal 'SQL_Latin1_General_CP1_CI_AS' + _(connection.collation).must_equal "SQL_Latin1_General_CP1_CI_AS" end - it 'creates database with given collation' do - quietly { db_tasks.create configuration.merge('collation' => 'Latin1_General_CI_AS') } - _(connection.collation).must_equal 'Latin1_General_CI_AS' + it "creates database with given collation" do + quietly { db_tasks.create configuration.merge("collation" => "Latin1_General_CI_AS") } + _(connection.collation).must_equal "Latin1_General_CI_AS" end - it 'prints error message when database exists' do + it "prints error message when database exists" do quietly { db_tasks.create configuration } message = capture(:stderr) { db_tasks.create configuration } _(message).must_match %r{activerecord_unittest_tasks.*already exists} @@ -70,16 +70,16 @@ class SQLServerRakeDropTest < SQLServerRakeTest self.azure_skip = false - it 'drops database and uses master' do + it "drops database and uses master" do quietly do db_tasks.create configuration db_tasks.drop configuration end - _(connection.current_database).must_equal 'master' + _(connection.current_database).must_equal "master" end - it 'prints error message when database does not exist' do - message = capture(:stderr) { db_tasks.drop configuration.merge('database' => 'doesnotexist') } + it "prints error message when database does not exist" do + message = capture(:stderr) { db_tasks.drop configuration.merge("database" => "doesnotexist") } _(message).must_match %r{'doesnotexist' does not exist} end @@ -95,12 +95,12 @@ class SQLServerRakePurgeTest < SQLServerRakeTest end end - it 'clears active connections, drops database, and recreates with established connection' do + it "clears active connections, drops database, and recreates with established connection" do _(connection.current_database).must_equal(new_database) - _(connection.tables).must_include 'users' + _(connection.tables).must_include "users" quietly { db_tasks.purge(configuration) } _(connection.current_database).must_equal(new_database) - _(connection.tables).wont_include 'users' + _(connection.tables).wont_include "users" end end @@ -111,8 +111,8 @@ class SQLServerRakeCharsetTest < SQLServerRakeTest quietly { db_tasks.create(configuration) } end - it 'retrieves charset' do - _(db_tasks.charset(configuration)).must_equal 'iso_1' + it "retrieves charset" do + _(db_tasks.charset(configuration)).must_equal "iso_1" end end @@ -123,15 +123,15 @@ class SQLServerRakeCollationTest < SQLServerRakeTest quietly { db_tasks.create(configuration) } end - it 'retrieves collation' do - _(db_tasks.collation(configuration)).must_equal 'SQL_Latin1_General_CP1_CI_AS' + it "retrieves collation" do + _(db_tasks.collation(configuration)).must_equal "SQL_Latin1_General_CP1_CI_AS" end end class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest - let(:filename) { File.join ARTest::SQLServer.migrations_root, 'structure.sql' } + let(:filename) { File.join ARTest::SQLServer.migrations_root, "structure.sql" } let(:filedata) { File.read(filename) } before do @@ -148,8 +148,8 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest FileUtils.rm_rf(filename) end - it 'dumps structure and accounts for defncopy oddities' do - skip 'debug defncopy on windows later' if host_windows? + it "dumps structure and accounts for defncopy oddities" do + skip "debug defncopy on windows later" if host_windows? quietly { db_tasks.structure_dump configuration, filename } _(filedata).wont_match %r{\AUSE.*\z} _(filedata).wont_match %r{\AGO.*\z} @@ -158,14 +158,14 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest _(filedata).must_match %r{background2\s+text\s+} end - it 'can load dumped structure' do - skip 'debug defncopy on windows later' if host_windows? + it "can load dumped structure" do + skip "debug defncopy on windows later" if host_windows? quietly { db_tasks.structure_dump configuration, filename } _(filedata).must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) - _(connection.tables).wont_include 'users' + _(connection.tables).wont_include "users" db_tasks.load_schema configuration, :sql, filename - _(connection.tables).must_include 'users' + _(connection.tables).must_include "users" end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index a5a5b6677..44cb73e02 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SchemaDumperTestSQLServer < ActiveRecord::TestCase @@ -9,138 +9,138 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase let(:all_tables) { ActiveRecord::Base.connection.tables } let(:schema) { @generated_schema } - it 'sst_datatypes' do - generate_schema_for_table 'sst_datatypes' - assert_line :bigint, type: 'bigint', limit: nil, precision: nil, scale: nil, default: 42 - assert_line :int, type: 'integer', limit: nil, precision: nil, scale: nil, default: 42 - assert_line :smallint, type: 'integer', limit: 2, precision: nil, scale: nil, default: 42 - assert_line :tinyint, type: 'integer', limit: 1, precision: nil, scale: nil, default: 42 - assert_line :bit, type: 'boolean', limit: nil, precision: nil, scale: nil, default: true - assert_line :decimal_9_2, type: 'decimal', limit: nil, precision: 9, scale: 2, default: 12345.01 - assert_line :numeric_18_0, type: 'decimal', limit: nil, precision: 18, scale: 0, default: 191.0 - assert_line :numeric_36_2, type: 'decimal', limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01 - assert_line :money, type: 'money', limit: nil, precision: 19, scale: 4, default: 4.2 - assert_line :smallmoney, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: 4.2 + it "sst_datatypes" do + generate_schema_for_table "sst_datatypes" + assert_line :bigint, type: "bigint", limit: nil, precision: nil, scale: nil, default: 42 + assert_line :int, type: "integer", limit: nil, precision: nil, scale: nil, default: 42 + assert_line :smallint, type: "integer", limit: 2, precision: nil, scale: nil, default: 42 + assert_line :tinyint, type: "integer", limit: 1, precision: nil, scale: nil, default: 42 + assert_line :bit, type: "boolean", limit: nil, precision: nil, scale: nil, default: true + assert_line :decimal_9_2, type: "decimal", limit: nil, precision: 9, scale: 2, default: 12345.01 + assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: 0, default: 191.0 + assert_line :numeric_36_2, type: "decimal", limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01 + assert_line :money, type: "money", limit: nil, precision: 19, scale: 4, default: 4.2 + assert_line :smallmoney, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: 4.2 # Approximate Numerics - assert_line :float, type: 'float', limit: nil, precision: nil, scale: nil, default: 123.00000001 - assert_line :real, type: 'real', limit: nil, precision: nil, scale: nil, default: 123.45 + assert_line :float, type: "float", limit: nil, precision: nil, scale: nil, default: 123.00000001 + assert_line :real, type: "real", limit: nil, precision: nil, scale: nil, default: 123.45 # Date and Time - assert_line :date, type: 'date', limit: nil, precision: nil, scale: nil, default: "01-01-0001" - assert_line :datetime, type: 'datetime', limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" + assert_line :date, type: "date", limit: nil, precision: nil, scale: nil, default: "01-01-0001" + assert_line :datetime, type: "datetime", limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? - assert_line :datetime2_7, type: 'datetime', limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" - assert_line :datetime2_3, type: 'datetime', limit: nil, precision: 3, scale: nil, default: nil - assert_line :datetime2_1, type: 'datetime', limit: nil, precision: 1, scale: nil, default: nil + assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" + assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil + assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil end - assert_line :smalldatetime, type: 'smalldatetime',limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" + assert_line :smalldatetime, type: "smalldatetime",limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? - assert_line :time_7, type: 'time', limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" - assert_line :time_2, type: 'time', limit: nil, precision: 2, scale: nil, default: nil - assert_line :time_default, type: 'time', limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" + assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" + assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil + assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" end # Character Strings - assert_line :char_10, type: 'char', limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil - assert_line :varchar_50, type: 'varchar', limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil - assert_line :varchar_max, type: 'varchar_max', limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil - assert_line :text, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: "test text", collation: nil + assert_line :char_10, type: "char", limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil + assert_line :varchar_50, type: "varchar", limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil + assert_line :varchar_max, type: "varchar_max", limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil + assert_line :text, type: "text_basic", limit: nil, precision: nil, scale: nil, default: "test text", collation: nil # Unicode Character Strings - assert_line :nchar_10, type: 'nchar', limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil - assert_line :nvarchar_50, type: 'string', limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil - assert_line :nvarchar_max, type: 'text', limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil - assert_line :ntext, type: 'ntext', limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil + assert_line :nchar_10, type: "nchar", limit: 10, precision: nil, scale: nil, default: "12345678åå", collation: nil + assert_line :nvarchar_50, type: "string", limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil + assert_line :nvarchar_max, type: "text", limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil + assert_line :ntext, type: "ntext", limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil # Binary Strings - assert_line :binary_49, type: 'binary_basic', limit: 49, precision: nil, scale: nil, default: nil - assert_line :varbinary_49, type: 'varbinary', limit: 49, precision: nil, scale: nil, default: nil - assert_line :varbinary_max, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil + assert_line :binary_49, type: "binary_basic", limit: 49, precision: nil, scale: nil, default: nil + assert_line :varbinary_49, type: "varbinary", limit: 49, precision: nil, scale: nil, default: nil + assert_line :varbinary_max, type: "binary", limit: nil, precision: nil, scale: nil, default: nil # Other Data Types - assert_line :uniqueidentifier, type: 'uuid', limit: nil, precision: nil, scale: nil, default: -> { "newid()" } - assert_line :timestamp, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil + assert_line :uniqueidentifier, type: "uuid", limit: nil, precision: nil, scale: nil, default: -> { "newid()" } + assert_line :timestamp, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil end - it 'sst_datatypes_migration' do + it "sst_datatypes_migration" do columns = SSTestDatatypeMigration.columns_hash - generate_schema_for_table 'sst_datatypes_migration' + 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['datetime_col'].sql_type).must_equal 'datetime' - _(columns['timestamp_col'].sql_type).must_equal 'datetime' - _(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', limit: nil, precision: nil, scale: nil, default: nil - assert_line :bigint_col, type: 'bigint', limit: nil, precision: nil, scale: nil, default: nil - assert_line :boolean_col, type: 'boolean', limit: nil, precision: nil, scale: nil, default: nil - assert_line :decimal_col, type: 'decimal', limit: nil, precision: 18, scale: 0, default: nil - assert_line :float_col, type: 'float', limit: nil, precision: nil, scale: nil, default: nil - assert_line :string_col, type: 'string', limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :timestamp_col, type: 'datetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: 'time', limit: nil, precision: 7, scale: nil, default: nil - assert_line :date_col, type: 'date', limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_col, type: 'binary', limit: nil, precision: nil, scale: nil, default: nil + _(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_col"].sql_type).must_equal "datetime" + _(columns["timestamp_col"].sql_type).must_equal "datetime" + _(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", limit: nil, precision: nil, scale: nil, default: nil + assert_line :bigint_col, type: "bigint", limit: nil, precision: nil, scale: nil, default: nil + assert_line :boolean_col, type: "boolean", limit: nil, precision: nil, scale: nil, default: nil + assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: 0, default: nil + assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil + assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil + assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil + assert_line :datetime_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil + assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil + assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil + assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil + assert_line :binary_col, type: "binary", limit: nil, precision: nil, scale: nil, default: nil # Our type methods. - _(columns['real_col'].sql_type).must_equal 'real' - _(columns['money_col'].sql_type).must_equal 'money' - _(columns['smalldatetime_col'].sql_type).must_equal 'smalldatetime' - _(columns['datetime2_col'].sql_type).must_equal 'datetime2(7)' - _(columns['datetimeoffset'].sql_type).must_equal 'datetimeoffset(7)' - _(columns['smallmoney_col'].sql_type).must_equal 'smallmoney' - _(columns['char_col'].sql_type).must_equal 'char(1)' - _(columns['varchar_col'].sql_type).must_equal 'varchar(8000)' - _(columns['text_basic_col'].sql_type).must_equal 'text' - _(columns['nchar_col'].sql_type).must_equal 'nchar(1)' - _(columns['ntext_col'].sql_type).must_equal 'ntext' - _(columns['binary_basic_col'].sql_type).must_equal 'binary(1)' - _(columns['varbinary_col'].sql_type).must_equal 'varbinary(8000)' - _(columns['uuid_col'].sql_type).must_equal 'uniqueidentifier' - _(columns['sstimestamp_col'].sql_type).must_equal 'timestamp' - _(columns['json_col'].sql_type).must_equal 'nvarchar(max)' - assert_line :real_col, type: 'real', limit: nil, precision: nil, scale: nil, default: nil - assert_line :money_col, type: 'money', limit: nil, precision: 19, scale: 4, default: nil - assert_line :smalldatetime_col, type: 'smalldatetime', limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime2_col, type: 'datetime', limit: nil, precision: 7, scale: nil, default: nil - assert_line :datetimeoffset, type: 'datetimeoffset', limit: nil, precision: 7, scale: nil, default: nil - assert_line :smallmoney_col, type: 'smallmoney', limit: nil, precision: 10, scale: 4, default: nil - assert_line :char_col, type: 'char', limit: 1, precision: nil, scale: nil, default: nil - assert_line :varchar_col, type: 'varchar', limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_basic_col, type: 'text_basic', limit: nil, precision: nil, scale: nil, default: nil - assert_line :nchar_col, type: 'nchar', limit: 1, precision: nil, scale: nil, default: nil - assert_line :ntext_col, type: 'ntext', limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_basic_col, type: 'binary_basic', limit: 1, precision: nil, scale: nil, default: nil - assert_line :varbinary_col, type: 'varbinary', limit: nil, precision: nil, scale: nil, default: nil - assert_line :uuid_col, type: 'uuid', limit: nil, precision: nil, scale: nil, default: nil - assert_line :sstimestamp_col, type: 'ss_timestamp', limit: nil, precision: nil, scale: nil, default: nil - assert_line :json_col, type: 'text', limit: nil, precision: nil, scale: nil, default: nil + _(columns["real_col"].sql_type).must_equal "real" + _(columns["money_col"].sql_type).must_equal "money" + _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" + _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" + _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" + _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" + _(columns["char_col"].sql_type).must_equal "char(1)" + _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" + _(columns["text_basic_col"].sql_type).must_equal "text" + _(columns["nchar_col"].sql_type).must_equal "nchar(1)" + _(columns["ntext_col"].sql_type).must_equal "ntext" + _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" + _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" + _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" + _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" + _(columns["json_col"].sql_type).must_equal "nvarchar(max)" + assert_line :real_col, type: "real", limit: nil, precision: nil, scale: nil, default: nil + assert_line :money_col, type: "money", limit: nil, precision: 19, scale: 4, default: nil + assert_line :smalldatetime_col, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: nil + assert_line :datetime2_col, type: "datetime", limit: nil, precision: 7, scale: nil, default: nil + assert_line :datetimeoffset, type: "datetimeoffset", limit: nil, precision: 7, scale: nil, default: nil + assert_line :smallmoney_col, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: nil + assert_line :char_col, type: "char", limit: 1, precision: nil, scale: nil, default: nil + assert_line :varchar_col, type: "varchar", limit: nil, precision: nil, scale: nil, default: nil + assert_line :text_basic_col, type: "text_basic", limit: nil, precision: nil, scale: nil, default: nil + assert_line :nchar_col, type: "nchar", limit: 1, precision: nil, scale: nil, default: nil + assert_line :ntext_col, type: "ntext", limit: nil, precision: nil, scale: nil, default: nil + assert_line :binary_basic_col, type: "binary_basic", limit: 1, precision: nil, scale: nil, default: nil + assert_line :varbinary_col, type: "varbinary", limit: nil, precision: nil, scale: nil, default: nil + assert_line :uuid_col, type: "uuid", limit: nil, precision: nil, scale: nil, default: nil + assert_line :sstimestamp_col, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil + assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil end # Special Cases - it 'honor nonstandard primary keys' do - generate_schema_for_table('movies') do |output| + it "honor nonstandard primary keys" do + generate_schema_for_table("movies") do |output| match = output.match(%r{create_table "movies"(.*)do}) assert_not_nil(match, "nonstandardpk table not found") assert_match %r(primary_key: "movieid"), match[1], "non-standard primary key not preserved" end end - it 'no id with model driven primary key' do - output = generate_schema_for_table 'sst_no_pk_data' + it "no id with model driven primary key" do + output = generate_schema_for_table "sst_no_pk_data" _(output).must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} - assert_line :name, type: 'string', limit: nil, default: nil, collation: nil + assert_line :name, type: "string", limit: nil, default: nil, collation: nil end private def generate_schema_for_table(*table_names) - require 'stringio' + require "stringio" stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index a913923bc..02244d358 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -1,26 +1,26 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SchemaTestSQLServer < ActiveRecord::TestCase - describe 'When table is dbo schema' do + describe "When table is dbo schema" do - it 'find primary key for tables with odd schema' do - _(connection.primary_key('sst_natural_pk_data')).must_equal 'legacy_id' + it "find primary key for tables with odd schema" do + _(connection.primary_key("sst_natural_pk_data")).must_equal "legacy_id" end end - describe 'When table is in non-dbo schema' do + describe "When table is in non-dbo schema" do - it 'work with table exists' do - assert connection.data_source_exists?('test.sst_schema_natural_id') - assert connection.data_source_exists?('[test].[sst_schema_natural_id]') + it "work with table exists" do + assert connection.data_source_exists?("test.sst_schema_natural_id") + assert connection.data_source_exists?("[test].[sst_schema_natural_id]") end - it 'find primary key for tables with odd schema' do - _(connection.primary_key('test.sst_schema_natural_id')).must_equal 'legacy_id' + it "find primary key for tables with odd schema" do + _(connection.primary_key("test.sst_schema_natural_id")).must_equal "legacy_id" end it "have only one identity column" do @@ -43,10 +43,10 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do columns = connection.columns("test.sst_schema_columns") - assert_equal 255, columns.find {|c| c.name == 'name'}.limit - assert_equal 1000, columns.find {|c| c.name == 'description'}.limit - assert_equal 255, columns.find {|c| c.name == 'n_name'}.limit - assert_equal 1000, columns.find {|c| c.name == 'n_description'}.limit + assert_equal 255, columns.find {|c| c.name == "name"}.limit + assert_equal 1000, columns.find {|c| c.name == "description"}.limit + assert_equal 255, columns.find {|c| c.name == "n_name"}.limit + assert_equal 1000, columns.find {|c| c.name == "n_description"}.limit end end diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb index f553170ca..a88cdb56d 100644 --- a/test/cases/scratchpad_test_sqlserver.rb +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class ScratchpadTestSQLServer < ActiveRecord::TestCase - it 'helps debug things' do + it "helps debug things" do end end diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index c43122610..b59e79ff9 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -1,65 +1,65 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/car' +require "cases/helper_sqlserver" +require "models/car" class ShowplanTestSQLServer < ActiveRecord::TestCase fixtures :cars - describe 'Unprepare previously prepared SQL' do + describe "Unprepare previously prepared SQL" do - it 'from simple statement' do + it "from simple statement" do plan = Car.where(id: 1).explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" - _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end - it 'from multiline statement' do + it "from multiline statement" do plan = Car.where("\n id = 1 \n").explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)" - _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end - it 'from prepared statement' do - plan = Car.where(name: ',').limit(1).explain + it "from prepared statement" do + plan = Car.where(name: ",").limit(1).explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[name]" - _(plan).must_include "TOP EXPRESSION", 'make sure we do not showplan the sp_executesql' - _(plan).must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "TOP EXPRESSION", "make sure we do not showplan the sp_executesql" + _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end - it 'from array condition using index' do + it "from array condition using index" do plan = Car.where(id: [1, 2]).explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" - _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end - it 'from array condition' do - plan = Car.where(name: ['honda', 'zyke']).explain + it "from array condition" do + plan = Car.where(name: ["honda", "zyke"]).explain _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" - _(plan).must_include "Clustered Index Scan", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end end - describe 'With SHOWPLAN_TEXT option' do + describe "With SHOWPLAN_TEXT option" do - it 'use simple table printer' do - with_showplan_option('SHOWPLAN_TEXT') do + it "use simple table printer" do + with_showplan_option("SHOWPLAN_TEXT") do plan = Car.where(id: 1).explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" - _(plan).must_include "Clustered Index Seek", 'make sure we do not showplan the sp_executesql' + _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end end end - describe 'With SHOWPLAN_XML option' do + describe "With SHOWPLAN_XML option" do - it 'show formatted xml' do - with_showplan_option('SHOWPLAN_XML') do + it "show formatted xml" do + with_showplan_option("SHOWPLAN_XML") do plan = Car.where(id: 1).explain - _(plan).must_include 'ShowPlanXML' + _(plan).must_include "ShowPlanXML" end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 21c75a1be..a11cf52e9 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -1,29 +1,29 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SpecificSchemaTestSQLServer < ActiveRecord::TestCase after { SSTestEdgeSchema.delete_all } - it 'handle dollar symbols' do + it "handle dollar symbols" do SSTestDollarTableName.create! SSTestDollarTableName.limit(20).offset(1) end - it 'models can use tinyint pk tables' do - obj = SSTestTinyintPk.create! name: '1' - _(['Fixnum', 'Integer']).must_include obj.id.class.name + it "models can use tinyint pk tables" do + obj = SSTestTinyintPk.create! name: "1" + _(["Fixnum", "Integer"]).must_include obj.id.class.name _(SSTestTinyintPk.find(obj.id)).must_equal obj end - it 'be able to complex count tables with no primary key' do + it "be able to complex count tables with no primary key" do SSTestNoPkData.delete_all 10.times { |n| SSTestNoPkData.create! name: "Test#{n}" } - assert_equal 1, SSTestNoPkData.where(name: 'Test5').count + assert_equal 1, SSTestNoPkData.where(name: "Test5").count end - it 'quote table names properly even when they are views' do + it "quote table names properly even when they are views" do obj = SSTestQuotedTable.create! assert_nothing_raised { assert SSTestQuotedTable.first } obj = SSTestQuotedTableUser.create! @@ -34,40 +34,40 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase assert_nothing_raised { assert SSTestQuotedView2.first } end - it 'cope with multi line defaults' do + it "cope with multi line defaults" do default = SSTestStringDefault.new assert_equal "Some long default with a\nnew line.", default.string_with_multiline_default end - it 'default strings before save' do + it "default strings before save" do default = SSTestStringDefault.new assert_nil default.string_with_null_default - assert_equal 'null', default.string_with_pretend_null_one - assert_equal '(null)', default.string_with_pretend_null_two - assert_equal 'NULL', default.string_with_pretend_null_three - assert_equal '(NULL)', default.string_with_pretend_null_four - assert_equal '(3)', default.string_with_pretend_paren_three + assert_equal "null", default.string_with_pretend_null_one + assert_equal "(null)", default.string_with_pretend_null_two + assert_equal "NULL", default.string_with_pretend_null_three + assert_equal "(NULL)", default.string_with_pretend_null_four + assert_equal "(3)", default.string_with_pretend_paren_three end - it 'default strings after save' do + it "default strings after save" do default = SSTestStringDefault.create assert_nil default.string_with_null_default - assert_equal 'null', default.string_with_pretend_null_one - assert_equal '(null)', default.string_with_pretend_null_two - assert_equal 'NULL', default.string_with_pretend_null_three - assert_equal '(NULL)', default.string_with_pretend_null_four + assert_equal "null", default.string_with_pretend_null_one + assert_equal "(null)", default.string_with_pretend_null_two + assert_equal "NULL", default.string_with_pretend_null_three + assert_equal "(NULL)", default.string_with_pretend_null_four end - it 'default objects work' do - obj = SSTestObjectDefault.create! name: 'MetaSkills' - _(obj.date).must_be_nil 'since this is set on insert' + it "default objects work" do + obj = SSTestObjectDefault.create! name: "MetaSkills" + _(obj.date).must_be_nil "since this is set on insert" _(obj.reload.date).must_be_instance_of Date end - it 'allows datetime2 as timestamps' do - _(SSTestBooking.columns_hash['created_at'].sql_type).must_equal 'datetime2(7)' - _(SSTestBooking.columns_hash['updated_at'].sql_type).must_equal 'datetime2(7)' - obj1 = SSTestBooking.new name: 'test1' + it "allows datetime2 as timestamps" do + _(SSTestBooking.columns_hash["created_at"].sql_type).must_equal "datetime2(7)" + _(SSTestBooking.columns_hash["updated_at"].sql_type).must_equal "datetime2(7)" + obj1 = SSTestBooking.new name: "test1" obj1.save! _(obj1.created_at).must_be_instance_of Time _(obj1.updated_at).must_be_instance_of Time @@ -75,35 +75,35 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase # Natural primary keys. - it 'work with identity inserts' do - record = SSTestNaturalPkData.new name: 'Test', description: 'Natural identity inserts.' - record.id = '12345ABCDE' + it "work with identity inserts" do + record = SSTestNaturalPkData.new name: "Test", description: "Natural identity inserts." + record.id = "12345ABCDE" assert record.save - assert_equal '12345ABCDE', record.reload.id + assert_equal "12345ABCDE", record.reload.id end - it 'work with identity inserts when the key is an int' do - record = SSTestNaturalPkIntData.new name: 'Test', description: 'Natural identity inserts.' + it "work with identity inserts when the key is an int" do + record = SSTestNaturalPkIntData.new name: "Test", description: "Natural identity inserts." record.id = 12 assert record.save assert_equal 12, record.reload.id end - it 'use primary key for row table order in pagination sql' do + it "use primary key for row table order in pagination sql" do sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load } end # Special quoted column - it 'work as normal' do + it "work as normal" do SSTestEdgeSchema.delete_all - r = SSTestEdgeSchema.create! 'crazy]]quote' => 'crazyqoute' - assert SSTestEdgeSchema.columns_hash['crazy]]quote'] - assert_equal r, SSTestEdgeSchema.where('crazy]]quote' => 'crazyqoute').first + r = SSTestEdgeSchema.create! "crazy]]quote" => "crazyqoute" + assert SSTestEdgeSchema.columns_hash["crazy]]quote"] + assert_equal r, SSTestEdgeSchema.where("crazy]]quote" => "crazyqoute").first end - it 'various methods to bypass national quoted columns for any column, but primarily useful for char/varchar' do + it "various methods to bypass national quoted columns for any column, but primarily useful for char/varchar" do value = Class.new do def quoted_id "'T'" @@ -115,14 +115,14 @@ def quoted_id # Using our custom char type data. type = ActiveRecord::Type::SQLServer::Char data = ActiveRecord::Type::SQLServer::Data - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new('T', type.new)).first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new('T', type.new)).first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } # Taking care of everything. - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: 'T').first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: 'T').first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first } + assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first } end - it 'can update and hence properly quoted non-national char/varchar columns' do + it "can update and hence properly quoted non-national char/varchar columns" do o = SSTestDatatypeMigration.create! o.varchar_col = "O'Reilly" o.save! @@ -134,28 +134,28 @@ def quoted_id # With column names that have spaces - it 'create record using a custom attribute reader and be able to load it back in' do - value = 'Saved value into a column that has a space in the name.' + it "create record using a custom attribute reader and be able to load it back in" do + value = "Saved value into a column that has a space in the name." record = SSTestEdgeSchema.create! with_spaces: value assert_equal value, SSTestEdgeSchema.find(record.id).with_spaces end # With description column - it 'allow all sorts of ordering without adapter munging it up with special description column' do - SSTestEdgeSchema.create! description: 'A' - SSTestEdgeSchema.create! description: 'B' - SSTestEdgeSchema.create! description: 'C' - assert_equal ['A','B','C'], SSTestEdgeSchema.order('description').map(&:description) - assert_equal ['A','B','C'], SSTestEdgeSchema.order('description asc').map(&:description) - assert_equal ['A','B','C'], SSTestEdgeSchema.order('description ASC').map(&:description) - assert_equal ['C','B','A'], SSTestEdgeSchema.order('description desc').map(&:description) - assert_equal ['C','B','A'], SSTestEdgeSchema.order('description DESC').map(&:description) + it "allow all sorts of ordering without adapter munging it up with special description column" do + SSTestEdgeSchema.create! description: "A" + SSTestEdgeSchema.create! description: "B" + SSTestEdgeSchema.create! description: "C" + assert_equal ["A","B","C"], SSTestEdgeSchema.order("description").map(&:description) + assert_equal ["A","B","C"], SSTestEdgeSchema.order("description asc").map(&:description) + assert_equal ["A","B","C"], SSTestEdgeSchema.order("description ASC").map(&:description) + assert_equal ["C","B","A"], SSTestEdgeSchema.order("description desc").map(&:description) + assert_equal ["C","B","A"], SSTestEdgeSchema.order("description DESC").map(&:description) end # For uniqueidentifier model helpers - it 'returns a new id via connection newid_function' do + it "returns a new id via connection newid_function" do acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID db_uuid = ActiveRecord::Base.connection.newid_function _(db_uuid).must_match(acceptable_uuid) @@ -163,10 +163,10 @@ def quoted_id # with similar table definition in two schemas - it 'returns the correct primary columns' do + it "returns the correct primary columns" do connection = ActiveRecord::Base.connection - assert_equal 'field_1', connection.columns('test.sst_schema_test_mulitple_schema').detect(&:is_primary?).name - assert_equal 'field_2', connection.columns('test2.sst_schema_test_mulitple_schema').detect(&:is_primary?).name + assert_equal "field_1", connection.columns("test.sst_schema_test_mulitple_schema").detect(&:is_primary?).name + assert_equal "field_2", connection.columns("test2.sst_schema_test_mulitple_schema").detect(&:is_primary?).name end end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index f94694e36..aa9f99532 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' -require 'models/ship' -require 'models/developer' +require "cases/helper_sqlserver" +require "models/ship" +require "models/developer" class TransactionTestSQLServer < ActiveRecord::TestCase @@ -10,21 +10,21 @@ class TransactionTestSQLServer < ActiveRecord::TestCase before { delete_ships } - it 'allow ActiveRecord::Rollback to work in 1 transaction block' do + it "allow ActiveRecord::Rollback to work in 1 transaction block" do Ship.transaction do - Ship.create! name: 'Black Pearl' + Ship.create! name: "Black Pearl" raise ActiveRecord::Rollback end assert_no_ships end - it 'allow nested transactions to totally rollback' do + it "allow nested transactions to totally rollback" do begin Ship.transaction do - Ship.create! name: 'Black Pearl' + Ship.create! name: "Black Pearl" Ship.transaction do - Ship.create! name: 'Flying Dutchman' - raise 'HELL' + Ship.create! name: "Flying Dutchman" + raise "HELL" end end rescue Exception => e @@ -32,12 +32,12 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end end - it 'can use an isolation level and reverts back to starting isolation level' do + it "can use an isolation level and reverts back to starting isolation level" do in_level = nil begin_level = connection.user_options_isolation_level _(begin_level).must_match %r{read committed}i Ship.transaction(isolation: :serializable) do - Ship.create! name: 'Black Pearl' + Ship.create! name: "Black Pearl" in_level = connection.user_options_isolation_level end after_level = connection.user_options_isolation_level @@ -45,7 +45,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase _(after_level).must_match %r{read committed}i end - it 'can use an isolation level and reverts back to starting isolation level under exceptions' do + it "can use an isolation level and reverts back to starting isolation level under exceptions" do _(connection.user_options_isolation_level).must_match %r{read committed}i _(lambda { Ship.transaction(isolation: :serializable) { Ship.create! } @@ -53,7 +53,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase _(connection.user_options_isolation_level).must_match %r{read committed}i end - describe 'when READ_COMMITTED_SNAPSHOT is set' do + describe "when READ_COMMITTED_SNAPSHOT is set" do before do connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON" connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE" @@ -64,11 +64,11 @@ class TransactionTestSQLServer < ActiveRecord::TestCase connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" end - it 'should use READ COMMITTED as an isolation level' do + it "should use READ COMMITTED as an isolation level" do _(connection.user_options_isolation_level).must_match "read committed snapshot" Ship.transaction(isolation: :serializable) do - Ship.create! name: 'Black Pearl' + Ship.create! name: "Black Pearl" end # We're actually testing that the isolation level was correctly reset to diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index 88ccf157e..fc0a6598a 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SQLServerTriggerTest < ActiveRecord::TestCase after { exclude_output_inserted_table_names.clear } @@ -9,22 +9,22 @@ class SQLServerTriggerTest < ActiveRecord::TestCase ActiveRecord::ConnectionAdapters::SQLServerAdapter.exclude_output_inserted_table_names end - it 'can insert into a table with output inserted - with a true setting for table name' do - exclude_output_inserted_table_names['sst_table_with_trigger'] = true + it "can insert into a table with output inserted - with a true setting for table name" do + exclude_output_inserted_table_names["sst_table_with_trigger"] = true assert SSTestTriggerHistory.all.empty? - obj = SSTestTrigger.create! event_name: 'test trigger' - _(['Fixnum', 'Integer']).must_include obj.id.class.name - _(obj.event_name).must_equal 'test trigger' + obj = SSTestTrigger.create! event_name: "test trigger" + _(["Fixnum", "Integer"]).must_include obj.id.class.name + _(obj.event_name).must_equal "test trigger" _(obj.id).must_be :present? _(obj.id.to_s).must_equal SSTestTriggerHistory.first.id_source end - it 'can insert into a table with output inserted - with a uniqueidentifier value' do - exclude_output_inserted_table_names['sst_table_with_uuid_trigger'] = 'uniqueidentifier' + it "can insert into a table with output inserted - with a uniqueidentifier value" do + exclude_output_inserted_table_names["sst_table_with_uuid_trigger"] = "uniqueidentifier" assert SSTestTriggerHistory.all.empty? - obj = SSTestTriggerUuid.create! event_name: 'test uuid trigger' - _(obj.id.class.name).must_equal 'String' - _(obj.event_name).must_equal 'test uuid trigger' + obj = SSTestTriggerUuid.create! event_name: "test uuid trigger" + _(obj.id.class.name).must_equal "String" + _(obj.event_name).must_equal "test uuid trigger" _(obj.id).must_be :present? _(obj.id.to_s).must_equal SSTestTriggerHistory.first.id_source end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 8274a93af..c89074a21 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -1,56 +1,56 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class UtilsTestSQLServer < ActiveRecord::TestCase - it '.quote_string' do + it ".quote_string" do _(SQLServer::Utils.quote_string("I'll store this in C:\\Users")).must_equal "I''ll store this in C:\\Users" end - it '.unquote_string' do + it ".unquote_string" do _(SQLServer::Utils.unquote_string("I''ll store this in C:\\Users")).must_equal "I'll store this in C:\\Users" end - it '.quoted_raw' do + it ".quoted_raw" do _(SQLServer::Utils.quoted_raw("some.Name")).must_equal "[some.Name]" end - describe '.extract_identifiers constructor and thus SQLServer::Utils::Name value object' do + describe ".extract_identifiers constructor and thus SQLServer::Utils::Name value object" do let(:valid_names) { valid_names_unquoted + valid_names_quoted } let(:valid_names_unquoted) {[ - 'server.database.schema.object', - 'server.database..object', - 'server..schema.object', - 'server...object', - 'database.schema.object', - 'database..object', - 'schema.object', - 'object' + "server.database.schema.object", + "server.database..object", + "server..schema.object", + "server...object", + "database.schema.object", + "database..object", + "schema.object", + "object" ]} let(:valid_names_quoted) {[ - '[server].[database].[schema].[object]', - '[server].[database]..[object]', - '[server]..[schema].[object]', - '[server]...[object]', - '[database].[schema].[object]', - '[database]..[object]', - '[schema].[object]', - '[object]' + "[server].[database].[schema].[object]", + "[server].[database]..[object]", + "[server]..[schema].[object]", + "[server]...[object]", + "[database].[schema].[object]", + "[database]..[object]", + "[schema].[object]", + "[object]" ]} 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/ } } - it 'extracts and returns #object identifier unquoted by default or quoted as needed' do + it "extracts and returns #object identifier unquoted by default or quoted as needed" do valid_names.each do |n| name = extract_identifiers(n) - _(name.object).must_equal 'object', "With #{n.inspect} for #object" - _(name.object_quoted).must_equal '[object]', "With #{n.inspect} for #object_quoted" + _(name.object).must_equal "object", "With #{n.inspect} for #object" + _(name.object_quoted).must_equal "[object]", "With #{n.inspect} for #object_quoted" end end @@ -72,52 +72,52 @@ class UtilsTestSQLServer < ActiveRecord::TestCase end - it 'does not blow up on nil or blank string name' do + it "does not blow up on nil or blank string name" do _(extract_identifiers(nil).object).must_be_nil - _(extract_identifiers(' ').object).must_be_nil + _(extract_identifiers(" ").object).must_be_nil end - it 'has a #quoted that returns a fully quoted name with all identifiers as orginially passed in' do - _(extract_identifiers('object').quoted).must_equal '[object]' - _(extract_identifiers('server.database..object').quoted).must_equal '[server].[database]..[object]' - _(extract_identifiers('[server]...[object]').quoted).must_equal '[server]...[object]' + it "has a #quoted that returns a fully quoted name with all identifiers as orginially passed in" do + _(extract_identifiers("object").quoted).must_equal "[object]" + _(extract_identifiers("server.database..object").quoted).must_equal "[server].[database]..[object]" + _(extract_identifiers("[server]...[object]").quoted).must_equal "[server]...[object]" end - it 'can take a symbol argument' do - _(extract_identifiers(:object).object).must_equal 'object' + it "can take a symbol argument" do + _(extract_identifiers(:object).object).must_equal "object" end - it 'allows identifiers with periods to work' do - _(extract_identifiers('[obj.name]').quoted).must_equal '[obj.name]' - _(extract_identifiers('[obj.name].[foo]').quoted).must_equal '[obj.name].[foo]' + it "allows identifiers with periods to work" do + _(extract_identifiers("[obj.name]").quoted).must_equal "[obj.name]" + _(extract_identifiers("[obj.name].[foo]").quoted).must_equal "[obj.name].[foo]" end - it 'should indicate if a name is fully qualitified' do - _(extract_identifiers('object').fully_qualified?).must_equal false - _(extract_identifiers('schema.object').fully_qualified?).must_equal false - _(extract_identifiers('database.schema.object').fully_qualified?).must_equal false - _(extract_identifiers('database.object').fully_qualified?).must_equal false - _(extract_identifiers('server...object').fully_qualified?).must_equal false - _(extract_identifiers('server.database..object').fully_qualified?).must_equal false - _(extract_identifiers('server.database.schema.object').fully_qualified?).must_equal true - _(extract_identifiers('server.database.schema.').fully_qualified?).must_equal true - _(extract_identifiers('[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[schema].[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[database].[schema].[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[database].[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[server.name]...[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[server.name].[database]..[obj.name]').fully_qualified?).must_equal false - _(extract_identifiers('[server.name].[database].[schema].[obj.name]').fully_qualified?).must_equal true - _(extract_identifiers('[server.name].[database].[schema].').fully_qualified?).must_equal true + it "should indicate if a name is fully qualitified" do + _(extract_identifiers("object").fully_qualified?).must_equal false + _(extract_identifiers("schema.object").fully_qualified?).must_equal false + _(extract_identifiers("database.schema.object").fully_qualified?).must_equal false + _(extract_identifiers("database.object").fully_qualified?).must_equal false + _(extract_identifiers("server...object").fully_qualified?).must_equal false + _(extract_identifiers("server.database..object").fully_qualified?).must_equal false + _(extract_identifiers("server.database.schema.object").fully_qualified?).must_equal true + _(extract_identifiers("server.database.schema.").fully_qualified?).must_equal true + _(extract_identifiers("[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[schema].[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[database].[schema].[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[database].[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[server.name]...[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[server.name].[database]..[obj.name]").fully_qualified?).must_equal false + _(extract_identifiers("[server.name].[database].[schema].[obj.name]").fully_qualified?).must_equal true + _(extract_identifiers("[server.name].[database].[schema].").fully_qualified?).must_equal true end - it 'can return fully qualified quoted table name' do - name = extract_identifiers('[my.server].db.schema.') - _(name.fully_qualified_database_quoted).must_equal '[my.server].[db]' - name = extract_identifiers('[server.name].[database].[schema].[object]') - _(name.fully_qualified_database_quoted).must_equal '[server.name].[database]' - name = extract_identifiers('server.database.schema.object') - _(name.fully_qualified_database_quoted).must_equal '[server].[database]' + it "can return fully qualified quoted table name" do + name = extract_identifiers("[my.server].db.schema.") + _(name.fully_qualified_database_quoted).must_equal "[my.server].[db]" + name = extract_identifiers("[server.name].[database].[schema].[object]") + _(name.fully_qualified_database_quoted).must_equal "[server.name].[database]" + name = extract_identifiers("server.database.schema.object") + _(name.fully_qualified_database_quoted).must_equal "[server].[database]" end end diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index 4a96c29ce..a79a51e04 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -1,46 +1,46 @@ # frozen_string_literal: true -require 'cases/helper_sqlserver' +require "cases/helper_sqlserver" class SQLServerUuidTest < ActiveRecord::TestCase let(:acceptable_uuid) { ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID } - it 'has a uuid primary key' do - _(SSTestUuid.columns_hash['id'].type).must_equal :uuid + it "has a uuid primary key" do + _(SSTestUuid.columns_hash["id"].type).must_equal :uuid assert SSTestUuid.primary_key end - it 'can create with a new pk' do + it "can create with a new pk" do obj = SSTestUuid.create! _(obj.id).must_be :present? _(obj.id).must_match acceptable_uuid end - it 'can create other uuid column on reload' do + it "can create other uuid column on reload" do obj = SSTestUuid.create! obj.reload _(obj.other_uuid).must_match acceptable_uuid end - it 'can find uuid pk via connection' do - _(connection.primary_key(SSTestUuid.table_name)).must_equal 'id' + it "can find uuid pk via connection" do + _(connection.primary_key(SSTestUuid.table_name)).must_equal "id" end - it 'changing column default' do + it "changing column default" do table_name = SSTestUuid.table_name connection.add_column table_name, :thingy, :uuid, null: false, default: "NEWSEQUENTIALID()" SSTestUuid.reset_column_information - column = SSTestUuid.columns_hash['thingy'] + column = SSTestUuid.columns_hash["thingy"] _(column.default_function).must_equal "newsequentialid()" # Now to a different function. connection.change_column table_name, :thingy, :uuid, null: false, default: "NEWID()" SSTestUuid.reset_column_information - column = SSTestUuid.columns_hash['thingy'] + column = SSTestUuid.columns_hash["thingy"] _(column.default_function).must_equal "newid()" end - it 'can insert even when use_output_inserted to false ' do + it "can insert even when use_output_inserted to false " do obj = with_use_output_inserted_disabled { SSTestUuid.create!(name: "😢") } _(obj.id).must_be :nil? end diff --git a/test/debug.rb b/test/debug.rb index 1b1f8faad..dc918ddb0 100644 --- a/test/debug.rb +++ b/test/debug.rb @@ -1,15 +1,15 @@ # frozen_string_literal: true # require 'rails/all' -require 'tiny_tds' +require "tiny_tds" c = TinyTds::Client.new( - host: ENV['CI_AZURE_HOST'], - username: 'rails', - password: ENV['CI_AZURE_PASS'], - database: 'activerecord_unittest', + host: ENV["CI_AZURE_HOST"], + username: "rails", + password: ENV["CI_AZURE_PASS"], + database: "activerecord_unittest", azure: true, - tds_version: '7.3' + tds_version: "7.3" ) puts c.execute("SELECT 1 AS [one]").each diff --git a/test/migrations/create_clients_and_change_column_null.rb b/test/migrations/create_clients_and_change_column_null.rb index 18f74cdcf..7f527806c 100644 --- a/test/migrations/create_clients_and_change_column_null.rb +++ b/test/migrations/create_clients_and_change_column_null.rb @@ -11,7 +11,7 @@ def up end change_column :clients, :name, :string, limit: 15 - change_column :clients, :code, :string, default: 'n/a' + change_column :clients, :code, :string, default: "n/a" change_column :clients, :value, :decimal, precision: 32, scale: 8 change_column_null :clients, :name, false diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index dfcec8368..769ce7967 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -4,7 +4,7 @@ class TableWillNeverBeCreated < ActiveRecord::Migration def self.up create_table(:sqlserver_trans_table1) { } - create_table(:sqlserver_trans_table2) { raise('HELL') } + create_table(:sqlserver_trans_table2) { raise("HELL") } end def self.down diff --git a/test/models/sqlserver/booking.rb b/test/models/sqlserver/booking.rb index 4b70013d1..e3f70a5c5 100644 --- a/test/models/sqlserver/booking.rb +++ b/test/models/sqlserver/booking.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestBooking < ActiveRecord::Base - self.table_name = 'sst_bookings' + self.table_name = "sst_bookings" end diff --git a/test/models/sqlserver/customers_view.rb b/test/models/sqlserver/customers_view.rb index ef3ad2160..f362216cb 100644 --- a/test/models/sqlserver/customers_view.rb +++ b/test/models/sqlserver/customers_view.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestCustomersView < ActiveRecord::Base - self.table_name = 'sst_customers_view' + self.table_name = "sst_customers_view" end diff --git a/test/models/sqlserver/dollar_table_name.rb b/test/models/sqlserver/dollar_table_name.rb index 8b20e5275..ba0fc1596 100644 --- a/test/models/sqlserver/dollar_table_name.rb +++ b/test/models/sqlserver/dollar_table_name.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestDollarTableName < ActiveRecord::Base - self.table_name = 'sst_my$strange_table' + self.table_name = "sst_my$strange_table" end diff --git a/test/models/sqlserver/edge_schema.rb b/test/models/sqlserver/edge_schema.rb index 30c79ad54..40d03c3ab 100644 --- a/test/models/sqlserver/edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -2,7 +2,7 @@ class SSTestEdgeSchema < ActiveRecord::Base - self.table_name = 'sst_edge_schemas' + self.table_name = "sst_edge_schemas" def with_spaces read_attribute :'with spaces' diff --git a/test/models/sqlserver/fk_has_fk.rb b/test/models/sqlserver/fk_has_fk.rb index 95486c232..6214b5055 100644 --- a/test/models/sqlserver/fk_has_fk.rb +++ b/test/models/sqlserver/fk_has_fk.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestHasFk < ActiveRecord::Base - self.table_name = 'sst_has_fks' + self.table_name = "sst_has_fks" end diff --git a/test/models/sqlserver/fk_has_pk.rb b/test/models/sqlserver/fk_has_pk.rb index 1bd2506e8..814e22480 100644 --- a/test/models/sqlserver/fk_has_pk.rb +++ b/test/models/sqlserver/fk_has_pk.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestHasPk < ActiveRecord::Base - self.table_name = 'sst_has_pks' + self.table_name = "sst_has_pks" end diff --git a/test/models/sqlserver/natural_pk_data.rb b/test/models/sqlserver/natural_pk_data.rb index 01cfdca06..9da2a6c11 100644 --- a/test/models/sqlserver/natural_pk_data.rb +++ b/test/models/sqlserver/natural_pk_data.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true class SSTestNaturalPkData < ActiveRecord::Base - self.table_name = 'sst_natural_pk_data' - self.primary_key = 'legacy_id' + self.table_name = "sst_natural_pk_data" + self.primary_key = "legacy_id" end diff --git a/test/models/sqlserver/natural_pk_int_data.rb b/test/models/sqlserver/natural_pk_int_data.rb index 4eb770809..294922e0f 100644 --- a/test/models/sqlserver/natural_pk_int_data.rb +++ b/test/models/sqlserver/natural_pk_int_data.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestNaturalPkIntData < ActiveRecord::Base - self.table_name = 'sst_natural_pk_int_data' + self.table_name = "sst_natural_pk_int_data" end diff --git a/test/models/sqlserver/no_pk_data.rb b/test/models/sqlserver/no_pk_data.rb index b9ef19ba3..1a5d15d36 100644 --- a/test/models/sqlserver/no_pk_data.rb +++ b/test/models/sqlserver/no_pk_data.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestNoPkData < ActiveRecord::Base - self.table_name = 'sst_no_pk_data' + self.table_name = "sst_no_pk_data" end diff --git a/test/models/sqlserver/object_default.rb b/test/models/sqlserver/object_default.rb index 5ba7d4912..728c4a802 100644 --- a/test/models/sqlserver/object_default.rb +++ b/test/models/sqlserver/object_default.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestObjectDefault < ActiveRecord::Base - self.table_name = 'sst_defaultobjects' + self.table_name = "sst_defaultobjects" end diff --git a/test/models/sqlserver/quoted_table.rb b/test/models/sqlserver/quoted_table.rb index bee44dd9a..e387bb3df 100644 --- a/test/models/sqlserver/quoted_table.rb +++ b/test/models/sqlserver/quoted_table.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true class SSTestQuotedTable < ActiveRecord::Base - self.table_name = '[sst_quoted-table]' + self.table_name = "[sst_quoted-table]" end class SSTestQuotedTableUser < ActiveRecord::Base - self.table_name = '[dbo].[sst_quoted-table]' + self.table_name = "[dbo].[sst_quoted-table]" end diff --git a/test/models/sqlserver/quoted_view_1.rb b/test/models/sqlserver/quoted_view_1.rb index 587f28691..5e816ae8c 100644 --- a/test/models/sqlserver/quoted_view_1.rb +++ b/test/models/sqlserver/quoted_view_1.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestQuotedView1 < ActiveRecord::Base - self.table_name = 'sst_quoted-view1' + self.table_name = "sst_quoted-view1" end diff --git a/test/models/sqlserver/quoted_view_2.rb b/test/models/sqlserver/quoted_view_2.rb index d993c274c..be9d62f46 100644 --- a/test/models/sqlserver/quoted_view_2.rb +++ b/test/models/sqlserver/quoted_view_2.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestQuotedView2 < ActiveRecord::Base - self.table_name = 'sst_quoted-view2' + self.table_name = "sst_quoted-view2" end diff --git a/test/models/sqlserver/sst_memory.rb b/test/models/sqlserver/sst_memory.rb index 5abbfd2e6..b4d26e602 100644 --- a/test/models/sqlserver/sst_memory.rb +++ b/test/models/sqlserver/sst_memory.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTMemory < ActiveRecord::Base - self.table_name = 'sst_memory' + self.table_name = "sst_memory" end diff --git a/test/models/sqlserver/string_default.rb b/test/models/sqlserver/string_default.rb index d797a401e..f4c2db47c 100644 --- a/test/models/sqlserver/string_default.rb +++ b/test/models/sqlserver/string_default.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestStringDefault < ActiveRecord::Base - self.table_name = 'sst_string_defaults' + self.table_name = "sst_string_defaults" end diff --git a/test/models/sqlserver/string_defaults_big_view.rb b/test/models/sqlserver/string_defaults_big_view.rb index 50bfcbb43..af8e6da6d 100644 --- a/test/models/sqlserver/string_defaults_big_view.rb +++ b/test/models/sqlserver/string_defaults_big_view.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestStringDefaultsBigView < ActiveRecord::Base - self.table_name = 'sst_string_defaults_big_view' + self.table_name = "sst_string_defaults_big_view" end diff --git a/test/models/sqlserver/string_defaults_view.rb b/test/models/sqlserver/string_defaults_view.rb index 6bf3c8b6b..4bcb4fe60 100644 --- a/test/models/sqlserver/string_defaults_view.rb +++ b/test/models/sqlserver/string_defaults_view.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestStringDefaultsView < ActiveRecord::Base - self.table_name = 'sst_string_defaults_view' + self.table_name = "sst_string_defaults_view" end diff --git a/test/models/sqlserver/tinyint_pk.rb b/test/models/sqlserver/tinyint_pk.rb index ca52f8de5..55c05c1a4 100644 --- a/test/models/sqlserver/tinyint_pk.rb +++ b/test/models/sqlserver/tinyint_pk.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestTinyintPk < ActiveRecord::Base - self.table_name = 'sst_tinyint_pk' + self.table_name = "sst_tinyint_pk" end diff --git a/test/models/sqlserver/trigger.rb b/test/models/sqlserver/trigger.rb index 50e4942be..3293cb6eb 100644 --- a/test/models/sqlserver/trigger.rb +++ b/test/models/sqlserver/trigger.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true class SSTestTrigger < ActiveRecord::Base - self.table_name = 'sst_table_with_trigger' + self.table_name = "sst_table_with_trigger" end class SSTestTriggerUuid < ActiveRecord::Base - self.table_name = 'sst_table_with_uuid_trigger' + self.table_name = "sst_table_with_uuid_trigger" end diff --git a/test/models/sqlserver/trigger_history.rb b/test/models/sqlserver/trigger_history.rb index c358d935d..d93bc4384 100644 --- a/test/models/sqlserver/trigger_history.rb +++ b/test/models/sqlserver/trigger_history.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestTriggerHistory < ActiveRecord::Base - self.table_name = 'sst_table_with_trigger_history' + self.table_name = "sst_table_with_trigger_history" end diff --git a/test/models/sqlserver/upper.rb b/test/models/sqlserver/upper.rb index 05432b476..0fa400977 100644 --- a/test/models/sqlserver/upper.rb +++ b/test/models/sqlserver/upper.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestUpper < ActiveRecord::Base - self.table_name = 'sst_upper_tests' + self.table_name = "sst_upper_tests" end diff --git a/test/models/sqlserver/uppered.rb b/test/models/sqlserver/uppered.rb index bb9dce777..9d025b0ab 100644 --- a/test/models/sqlserver/uppered.rb +++ b/test/models/sqlserver/uppered.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestUppered < ActiveRecord::Base - self.table_name = 'SST_UPPER_TESTS' + self.table_name = "SST_UPPER_TESTS" end diff --git a/test/models/sqlserver/uuid.rb b/test/models/sqlserver/uuid.rb index e6eea62e3..91da8fd9a 100644 --- a/test/models/sqlserver/uuid.rb +++ b/test/models/sqlserver/uuid.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true class SSTestUuid < ActiveRecord::Base - self.table_name = 'sst_uuids' + self.table_name = "sst_uuids" end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index eefcb363d..35b018b51 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -45,28 +45,28 @@ # Edge Cases - 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| + 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| t.primary_key_nonclustered :id t.string :name t.timestamps end end - create_table 'sst_bookings', force: true do |t| + create_table "sst_bookings", force: true do |t| t.string :name t.datetime2 :created_at, null: false t.datetime2 :updated_at, null: false end - create_table 'sst_uuids', force: true, id: :uuid do |t| + create_table "sst_uuids", force: true, id: :uuid do |t| t.string :name - t.uuid :other_uuid, default: 'NEWID()' + t.uuid :other_uuid, default: "NEWID()" t.uuid :uuid_nil_default, default: nil end - create_table 'sst_my$strange_table', force: true do |t| + create_table "sst_my$strange_table", force: true do |t| t.string :name end @@ -79,7 +79,7 @@ t.string :name end - create_table 'sst_quoted-table', force: true do |t| + create_table "sst_quoted-table", force: true do |t| end 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]" @@ -88,18 +88,18 @@ create_table :sst_string_defaults, force: true do |t| t.column :string_with_null_default, :string, default: nil - t.column :string_with_pretend_null_one, :string, default: 'null' - t.column :string_with_pretend_null_two, :string, default: '(null)' - t.column :string_with_pretend_null_three, :string, default: 'NULL' - t.column :string_with_pretend_null_four, :string, default: '(NULL)' - t.column :string_with_pretend_paren_three, :string, default: '(3)' + t.column :string_with_pretend_null_one, :string, default: "null" + t.column :string_with_pretend_null_two, :string, default: "(null)" + t.column :string_with_pretend_null_three, :string, default: "NULL" + t.column :string_with_pretend_null_four, :string, default: "(NULL)" + t.column :string_with_pretend_paren_three, :string, default: "(3)" t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line." end create_table :sst_edge_schemas, force: true do |t| t.string :description - t.column 'crazy]]quote', :string - t.column 'with spaces', :string + t.column "crazy]]quote", :string + t.column "with spaces", :string end execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_data') DROP TABLE sst_natural_pk_data" @@ -132,7 +132,7 @@ execute "DROP DEFAULT [sst_getdateobject];" rescue nil execute "CREATE DEFAULT [sst_getdateobject] AS getdate();" rescue nil - create_table 'sst_defaultobjects', force: true do |t| + create_table "sst_defaultobjects", force: true do |t| t.string :name t.date :date end diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb index 1debd57db..c695b2147 100644 --- a/test/support/core_ext/query_cache.rb +++ b/test/support/core_ext/query_cache.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -require 'active_record/connection_adapters/sqlserver_adapter' +require "active_record/connection_adapters/sqlserver_adapter" module SqlIgnoredCache extend ActiveSupport::Concern diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index 4b8a3c08b..a1641383f 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -6,15 +6,15 @@ module SQLServer extend self def schema_root - File.join ARTest::SQLServer.test_root_sqlserver, 'schema' + File.join ARTest::SQLServer.test_root_sqlserver, "schema" end def schema_file - File.join schema_root, 'sqlserver_specific_schema.rb' + File.join schema_root, "sqlserver_specific_schema.rb" end def schema_datatypes_2012_file - File.join schema_root, 'datatypes', '2012.sql' + File.join schema_root, "datatypes", "2012.sql" end def load_schema diff --git a/test/support/minitest_sqlserver.rb b/test/support/minitest_sqlserver.rb index 4e5574ef0..3fb0c9ed0 100644 --- a/test/support/minitest_sqlserver.rb +++ b/test/support/minitest_sqlserver.rb @@ -1,3 +1,3 @@ # frozen_string_literal: true -require 'minitest-spec-rails/init/active_support' +require "minitest-spec-rails/init/active_support" diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index a495474f2..fe4421131 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -6,27 +6,27 @@ module SQLServer extend self def root_sqlserver - File.expand_path File.join(File.dirname(__FILE__), '..', '..') + File.expand_path File.join(File.dirname(__FILE__), "..", "..") end def test_root_sqlserver - File.join root_sqlserver, 'test' + File.join root_sqlserver, "test" end def root_activerecord - File.join Gem.loaded_specs['rails'].full_gem_path, 'activerecord' + File.join Gem.loaded_specs["rails"].full_gem_path, "activerecord" end def root_activerecord_lib - File.join root_activerecord, 'lib' + File.join root_activerecord, "lib" end def root_activerecord_test - File.join root_activerecord, 'test' + File.join root_activerecord, "test" end def test_load_paths - ['lib', 'test', root_activerecord_lib, root_activerecord_test] + ["lib", "test", root_activerecord_lib, root_activerecord_test] end def add_to_load_paths! @@ -34,15 +34,15 @@ def add_to_load_paths! end def migrations_root - File.join test_root_sqlserver, 'migrations' + File.join test_root_sqlserver, "migrations" end def arconfig_file - File.join test_root_sqlserver, 'config.yml' + File.join test_root_sqlserver, "config.yml" end def arconfig_file_env! - ENV['ARCONFIG'] = arconfig_file + ENV["ARCONFIG"] = arconfig_file end end diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index db04b5bba..413faa576 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -1,24 +1,24 @@ # frozen_string_literal: true -SQLSERVER_HELPER = 'test/cases/helper_sqlserver.rb' -SQLSERVER_COERCED = 'test/cases/coerced_tests.rb' +SQLSERVER_HELPER = "test/cases/helper_sqlserver.rb" +SQLSERVER_COERCED = "test/cases/coerced_tests.rb" def env_ar_test_files - return unless ENV['TEST_FILES_AR'] && !ENV['TEST_FILES_AR'].empty? + return unless ENV["TEST_FILES_AR"] && !ENV["TEST_FILES_AR"].empty? @env_ar_test_files ||= begin - ENV['TEST_FILES_AR'].split(',').map { |file| + ENV["TEST_FILES_AR"].split(",").map { |file| File.join ARTest::SQLServer.root_activerecord, file.strip }.sort end end def env_test_files - return unless ENV['TEST_FILES'] && !ENV['TEST_FILES'].empty? - @env_test_files ||= ENV['TEST_FILES'].split(',').map(&:strip) + return unless ENV["TEST_FILES"] && !ENV["TEST_FILES"].empty? + @env_test_files ||= ENV["TEST_FILES"].split(",").map(&:strip) end def sqlserver_cases - @sqlserver_cases ||= Dir.glob('test/cases/*_test_sqlserver.rb') + @sqlserver_cases ||= Dir.glob("test/cases/*_test_sqlserver.rb") end def ar_cases @@ -32,9 +32,9 @@ def test_files [SQLSERVER_HELPER] + env_ar_test_files elsif env_test_files env_test_files - elsif ENV['ONLY_SQLSERVER'] + elsif ENV["ONLY_SQLSERVER"] sqlserver_cases - elsif ENV['ONLY_ACTIVERECORD'] + elsif ENV["ONLY_ACTIVERECORD"] [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED]) else [SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED] + sqlserver_cases) diff --git a/test/support/test_in_memory_oltp.rb b/test/support/test_in_memory_oltp.rb index ba169951c..88c918c92 100644 --- a/test/support/test_in_memory_oltp.rb +++ b/test/support/test_in_memory_oltp.rb @@ -1,16 +1,16 @@ # frozen_string_literal: true -if ENV['IN_MEMORY_OLTP'] - require 'config' - require 'active_record' - require 'support/config' - require 'support/connection' +if ENV["IN_MEMORY_OLTP"] + require "config" + require "active_record" + require "support/config" + require "support/connection" ARTest.connect if ActiveRecord::Base.connection.supports_in_memory_oltp? - puts 'Configuring In-Memory OLTP...' - inmem_file = ARTest::SQLServer.test_root_sqlserver, 'schema', 'enable-in-memory-oltp.sql' + puts "Configuring In-Memory OLTP..." + inmem_file = ARTest::SQLServer.test_root_sqlserver, "schema", "enable-in-memory-oltp.sql" inmem_sql = File.read File.join(inmem_file) ActiveRecord::Base.connection.execute(inmem_sql) end From 237ff36f8aee7075e278f03b7cb5779bdfdb00d9 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 19:58:30 +0100 Subject: [PATCH 0888/1412] Rubocop: Enable Layout/EmptyLinesAroundClassBody cop --- .../sqlserver/schema_creation.rb | 2 -- .../connection_adapters/sqlserver/schema_dumper.rb | 2 -- .../sqlserver/sql_type_metadata.rb | 2 -- .../sqlserver/type/big_integer.rb | 2 -- .../connection_adapters/sqlserver/type/binary.rb | 2 -- .../connection_adapters/sqlserver/type/boolean.rb | 2 -- .../connection_adapters/sqlserver/type/char.rb | 2 -- .../connection_adapters/sqlserver/type/data.rb | 2 -- .../connection_adapters/sqlserver/type/date.rb | 2 -- .../connection_adapters/sqlserver/type/datetime.rb | 1 - .../sqlserver/type/datetime2.rb | 2 -- .../sqlserver/type/datetimeoffset.rb | 2 -- .../connection_adapters/sqlserver/type/decimal.rb | 2 -- .../connection_adapters/sqlserver/type/float.rb | 2 -- .../connection_adapters/sqlserver/type/integer.rb | 2 -- .../connection_adapters/sqlserver/type/json.rb | 1 - .../connection_adapters/sqlserver/type/money.rb | 2 -- .../connection_adapters/sqlserver/type/real.rb | 2 -- .../sqlserver/type/small_integer.rb | 2 -- .../sqlserver/type/small_money.rb | 2 -- .../sqlserver/type/smalldatetime.rb | 2 -- .../connection_adapters/sqlserver/type/string.rb | 2 -- .../connection_adapters/sqlserver/type/text.rb | 2 -- .../connection_adapters/sqlserver/type/time.rb | 2 -- .../sqlserver/type/timestamp.rb | 2 -- .../sqlserver/type/tiny_integer.rb | 2 -- .../sqlserver/type/unicode_char.rb | 2 -- .../sqlserver/type/unicode_string.rb | 2 -- .../sqlserver/type/unicode_text.rb | 2 -- .../sqlserver/type/unicode_varchar.rb | 2 -- .../sqlserver/type/unicode_varchar_max.rb | 2 -- .../connection_adapters/sqlserver/type/uuid.rb | 2 -- .../sqlserver/type/varbinary.rb | 2 -- .../sqlserver/type/varbinary_max.rb | 2 -- .../connection_adapters/sqlserver/type/varchar.rb | 2 -- .../sqlserver/type/varchar_max.rb | 2 -- .../connection_adapters/sqlserver/utils.rb | 2 -- .../connection_adapters/sqlserver_adapter.rb | 1 - .../connection_adapters/sqlserver_column.rb | 2 -- .../tasks/sqlserver_database_tasks.rb | 2 -- lib/arel/visitors/sqlserver.rb | 1 - test/cases/adapter_test_sqlserver.rb | 2 -- test/cases/coerced_tests.rb | 2 -- test/cases/column_test_sqlserver.rb | 2 -- test/cases/connection_test_sqlserver.rb | 2 -- test/cases/execute_procedure_test_sqlserver.rb | 2 -- test/cases/fetch_test_sqlserver.rb | 2 -- .../fully_qualified_identifier_test_sqlserver.rb | 2 -- test/cases/helper_sqlserver.rb | 2 -- test/cases/index_test_sqlserver.rb | 2 -- test/cases/json_test_sqlserver.rb | 2 -- test/cases/migration_test_sqlserver.rb | 2 -- test/cases/order_test_sqlserver.rb | 3 --- test/cases/pessimistic_locking_test_sqlserver.rb | 2 -- test/cases/rake_test_sqlserver.rb | 14 -------------- test/cases/schema_dumper_test_sqlserver.rb | 4 ---- test/cases/schema_test_sqlserver.rb | 3 --- test/cases/scratchpad_test_sqlserver.rb | 2 -- test/cases/showplan_test_sqlserver.rb | 2 -- test/cases/specific_schema_test_sqlserver.rb | 2 -- test/cases/transaction_test_sqlserver.rb | 2 -- test/cases/utils_test_sqlserver.rb | 2 -- test/cases/uuid_test_sqlserver.rb | 2 -- .../1_table_will_never_be_created.rb | 2 -- test/models/sqlserver/edge_schema.rb | 2 -- 65 files changed, 142 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 361e7bc53..4f65bd228 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer class SchemaCreation < AbstractAdapter::SchemaCreation - private def visit_TableDefinition(o) @@ -63,7 +62,6 @@ def options_include_default?(options) def options_primary_key_with_nil_default?(options) options[:primary_key] && options.include?(:default) && options[:default].nil? end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 198bc293a..99b4dab8d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer class SchemaDumper < ConnectionAdapters::SchemaDumper - SQLSEVER_NO_LIMIT_TYPES = [ "text", "ntext", @@ -32,7 +31,6 @@ def schema_collation(column) def default_primary_key?(column) super && column.is_primary? && column.is_identity? end - end end end 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 09b59ebd2..40f77714c 100644 --- a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer class SqlTypeMetadata < ActiveRecord::ConnectionAdapters::SqlTypeMetadata - def initialize(**kwargs) @sqlserver_options = kwargs.extract!(:sqlserver_options) super(**kwargs) @@ -15,7 +14,6 @@ def initialize(**kwargs) def attributes_for_hash super + [@sqlserver_options] end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb index 083b736be..2cb2978ab 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb @@ -5,11 +5,9 @@ module ConnectionAdapters module SQLServer module Type class BigInteger < Integer - def sqlserver_type "bigint" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/binary.rb b/lib/active_record/connection_adapters/sqlserver/type/binary.rb index a700846a4..1f14a4078 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/binary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/binary.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Binary < ActiveRecord::Type::Binary - def type :binary_basic end @@ -16,7 +15,6 @@ def sqlserver_type type end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb index 1b56ebc16..6c31f390d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/boolean.rb @@ -5,11 +5,9 @@ module ConnectionAdapters module SQLServer module Type class Boolean < ActiveRecord::Type::Boolean - def sqlserver_type "bit" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 96832ad52..55b1eae4b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Char < String - def type :char end @@ -27,7 +26,6 @@ def quoted(value) return value.quoted_id if value.respond_to?(:quoted_id) Utils.quote_string_single(value) end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 599b49ee7..3cfaf6ddf 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Data - attr_reader :value, :type def initialize(value, type) @@ -29,7 +28,6 @@ def eql?(other) self.class == other.class && self.value == other.value end alias :== :eql? - 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 43896458d..f8993e3be 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Date < ActiveRecord::Type::Date - def sqlserver_type "date" end @@ -39,7 +38,6 @@ def fast_string_to_date(string) def fast_string_to_date_format ::Date::DATE_FORMATS[:_sqlserver_dateformat] end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 36c861f27..5f05ef907 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class DateTime < ActiveRecord::Type::DateTime - include TimeValueFractional def sqlserver_type diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb index 77ab42f22..bb6867416 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb @@ -5,13 +5,11 @@ module ConnectionAdapters module SQLServer module Type class DateTime2 < DateTime - include TimeValueFractional2 def sqlserver_type "datetime2(#{precision.to_i})" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb index 71f3d3529..195e5c973 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class DateTimeOffset < DateTime2 - def type :datetimeoffset end @@ -17,7 +16,6 @@ def sqlserver_type def quoted(value) Utils.quote_string_single(value) end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb index 7bf421f71..e7b89f32d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Decimal < ActiveRecord::Type::Decimal - def sqlserver_type "decimal".yield_self do |type| type += "(#{precision.to_i},#{scale.to_i})" if precision || scale @@ -16,7 +15,6 @@ def sqlserver_type def type_cast_for_schema(value) value.is_a?(BigDecimal) ? value.to_s : value.inspect end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/float.rb b/lib/active_record/connection_adapters/sqlserver/type/float.rb index aecb7438d..95589bd33 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/float.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/float.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Float < ActiveRecord::Type::Float - def type :float end @@ -13,7 +12,6 @@ def type def sqlserver_type "float" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/integer.rb b/lib/active_record/connection_adapters/sqlserver/type/integer.rb index 9b4d63725..c6cabcd8a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/integer.rb @@ -5,11 +5,9 @@ module ConnectionAdapters module SQLServer module Type class Integer < ActiveRecord::Type::Integer - def sqlserver_type "int" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/json.rb b/lib/active_record/connection_adapters/sqlserver/type/json.rb index d942cd3a1..5a90b3e38 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/json.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/json.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Json < ActiveRecord::Type::Json - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/money.rb b/lib/active_record/connection_adapters/sqlserver/type/money.rb index 2906859e3..8997832dd 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/money.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Money < Decimal - def initialize(**args) super @precision = 19 @@ -19,7 +18,6 @@ def type def sqlserver_type "money" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/real.rb b/lib/active_record/connection_adapters/sqlserver/type/real.rb index 7a5a36922..dbeef954c 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/real.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/real.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Real < Float - def type :real end @@ -13,7 +12,6 @@ def type def sqlserver_type "real" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb index 1dff23244..1b408c101 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb @@ -5,11 +5,9 @@ module ConnectionAdapters module SQLServer module Type class SmallInteger < Integer - def sqlserver_type "smallint" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb index 0ecfa00c8..f69c5dc00 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/small_money.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class SmallMoney < Money - def initialize(**args) super @precision = 10 @@ -19,7 +18,6 @@ def type def sqlserver_type "smallmoney" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb index 99dc70a8a..db1a1ba78 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class SmallDateTime < DateTime - def type :smalldatetime end @@ -23,7 +22,6 @@ def fast_string_to_time_format def apply_seconds_precision(value) value.change usec: 0 if value end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/string.rb b/lib/active_record/connection_adapters/sqlserver/type/string.rb index 110bcebde..97f15bad0 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/string.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/string.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class String < ActiveRecord::Type::String - def changed_in_place?(raw_old_value, new_value) if raw_old_value.is_a?(Data) raw_old_value.value != new_value @@ -13,7 +12,6 @@ def changed_in_place?(raw_old_value, new_value) super end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/text.rb b/lib/active_record/connection_adapters/sqlserver/type/text.rb index 087b8fb85..248e27de6 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/text.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Text < VarcharMax - def type :text_basic end @@ -13,7 +12,6 @@ def type def sqlserver_type "text" end - 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 38f2354c9..34adf6296 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Time < ActiveRecord::Type::Time - include TimeValueFractional2 def serialize(value) @@ -45,7 +44,6 @@ def cast_value(value) def fractional_scale precision end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb index 15125e849..8eead81a3 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Timestamp < Binary - def type :ss_timestamp end @@ -13,7 +12,6 @@ def type def sqlserver_type "timestamp" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb index 4370bc574..b90d94ffb 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class TinyInteger < Integer - def sqlserver_type "tinyint" end @@ -19,7 +18,6 @@ def max_value def min_value 0 end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb index 0eb364234..8e5ff566e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class UnicodeChar < UnicodeString - def type :nchar end @@ -16,7 +15,6 @@ def sqlserver_type type end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb index c2e376c89..3a6ac257e 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb @@ -5,8 +5,6 @@ module ConnectionAdapters module SQLServer module Type class UnicodeString < String - - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb index 38139f2b7..de0be9af4 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class UnicodeText < UnicodeVarcharMax - def type :ntext end @@ -13,7 +12,6 @@ def type def sqlserver_type "ntext" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb index a100b6baa..53551c442 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class UnicodeVarchar < UnicodeChar - def initialize(**args) super @limit = 4000 if @limit.to_i == 0 @@ -21,7 +20,6 @@ def sqlserver_type type end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb index 160a1f80a..aee2ddc7b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class UnicodeVarcharMax < UnicodeVarchar - def initialize(**args) super @limit = 2_147_483_647 @@ -18,7 +17,6 @@ def type def sqlserver_type "nvarchar(max)" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index 07b9682a7..387796c12 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Uuid < String - ACCEPTABLE_UUID = %r{\A\{?([a-fA-F0-9]{4}-?){8}\}?\z}x alias_method :serialize, :deserialize @@ -30,7 +29,6 @@ def cast(value) def quoted(value) Utils.quote_string_single(value) if value end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb index c00473dd5..2caa1228b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Varbinary < Binary - def initialize(**args) super @limit = 8000 if @limit.to_i == 0 @@ -21,7 +20,6 @@ def sqlserver_type type end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb index 21476e544..79291ba63 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class VarbinaryMax < Varbinary - def initialize(**args) super @limit = 2_147_483_647 @@ -18,7 +17,6 @@ def type def sqlserver_type "varbinary(max)" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb index 0e08c4e75..466a3a43a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class Varchar < Char - def initialize(**args) super @limit = 8000 if @limit.to_i == 0 @@ -21,7 +20,6 @@ def sqlserver_type type end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb index 1582e500d..75cb2260d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module Type class VarcharMax < Varchar - def initialize(**args) super @limit = 2_147_483_647 @@ -18,7 +17,6 @@ def type def sqlserver_type "varchar(max)" end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index a5b74faa0..ebe3bc0b9 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -13,7 +13,6 @@ module Utils # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils. # class Name - SEPARATOR = "." UNQUOTED_SCANNER = /\]?\./ QUOTED_SCANNER = /\A\[.*?\]\./ @@ -113,7 +112,6 @@ def unquote(part) def parts @parts end - end extend self diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 2f58eb5c2..c4523c885 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -34,7 +34,6 @@ module ActiveRecord module ConnectionAdapters class SQLServerAdapter < AbstractAdapter - include SQLServer::Version, SQLServer::Quoting, SQLServer::DatabaseStatements, diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 535a75967..b03e8e7e4 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -3,7 +3,6 @@ module ActiveRecord module ConnectionAdapters class SQLServerColumn < Column - def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **sqlserver_options) @sqlserver_options = sqlserver_options super @@ -28,7 +27,6 @@ def is_utf8? def case_sensitive? collation && collation.match(/_CS/) end - end end end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index b95297b28..a43b25e88 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -9,7 +9,6 @@ module ActiveRecord module Tasks class SQLServerDatabaseTasks - DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" delegate :connection, :establish_connection, :clear_active_connections!, @@ -93,7 +92,6 @@ def default_collation def establish_master_connection establish_connection configuration.merge("database" => "master") end - end module DatabaseTasksSQLServer diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 04daae5da..0be8336f9 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -3,7 +3,6 @@ module Arel module Visitors class SQLServer < Arel::Visitors::ToSql - OFFSET = " OFFSET " ROWS = " ROWS" FETCH = " FETCH NEXT " diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 7e2161513..480fd5614 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -8,7 +8,6 @@ require "models/minimalistic" class AdapterTestSQLServer < ActiveRecord::TestCase - fixtures :tasks let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } @@ -492,6 +491,5 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end end - end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0c52cd88b..f038d1510 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -580,7 +580,6 @@ def test_sqlserver_charset end end end - end class DatabaseTasksCollationTest < ActiveRecord::TestCase @@ -1533,7 +1532,6 @@ class EnumTest < ActiveRecord::TestCase require "models/task" class QueryCacheExpiryTest < ActiveRecord::TestCase - # SQL Server does not support skipping or upserting duplicates. coerce_tests! :test_insert_all def test_insert_all_coerced diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 6b57c7479..b2a46f9c6 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class ColumnTestSQLServer < ActiveRecord::TestCase - it "#table_name" do assert SSTestDatatype.columns.all? { |c| c.table_name == "sst_datatypes" } assert SSTestCustomersView.columns.all? { |c| c.table_name == "customers" } @@ -838,5 +837,4 @@ def assert_obj_set_and_save(attribute, value) end end - end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 7309506c4..2df39822f 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -5,7 +5,6 @@ require "models/topic" class ConnectionTestSQLServer < ActiveRecord::TestCase - self.use_transactional_tests = false fixtures :topics, :accounts @@ -69,5 +68,4 @@ def disconnect_raw_connection! connection.raw_connection.close rescue nil end end - end diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 46d1774c3..cf270482e 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase - it "execute a simple procedure" do tables = ActiveRecord::Base.execute_procedure :sp_tables assert_instance_of Array, tables @@ -42,5 +41,4 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase date_base = connection.select_value("select GETUTCDATE()") assert_equal date_base.change(usec: 0), date_proc.change(usec: 0) end - end diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 4f32b9db2..bed448631 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -4,7 +4,6 @@ require "models/book" class FetchTestSqlserver < ActiveRecord::TestCase - let(:books) { @books } before { create_10_books } @@ -54,6 +53,5 @@ def create_10_books Book.delete_all @books = (1..10).map { |i| Book.create! name: "Name-#{i}" } end - end diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index 33d042330..303efa1e1 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase - describe "local server" do it "should use table name in select projections" do @@ -74,5 +73,4 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase end end - end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4c1d3dcfd..4435eb95b 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -16,7 +16,6 @@ module ActiveRecord class TestCase < ActiveSupport::TestCase - SQLServer = ActiveRecord::ConnectionAdapters::SQLServer include ARTest::SQLServer::CoerceableTest, @@ -50,7 +49,6 @@ def with_use_output_inserted_disabled ensure klass.use_output_inserted = true end - end end diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index fbea49b73..b66e3acd2 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class IndexTestSQLServer < ActiveRecord::TestCase - before do connection.create_table(:testings) do |t| t.column :foo, :string, limit: 100 @@ -45,5 +44,4 @@ class IndexTestSQLServer < ActiveRecord::TestCase connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])" connection.add_index "testings", "first_name_upper" end - end diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index 66ab4180d..176add811 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -4,7 +4,6 @@ if ActiveRecord::Base.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" } @@ -29,6 +28,5 @@ class JsonTestSQLServer < ActiveRecord::TestCase it "can use JSON_VALUE function" do _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2 end - end end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 1e54b831d..f27dcccb3 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -4,7 +4,6 @@ require "models/person" class MigrationTestSQLServer < ActiveRecord::TestCase - describe "For transactions" do before do @@ -69,5 +68,4 @@ class MigrationTestSQLServer < ActiveRecord::TestCase end end - end diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index ae8bcd6ed..712fff1a2 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -4,7 +4,6 @@ require "models/post" class OrderTestSQLServer < ActiveRecord::TestCase - fixtures :posts it "not mangel complex order clauses" do @@ -144,6 +143,4 @@ class OrderTestSQLServer < ActiveRecord::TestCase assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end - - end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index b7bfadc8a..a9022816c 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -5,7 +5,6 @@ require "models/reader" class PessimisticLockingTestSQLServer < ActiveRecord::TestCase - fixtures :people, :readers before do @@ -105,5 +104,4 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end end - end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index bb5fa8b1b..fac7f34f5 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class SQLServerRakeTest < ActiveRecord::TestCase - self.use_transactional_tests = false cattr_accessor :azure_skip @@ -36,11 +35,9 @@ def reconnect connection.drop_database(new_database) rescue nil end end - end class SQLServerRakeCreateTest < SQLServerRakeTest - self.azure_skip = false it "establishes connection to database after create " do @@ -63,11 +60,9 @@ class SQLServerRakeCreateTest < SQLServerRakeTest message = capture(:stderr) { db_tasks.create configuration } _(message).must_match %r{activerecord_unittest_tasks.*already exists} end - end class SQLServerRakeDropTest < SQLServerRakeTest - self.azure_skip = false it "drops database and uses master" do @@ -82,11 +77,9 @@ class SQLServerRakeDropTest < SQLServerRakeTest message = capture(:stderr) { db_tasks.drop configuration.merge("database" => "doesnotexist") } _(message).must_match %r{'doesnotexist' does not exist} end - end class SQLServerRakePurgeTest < SQLServerRakeTest - before do quietly { db_tasks.create(configuration) } connection.create_table :users, force: true do |t| @@ -102,11 +95,9 @@ class SQLServerRakePurgeTest < SQLServerRakeTest _(connection.current_database).must_equal(new_database) _(connection.tables).wont_include "users" end - end class SQLServerRakeCharsetTest < SQLServerRakeTest - before do quietly { db_tasks.create(configuration) } end @@ -114,11 +105,9 @@ class SQLServerRakeCharsetTest < SQLServerRakeTest it "retrieves charset" do _(db_tasks.charset(configuration)).must_equal "iso_1" end - end class SQLServerRakeCollationTest < SQLServerRakeTest - before do quietly { db_tasks.create(configuration) } end @@ -126,11 +115,9 @@ class SQLServerRakeCollationTest < SQLServerRakeTest it "retrieves collation" do _(db_tasks.collation(configuration)).must_equal "SQL_Latin1_General_CP1_CI_AS" end - end class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest - let(:filename) { File.join ARTest::SQLServer.migrations_root, "structure.sql" } let(:filedata) { File.read(filename) } @@ -167,5 +154,4 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest db_tasks.load_schema configuration, :sql, filename _(connection.tables).must_include "users" end - end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 44cb73e02..d2b1b4406 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class SchemaDumperTestSQLServer < ActiveRecord::TestCase - before { all_tables } let(:all_tables) { ActiveRecord::Base.connection.tables } @@ -182,7 +181,6 @@ def assert_line(column_name, options={}) end class SchemaLine - LINE_PARSER = %r{t\.(\w+)\s+"(.*?)"[,\s+](.*)} attr_reader :line, @@ -230,8 +228,6 @@ def parse_options(opts) rescue SyntaxError {} end - end - end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 02244d358..0c45eb1c4 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class SchemaTestSQLServer < ActiveRecord::TestCase - describe "When table is dbo schema" do it "find primary key for tables with odd schema" do @@ -50,7 +49,5 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end end - - end diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb index a88cdb56d..ca1f72961 100644 --- a/test/cases/scratchpad_test_sqlserver.rb +++ b/test/cases/scratchpad_test_sqlserver.rb @@ -3,8 +3,6 @@ require "cases/helper_sqlserver" class ScratchpadTestSQLServer < ActiveRecord::TestCase - it "helps debug things" do end - end diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index b59e79ff9..7682c4408 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -4,7 +4,6 @@ require "models/car" class ShowplanTestSQLServer < ActiveRecord::TestCase - fixtures :cars describe "Unprepare previously prepared SQL" do @@ -75,5 +74,4 @@ def with_showplan_option(option) ensure ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = old_option end - end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index a11cf52e9..ddd6ad4b2 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class SpecificSchemaTestSQLServer < ActiveRecord::TestCase - after { SSTestEdgeSchema.delete_all } it "handle dollar symbols" do @@ -168,5 +167,4 @@ def quoted_id assert_equal "field_1", connection.columns("test.sst_schema_test_mulitple_schema").detect(&:is_primary?).name assert_equal "field_2", connection.columns("test2.sst_schema_test_mulitple_schema").detect(&:is_primary?).name end - end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index aa9f99532..56daf1350 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -5,7 +5,6 @@ require "models/developer" class TransactionTestSQLServer < ActiveRecord::TestCase - self.use_transactional_tests = false before { delete_ships } @@ -88,5 +87,4 @@ def delete_ships def assert_no_ships assert Ship.count.zero?, "Expected Ship to have no models but it did have:\n#{Ship.all.inspect}" end - end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index c89074a21..ea433d9dc 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class UtilsTestSQLServer < ActiveRecord::TestCase - it ".quote_string" do _(SQLServer::Utils.quote_string("I'll store this in C:\\Users")).must_equal "I''ll store this in C:\\Users" end @@ -127,5 +126,4 @@ class UtilsTestSQLServer < ActiveRecord::TestCase def extract_identifiers(name) SQLServer::Utils.extract_identifiers(name) end - end diff --git a/test/cases/uuid_test_sqlserver.rb b/test/cases/uuid_test_sqlserver.rb index a79a51e04..bcfdbe031 100644 --- a/test/cases/uuid_test_sqlserver.rb +++ b/test/cases/uuid_test_sqlserver.rb @@ -3,7 +3,6 @@ require "cases/helper_sqlserver" class SQLServerUuidTest < ActiveRecord::TestCase - let(:acceptable_uuid) { ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID } it "has a uuid primary key" do @@ -44,5 +43,4 @@ class SQLServerUuidTest < ActiveRecord::TestCase obj = with_use_output_inserted_disabled { SSTestUuid.create!(name: "😢") } _(obj.id).must_be :nil? end - end diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index 769ce7967..388f50da3 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class TableWillNeverBeCreated < ActiveRecord::Migration - def self.up create_table(:sqlserver_trans_table1) { } create_table(:sqlserver_trans_table2) { raise("HELL") } @@ -9,5 +8,4 @@ def self.up def self.down end - end diff --git a/test/models/sqlserver/edge_schema.rb b/test/models/sqlserver/edge_schema.rb index 40d03c3ab..9c7df7079 100644 --- a/test/models/sqlserver/edge_schema.rb +++ b/test/models/sqlserver/edge_schema.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class SSTestEdgeSchema < ActiveRecord::Base - self.table_name = "sst_edge_schemas" def with_spaces @@ -11,5 +10,4 @@ def with_spaces def with_spaces=(value) write_attribute :'with spaces', value end - end From e1040db361ab0b892a0901d8211df02260322813 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 20:29:26 +0100 Subject: [PATCH 0889/1412] Rubocop: Enable Layout/EmptyLines cop --- .../sqlserver/core_ext/attribute_methods.rb | 1 - .../sqlserver/database_statements.rb | 1 - .../connection_adapters/sqlserver/errors.rb | 1 - .../tasks/sqlserver_database_tasks.rb | 1 - lib/arel/visitors/sqlserver.rb | 1 - test/cases/coerced_tests.rb | 187 ------------------ test/cases/connection_test_sqlserver.rb | 1 - test/cases/fetch_test_sqlserver.rb | 1 - test/cases/schema_dumper_test_sqlserver.rb | 1 - test/cases/showplan_test_sqlserver.rb | 1 - test/cases/transaction_test_sqlserver.rb | 1 - 11 files changed, 197 deletions(-) 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 715472c81..3df31c56b 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 @@ -8,7 +8,6 @@ module SQLServer module CoreExt module AttributeMethods - private def attributes_for_update(attribute_names) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 98a2b9efe..17a6f9892 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -248,7 +248,6 @@ def newsequentialid_function select_value "SELECT NEWSEQUENTIALID()" end - protected def sql_for_insert(sql, pk, binds) diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 3ff5faec0..45c990a54 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -5,5 +5,4 @@ module ActiveRecord class DeadlockVictim < WrappedDatabaseException end - end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index a43b25e88..e436c05fe 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -78,7 +78,6 @@ def structure_load(filename, extra_flags) connection.execute File.read(filename) end - private def configuration diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 0be8336f9..4cf5ea790 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -9,7 +9,6 @@ class SQLServer < Arel::Visitors::ToSql FETCH0 = " FETCH FIRST (SELECT 0) " ROWS_ONLY = " ROWS ONLY" - private # SQLServer ToSql/Visitor (Overides) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f038d1510..ce0b09158 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2,8 +2,6 @@ require "cases/helper_sqlserver" - - require "models/event" class UniquenessValidationTest < ActiveRecord::TestCase # So sp_executesql swallows this exception. Run without prepared to see it. @@ -36,9 +34,6 @@ def test_validate_case_sensitive_uniqueness_by_default_coerced end end - - - require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase @@ -69,9 +64,6 @@ def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced end end - - - module ActiveRecord class AdapterTestWithoutTransaction < ActiveRecord::TestCase # SQL Server does not allow truncation of tables that are referenced by foreign key @@ -125,9 +117,6 @@ def test_truncate_tables_with_query_cache end end - - - require "models/topic" class AttributeMethodsTest < ActiveRecord::TestCase # Use IFF for boolean statement in SELECT @@ -147,9 +136,6 @@ def test_typecast_attribute_from_select_to_true_coerced end end - - - class BasicsTest < ActiveRecord::TestCase # Use square brackets as SQL Server escaped character coerce_tests! :test_column_names_are_escaped @@ -200,9 +186,6 @@ def test_update_date_time_attributes_with_default_timezone_local end end - - - class BelongsToAssociationsTest < ActiveRecord::TestCase # Since @client.firm is a single first/top, and we use FETCH the order clause is used. coerce_tests! :test_belongs_to_does_not_use_order_by @@ -227,9 +210,6 @@ def test_belongs_to_coerced end end - - - module ActiveRecord class BindParameterTest < ActiveRecord::TestCase # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper. @@ -257,9 +237,6 @@ def test_binds_are_logged_coerced end end - - - module ActiveRecord class InstrumentationTest < ActiveRecord::TestCase # Fix randomly failing test. The loading of the model's schema was affecting the test. @@ -271,9 +248,6 @@ def test_payload_name_on_load_coerced end end - - - class CalculationsTest < ActiveRecord::TestCase # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept @@ -317,9 +291,6 @@ def test_distinct_count_all_with_custom_select_and_order_coerced coerce_tests! :test_having_with_strong_parameters end - - - module ActiveRecord class Migration class ChangeSchemaTest < ActiveRecord::TestCase @@ -351,9 +322,6 @@ def test_create_table_with_defaults_coerce end end - - - module ActiveRecord module ConnectionAdapters class QuoteARBaseTest < ActiveRecord::TestCase @@ -374,9 +342,6 @@ def test_type_cast_ar_object_coerced end end - - - module ActiveRecord class Migration class ColumnAttributesTest < ActiveRecord::TestCase @@ -391,9 +356,6 @@ def test_add_column_without_limit_coerced end end - - - module ActiveRecord class Migration class ColumnsTest < ActiveRecord::TestCase @@ -433,9 +395,6 @@ def test_rename_nonexistent_column_coerced end end - - - class MigrationTest < ActiveRecord::TestCase # We do not have do the DecimalWithoutScale type. coerce_tests! :test_add_table_with_decimals @@ -475,9 +434,6 @@ def test_add_table_with_decimals_coerced coerce_tests! :test_internal_metadata_stores_environment end - - - class CoreTest < ActiveRecord::TestCase # I think fixtures are using the wrong time zone and the `:first` # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is @@ -485,9 +441,6 @@ class CoreTest < ActiveRecord::TestCase coerce_tests! :test_pretty_print_persisted end - - - module ActiveRecord module ConnectionAdapters # Just like PostgreSQLAdapter does. @@ -501,9 +454,6 @@ module ConnectionAdapters end end - - - module ActiveRecord # The original module is hardcoded for PostgreSQL/SQLite/MySQL tests. module DatabaseTasksSetupper @@ -644,9 +594,6 @@ class DatabaseTasksDropAllTest < ActiveRecord::TestCase end end - - - class DefaultScopingTest < ActiveRecord::TestCase # We are not doing order duplicate removal anymore. coerce_tests! :test_order_in_default_scope_should_not_prevail @@ -660,9 +607,6 @@ def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced end end - - - require "models/post" require "models/subscriber" class EachTest < ActiveRecord::TestCase @@ -691,9 +635,6 @@ def test_in_batches_should_quote_batch_order_coerced end end - - - class EagerAssociationTest < ActiveRecord::TestCase # Use LEN() vs length() function. coerce_tests! :test_count_with_include @@ -705,9 +646,6 @@ def test_count_with_include_coerced coerce_tests! %r{including association based on sql condition and no database column} end - - - require "models/topic" class FinderTest < ActiveRecord::TestCase # We have implicit ordering, via FETCH. @@ -756,9 +694,6 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced end end - - - module ActiveRecord class Migration class ForeignKeyTest < ActiveRecord::TestCase @@ -776,9 +711,6 @@ def test_add_on_delete_restrict_foreign_key_coerced end end - - - class HasOneAssociationsTest < ActiveRecord::TestCase # We use OFFSET/FETCH vs TOP. So we always have an order. coerce_tests! :test_has_one_does_not_use_order_by @@ -795,9 +727,6 @@ def test_has_one_coerced end end - - - class HasOneThroughAssociationsTest < ActiveRecord::TestCase # Asserted SQL to get one row different from original test. coerce_tests! :test_has_one_through_executes_limited_query @@ -809,10 +738,6 @@ def test_has_one_through_executes_limited_query_coerced end end - - - - require "models/company" class InheritanceTest < ActiveRecord::TestCase # Rails test required inserting to a identity column. @@ -834,17 +759,11 @@ def test_eager_load_belongs_to_primary_key_quoting_coerced end end - - - class LeftOuterJoinAssociationTest < ActiveRecord::TestCase # Uses || operator in SQL. Just trust core gets value out of this test. coerce_tests! :test_does_not_override_select end - - - require "models/developer" require "models/computer" class NestedRelationScopingTest < ActiveRecord::TestCase @@ -862,9 +781,6 @@ def test_merge_options_coerced end end - - - require "models/topic" class PersistenceTest < ActiveRecord::TestCase # Rails test required updating a identity column. @@ -889,9 +805,6 @@ def test_update_coerced end end - - - require "models/author" class UpdateAllTest < ActiveRecord::TestCase # Rails test required updating a identity column. @@ -909,9 +822,6 @@ def test_update_all_doesnt_ignore_order_coerced end end - - - require "models/topic" module ActiveRecord class PredicateBuilderTest < ActiveRecord::TestCase @@ -929,9 +839,6 @@ def test_registering_new_handlers_for_association_coerced end end - - - class PrimaryKeysTest < ActiveRecord::TestCase # SQL Server does not have query for release_savepoint coerce_tests! :test_create_without_primary_key_no_extra_query @@ -944,9 +851,6 @@ def test_create_without_primary_key_no_extra_query_coerced end end - - - require "models/task" class QueryCacheTest < ActiveRecord::TestCase # SQL Server adapter not in list of supported adapters in original test. @@ -984,9 +888,6 @@ def test_query_cached_even_when_types_are_reset_coerced end end - - - require "models/post" class RelationTest < ActiveRecord::TestCase # Use LEN vs LENGTH function. @@ -1028,7 +929,6 @@ def test_reorder_with_first_coerced 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. coerce_tests! :test_order_using_scoping @@ -1076,9 +976,6 @@ def test_reverse_arel_assoc_order_with_function_coerced end end - - - require "models/post" class SanitizeTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert @@ -1104,9 +1001,6 @@ def self.search_as_method(term) end end - - - class SchemaDumperTest < ActiveRecord::TestCase # We have precision to 38. coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal @@ -1133,25 +1027,16 @@ def test_schema_dump_includes_decimal_options_coerced end end - - - class SchemaDumperDefaultsTest < ActiveRecord::TestCase # These date formats do not match ours. We got these covered in our dumper tests. coerce_tests! :test_schema_dump_defaults_with_universally_supported_types end - - - class TestAdapterWithInvalidConnection < ActiveRecord::TestCase # We trust Rails on this since we do not want to install mysql. 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 @@ -1166,9 +1051,6 @@ def test_releasing_named_savepoints_coerced end end - - - require "models/tag" class TransactionIsolationTest < ActiveRecord::TestCase # SQL Server will lock the table for counts even when both @@ -1189,9 +1071,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. @@ -1208,9 +1087,6 @@ def test_does_not_assume_id_column_as_primary_key_coerced end end - - - class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase # We have a few view tables. use includes vs equality. coerce_tests! :test_views @@ -1219,9 +1095,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 @@ -1233,9 +1106,6 @@ def test_types_of_virtual_columns_are_not_changed_on_round_trip_coerced end end - - - class DateTimePrecisionTest < ActiveRecord::TestCase # Original test had `7` which we support vs `8` which we use. coerce_tests! :test_invalid_datetime_precision_raises_error @@ -1268,9 +1138,6 @@ def test_datetime_precision_is_truncated_on_assignment_coerced end end - - - class TimePrecisionTest < ActiveRecord::TestCase # datetime is rounded to increments of .000, .003, or .007 seconds coerce_tests! :test_time_precision_is_truncated_on_assignment @@ -1296,9 +1163,6 @@ def test_time_precision_is_truncated_on_assignment_coerced coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment end - - - class DefaultNumbersTest < ActiveRecord::TestCase # We do better with native types and do not return strings for everything. coerce_tests! :test_default_positive_integer @@ -1325,9 +1189,6 @@ def test_default_decimal_number_coerced end end - - - module ActiveRecord class CollectionCacheKeyTest < ActiveRecord::TestCase # Will trust rails has this sorted since you cant offset without a limit. @@ -1335,9 +1196,6 @@ class CollectionCacheKeyTest < ActiveRecord::TestCase end end - - - module ActiveRecord class CacheKeyTest < ActiveRecord::TestCase # Like Mysql2 and PostgreSQL, SQL Server doesn't return a string value for updated_at. In the Rails tests @@ -1348,9 +1206,6 @@ class CacheKeyTest < ActiveRecord::TestCase end end - - - require "models/book" module ActiveRecord class StatementCacheTest < ActiveRecord::TestCase @@ -1369,9 +1224,6 @@ def test_statement_cache_values_differ_coerced end end - - - module ActiveRecord module ConnectionAdapters class SchemaCacheTest < ActiveRecord::TestCase @@ -1384,9 +1236,6 @@ def schema_dump_path end end - - - class UnsafeRawSqlTest < ActiveRecord::TestCase # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} @@ -1419,9 +1268,6 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase end end - - - class ReservedWordTest < ActiveRecord::TestCase coerce_tests! :test_change_columns def test_change_columns_coerced @@ -1432,9 +1278,6 @@ def test_change_columns_coerced end end - - - class OptimisticLockingTest < ActiveRecord::TestCase # We do not allow updating identities, but we can test using a non-identity key coerce_tests! :test_update_with_dirty_primary_key @@ -1456,9 +1299,6 @@ def test_update_with_dirty_primary_key_coerced end end - - - class RelationMergingTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert coerce_tests! :test_merging_with_order_with_binds @@ -1468,9 +1308,6 @@ def test_merging_with_order_with_binds_coerced end end - - - module ActiveRecord class DatabaseTasksTruncateAllTest < ActiveRecord::TestCase # SQL Server does not allow truncation of tables that are referenced by foreign key @@ -1480,9 +1317,6 @@ 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. @@ -1526,10 +1360,6 @@ class EnumTest < ActiveRecord::TestCase end end - - - - require "models/task" class QueryCacheExpiryTest < ActiveRecord::TestCase # SQL Server does not support skipping or upserting duplicates. @@ -1561,8 +1391,6 @@ def test_insert_all_coerced end end - - require "models/citation" class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and @@ -1585,9 +1413,6 @@ def test_eager_loading_too_may_ids_coerced end end - - - class LogSubscriberTest < ActiveRecord::TestCase # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. coerce_tests! :test_vebose_query_logs @@ -1596,9 +1421,6 @@ def test_vebose_query_logs_coerced end end - - - class ActiveRecordSchemaTest < ActiveRecord::TestCase # Workaround for randomly failing test. coerce_tests! :test_has_primary_key @@ -1608,9 +1430,6 @@ def test_has_primary_key_coerced end end - - - module ActiveRecord module ConnectionAdapters class ReaperTest < ActiveRecord::TestCase @@ -1620,17 +1439,11 @@ class ReaperTest < ActiveRecord::TestCase end end - - - class FixturesTest < ActiveRecord::TestCase # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged. coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end - - - class ReloadModelsTest < ActiveRecord::TestCase # Skip test on Windows. The number of arguements passed to `IO.popen` in # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle. diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 2df39822f..39c0d7f4b 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -59,7 +59,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase end - private def disconnect_raw_connection! diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index bed448631..e35d42d2f 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -46,7 +46,6 @@ class FetchTestSqlserver < ActiveRecord::TestCase end - protected def create_10_books diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index d2b1b4406..6fad1df9d 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -135,7 +135,6 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :name, type: "string", limit: nil, default: nil, collation: nil end - private def generate_schema_for_table(*table_names) diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 7682c4408..619f40e3d 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -64,7 +64,6 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase end - private def with_showplan_option(option) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 56daf1350..974f5829c 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -77,7 +77,6 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end end - protected def delete_ships From eae6065ea43f782e878729ad204350d17fa4a48d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 22:29:04 +0100 Subject: [PATCH 0890/1412] Rubocop: Enable Layout/Layout/EmptyLinesAround* cops --- Rakefile | 4 ---- .../sqlserver/core_ext/active_record.rb | 4 ---- .../sqlserver/core_ext/attribute_methods.rb | 2 -- .../sqlserver/core_ext/calculations.rb | 1 - .../sqlserver/core_ext/explain.rb | 2 -- .../sqlserver/core_ext/finder_methods.rb | 1 - .../sqlserver/core_ext/query_methods.rb | 1 - .../sqlserver/database_statements.rb | 1 - .../sqlserver/database_tasks.rb | 2 -- .../connection_adapters/sqlserver/errors.rb | 2 -- .../connection_adapters/sqlserver/quoting.rb | 2 -- .../sqlserver/schema_statements.rb | 2 -- .../connection_adapters/sqlserver/showplan.rb | 2 -- .../sqlserver/table_definition.rb | 3 --- .../sqlserver/transaction.rb | 5 ----- .../sqlserver/type/time_value_fractional.rb | 6 ------ .../connection_adapters/sqlserver/utils.rb | 2 -- .../connection_adapters/sqlserver/version.rb | 2 -- .../tasks/sqlserver_database_tasks.rb | 6 ------ test/cases/adapter_test_sqlserver.rb | 18 ------------------ test/cases/column_test_sqlserver.rb | 2 -- test/cases/connection_test_sqlserver.rb | 2 -- test/cases/fetch_test_sqlserver.rb | 4 ---- ...ully_qualified_identifier_test_sqlserver.rb | 4 ---- test/cases/migration_test_sqlserver.rb | 4 ---- .../pessimistic_locking_test_sqlserver.rb | 6 ------ test/cases/schema_test_sqlserver.rb | 4 ---- test/cases/showplan_test_sqlserver.rb | 6 ------ test/cases/utils_test_sqlserver.rb | 4 ---- test/schema/sqlserver_specific_schema.rb | 2 -- test/support/coerceable_test_sqlserver.rb | 4 ---- test/support/connection_reflection.rb | 2 -- test/support/load_schema_sqlserver.rb | 2 -- test/support/paths_sqlserver.rb | 2 -- test/support/sql_counter_sqlserver.rb | 4 ---- 35 files changed, 120 deletions(-) diff --git a/Rakefile b/Rakefile index d8174b1b2..ef69c87d4 100644 --- a/Rakefile +++ b/Rakefile @@ -9,22 +9,18 @@ task test: ["test:dblib"] task default: [:test] namespace :test do - %w(dblib).each do |mode| - Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths t.test_files = test_files t.warning = !!ENV["WARNING"] t.verbose = false end - end task "dblib:env" do ENV["ARCONN"] = "dblib" end - end task "test:dblib" => "test:dblib:env" diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb index 03849a5cb..70b9aaee4 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb @@ -5,11 +5,9 @@ module ConnectionAdapters module SQLServer module CoreExt module ActiveRecord - extend ActiveSupport::Concern module ClassMethods - def execute_procedure(proc_name, *variables) if connection.respond_to?(:execute_procedure) connection.execute_procedure(proc_name, *variables) @@ -17,9 +15,7 @@ def execute_procedure(proc_name, *variables) [] end end - end - end end end 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 3df31c56b..1becf129b 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 @@ -7,7 +7,6 @@ module ConnectionAdapters module SQLServer module CoreExt module AttributeMethods - private def attributes_for_update(attribute_names) @@ -16,7 +15,6 @@ def attributes_for_update(attribute_names) column && column.respond_to?(:is_identity?) && column.is_identity? end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index dc6e73360..1b9bcb6ba 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -8,7 +8,6 @@ module ConnectionAdapters module SQLServer module CoreExt module Calculations - # Same as original except we don't perform PostgreSQL hack that removes ordering. def calculate(operation, column_name) if has_include?(column_name) 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 b71e7a0ad..89e1d6bb1 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -5,7 +5,6 @@ module ConnectionAdapters module SQLServer module CoreExt module Explain - SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql " SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ @@ -34,7 +33,6 @@ def unprepare_sqlserver_statement(sql, binds) executesql end - end end end 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 8a37a27b6..ccfa32620 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 @@ -8,7 +8,6 @@ module ConnectionAdapters module SQLServer module CoreExt module FinderMethods - private # Same as original except we order by values in distinct select if present. diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb index ae89a12a0..d29fb8c4d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb @@ -8,7 +8,6 @@ module ConnectionAdapters module SQLServer module CoreExt module QueryMethods - private # Copy of original from Rails master. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 17a6f9892..d67238de7 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -454,7 +454,6 @@ def finish_statement_handle(handle) end handle end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 3e6c47fdb..efab05a9e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseTasks - def create_database(database, options = {}) name = SQLServer::Utils.extract_identifiers(database) db_options = create_database_options(options) @@ -60,7 +59,6 @@ def create_database_edition_options(options={}) edition_options = "( #{edition_options} )" if edition_options.present? edition_options end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/errors.rb b/lib/active_record/connection_adapters/sqlserver/errors.rb index 45c990a54..5d0e358f7 100644 --- a/lib/active_record/connection_adapters/sqlserver/errors.rb +++ b/lib/active_record/connection_adapters/sqlserver/errors.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true module ActiveRecord - class DeadlockVictim < WrappedDatabaseException end - end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 89583bbfb..430a1bc4c 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Quoting - QUOTED_TRUE = "1".freeze QUOTED_FALSE = "0".freeze QUOTED_STRING_PREFIX = "N".freeze @@ -130,7 +129,6 @@ def _type_cast(value) super end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e50b21217..f2bfe10c1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -4,7 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module SchemaStatements - def native_database_types @native_database_types ||= initialize_native_database_types.freeze end @@ -561,7 +560,6 @@ def views_real_column_name(table_name, column_name) def create_table_definition(*args, **options) SQLServer::TableDefinition.new(self, *args, **options) end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 9c296a8ee..b2a737518 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -7,7 +7,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Showplan - OPTION_ALL = "SHOWPLAN_ALL" OPTION_TEXT = "SHOWPLAN_TEXT" OPTION_XML = "SHOWPLAN_XML" @@ -61,7 +60,6 @@ def showplan_printer else PrinterTable end end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 1027a6d97..cae8903c5 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -3,9 +3,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer - module ColumnMethods - def primary_key(name, type = :primary_key, **options) if [:integer, :bigint].include?(type) options[:is_identity] = true unless options.key?(:default) @@ -97,7 +95,6 @@ def ss_timestamp(*names, **options) def json(*names, **options) names.each { |name| column(name, :text, **options) } end - end class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index bdc8d78a2..08deeed44 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -4,9 +4,7 @@ module ActiveRecord module ConnectionAdapters - module SQLServerTransaction - private def sqlserver? @@ -25,13 +23,11 @@ def current_isolation_level level.upcase end end - end Transaction.send :prepend, SQLServerTransaction module SQLServerRealTransaction - attr_reader :starting_isolation_level def initialize(connection, options, **args) @@ -57,7 +53,6 @@ def reset_starting_isolation_level connection.set_transaction_isolation_level(starting_isolation_level) end end - end RealTransaction.send :prepend, SQLServerRealTransaction 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 45933248c..aa9dd2701 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 @@ -4,9 +4,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Type - module TimeValueFractional - private def apply_seconds_precision(value) @@ -47,11 +45,9 @@ def fractional_precision def fractional_scale 3 end - end module TimeValueFractional2 - include TimeValueFractional private @@ -84,9 +80,7 @@ def fractional_max def fractional_scale_max ("9" * fractional_scale) + ("0" * (fractional_digits - fractional_scale)) end - end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index ebe3bc0b9..ed1762993 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -6,7 +6,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Utils - QUOTED_STRING_PREFIX = "N" # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL @@ -139,7 +138,6 @@ def unquote_string(s) def extract_identifiers(name) SQLServer::Utils::Name.new(name) end - end end end diff --git a/lib/active_record/connection_adapters/sqlserver/version.rb b/lib/active_record/connection_adapters/sqlserver/version.rb index 4ab6f0bf4..ff7577895 100644 --- a/lib/active_record/connection_adapters/sqlserver/version.rb +++ b/lib/active_record/connection_adapters/sqlserver/version.rb @@ -4,9 +4,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Version - VERSION = File.read(File.expand_path("../../../../../VERSION", __FILE__)).chomp - end end end diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index e436c05fe..df1415e0d 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -7,7 +7,6 @@ module ActiveRecord module Tasks - class SQLServerDatabaseTasks DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" @@ -94,11 +93,9 @@ def establish_master_connection end module DatabaseTasksSQLServer - extend ActiveSupport::Concern module ClassMethods - LOCAL_IPADDR = [ IPAddr.new("192.168.0.0/16"), IPAddr.new("10.0.0.0/8"), @@ -120,13 +117,10 @@ def local_ipaddr?(host_ip) return false unless host_ip LOCAL_IPADDR.any? { |ip| ip.include?(host_ip) } end - end - end DatabaseTasks.register_task %r{sqlserver}, SQLServerDatabaseTasks DatabaseTasks.send :include, DatabaseTasksSQLServer - end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 480fd5614..584e68cc1 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -86,7 +86,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end describe "with different language" do - before do @default_language = connection.user_options_language end @@ -116,11 +115,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase Task.create! starting: starting, ending: ending end end - end describe "testing #lowercase_schema_reflection" do - before do SSTestUpper.delete_all SSTestUpper.create COLUMN1: "Got a minute?", COLUMN2: 419 @@ -145,11 +142,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "Favorite number?", SSTestUppered.last.column1 assert SSTestUppered.columns_hash["column2"] end - end describe "identity inserts" do - before do @identity_insert_sql = "INSERT INTO [funny_jokes] ([id],[name]) VALUES(420,'Knock knock')" @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')" @@ -183,11 +178,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "return an empty array when calling #identity_columns for a table_name with no identity" do _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end - end describe "quoting" do - it "return 1 for #quoted_true" do assert_equal "1", connection.quoted_true end @@ -224,11 +217,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "escape all single quotes by repeating them" do assert_equal "N'''quotation''s'''", connection.quote("'quotation's'") end - end describe "disabling referential integrity" do - before do connection.disable_referential_integrity { SSTestHasPk.delete_all; SSTestHasFk.delete_all } @parent = SSTestHasPk.create! @@ -255,11 +246,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase tables = SSTestHasPk.connection.tables_with_referential_integrity assert_equal tables.size, tables.uniq.size end - end describe "database statements" do - it "run the database consistency checker useroptions command" do skip "on azure" if connection_sqlserver_azure? keys = [:textsize, :language, :isolation_level, :dateformat] @@ -276,11 +265,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "read committed", user_options["isolation_level"] assert_equal "read committed", user_options[:isolation_level] end - end describe "schema statements" do - it "create integers when no limit supplied" do assert_equal "integer", connection.type_to_sql(:integer) end @@ -311,11 +298,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "create floats when no limit supplied" do assert_equal "float", connection.type_to_sql(:float) end - end describe "views" do - # Using connection.views it "return an array" do @@ -415,11 +400,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "null", SSTestStringDefaultsBigView.new.pretend_null, SSTestStringDefaultsBigView.columns_hash["pretend_null"].inspect end - end describe "database_prefix_remote_server?" do - after do connection_options.delete(:database_prefix) end @@ -437,7 +420,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase connection_options[:database_prefix] = "server.database.schema" assert_equal false, connection.database_prefix_remote_server? end - end it "in_memory_oltp" do diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index b2a46f9c6..71560ce85 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -9,7 +9,6 @@ class ColumnTestSQLServer < ActiveRecord::TestCase end describe "ActiveRecord::ConnectionAdapters::SQLServer::Type" do - let(:obj) { SSTestDatatype.new } Type = ActiveRecord::ConnectionAdapters::SQLServer::Type @@ -835,6 +834,5 @@ def assert_obj_set_and_save(attribute, value) obj.attributes _(obj.changed?).must_equal false end - end end diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index 39c0d7f4b..ddf9bbbca 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -35,7 +35,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase end unless connection_sqlserver_azure? describe "Connection management" do - it "set spid on connect" do _(["Fixnum", "Integer"]).must_include connection.spid.class.name end @@ -56,7 +55,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase connection.reconnect! assert connection.active? end - end private diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index e35d42d2f..08dfe9193 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -14,7 +14,6 @@ class FetchTestSqlserver < ActiveRecord::TestCase end describe "count" do - it "gauntlet" do books[0].destroy books[1].destroy @@ -30,11 +29,9 @@ class FetchTestSqlserver < ActiveRecord::TestCase assert_equal 0, Book.limit(3).offset(7).count assert_equal 0, Book.limit(3).offset(8).count end - end describe "order" do - it "gauntlet" do Book.where(name:"Name-10").delete_all _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ["Name-2"] @@ -43,7 +40,6 @@ class FetchTestSqlserver < ActiveRecord::TestCase _(Book.order(:name).limit(3).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"] _(Book.order(:name).limit(3).offset(9).map(&:name)).must_equal [] end - end protected diff --git a/test/cases/fully_qualified_identifier_test_sqlserver.rb b/test/cases/fully_qualified_identifier_test_sqlserver.rb index 303efa1e1..73c173b71 100644 --- a/test/cases/fully_qualified_identifier_test_sqlserver.rb +++ b/test/cases/fully_qualified_identifier_test_sqlserver.rb @@ -4,17 +4,14 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase describe "local server" do - it "should use table name in select projections" do table = Arel::Table.new(:table) expected_sql = "SELECT [table].[name] FROM [table]" assert_equal expected_sql, table.project(table[:name]).to_sql end - end describe "remote server" do - before do connection_options[:database_prefix] = "[my.server].db.schema." end @@ -71,6 +68,5 @@ class FullyQualifiedIdentifierTestSQLServer < ActiveRecord::TestCase expected_sql = "DELETE FROM [my.server].[db].[schema].[table] WHERE [table].[id] = 42" quietly { assert_equal expected_sql, manager.to_sql } end - end end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index f27dcccb3..0e82ba507 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -5,7 +5,6 @@ class MigrationTestSQLServer < ActiveRecord::TestCase describe "For transactions" do - before do @trans_test_table1 = "sqlserver_trans_table1" @trans_test_table2 = "sqlserver_trans_table2" @@ -28,11 +27,9 @@ class MigrationTestSQLServer < ActiveRecord::TestCase _(connection.tables).wont_include @trans_test_table1 _(connection.tables).wont_include @trans_test_table2 end - end describe "For changing column" do - it "not raise exception when column contains default constraint" do lock_version_column = Person.columns_hash["lock_version"] assert_equal :integer, lock_version_column.type @@ -66,6 +63,5 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it "change null and default" do assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } end - end end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index a9022816c..34212f287 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -19,7 +19,6 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end describe "For simple finds with default lock option" do - it "lock with simple find" do assert_nothing_raised do Person.transaction do @@ -54,7 +53,6 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end describe "joining tables" do - it "joined tables use updlock by default" do assert_sql %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 @@ -78,13 +76,10 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase Person.lock("WITH(NOLOCK)").left_joins(:readers).load end end - end - end describe "For paginated finds" do - before do Person.delete_all 20.times { |n| Person.create!(first_name: "Thing_#{n}") } @@ -102,6 +97,5 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase _(people[4].first_name).must_equal "Thing_14" end end - end end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 0c45eb1c4..c6af81f0e 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -4,15 +4,12 @@ class SchemaTestSQLServer < ActiveRecord::TestCase describe "When table is dbo schema" do - it "find primary key for tables with odd schema" do _(connection.primary_key("sst_natural_pk_data")).must_equal "legacy_id" end - end describe "When table is in non-dbo schema" do - it "work with table exists" do assert connection.data_source_exists?("test.sst_schema_natural_id") assert connection.data_source_exists?("[test].[sst_schema_natural_id]") @@ -47,7 +44,6 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal 255, columns.find {|c| c.name == "n_name"}.limit assert_equal 1000, columns.find {|c| c.name == "n_description"}.limit end - end end diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 619f40e3d..74bf9ffa9 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -7,7 +7,6 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase fixtures :cars describe "Unprepare previously prepared SQL" do - it "from simple statement" do plan = Car.where(id: 1).explain _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" @@ -38,11 +37,9 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end - end describe "With SHOWPLAN_TEXT option" do - it "use simple table printer" do with_showplan_option("SHOWPLAN_TEXT") do plan = Car.where(id: 1).explain @@ -50,18 +47,15 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end end - end describe "With SHOWPLAN_XML option" do - it "show formatted xml" do with_showplan_option("SHOWPLAN_XML") do plan = Car.where(id: 1).explain _(plan).must_include "ShowPlanXML" end end - end private diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index ea433d9dc..1ccd643e3 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -16,7 +16,6 @@ class UtilsTestSQLServer < ActiveRecord::TestCase end describe ".extract_identifiers constructor and thus SQLServer::Utils::Name value object" do - let(:valid_names) { valid_names_unquoted + valid_names_quoted } let(:valid_names_unquoted) {[ @@ -54,7 +53,6 @@ class UtilsTestSQLServer < ActiveRecord::TestCase end [:schema, :database, :server].each do |part| - it "extracts and returns #{part} identifier unquoted by default or quoted as needed" do present, blank = send(:"#{part}_names") present.each do |n| @@ -68,7 +66,6 @@ class UtilsTestSQLServer < ActiveRecord::TestCase _(name.send(:"#{part}_quoted")).must_be_nil "With #{n.inspect} for ##{part}_quoted method" end end - end it "does not blow up on nil or blank string name" do @@ -118,7 +115,6 @@ class UtilsTestSQLServer < ActiveRecord::TestCase name = extract_identifiers("server.database.schema.object") _(name.fully_qualified_database_quoted).must_equal "[server].[database]" end - end private diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 35b018b51..1d63c1339 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true ActiveRecord::Schema.define do - # Exhaustive Data Types execute File.read(ARTest::SQLServer.schema_datatypes_2012_file) @@ -278,5 +277,4 @@ field_2 int NOT NULL PRIMARY KEY, ) SCHEMATESTMULTIPLESCHEMA - end diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index 6ecbe7953..a0fba3fc0 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -3,7 +3,6 @@ module ARTest module SQLServer module CoerceableTest - extend ActiveSupport::Concern included do @@ -12,7 +11,6 @@ module CoerceableTest end module ClassMethods - def coerce_tests!(*methods) methods.each do |method| self.coerced_tests.push(method) @@ -50,9 +48,7 @@ def coerced_test_warning(test_to_coerce) end end end - end - end end end diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index 777fae534..9fe1ac1fb 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -3,7 +3,6 @@ module ARTest module SQLServer module ConnectionReflection - extend ActiveSupport::Concern included { extend ConnectionReflection } @@ -29,7 +28,6 @@ def connection_dblib_73? def connection_sqlserver_azure? connection.sqlserver_azure? end - end end end diff --git a/test/support/load_schema_sqlserver.rb b/test/support/load_schema_sqlserver.rb index a1641383f..73ff9219c 100644 --- a/test/support/load_schema_sqlserver.rb +++ b/test/support/load_schema_sqlserver.rb @@ -2,7 +2,6 @@ module ARTest module SQLServer - extend self def schema_root @@ -24,7 +23,6 @@ def load_schema ensure $stdout = original_stdout end - end end diff --git a/test/support/paths_sqlserver.rb b/test/support/paths_sqlserver.rb index fe4421131..5d2735d6a 100644 --- a/test/support/paths_sqlserver.rb +++ b/test/support/paths_sqlserver.rb @@ -2,7 +2,6 @@ module ARTest module SQLServer - extend self def root_sqlserver @@ -44,7 +43,6 @@ def arconfig_file def arconfig_file_env! ENV["ARCONFIG"] = arconfig_file end - end end diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 6977c17d6..20819cf6a 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -2,16 +2,13 @@ module ARTest module SQLServer - module SqlCounterSqlserver - # Only return the log vs. log_all def capture_sql_ss ActiveRecord::SQLCounter.clear_log yield ActiveRecord::SQLCounter.log.dup end - end ignored_sql = [ @@ -26,6 +23,5 @@ def capture_sql_ss sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first sqlcounter.instance_variable_set :@ignore, Regexp.union(ignored_sql.push(sqlcounter.ignore)) - end end From 9905c4ac33c01752a3b055cf0b358096ff90e156 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 22:36:51 +0100 Subject: [PATCH 0891/1412] Rubocop: Enable Layout/IndentationWidth,Layout/TrailingWhitespace cops --- .../sqlserver/schema_statements.rb | 8 ++-- .../sqlserver/type/datetime.rb | 2 +- .../sqlserver/type/time.rb | 2 +- test/cases/json_test_sqlserver.rb | 44 +++++++++---------- test/cases/migration_test_sqlserver.rb | 4 +- .../pessimistic_locking_test_sqlserver.rb | 12 ++--- test/cases/schema_dumper_test_sqlserver.rb | 4 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f2bfe10c1..629287b28 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -143,7 +143,7 @@ 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 + column_object.default else options[:default] end @@ -415,8 +415,8 @@ def column_definitions(table_name) def column_definitions_sql(database, identifier) object_name = prepared_statements ? "@0" : quote(identifier.object) - schema_name = if identifier.schema.blank? - "schema_name()" + schema_name = if identifier.schema.blank? + "schema_name()" else prepared_statements ? "@1" : quote(identifier.schema) end @@ -506,7 +506,7 @@ def remove_indexes(table_name, column_name) def get_table_name(sql) tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i - Regexp.last_match[3] || Regexp.last_match[4] + Regexp.last_match[3] || Regexp.last_match[4] elsif sql =~ /FROM\s+([^\(\s]+)\s*/i Regexp.last_match[1] else diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index 5f05ef907..eedabb94c 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -14,7 +14,7 @@ def sqlserver_type def serialize(value) value = super return value unless value.acts_like?(:time) - + datetime = "#{value.to_s(:_sqlserver_datetime)}.#{quote_fractional(value)}" Data.new datetime, self diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 34adf6296..4261ae7b7 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -12,7 +12,7 @@ def serialize(value) return value unless value.acts_like?(:time) time = "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}" - + Data.new time, self end diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index 176add811..a863868a8 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -3,30 +3,30 @@ require "cases/helper_sqlserver" if ActiveRecord::Base.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] } - @o5 = SSTestDatatypeMigrationJson.create! json_col: nil - end + 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] } + @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" }) - @o1.save! - _(@o1.reload.json_col).must_equal({ "a" => "a" }) - 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" }) + @o1.save! + _(@o1.reload.json_col).must_equal({ "a" => "a" }) + end - it "can use ISJSON function" do - _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) > 0").count).must_equal 4 - _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) IS NULL").count).must_equal 1 - end + it "can use ISJSON function" do + _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) > 0").count).must_equal 4 + _(SSTestDatatypeMigrationJson.where("ISJSON(json_col) IS NULL").count).must_equal 1 + end - it "can use JSON_VALUE function" do - _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2 + it "can use JSON_VALUE function" do + _(SSTestDatatypeMigrationJson.where("JSON_VALUE(json_col, '$.b') = 'b'").count).must_equal 2 + end end end -end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 0e82ba507..e02c87602 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -55,11 +55,11 @@ class MigrationTestSQLServer < ActiveRecord::TestCase assert default_after assert_equal default_before["constraint_keys"], default_after["constraint_keys"] end - + it "change limit" do assert_nothing_raised { connection.change_column :people, :lock_version, :integer, limit: 8 } end - + it "change null and default" do assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } end diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index 34212f287..f63365356 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -38,12 +38,12 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase end it "lock with eager find" do - assert_nothing_raised do - Person.transaction do - person = Person.lock(true).includes(:readers).find(1) - _(person).must_equal Person.find(1) - end - end + assert_nothing_raised do + Person.transaction do + person = Person.lock(true).includes(:readers).find(1) + _(person).must_equal Person.find(1) + end + end end it "can add a custom lock directive" do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 6fad1df9d..733f52d78 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -27,13 +27,13 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :date, type: "date", limit: nil, precision: nil, scale: nil, default: "01-01-0001" assert_line :datetime, type: "datetime", limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? - assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" + assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil end assert_line :smalldatetime, type: "smalldatetime",limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? - assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" + assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" end From 78aa3859d6df1583fa6c8a5b57eff0a9e35241f2 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 22:45:19 +0100 Subject: [PATCH 0892/1412] Rubocop: Enable Spacing cops --- Gemfile | 2 +- .../sqlserver/core_ext/calculations.rb | 2 +- .../sqlserver/database_statements.rb | 6 +- .../sqlserver/database_tasks.rb | 8 +- .../sqlserver/schema_statements.rb | 2 +- .../sqlserver/type/time_value_fractional.rb | 4 +- .../connection_adapters/sqlserver/utils.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 10 +- lib/arel/visitors/sqlserver.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 36 ++-- test/cases/coerced_tests.rb | 10 +- test/cases/column_test_sqlserver.rb | 200 +++++++++--------- .../cases/execute_procedure_test_sqlserver.rb | 2 +- test/cases/fetch_test_sqlserver.rb | 4 +- test/cases/migration_test_sqlserver.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 4 +- test/cases/schema_test_sqlserver.rb | 16 +- test/cases/specific_schema_test_sqlserver.rb | 10 +- test/cases/trigger_test_sqlserver.rb | 2 +- test/cases/utils_test_sqlserver.rb | 4 +- .../1_table_will_never_be_created.rb | 2 +- test/schema/sqlserver_specific_schema.rb | 8 +- test/support/rake_helpers.rb | 2 +- 23 files changed, 171 insertions(+), 171 deletions(-) diff --git a/Gemfile b/Gemfile index a5b35593e..ca095824d 100644 --- a/Gemfile +++ b/Gemfile @@ -21,7 +21,7 @@ else require "yaml" spec = eval(File.read("activerecord-sqlserver-adapter.gemspec")) - ver = spec.dependencies.detect{ |d|d.name == "activerecord" }.requirement.requirements.first.last.version + ver = spec.dependencies.detect { |d| d.name == "activerecord" }.requirement.requirements.first.last.version major, minor, tiny, pre = ver.split(".") if pre diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 1b9bcb6ba..d02e6a302 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -16,7 +16,7 @@ def calculate(operation, column_name) if operation.to_s.downcase == "count" unless distinct_value || distinct_select?(column_name || select_for_count) relation.distinct! - relation.select_values = [ klass.primary_key || table[Arel.star] ] + relation.select_values = [klass.primary_key || table[Arel.star]] end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index d67238de7..bba3752a9 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -143,7 +143,7 @@ def build_insert_sql(insert) # :nodoc: sql = +"INSERT #{insert.into}" if returning = insert.send(:insert_all).returning - sql << " OUTPUT " << returning.map {|column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + sql << " OUTPUT " << returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") end sql << " #{insert.values_list}" @@ -198,7 +198,7 @@ def user_options if row.instance_of? Hash set_option = row.values[0].gsub(/\s+/, "_") user_value = row.values[1] - elsif row.instance_of? Array + elsif row.instance_of? Array set_option = row[0].gsub(/\s+/, "_") user_value = row[1] end @@ -341,7 +341,7 @@ def sp_executesql_sql(sql, types, params, name) end else types = quote(types.join(", ")) - params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(", ") # Only p is needed, but with @i helps explain regexp. + params = params.map.with_index { |p, i| "@#{i} = #{p}" }.join(", ") # Only p is needed, but with @i helps explain regexp. sql = "EXEC sp_executesql #{quote(sql)}" sql += ", #{types}, #{params}" unless params.empty? end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index efab05a9e..954d3d0fe 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -31,20 +31,20 @@ def collation private - def create_database_options(options={}) + def create_database_options(options = {}) keys = [:collate] copts = @connection_options options = { collate: copts[:collation] }.merge(options.symbolize_keys).select { |_, v| v.present? - }.slice(*keys).map { |k,v| + }.slice(*keys).map { |k, v| "#{k.to_s.upcase} #{v}" }.join(" ") options end - def create_database_edition_options(options={}) + def create_database_edition_options(options = {}) keys = [:maxsize, :edition, :service_objective] copts = @connection_options edition_options = { @@ -53,7 +53,7 @@ def create_database_edition_options(options={}) service_objective: copts[:azure_service_objective] }.merge(options.symbolize_keys).select { |_, v| v.present? - }.slice(*keys).map { |k,v| + }.slice(*keys).map { |k, v| "#{k.to_s.upcase} = #{v}" }.join(", ") edition_options = "( #{edition_options} )" if edition_options.present? diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 629287b28..3f8eea12f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -248,7 +248,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) end def columns_for_distinct(columns, orders) - order_columns = orders.reject(&:blank?).map{ |s| + order_columns = orders.reject(&:blank?).map { |s| s = s.to_sql unless s.is_a?(String) s.gsub(/\s+(?:ASC|DESC)\b/i, "") .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") 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 aa9dd2701..fe67d3d9e 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 @@ -23,7 +23,7 @@ def quote_fractional(value) return 0 if fractional_scale == 0 frac_seconds = seconds_precision(value) seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) - seconds.to_d.to_s.split(".").last.to(fractional_scale-1) + seconds.to_d.to_s.split(".").last.to(fractional_scale - 1) end def fractional_property @@ -35,7 +35,7 @@ def fractional_digits end def fractional_operator - 10 ** fractional_digits + 10**fractional_digits end def fractional_precision diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index ed1762993..c8836ae5f 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -54,7 +54,7 @@ def to_s end def quoted - parts.map{ |p| quote(p) if p }.join SEPARATOR + parts.map { |p| quote(p) if p }.join SEPARATOR end def quoted_raw diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c4523c885..8d77c8b6c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -293,7 +293,7 @@ def get_database_version # :nodoc: # === Abstract Adapter (Misc Support) =========================== # def initialize_type_map(m = type_map) - 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)" @@ -326,11 +326,11 @@ def initialize_type_map(m = type_map) SQLServer::Type::DateTime.new end end - m.register_type %r{\Adatetimeoffset}i do |sql_type| + m.register_type %r{\Adatetimeoffset}i do |sql_type| precision = extract_precision(sql_type) SQLServer::Type::DateTimeOffset.new precision: precision end - m.register_type "smalldatetime", SQLServer::Type::SmallDateTime.new + 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 @@ -456,9 +456,9 @@ def config_encoding(config) config[:encoding].present? ? config[:encoding] : nil end - def configure_connection ; end + def configure_connection; end - def configure_application_name ; end + def configure_application_name; end def initialize_dateformatter @database_dateformat = user_options_dateformat diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 4cf5ea790..f70cac55c 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -14,7 +14,7 @@ class SQLServer < Arel::Visitors::ToSql # SQLServer ToSql/Visitor (Overides) def visit_Arel_Nodes_BindParam o, collector - collector.add_bind(o.value) { |i| "@#{i-1}" } + collector.add_bind(o.value) { |i| "@#{i - 1}" } end def visit_Arel_Nodes_Bin o, collector @@ -70,7 +70,7 @@ def visit_Arel_Nodes_SelectStatement o, collector collector = visit o.with, collector collector << " " end - collector = o.cores.inject(collector) { |c,x| + collector = o.cores.inject(collector) { |c, x| visit_Arel_Nodes_SelectCore(x, c) } collector = visit_Orders_And_Let_Fetch_Happen o, collector diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 584e68cc1..d20ced9e6 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -54,10 +54,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return true to insert sql query for inserts only" do - assert connection.send(:insert_sql?,"INSERT...") + assert connection.send(:insert_sql?, "INSERT...") assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") - assert !connection.send(:insert_sql?,"UPDATE...") - assert !connection.send(:insert_sql?,"SELECT...") + assert !connection.send(:insert_sql?, "UPDATE...") + assert !connection.send(:insert_sql?, "SELECT...") end it "return unquoted table name object from basic INSERT UPDATE and SELECT statements" do @@ -155,17 +155,17 @@ 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) end it "return false to #query_requires_identity_insert? for normal SQL" do [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| - assert !connection.send(:query_requires_identity_insert?,sql), "SQL was #{sql}" + assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}" end end @@ -227,7 +227,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "NOT ALLOW by default the deletion of a referenced parent" do - SSTestHasPk.connection.disable_referential_integrity { } + SSTestHasPk.connection.disable_referential_integrity {} assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end @@ -237,7 +237,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do assert_raise(ActiveRecord::StatementInvalid) do - SSTestHasPk.connection.disable_referential_integrity { } + SSTestHasPk.connection.disable_referential_integrity {} @parent.destroy end end @@ -318,27 +318,27 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "not contain system views" do - systables = ["sysconstraints","syssegments"] + systables = ["sysconstraints", "syssegments"] systables.each do |systable| assert !connection.views.include?(systable), "This systable #{systable} should not be in the views array." end end it "allow the connection#view_information method to return meta data on the view" do - view_info = connection.send(:view_information,"sst_customers_view") + view_info = connection.send(:view_information, "sst_customers_view") assert_equal("sst_customers_view", view_info["TABLE_NAME"]) assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) end it "allow the connection#view_table_name method to return true table_name for the view" do - assert_equal "customers", connection.send(:view_table_name,"sst_customers_view") - assert_equal "topics", connection.send(:view_table_name,"topics"), "No view here, the same table name should come back." + assert_equal "customers", connection.send(:view_table_name, "sst_customers_view") + assert_equal "topics", connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." end # With same column names it "have matching column objects" do - columns = ["id","name","balance"] + columns = ["id", "name", "balance"] assert !SSTestCustomersView.columns.blank? assert_equal columns.size, SSTestCustomersView.columns.size columns.each do |colname| @@ -365,7 +365,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase # With aliased column names it "have matching column objects" do - columns = ["id","pretend_null"] + columns = ["id", "pretend_null"] assert !SSTestStringDefaultsView.columns.blank? assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index ce0b09158..782c7d15a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -329,14 +329,14 @@ class QuoteARBaseTest < ActiveRecord::TestCase coerce_tests! :test_quote_ar_object def test_quote_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) + assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) end # Use our date format. coerce_tests! :test_type_cast_ar_object def test_type_cast_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) + assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) end end end @@ -883,7 +883,7 @@ def test_query_cached_even_when_types_are_reset_coerced Task.find(1) end - assert_includes ActiveRecord::SQLCounter.log_all.first , "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''" + assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''" end end end @@ -986,7 +986,7 @@ def self.search_as_method(term) where("title LIKE ?", sanitize_sql_like(term, "!")) end - scope :search_as_scope, -> (term) { + scope :search_as_scope, ->(term) { where("title LIKE ?", sanitize_sql_like(term, "!")) } end @@ -1121,7 +1121,7 @@ def test_invalid_datetime_precision_raises_error_coerced coerce_tests! :test_datetime_precision_is_truncated_on_assignment def test_datetime_precision_is_truncated_on_assignment_coerced @connection.create_table(:foos, force: true) - @connection.add_column :foos, :created_at, :datetime, precision: 0 + @connection.add_column :foos, :created_at, :datetime, precision: 0 @connection.add_column :foos, :updated_at, :datetime, precision: 6 time = ::Time.now.change(nsec: 123456789) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 71560ce85..09f57a8dc 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -13,8 +13,8 @@ class ColumnTestSQLServer < ActiveRecord::TestCase Type = ActiveRecord::ConnectionAdapters::SQLServer::Type - def new_obj ; SSTestDatatype.new ; end - def column(name) ; SSTestDatatype.columns_hash[name] ; end + def new_obj; SSTestDatatype.new; end + def column(name); SSTestDatatype.columns_hash[name]; end def assert_obj_set_and_save(attribute, value) obj.send :"#{attribute}=", value _(obj.send(attribute)).must_equal value @@ -101,7 +101,7 @@ def assert_obj_set_and_save(attribute, value) _(obj.bit).must_equal true _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Boolean + _(type).must_be_instance_of Type::Boolean _(type.limit).must_be_nil obj.bit = 0 _(obj.bit).must_equal false @@ -122,12 +122,12 @@ def assert_obj_set_and_save(attribute, value) _(obj.decimal_9_2).must_equal BigDecimal("12345.01") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(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.decimal_9_2).must_equal BigDecimal("1234567.89") obj.save! _(obj.reload.decimal_9_2).must_equal BigDecimal("1234567.89") end @@ -142,7 +142,7 @@ def assert_obj_set_and_save(attribute, value) _(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.decimal_16_4).must_equal BigDecimal("1234567.8901") obj.save! _(obj.reload.decimal_16_4).must_equal BigDecimal("1234567.8901") end @@ -156,12 +156,12 @@ def assert_obj_set_and_save(attribute, value) _(obj.numeric_18_0).must_equal BigDecimal("191") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 18 _(type.scale).must_equal 0 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") end @@ -175,12 +175,12 @@ def assert_obj_set_and_save(attribute, value) _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(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.numeric_36_2).must_equal BigDecimal("192.12") obj.save! _(obj.reload.numeric_36_2).must_equal BigDecimal("192.12") end @@ -194,14 +194,14 @@ def assert_obj_set_and_save(attribute, value) _(obj.money).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Money + _(type).must_be_instance_of Type::Money _(type.limit).must_be_nil _(type.precision).must_equal 19 _(type.scale).must_equal 4 obj.money = "922337203685477.58061" - _(obj.money).must_equal BigDecimal("922337203685477.5806") + _(obj.money).must_equal BigDecimal("922337203685477.5806") obj.save! - _(obj.reload.money).must_equal BigDecimal("922337203685477.5806") + _(obj.reload.money).must_equal BigDecimal("922337203685477.5806") end it "smallmoney" do @@ -213,12 +213,12 @@ def assert_obj_set_and_save(attribute, value) _(obj.smallmoney).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::SmallMoney + _(type).must_be_instance_of Type::SmallMoney _(type.limit).must_be_nil _(type.precision).must_equal 10 _(type.scale).must_equal 4 obj.smallmoney = "214748.36461" - _(obj.smallmoney).must_equal BigDecimal("214748.3646") + _(obj.smallmoney).must_equal BigDecimal("214748.3646") obj.save! _(obj.reload.smallmoney).must_equal BigDecimal("214748.3646") end @@ -236,12 +236,12 @@ def assert_obj_set_and_save(attribute, value) _(obj.float).must_equal 123.00000001 _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Float + _(type).must_be_instance_of Type::Float _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil obj.float = "214748.36461" - _(obj.float).must_equal 214748.36461 + _(obj.float).must_equal 214748.36461 obj.save! _(obj.reload.float).must_equal 214748.36461 end @@ -255,14 +255,14 @@ def assert_obj_set_and_save(attribute, value) _(obj.real).must_be_close_to 123.45, 0.01 _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Real + _(type).must_be_instance_of Type::Real _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil obj.real = "214748.36461" - _(obj.real).must_be_close_to 214748.36461, 0.01 + _(obj.real).must_be_close_to 214748.36461, 0.01 obj.save! - _(obj.reload.real).must_be_close_to 214748.36461, 0.01 + _(obj.reload.real).must_be_close_to 214748.36461, 0.01 end # Date and Time @@ -276,31 +276,31 @@ def assert_obj_set_and_save(attribute, value) _(obj.date).must_equal Date.civil(0001, 1, 1) _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Date + _(type).must_be_instance_of Type::Date _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil # Can cast strings. SQL Server format. obj.date = "04-01-0001" - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) # Can cast strings. ISO format. obj.date = "0001-04-01" - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.save! - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(0001, 4, 1) # Can keep and return assigned date. assert_obj_set_and_save :date, Date.civil(1972, 04, 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.save! - _(obj.reload.date).must_equal Date.civil(2010, 4, 14) + _(obj.reload.date).must_equal Date.civil(2010, 4, 14) end it "datetime" do @@ -313,7 +313,7 @@ def assert_obj_set_and_save(attribute, value) _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <123000>" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::DateTime + _(type).must_be_instance_of Type::DateTime _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil @@ -322,21 +322,21 @@ def assert_obj_set_and_save(attribute, value) # Can save to proper accuracy and return again. time = Time.utc 2010, 04, 01, 12, 34, 56, 3000 obj.datetime = time - _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! - _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.reload - _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" + _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" _(obj).must_equal obj.class.where(datetime: time).first # Will cast to true DB value on attribute write, save and return again. - time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 + time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 obj.datetime = time - _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.save! - _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.reload - _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" + _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" _(obj).must_equal obj.class.where(datetime: time).first _(obj).must_equal obj.class.where(datetime: time2).first # Set and find nil. @@ -358,30 +358,30 @@ def assert_obj_set_and_save(attribute, value) _(obj.datetime2_7).must_equal time, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <999999900>" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::DateTime2 + _(type).must_be_instance_of Type::DateTime2 _(type.limit).must_be_nil - _(type.precision).must_equal 7 + _(type.precision).must_equal 7 _(type.scale).must_be_nil obj.save! _(obj).must_equal obj.class.where(datetime2_7: time).first # Can save 100 nanosecond precisoins and return again. - time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) + time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) time2 = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetime2_7 = time - _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.save! - _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" obj.reload - _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" _(obj).must_equal obj.class.where(datetime2_7: time).first _(obj).must_equal obj.class.where(datetime2_7: time2).first # Can save small fraction nanosecond precisoins and return again. - time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) + time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) time2 = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15000, 1000) obj.datetime2_7 = time - _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" obj.save! - _(obj.reload.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" + _(obj.reload.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <15000>" _(obj).must_equal obj.class.where(datetime2_7: time).first _(obj).must_equal obj.class.where(datetime2_7: time2).first # datetime2_3 @@ -390,7 +390,7 @@ def assert_obj_set_and_save(attribute, value) _(connection.lookup_cast_type_from_column(col).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 @@ -398,7 +398,7 @@ def assert_obj_set_and_save(attribute, value) _(connection.lookup_cast_type_from_column(col).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 @@ -407,7 +407,7 @@ 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 @@ -422,17 +422,17 @@ def assert_obj_set_and_save(attribute, value) _(obj.datetimeoffset_7).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::DateTimeOffset + _(type).must_be_instance_of Type::DateTimeOffset _(type.limit).must_be_nil - _(type.precision).must_equal 7 + _(type.precision).must_equal 7 _(type.scale).must_be_nil # Can save 100 nanosecond precisoins and return again. obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755) - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.save! - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # Maintains the timezone time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetimeoffset_7 = time @@ -465,17 +465,17 @@ def assert_obj_set_and_save(attribute, value) _(obj.smalldatetime).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::SmallDateTime + _(type).must_be_instance_of Type::SmallDateTime _(type.limit).must_be_nil _(type.precision).must_be_nil _(type.scale).must_be_nil # Will remove fractional seconds and return again. obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" obj.save! - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" obj.reload - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end it "time(7)" do @@ -487,27 +487,27 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil - _(type.precision).must_equal 7 + _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" - obj.save! ; obj.reload + obj.save!; obj.reload _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" # Time's #usec precision (high micro) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" - obj.save! ; obj.reload - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + obj.save!; obj.reload + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" # Time's #usec precision (high nano rounded) obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" - obj.save! ; obj.reload - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + obj.save!; obj.reload + _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" end it "time(2)" do @@ -519,25 +519,25 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_be_nil _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil - _(type.precision).must_equal 2 + _(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, 01, 10, 15, 45, 00, 0) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) - obj.save! ; obj.reload - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + obj.save!; obj.reload + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) # Time's #usec precision (barely in 2 precision equal to 0.03 seconds) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 30000) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" - obj.save! ; obj.reload - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + obj.save!; obj.reload + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" # Time's #usec precision (below 2 precision) obj.time_2 = Time.utc(2000, 01, 01, 15, 45, 00, 4000) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" - obj.save! ; obj.reload - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + obj.save!; obj.reload + _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" end it "time using default precision" do @@ -549,27 +549,27 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal Time.utc(1900, 01, 01, 15, 03, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Time + _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil - _(type.precision).must_equal 7 + _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 300) _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" - obj.save! ; obj.reload + obj.save!; obj.reload _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" # Time's #usec precision (high micro) obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" - obj.save! ; obj.reload - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + obj.save!; obj.reload + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" # Time's #usec precision (high nano rounded) obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321545, 1000)) - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" - obj.save! ; obj.reload - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + obj.save!; obj.reload + _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" end # Character Strings @@ -589,9 +589,9 @@ def assert_obj_set_and_save(attribute, value) _(type.scale).must_be_nil # Basic set and save. obj.char_10 = "012345" - _(obj.char_10.strip).must_equal "012345" + _(obj.char_10.strip).must_equal "012345" obj.save! - _(obj.reload.char_10.strip).must_equal "012345" + _(obj.reload.char_10.strip).must_equal "012345" end it "varchar(50)" do @@ -662,9 +662,9 @@ def assert_obj_set_and_save(attribute, value) _(type.scale).must_be_nil # Basic set and save. obj.nchar_10 = "五六" - _(obj.nchar_10.strip).must_equal "五六" + _(obj.nchar_10.strip).must_equal "五六" obj.save! - _(obj.reload.nchar_10.strip).must_equal "五六" + _(obj.reload.nchar_10.strip).must_equal "五六" end it "nvarchar(50)" do @@ -790,21 +790,21 @@ def assert_obj_set_and_save(attribute, value) _(col.type).must_equal :uuid _(col.null).must_equal true _(col.default).must_be_nil - _(col.default_function).must_equal "newid()" + _(col.default_function).must_equal "newid()" type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Uuid + _(type).must_be_instance_of 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 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.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" + _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" + obj.save!; obj.reload + _(obj.uniqueidentifier).must_equal "6F9619FF-8B86-D011-B42D-00C04FC964FF" end it "timestamp" do @@ -815,14 +815,14 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_be_nil _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Timestamp + _(type).must_be_instance_of 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/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index cf270482e..19164f70f 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -18,7 +18,7 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase end it "allow multiple result sets to be returned" do - results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint","accounts") + results1, results2 = ActiveRecord::Base.execute_procedure("sp_helpconstraint", "accounts") assert_instance_of Array, results1 assert results1.first.respond_to?(:keys) assert results1.first["Object Name"] diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 08dfe9193..922bf9e37 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -10,7 +10,7 @@ class FetchTestSqlserver < ActiveRecord::TestCase it "work with fully qualified table and columns in select" do books = Book.select("books.id, books.name").limit(3).offset(5) - assert_equal Book.all[5,3].map(&:id), books.map(&:id) + assert_equal Book.all[5, 3].map(&:id), books.map(&:id) end describe "count" do @@ -33,7 +33,7 @@ class FetchTestSqlserver < ActiveRecord::TestCase describe "order" do it "gauntlet" do - Book.where(name:"Name-10").delete_all + Book.where(name: "Name-10").delete_all _(Book.order(:name).limit(1).offset(1).map(&:name)).must_equal ["Name-2"] _(Book.order(:name).limit(2).offset(2).map(&:name)).must_equal ["Name-3", "Name-4"] _(Book.order(:name).limit(2).offset(7).map(&:name)).must_equal ["Name-8", "Name-9"] diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index e02c87602..7b0003fa6 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -8,7 +8,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase before do @trans_test_table1 = "sqlserver_trans_table1" @trans_test_table2 = "sqlserver_trans_table2" - @trans_tables = [@trans_test_table1,@trans_test_table2] + @trans_tables = [@trans_test_table1, @trans_test_table2] end after do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 733f52d78..9a348efa5 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -31,7 +31,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil end - assert_line :smalldatetime, type: "smalldatetime",limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" + assert_line :smalldatetime, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil @@ -157,7 +157,7 @@ def line(column_name) @schema_lines[column_name.to_s] end - def assert_line(column_name, options={}) + def assert_line(column_name, options = {}) line = line(column_name) assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" [:type, :limit, :precision, :scale, :collation, :default].each do |key| diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index c6af81f0e..b51e7a974 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -22,7 +22,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it "have only one identity column" do 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.select { |c| c.is_identity? }.size end it "read only column properties for table in specific schema" do @@ -32,17 +32,17 @@ 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.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 end it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do columns = connection.columns("test.sst_schema_columns") - assert_equal 255, columns.find {|c| c.name == "name"}.limit - assert_equal 1000, columns.find {|c| c.name == "description"}.limit - assert_equal 255, columns.find {|c| c.name == "n_name"}.limit - assert_equal 1000, columns.find {|c| c.name == "n_description"}.limit + assert_equal 255, columns.find { |c| c.name == "name" }.limit + assert_equal 1000, columns.find { |c| c.name == "description" }.limit + assert_equal 255, columns.find { |c| c.name == "n_name" }.limit + assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit end end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index ddd6ad4b2..2aeb67990 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -145,11 +145,11 @@ def quoted_id SSTestEdgeSchema.create! description: "A" SSTestEdgeSchema.create! description: "B" SSTestEdgeSchema.create! description: "C" - assert_equal ["A","B","C"], SSTestEdgeSchema.order("description").map(&:description) - assert_equal ["A","B","C"], SSTestEdgeSchema.order("description asc").map(&:description) - assert_equal ["A","B","C"], SSTestEdgeSchema.order("description ASC").map(&:description) - assert_equal ["C","B","A"], SSTestEdgeSchema.order("description desc").map(&:description) - assert_equal ["C","B","A"], SSTestEdgeSchema.order("description DESC").map(&:description) + assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description").map(&:description) + assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description asc").map(&:description) + assert_equal ["A", "B", "C"], SSTestEdgeSchema.order("description ASC").map(&:description) + assert_equal ["C", "B", "A"], SSTestEdgeSchema.order("description desc").map(&:description) + assert_equal ["C", "B", "A"], SSTestEdgeSchema.order("description DESC").map(&:description) end # For uniqueidentifier model helpers diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index fc0a6598a..8af51e68a 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -3,7 +3,7 @@ require "cases/helper_sqlserver" class SQLServerTriggerTest < ActiveRecord::TestCase - after { exclude_output_inserted_table_names.clear } + after { exclude_output_inserted_table_names.clear } let(:exclude_output_inserted_table_names) do ActiveRecord::ConnectionAdapters::SQLServerAdapter.exclude_output_inserted_table_names diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 1ccd643e3..173b91a72 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -18,7 +18,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase describe ".extract_identifiers constructor and thus SQLServer::Utils::Name value object" do let(:valid_names) { valid_names_unquoted + valid_names_quoted } - let(:valid_names_unquoted) {[ + let(:valid_names_unquoted) { [ "server.database.schema.object", "server.database..object", "server..schema.object", @@ -29,7 +29,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase "object" ]} - let(:valid_names_quoted) {[ + let(:valid_names_quoted) { [ "[server].[database].[schema].[object]", "[server].[database]..[object]", "[server]..[schema].[object]", diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index 388f50da3..3430cf9ed 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -2,7 +2,7 @@ class TableWillNeverBeCreated < ActiveRecord::Migration def self.up - create_table(:sqlserver_trans_table1) { } + create_table(:sqlserver_trans_table1) {} create_table(:sqlserver_trans_table2) { raise("HELL") } end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 1d63c1339..939d5d077 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -83,7 +83,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 @@ -150,7 +150,7 @@ t.column(:fk_id2, :bigint) end - create_table(:sst_has_pks, force: true) { } + create_table(:sst_has_pks, force: true) {} execute <<-ADDFKSQL ALTER TABLE sst_has_fks ADD CONSTRAINT FK__sst_has_fks_id @@ -182,7 +182,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 @@ -227,7 +227,7 @@ # Another schema. create_table :sst_schema_columns, force: true do |t| - t.column :field1 , :integer + t.column :field1, :integer end execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test') EXEC sp_executesql N'CREATE SCHEMA test'" diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index 413faa576..bc0192d00 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -23,7 +23,7 @@ def sqlserver_cases def ar_cases @ar_cases ||= begin - Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject{ |x| x =~ /\/adapters\// }.sort + Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { |x| x =~ /\/adapters\// }.sort end end From 127b5949dff0c11f00d1ecc9fa0c246cc5cb58dd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 23:06:05 +0100 Subject: [PATCH 0893/1412] Rubocop: Enable Bundle/* cops --- .rubocop.yml | 3 +++ CHANGELOG.md | 12 ++++++++++++ Gemfile | 6 ++++-- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.rubocop.yml b/.rubocop.yml index 55040ad53..1f6fd0a3d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,5 +1,8 @@ AllCops: TargetRubyVersion: 2.5 +Layout/LineLength: + Max: 120 + Style/StringLiterals: EnforcedStyle: double_quotes diff --git a/CHANGELOG.md b/CHANGELOG.md index ab300f12e..351c2ece5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## v6.0.0.rc2 (unreleased) + +#### Fixed + +- [#826](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/826) Rubocop: Enable Style/StringLiterals cop +- [#827](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/827) Rubocop: Enable Layout/EmptyLinesAroundClassBody cop +- [#828](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/828) Rubocop: Enable Layout/EmptyLines cop +- [#829](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/829) Rubocop: Enable Layout/Layout/EmptyLinesAround* cops +- [#830](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/830) Rubocop: Enable Layout/IndentationWidth and Layout/TrailingWhitespace cops +- [#831](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/831) Rubocop: Enable Spacing cops +- [#832](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/832) Rubocop: Enable Bundler cops + ## v6.0.0.rc1 #### Fixed diff --git a/Gemfile b/Gemfile index ca095824d..e1e4894a5 100644 --- a/Gemfile +++ b/Gemfile @@ -40,6 +40,7 @@ else gem "rails", github: "rails/rails", tag: "v#{version}" end +# rubocop:disable Bundler/DuplicatedGem group :tinytds do if ENV["TINYTDS_SOURCE"] gem "tiny_tds", path: ENV["TINYTDS_SOURCE"] @@ -49,11 +50,12 @@ group :tinytds do gem "tiny_tds" end end +# rubocop:enable Bundler/DuplicatedGem group :development do - gem "pry-byebug", platform: [:mri, :mingw, :x64_mingw] - gem "mocha" gem "minitest-spec-rails" + gem "mocha" + gem "pry-byebug", platform: [:mri, :mingw, :x64_mingw] end group :guard do From 2cbc5b927f516f2e6b52e1259417ced206729c46 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 23:27:59 +0100 Subject: [PATCH 0894/1412] Rubocop: Enable Layout/* cops --- activerecord-sqlserver-adapter.gemspec | 6 +-- .../sqlserver/database_statements.rb | 5 ++ .../sqlserver/database_tasks.rb | 3 -- .../connection_adapters/sqlserver/quoting.rb | 2 +- .../sqlserver/schema_dumper.rb | 2 + .../sqlserver/schema_statements.rb | 35 ++++++++------ .../sqlserver/transaction.rb | 1 + .../sqlserver/type/char.rb | 2 + .../sqlserver/type/date.rb | 1 + .../sqlserver/type/time.rb | 1 + .../sqlserver/type/time_value_fractional.rb | 3 ++ .../sqlserver/type/uuid.rb | 1 + .../connection_adapters/sqlserver/utils.rb | 1 + .../connection_adapters/sqlserver_adapter.rb | 5 +- .../tasks/sqlserver_database_tasks.rb | 3 ++ lib/arel/visitors/sqlserver.rb | 10 ++-- test/cases/adapter_test_sqlserver.rb | 17 ++++--- test/cases/coerced_tests.rb | 37 +++++++++------ test/cases/column_test_sqlserver.rb | 2 + test/cases/fetch_test_sqlserver.rb | 1 - .../pessimistic_locking_test_sqlserver.rb | 10 ++-- test/cases/schema_dumper_test_sqlserver.rb | 11 +++-- test/cases/schema_test_sqlserver.rb | 1 - test/cases/utils_test_sqlserver.rb | 46 ++++++++++--------- test/schema/sqlserver_specific_schema.rb | 2 +- test/support/coerceable_test_sqlserver.rb | 1 + test/support/connection_reflection.rb | 1 + test/support/rake_helpers.rb | 2 + 28 files changed, 128 insertions(+), 84 deletions(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 1b66fbf32..a9c72c9e7 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -17,9 +17,9 @@ Gem::Specification.new do |spec| spec.description = "ActiveRecord SQL Server Adapter. SQL Server 2012 and upward." 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}", + "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}", } spec.files = `git ls-files -z`.split("\x0") diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index bba3752a9..20f940413 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -186,12 +186,14 @@ def with_identity_insert_enabled(table_name) def use_database(database = nil) return if sqlserver_azure? + name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted do_execute "USE #{name}" unless name.blank? end def user_options return {} if sqlserver_azure? + 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| @@ -315,6 +317,7 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) + case value = attr.value_for_database when Numeric value > 2_147_483_647 ? "bigint".freeze : "int".freeze @@ -376,8 +379,10 @@ def exclude_output_inserted_table_names? def exclude_output_inserted_table_name?(table_name, sql) return false unless exclude_output_inserted_table_names? + table_name ||= get_table_name(sql) return false unless table_name + self.class.exclude_output_inserted_table_names[table_name] end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 954d3d0fe..63f33356f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -63,6 +63,3 @@ def create_database_edition_options(options = {}) end end end - - - diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 430a1bc4c..6c955057f 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -65,7 +65,7 @@ def quoted_date(value) if value.acts_like?(:date) Type::Date.new.serialize(value) else value.acts_like?(:time) - Type::DateTime.new.serialize(value) + Type::DateTime.new.serialize(value) end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 99b4dab8d..ef69fee42 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -20,11 +20,13 @@ def explicit_primary_key_default?(column) def schema_limit(column) return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type) + super end def schema_collation(column) return unless column.collation + column.collation if column.collation != @connection.collation end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3f8eea12f..17b3976f4 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -66,6 +66,7 @@ def indexes(table_name) def columns(table_name) return [] if table_name.blank? + column_definitions(table_name).map do |ci| sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options @@ -131,6 +132,7 @@ def rename_table(table_name, new_name) def remove_column(table_name, column_name, type = nil, options = {}) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_name.is_a? Array + remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) @@ -144,9 +146,9 @@ def change_column(table_name, column_name, type, options = {}) without_constraints = options.key?(:default) || options.key?(:limit) default = if !options.key?(:default) && column_object column_object.default - else - options[:default] - end + else + options[:default] + end if without_constraints || (column_object && column_object.type != type.to_sym) remove_default_constraint(table_name, column_name) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } @@ -172,6 +174,7 @@ def change_column_default(table_name, column_name, default_or_changes) clear_cache! column = column_for(table_name, column_name) return unless column + remove_default_constraint(table_name, column_name) default = extract_new_default_value(default_or_changes) do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}" @@ -188,6 +191,7 @@ def rename_column(table_name, column_name, new_column_name) def rename_index(table_name, old_name, new_name) raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length + identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}") execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX" end @@ -249,10 +253,10 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map { |s| - s = s.to_sql unless s.is_a?(String) - s.gsub(/\s+(?:ASC|DESC)\b/i, "") - .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") - }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + s = s.to_sql unless s.is_a?(String) + s.gsub(/\s+(?:ASC|DESC)\b/i, "") + .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") + }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } (order_columns << super).join(", ") end @@ -507,11 +511,11 @@ def remove_indexes(table_name, column_name) def get_table_name(sql) tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i Regexp.last_match[3] || Regexp.last_match[4] - elsif sql =~ /FROM\s+([^\(\s]+)\s*/i - Regexp.last_match[1] - else - nil - end + elsif sql =~ /FROM\s+([^\(\s]+)\s*/i + Regexp.last_match[1] + else + nil + end SQLServer::Utils.extract_identifiers(tn).object end @@ -540,9 +544,9 @@ def view_information(table_name) 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 + rescue + warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;" + nil end end end @@ -553,6 +557,7 @@ def view_information(table_name) def views_real_column_name(table_name, column_name) view_definition = view_information(table_name)[:VIEW_DEFINITION] return column_name unless view_definition + match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) match_data ? match_data[1] : column_name end diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 08deeed44..d25617e9e 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -13,6 +13,7 @@ def sqlserver? def current_isolation_level return unless sqlserver? + level = connection.user_options_isolation_level # When READ_COMMITTED_SNAPSHOT is set to ON, # user_options_isolation_level will be equal to 'read committed diff --git a/lib/active_record/connection_adapters/sqlserver/type/char.rb b/lib/active_record/connection_adapters/sqlserver/type/char.rb index 55b1eae4b..2084f5132 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/char.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/char.rb @@ -12,6 +12,7 @@ def type def serialize(value) return if value.nil? return value if value.is_a?(Data) + Data.new super, self end @@ -24,6 +25,7 @@ def sqlserver_type def quoted(value) return value.quoted_id if value.respond_to?(:quoted_id) + Utils.quote_string_single(value) 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 f8993e3be..281fd2b0b 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -11,6 +11,7 @@ def sqlserver_type def serialize(value) return unless value.present? + date = super(value).to_s(:_sqlserver_dateformat) Data.new date, self end diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index 4261ae7b7..ef209641a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -37,6 +37,7 @@ def quoted(value) def cast_value(value) value = super return if value.blank? + value = value.change year: 2000, month: 01, day: 01 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 fe67d3d9e..f5c8744ea 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 @@ -9,11 +9,13 @@ module TimeValueFractional def apply_seconds_precision(value) return value if !value.respond_to?(fractional_property) || value.send(fractional_property).zero? + value.change fractional_property => seconds_precision(value) end def seconds_precision(value) return 0 if fractional_scale == 0 + seconds = value.send(fractional_property).to_f / fractional_operator.to_f seconds = ((seconds * (1 / fractional_precision)).round / (1 / fractional_precision)).round(fractional_scale) (seconds * fractional_operator).round(0).to_i @@ -21,6 +23,7 @@ def seconds_precision(value) def quote_fractional(value) return 0 if fractional_scale == 0 + frac_seconds = seconds_precision(value) seconds = (frac_seconds.to_f / fractional_operator.to_f).round(fractional_scale) seconds.to_d.to_s.split(".").last.to(fractional_scale - 1) diff --git a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb index 387796c12..d469f0bfd 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/uuid.rb @@ -19,6 +19,7 @@ def sqlserver_type def serialize(value) return unless value + Data.new super, self end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index c8836ae5f..c574dc693 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -75,6 +75,7 @@ def hash def parse_raw_name @parts = [] return if raw_name.blank? + scanner = StringScanner.new(raw_name) matched = scanner.exist?(QUOTED_CHECKER) ? scanner.scan_until(QUOTED_SCANNER) : scanner.scan_until(UNQUOTED_SCANNER) while matched diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 8d77c8b6c..dc641eff1 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -188,6 +188,7 @@ def disable_referential_integrity def active? return false unless @connection + raw_connection_do "SELECT 1" true rescue *connection_errors @@ -253,6 +254,7 @@ def sqlserver_azure? def database_prefix_remote_server? return false if database_prefix.blank? + name = SQLServer::Utils.extract_identifiers(database_prefix) name.fully_qualified? && name.object.blank? end @@ -420,7 +422,7 @@ def dblib_connect(config) appname: config_appname(config), login_timeout: config_login_timeout(config), timeout: config_timeout(config), - encoding: config_encoding(config), + encoding: config_encoding(config), azure: config[:azure], contained: config[:contained] ).tap do |client| @@ -476,6 +478,7 @@ def initialize_dateformatter def version_year return 2016 if sqlserver_version =~ /vNext/ + /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i rescue StandardError => e 2016 diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index df1415e0d..6b2df7e7c 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -64,6 +64,7 @@ def structure_dump(filename, extra_flags) view_args = connection.views.map { |v| Shellwords.escape(v) } command.concat(view_args) raise "Error dumping database" unless Kernel.system(command.join(" ")) + dump = File.read(filename) dump.gsub!(/^USE .*$\nGO\n/, "") # Strip db USE statements dump.gsub!(/^GO\n/, "") # Strip db GO statements @@ -110,11 +111,13 @@ def local_database?(configuration) def configuration_host_ip(configuration) return nil unless configuration["host"] + Socket::getaddrinfo(configuration["host"], "echo", Socket::AF_INET)[0][3] end def local_ipaddr?(host_ip) return false unless host_ip + LOCAL_IPADDR.any? { |ip| ip.include?(host_ip) } end end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f70cac55c..c28b190f0 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -90,8 +90,8 @@ def visit_Arel_Table o, collector else quote_table_name(o.name) end - rescue Exception => e - quote_table_name(o.name) + rescue Exception => e + quote_table_name(o.name) end if o.table_alias collector << "#{table_name} #{quote_table_name o.table_alias}" @@ -176,6 +176,7 @@ def visit_Make_Fetch_Happen o, collector def node_value(node) return nil unless node + case node.expr when NilClass then nil when Numeric then node.expr @@ -189,9 +190,11 @@ def select_statement_lock? def make_Fetch_Possible_And_Deterministic o return if o.limit.nil? && o.offset.nil? + t = table_From_Statement o pk = primary_Key_From_Table t return unless pk + if o.orders.empty? # Prefer deterministic vs a simple `(SELECT NULL)` expr. o.orders = [pk.asc] @@ -222,8 +225,9 @@ def table_From_Statement o def primary_Key_From_Table t return unless t + column_name = @connection.schema_cache.primary_keys(t.name) || - @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) + @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) column_name ? t[column_name] : nil end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index d20ced9e6..f11ce7f77 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -76,13 +76,13 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "test database exists returns false if database does not exist" do config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest") assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), - "expected database to not exist" + "expected database to not exist" end it "test database exists returns true when the database exists" do config = ActiveRecord::Base.configurations["arunit"] assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), - "expected database #{config[:database]} to exist" + "expected database #{config[:database]} to exist" end describe "with different language" do @@ -343,8 +343,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal columns.size, SSTestCustomersView.columns.size columns.each do |colname| assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - 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 @@ -370,8 +370,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, - 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 @@ -383,7 +383,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 @@ -398,7 +398,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "using alternate view defintion 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 @@ -474,4 +474,3 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end end - diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 782c7d15a..e33fa0bc5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -459,15 +459,21 @@ module ActiveRecord module DatabaseTasksSetupper def setup @sqlserver_tasks = - Class.new do - def create; end - def drop; end - def purge; end - def charset; end - def collation; end - def structure_dump(*); end - def structure_load(*); end - end.new + Class.new do + def create; end + + def drop; end + + def purge; end + + def charset; end + + def collation; end + + def structure_dump(*); end + + def structure_load(*); end + end.new $stdout, @original_stdout = StringIO.new, $stdout $stderr, @original_stderr = StringIO.new, $stderr @@ -552,8 +558,8 @@ class DatabaseTasksStructureDumpTest < ActiveRecord::TestCase def test_sqlserver_structure_dump with_stubbed_new do assert_called_with( - eval("@sqlserver_tasks"), :structure_dump, - ["awesome-file.sql", nil] + eval("@sqlserver_tasks"), :structure_dump, + ["awesome-file.sql", nil] ) do ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :sqlserver }, "awesome-file.sql") end @@ -568,9 +574,9 @@ class DatabaseTasksStructureLoadTest < ActiveRecord::TestCase def test_sqlserver_structure_load with_stubbed_new do assert_called_with( - eval("@sqlserver_tasks"), - :structure_load, - ["awesome-file.sql", nil] + eval("@sqlserver_tasks"), + :structure_load, + ["awesome-file.sql", nil] ) do ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => :sqlserver }, "awesome-file.sql") end @@ -1228,6 +1234,7 @@ module ActiveRecord module ConnectionAdapters class SchemaCacheTest < ActiveRecord::TestCase private + # We need to give the full path for this to work. def schema_dump_path File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml" @@ -1237,7 +1244,7 @@ def schema_dump_path end class UnsafeRawSqlTest < ActiveRecord::TestCase - # Use LEN() vs length() function. + # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} test "order: always allows Arel" do ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 09f57a8dc..c1d58a66c 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -14,7 +14,9 @@ class ColumnTestSQLServer < ActiveRecord::TestCase Type = ActiveRecord::ConnectionAdapters::SQLServer::Type def new_obj; SSTestDatatype.new; end + def column(name); SSTestDatatype.columns_hash[name]; end + def assert_obj_set_and_save(attribute, value) obj.send :"#{attribute}=", value _(obj.send(attribute)).must_equal value diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 922bf9e37..266052ce7 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -49,4 +49,3 @@ def create_10_books @books = (1..10).map { |i| Book.create! name: "Name-#{i}" } end end - diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index f63365356..a8dbbcdb5 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -39,11 +39,11 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase it "lock with eager find" do assert_nothing_raised do - Person.transaction do - person = Person.lock(true).includes(:readers).find(1) - _(person).must_equal Person.find(1) - end - end + Person.transaction do + person = Person.lock(true).includes(:readers).find(1) + _(person).must_equal Person.find(1) + end + end end it "can add a custom lock directive" do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 9a348efa5..3a9a3ad8c 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -28,14 +28,14 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :datetime, type: "datetime", limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" - assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil - assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil + assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil + assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil end assert_line :smalldatetime, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" if connection_dblib_73? assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" - assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil - assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" + assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil + assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" end # Character Strings assert_line :char_10, type: "char", limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil @@ -148,6 +148,7 @@ def generate_schema_for_table(*table_names) type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/ @generated_schema.each_line do |line| next unless line =~ type_matcher + @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line) end @generated_schema @@ -162,6 +163,7 @@ def assert_line(column_name, options = {}) assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" [:type, :limit, :precision, :scale, :collation, :default].each do |key| next unless options.key?(key) + actual = key == :type ? line.send(:type_method) : line.send(key) expected = options[key] message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" @@ -229,4 +231,3 @@ def parse_options(opts) end end end - diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index b51e7a974..40379cd51 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -46,4 +46,3 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end end end - diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 173b91a72..9ede9ab2b 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -18,27 +18,31 @@ class UtilsTestSQLServer < ActiveRecord::TestCase describe ".extract_identifiers constructor and thus SQLServer::Utils::Name value object" do let(:valid_names) { valid_names_unquoted + valid_names_quoted } - let(:valid_names_unquoted) { [ - "server.database.schema.object", - "server.database..object", - "server..schema.object", - "server...object", - "database.schema.object", - "database..object", - "schema.object", - "object" - ]} - - let(:valid_names_quoted) { [ - "[server].[database].[schema].[object]", - "[server].[database]..[object]", - "[server]..[schema].[object]", - "[server]...[object]", - "[database].[schema].[object]", - "[database]..[object]", - "[schema].[object]", - "[object]" - ]} + let(:valid_names_unquoted) { + [ + "server.database.schema.object", + "server.database..object", + "server..schema.object", + "server...object", + "database.schema.object", + "database..object", + "schema.object", + "object" + ] + } + + let(:valid_names_quoted) { + [ + "[server].[database].[schema].[object]", + "[server].[database]..[object]", + "[server]..[schema].[object]", + "[server]...[object]", + "[database].[schema].[object]", + "[database]..[object]", + "[schema].[object]", + "[object]" + ] + } let(:server_names) { valid_names.partition { |name| name =~ /server/ } } let(:database_names) { valid_names.partition { |name| name =~ /database/ } } diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 939d5d077..bae4e4b78 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -46,7 +46,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 diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index a0fba3fc0..bd8cd0e6d 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -21,6 +21,7 @@ def coerce_tests!(*methods) def coerce_all_tests! instance_methods(false).each do |method| next unless method.to_s =~ /\Atest/ + undef_method(method) end STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{self.name}" diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index 9fe1ac1fb..154346470 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -21,6 +21,7 @@ def connection_dblib? def connection_dblib_73? return false unless connection_dblib? + rc = connection.raw_connection rc.respond_to?(:tds_73?) && rc.tds_73? end diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index bc0192d00..fb22215d2 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -5,6 +5,7 @@ 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 @@ -14,6 +15,7 @@ def env_ar_test_files def env_test_files return unless ENV["TEST_FILES"] && !ENV["TEST_FILES"].empty? + @env_test_files ||= ENV["TEST_FILES"].split(",").map(&:strip) end From b7c6bf383678629d907933913a15413bf8880a08 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 15 May 2020 23:53:55 +0100 Subject: [PATCH 0895/1412] Update CHANGELOG --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 351c2ece5..e514d32c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Fixed +- [#720](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/720) quoted_date doesn't work for Type::DateTime - [#826](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/826) Rubocop: Enable Style/StringLiterals cop - [#827](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/827) Rubocop: Enable Layout/EmptyLinesAroundClassBody cop - [#828](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/828) Rubocop: Enable Layout/EmptyLines cop @@ -9,6 +10,7 @@ - [#830](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/830) Rubocop: Enable Layout/IndentationWidth and Layout/TrailingWhitespace cops - [#831](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/831) Rubocop: Enable Spacing cops - [#832](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/832) Rubocop: Enable Bundler cops +- [#833](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/833) Rubocop: Enable Layout/* cops ## v6.0.0.rc1 From 0b98c15d078440e745a4319aa6e4abbab696e652 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 00:04:42 +0100 Subject: [PATCH 0896/1412] Rubocop: Enable Lint/UselessAssignment cop --- CHANGELOG.md | 1 + Gemfile | 4 ++-- .../connection_adapters/sqlserver_adapter.rb | 2 +- lib/arel/visitors/sqlserver.rb | 16 +++++++++------- test/cases/coerced_tests.rb | 6 +++--- test/cases/specific_schema_test_sqlserver.rb | 11 +++++++---- test/cases/transaction_test_sqlserver.rb | 2 +- 7 files changed, 24 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e514d32c8..afa558b55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#831](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/831) Rubocop: Enable Spacing cops - [#832](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/832) Rubocop: Enable Bundler cops - [#833](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/833) Rubocop: Enable Layout/* cops +- [#834](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/834) Rubocop: Enable Lint/UselessAssignment cop ## v6.0.0.rc1 diff --git a/Gemfile b/Gemfile index e1e4894a5..e81c9033f 100644 --- a/Gemfile +++ b/Gemfile @@ -22,7 +22,7 @@ else spec = eval(File.read("activerecord-sqlserver-adapter.gemspec")) ver = spec.dependencies.detect { |d| d.name == "activerecord" }.requirement.requirements.first.last.version - major, minor, tiny, pre = ver.split(".") + major, minor, _tiny, pre = ver.split(".") if pre ver @@ -32,7 +32,7 @@ else http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_NONE YAML.load(http.request(Net::HTTP::Get.new(uri.request_uri)).body).find do |data| - a, b, c = data["number"].split(".") + a, b, = data["number"].split(".") !data["prerelease"] && major == a && (minor.nil? || minor == b) end["number"] end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index dc641eff1..867a66001 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -480,7 +480,7 @@ def version_year return 2016 if sqlserver_version =~ /vNext/ /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i - rescue StandardError => e + rescue StandardError 2016 end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index c28b190f0..680ee8be5 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -84,15 +84,17 @@ def visit_Arel_Table o, collector # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 - table_name = begin - if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? - remote_server_table_name(o) - else + table_name = + begin + if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? + remote_server_table_name(o) + else + quote_table_name(o.name) + end + rescue Exception quote_table_name(o.name) end - rescue Exception => e - quote_table_name(o.name) - end + if o.table_alias collector << "#{table_name} #{quote_table_name o.table_alias}" else diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e33fa0bc5..a5ed54869 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -619,7 +619,7 @@ class EachTest < ActiveRecord::TestCase # Quoting in tests does not cope with bracket quoting. coerce_tests! :test_find_in_batches_should_quote_batch_order def test_find_in_batches_should_quote_batch_order_coerced - c = Post.connection + Post.connection assert_sql(/ORDER BY \[posts\]\.\[id\]/) do Post.find_in_batches(:batch_size => 1) do |batch| assert_kind_of Array, batch @@ -631,7 +631,7 @@ def test_find_in_batches_should_quote_batch_order_coerced # Quoting in tests does not cope with bracket quoting. coerce_tests! :test_in_batches_should_quote_batch_order def test_in_batches_should_quote_batch_order_coerced - c = Post.connection + Post.connection assert_sql(/ORDER BY \[posts\]\.\[id\]/) do Post.in_batches(of: 1) do |relation| assert_kind_of ActiveRecord::Relation, relation @@ -758,7 +758,7 @@ def test_a_bad_type_column_coerced # Use Square brackets around column name coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced - con = Account.connection + Account.connection assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do Account.all.merge!(:includes => :firm).find(1) end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 2aeb67990..6148f0874 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -23,13 +23,16 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase end it "quote table names properly even when they are views" do - obj = SSTestQuotedTable.create! + SSTestQuotedTable.create! assert_nothing_raised { assert SSTestQuotedTable.first } - obj = SSTestQuotedTableUser.create! + + SSTestQuotedTableUser.create! assert_nothing_raised { assert SSTestQuotedTableUser.first } - obj = SSTestQuotedView1.create! + + SSTestQuotedView1.create! assert_nothing_raised { assert SSTestQuotedView1.first } - obj = SSTestQuotedView2.create! + + SSTestQuotedView2.create! assert_nothing_raised { assert SSTestQuotedView2.first } end diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 974f5829c..f48bb2db1 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -26,7 +26,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase raise "HELL" end end - rescue Exception => e + rescue Exception assert_no_ships end end From b4856368a6fc4e815a7166c367be66aee6f430e9 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 14:55:22 +0100 Subject: [PATCH 0897/1412] Rubocop: Enable Naming/VariableNumber cop --- test/cases/column_test_sqlserver.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index c1d58a66c..01339faff 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -759,12 +759,12 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Basic set and save. - binary_data_20 = binary_data.to(20) - _(binary_data_20.encoding).must_equal Encoding::BINARY - obj.varbinary_49 = binary_data_20 - _(obj.varbinary_49).must_equal binary_data_20 + binary_data20 = binary_data.to(20) + _(binary_data20.encoding).must_equal Encoding::BINARY + obj.varbinary_49 = binary_data20 + _(obj.varbinary_49).must_equal binary_data20 obj.save! - _(obj.reload.varbinary_49).must_equal binary_data_20 + _(obj.reload.varbinary_49).must_equal binary_data20 end it "varbinary(max)" do From 16c4c3f7c6dba0331e18f6a650e2a9786702abc5 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 14:58:43 +0100 Subject: [PATCH 0898/1412] Rubocop: Enable Naming/RescuedExceptionsVariableName cop --- lib/active_record/tasks/sqlserver_database_tasks.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 6b2df7e7c..cb5ee2dee 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -21,8 +21,8 @@ def create(master_established = false) establish_master_connection unless master_established connection.create_database configuration["database"], configuration.merge("collation" => default_collation) establish_connection configuration - rescue ActiveRecord::StatementInvalid => error - if /database .* already exists/i === error.message + rescue ActiveRecord::StatementInvalid => e + if /database .* already exists/i === e.message raise DatabaseAlreadyExists else raise From 4123e780d218e98247dd73c0863c19093b7b3f7a Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:00:13 +0100 Subject: [PATCH 0899/1412] Rubocop: Enable Naming/FileName cop --- .rubocop.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 1f6fd0a3d..4d5ed898d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,5 +4,9 @@ AllCops: Layout/LineLength: Max: 120 +Naming/FileName: + Exclude: + - lib/activerecord-sqlserver-adapter.rb + Style/StringLiterals: EnforcedStyle: double_quotes From d3aa43b1f1a4f1ceb093f3069242fc439b3a516d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:01:02 +0100 Subject: [PATCH 0900/1412] Rubocop: Enable Naming/BinaryOperatorParameterName cop --- lib/active_record/connection_adapters/sqlserver/utils.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index c574dc693..3a7a71635 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -61,8 +61,8 @@ def quoted_raw quote @raw_name end - def ==(o) - o.class == self.class && o.parts == parts + def ==(other) + other.class == self.class && other.parts == parts end alias_method :eql?, :== From 477144797b427c976641a908a141baadfa6756ca Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:11:23 +0100 Subject: [PATCH 0901/1412] Rubocop: Disable Naming/AccessorMethodName cop --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 4d5ed898d..f73aa4c9d 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -4,6 +4,9 @@ AllCops: Layout/LineLength: Max: 120 +Naming/AccessorMethodName: + Enabled: false + Naming/FileName: Exclude: - lib/activerecord-sqlserver-adapter.rb From 4ec85901f38888dd49f1ae4506b29e7b7047c857 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:17:02 +0100 Subject: [PATCH 0902/1412] Rubocop: Enable Naming/MethodName cop --- .rubocop.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index f73aa4c9d..8cc702ce4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,5 +11,13 @@ Naming/FileName: Exclude: - lib/activerecord-sqlserver-adapter.rb +Naming/MethodName: + IgnoredPatterns: + - visit_.* + - primary_Key_From_Table + - table_From_Statement + - distinct_One_As_One_Is_So_Not_Fetch + - make_Fetch_Possible_And_Deterministic + Style/StringLiterals: EnforcedStyle: double_quotes From 63e06ccef72bb879cbb7a040ad9747b54141970e Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:19:08 +0100 Subject: [PATCH 0903/1412] Rubocop: Disable Naming/MethodParameterName cop --- .rubocop.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index 8cc702ce4..da16b3391 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -19,5 +19,8 @@ Naming/MethodName: - distinct_One_As_One_Is_So_Not_Fetch - make_Fetch_Possible_And_Deterministic +Naming/MethodParameterName: + Enabled: false + Style/StringLiterals: EnforcedStyle: double_quotes From 56df14aba64da134d98e085c69d66adb9f87f248 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 16 May 2020 15:27:15 +0100 Subject: [PATCH 0904/1412] Rubocop: Disable Naming/PredicateName cop --- .rubocop.yml | 3 +++ CHANGELOG.md | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/.rubocop.yml b/.rubocop.yml index da16b3391..048b917d8 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -22,5 +22,8 @@ Naming/MethodName: Naming/MethodParameterName: Enabled: false +Naming/PredicateName: + Enabled: false + Style/StringLiterals: EnforcedStyle: double_quotes diff --git a/CHANGELOG.md b/CHANGELOG.md index afa558b55..4f6608997 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ #### Fixed - [#720](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/720) quoted_date doesn't work for Type::DateTime + +#### Changed + - [#826](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/826) Rubocop: Enable Style/StringLiterals cop - [#827](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/827) Rubocop: Enable Layout/EmptyLinesAroundClassBody cop - [#828](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/828) Rubocop: Enable Layout/EmptyLines cop @@ -12,6 +15,7 @@ - [#832](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/832) Rubocop: Enable Bundler cops - [#833](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/833) Rubocop: Enable Layout/* cops - [#834](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/834) Rubocop: Enable Lint/UselessAssignment cop +- [#835](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/835) Rubocop: Configure Naming cops ## v6.0.0.rc1 From 661f5dfa4d080f3db57f441c18623ffc05b4547f Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 19 May 2020 19:17:03 +0100 Subject: [PATCH 0905/1412] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f6608997..ed2425c88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Fixed +- [#639](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/639) Primary key should be lowercase if schema forced to lowercase - [#720](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/720) quoted_date doesn't work for Type::DateTime #### Changed From f5861b72594862f682689d677722354238bc9029 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 19 May 2020 20:11:08 +0100 Subject: [PATCH 0906/1412] Release 6.0.0.rc2 --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed2425c88..11ee63c45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## v6.0.0.rc2 (unreleased) +## v6.0.0.rc2 #### Fixed diff --git a/VERSION b/VERSION index 9746b9fe0..d1b5748eb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0.rc1 +6.0.0.rc2 From 40a46adaf907b2912d97e5134edbf6e05274c09e Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 8 Jun 2020 18:37:36 +0100 Subject: [PATCH 0907/1412] Release v6.0.0 --- CHANGELOG.md | 4 ++++ VERSION | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 11ee63c45..b85a2a36e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v6.0.0 + +**No Changes** + ## v6.0.0.rc2 #### Fixed diff --git a/VERSION b/VERSION index d1b5748eb..09b254e90 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0.rc2 +6.0.0 From dfb79b2898baf60670fe8c56aa2ff2a63723897c Mon Sep 17 00:00:00 2001 From: Ben McHone Date: Sun, 17 Jan 2021 09:01:43 -0600 Subject: [PATCH 0908/1412] Updated 'column_definitions_sql' to ensure that only primary key key constraints are queried for --- .../connection_adapters/sqlserver/schema_statements.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 521f51ee2..01e4e45b8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -472,6 +472,7 @@ def column_definitions_sql(database, identifier) AND c.default_object_id = d.object_id LEFT OUTER JOIN #{database}.sys.key_constraints k ON c.object_id = k.parent_object_id + AND k.type = 'PK' LEFT OUTER JOIN #{database}.sys.index_columns ic ON k.parent_object_id = ic.object_id AND k.unique_index_id = ic.index_id From 42c8e81e4f256e0172456d52a148fd90755d40fc Mon Sep 17 00:00:00 2001 From: Ben McHone Date: Sat, 23 Jan 2021 10:04:07 -0600 Subject: [PATCH 0909/1412] Added test case for duplicate entries in schema.rb --- test/cases/schema_dumper_test_sqlserver.rb | 6 ++++++ test/schema/sqlserver_specific_schema.rb | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 3a9a3ad8c..4e30dd458 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -135,6 +135,12 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :name, type: "string", limit: nil, default: nil, collation: nil end + it "dumps field with unique key constraints only once" do + output = generate_schema_for_table "unique_key_dumped_table" + + _(output.scan('t.integer "unique_field"').length).must_equal(1) + end + private def generate_schema_for_table(*table_names) diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index bae4e4b78..9964cef4f 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -277,4 +277,14 @@ field_2 int NOT NULL PRIMARY KEY, ) SCHEMATESTMULTIPLESCHEMA + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'unique_key_dumped_table') DROP TABLE unique_key_dumped_table" + execute <<-SQLSERVERUNIQUEKEYS + CREATE TABLE unique_key_dumped_table ( + id int IDENTITY(1,1) NOT NULL, + unique_field int DEFAULT 0 NOT NULL, + CONSTRAINT IX_UNIQUE_KEY UNIQUE (unique_field), + CONSTRAINT PK_UNIQUE_KEY PRIMARY KEY (id) + ); + SQLSERVERUNIQUEKEYS end From a657057cba61d19f25a53669bfff1017d6fc5687 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 16 Feb 2021 21:28:45 +0000 Subject: [PATCH 0910/1412] Update CHANGELOG.md --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b85a2a36e..d7a65b22c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## v6.0.1 + +#### Fixed + +- [#851](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/851) Updated 'column_definitions_sql' to ensure that only primary key key constraints are queried for + ## v6.0.0 **No Changes** From 6b472dcd76d7b218d43c68726b06b7903f5e818e Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 16 Feb 2021 21:32:11 +0000 Subject: [PATCH 0911/1412] Release v6.0.1 --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 09b254e90..5fe607230 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0 +6.0.1 From 0937226539be1e08f56411cba26a9765c4478a0d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 9 Mar 2021 21:56:20 +0000 Subject: [PATCH 0912/1412] Update CHANGELOG.md --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d7a65b22c..f2c26db0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## Unreleased + +#### Fixed + +- ... + +#### Changed + +- ... + +#### Added + +- ... + ## v6.0.1 #### Fixed From 01340d7ae4a01319153ab7f4af696dab7acaf882 Mon Sep 17 00:00:00 2001 From: Bruno Castro Date: Wed, 10 Mar 2021 11:18:47 -0300 Subject: [PATCH 0913/1412] feat: add helpers to create/drop a schema (#855) --- CHANGELOG.md | 2 +- README.md | 18 ++++++++ .../sqlserver/schema_statements.rb | 15 +++++++ test/cases/migration_test_sqlserver.rb | 44 +++++++++++++++++++ 4 files changed, 78 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2c26db0a..a5e72b7df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ #### Added -- ... +- [#855](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/855) Add helpers to create/change/drop a schema. ## v6.0.1 diff --git a/README.md b/README.md index 901381388..6e33771c4 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,24 @@ Depending on your user and schema setup, it may be needed to use a table name pr ActiveRecord::Base.table_name_prefix = 'dbo.' ``` +It's also possible to create/change/drop a schema in the migration file as in the example below: + +```ruby +class CreateFooSchema < ActiveRecord::Migration[6.0] + def up + create_schema('foo') + + # Or you could move a table to a different schema + + change_table_schema('foo', 'dbo.admin') + end + + def down + drop_schema('foo') + end +end +``` + #### Configure Connection & App Name diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 01e4e45b8..14bece19a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -281,6 +281,21 @@ def create_schema_dumper(options) SQLServer::SchemaDumper.create(self, options) end + def create_schema(schema_name, authorization = nil) + sql = "CREATE SCHEMA [#{schema_name}]" + sql += " AUTHORIZATION [#{authorization}]" if authorization + + execute sql + end + + def change_table_schema(schema_name, table_name) + execute "ALTER SCHEMA [#{schema_name}] TRANSFER [#{table_name}]" + end + + def drop_schema(schema_name) + execute "DROP SCHEMA [#{schema_name}]" + end + private def data_source_sql(name = nil, type: nil) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 7b0003fa6..31fa0180a 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -64,4 +64,48 @@ class MigrationTestSQLServer < ActiveRecord::TestCase assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } end end + + describe "#create_schema" do + it "creates a new schema" do + connection.create_schema("some schema") + + schemas = connection.exec_query("select name from sys.schemas").to_a + + assert_includes schemas, { "name" => "some schema" } + end + + it "creates a new schema with an owner" do + connection.create_schema("some schema", :guest) + + schemas = connection.exec_query("select name, principal_id from sys.schemas").to_a + + assert_includes schemas, { "name" => "some schema", "principal_id" => 2 } + end + end + + describe "#change_table_schema" do + before { connection.create_schema("foo") } + + it "transfer the given table to the given schema" do + connection.change_table_schema("foo", "orders") + + assert connection.data_source_exists?("foo.orders") + end + end + + describe "#drop_schema" do + before { connection.create_schema("some schema") } + + it "drops a schema" do + schemas = connection.exec_query("select name from sys.schemas").to_a + + 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" } + end + end end From 028d7af1e47b8729814edbabfeddb8e050b0177e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 10 Mar 2021 19:50:00 +0000 Subject: [PATCH 0914/1412] Include database and owner names in column name matchers (#852) --- CHANGELOG.md | 2 +- .../connection_adapters/sqlserver/quoting.rb | 8 +- test/cases/coerced_tests.rb | 84 +++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5e72b7df..c6a4acd8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ #### Changed -- ... +- [#852](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/852) Updated the column name matchers to accept database and owner names #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 81b5aad5d..f8d335eb0 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -83,8 +83,8 @@ def column_name_with_order_matcher \A ( (?: - # [table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) ) (?:\s+AS\s+(?:\w+|\[\w+\]))? ) @@ -96,8 +96,8 @@ def column_name_with_order_matcher \A ( (?: - # [table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) ) (?:\s+ASC|\s+DESC)? (?:\s+NULLS\s+(?:FIRST|LAST))? diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a5ed54869..3e8181a3a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1243,7 +1243,11 @@ def schema_dump_path end end +require "models/post" +require "models/comment" class UnsafeRawSqlTest < ActiveRecord::TestCase + fixtures :posts + # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} test "order: always allows Arel" do @@ -1273,6 +1277,86 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids_depr assert_equal ids_expected, ids_disabled end + + test "order: allows string column names that are quoted" do + ids_expected = Post.order(Arel.sql("id")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[id]").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[id]").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: allows string column names that are quoted with table" do + ids_expected = Post.order(Arel.sql("id")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[posts].[id]").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[posts].[id]").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: allows string column names that are quoted with table and user" do + ids_expected = Post.order(Arel.sql("id")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[dbo].[posts].[id]").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[dbo].[posts].[id]").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "order: allows string column names that are quoted with table, user and database" do + ids_expected = Post.order(Arel.sql("id")).pluck(:id) + + ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id) } + ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id) } + + assert_equal ids_expected, ids_depr + assert_equal ids_expected, ids_disabled + end + + test "pluck: allows string column name that are quoted" do + titles_expected = Post.pluck(Arel.sql("title")) + + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[title]") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[title]") } + + assert_equal titles_expected, titles_depr + assert_equal titles_expected, titles_disabled + end + + test "pluck: allows string column name that are quoted with table" do + titles_expected = Post.pluck(Arel.sql("title")) + + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[posts].[title]") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[posts].[title]") } + + assert_equal titles_expected, titles_depr + assert_equal titles_expected, titles_disabled + end + + test "pluck: allows string column name that are quoted with table and user" do + titles_expected = Post.pluck(Arel.sql("title")) + + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[dbo].[posts].[title]") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[dbo].[posts].[title]") } + + assert_equal titles_expected, titles_depr + assert_equal titles_expected, titles_disabled + end + + test "pluck: allows string column name that are quoted with table, user and database" do + titles_expected = Post.pluck(Arel.sql("title")) + + titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[activerecord_unittest].[dbo].[posts].[title]") } + titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[activerecord_unittest].[dbo].[posts].[title]") } + + assert_equal titles_expected, titles_depr + assert_equal titles_expected, titles_disabled + end end class ReservedWordTest < ActiveRecord::TestCase From 3dcf7970385c9fedc4c491aab7854a53380a7e68 Mon Sep 17 00:00:00 2001 From: runephilosof-abtion <57357936+runephilosof-abtion@users.noreply.github.com> Date: Mon, 22 Mar 2021 17:30:42 +0100 Subject: [PATCH 0915/1412] Documentation: Fix link (#860) --- RUNNING_UNIT_TESTS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 4b06c391a..6089d06c0 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -5,7 +5,7 @@ This process is much easier than it has been before! ## MS SQL SERVER -If you don't have easy access to MS SQL Server, you can set up a Vagrant/VirtualBox virtual machine with MS SQL Server. [Here's how](/https://github.com/rails-sqlserver/activerecord-sqlserver-adapter-dev-box). +If you don't have easy access to MS SQL Server, you can set up a Vagrant/VirtualBox virtual machine with MS SQL Server. [Here's how](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter-dev-box). ## TL;DR From f13604b37d65b889a86517fd4d32eca947ff2627 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Mar 2021 16:31:10 +0000 Subject: [PATCH 0916/1412] Allow table existence to be tested across database schemas (#858) --- CHANGELOG.md | 2 +- .../sqlserver/schema_statements.rb | 11 ++++-- test/cases/adapter_test_sqlserver.rb | 36 +++++++++++++++++-- 3 files changed, 43 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c6a4acd8a..0249a4be9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ #### Fixed -- ... +- [#858](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/858) Allow table existence to be tested across database schemas. #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 14bece19a..7d19774a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -300,10 +300,14 @@ def drop_schema(schema_name) def data_source_sql(name = nil, type: nil) scope = quoted_scope name, type: type - table_name = lowercase_schema_reflection_sql "TABLE_NAME" + + 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 #{table_name}" - sql += " FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)" - sql += " WHERE TABLE_CATALOG = DB_NAME()" + sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)" + sql += " WHERE TABLE_CATALOG = #{table_catalog}" sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] @@ -314,6 +318,7 @@ def data_source_sql(name = nil, type: nil) def quoted_scope(name = nil, type: nil) identifier = SQLServer::Utils.extract_identifiers(name) {}.tap do |scope| + scope[:database] = identifier.database if identifier.database scope[:schema] = identifier.schema || "dbo" scope[:name] = identifier.object if identifier.object scope[:type] = type if type diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index f11ce7f77..799cc27d0 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -6,6 +6,7 @@ require "models/post" require "models/subscriber" require "models/minimalistic" +require "models/college" class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks @@ -42,17 +43,48 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert connection.supports_ddl_transactions? end - it "allow owner table name prefixs like dbo to still allow table exists to return true" do + it "table exists works if table name prefixed by schema and owner" do begin 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?, "Tasks table name of dbo.topics should return true for exists." + assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." + + # Test when database and owner included in table name. + Topic.table_name = "#{ActiveRecord::Base.configurations["arunit"]['database']}.dbo.topics" + assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." ensure Topic.table_name = "topics" end end + it "test table existence across database schemas" do + arunit_connection = Topic.connection + arunit2_connection = College.connection + + arunit_database = arunit_connection.pool.spec.config[:database] + arunit2_database = arunit2_connection.pool.spec.config[:database] + + # Assert that connections use different default databases schemas. + 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 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 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' + end + it "return true to insert sql query for inserts only" do assert connection.send(:insert_sql?, "INSERT...") assert connection.send(:insert_sql?, "EXEC sp_executesql N'INSERT INTO [fk_test_has_fks] ([fk_id]) VALUES (@0); SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident', N'@0 int', @0 = 0") From 90189126b4d0c2b393659a907910e570701afa5f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Mar 2021 16:32:00 +0000 Subject: [PATCH 0917/1412] Added WAITFOR as read query (#857) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0249a4be9..1d3100aca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ #### Added - [#855](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/855) Add helpers to create/change/drop a schema. +- [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. ## v6.0.1 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 20f940413..a3869cdc2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -4,7 +4,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseStatements - READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback) # :nodoc: + READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor) # :nodoc: private_constant :READ_QUERY def write_query?(sql) # :nodoc: From 2bf00780f6cd12c819a9f4b34b0764cd4326e708 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 31 Mar 2021 15:08:45 -0300 Subject: [PATCH 0918/1412] Implement optimizer_hints using OPTION clause (#865) * add support to optimizer_hints * Update changelog * add more specs * coerce tests * case insensitive sanitize --- CHANGELOG.md | 3 +- .../connection_adapters/sqlserver_adapter.rb | 4 ++ lib/arel/visitors/sqlserver.rb | 18 +++++ test/cases/coerced_tests.rb | 15 ++++ test/cases/optimizer_hints_test_sqlserver.rb | 72 +++++++++++++++++++ 5 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 test/cases/optimizer_hints_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d3100aca..bd6539a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,8 @@ #### Added - [#855](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/855) Add helpers to create/change/drop a schema. -- [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. +- [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. +- [#865](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/865) Implemented optimizer hints. ## v6.0.1 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 867a66001..c30909aee 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -152,6 +152,10 @@ def supports_savepoints? true end + def supports_optimizer_hints? + true + end + def supports_lazy_transactions? true end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 680ee8be5..2642c6950 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -80,6 +80,16 @@ def visit_Arel_Nodes_SelectStatement o, collector @select_statement = nil end + def visit_Arel_Nodes_SelectCore(o, collector) + collector = super + maybe_visit o.optimizer_hints, collector + end + + def visit_Arel_Nodes_OptimizerHints(o, collector) + hints = o.expr.map { |v| sanitize_as_option_clause(v) }.join(", ") + collector << "OPTION (#{hints})" + end + def visit_Arel_Table o, collector # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: @@ -144,6 +154,10 @@ def collect_in_clause(left, right, collector) super end + def collect_optimizer_hints(o, collector) + collector + end + # SQLServer ToSql/Visitor (Additions) def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} @@ -247,6 +261,10 @@ def remove_invalid_ordering_from_select_statement(node) node.orders = [] unless node.offset || node.limit end + + def sanitize_as_option_clause(value) + value.gsub(%r{OPTION \s* \( (.+) \)}xi, "\\1") + end end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3e8181a3a..bc221e98d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -982,6 +982,21 @@ def test_reverse_arel_assoc_order_with_function_coerced end end +module ActiveRecord + class RelationTest < ActiveRecord::TestCase + # Skipping this test. SQL Server doesn't support optimizer hint as comments + coerce_tests! :test_relation_with_optimizer_hints_filters_sql_comment_delimiters + + coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge + def test_does_not_duplicate_optimizer_hints_on_merge_coerced + escaped_table = Post.connection.quote_table_name("posts") + expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)" + query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql + assert_equal expected, query + end + end +end + require "models/post" class SanitizeTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb new file mode 100644 index 000000000..ba0438c46 --- /dev/null +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "models/company" + +class OptimizerHitsTestSQLServer < ActiveRecord::TestCase + fixtures :companies + + it "apply optimizations" do + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + companies = Company.optimizer_hints("HASH GROUP") + companies = companies.distinct.select("firm_id") + assert_includes companies.explain, "| Hash Match | Aggregate |" + end + + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do + companies = Company.optimizer_hints("ORDER GROUP") + companies = companies.distinct.select("firm_id") + assert_includes companies.explain, "| Stream Aggregate | Aggregate |" + end + end + + it "apply multiple optimizations" do + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do + companies = Company.optimizer_hints("HASH GROUP", "FAST 1") + companies = companies.distinct.select("firm_id") + assert_includes companies.explain, "| Hash Match | Flow Distinct |" + end + end + + it "support subqueries" do + assert_sql(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do + companies = Company.optimizer_hints("MAXDOP 2") + companies = companies.select(:id).where(firm_id: [0, 1]).limit(3) + assert_equal 3, companies.count + end + end + + it "sanitize values" do + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + companies = Company.optimizer_hints("OPTION (HASH GROUP)") + companies = companies.distinct.select("firm_id") + companies.to_a + end + + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + companies = Company.optimizer_hints("OPTION(HASH GROUP)") + companies = companies.distinct.select("firm_id") + companies.to_a + end + + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(TABLE HINT \(\[companies\], INDEX\(1\)\)\)\z}) do + companies = Company.optimizer_hints("OPTION(TABLE HINT ([companies], INDEX(1)))") + companies = companies.distinct.select("firm_id") + companies.to_a + end + + assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + companies = Company.optimizer_hints("Option(HASH GROUP)") + companies = companies.distinct.select("firm_id") + companies.to_a + end + end + + it "skip optimization after unscope" do + assert_sql("SELECT DISTINCT [companies].[firm_id] FROM [companies]") do + companies = Company.optimizer_hints("HASH GROUP") + companies = companies.distinct.select("firm_id") + companies.unscope(:optimizer_hints).load + end + end +end From 08dc075a5bc2fcca3cf8a06c9f3bbea8dbf66146 Mon Sep 17 00:00:00 2001 From: runephilosof-abtion <57357936+runephilosof-abtion@users.noreply.github.com> Date: Mon, 5 Apr 2021 00:10:53 +0200 Subject: [PATCH 0919/1412] Update Ruby 2.7.x in CI. (#862) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c07aa2b9f..3681c8bb8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,5 +19,5 @@ matrix: env: TARGET_VERSION=2.5.8 - name: 2.6.6 env: TARGET_VERSION=2.6.6 - - name: 2.7.1 - env: TARGET_VERSION=2.7.1 + - name: 2.7.2 + env: TARGET_VERSION=2.7.2 From e48f94d67689601d131fb6a454b05036862868ed Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 5 Apr 2021 18:11:15 +0100 Subject: [PATCH 0920/1412] Move CI from Travis to GitHub Actions (#866) --- .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++++ .travis.yml | 23 ----------------------- README.md | 2 +- 3 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..530905f12 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,32 @@ +name: CI + +on: [push, pull_request] + +jobs: + test: + name: Run test suite + runs-on: ubuntu-latest + + env: + COMPOSE_FILE: docker-compose.ci.yml + + strategy: + fail-fast: false + matrix: + ruby: [2.5.8, 2.6.6, 2.7.2, 3.0.0] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build docker images + run: docker-compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} + + - name: Run tests + run: docker-compose run ci \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3681c8bb8..000000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -sudo: required -cache: bundler -services: - - docker -env: - global: - - COMPOSE_FILE: docker-compose.ci.yml -before_install: - - sudo rm /usr/local/bin/docker-compose - - sudo curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose - - sudo chmod +x /usr/local/bin/docker-compose -install: - - docker-compose build --build-arg TARGET_VERSION=$TARGET_VERSION -script: - - docker-compose run ci -matrix: - include: - - name: 2.5.8 - env: TARGET_VERSION=2.5.8 - - name: 2.6.6 - env: TARGET_VERSION=2.6.6 - - name: 2.7.2 - env: TARGET_VERSION=2.7.2 diff --git a/README.md b/README.md index 6e33771c4..a5a94f94a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. -* [![TravisCI](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter.svg?branch=master)](https://travis-ci.org/rails-sqlserver/activerecord-sqlserver-adapter) - TravisCI +* ![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg) - CI * [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community From b65c437d0d84e2b394146fa80a771237d1dc4f0d Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 5 Apr 2021 18:17:47 +0100 Subject: [PATCH 0921/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a5a94f94a..2f10d7a4d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. -* ![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg) - CI +* [![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml) - CI * [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community From 4319bcecd0f0bcff7f907d20c3a43d8883d5f904 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 6 Apr 2021 17:37:18 +0100 Subject: [PATCH 0922/1412] Update CI matrix (#867) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 530905f12..d319a5a8e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.5.8, 2.6.6, 2.7.2, 3.0.0] + ruby: [2.5.9, 2.6.7, 2.7.3, 3.0.1] steps: - name: Checkout code @@ -29,4 +29,4 @@ jobs: run: docker-compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} - name: Run tests - run: docker-compose run ci \ No newline at end of file + run: docker-compose run ci From cd6eb9992d506bf8ab2dc89b6f464d778245cdfc Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Thu, 8 Apr 2021 12:51:45 +0100 Subject: [PATCH 0923/1412] Update ci.yml Remove Docker login so PRs contributors don't need to setup Docker Hub themselves. --- .github/workflows/ci.yml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d319a5a8e..c08390700 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,12 +19,6 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - - name: Login to Docker Hub - uses: docker/login-action@v1 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build docker images run: docker-compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} From 62777d2c11943b36ca417d9b00a23802004f99f4 Mon Sep 17 00:00:00 2001 From: Simon Dahlbacka Date: Sat, 10 Apr 2021 00:15:15 +0300 Subject: [PATCH 0924/1412] Lateral apply (#845) * Add tests for LATERAL implemented as OUTER/CROSS APPLY * implement lateral as CROSS/OUTER APPLY * Update changelog * remove spurious newline --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 46 ++++++++++++++++++++-------- test/cases/lateral_test_sqlserver.rb | 35 +++++++++++++++++++++ 3 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 test/cases/lateral_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bd6539a2d..3144e4d53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - [#855](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/855) Add helpers to create/change/drop a schema. - [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. - [#865](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/865) Implemented optimizer hints. +- [#845](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/845) Add support for lateral using CROSS/OUTER APPLY. ## v6.0.1 diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 2642c6950..6910ef39b 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -125,23 +125,33 @@ def visit_Arel_Nodes_JoinSource o, collector end def visit_Arel_Nodes_InnerJoin o, collector - collector << "INNER JOIN " - collector = visit o.left, collector - collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true - if o.right - collector << " " - visit(o.right, collector) + if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral) + collector << "CROSS " + visit o.left, collector else - collector + collector << "INNER JOIN " + collector = visit o.left, collector + collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true + if o.right + collector << " " + visit(o.right, collector) + else + collector + end end end def visit_Arel_Nodes_OuterJoin o, collector - collector << "LEFT OUTER JOIN " - collector = visit o.left, collector - collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true - collector << " " - visit o.right, collector + if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral) + collector << "OUTER " + visit o.left, collector + else + collector << "LEFT OUTER JOIN " + collector = visit o.left, collector + collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true + collector << " " + visit o.right, collector + end end def collect_in_clause(left, right, collector) @@ -188,6 +198,18 @@ def visit_Make_Fetch_Happen o, collector collector end + def visit_Arel_Nodes_Lateral o, collector + collector << "APPLY" + collector << " " + if o.expr.is_a?(Arel::Nodes::SelectStatement) + collector << "(" + visit(o.expr, collector) + collector << ")" + else + visit(o.expr, collector) + end + end + # SQLServer Helpers def node_value(node) diff --git a/test/cases/lateral_test_sqlserver.rb b/test/cases/lateral_test_sqlserver.rb new file mode 100644 index 000000000..17aea3c9b --- /dev/null +++ b/test/cases/lateral_test_sqlserver.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "models/post" +require "models/author" + +class LateralTestSQLServer < ActiveRecord::TestCase + fixtures :posts, :authors + + 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)) + + one = Arel::Nodes::Quoted.new(1) + eq = Arel::Nodes::Equality.new(one, one) + + sql = author.project(Arel.star).where(author[:name].matches("David")).outer_join(subselect.lateral.as("bar")).on(eq).to_sql + results = ActiveRecord::Base.connection.exec_query sql + assert_equal sql, "SELECT * FROM [authors] OUTER APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'" + assert_equal results.length, 1 + end + + 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)) + + sql = author.project(Arel.star).where(author[:name].matches("David")).join(subselect.lateral.as("bar")).to_sql + results = ActiveRecord::Base.connection.exec_query sql + + assert_equal sql, "SELECT * FROM [authors] CROSS APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'" + assert_equal results.length, 0 + end +end From d50942366b5ab4c4cbd7c9cceaecbde72de91036 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 9 Apr 2021 22:46:32 +0100 Subject: [PATCH 0925/1412] Added DecimalWithoutScale so tests no longer need to be coerced (#870) Co-authored-by: Wanderson Policarpo --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/type.rb | 1 + .../sqlserver/type/decimal_without_scale.rb | 22 +++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 15 +++++++-- test/cases/coerced_tests.rb | 33 ------------------- test/cases/column_test_sqlserver.rb | 7 ++-- test/cases/schema_dumper_test_sqlserver.rb | 8 +++-- 7 files changed, 47 insertions(+), 40 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 3144e4d53..14070c910 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. - [#865](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/865) Implemented optimizer hints. - [#845](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/845) Add support for lateral using CROSS/OUTER APPLY. +- [#870](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/870) Added DecimalWithoutScale type ## v6.0.1 diff --git a/lib/active_record/connection_adapters/sqlserver/type.rb b/lib/active_record/connection_adapters/sqlserver/type.rb index 6c5d82b4d..25be8287a 100644 --- a/lib/active_record/connection_adapters/sqlserver/type.rb +++ b/lib/active_record/connection_adapters/sqlserver/type.rb @@ -11,6 +11,7 @@ require "active_record/connection_adapters/sqlserver/type/tiny_integer" require "active_record/connection_adapters/sqlserver/type/boolean" require "active_record/connection_adapters/sqlserver/type/decimal" +require "active_record/connection_adapters/sqlserver/type/decimal_without_scale" require "active_record/connection_adapters/sqlserver/type/money" require "active_record/connection_adapters/sqlserver/type/small_money" # Approximate Numerics diff --git a/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb b/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb new file mode 100644 index 000000000..ddf8068c0 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Type + class DecimalWithoutScale < ActiveRecord::Type::DecimalWithoutScale + def sqlserver_type + "decimal".yield_self do |type| + type += "(#{precision.to_i},0)" if precision + type + end + end + + def type_cast_for_schema(value) + value.is_a?(BigDecimal) ? value.to_s : value.inspect + end + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c30909aee..92ecc2248 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -300,6 +300,7 @@ def get_database_version # :nodoc: def initialize_type_map(m = type_map) 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)" @@ -312,16 +313,22 @@ def initialize_type_map(m = type_map) 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) + scale = extract_scale(sql_type) precision = extract_precision(sql_type) - SQLServer::Type::Decimal.new precision: precision, scale: scale + if scale == 0 + SQLServer::Type::DecimalWithoutScale.new(precision: precision) + else + 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 + # Approximate Numerics 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| @@ -341,11 +348,13 @@ def initialize_type_map(m = type_map) 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 + # 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 @@ -353,10 +362,12 @@ def initialize_type_map(m = type_map) 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 + # Other Data Types m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new m.register_type "timestamp", SQLServer::Type::Timestamp.new diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bc221e98d..48d886053 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -396,39 +396,6 @@ def test_rename_nonexistent_column_coerced end class MigrationTest < ActiveRecord::TestCase - # We do not have do the DecimalWithoutScale type. - coerce_tests! :test_add_table_with_decimals - def test_add_table_with_decimals_coerced - Person.connection.drop_table :big_numbers rescue nil - assert !BigNumber.table_exists? - GiveMeBigNumbers.up - BigNumber.reset_column_information - assert BigNumber.create( - :bank_balance => 1586.43, - :big_bank_balance => BigDecimal("1000234000567.95"), - :world_population => 6000000000, - :my_house_population => 3, - :value_of_e => BigDecimal("2.7182818284590452353602875") - ) - b = BigNumber.first - assert_not_nil b - assert_not_nil b.bank_balance - assert_not_nil b.big_bank_balance - assert_not_nil b.world_population - assert_not_nil b.my_house_population - assert_not_nil b.value_of_e - assert_kind_of BigDecimal, b.world_population - assert_equal "6000000000.0", b.world_population.to_s - assert_kind_of Integer, b.my_house_population - assert_equal 3, b.my_house_population - assert_kind_of BigDecimal, b.bank_balance - assert_equal BigDecimal("1586.43"), b.bank_balance - assert_kind_of BigDecimal, b.big_bank_balance - assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance - GiveMeBigNumbers.down - assert_raise(ActiveRecord::StatementInvalid) { BigNumber.first } - end - # For some reason our tests set Rails.@_env which breaks test env switching. coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists coerce_tests! :test_internal_metadata_stores_environment diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 01339faff..532f65837 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -157,13 +157,16 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("191") _(obj.numeric_18_0).must_equal BigDecimal("191") _(col.default_function).must_be_nil + type = connection.lookup_cast_type_from_column(col) - _(type).must_be_instance_of Type::Decimal + _(type).must_be_instance_of Type::DecimalWithoutScale _(type.limit).must_be_nil _(type.precision).must_equal 18 - _(type.scale).must_equal 0 + _(type.scale).must_be_nil + obj.numeric_18_0 = "192.1" _(obj.numeric_18_0).must_equal BigDecimal("192") + obj.save! _(obj.reload.numeric_18_0).must_equal BigDecimal("192") end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 4e30dd458..748b0d988 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -16,7 +16,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :tinyint, type: "integer", limit: 1, precision: nil, scale: nil, default: 42 assert_line :bit, type: "boolean", limit: nil, precision: nil, scale: nil, default: true assert_line :decimal_9_2, type: "decimal", limit: nil, precision: 9, scale: 2, default: 12345.01 - assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: 0, default: 191.0 + assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: nil, default: 191 assert_line :numeric_36_2, type: "decimal", limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01 assert_line :money, type: "money", limit: nil, precision: 19, scale: 4, default: 4.2 assert_line :smallmoney, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: 4.2 @@ -75,7 +75,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :integer_col, type: "integer", limit: nil, precision: nil, scale: nil, default: nil assert_line :bigint_col, type: "bigint", limit: nil, precision: nil, scale: nil, default: nil assert_line :boolean_col, type: "boolean", limit: nil, precision: nil, scale: nil, default: nil - assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: 0, default: nil + assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: nil, default: nil assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil @@ -166,13 +166,15 @@ def line(column_name) def assert_line(column_name, options = {}) line = line(column_name) - assert line, "Count not find line with column name: #{column_name.inspect} in schema:\n#{schema}" + assert line, "Could not find line with column name: #{column_name.inspect} in schema:\n#{schema}" + [:type, :limit, :precision, :scale, :collation, :default].each do |key| next unless options.key?(key) actual = key == :type ? line.send(:type_method) : line.send(key) expected = options[key] message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" + if expected.nil? _(actual).must_be_nil message elsif expected.is_a?(Array) From 9413096c1203af1b591596cc32245670a95e6ceb Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 12 Apr 2021 11:34:29 +0100 Subject: [PATCH 0926/1412] Release v6.0.2 (#871) --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14070c910..96913e3cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v6.0.2 #### Fixed diff --git a/VERSION b/VERSION index 5fe607230..9b9a24420 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.1 +6.0.2 From 448aa9369135f6c80ace9205d611574aef996f53 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 12 Apr 2021 12:01:13 +0100 Subject: [PATCH 0927/1412] Start Rails 6.1 support --- CHANGELOG.md | 73 ++------------------------ VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 3 files changed, 7 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 96913e3cc..c8602dd66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,78 +1,15 @@ -## v6.0.2 +## Unreleased #### Fixed -- [#858](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/858) Allow table existence to be tested across database schemas. +- ... #### Changed -- [#852](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/852) Updated the column name matchers to accept database and owner names +- ... #### Added -- [#855](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/855) Add helpers to create/change/drop a schema. -- [#857](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/857) Included WAITFOR as read query type. -- [#865](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/865) Implemented optimizer hints. -- [#845](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/845) Add support for lateral using CROSS/OUTER APPLY. -- [#870](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/870) Added DecimalWithoutScale type +- ... -## v6.0.1 - -#### Fixed - -- [#851](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/851) Updated 'column_definitions_sql' to ensure that only primary key key constraints are queried for - -## v6.0.0 - -**No Changes** - -## v6.0.0.rc2 - -#### Fixed - -- [#639](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/639) Primary key should be lowercase if schema forced to lowercase -- [#720](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/720) quoted_date doesn't work for Type::DateTime - -#### Changed - -- [#826](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/826) Rubocop: Enable Style/StringLiterals cop -- [#827](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/827) Rubocop: Enable Layout/EmptyLinesAroundClassBody cop -- [#828](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/828) Rubocop: Enable Layout/EmptyLines cop -- [#829](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/829) Rubocop: Enable Layout/Layout/EmptyLinesAround* cops -- [#830](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/830) Rubocop: Enable Layout/IndentationWidth and Layout/TrailingWhitespace cops -- [#831](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/831) Rubocop: Enable Spacing cops -- [#832](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/832) Rubocop: Enable Bundler cops -- [#833](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/833) Rubocop: Enable Layout/* cops -- [#834](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/834) Rubocop: Enable Lint/UselessAssignment cop -- [#835](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/835) Rubocop: Configure Naming cops - -## v6.0.0.rc1 - -#### Fixed - -- [#690](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/690) Rails 6 support -- [#805](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/805) Rails 6: Fix database tasks tests for SQL Server -- [#807](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/807) Rails 6: Skip binary fixtures test on Windows -- [#809](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/809) Rails 6: Coerce reaper test using fork -- [#810](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/810) Rails 6: Fix randomly failing tests due to schema load -- [#812](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/812) Rails 6: Coerce ReloadModelsTest test on Windows -- [#818](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/818) Handle false return by TinyTDS if connection fails and fixed CI -- [#819](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/819) Fix Ruby 2.7 kwargs warnings -- [#825](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/825) Adjust error message when connection is dead - -#### Changed - -- [#716](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/716) Translate the connection timed out error -- [#763](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/763) Refactor columns introspection query to make it faster -- [#783](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/783) Update test matrix -- [#820](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/820) Enable frozen strings for tests -- [#821](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/821) Enable frozen strings - part 1 -- [#822](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/822) Enable frozen strings - part 2 -- [#823](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/823) Enable frozen strings - final -- [#824](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/824) Tidy up Gemfile - -#### Added - -- [#726](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/726) How to Develop ActiveRecord SQL Server Adapter with Pre-Installed MS SQL - -Please check [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/5-2-stable/CHANGELOG.md) for previous changes. +Please check [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-0-stable/CHANGELOG.md) for previous changes. diff --git a/VERSION b/VERSION index 9b9a24420..f5999028a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.2 +6.1.0.beta1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index a9c72c9e7..5a875edcc 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 6.0.0" + spec.add_dependency "activerecord", "~> 6.1.0" spec.add_dependency "tiny_tds" end From 1f46d2c062f8ed172468d52eb28caa602b6578b9 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 12 Apr 2021 13:11:52 +0100 Subject: [PATCH 0928/1412] Use native String#start_with (#872) --- CHANGELOG.md | 2 +- .../connection_adapters/sqlserver/core_ext/explain.rb | 2 +- lib/active_record/connection_adapters/sqlserver/utils.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c8602dd66..33de060f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ #### Fixed -- ... +- [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with #### Changed 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 89e1d6bb1..bad3d8f12 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -21,7 +21,7 @@ def exec_explain(queries) # which uses sp_executesql to just the first argument, then unquote it. Likewise our # `sp_executesql` method should substitude the @n args with the quoted values. def unprepare_sqlserver_statement(sql, binds) - return sql unless sql.starts_with?(SQLSERVER_STATEMENT_PREFIX) + return sql unless sql.start_with?(SQLSERVER_STATEMENT_PREFIX) executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length) executesql = executesql.match(SQLSERVER_STATEMENT_REGEXP).to_a[1] diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 3a7a71635..669c25d73 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -92,7 +92,7 @@ def parse_raw_name @schema = @parts.first end rest = scanner.rest - rest = rest.starts_with?(".") ? rest[1..-1] : rest[0..-1] + rest = rest.start_with?(".") ? rest[1..-1] : rest[0..-1] @object = unquote(rest) @parts << @object end From 11c270acc88ea907821f507a0c899d304cecc509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Kornek?= Date: Tue, 13 Apr 2021 21:24:13 +0200 Subject: [PATCH 0929/1412] Rails 6.1 support (#853) * use ActiveRecord::ConnectionAdapters::SchemaCreation for rails 6.1 * use just **args instead of options, **args in SQLServerRealTransaction * only use updated initialize arguments in SQLServerRealTransaction for rails 6.1 * fix for type_cast_calculated_value has already been applied to rails 5.2, so we don't need to it anymore * Remove backward compatible code Co-authored-by: Wanderson Policarpo --- .../sqlserver/core_ext/calculations.rb | 9 --------- .../connection_adapters/sqlserver/schema_creation.rb | 2 +- .../connection_adapters/sqlserver/transaction.rb | 4 ++-- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index d02e6a302..a69058b81 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -31,15 +31,6 @@ def calculate(operation, column_name) def build_count_subquery(relation, column_name, distinct) super(relation.unscope(:order), column_name, distinct) end - - def type_cast_calculated_value(value, type, operation = nil) - case operation - when "count" then value.to_i - when "sum" then type.deserialize(value || 0) - when "average" then value&.respond_to?(:to_d) ? value.to_d : value - else type.deserialize(value) - end - end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 4f65bd228..16f98aa54 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -3,7 +3,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer - class SchemaCreation < AbstractAdapter::SchemaCreation + class SchemaCreation < SchemaCreation private def visit_TableDefinition(o) diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index d25617e9e..86f4aa777 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -31,9 +31,9 @@ def current_isolation_level module SQLServerRealTransaction attr_reader :starting_isolation_level - def initialize(connection, options, **args) + def initialize(connection, isolation: nil, joinable: true, run_commit_callbacks: false) @connection = connection - @starting_isolation_level = current_isolation_level if options[:isolation] + @starting_isolation_level = current_isolation_level if isolation super end From 7e4842a5d9149c0b9cc6c3d5818308a626679328 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 13 Apr 2021 21:36:45 +0100 Subject: [PATCH 0930/1412] Update README.md --- README.md | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2f10d7a4d..3aefe84b9 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,20 @@ ## About The Adapter -The SQL Server adapter for ActiveRecord v6.0 using SQL Server 2012 or higher. +The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 5.2.x version of the adapter is only for the latest 5.2 version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 6.x version of the adapter is only for the latest 6.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. + +| Adapter Version | Rails Version | Support | +| ------------- | --- | --- | +| `6.1.0.0.rc1` | `6.1.x` | [wip](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | + +For older versions, please check their stable branches. #### Native Data Type Support From 1eea39ab993b72ee7327aa5b2885be9dd7a08b1c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 13 Apr 2021 21:55:55 +0100 Subject: [PATCH 0931/1412] Changes to get tests running against Rails 6.1 (#873) Co-authored-by: Aidan Haran Co-authored-by: Wanderson Policarpo --- .../sqlserver/database_statements.rb | 12 ++++----- .../sqlserver/schema_creation.rb | 4 +++ .../sqlserver/schema_statements.rb | 2 +- test/support/sql_counter_sqlserver.rb | 26 ++++++++++--------- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index a3869cdc2..3596af9dd 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -54,7 +54,7 @@ def exec_update(sql, name, binds) end def begin_db_transaction - do_execute "BEGIN TRANSACTION" + do_execute "BEGIN TRANSACTION", "TRANSACTION" end def transaction_isolation_levels @@ -67,25 +67,25 @@ def begin_isolated_db_transaction(isolation) end def set_transaction_isolation_level(isolation_level) - do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}" + do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION" end def commit_db_transaction - do_execute "COMMIT TRANSACTION" + do_execute "COMMIT TRANSACTION", "TRANSACTION" end def exec_rollback_db_transaction - do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" + do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION" end include Savepoints def create_savepoint(name = current_savepoint_name) - do_execute "SAVE TRANSACTION #{name}" + do_execute "SAVE TRANSACTION #{name}", "TRANSACTION" end def exec_rollback_to_savepoint(name = current_savepoint_name) - do_execute "ROLLBACK TRANSACTION #{name}" + do_execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION" end def release_savepoint(name = current_savepoint_name) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 16f98aa54..4753e8b5e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -6,6 +6,10 @@ module SQLServer class SchemaCreation < SchemaCreation private + def supports_index_using? + false + end + def visit_TableDefinition(o) if_not_exists = o.if_not_exists diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 7d19774a8..f4bf1c6eb 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -27,7 +27,7 @@ def drop_table(table_name, **options) end end if options[:if_exists] && @version_year < 2016 - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA" else super end diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index 20819cf6a..acf51208e 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -11,17 +11,19 @@ def capture_sql_ss end end - ignored_sql = [ - /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, - /sys.columns/i, - /SELECT @@version/, - /SELECT @@TRANCOUNT/, - /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, - /SELECT CAST\(.* AS .*\) AS value/, - /SELECT DATABASEPROPERTYEX/im - ] - - sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first - sqlcounter.instance_variable_set :@ignore, Regexp.union(ignored_sql.push(sqlcounter.ignore)) + # TODO: Delete the code below after all Rails 6.1 tests passing. + # + # ignored_sql = [ + # /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, + # /sys.columns/i, + # /SELECT @@version/, + # /SELECT @@TRANCOUNT/, + # /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, + # /SELECT CAST\(.* AS .*\) AS value/, + # /SELECT DATABASEPROPERTYEX/im + # ] + # + # sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first + # sqlcounter.instance_variable_set :@ignore, Regexp.union(ignored_sql.push(sqlcounter.ignore)) end end From 378027604b8a773631d9bf34a3a838093fe67717 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 14 Apr 2021 11:25:52 +0100 Subject: [PATCH 0932/1412] Deduplicate schema cache structures (#874) Co-authored-by: Aidan Haran --- .../connection_adapters/sqlserver/quoting.rb | 6 ++-- .../sqlserver/sql_type_metadata.rb | 34 +++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index f8d335eb0..09d8226bc 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -10,14 +10,14 @@ module Quoting def fetch_type_metadata(sql_type, sqlserver_options = {}) cast_type = lookup_cast_type(sql_type) - SQLServer::SqlTypeMetadata.new( + simple_type = SqlTypeMetadata.new( sql_type: sql_type, type: cast_type.type, limit: cast_type.limit, precision: cast_type.precision, - scale: cast_type.scale, - sqlserver_options: sqlserver_options + scale: cast_type.scale ) + SQLServer::TypeMetadata.new(simple_type, sqlserver_options: sqlserver_options) end def quote_string(s) 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 40f77714c..ebcacc5cd 100644 --- a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -3,16 +3,36 @@ module ActiveRecord module ConnectionAdapters module SQLServer - class SqlTypeMetadata < ActiveRecord::ConnectionAdapters::SqlTypeMetadata - def initialize(**kwargs) - @sqlserver_options = kwargs.extract!(:sqlserver_options) - super(**kwargs) + class TypeMetadata < DelegateClass(SqlTypeMetadata) + undef to_yaml if method_defined?(:to_yaml) + + include Deduplicable + + attr_reader :sqlserver_options + + def initialize(type_metadata, sqlserver_options: nil) + super(type_metadata) + @sqlserver_options = sqlserver_options + end + + def ==(other) + other.is_a?(TypeMetadata) && + __getobj__ == other.__getobj__ && + sqlserver_options == other.sqlserver_options + end + alias eql? == + + def hash + TypeMetadata.hash ^ + __getobj__.hash ^ + sqlserver_options.hash end - protected + private - def attributes_for_hash - super + [@sqlserver_options] + def deduplicated + __setobj__(__getobj__.deduplicate) + super end end end From 8d7d4424cb49593a3cbe4e0056e4026c0bc77b93 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 14 Apr 2021 15:00:19 +0100 Subject: [PATCH 0933/1412] Added missing changelogs (#877) Co-authored-by: Aidan Haran --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 33de060f7..9cda2c8d7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ #### Fixed - [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with +- [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1 +- [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures #### Changed From 168cabeb9bc7009c7aacd33fa36845a30108ddcf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 14 Apr 2021 15:01:00 +0100 Subject: [PATCH 0934/1412] Use native String#end_with (#876) Co-authored-by: Aidan Haran Co-authored-by: Wanderson Policarpo --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cda2c8d7..19dd13a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with +- [#876](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/876) Use native String#end_with - [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1 - [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f4bf1c6eb..88828191f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -51,7 +51,7 @@ def indexes(table_name) index[:index_keys].split(",").each do |column| column.strip! - if column.ends_with?("(-)") + if column.end_with?("(-)") column.gsub! "(-)", "" orders[column] = :desc end From 2fbded924157493e7b2569e373b1f91a81078481 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 14 Apr 2021 15:01:57 +0100 Subject: [PATCH 0935/1412] Handle default boolean column values when deduplicating (#875) Co-authored-by: Aidan Haran Co-authored-by: Wanderson Policarpo --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver_column.rb | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 19dd13a70..bacee02ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [#876](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/876) Use native String#end_with - [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1 - [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures +- [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index b03e8e7e4..acd2c40ef 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -27,6 +27,22 @@ def is_utf8? def case_sensitive? collation && collation.match(/_CS/) end + + private + + # Handle when the default value is a boolean. Boolean's do not respond to the method `:-@`. Method now + # checks whether the default value is already frozen and if so it uses that, otherwise it calls `:-@` to + # freeze it. + def deduplicated + @name = -name + @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata + @default = (default.frozen? ? default : -default) if default + @default_function = -default_function if default_function + @collation = -collation if collation + @comment = -comment if comment + + freeze + end end end end From bee6e1dd2871ea3d9b1ef4e056be7d80de973ecf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 15 Apr 2021 16:04:05 +0100 Subject: [PATCH 0936/1412] Added visit method for HomogeneousIn Arel type (#879) * Added visit method for HomogeneousIn * Updated changelog * Removed method that has same definition as the parent Abstract adapter Co-authored-by: Aidan Haran --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 78 +++++++++++++++++++++++----------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bacee02ac..7207b9695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1 - [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures - [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating +- [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn #### Changed diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 6910ef39b..f98fcabae 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -11,13 +11,14 @@ class SQLServer < Arel::Visitors::ToSql private - # SQLServer ToSql/Visitor (Overides) + # SQLServer ToSql/Visitor (Overrides) - def visit_Arel_Nodes_BindParam o, collector - collector.add_bind(o.value) { |i| "@#{i - 1}" } - end + BIND_BLOCK = proc { |i| "@#{i - 1}" } + private_constant :BIND_BLOCK + + def bind_block; BIND_BLOCK; end - def visit_Arel_Nodes_Bin o, collector + def visit_Arel_Nodes_Bin(o, collector) visit o.expr, collector collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} " end @@ -28,26 +29,26 @@ def visit_Arel_Nodes_Concat(o, collector) visit o.right, collector end - def visit_Arel_Nodes_UpdateStatement(o, a) + def visit_Arel_Nodes_UpdateStatement(o, collector) if o.orders.any? && o.limit.nil? o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) end super end - def visit_Arel_Nodes_Lock o, collector + def visit_Arel_Nodes_Lock(o, collector) o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/ collector << " " visit o.expr, collector end - def visit_Arel_Nodes_Offset o, collector + def visit_Arel_Nodes_Offset(o, collector) collector << OFFSET visit o.expr, collector collector << ROWS end - def visit_Arel_Nodes_Limit o, collector + def visit_Arel_Nodes_Limit(o, collector) if node_value(o) == 0 collector << FETCH0 collector << ROWS_ONLY @@ -63,7 +64,36 @@ def visit_Arel_Nodes_Grouping(o, collector) super end - def visit_Arel_Nodes_SelectStatement o, collector + def visit_Arel_Nodes_HomogeneousIn(o, collector) + collector.preparable = false + + collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name) + + if o.type == :in + collector << " IN (" + else + collector << " NOT IN (" + end + + values = o.casted_values + + if values.empty? + collector << @connection.quote(nil) + else + # Monkey-patch start. Add query attribute bindings rather than just values. + column_name = o.column_name + column_type = o.attribute.relation.type_for_attribute(o.column_name) + attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) } + + collector.add_binds(attrs, &bind_block) + # Monkey-patch end. + end + + collector << ")" + collector + end + + def visit_Arel_Nodes_SelectStatement(o, collector) @select_statement = o distinct_One_As_One_Is_So_Not_Fetch o if o.with @@ -90,7 +120,7 @@ def visit_Arel_Nodes_OptimizerHints(o, collector) collector << "OPTION (#{hints})" end - def visit_Arel_Table o, collector + def visit_Arel_Table(o, collector) # Apparently, o.engine.connection can actually be a different adapter # than sqlserver. Can be removed if fixed in ActiveRecord. See: # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 @@ -112,7 +142,7 @@ def visit_Arel_Table o, collector end end - def visit_Arel_Nodes_JoinSource o, collector + def visit_Arel_Nodes_JoinSource(o, collector) if o.left collector = visit o.left, collector collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector @@ -124,7 +154,7 @@ def visit_Arel_Nodes_JoinSource o, collector collector end - def visit_Arel_Nodes_InnerJoin o, collector + def visit_Arel_Nodes_InnerJoin(o, collector) if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral) collector << "CROSS " visit o.left, collector @@ -141,7 +171,7 @@ def visit_Arel_Nodes_InnerJoin o, collector end end - def visit_Arel_Nodes_OuterJoin o, collector + def visit_Arel_Nodes_OuterJoin(o, collector) if o.left.is_a?(Arel::Nodes::As) && o.left.left.is_a?(Arel::Nodes::Lateral) collector << "OUTER " visit o.left, collector @@ -170,7 +200,7 @@ def collect_optimizer_hints(o, collector) # SQLServer ToSql/Visitor (Additions) - def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} + def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {}) if select_statement_lock? collector = visit @select_statement.lock, collector collector << " " if options[:space] @@ -178,7 +208,7 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {} collector end - def visit_Orders_And_Let_Fetch_Happen o, collector + def visit_Orders_And_Let_Fetch_Happen(o, collector) make_Fetch_Possible_And_Deterministic o unless o.orders.empty? collector << " ORDER BY " @@ -191,14 +221,14 @@ def visit_Orders_And_Let_Fetch_Happen o, collector collector end - def visit_Make_Fetch_Happen o, collector + def visit_Make_Fetch_Happen(o, collector) o.offset = Nodes::Offset.new(0) if o.limit && !o.offset collector = visit o.offset, collector if o.offset collector = visit o.limit, collector if o.limit collector end - def visit_Arel_Nodes_Lateral o, collector + def visit_Arel_Nodes_Lateral(o, collector) collector << "APPLY" collector << " " if o.expr.is_a?(Arel::Nodes::SelectStatement) @@ -226,7 +256,7 @@ def select_statement_lock? @select_statement && @select_statement.lock end - def make_Fetch_Possible_And_Deterministic o + def make_Fetch_Possible_And_Deterministic(o) return if o.limit.nil? && o.offset.nil? t = table_From_Statement o @@ -239,7 +269,7 @@ def make_Fetch_Possible_And_Deterministic o end end - def distinct_One_As_One_Is_So_Not_Fetch o + def distinct_One_As_One_Is_So_Not_Fetch(o) core = o.cores.first distinct = Nodes::Distinct === core.set_quantifier oneasone = core.projections.all? { |x| x == ActiveRecord::FinderMethods::ONE_AS_ONE } @@ -250,7 +280,7 @@ def distinct_One_As_One_Is_So_Not_Fetch o end end - def table_From_Statement o + def table_From_Statement(o) core = o.cores.first if Arel::Table === core.from core.from @@ -261,15 +291,15 @@ def table_From_Statement o end end - def primary_Key_From_Table t + def primary_Key_From_Table(t) return unless t column_name = @connection.schema_cache.primary_keys(t.name) || - @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) + @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) column_name ? t[column_name] : nil end - def remote_server_table_name o + def remote_server_table_name(o) ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( "#{o.class.engine.connection.database_prefix}#{o.name}" ).quoted From c98e72fccbe721f8fa735b2205077cc0b805f86b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 15 Apr 2021 17:16:04 +0100 Subject: [PATCH 0937/1412] Handle any default column class when deduplicating (#880) Co-authored-by: Aidan Haran --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver_column.rb | 9 +++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7207b9695..74bea42e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures - [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating - [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn +- [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index acd2c40ef..8cfe709c3 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -30,13 +30,14 @@ def case_sensitive? private - # Handle when the default value is a boolean. Boolean's do not respond to the method `:-@`. Method now - # checks whether the default value is already frozen and if so it uses that, otherwise it calls `:-@` to - # freeze it. + # In the Rails version of this method there is an assumption that the `default` value will always be a + # `String` class, which must be true for the MySQL/PostgreSQL/SQLite adapters. However, in the SQL Server + # adapter the `default` value can also be Boolean/Date/Time/etc. Changed the implementation of this method + # to handle non-String `default` objects. def deduplicated @name = -name @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata - @default = (default.frozen? ? default : -default) if default + @default = (default.is_a?(String) ? -default : default.dup.freeze) if default @default_function = -default_function if default_function @collation = -collation if collation @comment = -comment if comment From 389af1457ac6143d1e00f2d42787e2c50f72a8ff Mon Sep 17 00:00:00 2001 From: Josh LeBlanc Date: Fri, 16 Apr 2021 09:39:56 -0300 Subject: [PATCH 0938/1412] use configuration hash (#861) --- .../tasks/sqlserver_database_tasks.rb | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index cb5ee2dee..375369f57 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -13,13 +13,18 @@ class SQLServerDatabaseTasks delegate :connection, :establish_connection, :clear_active_connections!, to: ActiveRecord::Base + def self.using_database_configurations? + true + end + def initialize(configuration) @configuration = configuration + @configuration_hash = @configuration.configuration_hash end def create(master_established = false) establish_master_connection unless master_established - connection.create_database configuration["database"], configuration.merge("collation" => default_collation) + connection.create_database configuration.database, configuration_hash.merge("collation" => default_collation) establish_connection configuration rescue ActiveRecord::StatementInvalid => e if /database .* already exists/i === e.message @@ -31,7 +36,7 @@ def create(master_established = false) def drop establish_master_connection - connection.drop_database configuration["database"] + connection.drop_database configuration.database end def charset @@ -49,14 +54,14 @@ def purge end def structure_dump(filename, extra_flags) - server_arg = "-S #{Shellwords.escape(configuration['host'])}" - server_arg += ":#{Shellwords.escape(configuration['port'])}" if configuration["port"] + server_arg = "-S #{Shellwords.escape(configuration_hash['host'])}" + server_arg += ":#{Shellwords.escape(configuration_hash['port'])}" if configuration_hash["port"] command = [ "defncopy-ttds", server_arg, - "-D #{Shellwords.escape(configuration['database'])}", - "-U #{Shellwords.escape(configuration['username'])}", - "-P #{Shellwords.escape(configuration['password'])}", + "-D #{Shellwords.escape(configuration_hash['database'])}", + "-U #{Shellwords.escape(configuration_hash['username'])}", + "-P #{Shellwords.escape(configuration_hash['password'])}", "-o #{Shellwords.escape(filename)}", ] table_args = connection.tables.map { |t| Shellwords.escape(t) } @@ -80,16 +85,14 @@ def structure_load(filename, extra_flags) private - def configuration - @configuration - end + attr_reader :configuration, :configuration_hash def default_collation - configuration["collation"] || DEFAULT_COLLATION + configuration_hash["collation"] || DEFAULT_COLLATION end def establish_master_connection - establish_connection configuration.merge("database" => "master") + establish_connection configuration_hash.merge("database" => "master") end end @@ -110,9 +113,9 @@ def local_database?(configuration) end def configuration_host_ip(configuration) - return nil unless configuration["host"] + 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) From 31bc0749a0360fd5e41e5bec2183ff4e7ea5a320 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 16 Apr 2021 13:41:49 +0100 Subject: [PATCH 0939/1412] [skip ci] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 74bea42e3..b562aba3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating - [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating +- [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config #### Changed From d67c2f6ef9c1ec92daaf1c5474f4013c4d1f4d1e Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Sat, 17 Apr 2021 06:30:20 -0300 Subject: [PATCH 0940/1412] Rails 6.1 fixes: Use renamed connection_config method and rewrite coerced tests using unsafe raw SQL (#882) * Use renamed ARTest.connfiguration_config method See more: https://github.com/rails/rails/pull/38007 * rewrite coerced specs using unsafe raw SQL Support for using unsafe raw SQL in `ActiveRecord::Relation` methods has been removed. See https://github.com/rails/rails/commit/317465a4a8f8c0ebaf9534921350c4540584c03c --- test/cases/coerced_tests.rb | 65 +++++++++++-------------------- test/cases/rake_test_sqlserver.rb | 2 +- 2 files changed, 24 insertions(+), 43 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 48d886053..a31ff44cc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1233,19 +1233,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase # Use LEN() vs length() function. coerce_tests! %r{order: always allows Arel} test "order: always allows Arel" do - ids_depr = with_unsafe_raw_sql_deprecated { Post.order(Arel.sql("len(title)")).pluck(:title) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(Arel.sql("len(title)")).pluck(:title) } + titles = Post.order(Arel.sql("len(title)")).pluck(:title) - assert_equal ids_depr, ids_disabled + assert_not_empty titles end # Use LEN() vs length() function. coerce_tests! %r{pluck: always allows Arel} test "pluck: always allows Arel" do - values_depr = with_unsafe_raw_sql_deprecated { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } - values_disabled = with_unsafe_raw_sql_disabled { Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) } + excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] } + values = Post.includes(:comments).pluck(:title, Arel.sql("len(title)")) - assert_equal values_depr, values_disabled + assert_equal excepted_values, values end # Use LEN() vs length() function. @@ -1253,91 +1252,73 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase test "order: allows valid Array arguments" do ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order(["author_id", "len(title)"]).pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order(["author_id", "len(title)"]).pluck(:id) } + ids = Post.order(["author_id", "len(title)"]).pluck(:id) - assert_equal ids_expected, ids_depr - assert_equal ids_expected, ids_disabled + assert_equal ids_expected, ids end test "order: allows string column names that are quoted" do ids_expected = Post.order(Arel.sql("id")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[id]").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[id]").pluck(:id) } + ids = Post.order("[id]").pluck(:id) - assert_equal ids_expected, ids_depr - assert_equal ids_expected, ids_disabled + assert_equal ids_expected, ids end test "order: allows string column names that are quoted with table" do ids_expected = Post.order(Arel.sql("id")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[posts].[id]").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[posts].[id]").pluck(:id) } + ids = Post.order("[posts].[id]").pluck(:id) - assert_equal ids_expected, ids_depr - assert_equal ids_expected, ids_disabled + assert_equal ids_expected, ids end test "order: allows string column names that are quoted with table and user" do ids_expected = Post.order(Arel.sql("id")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[dbo].[posts].[id]").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[dbo].[posts].[id]").pluck(:id) } + ids = Post.order("[dbo].[posts].[id]").pluck(:id) - assert_equal ids_expected, ids_depr - assert_equal ids_expected, ids_disabled + assert_equal ids_expected, ids end test "order: allows string column names that are quoted with table, user and database" do ids_expected = Post.order(Arel.sql("id")).pluck(:id) - ids_depr = with_unsafe_raw_sql_deprecated { Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id) } - ids_disabled = with_unsafe_raw_sql_disabled { Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id) } + ids = Post.order("[activerecord_unittest].[dbo].[posts].[id]").pluck(:id) - assert_equal ids_expected, ids_depr - assert_equal ids_expected, ids_disabled + assert_equal ids_expected, ids end test "pluck: allows string column name that are quoted" do titles_expected = Post.pluck(Arel.sql("title")) - titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[title]") } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[title]") } + titles = Post.pluck("[title]") - assert_equal titles_expected, titles_depr - assert_equal titles_expected, titles_disabled + assert_equal titles_expected, titles end test "pluck: allows string column name that are quoted with table" do titles_expected = Post.pluck(Arel.sql("title")) - titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[posts].[title]") } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[posts].[title]") } + titles = Post.pluck("[posts].[title]") - assert_equal titles_expected, titles_depr - assert_equal titles_expected, titles_disabled + assert_equal titles_expected, titles end test "pluck: allows string column name that are quoted with table and user" do titles_expected = Post.pluck(Arel.sql("title")) - titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[dbo].[posts].[title]") } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[dbo].[posts].[title]") } + titles = Post.pluck("[dbo].[posts].[title]") - assert_equal titles_expected, titles_depr - assert_equal titles_expected, titles_disabled + assert_equal titles_expected, titles end test "pluck: allows string column name that are quoted with table, user and database" do titles_expected = Post.pluck(Arel.sql("title")) - titles_depr = with_unsafe_raw_sql_deprecated { Post.pluck("[activerecord_unittest].[dbo].[posts].[title]") } - titles_disabled = with_unsafe_raw_sql_disabled { Post.pluck("[activerecord_unittest].[dbo].[posts].[title]") } + titles = Post.pluck("[activerecord_unittest].[dbo].[posts].[title]") - assert_equal titles_expected, titles_depr - assert_equal titles_expected, titles_disabled + assert_equal titles_expected, titles end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index fac7f34f5..ce76d3972 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -10,7 +10,7 @@ class SQLServerRakeTest < ActiveRecord::TestCase let(:db_tasks) { ActiveRecord::Tasks::DatabaseTasks } let(:new_database) { "activerecord_unittest_tasks" } - let(:default_configuration) { ARTest.connection_config["arunit"] } + let(:default_configuration) { ARTest.test_configuration_hashes["arunit"] } let(:configuration) { default_configuration.merge("database" => new_database) } before { skip "on azure" if azure_skip } From 4d0de649b7c195ba34a2e44a300e82decf38ef07 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 19 Apr 2021 10:19:46 -0300 Subject: [PATCH 0941/1412] Rails 6.1: remove deprecation warnings (#884) * DEPRECATION WARNING: allowed_index_name_length is deprecated and will be removed from Rails 6.2 Deprecation commit https://github.com/rails/rails/commit/ab2d859e6c5430326a22a1328cc296c8cbae8727 * DEPRECATION WARNING: [] is deprecated and will be removed from Rails 6.2 (Use configs_for) Deprecation commit https://github.com/rails/rails/commit/2a53fe638de2c485c2655b6b7236ec1ab7dca5ad * DEPRECATION WARNING: Passing an Active Record object to directly is deprecated and will be no longer quoted as id value in Rails 6.2 Deprecation commit https://github.com/rails/rails/commit/87886c93dc22d528d8529d8b63873f699beaf695 * DEPRECATION WARNING: with / no longer takes non-deterministic result in Rails 6.2. To continue taking non-deterministic result, use / instead. Deprecation commit https://github.com/rails/rails/commit/eec562dbcf5624e38085b8262512a6584784dfdc --- .../sqlserver/schema_statements.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 19 +++++++++++-------- test/cases/coerced_tests.rb | 17 ++++++++++++++--- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 88828191f..ab5bba707 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -190,7 +190,7 @@ def rename_column(table_name, column_name, new_column_name) end def rename_index(table_name, old_name, new_name) - raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length + raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters" if new_name.length > index_name_length identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}") execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX" diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 799cc27d0..d86d6ffd5 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -53,7 +53,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." # Test when database and owner included in table name. - Topic.table_name = "#{ActiveRecord::Base.configurations["arunit"]['database']}.dbo.topics" + 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" @@ -100,21 +101,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "test bad connection" do assert_raise ActiveRecord::NoDatabaseError do - config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest") - ActiveRecord::Base.sqlserver_connection config + db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") + configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest") + ActiveRecord::Base.sqlserver_connection configuration end end it "test database exists returns false if database does not exist" do - config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest") - assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), + db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") + configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest") + assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration), "expected database to not exist" end it "test database exists returns true when the database exists" do - config = ActiveRecord::Base.configurations["arunit"] - assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config), - "expected database #{config[:database]} to exist" + 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" end describe "with different language" do diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a31ff44cc..c81b1712e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -329,14 +329,18 @@ class QuoteARBaseTest < ActiveRecord::TestCase coerce_tests! :test_quote_ar_object def test_quote_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) + assert_deprecated do + assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) + end end # Use our date format. coerce_tests! :test_type_cast_ar_object def test_type_cast_ar_object_coerced value = DatetimePrimaryKey.new(id: @time) - assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) + assert_deprecated do + assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) + end end end end @@ -896,7 +900,14 @@ def test_reorder_with_take_coerced coerce_tests! :test_reorder_with_first def test_reorder_with_first_coerced sql_log = capture_sql do - assert Post.order(:title).reorder(nil).first + message = <<~MSG.squish + `.reorder(nil)` with `.first` / `.first!` no longer + takes non-deterministic result in Rails 6.2. + To continue taking non-deterministic result, use `.take` / `.take!` instead. + MSG + assert_deprecated(message) do + assert Post.order(:title).reorder(nil).first + end 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}" From bc1d46e2cdd47f12a86b58b54ec47b7542e8f1ad Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 19 Apr 2021 15:45:48 +0100 Subject: [PATCH 0942/1412] Rails 6.1: Fix the quoting of ActiveModel attributes (#885) * Fix when quoting ActiveModel::Attribute * Updated changelog Co-authored-by: Aidan Haran --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver/quoting.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b562aba3a..f9e468804 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config +- [#885](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix the quoting of ActiveModel attributes #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 09d8226bc..c7f5550de 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -118,6 +118,8 @@ def _quote(value) value.quoted when String, ActiveSupport::Multibyte::Chars "#{QUOTED_STRING_PREFIX}#{super}" + when ActiveModel::Attribute + quote(value.value_for_database) else super end From 5f0c423d9383f109c37245db6e6b39375fe8d3fd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 19 Apr 2021 16:19:24 +0100 Subject: [PATCH 0943/1412] Remove coerced tests that were removed from Rails test suite (#887) --- test/cases/coerced_tests.rb | 39 ------------------------------------- 1 file changed, 39 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c81b1712e..83ced0d39 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -23,22 +23,12 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced end end end - - # Skip the test if database is case-insensitive. - coerce_tests! :test_validate_case_sensitive_uniqueness_by_default - def test_validate_case_sensitive_uniqueness_by_default_coerced - database_collation = connection.select_one("SELECT collation_name FROM sys.databases WHERE name = 'activerecord_unittest'").values.first - skip if database_collation.include?("_CI_") - - original_test_validate_case_sensitive_uniqueness_by_default_coerced - end end require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase # I really don`t think we can support legacy binds. - coerce_tests! :test_select_all_with_legacy_binds coerce_tests! :test_insert_update_delete_with_legacy_binds # As far as I can tell, SQL Server does not support null bytes in strings. @@ -54,13 +44,6 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced assert_not_nil error.cause end end - - # Fix randomly failing test. The loading of the model's schema was affecting the test. - coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes - def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema!) - original_test_errors_when_an_insert_query_is_called_while_preventing_writes - end end end @@ -1456,28 +1439,6 @@ def test_insert_all_coerced end end -require "models/citation" -class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase - # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and - # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012) - # (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/). - # However, you cannot change the compatibility level during a test. The purpose of the test is to ensure that an - # unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test - # still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of - # adapter is 2,098. - coerce_tests! :test_eager_loading_too_may_ids - def test_eager_loading_too_may_ids_coerced - # Remove excess records. - Citation.limit(32768).order(id: :desc).delete_all - - # Perform test - citation_count = Citation.count - assert_sql(/WHERE \(\[citations\]\.\[id\] IN \(0, 1/) do - assert_equal citation_count, Citation.eager_load(:citations).offset(0).size - end - end -end - class LogSubscriberTest < ActiveRecord::TestCase # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. coerce_tests! :test_vebose_query_logs From c98ea79020aa7a757c8e5694847ba7551cdd7993 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 19 Apr 2021 17:07:11 -0300 Subject: [PATCH 0944/1412] Dump column collation to schema.rb and allow collation changes using column_change (#881) * add column collation to schema dump. Dump string value * be able to alter column collation * spec showing collation change using 'change_column' * update changelog --- CHANGELOG.md | 1 + .../sqlserver/schema_creation.rb | 3 ++ .../sqlserver/schema_dumper.rb | 8 ++++- .../sqlserver/schema_statements.rb | 1 + .../change_column_collation_test_sqlserver.rb | 33 +++++++++++++++++++ test/cases/migration_test_sqlserver.rb | 7 ++++ test/cases/schema_dumper_test_sqlserver.rb | 9 +++++ ...ate_clients_and_change_column_collation.rb | 19 +++++++++++ test/models/sqlserver/sst_string_collation.rb | 3 ++ test/schema/sqlserver_specific_schema.rb | 7 ++++ 10 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 test/cases/change_column_collation_test_sqlserver.rb create mode 100644 test/migrations/create_clients_and_change_column_collation.rb create mode 100644 test/models/sqlserver/sst_string_collation.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index f9e468804..d72a62644 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config - [#885](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix the quoting of ActiveModel attributes +- [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 4753e8b5e..44b96602b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -38,6 +38,9 @@ def add_column_options!(sql, options) if options[:null] == false sql << " NOT NULL" end + if options[:collation].present? + sql << " COLLATE #{options[:collation]}" + end if options[:is_identity] == true sql << " IDENTITY(1,1)" end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index ef69fee42..990191539 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -27,7 +27,13 @@ def schema_limit(column) def schema_collation(column) return unless column.collation - column.collation if column.collation != @connection.collation + # use inspect to ensure collation is dumped as string. Without this it's dumped as + # a constant ('collation: SQL_Latin1_General_CP1_CI_AS') + collation = column.collation.inspect + # use inspect to ensure string comparison + default_collation = @connection.collation.inspect + + collation if collation != default_collation end def default_primary_key?(column) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index ab5bba707..7bb1fa5b2 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -156,6 +156,7 @@ def change_column(table_name, column_name, type, options = {}) end sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" + alter_command += " COLLATE #{options[:collation]}" if options[:collation].present? alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false sql_commands << alter_command if without_constraints diff --git a/test/cases/change_column_collation_test_sqlserver.rb b/test/cases/change_column_collation_test_sqlserver.rb new file mode 100644 index 000000000..94ddf2ee7 --- /dev/null +++ b/test/cases/change_column_collation_test_sqlserver.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "migrations/create_clients_and_change_column_collation" + +class ChangeColumnCollationTestSqlServer < ActiveRecord::TestCase + before do + @old_verbose = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + CreateClientsAndChangeColumnCollation.new.up + end + + after do + CreateClientsAndChangeColumnCollation.new.down + ActiveRecord::Migration.verbose = @old_verbose + end + + def find_column(table, name) + table.find { |column| column.name == name } + end + + let(:clients_table) { connection.columns("clients") } + let(:name_column) { find_column(clients_table, "name") } + let(:code_column) { find_column(clients_table, "code") } + + it "change column collation to other than default" do + _(name_column.collation).must_equal "SQL_Latin1_General_CP1_CS_AS" + end + + it "change column collation to default" do + _(code_column.collation).must_equal "SQL_Latin1_General_CP1_CI_AS" + end +end diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 31fa0180a..132d69115 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -63,6 +63,13 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it "change null and default" do assert_nothing_raised { connection.change_column :people, :first_name, :text, null: true, default: nil } end + + it "change collation" do + 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 + end end describe "#create_schema" do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 748b0d988..1b59525fe 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -119,6 +119,15 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil end + it "dump column collation" do + generate_schema_for_table('sst_string_collation') + + assert_line :string_without_collation, type: "string", limit: nil, default: nil, collation: nil + assert_line :string_default_collation, type: "varchar", limit: nil, default: nil, collation: nil + assert_line :string_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS" + assert_line :varchar_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS" + end + # Special Cases it "honor nonstandard primary keys" do diff --git a/test/migrations/create_clients_and_change_column_collation.rb b/test/migrations/create_clients_and_change_column_collation.rb new file mode 100644 index 000000000..9e322d150 --- /dev/null +++ b/test/migrations/create_clients_and_change_column_collation.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class CreateClientsAndChangeColumnCollation < ActiveRecord::Migration[5.2] + def up + create_table :clients do |t| + t.string :name + t.string :code, collation: :SQL_Latin1_General_CP1_CS_AS + + 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' + end + + def down + drop_table :clients + end +end diff --git a/test/models/sqlserver/sst_string_collation.rb b/test/models/sqlserver/sst_string_collation.rb new file mode 100644 index 000000000..cdb08d775 --- /dev/null +++ b/test/models/sqlserver/sst_string_collation.rb @@ -0,0 +1,3 @@ +class SstStringCollation < ActiveRecord::Base + self.table_name = "sst_string_collation" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 9964cef4f..3486c7942 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -95,6 +95,13 @@ t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line." end + create_table :sst_string_collation, collation: :SQL_Latin1_General_CP1_CI_AS, force: true do |t| + t.string :string_without_collation + t.varchar :string_default_collation, collation: :SQL_Latin1_General_CP1_CI_AS + t.varchar :string_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS + t.varchar :varchar_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS + end + create_table :sst_edge_schemas, force: true do |t| t.string :description t.column "crazy]]quote", :string From 059d244f3dbe988e850f71e87698d319ea0925a6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 19 Apr 2021 21:09:24 +0100 Subject: [PATCH 0945/1412] Fix removal of invalid ordering from select statements (#890) Co-authored-by: Aidan Haran Co-authored-by: Wanderson Policarpo --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 8 ++++---- test/cases/in_clause_test_sqlserver.rb | 27 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d72a62644..38bc6660b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config - [#885](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix the quoting of ActiveModel attributes +- [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change #### Changed diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f98fcabae..1d377110f 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -184,11 +184,11 @@ def visit_Arel_Nodes_OuterJoin(o, collector) end end - def collect_in_clause(left, right, collector) - if Array === right - right.each { |node| remove_invalid_ordering_from_select_statement(node) } + def visit_Arel_Nodes_In(o, collector) + if Array === o.right + o.right.each { |node| remove_invalid_ordering_from_select_statement(node) } else - remove_invalid_ordering_from_select_statement(right) + remove_invalid_ordering_from_select_statement(o.right) end super diff --git a/test/cases/in_clause_test_sqlserver.rb b/test/cases/in_clause_test_sqlserver.rb index 360bf6003..539d57ae9 100644 --- a/test/cases/in_clause_test_sqlserver.rb +++ b/test/cases/in_clause_test_sqlserver.rb @@ -33,4 +33,31 @@ class InClauseTestSQLServer < ActiveRecord::TestCase assert_includes posts.to_sql, "ORDER BY [authors].[name]" assert_equal 8, posts.length end + + it "removes ordering from 'not' subqueries" do + authors_subquery = Author.where.not(name: ["Mary", "Bob"]).order(:name) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_not_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 5, posts.length + end + + it "does not remove ordering from 'not' subquery that includes a limit" do + authors_subquery = Author.where.not(name: ["Ronan", "Mary", "Bob"]).order(:name).limit(2) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 5, posts.length + end + + it "does not remove ordering from 'not' subquery that includes an offset" do + authors_subquery = Author.where.not(name: ["David", "Ronan", "Cian"]).order(:name).offset(1) + posts = Post.where(author: authors_subquery) + + assert_includes authors_subquery.to_sql, "ORDER BY [authors].[name]" + assert_includes posts.to_sql, "ORDER BY [authors].[name]" + assert_equal 3, posts.length + end end From 3cfc0b76006bed4ddd7863d419bd2b6602ac9a9d Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 19 Apr 2021 17:11:51 -0300 Subject: [PATCH 0946/1412] Rails 6.1 fixes: coerce bind param test and fix pool spec access (#889) * ConnectionPool is instantiated with a DatabaseConfig rather than a ConnectionSpecification See https://github.com/rails/rails/commit/48c716bbfa971668cedd0fffa874459044459ea1#diff-642b90553b888bd2c724c093a1a685a5408a7d8293f3751366c25dc548936eb7 * coerce bind parameter test. We include EXEC sp_executesql for prepared statements --- test/cases/adapter_test_sqlserver.rb | 4 +-- test/cases/coerced_tests.rb | 49 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index d86d6ffd5..b3f285a65 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -65,8 +65,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase arunit_connection = Topic.connection arunit2_connection = College.connection - arunit_database = arunit_connection.pool.spec.config[:database] - arunit2_database = arunit2_connection.pool.spec.config[:database] + arunit_database = arunit_connection.pool.db_config.database + arunit2_database = arunit2_connection.pool.db_config.database # Assert that connections use different default databases schemas. assert_not_equal arunit_database, arunit2_database diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 83ced0d39..a98d4b667 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -217,6 +217,55 @@ def test_binds_are_logged_coerced coerce_tests! :test_statement_cache_with_find_by coerce_tests! :test_statement_cache_with_in_clause coerce_tests! :test_statement_cache_with_sql_string_literal + + # Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper. + coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements + def test_bind_params_to_sql_with_prepared_statements_coerced + assert_bind_params_to_sql_coerced(prepared: true) + end + + def test_bind_params_to_sql_with_unprepared_statements_coerced + @connection.unprepared_statement do + assert_bind_params_to_sql_coerced(prepared: false) + end + end + + private + + def assert_bind_params_to_sql_coerced(prepared:) + table = Author.quoted_table_name + pk = "#{table}.#{Author.quoted_primary_key}" + + # prepared_statements: true + # + # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 + # + # prepared_statements: false + # + # SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL) + # + sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)" + sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" + + authors = Author.where(id: [1, 2, 3, nil]) + assert_equal sql_unprepared, @connection.to_sql(authors.arel) + assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } + + # prepared_statements: true + # + # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 + # + # prepared_statements: false + # + # SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3) + # + sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})" + sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" + + authors = Author.where(id: [1, 2, 3, 9223372036854775808]) + assert_equal sql_unprepared, @connection.to_sql(authors.arel) + assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } + end end end From fe4ebf5b0df2685ac065ccf8a0e074f4fa08447a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 19 Apr 2021 21:12:48 +0100 Subject: [PATCH 0947/1412] Rails 6.1: Coerce tests that check for LIMIT in SQL (#886) * Coerce tests that check for LIMIT in SQL * Added missing call to coerce test Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a98d4b667..1b3d4098f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -656,7 +656,11 @@ def test_count_with_include_coerced 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 @@ -701,6 +705,84 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced end end end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_include_on_unloaded_relation_with_match + def test_include_on_unloaded_relation_with_match_coerced + assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_equal true, Customer.where(name: "David").include?(customers(:david)) + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_include_on_unloaded_relation_without_match + def test_include_on_unloaded_relation_without_match_coerced + assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_equal false, Customer.where(name: "David").include?(customers(:mary)) + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_member_on_unloaded_relation_with_match + def test_member_on_unloaded_relation_with_match_coerced + assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_equal true, Customer.where(name: "David").member?(customers(:david)) + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_member_on_unloaded_relation_without_match + def test_member_on_unloaded_relation_without_match_coerced + assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_equal false, Customer.where(name: "David").member?(customers(:mary)) + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_column_is_configurable + def test_implicit_order_column_is_configurable_coerced + old_implicit_order_column = Topic.implicit_order_column + Topic.implicit_order_column = "title" + + assert_equal topics(:fifth), Topic.first + assert_equal topics(:third), Topic.last + + c = Topic.connection + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + Topic.last + } + ensure + Topic.implicit_order_column = old_implicit_order_column + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_set_to_primary_key + def test_implicit_order_set_to_primary_key_coerced + old_implicit_order_column = Topic.implicit_order_column + Topic.implicit_order_column = "id" + + c = Topic.connection + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + Topic.last + } + ensure + Topic.implicit_order_column = old_implicit_order_column + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_for_model_without_primary_key + def test_implicit_order_for_model_without_primary_key_coerced + old_implicit_order_column = NonPrimaryKey.implicit_order_column + NonPrimaryKey.implicit_order_column = "created_at" + + c = NonPrimaryKey.connection + + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + NonPrimaryKey.last + } + ensure + NonPrimaryKey.implicit_order_column = old_implicit_order_column + end end module ActiveRecord From c4cf20f1b99a0bb65c5c4522548a0a10b5e82a43 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 15:50:53 +0100 Subject: [PATCH 0948/1412] Removed tests that no longer need to be coerced (#899) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1b3d4098f..2acf225ca 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1587,20 +1587,6 @@ def test_has_primary_key_coerced end end -module ActiveRecord - module ConnectionAdapters - class ReaperTest < ActiveRecord::TestCase - # Coerce can be removed if Rails version > 6.0.3 - coerce_tests! :test_connection_pool_starts_reaper_in_fork unless Process.respond_to?(:fork) - end - end -end - -class FixturesTest < ActiveRecord::TestCase - # Skip test on Windows. Skip can be removed when Rails PR https://github.com/rails/rails/pull/39234 has been merged. - coerce_tests! :test_binary_in_fixtures if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ -end - class ReloadModelsTest < ActiveRecord::TestCase # Skip test on Windows. The number of arguements passed to `IO.popen` in # `activesupport/lib/active_support/testing/isolation.rb` exceeds what Windows can handle. From e309aee168cb074ff5397534e3d9652c6653b75b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 15:51:58 +0100 Subject: [PATCH 0949/1412] Revert PR #885 in favour of #883 (#896) Co-authored-by: Aidan Haran --- CHANGELOG.md | 1 - lib/active_record/connection_adapters/sqlserver/quoting.rb | 2 -- 2 files changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38bc6660b..2cebdff77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,6 @@ - [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn - [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config -- [#885](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix the quoting of ActiveModel attributes - [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index c7f5550de..09d8226bc 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -118,8 +118,6 @@ def _quote(value) value.quoted when String, ActiveSupport::Multibyte::Chars "#{QUOTED_STRING_PREFIX}#{super}" - when ActiveModel::Attribute - quote(value.value_for_database) else super end From 152ebf2fe9a649351b098e7576be7d2b59a1873c Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 20 Apr 2021 11:55:19 -0300 Subject: [PATCH 0950/1412] Rails 6.1: Add support for `if_exists` on `remove_column` (#892) * Add support for "if_exists" on "remove_column" See https://github.com/rails/rails/commit/95ed7e7809a9f5939ffb8dc9232e7739c39d9778 * add changelog item --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2cebdff77..6f6ecf8e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config - [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change +- [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 7bb1fa5b2..dadca051d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -130,8 +130,9 @@ def rename_table(table_name, new_name) rename_table_indexes(table_name, new_name) end - def remove_column(table_name, column_name, type = nil, options = {}) + def remove_column(table_name, column_name, type = nil, **options) raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_name.is_a? Array + return if options[:if_exists] == true && !column_exists?(table_name, column_name) remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) From 4db1c94efb639dec7be6a1fae08e48233a0e4368 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 15:56:21 +0100 Subject: [PATCH 0951/1412] Remove coerced test no longer in Rails test suite (#895) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2acf225ca..352143d74 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -28,9 +28,6 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase - # I really don`t think we can support legacy binds. - coerce_tests! :test_insert_update_delete_with_legacy_binds - # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement From 151c3fffba7e742e6454e28f32f577afb3739b47 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 15:57:58 +0100 Subject: [PATCH 0952/1412] Coerce annotation tests as our SQL starts with 'EXEC sp_executesql' (#898) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 42 ++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 352143d74..d46c118db 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1585,7 +1585,47 @@ def test_has_primary_key_coerced end class ReloadModelsTest < ActiveRecord::TestCase - # Skip test on Windows. The number of arguements passed to `IO.popen` in + # 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/ end + +require "models/post" +class AnnotateTest < ActiveRecord::TestCase + # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. + coerce_tests! :test_annotate_wraps_content_in_an_inline_comment + def test_annotate_wraps_content_in_an_inline_comment_coerced + quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") + + assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do + posts = Post.select(:id).annotate("foo") + assert posts.first + end + end + + # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. + coerce_tests! :test_annotate_is_sanitized + def test_annotate_is_sanitized_coerced + quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") + + assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do + posts = Post.select(:id).annotate("*/foo/*") + assert posts.first + end + + assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do + posts = Post.select(:id).annotate("**//foo//**") + assert posts.first + end + + assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do + posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar") + assert posts.first + end + + assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do + posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)") + assert posts.first + end + end +end From 5d50851a3ccd3931cf6f2e30cfe5e01998ce857f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 15:59:02 +0100 Subject: [PATCH 0953/1412] Mark transaction as written if non-read SQL performed (#897) Co-authored-by: Aidan Haran --- .../connection_adapters/sqlserver/database_statements.rb | 3 +++ test/cases/execute_procedure_test_sqlserver.rb | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3596af9dd..194b0acd9 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -17,6 +17,7 @@ def execute(sql, name = nil) end materialize_transactions + mark_transaction_written_if_write(sql) if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } @@ -31,6 +32,7 @@ def exec_query(sql, name = "SQL", binds = [], prepare: false) end materialize_transactions + mark_transaction_written_if_write(sql) sp_executesql(sql, name, binds, prepare: prepare) end @@ -291,6 +293,7 @@ def set_identity_insert(table_name, enable = true) def do_execute(sql, name = "SQL") materialize_transactions + mark_transaction_written_if_write(sql) log(sql, name) { raw_connection_do(sql) } end diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 19164f70f..45b73949b 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -41,4 +41,13 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase date_base = connection.select_value("select GETUTCDATE()") assert_equal date_base.change(usec: 0), date_proc.change(usec: 0) end + + it 'test deprecation with transaction return when executing procedure' do + assert_deprecated do + ActiveRecord::Base.transaction do + connection.execute_procedure("my_getutcdate") + return + end + end + end end From d77951794ca164306f35ff7fd3b3f0f2d8e4cd26 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 20 Apr 2021 12:04:27 -0300 Subject: [PATCH 0954/1412] Rails 6.1: Add support for `if_not_exists` to indexes (#891) * add create index if exists support See https://github.com/rails/rails/commit/7ba08037f8d807cce2a25b0445743d07adfbe44c * add changelog item --- CHANGELOG.md | 1 + .../sqlserver/schema_creation.rb | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f6ecf8e1..a34535464 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ - [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config - [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change +- [#891](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/891) Add support for if_not_exists to indexes - [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 44b96602b..0b70b8d50 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -33,6 +33,20 @@ def visit_TableDefinition(o) sql end + def visit_CreateIndexDefinition(o) + if_not_exists = o.if_not_exists + + o.if_not_exists = false + + sql = super + + if if_not_exists + sql = "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '#{o.index.name}') #{sql}" + end + + sql + end + def add_column_options!(sql, options) sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) if options[:null] == false From a76c02d0e3b61124be7c2295770c79b282c83de6 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 20 Apr 2021 12:04:54 -0300 Subject: [PATCH 0955/1412] Rails 6.1: Fix 'TypeError: can't quote ActiveRecord::Relation::QueryAttribute' (#883) * don't patch unprepared statements took the idea from https://github.com/rails/rails/commit/157f6a6efe5dba7e502140194baf1af62e618a8a#diff-39e2986a9d501a5ce3587a8d5944feb67c91e777f44270627850c219709d6510 and the fact that SubstituteCollector is only used for unprepared_statements (see https://github.com/rails/rails/blob/v6.1.0/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb#L729) * handle explain with ::ActiveRecord::Relation::QueryAttribute binds Idea took from https://github.com/rails/rails/commit/157f6a6efe5dba7e502140194baf1af62e618a8a#diff-137c2c919e7860732301c49c60a700517455564bf463ff036756fb31c02a60e5 * add changelog entry * apply suggestion --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/core_ext/explain.rb | 7 ++++++- lib/arel/visitors/sqlserver.rb | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a34535464..cbd820aad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change - [#891](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/891) Add support for if_not_exists to indexes - [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column +- [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes #### Changed 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 bad3d8f12..752350e4c 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -27,7 +27,12 @@ def unprepare_sqlserver_statement(sql, binds) executesql = executesql.match(SQLSERVER_STATEMENT_REGEXP).to_a[1] binds.each_with_index do |bind, index| - value = connection.quote(bind) + + value = if bind.is_a?(::ActiveModel::Attribute) then + connection.quote(bind.value_for_database) + else + connection.quote(bind) + end executesql = executesql.sub("@#{index}", value) end diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 1d377110f..f0dea89a0 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -79,7 +79,7 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) if values.empty? collector << @connection.quote(nil) - else + elsif @connection.prepared_statements # Monkey-patch start. Add query attribute bindings rather than just values. column_name = o.column_name column_type = o.attribute.relation.type_for_attribute(o.column_name) @@ -87,6 +87,8 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) collector.add_binds(attrs, &bind_block) # Monkey-patch end. + else + collector.add_binds(values, &bind_block) end collector << ")" From 81ec762ffc76c29ff81fd295435ddb7ca2bb0499 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 20 Apr 2021 12:07:36 -0300 Subject: [PATCH 0956/1412] Rails 6.1: Fixes related to `while_preventing_writes` (#888) * fix tests: stop calling while_preventing_writes on ActiveRecord::Base.connection_handler, call it on ActiveRecord::Base raise added here https://github.com/rails/rails/pull/40489 * add missing coerced test Missing test was moved to another testcase here https://github.com/rails/rails/commit/31461d8a79cf7a6455589319b2e2d6fac76a04c1#diff-39aac151bf5772be2c6c4fec49bf5cdf5a21ca816fe181ef01466ecd941bef9b * coearce test that asserts no queries. We do some read queries --- test/cases/adapter_test_sqlserver.rb | 9 ++++----- test/cases/coerced_tests.rb | 29 +++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 6 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index b3f285a65..a0ea7faf7 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -469,12 +469,11 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe "block writes to a database" do def setup @conn = ActiveRecord::Base.connection - @connection_handler = ActiveRecord::Base.connection_handler end def test_errors_when_an_insert_query_is_called_while_preventing_writes assert_raises(ActiveRecord::ReadOnlyError) do - @connection_handler.while_preventing_writes do + ActiveRecord::Base.while_preventing_writes do @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") end end @@ -484,7 +483,7 @@ def test_errors_when_an_update_query_is_called_while_preventing_writes @conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") assert_raises(ActiveRecord::ReadOnlyError) do - @connection_handler.while_preventing_writes do + ActiveRecord::Base.while_preventing_writes do @conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'") end end @@ -494,7 +493,7 @@ def test_errors_when_a_delete_query_is_called_while_preventing_writes @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") assert_raises(ActiveRecord::ReadOnlyError) do - @connection_handler.while_preventing_writes do + ActiveRecord::Base.while_preventing_writes do @conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'") end end @@ -503,7 +502,7 @@ def test_errors_when_a_delete_query_is_called_while_preventing_writes def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") - @connection_handler.while_preventing_writes do + ActiveRecord::Base.while_preventing_writes do assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'") end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d46c118db..e594693cc 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -44,6 +44,33 @@ def test_value_limit_violations_are_translated_to_specific_exception_coerced end end +module ActiveRecord + class AdapterPreventWritesTest < ActiveRecord::TestCase + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes + def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_is_called_while_preventing_writes + end + end +end + +module ActiveRecord + class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase + # We do some read queries. Remove assert_no_queries + coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes + def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced + @connection_handler.while_preventing_writes do + @connection.transaction do + assert_raises(ActiveRecord::ReadOnlyError) do + @connection.insert("/* some comment */ INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false) + end + end + end + end + end +end + module ActiveRecord class AdapterTestWithoutTransaction < ActiveRecord::TestCase # SQL Server does not allow truncation of tables that are referenced by foreign key @@ -156,7 +183,7 @@ def test_update_date_time_attributes_with_default_timezone_local # SQL Server does not have query for release_savepoint coerce_tests! %r{an empty transaction does not raise if preventing writes} test "an empty transaction does not raise if preventing writes coerced" do - ActiveRecord::Base.connection_handler.while_preventing_writes do + ActiveRecord::Base.while_preventing_writes do assert_queries(1, ignore_none: true) do Bird.transaction do ActiveRecord::Base.connection.materialize_transactions From 35651d3cf805768a69e387dcfd625b597568a83f Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 20 Apr 2021 12:09:24 -0300 Subject: [PATCH 0957/1412] Rails 6.1: Add Active Record Marshal forward compatibility tests (#893) * add Active Record Marshal forward compatibility tests * add changelog item --- CHANGELOG.md | 1 + test/cases/coerced_tests.rb | 11 +++++++++++ .../SQLServer/rails_6_0_topic.dump | Bin 0 -> 2165 bytes .../SQLServer/rails_6_0_topic_associations.dump | Bin 0 -> 3086 bytes 4 files changed, 12 insertions(+) create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump diff --git a/CHANGELOG.md b/CHANGELOG.md index cbd820aad..cc54be65a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ - [#891](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/891) Add support for if_not_exists to indexes - [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column - [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes +- [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests #### Changed diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e594693cc..e6a44ef5f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1656,3 +1656,14 @@ def test_annotate_is_sanitized_coerced end end end + +class MarshalSerializationTest < ActiveRecord::TestCase + private + + def marshal_fixture_path(file_name) + File.expand_path( + "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump", + ARTest::SQLServer.test_root_sqlserver + ) + end +end diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump new file mode 100644 index 0000000000000000000000000000000000000000..bb1a80786a9bdd97beef9b46c0c92752d0734ebd GIT binary patch literal 2165 zcmbW2?`|7K5WvkJ`EQ(m6r~kyuv{X9szh!+Aa$-1n9oS9e9B}2am#+Fneb^u>%O%{&2fq5aCnSnhz6iDiV@(I?D@)K4^0JDSWIEcgif@e z#*#op=oXP2NGt2KGD7u`uJ*<~%TU30LDYe27kg4g+m%B8dk+z2z;-5B~C-t0J|1ZMYJH+=KVvR^h|nSN!Iqu?7x= zjJOCxgGu6oVe5W#<-$m7QE{#E`1Tfv??71W$N}z>XIaZ)Ymw=$R%ya5y~s_!T)?z5 z>#QzKtS(0)u>_O|7dPtSohFGQT?#Z&pa{EaXUg3Kq?{DuMv6#YVt87C8t21Z&QD8F zXGz+%j-?uNpU9k+H24eLbM_M-iFecZqGzR>o10%XY|Wb`cLep1dq;=+I!XtsL=3z7 z%2O4Am5NqL^B*Au9?;ybO$g8b-0ZPA1NXG^=gFJ3(V`uS`r`pqZX+uM!T zVu%gVpdN^gP>iZOJ3G}p;#qzPv>ALUzWd<01pg}+vM|pD*}R7Z$);4$`W6b>%rTKA za hUlWm?a1u+?cZ2gBd literal 0 HcmV?d00001 diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump new file mode 100644 index 0000000000000000000000000000000000000000..5448055642b3314dbe26d156d8db06a8f57a99b1 GIT binary patch literal 3086 zcmbVO>uwuG6gC(68YgLJ)mDXK87e?kA~#$+TfsOfrYkj2Ql|*{$7sAev8P$@EVHvt z-3U?fB)kJJ(C6S$_*1^wwH>bws7fLwGqY#T`R1JOT;47#QthfpdYfdEhT@cI;VJER zu;a#P*-wm!6}j2=j2w#t<%@uj_Uy}JVTw-ah4JO znm`mCIE8dB0P`r69-Q=qYj4PP*;#8j^_H_P-}u{YZt}zom1ZFyWfq*v=K^p3vEeqa zPbI{N%Ybp;*CLKB7`o*)H%_%QMubd-8NT)f@ZIM|bmd6w!m_NT*jiBCS78z}gAbYN zdlyh`)mp7-&iiys11?sl@xI=@XmaQ5BRe=zTkH0)vH&(DROIBEzE(`KerDa?yDdf zg(Yy<=2yxEGJ6!+qN@KCslYtAF7|3aq&xb)+gz8UNNK~fm)ny%k0PzcV(Q#)mQB7+ zPAL@#9?tQ@zgt>FffOud$vB_!Yr=N&lAEE1t`TyE`e9Q6Czqxt^JKO8QL({IC9tUuv`V7cZo>l$jqn*yi9@e| z@=4_9jud$>bK$heWn_B7gJgk#XVW?c9J07rE!^hi?oc#3Qpcu|7F26{HRo~XJ|AZ< z`Ja4un>TzbMgx`jW+=lG84h+lHOjOul>#>*6`Ota^}nvayg&RiDw4``6$-amKI-n{ zN@pd--NgB@zEdc?g#Y2DTl60MoXgiEQnAU`tI2|V>omNDfA~FK+ugl;_s$o0@~P-H z-`n2aZam;)(clf#K+*8|q`I@SQ_T?P`3s`u=nH(S#Ip|mPhQBvGB2QWg%&uQvjXdL zQed;hiPRy#e}D7kXt$=P4q4*Js>oU4tVpYhe3E0%y!R2((P{)Q@;;A6g;c36{j^Hn zrDMx0))&W03ucMSJQT|oMC^vv$Th08M7QGT1G9G9IxCQ?6qP}FlG}cXOWshN70KE( zagNBR`8v=(7e)a>Gd|$qWZ|$^a<+SQl*bxXr=ifafb|+^$9>^<(zW4wAWYAGxI|-v zUd!;;h!zgksSMGi<4pES4x992E0PBsdn!tA*3iS%vxy>~A0p=`7%I{UBP-=>0b$Hg zGJ|YBcx11v1IB_6=ug0YlGe*=rm9Ba@Br<9v#ss8(zUy?^Re5k89VBv9TiIEZWmgS z=A!*<=-5X@>;Q7Wkx)>Iz{J$I7p+2u9>e1noR!8hMr`U-9UsTSVC}Rzcsa%tUwZHr z>nAcm6Hv2JY1XnH%_R0Rfs#WMX)Ix}<_?76C|JDW+@-$rHE>`;!}C}Qh6?Aa7NRla zm}d}=UaeR1${5cSxszA6Y!Hsunok~N7mLmweTu8q^iVn9%?@tlwH%5dC7>dov6P4%uq7ip$P#He;(EeP#3Y-6(H9UYI~J)hm~=1+_#!r1O^`&W3=lp+r$S+1 z>3Nb)#vO9=T#WT6Y#{?$_-0d58)h*0#yq_-O;X_dm&0q6AA{RKoOnZ25hR!|3;zID CZ1czf literal 0 HcmV?d00001 From b5e38a18a182aed4e0b7c612a5240f97c8d99231 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 Apr 2021 17:48:40 +0100 Subject: [PATCH 0958/1412] Removed deprecated in_clause_length method (#900) Co-authored-by: Aidan Haran --- .../connection_adapters/sqlserver/database_limits.rb | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 14ad94865..4fa681115 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -37,10 +37,6 @@ def columns_per_multicolumn_index end deprecate :columns_per_multicolumn_index - def in_clause_length - 10_000 - end - def sql_query_length 65_536 * 4_096 end From c0f5f7bcb0f8c30c9db623530a83e82eef2b4206 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 21 Apr 2021 07:16:19 -0300 Subject: [PATCH 0959/1412] Rails 6.1: Fix "ActiveRecord::StatementInvalid: A column has been specified more than once in the order by list" (#894) * add spec showing adapters behaviour * coerce test avoiding duplicate columns in order list --- test/cases/coerced_tests.rb | 26 ++++++++++++++++++++++++++ test/cases/order_test_sqlserver.rb | 7 +++++++ 2 files changed, 33 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e6a44ef5f..fabd4fb98 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1667,3 +1667,29 @@ def marshal_fixture_path(file_name) ) end end + +class NestedThroughAssociationsTest < ActiveRecord::TestCase + # Same as original but replace order with "order(:id)" to ensure that assert_includes_and_joins_equal doesn't raise + # "A column has been specified more than once in the order by list" + # Example: original test generate queries like "ORDER BY authors.id, [authors].[id]". We don't support duplicate columns in the order list + coerce_tests! :test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins, :test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins + def test_has_many_through_has_many_with_has_many_through_habtm_source_reflection_preload_via_joins_coerced + # preload table schemas + Author.joins(:category_post_comments).first + + assert_includes_and_joins_equal( + Author.where("comments.id" => comments(:does_it_hurt).id).order(:id), + [authors(:david), authors(:mary)], :category_post_comments + ) + end + + def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflection_preload_via_joins_coerced + # preload table schemas + Category.joins(:post_comments).first + + assert_includes_and_joins_equal( + Category.where("comments.id" => comments(:more_greetings).id).order(:id), + [categories(:general), categories(:technology)], :post_comments + ) + end +end diff --git a/test/cases/order_test_sqlserver.rb b/test/cases/order_test_sqlserver.rb index 712fff1a2..82f43d539 100644 --- a/test/cases/order_test_sqlserver.rb +++ b/test/cases/order_test_sqlserver.rb @@ -143,4 +143,11 @@ class OrderTestSQLServer < ActiveRecord::TestCase assert_equal post1, Post.order(Arel.sql(order)).first assert_equal post2, Post.order(Arel.sql(order)).second end + + # Executing this kind of queries will raise "A column has been specified more than once in the order by list" + # This test shows that we don't do anything to prevent this + it "doesn't deduplicate semantically equal orders" do + sql = Post.order(:id).order("posts.id ASC").to_sql + assert_equal "SELECT [posts].* FROM [posts] ORDER BY [posts].[id] ASC, posts.id ASC", sql + end end From 2b5a37aaf48a378c7d106a54fefdf0a661a68ddb Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 21 Apr 2021 07:30:22 -0300 Subject: [PATCH 0960/1412] Rails 6.1: fix rake task (#904) * configuration_hash use symbols as keys * db_tasks.load_schema requires db_config object --- .../tasks/sqlserver_database_tasks.rb | 16 ++++++++-------- test/cases/rake_test_sqlserver.rb | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 375369f57..25d92c355 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -24,7 +24,7 @@ def initialize(configuration) def create(master_established = false) establish_master_connection unless master_established - connection.create_database configuration.database, configuration_hash.merge("collation" => default_collation) + connection.create_database configuration.database, configuration_hash.merge(collation: default_collation) establish_connection configuration rescue ActiveRecord::StatementInvalid => e if /database .* already exists/i === e.message @@ -54,14 +54,14 @@ def purge end def structure_dump(filename, extra_flags) - server_arg = "-S #{Shellwords.escape(configuration_hash['host'])}" - server_arg += ":#{Shellwords.escape(configuration_hash['port'])}" if configuration_hash["port"] + server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}" + server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port] command = [ "defncopy-ttds", server_arg, - "-D #{Shellwords.escape(configuration_hash['database'])}", - "-U #{Shellwords.escape(configuration_hash['username'])}", - "-P #{Shellwords.escape(configuration_hash['password'])}", + "-D #{Shellwords.escape(configuration_hash[:database])}", + "-U #{Shellwords.escape(configuration_hash[:username])}", + "-P #{Shellwords.escape(configuration_hash[:password])}", "-o #{Shellwords.escape(filename)}", ] table_args = connection.tables.map { |t| Shellwords.escape(t) } @@ -88,11 +88,11 @@ def structure_load(filename, extra_flags) attr_reader :configuration, :configuration_hash def default_collation - configuration_hash["collation"] || DEFAULT_COLLATION + configuration_hash[:collation] || DEFAULT_COLLATION end def establish_master_connection - establish_connection configuration_hash.merge("database" => "master") + establish_connection configuration_hash.merge(database: "master") end end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index ce76d3972..87b363a20 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -12,6 +12,7 @@ class SQLServerRakeTest < ActiveRecord::TestCase 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) } before { skip "on azure" if azure_skip } before { disconnect! unless azure_skip } @@ -151,7 +152,7 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest _(filedata).must_match %r{CREATE TABLE dbo\.users} db_tasks.purge(configuration) _(connection.tables).wont_include "users" - db_tasks.load_schema configuration, :sql, filename + db_tasks.load_schema db_config, :sql, filename _(connection.tables).must_include "users" end end From 887683e4da24f5c3c44b96b5ed8ff077d570bd47 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 21 Apr 2021 10:29:38 -0300 Subject: [PATCH 0961/1412] Rails 6.1: coerce tests when we produce more or less queries (#901) * coerce test because we produce some read queries * coerce test because we open less transactions * change coerced test --- test/cases/coerced_tests.rb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index fabd4fb98..afbfef5aa 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -68,6 +68,12 @@ def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_ end end end + + coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes + def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes + end end end @@ -1693,3 +1699,31 @@ def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflectio ) end end + +class BasePreventWritesTest < ActiveRecord::TestCase + # SQL Server does not have query for release_savepoint + coerce_tests! %r{an empty transaction does not raise if preventing writes} + test "an empty transaction does not raise if preventing writes coerced" do + ActiveRecord::Base.while_preventing_writes do + assert_queries(1, ignore_none: true) do + Bird.transaction do + ActiveRecord::Base.connection.materialize_transactions + end + end + end + end +end + +class BasePreventWritesLegacyTest < ActiveRecord::TestCase + # SQL Server does not have query for release_savepoint + coerce_tests! %r{an empty transaction does not raise if preventing writes} + test "an empty transaction does not raise if preventing writes coerced" do + ActiveRecord::Base.connection_handler.while_preventing_writes do + assert_queries(1, ignore_none: true) do + Bird.transaction do + ActiveRecord::Base.connection.materialize_transactions + end + end + end + end +end From fea63a92bc7f8fd95dde21897249230cce213ba3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 21 Apr 2021 14:56:12 +0100 Subject: [PATCH 0962/1412] Coerce test as SQL is invalid for MSSQL (#905) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index afbfef5aa..6f92398b0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -813,6 +813,9 @@ def test_implicit_order_for_model_without_primary_key_coerced ensure NonPrimaryKey.implicit_order_column = old_implicit_order_column end + + # SQL Server is unable to use aliased SELECT in the HAVING clause. + coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select end module ActiveRecord From 854ad39e5871dea68abde53785279aab3ed13a2e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 21 Apr 2021 15:25:13 +0100 Subject: [PATCH 0963/1412] Added comments (#906) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6f92398b0..bcbd71d58 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1629,6 +1629,7 @@ class ReloadModelsTest < ActiveRecord::TestCase require "models/post" class AnnotateTest < ActiveRecord::TestCase # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. + # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027) coerce_tests! :test_annotate_wraps_content_in_an_inline_comment def test_annotate_wraps_content_in_an_inline_comment_coerced quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") @@ -1640,6 +1641,7 @@ def test_annotate_wraps_content_in_an_inline_comment_coerced end # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. + # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027) coerce_tests! :test_annotate_is_sanitized def test_annotate_is_sanitized_coerced quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") From 5514dc0aee1dcfdcdb42eaab4dc52b26e24997a8 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 22 Apr 2021 06:28:15 -0300 Subject: [PATCH 0964/1412] coerse test to change binding syntax to @N (#908) --- test/cases/coerced_tests.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bcbd71d58..8724ba923 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1518,6 +1518,25 @@ def test_merging_with_order_with_binds_coerced relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"])) assert_equal ["title LIKE N'%suffix'"], relation.order_values end + + # Same as original but change first regexp to match sp_executesql binding syntax + coerce_tests! :test_merge_doesnt_duplicate_same_clauses + def test_merge_doesnt_duplicate_same_clauses_coerced + david, mary, bob = authors(:david, :mary, :bob) + + non_mary_and_bob = Author.where.not(id: [mary, bob]) + + author_id = Author.connection.quote_table_name("authors.id") + assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do + assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob) + end + + only_david = Author.where("#{author_id} IN (?)", david) + + assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do + assert_equal [david], only_david.merge(only_david) + end + end end module ActiveRecord From c4ae5f12dd9a71a03de3a0751dc771973cf6ac17 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 22 Apr 2021 11:05:58 +0100 Subject: [PATCH 0965/1412] Rails test was renamed and so the coerced test should have been renamed instead of removed (#909) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8724ba923..b90ed32b9 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1622,6 +1622,30 @@ def test_insert_all_coerced end end +require "models/citation" +class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase + fixtures :citations + + # Original Rails test fails with SQL Server error message "The query processor ran out of internal resources and + # could not produce a query plan". This error goes away if you change database compatibility level to 110 (SQL 2012) + # (see https://www.mssqltips.com/sqlservertip/5279/sql-server-error-query-processor-ran-out-of-internal-resources-and-could-not-produce-a-query-plan/). + # However, you cannot change the compatibility level during a test. The purpose of the test is to ensure that an + # unprepared statement is used if the number of values exceeds the adapter's `bind_params_length`. The coerced test + # still does this as there will be 32,768 remaining citation records in the database and the `bind_params_length` of + # adapter is 2,098. + coerce_tests! :test_eager_loading_too_many_ids + def test_eager_loading_too_many_ids_coerced + # Remove excess records. + Citation.limit(32768).order(id: :desc).delete_all + + # Perform test + citation_count = Citation.count + assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do + assert_equal citation_count, Citation.eager_load(:citations).offset(0).size + end + end +end + class LogSubscriberTest < ActiveRecord::TestCase # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. coerce_tests! :test_vebose_query_logs From df110e8c9c89c75ae62a26b2a625f7321f55ae07 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 22 Apr 2021 15:06:08 +0100 Subject: [PATCH 0966/1412] Fixed the namespace of the BasePreventWritesLegacyTest class (#911) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b90ed32b9..7efae8e54 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1760,18 +1760,20 @@ class BasePreventWritesTest < ActiveRecord::TestCase end end end -end -class BasePreventWritesLegacyTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint - coerce_tests! %r{an empty transaction does not raise if preventing writes} - test "an empty transaction does not raise if preventing writes coerced" do - ActiveRecord::Base.connection_handler.while_preventing_writes do - assert_queries(1, ignore_none: true) do - Bird.transaction do - ActiveRecord::Base.connection.materialize_transactions + class BasePreventWritesLegacyTest < ActiveRecord::TestCase + # SQL Server does not have query for release_savepoint + coerce_tests! %r{an empty transaction does not raise if preventing writes} + test "an empty transaction does not raise if preventing writes coerced" do + ActiveRecord::Base.connection_handler.while_preventing_writes do + assert_queries(1, ignore_none: true) do + Bird.transaction do + ActiveRecord::Base.connection.materialize_transactions + end end end end end end + + From 0e0da503c0268122f6ff96631ce1ae4e3622f72d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 22 Apr 2021 15:15:07 +0100 Subject: [PATCH 0967/1412] Coerce some calculation tests (#910) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 105 +++++++++++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7efae8e54..4f06fd607 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -318,11 +318,112 @@ def test_offset_is_kept_coerced original_test_offset_is_kept end - # Are decimal, not integer. + # The SQL Server `AVG()` function for a list of integers returns an integer (not a decimal). coerce_tests! :test_should_return_decimal_average_of_integer_field def test_should_return_decimal_average_of_integer_field_coerced value = Account.average(:id) - assert_equal BigDecimal("3.0").to_s, BigDecimal(value).to_s + assert_equal 3, value + end + + # In SQL Server the `AVG()` function for a list of integers returns an integer so need to cast values as decimals before averaging. + # Match SQL Server limit implementation. + coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_sql + def test_select_avg_with_group_by_as_virtual_attribute_with_sql_coerced + rails_core = companies(:rails_core) + + sql = <<~SQL + SELECT firm_id, AVG(CAST(credit_limit AS DECIMAL)) AS avg_credit_limit + FROM accounts + WHERE firm_id = ? + GROUP BY firm_id + ORDER BY firm_id + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY + SQL + + account = Account.find_by_sql([sql, rails_core]).first + + # id was not selected, so it should be nil + # (cannot select id because it wasn't used in the GROUP BY clause) + assert_nil account.id + + # firm_id was explicitly selected, so it should be present + assert_equal(rails_core, account.firm) + + # avg_credit_limit should be present as a virtual attribute + assert_equal(52.5, account.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. + # Order column must be in the GROUP clause. + coerce_tests! :test_select_avg_with_group_by_as_virtual_attribute_with_ar + 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! + + # id was not selected, so it should be nil + # (cannot select id because it wasn't used in the GROUP BY clause) + assert_nil account.id + + # firm_id was explicitly selected, so it should be present + assert_equal(rails_core, account.firm) + + # avg_credit_limit should be present as a virtual attribute + assert_equal(52.5, account.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. + # Match SQL Server limit implementation. + coerce_tests! :test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql + def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerced + rails_core = companies(:rails_core) + + sql = <<~SQL + SELECT companies.*, AVG(CAST(accounts.credit_limit AS DECIMAL)) AS avg_credit_limit + FROM companies + INNER JOIN accounts ON companies.id = accounts.firm_id + WHERE companies.id = ? + GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description + ORDER BY companies.id + OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY + SQL + + firm = DependentFirm.find_by_sql([sql, rails_core]).first + + # all the DependentFirm attributes should be present + assert_equal rails_core, firm + assert_equal rails_core.name, firm.name + + # avg_credit_limit should be present as a virtual attribute + 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 + 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) + .take! + + # all the DependentFirm attributes should be present + assert_equal rails_core, firm + assert_equal rails_core.name, firm.name + + # avg_credit_limit should be present as a virtual attribute + assert_equal(52.5, firm.avg_credit_limit) end # Match SQL Server limit implementation From 8a061a15f7fb91cbcbfe6cb98ce37d5e5c4bbedf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Apr 2021 18:13:10 +0100 Subject: [PATCH 0968/1412] Coerce test to handle default case-insensitive collation (#915) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4f06fd607..7c4fdafda 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -23,6 +23,28 @@ def test_validate_uniqueness_with_limit_and_utf8_coerced end end end + + # Same as original coerced test except that it handles default SQL Server case-insensitive collation. + coerce_tests! :test_validate_uniqueness_by_default_database_collation + def test_validate_uniqueness_by_default_database_collation_coerced + Topic.validates_uniqueness_of(:author_email_address) + + topic1 = Topic.new(author_email_address: "david@loudthinking.com") + topic2 = Topic.new(author_email_address: "David@loudthinking.com") + + assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count + + assert_not topic1.valid? + assert_not topic1.save + + # Case insensitive collation (SQL_Latin1_General_CP1_CI_AS) by default. + # Should not allow "David" if "david" exists. + assert_not topic2.valid? + assert_not topic2.save + + assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count + assert_equal 1, Topic.where(author_email_address: "David@loudthinking.com").count + end end require "models/event" From b1a119c4578327e6c69a1ee044b9f3573dbd9fc1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Apr 2021 18:14:08 +0100 Subject: [PATCH 0969/1412] Legacy binds are not supported (#914) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7c4fdafda..9cbbcb8d1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -50,6 +50,10 @@ def test_validate_uniqueness_by_default_database_collation_coerced require "models/event" module ActiveRecord class AdapterTest < ActiveRecord::TestCase + # Legacy binds are not supported. + coerce_tests! :test_select_all_insert_update_delete_with_casted_binds + coerce_tests! :test_select_all_insert_update_delete_with_legacy_binds + # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement From 05806941a668ab5102bb440f8778953b5a6ec9b3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 28 Apr 2021 10:26:32 +0100 Subject: [PATCH 0970/1412] Load schema before running Rails test (#912) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9cbbcb8d1..a8f6d679e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -78,23 +78,40 @@ def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced Subscriber.send(:load_schema!) original_test_errors_when_an_insert_query_is_called_while_preventing_writes end + + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes + def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes + end + + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes + def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes + end end end module ActiveRecord class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase - # We do some read queries. Remove assert_no_queries + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced - @connection_handler.while_preventing_writes do - @connection.transaction do - assert_raises(ActiveRecord::ReadOnlyError) do - @connection.insert("/* some comment */ INSERT INTO subscribers(nick) VALUES ('138853948594')", nil, false) - end - end - end + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes end + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes + def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes + end + + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced Subscriber.send(:load_schema!) From 6855ef8dc7cddc423ee92892c66a0bfac355844f Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 28 Apr 2021 06:30:19 -0300 Subject: [PATCH 0971/1412] change default column value before marshal Time. Only do this for ruby (#916) lower than 2.7 --- test/cases/coerced_tests.rb | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a8f6d679e..e6cc9e24e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1517,8 +1517,36 @@ def test_statement_cache_values_differ_coerced module ActiveRecord module ConnectionAdapters class SchemaCacheTest < ActiveRecord::TestCase + # Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753 + coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk + def test_marshal_dump_and_load_with_gzip_coerced + with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip } + end + def test_marshal_dump_and_load_via_disk_coerced + with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk } + end + private + def with_marshable_time_defaults + # Detect problems + if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7") + column = @connection.columns(:sst_datatypes).find { |c| c.name == "datetime" } + current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900 + end + + # Correct problems + if current_default.present? + @connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900)) + end + + # Run original test + yield + ensure + # Revert changes + @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present? + end + # We need to give the full path for this to work. def schema_dump_path File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml" @@ -1919,5 +1947,3 @@ class BasePreventWritesLegacyTest < ActiveRecord::TestCase end end end - - From fef6c98898e560dda6e97b34cb2d780677275ed0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 28 Apr 2021 10:49:41 +0100 Subject: [PATCH 0972/1412] Refactored to use new_client connection pattern (#917) Co-authored-by: Aidan Haran --- CHANGELOG.md | 2 +- .../connection_adapters/sqlserver_adapter.rb | 147 ++++++++++-------- lib/active_record/sqlserver_base.rb | 24 ++- 3 files changed, 91 insertions(+), 82 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc54be65a..35f13574f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ #### Changed -- ... +- [#917](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/917) Refactored to use new_client connection pattern #### Added diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 92ecc2248..5a20ae1ff 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -59,13 +59,73 @@ class SQLServerAdapter < AbstractAdapter self.use_output_inserted = true self.exclude_output_inserted_table_names = Concurrent::Map.new { false } - def initialize(connection, logger = nil, config = {}) + class << self + def new_client(config) + case config[:mode] + when :dblib + require "tiny_tds" + dblib_connect(config) + else + raise ArgumentError, "Unknown connection mode in #{config.inspect}." + end + end + + def dblib_connect(config) + TinyTds::Client.new( + dataserver: config[:dataserver], + host: config[:host], + port: config[:port], + username: config[:username], + password: config[:password], + database: config[:database], + tds_version: config[:tds_version] || "7.3", + appname: config_appname(config), + login_timeout: config_login_timeout(config), + timeout: config_timeout(config), + encoding: config_encoding(config), + azure: config[:azure], + contained: config[:contained] + ).tap do |client| + if config[:azure] + client.execute("SET ANSI_NULLS ON").do + client.execute("SET ANSI_NULL_DFLT_ON ON").do + client.execute("SET ANSI_PADDING ON").do + client.execute("SET ANSI_WARNINGS ON").do + else + client.execute("SET ANSI_DEFAULTS ON").do + end + client.execute("SET QUOTED_IDENTIFIER ON").do + client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do + client.execute("SET IMPLICIT_TRANSACTIONS OFF").do + client.execute("SET TEXTSIZE 2147483647").do + client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do + end + rescue TinyTds::Error => e + raise ActiveRecord::NoDatabaseError if e.message.match(/database .* does not exist/i) + raise e + end + + def config_appname(config) + config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil + end + + def config_login_timeout(config) + config[:login_timeout].present? ? config[:login_timeout].to_i : nil + end + + def config_timeout(config) + config[:timeout].present? ? config[:timeout].to_i / 1000 : nil + end + + def config_encoding(config) + config[:encoding].present? ? config[:encoding] : nil + end + end + + def initialize(connection, logger, _connection_options, config) super(connection, logger, config) - # Our Responsibility @connection_options = config - connect - initialize_dateformatter - use_database + configure_connection end # === Abstract Adapter ========================================== # @@ -226,6 +286,14 @@ def reset! do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" end + def configure_connection + @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first + @version_year = version_year + + initialize_dateformatter + use_database + end + # === Abstract Adapter (Misc Support) =========================== # def tables_with_referential_integrity @@ -408,78 +476,18 @@ def translate_exception(e, message:, sql:, binds:) # === SQLServer Specific (Connection Management) ================ # - def connect - config = @connection_options - @connection = case config[:mode] - when :dblib - dblib_connect(config) - end - @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first - @version_year = version_year - configure_connection - end - def connection_errors @connection_errors ||= [].tap do |errors| errors << TinyTds::Error if defined?(TinyTds::Error) end end - def dblib_connect(config) - TinyTds::Client.new( - dataserver: config[:dataserver], - host: config[:host], - port: config[:port], - username: config[:username], - password: config[:password], - database: config[:database], - tds_version: config[:tds_version] || "7.3", - appname: config_appname(config), - login_timeout: config_login_timeout(config), - timeout: config_timeout(config), - encoding: config_encoding(config), - azure: config[:azure], - contained: config[:contained] - ).tap do |client| - if config[:azure] - client.execute("SET ANSI_NULLS ON").do - client.execute("SET ANSI_NULL_DFLT_ON ON").do - client.execute("SET ANSI_PADDING ON").do - client.execute("SET ANSI_WARNINGS ON").do - else - client.execute("SET ANSI_DEFAULTS ON").do - end - client.execute("SET QUOTED_IDENTIFIER ON").do - client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do - client.execute("SET IMPLICIT_TRANSACTIONS OFF").do - client.execute("SET TEXTSIZE 2147483647").do - client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do - end - end - - def config_appname(config) - config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil - end - - def config_login_timeout(config) - config[:login_timeout].present? ? config[:login_timeout].to_i : nil - end - - def config_timeout(config) - config[:timeout].present? ? config[:timeout].to_i / 1000 : nil - end - - def config_encoding(config) - config[:encoding].present? ? config[:encoding] : nil - end - - def configure_connection; end - def configure_application_name; end def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a + [a, b, c].each { |f| f.upcase! if f == "y" } dateformat = "%#{a}-%#{b}-%#{c}" ::Date::DATE_FORMATS[:_sqlserver_dateformat] = dateformat @@ -502,6 +510,13 @@ def version_year def sqlserver_version @sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s end + + private + + def connect + @connection = self.class.new_client(@connection_options) + configure_connection + end end end end diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 8f2150ced..0040cde7a 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -4,21 +4,15 @@ module ActiveRecord module ConnectionHandling def sqlserver_connection(config) #:nodoc: config = config.symbolize_keys - config.reverse_merge! mode: :dblib - mode = config[:mode].to_s.downcase.underscore.to_sym - case mode - when :dblib - require "tiny_tds" - else - raise ArgumentError, "Unknown connection mode in #{config.inspect}." - end - ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode)) - rescue TinyTds::Error => e - if e.message.match(/database .* does not exist/i) - raise ActiveRecord::NoDatabaseError - else - raise - end + config.reverse_merge!(mode: :dblib) + config[:mode] = config[:mode].to_s.downcase.underscore.to_sym + + ConnectionAdapters::SQLServerAdapter.new( + ConnectionAdapters::SQLServerAdapter.new_client(config), + logger, + nil, + config + ) end end end From dac40d73b3f4fcbd949889d8ac9922fad96415da Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 28 Apr 2021 07:35:09 -0300 Subject: [PATCH 0973/1412] Rails 6.1: Raise `ActiveRecord::ConnectionNotEstablished` on calls to execute with a disconnected connection. (#903) * raise ActiveRecord::ConnectionNotEstablished instead of ActiveRecord::StatementInvalid when call execute and connection is disconnected * add test of other calls to connection.execute that should fail * add changelog item * Refactor * fix typo * check connection active to prevent usage of dead/closed connection * raise TinyTds::Error and translate it. 'active?' method is rescuing just those * Revert "check connection active to prevent usage of dead/closed connection" This reverts commit 686176340cea77337ebb2e6579a752bfc3344ef8. * check TinyTds FalseClass response in execute_procedure and raw_connection_run * raise ConnectionNotEstabilished when TinyTDS returns false Co-authored-by: Aidan Haran --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 27 ++++++++----- .../connection_adapters/sqlserver_adapter.rb | 2 + test/cases/disconnected_test_sqlserver.rb | 39 +++++++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 test/cases/disconnected_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 35f13574f..83545c3ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ - [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column - [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes - [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests +- [#903](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/903) Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 194b0acd9..fbab539be 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -167,7 +167,7 @@ def execute_procedure(proc_name, *variables) log(sql, name) do case @connection_options[:mode] when :dblib - result = @connection.execute(sql) + result = ensure_established_connection! { dblib_execute(sql) } options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc } result.each(options) do |row| r = row.with_indifferent_access @@ -357,13 +357,7 @@ def sp_executesql_sql(sql, types, params, name) def raw_connection_do(sql) case @connection_options[:mode] when :dblib - result = @connection.execute(sql) - - # TinyTDS returns false instead of raising an exception if connection fails. - # Getting around this by raising an exception ourselves while this PR - # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. - raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) - + result = ensure_established_connection! { dblib_execute(sql) } result.do end ensure @@ -428,7 +422,7 @@ def _raw_select(sql, options = {}) def raw_connection_run(sql) case @connection_options[:mode] when :dblib - @connection.execute(sql) + ensure_established_connection! { dblib_execute(sql) } end end @@ -462,6 +456,21 @@ def finish_statement_handle(handle) end handle end + + def dblib_execute(sql) + @connection.execute(sql).tap do |result| + # TinyTDS returns false instead of raising an exception if connection fails. + # Getting around this by raising an exception ourselves while this PR + # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. + raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) + end + end + + def ensure_established_connection! + raise TinyTds::Error, 'SQL Server client is not connected' unless @connection + + yield + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 5a20ae1ff..4ca6275b0 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -443,6 +443,8 @@ def initialize_type_map(m = type_map) def translate_exception(e, message:, sql:, binds:) case message + when /(SQL Server client is not connected)|(failed to execute statement)/i + ConnectionNotEstablished.new(message) when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i RecordNotUnique.new(message, sql: sql, binds: binds) when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb new file mode 100644 index 000000000..d7ccd33f1 --- /dev/null +++ b/test/cases/disconnected_test_sqlserver.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TestDisconnectedAdapter < ActiveRecord::TestCase + self.use_transactional_tests = false + + def setup + @connection = ActiveRecord::Base.connection + end + + teardown do + return if in_memory_db? + db_config = ActiveRecord::Base.connection_db_config + ActiveRecord::Base.establish_connection(db_config) + end + + test "can't execute procedures while disconnected" do + @connection.execute_procedure :sp_tables, "sst_datatypes" + @connection.disconnect! + assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do + @connection.execute_procedure :sp_tables, "sst_datatypes" + end + end + + test "can't execute query while disconnected" do + sql = "SELECT count(*) from products WHERE id IN(@0, @1)" + binds = [ + ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new), + ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new) + ] + + @connection.exec_query sql, "TEST", binds + @connection.disconnect! + assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do + @connection.exec_query sql, "TEST", binds + end + end +end From 36cf7ce6621b71e6a2aaa8856aa5e109e6b8b18b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 28 Apr 2021 11:59:41 +0100 Subject: [PATCH 0974/1412] Load schema and call original test to prevent randomly failing test (#919) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e6cc9e24e..9e0e660f8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -97,6 +97,13 @@ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called module ActiveRecord class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes + def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced + Subscriber.send(:load_schema!) + original_test_errors_when_an_insert_query_is_called_while_preventing_writes + end + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced From 1cc444b551b54b98c3e754718fca34ab9d8b6e5b Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 28 Apr 2021 09:47:38 -0300 Subject: [PATCH 0975/1412] Rails 6.1: change schema dumper condition to only dump explicit default for non identity integers (#902) * coerse test to change binding syntax to @N * add default option to pk dump * Revert "coerse test to change binding syntax to @N" This reverts commit 63eddd4beb44422c869b5311220e3859e0111727. * remove test coercion * set primary_key_nonclustered type to bigint seems this was missing when primary_key changed from integer to bigint (https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/commit/620baf2be175cfdb5333aa11c69f8efafb65fff6#diff-86264ef1995b0800604b26d64ede427e8a3ed59347993bfb409c596e83f4b86f) * remove redundat set of primary_key option. AbstractAdapter already does this Absctract SchemaDefinition sets primary_key option (https://github.com/rails/rails/blob/v6.1.0/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb#L234). There is no point in doing this * remove unnecesary primary check. default_primary_key is called on columns that return true to is_primary? * rewrite 'explicit_primary_key_default?'. String primary keys should not have explicit default * specs about primary keys * fix typo --- .../sqlserver/schema_dumper.rb | 4 +- .../sqlserver/schema_statements.rb | 2 +- .../sqlserver/table_definition.rb | 1 - test/cases/primary_keys_test_sqlserver.rb | 103 ++++++++++++++++++ 4 files changed, 106 insertions(+), 4 deletions(-) create mode 100644 test/cases/primary_keys_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 990191539..9da6eef6f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -15,7 +15,7 @@ class SchemaDumper < ConnectionAdapters::SchemaDumper private def explicit_primary_key_default?(column) - column.is_primary? && !column.is_identity? + column.type == :integer && !column.is_identity? end def schema_limit(column) @@ -37,7 +37,7 @@ def schema_collation(column) end def default_primary_key?(column) - super && column.is_primary? && column.is_identity? + super && column.is_identity? end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index dadca051d..641a09624 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -332,7 +332,7 @@ def quoted_scope(name = nil, type: nil) def initialize_native_database_types { primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY", - primary_key_nonclustered: "int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED", + primary_key_nonclustered: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED", integer: { name: "int", limit: 4 }, bigint: { name: "bigint" }, boolean: { name: "bit" }, diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index cae8903c5..6a0faa33a 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -9,7 +9,6 @@ def primary_key(name, type = :primary_key, **options) options[:is_identity] = true unless options.key?(:default) elsif type == :uuid options[:default] = options.fetch(:default, "NEWID()") - options[:primary_key] = true end super end diff --git a/test/cases/primary_keys_test_sqlserver.rb b/test/cases/primary_keys_test_sqlserver.rb new file mode 100644 index 000000000..433c17de0 --- /dev/null +++ b/test/cases/primary_keys_test_sqlserver.rb @@ -0,0 +1,103 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "support/schema_dumping_helper" + +class PrimaryKeyUuidTypeTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + self.use_transactional_tests = false + + class Barcode < ActiveRecord::Base + end + + setup do + @connection = ActiveRecord::Base.connection + @connection.create_table(:barcodes, primary_key: "code", id: :uuid, force: true) + end + + teardown do + @connection.drop_table(:barcodes, if_exists: true) + end + + def test_any_type_primary_key + assert_equal "code", Barcode.primary_key + + column = Barcode.column_for_attribute(Barcode.primary_key) + assert_not column.null + assert_equal :uuid, column.type + assert_not_predicate column, :is_identity? + assert_predicate column, :is_primary? + ensure + Barcode.reset_column_information + end + + test "schema dump primary key includes default" do + schema = dump_table_schema "barcodes" + assert_match %r/create_table "barcodes", primary_key: "code", id: :uuid, default: -> { "newid\(\)" }/, schema + end +end + +class PrimaryKeyIntegerTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + self.use_transactional_tests = false + + class Barcode < ActiveRecord::Base + end + + class Widget < ActiveRecord::Base + end + + setup do + @connection = ActiveRecord::Base.connection + end + + teardown do + @connection.drop_table :barcodes, if_exists: true + @connection.drop_table :widgets, if_exists: true + end + + test "integer primary key without default" do + @connection.create_table(:widgets, id: :integer, force: true) + column = @connection.columns(:widgets).find { |c| c.name == "id" } + assert_predicate column, :is_primary? + assert_predicate column, :is_identity? + assert_equal :integer, column.type + assert_not_predicate column, :bigint? + + schema = dump_table_schema "widgets" + assert_match %r/create_table "widgets", id: :integer, force: :cascade do/, schema + end + + test "bigint primary key without default" do + @connection.create_table(:widgets, id: :bigint, force: true) + column = @connection.columns(:widgets).find { |c| c.name == "id" } + assert_predicate column, :is_primary? + assert_predicate column, :is_identity? + assert_equal :integer, column.type + assert_predicate column, :bigint? + + schema = dump_table_schema "widgets" + 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 + @connection.create_table(:barcodes, id: :integer, default: nil, force: true) + @connection.create_table(:widgets, id: :bigint, default: nil, force: true) + + column = @connection.columns(:widgets).find { |c| c.name == "id" } + assert_predicate column, :is_primary? + 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 + + 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 + end +end From 778dc42bcc259b8ceff713f5a5db9ec331f2a368 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 29 Apr 2021 16:54:56 +0100 Subject: [PATCH 0976/1412] Original Rails test fails on Windows CI because the dump file was not being binary read (#922) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9e0e660f8..e2d740786 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1270,6 +1270,16 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query end + + # Original Rails test fails on Windows CI because the dump file was not being binary read. + coerce_tests! :test_marshal_load_legacy_relation + def test_marshal_load_legacy_relation_coerced + path = File.expand_path( + "support/marshal_compatibility_fixtures/legacy_relation.dump", + ARTest::SQLServer.root_activerecord_test + ) + assert_equal 11, Marshal.load(File.binread(path)).size + end end end From 84b35be904cdfd7a005002aac32e0bbb5c3bf7c1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 4 May 2021 10:32:14 +0100 Subject: [PATCH 0977/1412] Skip tests on Windows because they fail on AppVeyor CI due to file permissions issue (#920) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e2d740786..cf41b74fa 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -768,11 +768,6 @@ def test_sqlserver_structure_load end end - class DatabaseTasksDumpSchemaCacheTest < ActiveRecord::TestCase - # Skip this test with /tmp/my_schema_cache.yml path on Windows. - coerce_tests! :test_dump_schema_cache if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ - end - class DatabaseTasksCreateAllTest < ActiveRecord::TestCase # We extend `local_database?` so that common VM IPs can be used. coerce_tests! :test_ignores_remote_databases, :test_warning_for_remote_databases @@ -1534,13 +1529,20 @@ def test_statement_cache_values_differ_coerced 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/ + # Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753 coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk - def test_marshal_dump_and_load_with_gzip_coerced - with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip } - end - def test_marshal_dump_and_load_via_disk_coerced - with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk } + + # Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call. + unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ + def test_marshal_dump_and_load_with_gzip_coerced + with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip } + end + def test_marshal_dump_and_load_via_disk_coerced + with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk } + end end private From 6fd1dcac605ebb5e47bb1869efa487a0c35df6b7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 4 May 2021 10:32:36 +0100 Subject: [PATCH 0978/1412] Coerce migrator tests when DB has no schema migration table on Windows CI (#924) Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cf41b74fa..28728fc41 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1966,3 +1966,13 @@ class BasePreventWritesLegacyTest < ActiveRecord::TestCase end end end + +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/ +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/ +end From ab2b307524d1e05fdebf2bb77c3842c540e4fa57 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 5 May 2021 12:55:02 +0100 Subject: [PATCH 0979/1412] Release v6.1.0.0.rc1 (#925) --- CHANGELOG.md | 6 +----- VERSION | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83545c3ea..04412c400 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v6.1.0.0.rc1 #### Fixed @@ -22,8 +22,4 @@ - [#917](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/917) Refactored to use new_client connection pattern -#### Added - -- ... - Please check [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-0-stable/CHANGELOG.md) for previous changes. diff --git a/VERSION b/VERSION index f5999028a..086a053ce 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.0.beta1 +6.1.0.0.rc1 From 3d2f5deeb70620395b71d1cf8c446e1613bb3cbd Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 4 Jun 2021 13:32:03 +0100 Subject: [PATCH 0980/1412] Release v6.1.0.0 --- CHANGELOG.md | 4 ++++ VERSION | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 04412c400..c1a01f9c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## v6.1.0.0 + +- No changes + ## v6.1.0.0.rc1 #### Fixed diff --git a/VERSION b/VERSION index 086a053ce..fb90a07b1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.0.0.rc1 +6.1.0.0 From 3c89b8e52df7b8af564b7ae62dc2d716931b98eb Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 4 Jun 2021 13:34:29 +0100 Subject: [PATCH 0981/1412] Update README --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3aefe84b9..fbe2f5c9d 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,14 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 6.x version of the adapter is only for the latest 6.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. -| Adapter Version | Rails Version | Support | -| ------------- | --- | --- | -| `6.1.0.0.rc1` | `6.1.x` | [wip](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | +| --------------- | ------------- | ------------------------------------------------------------------------------------------- | +| `6.1.0.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | For older versions, please check their stable branches. From 524b2c026980b0424bfbc6bba32fcfb156640876 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 25 Jul 2021 22:10:22 +0100 Subject: [PATCH 0982/1412] Coerce and reimplement test (#931) Delete records that were created as part of test before adding index back Fix test Co-authored-by: Aidan Haran --- test/cases/coerced_tests.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 28728fc41..4d603513e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1521,6 +1521,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.connection.add_index(:books, [:author_id, :name], unique: true) end end @@ -1748,6 +1749,7 @@ class EnumTest < ActiveRecord::TestCase send(:'original_enums are distinct per class') ensure + Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end @@ -1758,6 +1760,7 @@ class EnumTest < ActiveRecord::TestCase send(:'original_creating new objects with enum scopes') ensure + Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end @@ -1768,6 +1771,7 @@ class EnumTest < ActiveRecord::TestCase send(:'original_enums are inheritable') ensure + Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end @@ -1778,6 +1782,7 @@ class EnumTest < ActiveRecord::TestCase send(:'original_declare multiple enums at a time') ensure + Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end end @@ -1843,6 +1848,14 @@ class LogSubscriberTest < ActiveRecord::TestCase def test_vebose_query_logs_coerced original_test_vebose_query_logs end + + # Bindings logged slightly differently. + coerce_tests! :test_where_in_binds_logging_include_attribute_names + def test_where_in_binds_logging_include_attribute_names_coerced + Developer.where(id: [1, 2, 3, 4, 5]).load + wait + assert_match(%{@0 = 1, @1 = 2, @2 = 3, @3 = 4, @4 = 5 [["id", nil], ["id", nil], ["id", nil], ["id", nil], ["id", nil]]}, @logger.logged(:debug).last) + end end class ActiveRecordSchemaTest < ActiveRecord::TestCase From e294a94cc428ed111e3d8829eac1129d8a8afaf7 Mon Sep 17 00:00:00 2001 From: Matt Brictson Date: Thu, 29 Jul 2021 05:43:34 -0700 Subject: [PATCH 0983/1412] Apply monkey patches for SQL Server connections only (#933) Recent versions of Rails support multiple database connections within the same app. It is possible for these connections to use different adapters. For example, one adapter may use SQL Server, and another uses PostgreSQL. This gem applies some monkey patches to ActiveRecord for SQL Server compatibility. These patches could break other adapters, though, in a multiple-database scenario. This commit modifies the patches so that they are applied only if the connection is SQL Server. If not, the original ActiveRecord implementation (`super`) is used instead. Fixes #929 --- CHANGELOG.md | 14 ++++++++++++++ .../sqlserver/core_ext/attribute_methods.rb | 2 ++ .../sqlserver/core_ext/calculations.rb | 4 ++++ .../sqlserver/core_ext/explain.rb | 2 ++ .../sqlserver/core_ext/finder_methods.rb | 2 ++ .../sqlserver/core_ext/preloader.rb | 2 ++ 6 files changed, 26 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c1a01f9c0..c55e893e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## Unreleased + +#### Fixed + +- [#933](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/933) Conditionally apply SQL Server monkey patches to ActiveRecord so that it is safe to use this gem alongside other database adapters (e.g. PostgreSQL) in a multi-database Rails app + +#### Changed + +- ... + +#### Added + +- ... + ## v6.1.0.0 - No changes 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 1becf129b..af0e6a4e7 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 @@ -10,6 +10,8 @@ module AttributeMethods private def attributes_for_update(attribute_names) + return super unless self.class.connection.adapter_name == "SQLServer" + super.reject do |name| column = self.class.columns_hash[name] column && column.respond_to?(:is_identity?) && column.is_identity? diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index a69058b81..ec20b40ff 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -10,6 +10,8 @@ module CoreExt module Calculations # Same as original except we don't perform PostgreSQL hack that removes ordering. def calculate(operation, column_name) + return super unless klass.connection.adapter_name == "SQLServer" + if has_include?(column_name) relation = apply_join_dependency @@ -29,6 +31,8 @@ def calculate(operation, column_name) private def build_count_subquery(relation, column_name, distinct) + return super unless klass.connection.adapter_name == "SQLServer" + super(relation.unscope(:order), column_name, distinct) 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 752350e4c..a267909f5 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -9,6 +9,8 @@ module Explain SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries) + return super unless connection.adapter_name == "SQLServer" + unprepared_queries = queries.map do |(sql, binds)| [unprepare_sqlserver_statement(sql, binds), binds] end 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 ccfa32620..03932d492 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 @@ -12,6 +12,8 @@ module FinderMethods # Same as original except we order by values in distinct select if present. def construct_relation_for_exists(conditions) + return super unless klass.connection.adapter_name == "SQLServer" + conditions = sanitize_forbidden_attributes(conditions) if distinct_value && offset_value diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index 0171f4c65..9d64c99e8 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -10,6 +10,8 @@ module Preloader private def records_for(ids) + return super unless klass.connection.adapter_name == "SQLServer" + ids.each_slice(in_clause_length).flat_map do |slice| scope.where(association_key_name => slice).load do |record| # Processing only the first owner From ace9588d47fbc9e095f58326e51e897b7810acc0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 29 Jul 2021 14:51:40 +0100 Subject: [PATCH 0984/1412] No longer required for Rails 6.1+ (#930) Removed require No longer required for Rails 6.1+ Removed require Co-authored-by: Aidan Haran --- .../sqlserver/core_ext/query_methods.rb | 28 ------------------- .../connection_adapters/sqlserver_adapter.rb | 1 - 2 files changed, 29 deletions(-) delete mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb deleted file mode 100644 index d29fb8c4d..000000000 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -require "active_record/relation" -require "active_record/version" - -module ActiveRecord - module ConnectionAdapters - module SQLServer - module CoreExt - module QueryMethods - private - - # Copy of original from Rails master. - # This patch can be removed when adapter supports Rails version greater than 6.0.2.2 - def table_name_matches?(from) - table_name = Regexp.escape(table.name) - quoted_table_name = Regexp.escape(connection.quote_table_name(table.name)) - /(?:\A|(? Date: Thu, 29 Jul 2021 12:29:00 -0500 Subject: [PATCH 0985/1412] Include Calculations module instead of prepending (#928) Another gem (groupdate) also patches the #calculate method and prepending this module prevents any other libraries from patching this method. --- .../connection_adapters/sqlserver/core_ext/calculations.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index ec20b40ff..da0e37f80 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -43,5 +43,5 @@ def build_count_subquery(relation, column_name, distinct) ActiveSupport.on_load(:active_record) do mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations - ActiveRecord::Relation.prepend(mod) + ActiveRecord::Relation.include(mod) end From e4d91fc56eeb6f58d93bc6b0aa1364cc51f4fd77 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Fri, 6 Aug 2021 23:16:40 +0100 Subject: [PATCH 0986/1412] Fix schema cache generation (#935) --- CHANGELOG.md | 2 + .../connection_adapters/sqlserver/quoting.rb | 3 +- .../sqlserver/schema_statements.rb | 2 +- .../sqlserver/sql_type_metadata.rb | 19 ++- .../connection_adapters/sqlserver_column.rb | 109 ++++++++++++------ test/cases/adapter_test_sqlserver.rb | 4 +- test/cases/rake_test_sqlserver.rb | 35 ++++++ 7 files changed, 130 insertions(+), 44 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c55e893e2..70db1d3d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ #### Fixed - [#933](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/933) Conditionally apply SQL Server monkey patches to ActiveRecord so that it is safe to use this gem alongside other database adapters (e.g. PostgreSQL) in a multi-database Rails app +- [#935](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/935) Fix schema cache generation + (**breaking change**) #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 09d8226bc..073e5860e 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -17,7 +17,8 @@ def fetch_type_metadata(sql_type, sqlserver_options = {}) precision: cast_type.precision, scale: cast_type.scale ) - SQLServer::TypeMetadata.new(simple_type, sqlserver_options: sqlserver_options) + + SQLServer::TypeMetadata.new(simple_type, **sqlserver_options) end def quote_string(s) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 641a09624..427454fde 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -84,7 +84,7 @@ def columns(table_name) end def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) - SQLServerColumn.new( + SQLServer::Column.new( name, default, sql_type_metadata, 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 ebcacc5cd..72abfd584 100644 --- a/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +++ b/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb @@ -8,24 +8,33 @@ class TypeMetadata < DelegateClass(SqlTypeMetadata) include Deduplicable - attr_reader :sqlserver_options + attr_reader :is_identity, :is_primary, :table_name, :ordinal_position - def initialize(type_metadata, sqlserver_options: nil) + def initialize(type_metadata, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil) super(type_metadata) - @sqlserver_options = sqlserver_options + @is_identity = is_identity + @is_primary = is_primary + @table_name = table_name + @ordinal_position = ordinal_position end def ==(other) other.is_a?(TypeMetadata) && __getobj__ == other.__getobj__ && - sqlserver_options == other.sqlserver_options + is_identity == other.is_identity && + is_primary == other.is_primary && + table_name == other.table_name && + ordinal_position == other.ordinal_position end alias eql? == def hash TypeMetadata.hash ^ __getobj__.hash ^ - sqlserver_options.hash + is_identity.hash ^ + is_primary.hash ^ + table_name.hash ^ + ordinal_position.hash end private diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 8cfe709c3..ffaf60a2a 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -2,48 +2,87 @@ module ActiveRecord module ConnectionAdapters - class SQLServerColumn < Column - def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **sqlserver_options) - @sqlserver_options = sqlserver_options - super - end + module SQLServer + class Column < ConnectionAdapters::Column + delegate :is_identity, :is_primary, :table_name, :ordinal_position, to: :sql_type_metadata - def is_identity? - @sqlserver_options[:is_identity] - end + def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, **) + super + @is_identity = is_identity + @is_primary = is_primary + @table_name = table_name + @ordinal_position = ordinal_position + end - def is_primary? - @sqlserver_options[:is_primary] - end + def is_identity? + is_identity + end - def table_name - @sqlserver_options[:table_name] - end + def is_primary? + is_primary + end - def is_utf8? - sql_type =~ /nvarchar|ntext|nchar/i - end + def is_utf8? + sql_type =~ /nvarchar|ntext|nchar/i + end - def case_sensitive? - collation && collation.match(/_CS/) - end + def case_sensitive? + collation && collation.match(/_CS/) + end - private - - # In the Rails version of this method there is an assumption that the `default` value will always be a - # `String` class, which must be true for the MySQL/PostgreSQL/SQLite adapters. However, in the SQL Server - # adapter the `default` value can also be Boolean/Date/Time/etc. Changed the implementation of this method - # to handle non-String `default` objects. - def deduplicated - @name = -name - @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata - @default = (default.is_a?(String) ? -default : default.dup.freeze) if default - @default_function = -default_function if default_function - @collation = -collation if collation - @comment = -comment if comment - - freeze + def init_with(coder) + @is_identity = coder["is_identity"] + @is_primary = coder["is_primary"] + @table_name = coder["table_name"] + @ordinal_position = coder["ordinal_position"] + super + end + + def encode_with(coder) + coder["is_identity"] = @is_identity + coder["is_primary"] = @is_primary + coder["table_name"] = @table_name + coder["ordinal_position"] = @ordinal_position + super + end + + def ==(other) + other.is_a?(Column) && + super && + is_identity? == other.is_identity? && + is_primary? == other.is_primary? && + table_name == other.table_name && + ordinal_position == other.ordinal_position + end + alias :eql? :== + + def hash + Column.hash ^ + super.hash ^ + is_identity?.hash ^ + is_primary?.hash ^ + table_name.hash ^ + ordinal_position.hash + end + + private + + # In the Rails version of this method there is an assumption that the `default` value will always be a + # `String` class, which must be true for the MySQL/PostgreSQL/SQLite adapters. However, in the SQL Server + # adapter the `default` value can also be Boolean/Date/Time/etc. Changed the implementation of this method + # to handle non-String `default` objects. + def deduplicated + @name = -name + @sql_type_metadata = sql_type_metadata.deduplicate if sql_type_metadata + @default = (default.is_a?(String) ? -default : default.dup.freeze) if default + @default_function = -default_function if default_function + @collation = -collation if collation + @comment = -comment if comment + freeze + end end + + SQLServerColumn = SQLServer::Column end end end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a0ea7faf7..c56954672 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -377,7 +377,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert !SSTestCustomersView.columns.blank? assert_equal columns.size, SSTestCustomersView.columns.size columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + 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}" end @@ -404,7 +404,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert !SSTestStringDefaultsView.columns.blank? assert_equal columns.size, SSTestStringDefaultsView.columns.size columns.each do |colname| - assert_instance_of ActiveRecord::ConnectionAdapters::SQLServerColumn, + 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}" end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 87b363a20..1195713d2 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -156,3 +156,38 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest _(connection.tables).must_include "users" end end + +class SQLServerRakeSchemaCacheDumpLoadTest < SQLServerRakeTest + let(:filename) { File.join ARTest::SQLServer.test_root_sqlserver, "schema_cache.yml" } + let(:filedata) { File.read(filename) } + + before do + quietly { db_tasks.create(configuration) } + + connection.create_table :users, force: true do |t| + t.string :name, null: false + end + end + + after do + FileUtils.rm_rf(filename) + end + + it "dumps schema cache with SQL Server metadata" do + quietly { db_tasks.dump_schema_cache connection, filename } + + schema_cache = YAML.load(File.read(filename)) + + col_id, col_name = connection.schema_cache.columns("users") + + assert col_id.is_identity + assert col_id.is_primary + assert_equal col_id.ordinal_position, 1 + assert_equal col_id.table_name, "users" + + assert_not col_name.is_identity + assert_not col_name.is_primary + assert_equal col_name.ordinal_position, 2 + assert_equal col_name.table_name, "users" + end +end From dec6836fe60cd8328b000aaf728c467c9bf8df65 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Sat, 7 Aug 2021 00:07:13 +0100 Subject: [PATCH 0987/1412] Fix deteministic fetch when table has a composite primary key (#936) Even though Rails does not support tables with primary keys, it still allows those tables to be defined without any problems. The SQL Server adapter will always try to retrieve records in a deterministic way but it currently fails if the table has a composite primary key, which shouldn't happen. This PR fixes how the adapter determines which column (generally the PK) is used as the default order when multiple primary keys are detected. It first checks if one of the primary keys is also an identity column. If there's an identity column, it will choose it as the "main primary key", otherwise it will just fallback to the first of the primary keys. --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 17 +++++++++++++++-- test/cases/fetch_test_sqlserver.rb | 18 ++++++++++++++++++ test/models/sqlserver/composite_pk.rb | 9 +++++++++ test/schema/sqlserver_specific_schema.rb | 18 ++++++++++++++++++ 5 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 test/models/sqlserver/composite_pk.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 70db1d3d6..1d8578392 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - [#933](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/933) Conditionally apply SQL Server monkey patches to ActiveRecord so that it is safe to use this gem alongside other database adapters (e.g. PostgreSQL) in a multi-database Rails app - [#935](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/935) Fix schema cache generation (**breaking change**) +- [#936](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/936) Fix deteministic fetch when table has a composite primary key #### Changed diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f0dea89a0..618d3a659 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -296,8 +296,21 @@ def table_From_Statement(o) def primary_Key_From_Table(t) return unless t - column_name = @connection.schema_cache.primary_keys(t.name) || - @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) + primary_keys = @connection.schema_cache.primary_keys(t.name) + column_name = nil + + case primary_keys + when NilClass + column_name = @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name) + when String + column_name = primary_keys + when Array + candidate_columns = @connection.schema_cache.columns_hash(t.name).slice(*primary_keys).values + candidate_column = candidate_columns.find(&:is_identity?) + candidate_column ||= candidate_columns.first + column_name = candidate_column.try(:name) + end + column_name ? t[column_name] : nil end diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 266052ce7..6a55dec7d 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -49,3 +49,21 @@ def create_10_books @books = (1..10).map { |i| Book.create! name: "Name-#{i}" } end end + +class DeterministicFetchWithCompositePkTestSQLServer < ActiveRecord::TestCase + it "orders by the identity column if table has one" do + SSCompositePkWithIdentity.delete_all + SSCompositePkWithIdentity.create(pk_col_two: 2) + SSCompositePkWithIdentity.create(pk_col_two: 1) + + _(SSCompositePkWithIdentity.take(1).map(&:pk_col_two)).must_equal [2] + end + + it "orders by the first column if table has no identity column" do + SSCompositePkWithoutIdentity.delete_all + SSCompositePkWithoutIdentity.create(pk_col_one: 2, pk_col_two: 2) + SSCompositePkWithoutIdentity.create(pk_col_one: 1, pk_col_two: 1) + + _(SSCompositePkWithoutIdentity.take(1).map(&:pk_col_two)).must_equal [1] + end +end diff --git a/test/models/sqlserver/composite_pk.rb b/test/models/sqlserver/composite_pk.rb new file mode 100644 index 000000000..180c8ac28 --- /dev/null +++ b/test/models/sqlserver/composite_pk.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class SSCompositePkWithoutIdentity < ActiveRecord::Base + self.table_name = :sst_composite_without_identity +end + +class SSCompositePkWithIdentity < ActiveRecord::Base + self.table_name = :sst_composite_with_identity +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 3486c7942..3ef44fac8 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -294,4 +294,22 @@ CONSTRAINT PK_UNIQUE_KEY PRIMARY KEY (id) ); SQLSERVERUNIQUEKEYS + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_without_identity') DROP TABLE sst_composite_without_identity" + execute <<-COMPOSITE_WITHOUT_IDENTITY + CREATE TABLE sst_composite_without_identity ( + pk_col_one int NOT NULL, + pk_col_two int NOT NULL, + CONSTRAINT PK_sst_composite_without_identity PRIMARY KEY (pk_col_one, pk_col_two) + ); + COMPOSITE_WITHOUT_IDENTITY + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_with_identity') DROP TABLE sst_composite_with_identity" + execute <<-COMPOSITE_WITH_IDENTITY + CREATE TABLE sst_composite_with_identity ( + pk_col_one int IDENTITY NOT NULL, + pk_col_two int NOT NULL, + CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two) + ); + COMPOSITE_WITH_IDENTITY end From c9a6a875763d0e1268d77b5fc136a54fba267be3 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Aug 2021 14:34:47 +0100 Subject: [PATCH 0988/1412] Fix date columns serialization for range values (#938) --- CHANGELOG.md | 1 + lib/active_record/connection_adapters/sqlserver/type/date.rb | 3 ++- test/cases/column_test_sqlserver.rb | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d8578392..5f9178af6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ - [#935](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/935) Fix schema cache generation (**breaking change**) - [#936](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/936) Fix deteministic fetch when table has a composite primary key +- [#938](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/938) Fix date columns serialization for range values #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index 281fd2b0b..dd335aa53 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -10,7 +10,8 @@ def sqlserver_type end def serialize(value) - return unless value.present? + value = super + return value unless value.acts_like?(:date) date = super(value).to_s(:_sqlserver_dateformat) Data.new date, self diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 532f65837..8636da763 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -299,6 +299,8 @@ def assert_obj_set_and_save(attribute, value) _(obj.date).must_equal Date.civil(0001, 4, 1) obj.reload _(obj.date).must_equal Date.civil(0001, 4, 1) + # Can filter by date range + _(obj).must_equal obj.class.where(date: obj.date..Date::Infinity.new).first # Can keep and return assigned date. assert_obj_set_and_save :date, Date.civil(1972, 04, 14) # Can accept and cast time objects. @@ -333,6 +335,8 @@ def assert_obj_set_and_save(attribute, value) obj.reload _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" _(obj).must_equal obj.class.where(datetime: time).first + # Can filter by datetime range + _(obj).must_equal obj.class.where(datetime: time..DateTime::Infinity.new).first # Will cast to true DB value on attribute write, save and return again. time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 From 6276e1b0f72561d1bec385d7aa712ffac5128458 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Aug 2021 14:48:08 +0100 Subject: [PATCH 0989/1412] Release v6.1.1.0 --- CHANGELOG.md | 14 +++++--------- VERSION | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f9178af6..5eb37409d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## Unreleased +## v6.1.1.0 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.0.0...v6.1.1.0) #### Fixed @@ -8,20 +10,14 @@ - [#936](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/936) Fix deteministic fetch when table has a composite primary key - [#938](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/938) Fix date columns serialization for range values -#### Changed - -- ... - -#### Added - -- ... - ## v6.1.0.0 - No changes ## v6.1.0.0.rc1 +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-0-stable...v6.1.0.0.rc1) + #### Fixed - [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with diff --git a/VERSION b/VERSION index fb90a07b1..5bde3316e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.0.0 +6.1.1.0 From 4af32b046aea6c1faa360df76487f35d1b718ceb Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 11 Aug 2021 14:49:00 +0100 Subject: [PATCH 0990/1412] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fbe2f5c9d..9c6727c88 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `6.1.0.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.1.1.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | From b7e52f72f27ed60e5ed18e4a41ec99214505b264 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 31 Aug 2021 10:11:12 +0100 Subject: [PATCH 0991/1412] Primary key violation should result in RecordNotUnique error (#940) * Primary key violation should result in ActiveRecord::RecordNotUnique error * Updated changelog Co-authored-by: Aidan Haran --- CHANGELOG.md | 14 ++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 8 ++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5eb37409d..d249696ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +#### Unreleased + +#### Fixed + +- [#940](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/940) Primary key violation should result in RecordNotUnique error + +#### Changed + +- ... + +#### Added + +- ... + ## v6.1.1.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.0.0...v6.1.1.0) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c7d30083d..adc5f0c0b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -444,7 +444,7 @@ def translate_exception(e, message:, sql:, binds:) case message when /(SQL Server client is not connected)|(failed to execute statement)/i ConnectionNotEstablished.new(message) - when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i + when /(cannot insert duplicate key .* with unique index) | (violation of (unique|primary) key constraint)/i RecordNotUnique.new(message, sql: sql, binds: binds) when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i InvalidForeignKey.new(message, sql: sql, binds: binds) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index c56954672..91337243b 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -120,6 +120,14 @@ class AdapterTestSQLServer < ActiveRecord::TestCase "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') + + assert_raise ActiveRecord::RecordNotUnique do + Post.create!(id: 0, title: 'Test', body: 'Try to create another post with primary key of zero') + end + end + describe "with different language" do before do @default_language = connection.user_options_language From ef4c1d3213820116b6a93665ec4acbe36e59da60 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 2 Sep 2021 17:41:57 +0100 Subject: [PATCH 0992/1412] Fix application name sent to TinyTDS (#941) * Fix application name * Updated changelog * No longer support configuring the application name by overriding the 'configure_application_name' method. Co-authored-by: Aidan Haran --- CHANGELOG.md | 2 +- README.md | 22 ++++++++++++------- .../connection_adapters/sqlserver_adapter.rb | 20 ++++++++++++++--- 3 files changed, 32 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d249696ac..35da98a87 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ #### Changed -- ... +- [#941](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/941) No longer support configuring the application name by overriding the 'configure_application_name' method. #### Added diff --git a/README.md b/README.md index 9c6727c88..9ac67f0b7 100644 --- a/README.md +++ b/README.md @@ -82,28 +82,34 @@ end ``` -#### Configure Connection & App Name +#### Configure Connection -We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just override the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. Also, TinyTDS supports an application name when it logs into SQL Server. This can be used to identify the connection in SQL Server's activity monitor. By default it will use the `appname` from your database.yml file or a lowercased version of your Rails::Application name. It is now possible to define a `configure_application_name` method that can give you per instance details. Below shows how you might use this to get the process id and thread id of the current connection. +We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just override the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. ```ruby module ActiveRecord module ConnectionAdapters class SQLServerAdapter < AbstractAdapter - def configure_connection raw_connection_do "SET TEXTSIZE #{64.megabytes}" end - - def configure_application_name - "myapp_#{$$}_#{Thread.current.object_id}".to(29) - end - end end end ``` +#### Configure Application Name + +TinyTDS supports an application name when it logs into SQL Server. This can be used to identify the connection in SQL Server's activity monitor. By default it will use the `appname` from your database.yml file or your Rails::Application name. + +Below shows how you might use the database.yml file to use the process ID in your application name. + +```yaml +development: + adapter: sqlserver + appname: <%= myapp_#{Process.pid} %> +``` + #### Executing Stored Procedures Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index adc5f0c0b..22b420b8c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -105,7 +105,23 @@ def dblib_connect(config) end def config_appname(config) - config[:appname] || configure_application_name || Rails.application.class.name.split("::").first rescue nil + if self.instance_methods.include?(:configure_application_name) + ActiveSupport::Deprecation.warn <<~MSG.squish + Configuring the application name used by TinyTDS by overriding the + `ActiveRecord::ConnectionAdapters::SQLServerAdapter#configure_application_name` + instance method is no longer supported. The application name should configured + using the `appname` setting in the `database.yml` file instead. Consult the + README for further information." + MSG + end + + config[:appname] || rails_application_name + end + + def rails_application_name + return nil if Rails.application.nil? + + Rails.application.class.name.split("::").first end def config_login_timeout(config) @@ -483,8 +499,6 @@ def connection_errors end end - def configure_application_name; end - def initialize_dateformatter @database_dateformat = user_options_dateformat a, b, c = @database_dateformat.each_char.to_a From 89f90b697507197f3f3e5469ef368ee4aa2767d6 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 6 Sep 2021 10:42:51 +0100 Subject: [PATCH 0993/1412] Prepare v6.1.2.0 --- CHANGELOG.md | 8 +++----- README.md | 4 ++-- VERSION | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35da98a87..5948c1b12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -#### Unreleased +## v6.1.2.0 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.1.0...v6.1.2.0) #### Fixed @@ -8,10 +10,6 @@ - [#941](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/941) No longer support configuring the application name by overriding the 'configure_application_name' method. -#### Added - -- ... - ## v6.1.1.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.0.0...v6.1.1.0) diff --git a/README.md b/README.md index 9ac67f0b7..f2993a697 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `6.1.1.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.1.2.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | @@ -115,7 +115,7 @@ development: Every class that sub classes ActiveRecord::Base will now have an execute_procedure class method to use. This method takes the name of the stored procedure which can be a string or symbol and any number of variables to pass to the procedure. Arguments will automatically be quoted per the connection's standards as normal. For example: ```ruby -Account.execute_procedure(:update_totals, 'admin', nil, true +Account.execute_procedure(:update_totals, 'admin', nil, true) # Or with named parameters. Account.execute_procedure(:update_totals, named: 'params') ``` diff --git a/VERSION b/VERSION index 5bde3316e..08d027dd0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.1.0 +6.1.2.0 From 434514a4cf6b4cf1be17bea724f11507c44f89f6 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 8 Sep 2021 13:56:56 +0100 Subject: [PATCH 0994/1412] Fix appname resolution when outside Rails context (#943) The adapter can be used outside a Rails context so TinyTDS appname config should be able to be resolved if a Rails application was not detected. Fix #942. --- CHANGELOG.md | 6 ++++++ lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5948c1b12..e09fbd004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#943](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/943) Fix appname resolution when outside Rails context + ## v6.1.2.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.1.0...v6.1.2.0) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 22b420b8c..4ce55cb96 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -119,9 +119,9 @@ def config_appname(config) end def rails_application_name - return nil if Rails.application.nil? - Rails.application.class.name.split("::").first + rescue + nil # Might not be in a Rails context so we fallback to `nil`. end def config_login_timeout(config) From 3a62f1252b599daa23e9e3cd571afdc1bc7c1f05 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Wed, 8 Sep 2021 13:59:32 +0100 Subject: [PATCH 0995/1412] Prepare v6.1.2.1 --- CHANGELOG.md | 4 +++- README.md | 2 +- VERSION | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e09fbd004..9e170796c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## Unreleased +## v6.1.2.1 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.2.0...v6.1.2.1) #### Fixed diff --git a/README.md b/README.md index f2993a697..caf382c30 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `6.1.2.0` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | diff --git a/VERSION b/VERSION index 08d027dd0..93165af1f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.2.0 +6.1.2.1 From 257312e35d5e67f39a2a8142af64960962494635 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Oct 2021 10:25:16 +0100 Subject: [PATCH 0996/1412] Refactored method to get the table name from raw SQL (#951) Co-authored-by: Aidan Haran --- .../sqlserver/schema_statements.rb | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 427454fde..9fd162a5f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -531,17 +531,22 @@ def remove_indexes(table_name, column_name) # === SQLServer Specific (Misc Helpers) ========================= # + # Parses just the table name from the SQL. Table name does not include database/schema/etc. def get_table_name(sql) - tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i - Regexp.last_match[3] || Regexp.last_match[4] - elsif sql =~ /FROM\s+([^\(\s]+)\s*/i - Regexp.last_match[1] - else - nil - end + tn = get_raw_table_name(sql) SQLServer::Utils.extract_identifiers(tn).object end + # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc. + def get_raw_table_name(sql) + case sql + when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i + Regexp.last_match[3] || Regexp.last_match[4] + when /FROM\s+([^\(\s]+)\s*/i + Regexp.last_match[1] + end + end + def default_constraint_name(table_name, column_name) "DF_#{table_name}_#{column_name}" end From dfe9363e0fac23783fcfc423f6ebbec14fe32554 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Oct 2021 10:25:39 +0100 Subject: [PATCH 0997/1412] New Rails application instructions (#945) New Rails application instructions Co-authored-by: Aidan Haran --- README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index caf382c30..404066023 100644 --- a/README.md +++ b/README.md @@ -152,16 +152,25 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. +## New Rails Applications + +When creating a new Rails application you can specify that you want to use the SQL Server adapter using the `database` option: + +``` +rails new my_app --database=sqlserver +``` + +To then connect the application to your SQL Server instance edit the `config/database.yml` file with the username, password and host of your SQL Server instance. + + ## Installation The adapter has no strict gem dependencies outside of `ActiveRecord`. You will have to pick a connection mode, the default is dblib which uses the `TinyTDS` gem. Just bundle the gem and the adapter will use it. ```ruby -gem 'tiny_tds' gem 'activerecord-sqlserver-adapter' ``` - ## Contributing If you would like to contribute a feature or bugfix, thanks! To make sure your fix/feature has a high chance of being added, please read the following guidelines. First, ask on the Gitter, or post a ticket on github issues. Second, make sure there are tests! We will not accept any patch that is not tested. Please read the [`RUNNING_UNIT_TESTS`](RUNNING_UNIT_TESTS.md) file for the details of how to run the unit tests. From 80e78d85f650dcdd5fbd4f892b66904e87417430 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 18 Oct 2021 13:54:35 +0100 Subject: [PATCH 0998/1412] Added utility method to get level of qualification of table name (#952) Co-authored-by: Aidan Haran --- .../connection_adapters/sqlserver/utils.rb | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 669c25d73..186593e65 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -46,7 +46,22 @@ def fully_qualified_database_quoted end def fully_qualified? - parts.compact.size == 4 + qualified_level == :fully + end + + def qualified_level + case parts.compact.size + when 4 + :fully + when 3 + :database + when 2 + :schema + when 1 + :table + else + :none + end end def to_s From 712e549e6adfa072e370d5403fb8889891ccd3ad Mon Sep 17 00:00:00 2001 From: Josua Schmid Date: Fri, 12 Nov 2021 11:37:02 +0100 Subject: [PATCH 0999/1412] Fix appname in example config (#954) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 404066023..88263931c 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Below shows how you might use the database.yml file to use the process ID in you ```yaml development: adapter: sqlserver - appname: <%= myapp_#{Process.pid} %> + appname: <%= "myapp_#{Process.pid}" %> ``` #### Executing Stored Procedures From 65b29cc9b58e0ae8125482a8a1d9f81e8f87c608 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 00:06:46 -0300 Subject: [PATCH 1000/1412] Start Rails 7.0 support --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 60 +++----------------------- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 4 +- appveyor.yml | 10 ++--- 5 files changed, 14 insertions(+), 64 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c08390700..3188b726c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.5.9, 2.6.7, 2.7.3, 3.0.1] + ruby: [2.7.3, 3.0.1] steps: - name: Checkout code diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e170796c..e27d1659c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,63 +1,15 @@ -## v6.1.2.1 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.2.0...v6.1.2.1) - -#### Fixed - -- [#943](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/943) Fix appname resolution when outside Rails context - -## v6.1.2.0 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.1.0...v6.1.2.0) +## Unreleased #### Fixed -- [#940](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/940) Primary key violation should result in RecordNotUnique error +... #### Changed -- [#941](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/941) No longer support configuring the application name by overriding the 'configure_application_name' method. - -## v6.1.1.0 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v6.1.0.0...v6.1.1.0) - -#### Fixed +... -- [#933](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/933) Conditionally apply SQL Server monkey patches to ActiveRecord so that it is safe to use this gem alongside other database adapters (e.g. PostgreSQL) in a multi-database Rails app -- [#935](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/935) Fix schema cache generation - (**breaking change**) -- [#936](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/936) Fix deteministic fetch when table has a composite primary key -- [#938](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/938) Fix date columns serialization for range values - -## v6.1.0.0 - -- No changes - -## v6.1.0.0.rc1 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-0-stable...v6.1.0.0.rc1) - -#### Fixed - -- [#872](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/872) Use native String#start_with -- [#876](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/876) Use native String#end_with -- [#873](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/873) Various fixes to get the tests running for Rails 6.1 -- [#874](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/874) Deduplicate schema cache structures -- [#875](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/875) Handle default boolean column values when deduplicating -- [#879](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/879) Added visit method for HomogeneousIn -- [#880](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/880) Handle any default column class when deduplicating -- [#861](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/861) Fix Rails 6.1 database config -- [#890](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/890) Fix removal of invalid ordering from select statements -- [#881](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/881) Dump column collation to schema.rb and allow collation changes using column_change -- [#891](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/891) Add support for if_not_exists to indexes -- [#892](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/892) Add support for if_exists on remove_column -- [#883](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/885) Fix quoting of ActiveRecord::Relation::QueryAttribute and ActiveModel::Attributes -- [#893](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/893) Add Active Record Marshal forward compatibility tests -- [#903](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/903) Raise ActiveRecord::ConnectionNotEstablished on calls to execute with a disconnected connection - -#### Changed +#### Added -- [#917](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/917) Refactored to use new_client connection pattern +... -Please check [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-0-stable/CHANGELOG.md) for previous changes. +Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. diff --git a/VERSION b/VERSION index 93165af1f..607cd5943 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.1.2.1 +7.0.0.0-beta.1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 5a875edcc..8bd93e464 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.version = version - spec.required_ruby_version = ">= 2.5.0" + spec.required_ruby_version = ">= 2.7.0" spec.license = "MIT" spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward"] @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 6.1.0" + spec.add_dependency "activerecord", "~> 7.0.0" spec.add_dependency "tiny_tds" end diff --git a/appveyor.yml b/appveyor.yml index 7b4d943de..da7123a06 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,10 +5,10 @@ build: off matrix: fast_finish: true allow_failures: - - ruby_version: "25" - - ruby_version: "26" - ruby_version: "27" - ruby_version: "27-x64" + - ruby_version: "30" + - ruby_version: "30-x64" services: - mssql2014 @@ -38,9 +38,7 @@ environment: CI_AZURE_PASS: secure: cSQp8sk4urJYvq0utpsK+r7J+snJ2wpcdp8RdXJfB+w= matrix: - - ruby_version: "25-x64" - - ruby_version: "25" - - ruby_version: "26-x64" - - ruby_version: "26" - ruby_version: "27-x64" - ruby_version: "27" + - ruby_version: "30-x64" + - ruby_version: "30" From 6863f82334c2256b93882af7f1f4b42e04c27a9f Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 00:51:57 -0300 Subject: [PATCH 1001/1412] Remove deprecation warning DEPRECATION WARNING: ActiveRecord::Base.default_timezone is deprecated and will be removed in Rails 7.1. Use `ActiveRecord.default_timezone` instead. --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fbab539be..cfd2deef6 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -168,7 +168,7 @@ def execute_procedure(proc_name, *variables) case @connection_options[:mode] when :dblib result = ensure_established_connection! { dblib_execute(sql) } - options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc } + options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc } result.each(options) do |row| r = row.with_indifferent_access yield(r) if block_given? @@ -441,7 +441,7 @@ def handle_to_names_and_values(handle, options = {}) def handle_to_names_and_values_dblib(handle, options = {}) query_options = {}.tap do |qo| - qo[:timezone] = ActiveRecord::Base.default_timezone || :utc + qo[:timezone] = ActiveRecord.default_timezone || :utc qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash end results = handle.each(query_options) From a82706dd23ac0e9a1b43690f80e36f008a9b822b Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 01:36:22 -0300 Subject: [PATCH 1002/1412] inline type_cast and quote methods Following what was done [here](https://github.com/rails/rails/commit/c5bf2b4736f2ddafbc477af5b9478dd7143e5466) and [here](https://github.com/rails/rails/commit/627406f6b6add2bf9a2a004889a60539dd8fe6eb) --- lib/active_record/connection_adapters/sqlserver/quoting.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 073e5860e..f9c094aba 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -109,9 +109,7 @@ def column_name_with_order_matcher private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER - private - - def _quote(value) + def quote(value) case value when Type::Binary::Data "0x#{value.hex}" @@ -124,7 +122,7 @@ def _quote(value) end end - def _type_cast(value) + def type_cast(value) case value when ActiveRecord::Type::SQLServer::Data value.to_s From b266eea340cdf8c30ac0ea1b774e1424d2c7b5ff Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 01:41:44 -0300 Subject: [PATCH 1003/1412] add benchmark-ips gem. Require by encryption testcase dependency introduced [here](https://github.com/rails/rails/commit/638a92f7344963c7e42eeb84eb816e4592bda7e4) --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index e81c9033f..87cba16a1 100644 --- a/Gemfile +++ b/Gemfile @@ -10,6 +10,7 @@ gem "bcrypt" gem "pg", ">= 0.18.0" gem "sqlite3", "~> 1.4" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem "benchmark-ips" if ENV["RAILS_SOURCE"] gemspec path: ENV["RAILS_SOURCE"] From bdafb028f936a5370dc669fb1459c625f8597629 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 01:45:39 -0300 Subject: [PATCH 1004/1412] Remove deprecation warning DEPRECATION WARNING: Time#to_s(:_sqlserver_datetime) is deprecated. Please use Time#to_formatted_s(:_sqlserver_datetime) instead. DEPRECATION WARNING: Date#to_s(:_sqlserver_dateformat) is deprecated. Please use Date#to_formatted_s(:_sqlserver_dateformat) instead. --- lib/active_record/connection_adapters/sqlserver/type/date.rb | 2 +- .../connection_adapters/sqlserver/type/datetime.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/date.rb b/lib/active_record/connection_adapters/sqlserver/type/date.rb index dd335aa53..f16eb77d5 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/date.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/date.rb @@ -13,7 +13,7 @@ def serialize(value) value = super return value unless value.acts_like?(:date) - date = super(value).to_s(:_sqlserver_dateformat) + date = super(value).to_formatted_s(:_sqlserver_dateformat) Data.new date, self end diff --git a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb index eedabb94c..9c05b3f8d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/datetime.rb @@ -15,7 +15,7 @@ def serialize(value) value = super return value unless value.acts_like?(:time) - datetime = "#{value.to_s(:_sqlserver_datetime)}.#{quote_fractional(value)}" + datetime = "#{value.to_formatted_s(:_sqlserver_datetime)}.#{quote_fractional(value)}" Data.new datetime, self end From d2af67b43ce193e0c0d6ecb0824a498bdb9c27c3 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 08:58:13 -0300 Subject: [PATCH 1005/1412] update readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 88263931c..3e68e1e1d 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,12 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 6.x version of the adapter is only for the latest 6.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.0.0` | `7.0.x` | [unreleased](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | @@ -66,7 +67,7 @@ ActiveRecord::Base.table_name_prefix = 'dbo.' It's also possible to create/change/drop a schema in the migration file as in the example below: ```ruby -class CreateFooSchema < ActiveRecord::Migration[6.0] +class CreateFooSchema < ActiveRecord::Migration[7.0] def up create_schema('foo') @@ -188,4 +189,3 @@ You can see an up-to-date list of contributors here: http://github.com/rails-sql ## License Copyright © 2008-2020. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file. - From 495008692ccc1349e3715a59570bb949f2a04da3 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 13:59:34 -0300 Subject: [PATCH 1006/1412] bump ruby to latest patch --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3188b726c..9bd7e244f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.7.3, 3.0.1] + ruby: [2.7.5, 3.0.3] steps: - name: Checkout code From ad80b7ca04f1dd163879b746e7c2c3d44a1cafc6 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 22 Dec 2021 14:28:42 -0300 Subject: [PATCH 1007/1412] Revert "bump ruby to latest patch" This reverts commit 495008692ccc1349e3715a59570bb949f2a04da3. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bd7e244f..3188b726c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.7.5, 3.0.3] + ruby: [2.7.3, 3.0.1] steps: - name: Checkout code From 8ec8f6ad0cbe8873708c7a6169f2ad98e0d61641 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Dec 2021 14:42:18 +0000 Subject: [PATCH 1008/1412] Added support for quoted_time --- lib/active_record/connection_adapters/sqlserver/type/data.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 3cfaf6ddf..f3bf6bab6 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -7,6 +7,8 @@ module Type class Data attr_reader :value, :type + delegate :sub, to: :value + def initialize(value, type) @value, @type = value, type end From a659f19dfa51f13e90d20fe514f28b81c93f9a7d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Dec 2021 15:44:07 +0000 Subject: [PATCH 1009/1412] Cleanup unfound coerced tests --- test/cases/coerced_tests.rb | 39 ++----------------------------------- 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4d603513e..ed721f42d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -52,7 +52,6 @@ module ActiveRecord class AdapterTest < ActiveRecord::TestCase # Legacy binds are not supported. coerce_tests! :test_select_all_insert_update_delete_with_casted_binds - coerce_tests! :test_select_all_insert_update_delete_with_legacy_binds # As far as I can tell, SQL Server does not support null bytes in strings. coerce_tests! :test_update_prepared_statement @@ -535,30 +534,6 @@ def test_create_table_with_defaults_coerce end end -module ActiveRecord - module ConnectionAdapters - class QuoteARBaseTest < ActiveRecord::TestCase - # Use our date format. - coerce_tests! :test_quote_ar_object - def test_quote_ar_object_coerced - value = DatetimePrimaryKey.new(id: @time) - assert_deprecated do - assert_equal "'02-14-2017 12:34:56.79'", @connection.quote(value) - end - end - - # Use our date format. - coerce_tests! :test_type_cast_ar_object - def test_type_cast_ar_object_coerced - value = DatetimePrimaryKey.new(id: @time) - assert_deprecated do - assert_equal "02-14-2017 12:34:56.79", @connection.type_cast(value) - end - end - end - end -end - module ActiveRecord class Migration class ColumnAttributesTest < ActiveRecord::TestCase @@ -1265,16 +1240,6 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query end - - # Original Rails test fails on Windows CI because the dump file was not being binary read. - coerce_tests! :test_marshal_load_legacy_relation - def test_marshal_load_legacy_relation_coerced - path = File.expand_path( - "support/marshal_compatibility_fixtures/legacy_relation.dump", - ARTest::SQLServer.root_activerecord_test - ) - assert_equal 11, Marshal.load(File.binread(path)).size - end end end @@ -1844,8 +1809,8 @@ def test_eager_loading_too_many_ids_coerced class LogSubscriberTest < ActiveRecord::TestCase # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. - coerce_tests! :test_vebose_query_logs - def test_vebose_query_logs_coerced + coerce_tests! :test_verbose_query_logs + def test_verbose_query_logs_coerced original_test_vebose_query_logs end From 10cc67fb0db35fd7437f56cc2ab226a4aba6492e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Dec 2021 16:07:46 +0000 Subject: [PATCH 1010/1412] Coerce foreign key test for SQL Server error message --- test/cases/coerced_tests.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4d603513e..8a4d1607a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -977,6 +977,19 @@ def test_add_on_delete_restrict_foreign_key_coerced @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", on_update: :restrict end end + + # Error message depends on the database adapter. + coerce_tests! :test_add_foreign_key_with_if_not_exists_not_set + def test_add_foreign_key_with_if_not_exists_not_set_coerced + @connection.add_foreign_key :astronauts, :rockets + assert_equal 1, @connection.foreign_keys("astronauts").size + + error = assert_raises do + @connection.add_foreign_key :astronauts, :rockets + end + + assert_match(/TinyTds::Error: There is already an object named '.*' in the database/, error.message) + end end end end From 2bdccc65b97423e0d9b30452b88aee3017101cdd Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 23 Dec 2021 17:06:27 -0300 Subject: [PATCH 1011/1412] add async keyword to exec_query Following https://github.com/rails/rails/commit/7fc174aadaefc2c0a8a6b7c8a7599dd9ca04811f changes --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index cfd2deef6..fd3c0f744 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -26,7 +26,7 @@ def execute(sql, name = nil) end end - def exec_query(sql, name = "SQL", binds = [], prepare: false) + def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) if preventing_writes? && write_query?(sql) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end @@ -34,7 +34,7 @@ def exec_query(sql, name = "SQL", binds = [], prepare: false) materialize_transactions mark_transaction_written_if_write(sql) - sp_executesql(sql, name, binds, prepare: prepare) + sp_executesql(sql, name, binds, prepare: prepare, async: async) end def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil) From b4f0ab2e2cbee7cea55688a5a36ef708b595fe4a Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 23 Dec 2021 17:06:56 -0300 Subject: [PATCH 1012/1412] log async queries --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fd3c0f744..ea92d6325 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -409,7 +409,7 @@ def identity_columns(table_name) # === SQLServer Specific (Selecting) ============================ # def raw_select(sql, name = "SQL", binds = [], options = {}) - log(sql, name, binds) { _raw_select(sql, options) } + log(sql, name, binds, async: options[:async]) { _raw_select(sql, options) } end def _raw_select(sql, options = {}) From e5596232bd7f8622dfe6da453c49347dbeee6176 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 23 Dec 2021 17:41:41 -0300 Subject: [PATCH 1013/1412] remove sql and bindings params from NoDatabaseError initialization Fixes: ``` SQLServerRakeDropTest#test_0002_prints error message when database does not exist: ArgumentError: unknown keywords: :sql, :binds ``` `NoDatabaseError` extends `InvalidStatement` which support :sql and :binds keywords but [this commit](https://github.com/rails/rails/commit/b28711ffa015217e2c24c87152a2f37d8aca1c83) declare initialize method without them. --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 4ce55cb96..40f45e271 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -467,7 +467,7 @@ def translate_exception(e, message:, sql:, binds:) when /has been chosen as the deadlock victim/i DeadlockVictim.new(message, sql: sql, binds: binds) when /database .* does not exist/i - NoDatabaseError.new(message, sql: sql, binds: binds) + NoDatabaseError.new(message) when /data would be truncated/ ValueTooLong.new(message, sql: sql, binds: binds) when /connection timed out/ From ecf5613c29cc573607cabe7dea56ebc6920d72cf Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 27 Dec 2021 10:13:42 -0300 Subject: [PATCH 1014/1412] remove Time#to_s(:_sqlserver_time) is deprecated ``` DEPRECATION WARNING: Time#to_s(:_sqlserver_time) is deprecated. Please use Time#to_formatted_s(:_sqlserver_time) instead. (called from serialize at /activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/time.rb:14) /usr/local/lib/ruby/2.7.0/delegate.rb:83:in `method_missing' /activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/time.rb:14:in `serialize' /activerecord-sqlserver-adapter/lib/active_record/connection_adapters/sqlserver/type/time.rb:24:in `type_cast_for_schema' ``` --- lib/active_record/connection_adapters/sqlserver/type/time.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index ef209641a..bdc8bbc86 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -11,7 +11,7 @@ def serialize(value) value = super return value unless value.acts_like?(:time) - time = "#{value.to_s(:_sqlserver_time)}.#{quote_fractional(value)}" + time = "#{value.to_formatted_s(:_sqlserver_time)}.#{quote_fractional(value)}" Data.new time, self end From 422cb68c7d4eff5ea93985ec2f26d959c1c860d2 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 29 Dec 2021 09:27:59 -0300 Subject: [PATCH 1015/1412] remove unnecesary coercion --- test/cases/coerced_tests.rb | 42 ------------------------------------- 1 file changed, 42 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d4dbd3030..5f2e5b9ea 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1851,48 +1851,6 @@ class ReloadModelsTest < ActiveRecord::TestCase coerce_tests! :test_has_one_with_reload if RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end -require "models/post" -class AnnotateTest < ActiveRecord::TestCase - # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. - # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027) - coerce_tests! :test_annotate_wraps_content_in_an_inline_comment - def test_annotate_wraps_content_in_an_inline_comment_coerced - quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") - - assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do - posts = Post.select(:id).annotate("foo") - assert posts.first - end - end - - # Same as original coerced test except our SQL starts with `EXEC sp_executesql`. - # TODO: Remove coerce after Rails 7 (see https://github.com/rails/rails/pull/42027) - coerce_tests! :test_annotate_is_sanitized - def test_annotate_is_sanitized_coerced - quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts") - - assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do - posts = Post.select(:id).annotate("*/foo/*") - assert posts.first - end - - assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do - posts = Post.select(:id).annotate("**//foo//**") - assert posts.first - end - - assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do - posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar") - assert posts.first - end - - assert_sql(%r{SELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do - posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)") - assert posts.first - end - end -end - class MarshalSerializationTest < ActiveRecord::TestCase private From 71fd229072905ab09403bf479ecf12d30bcaf020 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 30 Dec 2021 23:08:59 -0300 Subject: [PATCH 1016/1412] typo on coerced test original call --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5f2e5b9ea..1ced9320e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1824,7 +1824,7 @@ class LogSubscriberTest < ActiveRecord::TestCase # Call original test from coerced test. Fixes issue on CI with Rails installed as a gem. coerce_tests! :test_verbose_query_logs def test_verbose_query_logs_coerced - original_test_vebose_query_logs + original_test_verbose_query_logs end # Bindings logged slightly differently. From e0b06346567285d2876789744c0b03754c89fa6e Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 00:27:37 -0300 Subject: [PATCH 1017/1412] add missing release version to migration --- .../transaction_table/1_table_will_never_be_created.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/migrations/transaction_table/1_table_will_never_be_created.rb b/test/migrations/transaction_table/1_table_will_never_be_created.rb index 3430cf9ed..c850fae9b 100644 --- a/test/migrations/transaction_table/1_table_will_never_be_created.rb +++ b/test/migrations/transaction_table/1_table_will_never_be_created.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class TableWillNeverBeCreated < ActiveRecord::Migration +class TableWillNeverBeCreated < ActiveRecord::Migration[5.2] def self.up create_table(:sqlserver_trans_table1) {} create_table(:sqlserver_trans_table2) { raise("HELL") } From bd099f634d50609459be139b25b0918e14d6709b Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 00:49:57 -0300 Subject: [PATCH 1018/1412] coerce test to deal with unique index --- test/cases/coerced_tests.rb | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5f2e5b9ea..945423114 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1763,6 +1763,17 @@ class EnumTest < ActiveRecord::TestCase Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! %r{with large number label} + test "with large number label coerced" do + Book.connection.remove_index(:books, column: [:author_id, :name]) + + send(:'original_with large number label') + ensure + Book.where(author_id: nil, name: nil).delete_all + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end end require "models/task" @@ -1925,3 +1936,28 @@ 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/ 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 + def test_in_order_of_with_enums_values_coerced + Book.connection.remove_index(:books, column: [:author_id, :name]) + + original_test_in_order_of_with_enums_values + ensure + Book.where(author_id: nil, name: nil).delete_all + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end + + # 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_keys + def test_in_order_of_with_enums_keys_coerced + Book.connection.remove_index(:books, column: [:author_id, :name]) + + original_test_in_order_of_with_enums_keys + ensure + Book.where(author_id: nil, name: nil).delete_all + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end +end From 16d6d2e1da4f4e339f3ee13672d1c4c9243d68da Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 01:10:41 -0300 Subject: [PATCH 1019/1412] escape regexp name --- test/cases/coerced_tests.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 945423114..a3a68af71 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1765,11 +1765,11 @@ class EnumTest < ActiveRecord::TestCase end # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. - coerce_tests! %r{with large number label} - test "with large number label coerced" do + coerce_tests! %r{serializable\? with large number label} + test "serializable? with large number label coerced" do Book.connection.remove_index(:books, column: [:author_id, :name]) - send(:'original_with large number label') + send(:'original_serializable? with large number label') ensure Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) From 623e3fe1efd175fc0ad72d4f578715ba272a03b1 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 29 Dec 2021 09:57:54 -0300 Subject: [PATCH 1020/1412] transform queries to add comments ActiveRecord::QueryLogs was introduced [here](https://github.com/rails/rails/commit/2408615154d039264810edbe628f4fc06d8cdc45). Then Rails decided to delegate query transformation responsability to each adapter [here](https://github.com/rails/rails/commit/12cc24a733190693c9e703c3edbd024a445d52db) --- .../connection_adapters/sqlserver/database_statements.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index ea92d6325..fa2ed0e47 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -12,6 +12,7 @@ def write_query?(sql) # :nodoc: end def execute(sql, name = nil) + sql = transform_query(sql) if preventing_writes? && write_query?(sql) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end @@ -27,6 +28,7 @@ def execute(sql, name = nil) end def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) + sql = transform_query(sql) if preventing_writes? && write_query?(sql) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" end From fcfb2a3f7f409e77d8405d6d4e4018fd38258299 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 29 Dec 2021 12:16:17 -0300 Subject: [PATCH 1021/1412] update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27d1659c..e2e24cb61 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ #### Changed +- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Add support to `ActiveRecord::QueryLogs` ... #### Added From 847cb4ddfb3174e5f3790159dacf27fb6c7151f5 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 16:31:34 -0300 Subject: [PATCH 1022/1412] coerce some test --- test/cases/coerced_tests.rb | 41 +++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d284b7c8..f9baa61c7 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1961,3 +1961,44 @@ def test_in_order_of_with_enums_keys_coerced Book.connection.add_index(:books, [:author_id, :name], unique: true) end end + +require "models/dashboard" +class QueryLogsTest < ActiveRecord::TestCase + # Same as original coerced test except our SQL ends with binding. + coerce_tests! :test_custom_basic_tags, :test_custom_proc_tags, :test_multiple_custom_tags, :test_custom_proc_context_tags + def test_custom_basic_tags_coerced + ActiveRecord::QueryLogs.tags = [ :application, { custom_string: "test content" } ] + + assert_sql(%r{/\*application:active_record,custom_string:test content\*/}) do + Dashboard.first + end + end + + def test_custom_proc_tags_coerced + ActiveRecord::QueryLogs.tags = [ :application, { custom_proc: -> { "test content" } } ] + + assert_sql(%r{/\*application:active_record,custom_proc:test content\*/}) do + Dashboard.first + end + end + + def test_multiple_custom_tags_coerced + ActiveRecord::QueryLogs.tags = [ + :application, + { custom_proc: -> { "test content" }, another_proc: -> { "more test content" } }, + ] + + assert_sql(%r{/\*application:active_record,custom_proc:test content,another_proc:more test content\*/}) do + Dashboard.first + end + end + + def test_custom_proc_context_tags_coerced + ActiveSupport::ExecutionContext[:foo] = "bar" + ActiveRecord::QueryLogs.tags = [ :application, { custom_context_proc: ->(context) { context[:foo] } } ] + + assert_sql(%r{/\*application:active_record,custom_context_proc:bar\*/}) do + Dashboard.first + end + end +end From d6a5a8be4371c83a75753104e8f6f744d8ccbd45 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 00:16:54 -0300 Subject: [PATCH 1023/1412] change coerced test --- test/cases/coerced_tests.rb | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d284b7c8..0711b78d4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1180,16 +1180,11 @@ def test_reorder_with_take_coerced # We have implicit ordering, via FETCH. coerce_tests! :test_reorder_with_first def test_reorder_with_first_coerced + post = nil sql_log = capture_sql do - message = <<~MSG.squish - `.reorder(nil)` with `.first` / `.first!` no longer - takes non-deterministic result in Rails 6.2. - To continue taking non-deterministic result, use `.take` / `.take!` instead. - MSG - assert_deprecated(message) do - assert Post.order(:title).reorder(nil).first - end + post = Post.order(:title).reorder(nil).first 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}" end From 12d69cf46cf542f52d6bd181dcc4ded9caff331d Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 31 Dec 2021 16:41:15 -0300 Subject: [PATCH 1024/1412] fix original method call. Escape name --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d284b7c8..8561cc991 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1769,7 +1769,7 @@ class EnumTest < ActiveRecord::TestCase test "serializable? with large number label coerced" do Book.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.connection.add_index(:books, [:author_id, :name], unique: true) From 195c5c4c683be5c412a1cff4290972db76ae0579 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Sat, 1 Jan 2022 15:19:35 -0300 Subject: [PATCH 1025/1412] add 6-1 marshal fixture Files where generated by running the following spec in 6-1-stable branch: ``` class MarshalSerializationTest < ActiveRecord::TestCase fixtures :topics def test_deserializing_rails_6_0_marshal_basic File.binwrite(marshal_fixture_path("rails_6_1_topic"), Marshal.dump(Topic.find(1))) topic = Topic.find(1) topic.replies File.binwrite(marshal_fixture_path("rails_6_1_topic_associations"), Marshal.dump(topic)) end private def marshal_fixture_path(file_name) File.expand_path( "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump", ARTest::SQLServer.test_root_sqlserver ) end end ``` --- .../SQLServer/rails_6_1_topic.dump | Bin 0 -> 2023 bytes .../SQLServer/rails_6_1_topic_associations.dump | Bin 0 -> 2876 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump new file mode 100644 index 0000000000000000000000000000000000000000..56f191de9e0210234be579a28550a6d9cb7cfc1f GIT binary patch literal 2023 zcmbVN+in{-5LGXbv@6;2Epc35swyzhB9OZALyOD?C>^^(1lR}~%K-{M5Y#Ruv8I+= zE?16h6wRmf7m7X?@OShV`Uib+hr5zvyFd|G03{ zKJM}I0~H|#lSA5UHkn|EEsewf6@dz|>XTnqiKE~} z&ZO5F%>edP6mjI1igrU58?F<8qaO~AxE^y2(8Y@YFT|jOQ5czN zliW_E7zrchaHd%_;7IG&_FD#rzYSy;+#Vl@NH!hJrTx8wGLJ&}Vml&NQtBP5~5Ko`U#IE}`rv8HGFM9rx_~;)}RsJK_Un5QbGt#;~`1rRP znI~V91qHkJfMe-sX}(kT7epnq<&k9t&+nnj>al97Q#B9nr^Z4;HM!#+Sv!Wux54CD z#+(Z##W@d89H>w_-l6oQ1al-wdzL^LN*ic>Qc}>lt>A-LbURUp?CpPIFQ1gIuC9Jn zuzgoOnnhy%xO;f8r(`;cO2|W7AG4ALvqug~MD>0`I@18FL=se9Wh?S6S;w)V;Jdqe zKZN{BNzS4h0o=TwqS+IgWkNXqvhr8T{)4XnP&P~UQTVGE13MvG%v#J zR9<%%fVNg6WDx7Iggf}UY?B}kY~LbJR2mF9LSMwfBSuV5=qaBdfO%q@fQsa#4ePcj zjfF}RIiWYZXbt^Mj8+qlEp-F!=u@2#X@X;ZSuwuG6gC(6vbN)-w={$nTD&(NwH!n5hZadkbkVkyJLIEddJMn zI&~vNAB9&y{6^|?@Cv*Ef66zzc50^*R7teST+W>+M=j1X+^( zwj;9bnq!0sdDeA0?b@}@kXKtmX;V$CrmL$gHbberh>d1)&J#9)isa13Y?qIi2t5`A ziff&2Z3b41hB6M!P(&vp8f^J;lx|i|l`w`!o{W-(m=ro<#LFvxZM=Fi{9~LYg%>j7 z?OOi0vkQq(xCr;Z;_RgwLx1n>t%tTTZ&YB1U7W z3`@lu?OK_Q$4X9kVhjFVQG6Th5TWsDSy1{|XnUc5m! zj>(;6XVNSq=Yoae1rNFfQuMsZw&!(oq)c_(vj9RUZJ<>*CrNu*!8@U7IZ+4f`CG9M z@8w>;VZ!YS67w}6NauzKF@YdxN%}Z#W2~lvbO7~jcz20$eg!P;)3U?hbumgIVKBk}G zs{)Fj;;Z4iLmTuiZAfw}y~nH_r0IKR$^VU)&dBn*=S8kJE1B^>|+@o*3|aP;rx`7Z$rY_d&M|Bp0bXjkuy2j7w-*EZ{*a85^bs+;r{68r24r=Haj54J@os5rGSJ zDtmp&HxXznvh1+-1vgo(B;Gy<_>M*6@r}jt%ZY;!rw&43o z0%<2z1xjbV5G=deRQ5&Cv9p3(Z`X>(z9y3X>{5CJMK&8rYPdRoNX+TqLTA^e16+T= z&~;`5z;wecoMqgHHY*_hK==rY*Na7nl9U&T)P507+S+_-h zz)>ezliaW*r5Ku;~1S0b Date: Sat, 1 Jan 2022 15:19:50 -0300 Subject: [PATCH 1026/1412] remove 6-0 marshal fixtures --- .../SQLServer/rails_6_0_topic.dump | Bin 2165 -> 0 bytes .../SQLServer/rails_6_0_topic_associations.dump | Bin 3086 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump delete mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic.dump deleted file mode 100644 index bb1a80786a9bdd97beef9b46c0c92752d0734ebd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2165 zcmbW2?`|7K5WvkJ`EQ(m6r~kyuv{X9szh!+Aa$-1n9oS9e9B}2am#+Fneb^u>%O%{&2fq5aCnSnhz6iDiV@(I?D@)K4^0JDSWIEcgif@e z#*#op=oXP2NGt2KGD7u`uJ*<~%TU30LDYe27kg4g+m%B8dk+z2z;-5B~C-t0J|1ZMYJH+=KVvR^h|nSN!Iqu?7x= zjJOCxgGu6oVe5W#<-$m7QE{#E`1Tfv??71W$N}z>XIaZ)Ymw=$R%ya5y~s_!T)?z5 z>#QzKtS(0)u>_O|7dPtSohFGQT?#Z&pa{EaXUg3Kq?{DuMv6#YVt87C8t21Z&QD8F zXGz+%j-?uNpU9k+H24eLbM_M-iFecZqGzR>o10%XY|Wb`cLep1dq;=+I!XtsL=3z7 z%2O4Am5NqL^B*Au9?;ybO$g8b-0ZPA1NXG^=gFJ3(V`uS`r`pqZX+uM!T zVu%gVpdN^gP>iZOJ3G}p;#qzPv>ALUzWd<01pg}+vM|pD*}R7Z$);4$`W6b>%rTKA za hUlWm?a1u+?cZ2gBd diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_0_topic_associations.dump deleted file mode 100644 index 5448055642b3314dbe26d156d8db06a8f57a99b1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3086 zcmbVO>uwuG6gC(68YgLJ)mDXK87e?kA~#$+TfsOfrYkj2Ql|*{$7sAev8P$@EVHvt z-3U?fB)kJJ(C6S$_*1^wwH>bws7fLwGqY#T`R1JOT;47#QthfpdYfdEhT@cI;VJER zu;a#P*-wm!6}j2=j2w#t<%@uj_Uy}JVTw-ah4JO znm`mCIE8dB0P`r69-Q=qYj4PP*;#8j^_H_P-}u{YZt}zom1ZFyWfq*v=K^p3vEeqa zPbI{N%Ybp;*CLKB7`o*)H%_%QMubd-8NT)f@ZIM|bmd6w!m_NT*jiBCS78z}gAbYN zdlyh`)mp7-&iiys11?sl@xI=@XmaQ5BRe=zTkH0)vH&(DROIBEzE(`KerDa?yDdf zg(Yy<=2yxEGJ6!+qN@KCslYtAF7|3aq&xb)+gz8UNNK~fm)ny%k0PzcV(Q#)mQB7+ zPAL@#9?tQ@zgt>FffOud$vB_!Yr=N&lAEE1t`TyE`e9Q6Czqxt^JKO8QL({IC9tUuv`V7cZo>l$jqn*yi9@e| z@=4_9jud$>bK$heWn_B7gJgk#XVW?c9J07rE!^hi?oc#3Qpcu|7F26{HRo~XJ|AZ< z`Ja4un>TzbMgx`jW+=lG84h+lHOjOul>#>*6`Ota^}nvayg&RiDw4``6$-amKI-n{ zN@pd--NgB@zEdc?g#Y2DTl60MoXgiEQnAU`tI2|V>omNDfA~FK+ugl;_s$o0@~P-H z-`n2aZam;)(clf#K+*8|q`I@SQ_T?P`3s`u=nH(S#Ip|mPhQBvGB2QWg%&uQvjXdL zQed;hiPRy#e}D7kXt$=P4q4*Js>oU4tVpYhe3E0%y!R2((P{)Q@;;A6g;c36{j^Hn zrDMx0))&W03ucMSJQT|oMC^vv$Th08M7QGT1G9G9IxCQ?6qP}FlG}cXOWshN70KE( zagNBR`8v=(7e)a>Gd|$qWZ|$^a<+SQl*bxXr=ifafb|+^$9>^<(zW4wAWYAGxI|-v zUd!;;h!zgksSMGi<4pES4x992E0PBsdn!tA*3iS%vxy>~A0p=`7%I{UBP-=>0b$Hg zGJ|YBcx11v1IB_6=ug0YlGe*=rm9Ba@Br<9v#ss8(zUy?^Re5k89VBv9TiIEZWmgS z=A!*<=-5X@>;Q7Wkx)>Iz{J$I7p+2u9>e1noR!8hMr`U-9UsTSVC}Rzcsa%tUwZHr z>nAcm6Hv2JY1XnH%_R0Rfs#WMX)Ix}<_?76C|JDW+@-$rHE>`;!}C}Qh6?Aa7NRla zm}d}=UaeR1${5cSxszA6Y!Hsunok~N7mLmweTu8q^iVn9%?@tlwH%5dC7>dov6P4%uq7ip$P#He;(EeP#3Y-6(H9UYI~J)hm~=1+_#!r1O^`&W3=lp+r$S+1 z>3Nb)#vO9=T#WT6Y#{?$_-0d58)h*0#yq_-O;X_dm&0q6AA{RKoOnZ25hR!|3;zID CZ1czf From be3136af07fd0bbc78d0420a67d23cdbd9384fb5 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 3 Jan 2022 09:49:28 -0300 Subject: [PATCH 1027/1412] change changelog --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e2e24cb61..0e76f4716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,10 @@ #### Changed -- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Add support to `ActiveRecord::QueryLogs` ... #### Added -... +- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Add support to `ActiveRecord::QueryLogs` Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. From b02e9380e1b2325ce9fa363c88ffe058f58c4647 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 3 Jan 2022 14:25:58 -0300 Subject: [PATCH 1028/1412] add reove after comment --- test/cases/coerced_tests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f9baa61c7..31d891fda 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1965,6 +1965,7 @@ def test_in_order_of_with_enums_keys_coerced require "models/dashboard" class QueryLogsTest < ActiveRecord::TestCase # Same as original coerced test except our SQL ends with binding. + # TODO: Remove coerce after Rails 7.0.1 (see https://github.com/rails/rails/pull/44053) coerce_tests! :test_custom_basic_tags, :test_custom_proc_tags, :test_multiple_custom_tags, :test_custom_proc_context_tags def test_custom_basic_tags_coerced ActiveRecord::QueryLogs.tags = [ :application, { custom_string: "test content" } ] From 9934cebc4794df26619b501f6042aa53a35fb072 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 4 Jan 2022 10:11:19 -0300 Subject: [PATCH 1029/1412] change changelog again --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e76f4716..b96001223 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,6 @@ #### Added -- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Add support to `ActiveRecord::QueryLogs` +- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Support `ActiveRecord::QueryLogs` Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. From 8c77bbbd687122d0652ac0f55ad052f660eb33a3 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 4 Jan 2022 10:13:42 -0300 Subject: [PATCH 1030/1412] adapt remove_columns_for_alter to SQL Server syntax Rails introduced an optimization to remove_columns [here](https://github.com/rails/rails/commit/bff3523242d3fcdc7a069ff86863e08d01537674) to use a single SQL statement. The produced SQL is ```sql ALTER TABLE [my_table] DROP COLUMN [col_one], DROP COLUMN [col_two] ``` but SQL Server requires ```sql ALTER TABLE [my_table] DROP COLUMN [col_one], [col_two] ``` --- .../connection_adapters/sqlserver/schema_statements.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9fd162a5f..253c96f41 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -507,6 +507,13 @@ def column_definitions_sql(database, identifier) }.gsub(/[ \t\r\n]+/, " ").strip end + def remove_columns_for_alter(table_name, *column_names, **options) + first, *rest = column_names + + # return an array like this [DROP COLUMN col_1, col_2, col_3]. Abstract adapter joins fragments with ", " + [remove_column_for_alter(table_name, first)] + rest.map { |column_name| quote_column_name(column_name) } + end + def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", "SCHEMA" constraints.each do |constraint| From 0cfde54256e6cb30c5f010ac387c42858aeb7a68 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 4 Jan 2022 10:14:44 -0300 Subject: [PATCH 1031/1412] changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e27d1659c..162fbc50e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ #### Changed -... +- [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement #### Added From 965a722eb61facbf27982a696a210ebe2416bc8b Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 5 Jan 2022 10:15:45 -0300 Subject: [PATCH 1032/1412] update preloader monkey patch The adapter monkey patch `Preloader::Association` to preload in slices of 10.000 records (see [here](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/main/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb). Rails moved the method the adapter was patching to `Preloader::Association::LoaderQuery` [here](https://github.com/rails/rails/commit/eeef0bb26a23133d51e9031877985a29cd53bd66). --- .../sqlserver/core_ext/preloader.rb | 22 ++++++------------- .../eager_load_too_many_ids_test_sqlserver.rb | 18 +++++++++++++++ 2 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 test/cases/eager_load_too_many_ids_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index 9d64c99e8..9fb736795 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -6,20 +6,12 @@ module ActiveRecord module ConnectionAdapters module SQLServer module CoreExt - module Preloader - private + module LoaderQuery + def load_records_for_keys(keys, &block) + return super unless scope.connection.adapter_name == "SQLServer" - def records_for(ids) - return super unless klass.connection.adapter_name == "SQLServer" - - ids.each_slice(in_clause_length).flat_map do |slice| - scope.where(association_key_name => slice).load do |record| - # Processing only the first owner - # because the record is modified but not an owner - owner = owners_by_key[convert_key(record[association_key_name])].first - association = owner.association(reflection.name) - association.set_inverse_instance(record) - end.records + keys.each_slice(in_clause_length).flat_map do |slice| + scope.where(association_key_name => slice).load(&block).records end end @@ -33,6 +25,6 @@ def in_clause_length end ActiveSupport.on_load(:active_record) do - mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Preloader - ActiveRecord::Associations::Preloader::Association.prepend(mod) + mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::LoaderQuery + ActiveRecord::Associations::Preloader::Association::LoaderQuery.prepend(mod) end diff --git a/test/cases/eager_load_too_many_ids_test_sqlserver.rb b/test/cases/eager_load_too_many_ids_test_sqlserver.rb new file mode 100644 index 000000000..96eaa6cf9 --- /dev/null +++ b/test/cases/eager_load_too_many_ids_test_sqlserver.rb @@ -0,0 +1,18 @@ +require "cases/helper_sqlserver" +require "models/citation" +require "models/book" + +class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase + fixtures :citations + + def test_batch_preloading_too_many_ids + in_clause_length = 10_000 + + # We Monkey patch Preloader to work with batches of 10_000 records. + # Expect: N Books queries + Citation query + expected_query_count = (Citation.count / in_clause_length.to_f).ceil + 1 + assert_queries(expected_query_count) do + Citation.preload(:reference_of).to_a.size + end + end +end From 5e0ff4ac782267878428bba9e435c579e9d8bea5 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 7 Jan 2022 01:27:04 -0300 Subject: [PATCH 1033/1412] coerce migration test that was using blob column type --- test/cases/coerced_tests.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d284b7c8..8644c6404 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -591,6 +591,36 @@ class MigrationTest < ActiveRecord::TestCase # For some reason our tests set Rails.@_env which breaks test env switching. coerce_tests! :test_internal_metadata_stores_environment_when_other_data_exists coerce_tests! :test_internal_metadata_stores_environment + + # Same as original but using binary type instead of blob + 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 migrate(x) + add_column "people", "last_name", :binary + end + }.new + + migration_b = Class.new(ActiveRecord::Migration::Current) { + def version; 101 end + def migrate(x) + add_column "people", "last_name", :binary, if_not_exists: true + end + }.new + + ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, 100).migrate + assert_column Person, :last_name, "migration_a should have created the last_name column on people" + + assert_nothing_raised do + ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, 101).migrate + end + ensure + Person.reset_column_information + if Person.column_names.include?("last_name") + Person.connection.remove_column("people", "last_name") + end + end end class CoreTest < ActiveRecord::TestCase From aa72c349dbd079723797fcefd9238acce471b0e1 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 10 Jan 2022 10:32:41 -0300 Subject: [PATCH 1034/1412] change remove after rails text --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 31d891fda..8ce9ae5e0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1965,7 +1965,7 @@ def test_in_order_of_with_enums_keys_coerced require "models/dashboard" class QueryLogsTest < ActiveRecord::TestCase # Same as original coerced test except our SQL ends with binding. - # TODO: Remove coerce after Rails 7.0.1 (see https://github.com/rails/rails/pull/44053) + # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44053) coerce_tests! :test_custom_basic_tags, :test_custom_proc_tags, :test_multiple_custom_tags, :test_custom_proc_context_tags def test_custom_basic_tags_coerced ActiveRecord::QueryLogs.tags = [ :application, { custom_string: "test content" } ] From 4b12a97d6c8f3673168e4f8671854122e06c6b63 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 30 Dec 2021 10:25:59 -0300 Subject: [PATCH 1035/1412] coerce tests that use upsert. sql server does not support it yet --- test/cases/coerced_tests.rb | 53 +++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8ce9ae5e0..674c052fe 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2003,3 +2003,56 @@ def test_custom_proc_context_tags_coerced end end end + +# SQL Server does not support upsert yet +# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44050) +class InsertAllTest < ActiveRecord::TestCase + coerce_tests! :test_upsert_all_only_updates_the_column_provided_via_update_only + def test_upsert_all_only_updates_the_column_provided_via_update_only_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_only_updates_the_column_provided_via_update_only + end + end + + coerce_tests! :test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only + def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only + end + end + + coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true + def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true + end + end + + coerce_tests! :test_upsert_all_respects_created_at_precision_when_touched_implicitly + def test_upsert_all_respects_created_at_precision_when_touched_implicitly_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_respects_created_at_precision_when_touched_implicitly + end + end + + coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden + def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden + end + end + + coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false + def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false + end + end + + coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden + def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden_coerced + assert_raises(ArgumentError, /does not support upsert/) do + original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden + end + end +end From 0692d53ffb2a78f69d6b1d14687379b1d87d54da Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 30 Dec 2021 23:43:17 -0300 Subject: [PATCH 1036/1412] coerce test to match sql server table quoting --- test/cases/coerced_tests.rb | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 674c052fe..45d31a69d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2056,3 +2056,22 @@ def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_times end end end + +class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase + # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44051) + coerce_tests! :test_disable_joins_through_with_enum_type + def test_disable_joins_through_with_enum_type_coerced + joins = capture_sql { @member.club } + no_joins = capture_sql { @member.club_without_joins } + + assert_equal 1, joins.size + assert_equal 2, no_joins.size + + assert_match(/INNER JOIN/, joins.first) + no_joins.each do |nj| + assert_no_match(/INNER JOIN/, nj) + end + + assert_match(/\[memberships\]\.\[type\]/, no_joins.first) + end +end From bd046bd2f54ffae73f8454cb5e23f8794a3e83fc Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Sat, 1 Jan 2022 16:35:00 -0300 Subject: [PATCH 1037/1412] do not encrypt in values Take the following example ``` ActiveRecord::Encryption.config.support_unencrypted_data = true EncryptedBook.find_by(name: "Dune") ``` Before: ``` EXEC sp_executesql N'SELECT [encrypted_books].* FROM [encrypted_books] WHERE [encrypted_books].[name] IN (@0, @1) ORDER BY [encrypted_books].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 nvarchar(max), @1 nvarchar(max), @2 int', @0 = N'{"p":"BbyqDSWICbMEBTtrN4iv0paan00brX1ly6wT92lFrgOQZomP5jBjhPlalbTvucTFX249bJHp7j6usa7SBpAuQFzN+sZFa3dMnlTcaei5","h":{"iv":"2esXR4qnvSteG+o7","at":"pXZzi+SpMNxpAvA8eQYVqA=="}}', @1 = N'{"p":"DIohhw==","h":{"iv":"wEPaDcJP3VNIxaiz","at":"X7+2xvvcu1k1if6Dy28Esw=="}}', @2 = 1 [["name", nil], ["name", nil], ["LIMIT", nil]] ``` After: ``` EXEC sp_executesql N'SELECT [encrypted_books].* FROM [encrypted_books] WHERE [encrypted_books].[name] IN (@0, @1) ORDER BY [encrypted_books].[id] ASC OFFSET 0 ROWS FETCH NEXT @2 ROWS ONLY', N'@0 nvarchar(max), @1nvarchar(max), @2 int', @0 = N'{"p":"DIohhw==","h":{"iv":"wEPaDcJP3VNIxaiz","at":"X7+2xvvcu1k1if6Dy28Esw=="}}', @1 = N'Dune', @2 = 1 [["name", nil], ["name", nil], ["LIMIT", nil]] ``` --- CHANGELOG.md | 1 + lib/arel/visitors/sqlserver.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b96001223..0d25ffb31 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,5 +11,6 @@ #### Added - [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Support `ActiveRecord::QueryLogs` +- [#981](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/981) Support `find_by` an encrypted attribute Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 618d3a659..f05fb3a3f 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -83,6 +83,8 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) # Monkey-patch start. Add query attribute bindings rather than just values. column_name = o.column_name column_type = o.attribute.relation.type_for_attribute(o.column_name) + # Use cast_type on encrypted attributes. Don't encrypt them again + column_type = column_type.cast_type if column_type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) } collector.add_binds(attrs, &bind_block) From cc25a9ab5c62427937fe7362b1aaf926f8f1d9cf Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 4 Jan 2022 10:30:28 -0300 Subject: [PATCH 1038/1412] better handle sql queries with invalid encoding Following the same strategy as https://github.com/rails/rails/commit/8cf09b727549ad3a23de7846becc3739bce3c6e2. ``` If the SQL encoding somehow is invalid, `Regexp#match?` raises `ArgumentError`. Best we can do is to copy the string and try to match the regexp in "binary" mode. ``` After this change ActiveRecord::StatementInvalid is raised. Quoting [Rails Postgresql test](https://github.com/rails/rails/commit/8cf09b727549ad3a23de7846becc3739bce3c6e2#diff-39aac151bf5772be2c6c4fec49bf5cdf5a21ca816fe181ef01466ecd941bef9bR56) ``` At least we can assert it fails in the client and not before when trying to match the query. ``` --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 2 ++ test/cases/coerced_tests.rb | 12 ++++++++++++ 3 files changed, 15 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3a9366622..61ca5ca5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ #### Changed - [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement +- [#984](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/984) Better handle SQL queries with invalid encoding #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fa2ed0e47..a99b988aa 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -9,6 +9,8 @@ module DatabaseStatements def write_query?(sql) # :nodoc: !READ_QUERY.match?(sql) + rescue ArgumentError # Invalid encoding + !READ_QUERY.match?(sql.b) end def execute(sql, name = nil) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e3eb60f59..15002f7fb 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -91,6 +91,18 @@ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called Subscriber.send(:load_schema!) original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes end + + coerce_tests! :test_doesnt_error_when_a_select_query_has_encoding_errors + def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced + ActiveRecord::Base.while_preventing_writes do + # TinyTDS fail on encoding errors. + # But at least we can assert it fails in the client and not before when trying to + # match the query. + assert_raises ActiveRecord::StatementInvalid do + @connection.select_all("SELECT '\xC8'") + end + end + end end end From 7e76a8e09138dc0cbc03ca40accc278c11c327d5 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Tue, 4 Jan 2022 10:55:50 -0300 Subject: [PATCH 1039/1412] Support string returning clause for AR#insert_all [Rails added](https://github.com/rails/rails/commit/c7613dde539a8ffb710e432d131ccc7891520066) support to string returning clause for `insert_all`. This PR port that behaviour. We are not manipulating the string in any way, which means that clients need to use `INSERTED.column_name` --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 7 ++++++- test/cases/coerced_tests.rb | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61ca5ca5f..b1254c1af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,5 +13,6 @@ - [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Support `ActiveRecord::QueryLogs` - [#981](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/981) Support `find_by` an encrypted attribute +- [#985](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/985) Support string returning clause for `ActiveRecord#insert_all` Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index a99b988aa..10f19777e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -149,7 +149,12 @@ def build_insert_sql(insert) # :nodoc: sql = +"INSERT #{insert.into}" if returning = insert.send(:insert_all).returning - sql << " OUTPUT " << returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + returning_sql = if returning.is_a?(String) + returning + else + returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + end + sql << " OUTPUT #{returning_sql}" end sql << " #{insert.values_list}" diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e9be51514..e05f6bd97 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2082,3 +2082,14 @@ def test_disable_joins_through_with_enum_type_coerced assert_match(/\[memberships\]\.\[type\]/, no_joins.first) end end + +class InsertAllTest < ActiveRecord::TestCase + coerce_tests! :test_insert_all_returns_requested_sql_fields + # Same as original but using INSERTED.name as UPPER argument + 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") + end +end From d52738459cd08b163d24552f4597314a35902d18 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Wed, 12 Jan 2022 13:37:12 -0300 Subject: [PATCH 1040/1412] [Rails 7] Raise error asking for non-existing table columns (#988) [Rails 7] Raise error asking for non-existing table columns --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b1254c1af..d53e2faa7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement - [#984](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/984) Better handle SQL queries with invalid encoding +- [#988](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/988) Raise `ActiveRecord::StatementInvalid` when `columns` is called with a non-existing table (***breaking change***) #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 253c96f41..c9ad8a0ad 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -378,7 +378,7 @@ def column_definitions(table_name) binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? results = sp_executesql(sql, "SCHEMA", binds) - results.map do |ci| + columns = results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] ci[:table_name] = view_tblnm || table_name @@ -437,6 +437,13 @@ def column_definitions(table_name) ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class) ci end + + # Since Rails 7, it's expected that all adapter raise error when table doesn't exists. + # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those). + # Raise error if the method return an empty array + columns.tap do |result| + raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty? + end end def column_definitions_sql(database, identifier) From 15b1d2bc6e3b41fd8eb2a01fc43d4611c71bf21c Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 13 Jan 2022 06:43:42 -0300 Subject: [PATCH 1041/1412] [Rails 7] Define adapter type maps statically (#968) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver_adapter.rb | 156 ++++++++++-------- test/cases/coerced_tests.rb | 6 +- 3 files changed, 86 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d53e2faa7..4d0d897f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ #### Changed +- [#968](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/968) Define adapter type maps statically - [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement - [#984](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/984) Better handle SQL queries with invalid encoding - [#988](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/988) Raise `ActiveRecord::StatementInvalid` when `columns` is called with a non-existing table (***breaking change***) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 40f45e271..422c4add2 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -377,83 +377,93 @@ def get_database_version # :nodoc: version_year end - protected - - # === Abstract Adapter (Misc Support) =========================== # - - def initialize_type_map(m = type_map) - 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) - precision = extract_precision(sql_type) - if scale == 0 - SQLServer::Type::DecimalWithoutScale.new(precision: precision) - else - SQLServer::Type::Decimal.new(precision: precision, scale: scale) + class << self + protected + + def initialize_type_map(m) + 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) + precision = extract_precision(sql_type) + if scale == 0 + SQLServer::Type::DecimalWithoutScale.new(precision: precision) + else + SQLServer::Type::Decimal.new(precision: precision, scale: scale) + end 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 - - # Approximate Numerics - 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| - precision = extract_precision(sql_type) - if precision - SQLServer::Type::DateTime2.new precision: precision - else - SQLServer::Type::DateTime.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 + + # Date and Time + 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 + else + SQLServer::Type::DateTime.new + end end + m.register_type %r{\Adatetimeoffset}i do |sql_type| + 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| + 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 + + # 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 + + # 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 + + # Other Data Types + m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new + m.register_type "timestamp", SQLServer::Type::Timestamp.new end - m.register_type %r{\Adatetimeoffset}i do |sql_type| - 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| - precision = extract_precision(sql_type) || DEFAULT_TIME_PRECISION - SQLServer::Type::Time.new precision: precision - end + end + + TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) } + + protected + + # === Abstract Adapter (Misc Support) =========================== # - # 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 - - # 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 - - # 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 - - # Other Data Types - m.register_type "uniqueidentifier", SQLServer::Type::Uuid.new - m.register_type "timestamp", SQLServer::Type::Timestamp.new + def type_map + TYPE_MAP end def translate_exception(e, message:, sql:, binds:) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 264eb9181..bb084083f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1161,7 +1161,8 @@ def test_cache_does_not_wrap_results_in_arrays_coerced end end - # Same as original test except that we expect one query to be performed to retrieve the table's primary key. + # Same as original test except that we expect one query to be performed to retrieve the table's primary key + # and we don't call `reload_type_map` because SQL Server adapter doesn't support it. # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column # information then the primary key needs to be retrieved from the database again to generate the SQL causing the # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key. @@ -1171,9 +1172,6 @@ def test_query_cached_even_when_types_are_reset_coerced # Warm the cache Task.find(1) - # Preload the type cache again (so we don't have those queries issued during our assertions) - Task.connection.send(:reload_type_map) - # Clear places where type information is cached Task.reset_column_information Task.initialize_find_by_cache From 00eb52024d8bdb1b89d1751da5b25338efa045f0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 11:29:15 +0000 Subject: [PATCH 1042/1412] Coerce update all and delete all tests --- test/cases/coerced_tests.rb | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bb084083f..bdc0e2e52 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1120,6 +1120,40 @@ def test_update_all_doesnt_ignore_order_coerced _(david.reload.name).must_equal "David" _(mary.reload.name).must_equal "Test" end + + # SELECT columns must be in the GROUP clause. + coerce_tests! :test_update_all_with_group_by + def test_update_all_with_group_by_coerced + minimum_comments_count = 2 + + Post.most_commented(minimum_comments_count).update_all(title: "ig") + posts = Post.select(:id, :title).group(:title).most_commented(minimum_comments_count).all.to_a + + assert_operator posts.length, :>, 0 + assert posts.all? { |post| post.comments.length >= minimum_comments_count } + assert posts.all? { |post| "ig" == post.title } + + 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 + end +end + +class DeleteAllTest < ActiveRecord::TestCase + # SELECT columns must be in the GROUP clause. + coerce_tests! :test_delete_all_with_group_by_and_having + def test_delete_all_with_group_by_and_having_coerced + minimum_comments_count = 2 + posts_to_be_deleted = Post.select(:id).most_commented(minimum_comments_count).all.to_a + assert_operator posts_to_be_deleted.length, :>, 0 + + assert_difference("Post.count", -posts_to_be_deleted.length) do + Post.most_commented(minimum_comments_count).delete_all + end + + posts_to_be_deleted.each do |deleted_post| + assert_raise(ActiveRecord::RecordNotFound) { deleted_post.reload } + end + end end require "models/topic" From 603ee3bc904679f6205ddd4195d07eb5543d7da7 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Sat, 1 Jan 2022 15:32:05 -0300 Subject: [PATCH 1043/1412] reimplement test using sql server limits --- test/cases/coerced_tests.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bdc0e2e52..fe73d7f0f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2155,3 +2155,15 @@ def test_insert_all_returns_requested_sql_fields_coerced assert_equal %w[ REWORK ], result.pluck("name") end end + +class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase + # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052) + # Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters + coerce_tests! %r{validate column sizes} + test "validate column sizes coerced" do + assert EncryptedAuthor.new(name: "jorge").valid? + assert_not EncryptedAuthor.new(name: "a" * 4001).valid? + author = EncryptedAuthor.create(name: "a" * 4001) + assert_not author.valid? + end +end From 8a7aa8e64bc066fb65978cdd734213067bdf1a81 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 10 Jan 2022 10:11:50 -0300 Subject: [PATCH 1044/1412] datetime without precision now has default of 6 --- test/cases/coerced_tests.rb | 28 ++++++++++++++++++++++ test/cases/schema_dumper_test_sqlserver.rb | 4 ++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index bdc0e2e52..473156774 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -542,6 +542,34 @@ def test_create_table_with_defaults_coerce assert_equal 1, four.default assert_equal "hello", five.default end + + # Rails adds precision 6 by default, sql server uses datetime2 for datetimes with precision + coerce_tests! :test_add_column_with_postgresql_datetime_type + def test_add_column_with_postgresql_datetime_type_coerced + connection.create_table :testings do |t| + t.column :foo, :datetime + end + + column = connection.columns(:testings).find { |c| c.name == "foo" } + + assert_equal :datetime, column.type + assert_equal "datetime2(6)", column.sql_type + end + + # timestamp is datetime with default limit + coerce_tests! :test_change_column_with_timestamp_type + def test_change_column_with_timestamp_type_coerced + connection.create_table :testings do |t| + t.column :foo, :datetime, null: false + end + + connection.change_column :testings, :foo, :timestamp + + column = connection.columns(:testings).find { |c| c.name == "foo" } + + assert_equal :datetime, column.type + assert_equal "datetime", column.sql_type + end end end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 1b59525fe..08d681f52 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -67,7 +67,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase _(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_col"].sql_type).must_equal "datetime" + _(columns["datetime_col"].sql_type).must_equal "datetime2(6)" _(columns["timestamp_col"].sql_type).must_equal "datetime" _(columns["time_col"].sql_type).must_equal "time(7)" _(columns["date_col"].sql_type).must_equal "date" @@ -79,7 +79,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil + assert_line :datetime_col, type: "datetime", limit: nil, precision: 6, scale: nil, default: nil assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil From 40937934dbb6805c4b788bf7ad0c6ba6ef963afc Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Thu, 13 Jan 2022 11:27:18 -0300 Subject: [PATCH 1045/1412] exclude encryption performance from builds Rails is not running encryption performance test on builds. Commit https://github.com/rails/rails/commit/1fff866dd1239ff861574ed186e76bdbb166cff1) claims they were meant to be excluded from builds. The commit also includes the same flaky test failure we have now. Update `ar_cases` to exclude encryption performance tests --- test/support/rake_helpers.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/support/rake_helpers.rb b/test/support/rake_helpers.rb index fb22215d2..5d307ec25 100644 --- a/test/support/rake_helpers.rb +++ b/test/support/rake_helpers.rb @@ -25,7 +25,9 @@ def sqlserver_cases def ar_cases @ar_cases ||= begin - Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { |x| x =~ /\/adapters\// }.sort + Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject { + |x| x.include?("/adapters/") || x.include?("/encryption/performance") + }.sort end end From e9dc25a49c3adc329623c9b7c1fb6ee846623c1b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 14:38:20 +0000 Subject: [PATCH 1046/1412] Replace octal values with integers --- test/cases/column_test_sqlserver.rb | 116 ++++++++++++++-------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 8636da763..a5aafbbbf 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -277,8 +277,8 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "date" _(col.type).must_equal :date _(col.null).must_equal true - _(col.default).must_equal connection_dblib_73? ? Date.civil(0001, 1, 1) : "0001-01-01" - _(obj.date).must_equal Date.civil(0001, 1, 1) + _(col.default).must_equal connection_dblib_73? ? Date.civil(1, 1, 1) : "0001-01-01" + _(obj.date).must_equal Date.civil(1, 1, 1) _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Date @@ -287,22 +287,22 @@ def assert_obj_set_and_save(attribute, value) _(type.scale).must_be_nil # Can cast strings. SQL Server format. obj.date = "04-01-0001" - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) obj.save! - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) obj.reload - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) # Can cast strings. ISO format. obj.date = "0001-04-01" - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) obj.save! - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) obj.reload - _(obj.date).must_equal Date.civil(0001, 4, 1) + _(obj.date).must_equal Date.civil(1, 4, 1) # Can filter by date range _(obj).must_equal obj.class.where(date: obj.date..Date::Infinity.new).first # Can keep and return assigned date. - assert_obj_set_and_save :date, Date.civil(1972, 04, 14) + 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) @@ -315,7 +315,7 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "datetime" _(col.type).must_equal :datetime _(col.null).must_equal true - time = Time.utc 1753, 01, 01, 00, 00, 00, 123000 + 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_function).must_be_nil @@ -327,7 +327,7 @@ def assert_obj_set_and_save(attribute, value) obj.save! _(obj).must_equal obj.class.where(datetime: time).first # Can save to proper accuracy and return again. - time = Time.utc 2010, 04, 01, 12, 34, 56, 3000 + time = Time.utc 2010, 4, 1, 12, 34, 56, 3000 obj.datetime = time _(obj.datetime).must_equal time, "Microseconds were <#{obj.datetime.usec}> vs <3000>" obj.save! @@ -338,8 +338,8 @@ def assert_obj_set_and_save(attribute, value) # Can filter by datetime range _(obj).must_equal obj.class.where(datetime: time..DateTime::Infinity.new).first # Will cast to true DB value on attribute write, save and return again. - time = Time.utc 2010, 04, 01, 12, 34, 56, 234567 - time2 = Time.utc 2010, 04, 01, 12, 34, 56, 233000 + time = Time.utc 2010, 4, 1, 12, 34, 56, 234567 + time2 = Time.utc 2010, 4, 1, 12, 34, 56, 233000 obj.datetime = time _(obj.datetime).must_equal time2, "Microseconds were <#{obj.datetime.usec}> vs <233000>" obj.save! @@ -427,8 +427,8 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "datetimeoffset(7)" _(col.type).must_equal :datetimeoffset _(col.null).must_equal true - _(col.default).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds <#{col.default.nsec}> vs <123456700>" - _(obj.datetimeoffset_7).must_equal Time.new(1984, 01, 24, 04, 20, 00, -28800).change(nsec: 123456700), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <999999900>" + _(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 = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::DateTimeOffset @@ -436,12 +436,12 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_equal 7 _(type.scale).must_be_nil # Can save 100 nanosecond precisoins and return again. - obj.datetimeoffset_7 = Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456755) - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + obj.datetimeoffset_7 = Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456755) + _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.save! - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload - _(obj.datetimeoffset_7).must_equal Time.new(2010, 04, 01, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" # Maintains the timezone time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetimeoffset_7 = time @@ -470,8 +470,8 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "smalldatetime" _(col.type).must_equal :smalldatetime _(col.null).must_equal true - _(col.default).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) - _(obj.smalldatetime).must_equal Time.utc(1901, 01, 01, 15, 45, 00, 000) + _(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 = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::SmallDateTime @@ -479,12 +479,12 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_be_nil _(type.scale).must_be_nil # Will remove fractional seconds and return again. - obj.smalldatetime = Time.utc(2078, 06, 05, 4, 20, 00, 3000) - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" + obj.smalldatetime = Time.utc(2078, 6, 5, 4, 20, 0, 3000) + _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.smalldatetime.usec}> vs <0>" obj.save! - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" obj.reload - _(obj.smalldatetime).must_equal Time.utc(2078, 06, 05, 4, 20, 00, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" + _(obj.smalldatetime).must_equal Time.utc(2078, 6, 5, 4, 20, 0, 0), "Microseconds were <#{obj.reload.smalldatetime.usec}> vs <0>" end it "time(7)" do @@ -493,7 +493,7 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time _(col.null).must_equal true - _(col.default).must_equal Time.utc(1900, 01, 01, 04, 20, 00, Rational(288321500, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <288321500>" + _(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 = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Time @@ -501,22 +501,22 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 300) - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_7.nsec}> vs <300>" + 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, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_7.usec}> vs <0>" - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 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>" # Time's #usec precision (high micro) - obj.time_7 = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + 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.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_7.usec}> vs <234567>" + _(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, 01, 01, 15, 45, 00, Rational(288321545, 1000)) - _(obj.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + 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.time_7).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_7.nsec}> vs <288321500>" + _(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 @@ -533,20 +533,20 @@ def assert_obj_set_and_save(attribute, value) _(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, 01, 10, 15, 45, 00, 0) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + 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.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0) + _(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, 01, 01, 15, 45, 00, 30000) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + 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.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 30000), "Microseconds were <#{obj.time_2.usec}> vs <30000>" + _(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, 01, 01, 15, 45, 00, 4000) - _(obj.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + 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.time_2).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 0), "Microseconds were <#{obj.time_2.usec}> vs <0>" + _(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 @@ -555,7 +555,7 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time _(col.null).must_equal true - _(col.default).must_equal Time.utc(1900, 01, 01, 15, 03, 42, Rational(62197800, 1000)), "Nanoseconds were <#{col.default.nsec}> vs <62197800>" + _(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 = connection.lookup_cast_type_from_column(col) _(type).must_be_instance_of Type::Time @@ -563,22 +563,22 @@ def assert_obj_set_and_save(attribute, value) _(type.precision).must_equal 7 _(type.scale).must_be_nil # Time's #usec precision (low micro) - obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 300) - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 300), "Nanoseconds were <#{obj.time_default.nsec}> vs <300>" + 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, 01, 01, 15, 45, 00, 300), "Microseconds were <#{obj.time_default.usec}> vs <0>" - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 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>" # Time's #usec precision (high micro) - obj.time_default = Time.utc(2000, 01, 01, 15, 45, 00, 234567) - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + 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.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, 234567), "Microseconds were <#{obj.time_default.usec}> vs <234567>" + _(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, 01, 01, 15, 45, 00, Rational(288321545, 1000)) - _(obj.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + 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.time_default).must_equal Time.utc(2000, 01, 01, 15, 45, 00, Rational(288321500, 1000)), "Nanoseconds were <#{obj.time_default.nsec}> vs <288321500>" + _(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 # Character Strings From 092b522b75487684eaf30e9f06d879349298cb84 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 14:40:25 +0000 Subject: [PATCH 1047/1412] Updated the copyright years --- MIT-LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index ad08c4535..90cce529a 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2008-2015 +Copyright (c) 2008-2022 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/README.md b/README.md index 3e68e1e1d..f3dc45ccf 100644 --- a/README.md +++ b/README.md @@ -188,4 +188,4 @@ You can see an up-to-date list of contributors here: http://github.com/rails-sql ## License -Copyright © 2008-2020. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file. +Copyright © 2008-2022. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file. From cdded40d517d48bdcffdc0d7cd7844c7007ab3e7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 15:52:57 +0000 Subject: [PATCH 1048/1412] Rubocop: Style/RedundantSelf --- .../connection_adapters/sqlserver/type/data.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 2 +- test/support/coerceable_test_sqlserver.rb | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index f3bf6bab6..bc3f82f8d 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -27,7 +27,7 @@ def inspect end def eql?(other) - self.class == other.class && self.value == other.value + self.class == other.class && value == other.value end alias :== :eql? end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 422c4add2..28ebe97cf 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -105,7 +105,7 @@ def dblib_connect(config) end def config_appname(config) - if self.instance_methods.include?(:configure_application_name) + if instance_methods.include?(:configure_application_name) ActiveSupport::Deprecation.warn <<~MSG.squish Configuring the application name used by TinyTDS by overriding the `ActiveRecord::ConnectionAdapters::SQLServerAdapter#configure_application_name` diff --git a/test/support/coerceable_test_sqlserver.rb b/test/support/coerceable_test_sqlserver.rb index bd8cd0e6d..5419cc5a0 100644 --- a/test/support/coerceable_test_sqlserver.rb +++ b/test/support/coerceable_test_sqlserver.rb @@ -13,7 +13,7 @@ module CoerceableTest module ClassMethods def coerce_tests!(*methods) methods.each do |method| - self.coerced_tests.push(method) + coerced_tests.push(method) coerced_test_warning(method) end end @@ -24,7 +24,7 @@ def coerce_all_tests! undef_method(method) end - STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{self.name}" + STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{name}" end private @@ -43,9 +43,9 @@ def coerced_test_warning(test_to_coerce) end if result.blank? - STDOUT.puts "🐳 Unfound coerced test: #{self.name}##{m}" + STDOUT.puts "🐳 Unfound coerced test: #{name}##{m}" else - STDOUT.puts "🐵 Undefined coerced test: #{self.name}##{m}" + STDOUT.puts "🐵 Undefined coerced test: #{name}##{m}" end end end From 4fa4077a529a5783b8f5c19cfd6453e87520248a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 16:18:53 +0000 Subject: [PATCH 1049/1412] Coerce test so that results are sorted before comparision --- test/cases/coerced_tests.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a5d56ca70..0a185c060 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1350,6 +1350,14 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query end + + # Workaround for randomly failing test. Ordering of results not guaranteed. + coerce_tests! :test_select_quotes_when_using_from_clause + def test_select_quotes_when_using_from_clause_coerced + quoted_join = ActiveRecord::Base.connection.quote_table_name("join") + selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join) + assert_equal Post.pluck(:id).sort, selected.sort + end end end From c6169634cf75522a6c330b578d0be1068310e57c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jan 2022 16:39:40 +0000 Subject: [PATCH 1050/1412] Added comment --- test/cases/coerced_tests.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0a185c060..a76fa354b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1352,6 +1352,7 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced end # Workaround for randomly failing test. Ordering of results not guaranteed. + # TODO: Remove coerced test when https://github.com/rails/rails/pull/44168 merged. coerce_tests! :test_select_quotes_when_using_from_clause def test_select_quotes_when_using_from_clause_coerced quoted_join = ActiveRecord::Base.connection.quote_table_name("join") From 41a6e8054df6a2d756743f50afcc1b38f34dd734 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 18 Jan 2022 19:55:32 +0000 Subject: [PATCH 1051/1412] Update CI matrix --- .github/workflows/ci.yml | 5 ++++- README.md | 2 +- VERSION | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3188b726c..c11878541 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,10 @@ jobs: strategy: fail-fast: false matrix: - ruby: [2.7.3, 3.0.1] + ruby: + - 2.7.5 + - 3.0.3 + - 3.1.0 steps: - name: Checkout code diff --git a/README.md b/README.md index f3dc45ccf..9c9c33972 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.0.0` | `7.0.x` | [unreleased](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.0.0.rc1` | `7.0.x` | [unreleased](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | diff --git a/VERSION b/VERSION index 607cd5943..a1e6d1155 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.0.0-beta.1 +7.0.0.0.rc1 From fde9764508014f3558725a0741ac4a828d2bb7df Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 18 Jan 2022 20:26:38 +0000 Subject: [PATCH 1052/1412] Fix YAML load for ruby 3.1. See https://github.com/ruby/psych/pull/487 for more information. --- test/cases/coerced_tests.rb | 3 ++- test/cases/rake_test_sqlserver.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a76fa354b..29f15edb3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1486,7 +1486,8 @@ 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 author = Author.select("authors.*, 5 as posts_count").first - dumped = YAML.load(YAML.dump(author)) + dumped_author = YAML.dump(author) + dumped = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(dumped_author) : YAML.load(dumped_author) assert_equal 5, author.posts_count assert_equal 5, dumped.posts_count end diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 1195713d2..0cecf65e9 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -176,7 +176,8 @@ class SQLServerRakeSchemaCacheDumpLoadTest < SQLServerRakeTest it "dumps schema cache with SQL Server metadata" do quietly { db_tasks.dump_schema_cache connection, filename } - schema_cache = YAML.load(File.read(filename)) + filedata = File.read(filename) + schema_cache = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(filedata) : YAML.load(filedata) col_id, col_name = connection.schema_cache.columns("users") From f21788a7c5c17dee52c3dc654ffdff8a7a570985 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 18 Jan 2022 21:39:53 +0000 Subject: [PATCH 1053/1412] Release v7.0.0.0.rc1 --- CHANGELOG.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d0d897f4..675f0f5ec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ -## Unreleased +## v7.0.0.0.rc1 -#### Fixed - -... +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-1-stable...v7.0.0.0.rc1) #### Changed From e2cb6d331ee6b7990403e4a930dd340b95938ab6 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Mon, 7 Feb 2022 19:56:43 +0000 Subject: [PATCH 1054/1412] Update README.md Sunset 5.2.x series --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9c9c33972..ced3b9487 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.0.0.rc1` | `7.0.x` | [unreleased](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.0.0.rc1` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | | `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | | `4.1.8` | `4.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | From e571457e2fd1bd26bb1f690bf5802a286f076e02 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 9 Feb 2022 10:35:00 +0000 Subject: [PATCH 1055/1412] Dump the precision for datetime columns following the new defaults --- CHANGELOG.md | 6 ++++++ test/cases/schema_dumper_test_sqlserver.rb | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 675f0f5ec..200ccf0ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Changed + +- [#1004](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1004) Dump the precision for datetime columns following the new defaults. + ## v7.0.0.0.rc1 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-1-stable...v7.0.0.0.rc1) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 08d681f52..2efc88233 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -79,7 +79,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime_col, type: "datetime", limit: nil, precision: 6, scale: nil, default: nil + assert_line :datetime_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil From e2271bd3fb9ad367832320c952698221609e59eb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 8 Feb 2022 16:31:57 +0000 Subject: [PATCH 1056/1412] Fix support for different index types --- CHANGELOG.md | 4 ++ .../sqlserver/schema_creation.rb | 20 ++++--- test/cases/active_schema_test_sqlserver.rb | 55 +++++++++++++++++++ 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 test/cases/active_schema_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 200ccf0ef..9e5c033e4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +#### Fixed + +- [#1002](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1002) Fix support for index types + #### Changed - [#1004](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1004) Dump the precision for datetime columns following the new defaults. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 0b70b8d50..95710ae8e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -34,17 +34,19 @@ def visit_TableDefinition(o) end def visit_CreateIndexDefinition(o) - if_not_exists = o.if_not_exists - - o.if_not_exists = false - - sql = super + index = o.index - if if_not_exists - sql = "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '#{o.index.name}') #{sql}" - end + sql = [] + sql << "IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = '#{o.index.name}')" if o.if_not_exists + sql << "CREATE" + sql << "UNIQUE" if index.unique + sql << index.type.upcase if index.type + sql << "INDEX" + sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}" + sql << "(#{quoted_columns(index)})" + sql << "WHERE #{index.where}" if index.where - sql + sql.join(" ") end def add_column_options!(sql, options) diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb new file mode 100644 index 000000000..6d38f5820 --- /dev/null +++ b/test/cases/active_schema_test_sqlserver.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class ActiveSchemaTestSQLServer < ActiveRecord::TestCase + before do + connection.create_table :schema_test_table, force: true, id: false do |t| + t.column :foo, :string, limit: 100 + t.column :state, :string + end + end + + after do + connection.drop_table :schema_test_table rescue nil + end + + it 'default index' do + assert_sql('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_sql('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 + assert_sql("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 + assert_sql("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 + + describe "index types" do + it 'clustered index' do + assert_sql('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_sql('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 +end From e7e5b933114baee48383721134e67685d6c7cf11 Mon Sep 17 00:00:00 2001 From: Wanderson Policarpo Date: Tue, 22 Feb 2022 21:20:46 +0000 Subject: [PATCH 1057/1412] Release v7.0.0.0 --- CHANGELOG.md | 4 +++- README.md | 2 +- VERSION | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e5c033e4..8429b59fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## Unreleased +## v7.0.0.0 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0.rc1...v7.0.0.0) #### Fixed diff --git a/README.md b/README.md index ced3b9487..6c3095af7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | | --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.0.0.rc1` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.0.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | diff --git a/VERSION b/VERSION index a1e6d1155..79d0fb2d2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.0.0.rc1 +7.0.0.0 From f8d39e4ce9ee3a946109c533710dddff9723380b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 17 Aug 2022 16:40:47 +0100 Subject: [PATCH 1058/1412] Module containing silence_warnings needs to be required --- .../sqlserver/core_ext/explain_subscriber.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index 712465543..f151cad74 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +require "active_support/core_ext/kernel/reporting" + ActiveSupport.on_load(:active_record) do silence_warnings do # Already defined in Rails From 82b378f3d68a1f6de61bf8e76f46f6ee2cfd0941 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Aug 2022 12:06:46 +0100 Subject: [PATCH 1059/1412] Freeze the SQL sent to instrumentation --- CHANGELOG.md | 4 ++++ .../connection_adapters/sqlserver/database_statements.rb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8429b59fc..b0bd28b11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +- [#1021](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1021) Freeze the SQL sent to instrumentation. + ## v7.0.0.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0.rc1...v7.0.0.0) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 10f19777e..7fb1a762f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -360,7 +360,7 @@ def sp_executesql_sql(sql, types, params, name) sql = "EXEC sp_executesql #{quote(sql)}" sql += ", #{types}, #{params}" unless params.empty? end - sql + sql.freeze end def raw_connection_do(sql) From a4b56e6081449a77af09111dbb3921819c3e059a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Aug 2022 10:41:36 +0100 Subject: [PATCH 1060/1412] Use SQL Server method to generate UUID --- test/cases/coerced_tests.rb | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 29f15edb3..82fd1084f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1395,7 +1395,7 @@ def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced assert_match %r{t.decimal\s+"atoms_in_universe",\s+precision: 38}, output end - # This is a poorly written test and really does not catch the bottom'ness it is meant too. Ours throw it off. + # This is a poorly written test and really does not catch the bottom'ness it is meant to. Ours throw it off. coerce_tests! :test_foreign_keys_are_dumped_at_the_bottom_to_circumvent_dependency_issues # Fall through false positive with no filter. @@ -1416,6 +1416,35 @@ def test_schema_dump_includes_decimal_options_coerced class SchemaDumperDefaultsTest < ActiveRecord::TestCase # These date formats do not match ours. We got these covered in our dumper tests. coerce_tests! :test_schema_dump_defaults_with_universally_supported_types + + # SQL Server uses different method to generate a UUID than Rails test uses. Reimplemented the + # test in 'SchemaDumperDefaultsCoerceTest'. + coerce_tests! :test_schema_dump_with_text_column +end + +class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + setup do + @connection = ActiveRecord::Base.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.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.text :text_with_default, default: "John' Doe" + t.text :uuid, default: -> { "newid()" } + end + end + + def test_schema_dump_with_text_column_coerced + output = dump_table_schema("dump_defaults") + + assert_match %r{t\.text\s+"text_with_default",.*?default: "John' Doe"}, output + assert_match %r{t\.text\s+"uuid",.*?default: -> \{ "newid\(\)" \}}, output + end end class TestAdapterWithInvalidConnection < ActiveRecord::TestCase From e70e6c2d4336465cccf5bab28ff950b3c56f29f1 Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Wed, 28 Sep 2022 14:33:47 -0500 Subject: [PATCH 1061/1412] Add dbconsole support to adapter Adds dbconsole method to be used when invoking bin/rails dbconsole. Co-authored-by: Paarth Madan --- .../connection_adapters/sqlserver_adapter.rb | 17 +++++++++++++++++ lib/active_record/sqlserver_base.rb | 8 ++++++-- test/cases/dbconsole.rb | 19 +++++++++++++++++++ 3 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 test/cases/dbconsole.rb diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 28ebe97cf..f27603b09 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -59,6 +59,23 @@ class SQLServerAdapter < AbstractAdapter self.exclude_output_inserted_table_names = Concurrent::Map.new { false } class << self + 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] + + if sqlserver_config[:host] + host_arg = +"tcp:#{sqlserver_config[:host]}" + host_arg << ",#{sqlserver_config[:port]}" if sqlserver_config[:port] + args += ["-S", host_arg] + end + + find_cmd_and_exec("sqlcmd", *args) + end + def new_client(config) case config[:mode] when :dblib diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 0040cde7a..b8d74cc03 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -2,13 +2,17 @@ module ActiveRecord module ConnectionHandling + def sqlserver_connection_class + ConnectionAdapters::SQLServerAdapter + end + def sqlserver_connection(config) #:nodoc: config = config.symbolize_keys config.reverse_merge!(mode: :dblib) config[:mode] = config[:mode].to_s.downcase.underscore.to_sym - ConnectionAdapters::SQLServerAdapter.new( - ConnectionAdapters::SQLServerAdapter.new_client(config), + sqlserver_connection_class.new( + sqlserver_connection_class.new_client(config), logger, nil, config diff --git a/test/cases/dbconsole.rb b/test/cases/dbconsole.rb new file mode 100644 index 000000000..c23e53f0e --- /dev/null +++ b/test/cases/dbconsole.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class DbConsole < ActiveRecord::TestCase + subject { ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter } + + it "uses sqlplus to connect to database" do + subject.expects(:find_cmd_and_exec).with("sqlcmd", "-d", "db", "-U", "user", "-P", "secret", "-S", "tcp:localhost,1433") + + config = make_db_config(adapter: "sqlserver", database: "db", username: "user", password: "secret", host: "localhost", port: 1433) + + subject.dbconsole(config) + end + + private + + def make_db_config(config) + ActiveRecord::DatabaseConfigurations::HashConfig.new("test", "primary", config) + end +end From f5dfcedde38d5f210e3da624aa8d0ae30686188a Mon Sep 17 00:00:00 2001 From: DarkGuySM <78262720+DarkGuySM@users.noreply.github.com> Date: Sun, 2 Oct 2022 15:01:18 +0530 Subject: [PATCH 1062/1412] Update README.md Corrected spelling mistake. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6c3095af7..6b55b4244 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ For older versions, please check their stable branches. #### Native Data Type Support -We support every data type supported by FreeTDS. All simplified Rails types in migrations will coorespond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. +We support every data type supported by FreeTDS. All simplified Rails types in migrations will correspond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version `7.3` with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to `7.3`. The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. From aa7c157ffb9adbef7a159ae1df1a2298daffcacd Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Mon, 3 Oct 2022 13:58:21 -0500 Subject: [PATCH 1063/1412] Rename sqlserver_connection_class to sqlserver_adapter_class --- lib/active_record/sqlserver_base.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index b8d74cc03..4644e93e3 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -2,7 +2,7 @@ module ActiveRecord module ConnectionHandling - def sqlserver_connection_class + def sqlserver_adapter_class ConnectionAdapters::SQLServerAdapter end @@ -11,8 +11,8 @@ def sqlserver_connection(config) #:nodoc: config.reverse_merge!(mode: :dblib) config[:mode] = config[:mode].to_s.downcase.underscore.to_sym - sqlserver_connection_class.new( - sqlserver_connection_class.new_client(config), + sqlserver_adapter_class.new( + sqlserver_adapter_class.new_client(config), logger, nil, config From e9f5a4243fae9ccbfa2fab8a5fb13c272b3710b9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 Aug 2022 10:58:00 +0100 Subject: [PATCH 1064/1412] Lock minitest to 5.15 --- Gemfile | 1 + 1 file changed, 1 insertion(+) diff --git a/Gemfile b/Gemfile index 87cba16a1..8cf772ba3 100644 --- a/Gemfile +++ b/Gemfile @@ -11,6 +11,7 @@ gem "pg", ">= 0.18.0" gem "sqlite3", "~> 1.4" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" +gem "minitest", ">= 5.15.0", "< 5.16" if ENV["RAILS_SOURCE"] gemspec path: ENV["RAILS_SOURCE"] From 5c7482447720744d5d0498cc382d36cb77d8b920 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 6 Oct 2022 10:00:29 +0100 Subject: [PATCH 1065/1412] Bump Rails version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 79d0fb2d2..4489f5a6d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.0.0 +7.0.4 From 1548e92c8168c998d5e872b992a837c340517ce4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 19 Oct 2022 15:29:15 +0100 Subject: [PATCH 1066/1412] Coerce test so index removed --- test/cases/coerced_tests.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 82fd1084f..3ec7177cd 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2086,6 +2086,17 @@ def test_in_order_of_with_enums_values_coerced Book.connection.add_index(:books, [:author_id, :name], unique: true) end + # 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_string_column + def test_in_order_of_with_string_column_coerced + Book.connection.remove_index(:books, column: [:author_id, :name]) + + original_test_in_order_of_with_string_column + ensure + Book.where(author_id: nil, name: nil).delete_all + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end + # 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_keys def test_in_order_of_with_enums_keys_coerced From c3e583b217ac66644939646f0d71b5f5359b4efe Mon Sep 17 00:00:00 2001 From: Frederik Spang Thomsen Date: Wed, 19 Oct 2022 19:35:16 +0200 Subject: [PATCH 1067/1412] Handle VIEW defined in other database Fixes #1027 --- .../connection_adapters/sqlserver/schema_statements.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c9ad8a0ad..3ed8aee1b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -580,7 +580,8 @@ def view_information(table_name) @view_information ||= {} @view_information[table_name] ||= begin identifier = SQLServer::Utils.extract_identifiers(table_name) - view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA" + 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" if view_info view_info = view_info.with_indifferent_access if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000 From d3548bdfc90808a68a9b3f2b8fc1e8389c7f9f7e Mon Sep 17 00:00:00 2001 From: Frederik Spang Thomsen Date: Thu, 1 Dec 2022 12:58:37 +0100 Subject: [PATCH 1068/1412] Add a test --- test/cases/adapter_test_sqlserver.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 91337243b..ddb44ea43 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -373,6 +373,12 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) end + it "allows connection#view_information to work with qualified object names" do + view_info = connection.send(:view_information, "[activerecord_unittest].[dbo].[sst_customers_view]") + assert_equal("sst_customers_view", view_info["TABLE_NAME"]) + assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) + end + it "allow the connection#view_table_name method to return true table_name for the view" do assert_equal "customers", connection.send(:view_table_name, "sst_customers_view") assert_equal "topics", connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." From b666c891371755f54f79c6238acde445f0d8bcff Mon Sep 17 00:00:00 2001 From: Frederik Spang Thomsen Date: Mon, 5 Dec 2022 12:28:18 +0100 Subject: [PATCH 1069/1412] Add specs --- test/cases/adapter_test_sqlserver.rb | 11 +++++++++++ test/models/sqlserver/uuid_view.rb | 5 +++++ test/schema/sqlserver_specific_schema.rb | 7 +++++++ 3 files changed, 23 insertions(+) create mode 100644 test/models/sqlserver/uuid_view.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index ddb44ea43..00f4f3da6 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -379,11 +379,22 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) end + it "allows connection#view_information to work across databases when using qualified object names" do + # College is defined in activerecord_unittest2 database. + view_info = College.connection.send(:view_information, "[activerecord_unittest].[dbo].[sst_customers_view]") + assert_equal("sst_customers_view", view_info["TABLE_NAME"]) + assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) + end + it "allow the connection#view_table_name method to return true table_name for the view" do assert_equal "customers", connection.send(:view_table_name, "sst_customers_view") assert_equal "topics", connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." end + it "allow the connection#view_table_name method to return true table_name for the view for other connections" do + assert_equal "customers", College.connection.send(:view_table_name, "[activerecord_unittest].[dbo].[sst_customers_view]") + assert_equal "topics", College.connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." + end # With same column names it "have matching column objects" do diff --git a/test/models/sqlserver/uuid_view.rb b/test/models/sqlserver/uuid_view.rb new file mode 100644 index 000000000..10059a706 --- /dev/null +++ b/test/models/sqlserver/uuid_view.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class SSTestUuidView < ActiveRecord::Base + self.table_name = "[activerecord_unittest2].[test2].[sst_uuid_view]" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 3ef44fac8..90efc4feb 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -312,4 +312,11 @@ CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two) ); COMPOSITE_WITH_IDENTITY + + # Separate db view. + execute "DROP VIEW IF EXISTS sst_cross_college_view" + execute <<-SPECIFIC_SCHEMA_VIEW + CREATE VIEW sst_cross_college_view AS + SELECT * FROM activerecord_unittest2.dbo.colleges + SPECIFIC_SCHEMA_VIEW end From d628941c5c4c7a06f4f9352b80ceb2aa611ef7d6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 12 Dec 2022 15:54:39 +0000 Subject: [PATCH 1070/1412] Added changelog. Removed unnecessary changes. --- CHANGELOG.md | 6 ++++++ test/models/sqlserver/uuid_view.rb | 5 ----- test/schema/sqlserver_specific_schema.rb | 7 ------- 3 files changed, 6 insertions(+), 12 deletions(-) delete mode 100644 test/models/sqlserver/uuid_view.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index b0bd28b11..b7e6b328f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ ## Unreleased +#### Fixed + +- [#1029](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1029) Handle views defined in other databases. + +#### Changed + - [#1021](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1021) Freeze the SQL sent to instrumentation. ## v7.0.0.0 diff --git a/test/models/sqlserver/uuid_view.rb b/test/models/sqlserver/uuid_view.rb deleted file mode 100644 index 10059a706..000000000 --- a/test/models/sqlserver/uuid_view.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -class SSTestUuidView < ActiveRecord::Base - self.table_name = "[activerecord_unittest2].[test2].[sst_uuid_view]" -end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 90efc4feb..3ef44fac8 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -312,11 +312,4 @@ CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two) ); COMPOSITE_WITH_IDENTITY - - # Separate db view. - execute "DROP VIEW IF EXISTS sst_cross_college_view" - execute <<-SPECIFIC_SCHEMA_VIEW - CREATE VIEW sst_cross_college_view AS - SELECT * FROM activerecord_unittest2.dbo.colleges - SPECIFIC_SCHEMA_VIEW end From 3af04079b147096345bd55a113d1f0665b499107 Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Mon, 27 Feb 2023 19:19:25 -0300 Subject: [PATCH 1071/1412] support proc default values in ruby 3.2 Fixes: ``` NoMethodError: undefined method `=~' for # { "NEWSEQUENTIALID()" } + SSTestUuid.reset_column_information + column = SSTestUuid.columns_hash["thingy"] + _(column.default_function).must_equal "newsequentialid()" + end end From 47deb951eba996cf535a0a7c239a26dbaa9c3238 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Mar 2023 13:58:34 +0000 Subject: [PATCH 1072/1412] Added #1020 to the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 89200a5be..a0531ae2b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#1029](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1029) Handle views defined in other databases. - [#1033](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1033) Support proc default values in ruby 3.2. +- [#1020](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1020) Support using adapter as a stand-alone gem. #### Changed From 5d7fadc68486ceedd05210f4ee137f2870e0fa6f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 7 Mar 2023 14:49:52 +0000 Subject: [PATCH 1073/1412] Fix hook method that allows custom connection configuration --- CHANGELOG.md | 2 +- README.md | 2 +- .../connection_adapters/sqlserver_adapter.rb | 25 +++++++++++-------- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a0531ae2b..ebfbd1f39 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - [#1029](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1029) Handle views defined in other databases. - [#1033](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1033) Support proc default values in ruby 3.2. - [#1020](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1020) Support using adapter as a stand-alone gem. - +- [#1039](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1039) Fix hook method that allows custom connection configuration. #### Changed diff --git a/README.md b/README.md index 6b55b4244..960bab284 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ end #### Configure Connection -We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just override the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. +We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just implement the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. ```ruby module ActiveRecord diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f27603b09..a8cb14337 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -157,7 +157,7 @@ def config_encoding(config) def initialize(connection, logger, _connection_options, config) super(connection, logger, config) @connection_options = config - configure_connection + perform_connection_configuration end # === Abstract Adapter ========================================== # @@ -318,14 +318,6 @@ def reset! do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" end - def configure_connection - @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first - @version_year = version_year - - initialize_dateformatter - use_database - end - # === Abstract Adapter (Misc Support) =========================== # def tables_with_referential_integrity @@ -557,7 +549,20 @@ def sqlserver_version def connect @connection = self.class.new_client(@connection_options) - configure_connection + perform_connection_configuration + end + + def perform_connection_configuration + configure_connection_defaults + configure_connection if self.respond_to?(:configure_connection) + end + + def configure_connection_defaults + @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first + @version_year = version_year + + initialize_dateformatter + use_database end end end From c80cc2420a6fe0be8f4c4688f47dab09703522e4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Mar 2023 16:42:12 +0000 Subject: [PATCH 1074/1412] Revert change to VERSION VERSION contains the version of the adapter. It was incorrectly changed to contain a Rails version. --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 4489f5a6d..79d0fb2d2 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.4 +7.0.0.0 From 5213920f1087cce611595f4f5e9a609a0caff06a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 Mar 2023 15:36:27 +0000 Subject: [PATCH 1075/1412] Release v7.0.1.0 --- CHANGELOG.md | 4 +++- README.md | 4 ++-- VERSION | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebfbd1f39..7cbef5eac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,6 @@ -## Unreleased +## v7.0.1.0 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0...v7.0.1.0) #### Fixed diff --git a/README.md b/README.md index 960bab284..97508081a 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. | Adapter Version | Rails Version | Support | -| --------------- | ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.0.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +|-----------------| ------------- | ------------------------------------------------------------------------------------------- | +| `7.0.1.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | diff --git a/VERSION b/VERSION index 79d0fb2d2..8635fef95 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.0.0 +7.0.1.0 From 800334641ca8d464db4d633109ef818bc8402f40 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 Mar 2023 16:15:29 +0000 Subject: [PATCH 1076/1412] Updated CI rubies --- .github/workflows/ci.yml | 6 +++--- Dockerfile.ci | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c11878541..687823942 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: fail-fast: false matrix: ruby: - - 2.7.5 - - 3.0.3 - - 3.1.0 + - 2.7.7 + - 3.1.3 + - 3.2.1 steps: - name: Checkout code diff --git a/Dockerfile.ci b/Dockerfile.ci index e616cff26..9a031dfc2 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,6 +1,6 @@ -ARG TARGET_VERSION=2.6.3 +ARG TARGET_VERSION=3.2.1 -FROM railssqlserver/activerecord-sqlserver-adapter:${TARGET_VERSION} +FROM aidanharan/activerecord-sqlserver-adapter:${TARGET_VERSION} ENV WORKDIR /activerecord-sqlserver-adapter From e70705c7b0e4b2767294661b4ea2931405750fb2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 21 Mar 2023 16:20:54 +0000 Subject: [PATCH 1077/1412] Use Github container registry instead of Docker Hub --- Dockerfile.ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 9a031dfc2..0ded95afd 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -1,6 +1,6 @@ ARG TARGET_VERSION=3.2.1 -FROM aidanharan/activerecord-sqlserver-adapter:${TARGET_VERSION} +FROM ghcr.io/rails-sqlserver/activerecord-sqlserver-adapter:${TARGET_VERSION} ENV WORKDIR /activerecord-sqlserver-adapter From 9255377201ce419d5befd78750aab8832fa08fcb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 Mar 2023 16:05:23 +0000 Subject: [PATCH 1078/1412] Use Github container registry instead of Docker Hub --- docker-compose.ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 25c81cbd9..cbc842619 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,7 +1,7 @@ version: "2.2" services: sqlserver: - image: metaskills/mssql-server-linux-rails + image: ghcr.io/rails-sqlserver/mssql-server-linux-rails ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver From b382f0078d16af1a1a3b1704f068ffa359aae614 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 18 Apr 2023 11:43:58 +0100 Subject: [PATCH 1079/1412] Fix for inserting records into non-dbo schema table (#1049) --- CHANGELOG.md | 4 ++++ .../sqlserver/database_statements.rb | 14 ++++++-------- test/cases/adapter_test_sqlserver.rb | 2 +- test/cases/schema_test_sqlserver.rb | 7 +++++++ test/models/sqlserver/alien.rb | 5 +++++ test/schema/sqlserver_specific_schema.rb | 7 +++++++ 6 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 test/models/sqlserver/alien.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cbef5eac..e99bd9a92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## Unreleased + +- [#1049](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1049) Fix for inserting records into non-dbo schema table. + ## v7.0.1.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0...v7.0.1.0) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7fb1a762f..feeabcf1f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -397,14 +397,12 @@ def exec_insert_requires_identity?(sql, pk, binds) end def query_requires_identity_insert?(sql) - if insert_sql?(sql) - table_name = get_table_name(sql) - id_column = identity_columns(table_name).first - # id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false - id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? table_name : false - else - false - end + return false unless insert_sql?(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).object : false end def insert_sql?(sql) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 00f4f3da6..e8ec9b8b5 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -197,7 +197,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" end - it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do + it "return unquoted 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) diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 40379cd51..4135172d4 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -21,6 +21,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it "have only one identity column" do columns = connection.columns("test.sst_schema_identity") + assert_equal 2, columns.size assert_equal 1, columns.select { |c| c.is_identity? }.size end @@ -29,6 +30,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase test_columns = connection.columns("test.sst_schema_columns") dbo_columns = connection.columns("dbo.sst_schema_columns") columns = connection.columns("sst_schema_columns") # This returns table from dbo schema + assert_equal 7, test_columns.size assert_equal 2, dbo_columns.size assert_equal 2, columns.size @@ -39,10 +41,15 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do columns = connection.columns("test.sst_schema_columns") + assert_equal 255, columns.find { |c| c.name == "name" }.limit assert_equal 1000, columns.find { |c| c.name == "description" }.limit assert_equal 255, columns.find { |c| c.name == "n_name" }.limit assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit end + + it "creates new record" do + Alien.create!(name: 'Trisolarans') + end end end diff --git a/test/models/sqlserver/alien.rb b/test/models/sqlserver/alien.rb new file mode 100644 index 000000000..a41c51888 --- /dev/null +++ b/test/models/sqlserver/alien.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class Alien < ActiveRecord::Base + self.table_name = "test.aliens" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 3ef44fac8..aac37c73d 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -312,4 +312,11 @@ CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two) ); COMPOSITE_WITH_IDENTITY + + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'aliens' and TABLE_SCHEMA = 'test') DROP TABLE test.aliens" + execute <<-TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL + CREATE TABLE test.aliens( + name varchar(255) + ) + TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL end From 61e3c0d6578ec49ea6fa91dcfedba90f7b8d5d34 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 18 Apr 2023 11:47:01 +0100 Subject: [PATCH 1080/1412] Prepare for release 7.0.2.0 --- CHANGELOG.md | 6 +++++- VERSION | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e99bd9a92..1056bef53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ -## Unreleased +## v7.0.2.0 + +[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0) + +#### Fixed - [#1049](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1049) Fix for inserting records into non-dbo schema table. diff --git a/VERSION b/VERSION index 8635fef95..70caf8820 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.1.0 +7.0.2.0 From 98e57a65314ce5002925b626aae514331127f7bc Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 20 Apr 2023 15:56:13 +0100 Subject: [PATCH 1081/1412] Ignore casing of VALUES clause when inserting records using SQL (#1052) --- CHANGELOG.md | 6 ++++++ .../sqlserver/database_statements.rb | 6 +++--- test/cases/adapter_test_sqlserver.rb | 12 ++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1056bef53..295d541d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL + ## v7.0.2.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index feeabcf1f..c9e552119 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -278,11 +278,11 @@ def sql_for_insert(sql, pk, binds) id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted <<~SQL.squish DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); - #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} + #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable SQL else - sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}" + sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk}" end else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" @@ -392,7 +392,7 @@ def exclude_output_inserted_table_name?(table_name, sql) self.class.exclude_output_inserted_table_names[table_name] end - def exec_insert_requires_identity?(sql, pk, binds) + def exec_insert_requires_identity?(sql, _pk, _binds) query_requires_identity_insert?(sql) end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index e8ec9b8b5..bc30ca71e 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -532,4 +532,16 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end end + + describe "exec_insert" 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')") + + assert_equal first_insert.rows.map(&:first), [100, 102] + assert_equal second_insert.rows.map(&:first), [113, 114] + end + end + end end From 28c38ea299e9cd2337cd4cbb417907fb59052745 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 24 Apr 2023 09:39:38 +0100 Subject: [PATCH 1082/1412] Fix insertion of records to non-default schema table using raw SQL (#1053) --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 2 +- .../connection_adapters/sqlserver/utils.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 40 +++++++++++++++---- test/cases/schema_test_sqlserver.rb | 4 -- test/cases/utils_test_sqlserver.rb | 4 +- test/schema/sqlserver_specific_schema.rb | 1 + 7 files changed, 38 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 295d541d9..ff0a1d0d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL +- [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL ## v7.0.2.0 diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index c9e552119..5e154b6b2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -402,7 +402,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).object : 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) diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index 186593e65..d5f87097e 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -9,7 +9,7 @@ module Utils QUOTED_STRING_PREFIX = "N" # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL - # Inspiried from Rails PostgreSQL::Name adapter object in their own Utils. + # Inspired from Rails PostgreSQL::Name adapter object in their own Utils. # class Name SEPARATOR = "." diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index bc30ca71e..d31804403 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -193,17 +193,31 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_unquoted = "INSERT INTO funny_jokes (id, name) VALUES(420, 'Knock knock')" @identity_insert_sql_unordered = "INSERT INTO [funny_jokes] ([name],[id]) VALUES('Knock knock',420)" @identity_insert_sql_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'" - @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'" + @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO funny_jokes (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'" @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" + + @identity_insert_sql_non_dbo = "INSERT INTO [test].[aliens] ([id],[name]) VALUES(420,'Mork')" + @identity_insert_sql_non_dbo_unquoted = "INSERT INTO test.aliens ([id],[name]) VALUES(420,'Mork')" + @identity_insert_sql_non_dbo_unordered = "INSERT INTO [test].[aliens] ([name],[id]) VALUES('Mork',420)" + @identity_insert_sql_non_dbo_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" + @identity_insert_sql_non_dbo_unquoted_sp = "EXEC sp_executesql N'INSERT INTO test.aliens (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" + @identity_insert_sql_non_dbo_unordered_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Mork', @1 = 420" end - it "return unquoted 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) + 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 "[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) + assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered) + assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp) + assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp) + assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp) end it "return false to #query_requires_identity_insert? for normal SQL" do @@ -533,6 +547,16 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end + describe 'table is in non-dbo schema' do + it "records can be created successfully" do + Alien.create!(name: 'Trisolarans') + end + + it 'records can be inserted using SQL' do + Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')") + end + end + describe "exec_insert" do it 'values clause should be case-insensitive' do assert_difference("Post.count", 4) do diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 4135172d4..fc119fe27 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -47,9 +47,5 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal 255, columns.find { |c| c.name == "n_name" }.limit assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit end - - it "creates new record" do - Alien.create!(name: 'Trisolarans') - end end end diff --git a/test/cases/utils_test_sqlserver.rb b/test/cases/utils_test_sqlserver.rb index 9ede9ab2b..4674e061f 100644 --- a/test/cases/utils_test_sqlserver.rb +++ b/test/cases/utils_test_sqlserver.rb @@ -77,7 +77,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase _(extract_identifiers(" ").object).must_be_nil end - it "has a #quoted that returns a fully quoted name with all identifiers as orginially passed in" do + it "has a #quoted that returns a fully quoted name with all identifiers as originally passed in" do _(extract_identifiers("object").quoted).must_equal "[object]" _(extract_identifiers("server.database..object").quoted).must_equal "[server].[database]..[object]" _(extract_identifiers("[server]...[object]").quoted).must_equal "[server]...[object]" @@ -92,7 +92,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase _(extract_identifiers("[obj.name].[foo]").quoted).must_equal "[obj.name].[foo]" end - it "should indicate if a name is fully qualitified" do + it "should indicate if a name is fully qualified" do _(extract_identifiers("object").fully_qualified?).must_equal false _(extract_identifiers("schema.object").fully_qualified?).must_equal false _(extract_identifiers("database.schema.object").fully_qualified?).must_equal false diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index aac37c73d..0742d4624 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -316,6 +316,7 @@ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'aliens' and TABLE_SCHEMA = 'test') DROP TABLE test.aliens" execute <<-TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL CREATE TABLE test.aliens( + id int IDENTITY NOT NULL primary key, name varchar(255) ) TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL From c52a739bd325f313deaded58624cc9bea3187d0c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 May 2023 15:01:56 +0100 Subject: [PATCH 1083/1412] Updated for v6.0.3 release --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 97508081a..83c88f398 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Interested in older versions? We follow a rational versioning policy that tracks |-----------------| ------------- | ------------------------------------------------------------------------------------------- | | `7.0.1.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.2` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `6.0.3` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | | `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | From 288ceead23d81c1c6f3e40cc9135e2ee4a1499f7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 7 Jun 2023 16:46:25 +0100 Subject: [PATCH 1084/1412] Fix tests to handle companies status column (#1060) --- test/cases/coerced_tests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3ec7177cd..b23df1126 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -450,7 +450,7 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql_coerce FROM companies INNER JOIN accounts ON companies.id = accounts.firm_id WHERE companies.id = ? - GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description + GROUP BY companies.id, companies.type, companies.firm_id, companies.firm_name, companies.name, companies.client_of, companies.rating, companies.account_id, companies.description, companies.status ORDER BY companies.id OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY SQL @@ -476,7 +476,7 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced .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) + .group(:id, :type, :firm_id, :firm_name, :name, :client_of, :rating, :account_id, :description, :status) .take! # all the DependentFirm attributes should be present From e714ae3e57677ebf3a1ce67d641d21292d6e4973 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 8 Jun 2023 14:31:51 +0100 Subject: [PATCH 1085/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 83c88f398..ce2df4a85 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | |-----------------| ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.1.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.2.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | From 506ee4b6bc3bb52c72f25b0a24b1f4be6528289a Mon Sep 17 00:00:00 2001 From: Matias Grunberg Date: Fri, 9 Jun 2023 06:00:20 -0300 Subject: [PATCH 1086/1412] Fix enum defined on string columns (#1059) Fixes https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/1042 varchar, varchar_max and text sql server types extends from char (`ActiveRecord::ConnectionAdapters::SQLServer::Type:Char`). `ActiveRecord::ConnectionAdapters::SQLServer::Type::Char.serialize` return an `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` instance. `Data` defines ``` def eql?(other) self.class == other.class && value == other.value end ``` Let's imagine the following ActiveRecord definition ``` class MyRecord < ActiveRecord::Base enum release: { alpha: "A", beta: "B" } end ``` When we call `my_record.alpha?` it compares `release` serialized value with the string "A". It will be always false because `Data` and `String` clases are different. This commit reimplements `eql?` to deal with string arguments. --- CHANGELOG.md | 1 + .../sqlserver/type/data.rb | 6 +++ test/cases/enum_test_sqlserver.rb | 49 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 test/cases/enum_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index ff0a1d0d3..a45020f43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL - [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL +- [#1059](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1059) Fix enums defined on string columns ## v7.0.2.0 diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index bc3f82f8d..1cf241450 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -27,6 +27,12 @@ def inspect end def eql?(other) + # Support comparing `Type::Char`, `Type::Varchar` and `VarcharMax` with strings. + # This happens when we use enum with string columns. + if other.is_a?(::String) + return type.is_a?(ActiveRecord::ConnectionAdapters::SQLServer::Type::String) && value == other + end + self.class == other.class && value == other.value end alias :== :eql? diff --git a/test/cases/enum_test_sqlserver.rb b/test/cases/enum_test_sqlserver.rb new file mode 100644 index 000000000..210a4aa7f --- /dev/null +++ b/test/cases/enum_test_sqlserver.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +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. + %w[char_10 varchar_50 varchar_max text nchar_10 nvarchar_50 nvarchar_max ntext].each do |col_name| + describe "support #{col_name} enums" do + let(:klass) do + Class.new(ActiveRecord::Base) do + self.table_name = 'sst_datatypes' + + 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') + 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(:alpha) + assert_equal 'B', type.serialize(:beta) + end + + it "update by declaration" do + r = klass.new + + r.alpha! + assert_predicate r, :alpha? + + r.beta! + assert_not_predicate r, :alpha? + assert_predicate r, :beta? + end + end + end +end From ac95da9211f58ee1be385056e81027b994626be6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 12 Jun 2023 15:36:41 +0100 Subject: [PATCH 1087/1412] Set precision 6 by default for timestamps (#1057) --- CHANGELOG.md | 4 + .../sqlserver/schema_statements.rb | 2 +- .../sqlserver/table_definition.rb | 9 +- test/cases/coerced_tests.rb | 19 +- test/cases/schema_dumper_test_sqlserver.rb | 222 ++++++++++-------- test/schema/sqlserver_specific_schema.rb | 5 +- 6 files changed, 152 insertions(+), 109 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a45020f43..a97f7e9d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ - [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL - [#1059](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1059) Fix enums defined on string columns +#### Changed + +- [#1057](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1057) Set precision 6 by default for timestamps. + ## v7.0.2.0 [Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3ed8aee1b..34b112392 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -346,7 +346,7 @@ def initialize_native_database_types datetime2: { name: "datetime2" }, datetimeoffset: { name: "datetimeoffset" }, smalldatetime: { name: "smalldatetime" }, - timestamp: { name: "datetime" }, + timestamp: { name: "datetime2(6)" }, time: { name: "time" }, char: { name: "char" }, varchar: { name: "varchar", limit: 8000 }, diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 6a0faa33a..2740aad8d 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -101,11 +101,16 @@ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition def new_column_definition(name, type, **options) case type - when :datetime - type = :datetime2 if options[:precision] + when :datetime, :timestamp + # If no precision then default it to 6. + options[:precision] = 6 unless options.key?(:precision) + + # If there is precision then column must be of type 'datetime2'. + type = :datetime2 unless options[:precision].nil? when :primary_key options[:is_identity] = true end + super end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b23df1126..3ae18245a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -543,7 +543,7 @@ def test_create_table_with_defaults_coerce assert_equal "hello", five.default end - # Rails adds precision 6 by default, sql server uses datetime2 for datetimes with precision + # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision. coerce_tests! :test_add_column_with_postgresql_datetime_type def test_add_column_with_postgresql_datetime_type_coerced connection.create_table :testings do |t| @@ -556,7 +556,7 @@ def test_add_column_with_postgresql_datetime_type_coerced assert_equal "datetime2(6)", column.sql_type end - # timestamp is datetime with default limit + # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision. coerce_tests! :test_change_column_with_timestamp_type def test_change_column_with_timestamp_type_coerced connection.create_table :testings do |t| @@ -568,7 +568,20 @@ def test_change_column_with_timestamp_type_coerced column = connection.columns(:testings).find { |c| c.name == "foo" } assert_equal :datetime, column.type - assert_equal "datetime", column.sql_type + assert_equal "datetime2(6)", column.sql_type + end + + # Use precision 6 by default for datetime/timestamp columns. SQL Server uses `datetime2` for date-times with precision. + coerce_tests! :test_add_column_with_timestamp_type + def test_add_column_with_timestamp_type_coerced + connection.create_table :testings do |t| + t.column :foo, :timestamp + end + + column = connection.columns(:testings).find { |c| c.name == "foo" } + + assert_equal :datetime, column.type + assert_equal "datetime2(6)", column.sql_type end end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 2efc88233..61de79232 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -10,122 +10,130 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it "sst_datatypes" do generate_schema_for_table "sst_datatypes" - assert_line :bigint, type: "bigint", limit: nil, precision: nil, scale: nil, default: 42 - assert_line :int, type: "integer", limit: nil, precision: nil, scale: nil, default: 42 - assert_line :smallint, type: "integer", limit: 2, precision: nil, scale: nil, default: 42 - assert_line :tinyint, type: "integer", limit: 1, precision: nil, scale: nil, default: 42 - assert_line :bit, type: "boolean", limit: nil, precision: nil, scale: nil, default: true - assert_line :decimal_9_2, type: "decimal", limit: nil, precision: 9, scale: 2, default: 12345.01 - assert_line :numeric_18_0, type: "decimal", limit: nil, precision: 18, scale: nil, default: 191 - assert_line :numeric_36_2, type: "decimal", limit: nil, precision: 36, scale: 2, default: 12345678901234567890.01 - assert_line :money, type: "money", limit: nil, precision: 19, scale: 4, default: 4.2 - assert_line :smallmoney, type: "smallmoney", limit: nil, 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", limit: nil, precision: nil, scale: nil, default: 123.00000001 - assert_line :real, type: "real", limit: nil, precision: nil, scale: nil, 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", limit: nil, precision: nil, scale: nil, default: "01-01-0001" - assert_line :datetime, type: "datetime", limit: nil, precision: nil, scale: nil, default: "01-01-1753 00:00:00.123" + assert_line :date, type: "date", default: "01-01-0001" + assert_line :datetime, type: "datetime", precision: nil, default: "01-01-1753 00:00:00.123" if connection_dblib_73? - assert_line :datetime2_7, type: "datetime", limit: nil, precision: 7, scale: nil, default: "12-31-9999 23:59:59.9999999" - assert_line :datetime2_3, type: "datetime", limit: nil, precision: 3, scale: nil, default: nil - assert_line :datetime2_1, type: "datetime", limit: nil, precision: 1, scale: nil, default: nil + 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 end - assert_line :smalldatetime, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: "01-01-1901 15:45:00.0" + assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0" if connection_dblib_73? - assert_line :time_7, type: "time", limit: nil, precision: 7, scale: nil, default: "04:20:00.2883215" - assert_line :time_2, type: "time", limit: nil, precision: 2, scale: nil, default: nil - assert_line :time_default, type: "time", limit: nil, precision: 7, scale: nil, default: "15:03:42.0621978" + 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" end # Character Strings - assert_line :char_10, type: "char", limit: 10, precision: nil, scale: nil, default: "1234567890", collation: nil - assert_line :varchar_50, type: "varchar", limit: 50, precision: nil, scale: nil, default: "test varchar_50", collation: nil - assert_line :varchar_max, type: "varchar_max", limit: nil, precision: nil, scale: nil, default: "test varchar_max", collation: nil - assert_line :text, type: "text_basic", limit: nil, precision: nil, scale: nil, default: "test text", collation: nil + 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, precision: nil, scale: nil, default: "12345678åå", collation: nil - assert_line :nvarchar_50, type: "string", limit: 50, precision: nil, scale: nil, default: "test nvarchar_50 åå", collation: nil - assert_line :nvarchar_max, type: "text", limit: nil, precision: nil, scale: nil, default: "test nvarchar_max åå", collation: nil - assert_line :ntext, type: "ntext", limit: nil, precision: nil, scale: nil, default: "test ntext åå", collation: nil + 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, precision: nil, scale: nil, default: nil - assert_line :varbinary_49, type: "varbinary", limit: 49, precision: nil, scale: nil, default: nil - assert_line :varbinary_max, type: "binary", limit: nil, precision: nil, scale: nil, default: nil + 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", limit: nil, precision: nil, scale: nil, default: -> { "newid()" } - assert_line :timestamp, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil + assert_line :uniqueidentifier, type: "uuid", default: -> { "newid()" } + assert_line :timestamp, type: "ss_timestamp" end it "sst_datatypes_migration" do columns = SSTestDatatypeMigration.columns_hash 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["datetime_col"].sql_type).must_equal "datetime2(6)" - _(columns["timestamp_col"].sql_type).must_equal "datetime" - _(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", limit: nil, precision: nil, scale: nil, default: nil - assert_line :bigint_col, type: "bigint", limit: nil, precision: nil, scale: nil, default: nil - assert_line :boolean_col, type: "boolean", limit: nil, precision: nil, scale: nil, default: nil - assert_line :decimal_col, type: "decimal", limit: nil, precision: 18, scale: nil, default: nil - assert_line :float_col, type: "float", limit: nil, precision: nil, scale: nil, default: nil - assert_line :string_col, type: "string", limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil - assert_line :timestamp_col, type: "datetime", limit: nil, precision: nil, scale: nil, default: nil - assert_line :time_col, type: "time", limit: nil, precision: 7, scale: nil, default: nil - assert_line :date_col, type: "date", limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_col, type: "binary", limit: nil, precision: nil, scale: nil, default: nil + _(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" + # Our type methods. - _(columns["real_col"].sql_type).must_equal "real" - _(columns["money_col"].sql_type).must_equal "money" + _(columns["real_col"].sql_type).must_equal "real" + _(columns["money_col"].sql_type).must_equal "money" _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" - _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" - _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" - _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" - _(columns["char_col"].sql_type).must_equal "char(1)" - _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" - _(columns["text_basic_col"].sql_type).must_equal "text" - _(columns["nchar_col"].sql_type).must_equal "nchar(1)" - _(columns["ntext_col"].sql_type).must_equal "ntext" - _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" - _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" - _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" - _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" - _(columns["json_col"].sql_type).must_equal "nvarchar(max)" - assert_line :real_col, type: "real", limit: nil, precision: nil, scale: nil, default: nil - assert_line :money_col, type: "money", limit: nil, precision: 19, scale: 4, default: nil - assert_line :smalldatetime_col, type: "smalldatetime", limit: nil, precision: nil, scale: nil, default: nil - assert_line :datetime2_col, type: "datetime", limit: nil, precision: 7, scale: nil, default: nil - assert_line :datetimeoffset, type: "datetimeoffset", limit: nil, precision: 7, scale: nil, default: nil - assert_line :smallmoney_col, type: "smallmoney", limit: nil, precision: 10, scale: 4, default: nil - assert_line :char_col, type: "char", limit: 1, precision: nil, scale: nil, default: nil - assert_line :varchar_col, type: "varchar", limit: nil, precision: nil, scale: nil, default: nil - assert_line :text_basic_col, type: "text_basic", limit: nil, precision: nil, scale: nil, default: nil - assert_line :nchar_col, type: "nchar", limit: 1, precision: nil, scale: nil, default: nil - assert_line :ntext_col, type: "ntext", limit: nil, precision: nil, scale: nil, default: nil - assert_line :binary_basic_col, type: "binary_basic", limit: 1, precision: nil, scale: nil, default: nil - assert_line :varbinary_col, type: "varbinary", limit: nil, precision: nil, scale: nil, default: nil - assert_line :uuid_col, type: "uuid", limit: nil, precision: nil, scale: nil, default: nil - assert_line :sstimestamp_col, type: "ss_timestamp", limit: nil, precision: nil, scale: nil, default: nil - assert_line :json_col, type: "text", limit: nil, precision: nil, scale: nil, default: nil + _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" + _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" + _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" + _(columns["char_col"].sql_type).must_equal "char(1)" + _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" + _(columns["text_basic_col"].sql_type).must_equal "text" + _(columns["nchar_col"].sql_type).must_equal "nchar(1)" + _(columns["ntext_col"].sql_type).must_equal "ntext" + _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" + _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" + _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" + _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" + _(columns["json_col"].sql_type).must_equal "nvarchar(max)" + + assert_line :real_col, type: "real" + assert_line :money_col, type: "money", precision: 19, scale: 4 + assert_line :smalldatetime_col, type: "smalldatetime" + assert_line :datetime2_col, type: "datetime", precision: 7 + assert_line :datetimeoffset, type: "datetimeoffset", precision: 7 + assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4 + assert_line :char_col, type: "char", limit: 1 + assert_line :varchar_col, type: "varchar" + assert_line :text_basic_col, type: "text_basic" + assert_line :nchar_col, type: "nchar", limit: 1 + assert_line :ntext_col, type: "ntext" + assert_line :binary_basic_col, type: "binary_basic", limit: 1 + assert_line :varbinary_col, type: "varbinary" + assert_line :uuid_col, type: "uuid" + assert_line :sstimestamp_col, type: "ss_timestamp", null: false + assert_line :json_col, type: "text" end it "dump column collation" do generate_schema_for_table('sst_string_collation') - assert_line :string_without_collation, type: "string", limit: nil, default: nil, collation: nil - assert_line :string_default_collation, type: "varchar", limit: nil, default: nil, collation: nil - assert_line :string_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS" - assert_line :varchar_with_collation, type: "varchar", limit: nil, default: nil, collation: "SQL_Latin1_General_CP1_CS_AS" + 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" end # Special Cases @@ -140,8 +148,9 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it "no id with model driven primary key" do output = generate_schema_for_table "sst_no_pk_data" + _(output).must_match %r{create_table "sst_no_pk_data".*id:\sfalse.*do} - assert_line :name, type: "string", limit: nil, default: nil, collation: nil + assert_line :name, type: "string" end it "dumps field with unique key constraints only once" do @@ -154,6 +163,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase def generate_schema_for_table(*table_names) require "stringio" + stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) @@ -173,15 +183,19 @@ def line(column_name) @schema_lines[column_name.to_s] end - def assert_line(column_name, options = {}) + def assert_line(column_name, expected_options = {}) line = line(column_name) assert line, "Could not find line with column name: #{column_name.inspect} in schema:\n#{schema}" - [:type, :limit, :precision, :scale, :collation, :default].each do |key| - next unless options.key?(key) + # 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." + # Check the expected and actual option values. + expected_options.each do |key, expected| actual = key == :type ? line.send(:type_method) : line.send(key) - expected = options[key] + message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}" if expected.nil? @@ -207,7 +221,13 @@ class SchemaLine :options def self.option(method_name) - define_method(method_name) { options.present? ? options[method_name.to_sym] : nil } + define_method(method_name) do + if options.key?(method_name.to_sym) + options[method_name.to_sym] + else + throw "Schema line does include the '#{method_name}' option!" + end + end end def initialize(line) @@ -220,6 +240,7 @@ def initialize(line) option :scale option :default option :collation + option :null def to_s line.squish @@ -234,6 +255,7 @@ def inspect def parse_line _all, type_method, col_name, options = @line.match(LINE_PARSER).to_a options = parse_options(options) + [type_method, col_name, options] end @@ -243,8 +265,6 @@ def parse_options(opts) else {} end - rescue SyntaxError - {} end end end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 0742d4624..f0262f410 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -14,8 +14,9 @@ t.float :float_col t.string :string_col t.text :text_col - t.datetime :datetime_col - t.timestamp :timestamp_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 From 2015b2a354fc05c9bced2e1851810eb9751832e7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 12 Jun 2023 15:54:49 +0100 Subject: [PATCH 1088/1412] Prepare for release 7.0.3.0 --- CHANGELOG.md | 2 +- README.md | 8 ++++++-- VERSION | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a97f7e9d9..647901c7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.0.3.0 #### Fixed diff --git a/README.md b/README.md index ce2df4a85..9604946f6 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | |-----------------| ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.2.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.3.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | @@ -29,7 +29,11 @@ We support every data type supported by FreeTDS. All simplified Rails types in m The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version `7.3` with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to `7.3`. The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. -The Rails v5 adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. Using a precision with the `:datetime` type will signal the adapter to use the `datetime2` type under the hood. +The adapter supports ActiveRecord's `datetime_with_precision` setting. This means that passing `:precision` to a datetime column is supported. + +By default, precision 6 is used for `:datetime` types if precision is not specified. Any non-nil precision will tell +the adapter to use the `datetime2` column type. To create a `datetime` column using a migration a precision of `nil` +should be specified, otherwise the precision will default to 6 and a `datetime2` column will be created. #### Identity Inserts with Triggers diff --git a/VERSION b/VERSION index 70caf8820..8117d37ee 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.2.0 +7.0.3.0 From 41023f692fe682e62705253278c96fd692719db2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 14 Jun 2023 14:54:08 +0100 Subject: [PATCH 1089/1412] Added coerced tests (#1061) --- test/cases/coerced_tests.rb | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3ae18245a..e7996f968 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1310,9 +1310,26 @@ def test_reorder_with_first_coerced # We are not doing order duplicate removal anymore. coerce_tests! :test_default_scope_order_with_scope_order - # Leave it up to users to format selects/functions so HAVING works correctly. + # Order column must be in the GROUP clause. coerce_tests! :test_multiple_where_and_having_clauses + 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) + + assert_equal [post], having_then_where + end + + # Order column must be in the GROUP clause. coerce_tests! :test_having_with_binds_for_both_where_and_having + def test_having_with_binds_for_both_where_and_having + post = Post.first + having_then_where = Post.having(id: post.id).where(title: post.title).group(:id).select(:id) + where_then_having = Post.where(title: post.title).having(id: post.id).group(:id).select(:id) + + assert_equal [post], having_then_where + assert_equal [post], where_then_having + end # Find any limit via our expression. coerce_tests! %r{relations don't load all records in #inspect} @@ -1322,10 +1339,18 @@ def test_relations_dont_load_all_records_in_inspect_coerced end end - # I wanted to add `.order("author_id")` scope to avoid error: Column "posts.id" is invalid in the ORDER BY - # However, this pull request on Rails core drops order on exists relation. https://github.com/rails/rails/pull/28699 - # so we are skipping all together. + # Order column must be in the GROUP clause. coerce_tests! :test_empty_complex_chained_relations + def test_empty_complex_chained_relations_coerced + posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0") + + assert_queries(1) { assert_equal false, posts.empty? } + assert_not_predicate posts, :loaded? + + no_posts = posts.where(title: "") + assert_queries(1) { assert_equal true, no_posts.empty? } + assert_not_predicate no_posts, :loaded? + end # Can't apply offset without ORDER coerce_tests! %r{using a custom table affects the wheres} From 413d5f2f173ec7010b6a7198ab7dde45e75b40f2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 15 Jun 2023 10:19:56 +0100 Subject: [PATCH 1090/1412] Added author --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 8bd93e464..cb37a585b 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |spec| spec.required_ruby_version = ">= 2.7.0" spec.license = "MIT" - spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward"] + 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." From 76a887ab5b8ee832deec8492db6eb6cc27c300bb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 3 Jul 2023 10:20:55 +0100 Subject: [PATCH 1091/1412] Updated licence to remove copyright --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9604946f6..cf412b10e 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,7 @@ Many many people have contributed. If you do not see your name here and it shoul You can see an up-to-date list of contributors here: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors + ## License -Copyright © 2008-2022. It is free software, and may be redistributed under the terms specified in the [MIT-LICENSE](MIT-LICENSE) file. +ActiveRecord SQL Server Adapter is released under the [MIT License](https://opensource.org/licenses/MIT). From ed400f7d2a13a727f4fe99781b103f61f40168db Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Jul 2023 13:56:51 +0100 Subject: [PATCH 1092/1412] Updated to match Rails licence format --- MIT-LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index 90cce529a..c095e0832 100644 --- a/MIT-LICENSE +++ b/MIT-LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2008-2022 +Copyright (c) Ken Collins Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the From d9efae785b5bfc7d91f539dd9c413fcfab4a0368 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 1 Aug 2023 16:51:44 +0100 Subject: [PATCH 1093/1412] Updated for Rails 7.1 --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index cf412b10e..d906c65e8 100644 --- a/README.md +++ b/README.md @@ -11,15 +11,16 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. -| Adapter Version | Rails Version | Support | -|-----------------| ------------- | ------------------------------------------------------------------------------------------- | -| `7.0.3.0` | `7.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `6.1.2.1` | `6.1.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | [active](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | [ended](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|----------------------|----------------|-------------------------------------------------------------------------------------------------| +| n/a | `7.1.0` (unreleased) | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.3.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | For older versions, please check their stable branches. From 4143a2740e5dbbb56eda051a93c605b56434811a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Aug 2023 11:47:02 +0100 Subject: [PATCH 1094/1412] [Rails 7.1] Initial changes required to run tests (#1065) --- Dockerfile.ci | 2 +- Gemfile | 3 ++ Rakefile | 4 ++- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- docker-compose.ci.yml | 1 + .../sqlserver/database_statements.rb | 11 +++++-- .../sqlserver/schema_statements.rb | 2 +- .../sqlserver/table_definition.rb | 6 ++++ .../connection_adapters/sqlserver_adapter.rb | 33 +++++++++++-------- lib/active_record/sqlserver_base.rb | 11 +------ test/config.yml | 4 +-- 12 files changed, 48 insertions(+), 33 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..7f75f1a9e 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_MAIN=1 bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/Gemfile b/Gemfile index 8cf772ba3..0d0761ba0 100644 --- a/Gemfile +++ b/Gemfile @@ -12,9 +12,12 @@ gem "sqlite3", "~> 1.4" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" gem "minitest", ">= 5.15.0", "< 5.16" +gem "msgpack", ">= 1.7.0" if ENV["RAILS_SOURCE"] gemspec path: ENV["RAILS_SOURCE"] +elsif ENV["RAILS_MAIN"] + gem "rails", github: "rails/rails", branch: 'main' else # Need to get rails source because the gem doesn't include tests version = ENV["RAILS_VERSION"] || begin diff --git a/Rakefile b/Rakefile index ef69c87d4..93bc46719 100644 --- a/Rakefile +++ b/Rakefile @@ -9,6 +9,8 @@ task test: ["test:dblib"] task default: [:test] namespace :test do + ENV["ARCONN"] = "sqlserver" + %w(dblib).each do |mode| Rake::TestTask.new(mode) do |t| t.libs = ARTest::SQLServer.test_load_paths @@ -19,7 +21,7 @@ namespace :test do end task "dblib:env" do - ENV["ARCONN"] = "dblib" + ENV["ARCONN_MODE"] = "dblib" end end diff --git a/VERSION b/VERSION index 8117d37ee..d35a9b83a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.0.3.0 +7.1.0.alpha diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index cb37a585b..712c30e7a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.0.0" + spec.add_dependency "activerecord", "~> 7.1.0.alpha" spec.add_dependency "tiny_tds" end diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbc842619..82c48d48a 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_MAIN=1 build: context: . dockerfile: Dockerfile.ci diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 5e154b6b2..f62ee49a6 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -29,7 +29,7 @@ def execute(sql, name = nil) end end - def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) + def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) sql = transform_query(sql) if preventing_writes? && write_query?(sql) raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" @@ -41,7 +41,7 @@ def exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) sp_executesql(sql, name, binds, prepare: prepare, async: async) end - def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil) + def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil, returning: nil) if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk) } else @@ -263,7 +263,7 @@ def newsequentialid_function protected - def sql_for_insert(sql, pk, binds) + def sql_for_insert(sql, pk, binds, _returning) if pk.nil? table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) @@ -301,6 +301,8 @@ def set_identity_insert(table_name, enable = true) # === SQLServer Specific (Executing) ============================ # def do_execute(sql, name = "SQL") + connect if @connection.nil? + materialize_transactions mark_transaction_written_if_write(sql) @@ -328,6 +330,7 @@ def sp_executesql_types_and_parameters(binds) end def sp_executesql_sql_type(attr) + return "nvarchar(max)".freeze if attr.is_a?(Symbol) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) case value = attr.value_for_database @@ -339,6 +342,8 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) + return quote(attr) if attr.is_a?(Symbol) + case value = attr.value_for_database when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 34b112392..5a31fdc60 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -26,7 +26,7 @@ def drop_table(table_name, **options) do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" end end - if options[:if_exists] && @version_year < 2016 + if options[:if_exists] && version_year < 2016 execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA" else super diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 2740aad8d..5800ef0ca 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -113,6 +113,12 @@ def new_column_definition(name, type, **options) super end + + private + + def valid_column_definition_options + super + [:is_identity] + end end class Table < ActiveRecord::ConnectionAdapters::Table diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a8cb14337..6a2544100 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -154,10 +154,14 @@ def config_encoding(config) end end - def initialize(connection, logger, _connection_options, config) - super(connection, logger, config) - @connection_options = config - perform_connection_configuration + def initialize(...) + super + + @config = @config.symbolize_keys + @config.reverse_merge!(mode: :dblib) + @config[:mode] = @config[:mode].to_s.downcase.underscore.to_sym + + @connection_options ||= @config end # === Abstract Adapter ========================================== # @@ -229,7 +233,7 @@ def supports_datetime_with_precision? end def supports_json? - @version_year >= 2016 + version_year >= 2016 end def supports_comments? @@ -253,7 +257,7 @@ def supports_lazy_transactions? end def supports_in_memory_oltp? - @version_year >= 2014 + version_year >= 2014 end def supports_insert_returning? @@ -308,7 +312,7 @@ def disconnect! @collation = nil end - def clear_cache! + def clear_cache!(...) @view_information = nil super end @@ -534,11 +538,15 @@ def initialize_dateformatter end def version_year - return 2016 if sqlserver_version =~ /vNext/ - - /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i - rescue StandardError - 2016 + @version_year ||= begin + if sqlserver_version =~ /vNext/ + 2016 + else + /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i + end + rescue StandardError + 2016 + end end def sqlserver_version @@ -559,7 +567,6 @@ def perform_connection_configuration def configure_connection_defaults @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first - @version_year = version_year initialize_dateformatter use_database diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb index 4644e93e3..9dbcbc623 100644 --- a/lib/active_record/sqlserver_base.rb +++ b/lib/active_record/sqlserver_base.rb @@ -7,16 +7,7 @@ def sqlserver_adapter_class end def sqlserver_connection(config) #:nodoc: - config = config.symbolize_keys - config.reverse_merge!(mode: :dblib) - config[:mode] = config[:mode].to_s.downcase.underscore.to_sym - - sqlserver_adapter_class.new( - sqlserver_adapter_class.new_client(config), - logger, - nil, - config - ) + sqlserver_adapter_class.new(config) end end end diff --git a/test/config.yml b/test/config.yml index 9b30ae6be..c08e23b84 100644 --- a/test/config.yml +++ b/test/config.yml @@ -1,7 +1,7 @@ default_connection_info: &default_connection_info adapter: sqlserver - mode: <%= ENV['ARCONN'] || 'dblib' %> + mode: <%= ENV['ARCONN_MODE'] || 'dblib' %> host: <%= ENV['ACTIVERECORD_UNITTEST_HOST'] || 'localhost' %> port: <%= ENV['ACTIVERECORD_UNITTEST_PORT'] %> database: activerecord_unittest @@ -12,7 +12,7 @@ default_connection_info: &default_connection_info connections: - dblib: + sqlserver: arunit: <<: *default_connection_info appname: SQLServerAdptrUnit From d29a9cb02c40441d9b9371dc90e9470894b9ff21 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Aug 2023 14:05:01 +0100 Subject: [PATCH 1095/1412] Remove deprecated methods (#1066) --- .../sqlserver/database_limits.rb | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_limits.rb b/lib/active_record/connection_adapters/sqlserver/database_limits.rb index 4fa681115..5d8973ab0 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_limits.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_limits.rb @@ -8,45 +8,14 @@ def table_alias_length 128 end - def column_name_length - 128 - end - deprecate :column_name_length - def table_name_length 128 end - deprecate :table_name_length def index_name_length 128 end - def columns_per_table - 1024 - end - deprecate :columns_per_table - - def indexes_per_table - 999 - end - deprecate :indexes_per_table - - def columns_per_multicolumn_index - 16 - end - deprecate :columns_per_multicolumn_index - - def sql_query_length - 65_536 * 4_096 - end - deprecate :sql_query_length - - def joins_per_query - 256 - end - deprecate :joins_per_query - private # The max number of binds is 2100, but because sp_executesql takes From 399812c3115c4a66474b61cdacd6d6212b23257c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Aug 2023 14:57:42 +0100 Subject: [PATCH 1096/1412] Use latest Rubies (#1070) --- .github/workflows/ci.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 687823942..193bb6df4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,10 @@ jobs: fail-fast: false matrix: ruby: - - 2.7.7 - - 3.1.3 - - 3.2.1 + - 2.7.8 + - 3.0.6 + - 3.1.4 + - 3.2.2 steps: - name: Checkout code From 9ffb318536316b392c34d2277d990db29ef1f21b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Aug 2023 14:58:06 +0100 Subject: [PATCH 1097/1412] Fix deprecated warning message (#1069) --- test/cases/execute_procedure_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 45b73949b..9fa8df0df 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -43,7 +43,7 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase end it 'test deprecation with transaction return when executing procedure' do - assert_deprecated do + assert_deprecated(ActiveRecord.deprecator) do ActiveRecord::Base.transaction do connection.execute_procedure("my_getutcdate") return From e58ff7a92521133a8d31e742f5f3575c6b761186 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Aug 2023 15:55:29 +0100 Subject: [PATCH 1098/1412] Remove visit_Arel_Nodes_HomogeneousIn monkey-patch (#1068) --- lib/arel/visitors/sqlserver.rb | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index f05fb3a3f..74019cc9a 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -64,39 +64,6 @@ def visit_Arel_Nodes_Grouping(o, collector) super end - def visit_Arel_Nodes_HomogeneousIn(o, collector) - collector.preparable = false - - collector << quote_table_name(o.table_name) << "." << quote_column_name(o.column_name) - - if o.type == :in - collector << " IN (" - else - collector << " NOT IN (" - end - - values = o.casted_values - - if values.empty? - collector << @connection.quote(nil) - elsif @connection.prepared_statements - # Monkey-patch start. Add query attribute bindings rather than just values. - column_name = o.column_name - column_type = o.attribute.relation.type_for_attribute(o.column_name) - # Use cast_type on encrypted attributes. Don't encrypt them again - column_type = column_type.cast_type if column_type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) - attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) } - - collector.add_binds(attrs, &bind_block) - # Monkey-patch end. - else - collector.add_binds(values, &bind_block) - end - - collector << ")" - collector - end - def visit_Arel_Nodes_SelectStatement(o, collector) @select_statement = o distinct_One_As_One_Is_So_Not_Fetch o From 191510a9d06eb685a226e84222bb45e3522ca6da Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 10 Aug 2023 09:29:09 +0100 Subject: [PATCH 1099/1412] Improve performance of view default function lookup (#1074) * Improve performance of view default function lookup (#1073) * Update CHANGELOG.md --------- Co-authored-by: David Genord II --- CHANGELOG.md | 6 ++++++ .../sqlserver/schema_statements.rb | 18 +++++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 647901c7b..501714c3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Changed + +- [#1073](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1073) Improve performance of view default function lookup + ## v7.0.3.0 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5a31fdc60..50e06011b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -371,6 +371,15 @@ def column_definitions(table_name) view_exists = view_exists?(table_name) view_tblnm = view_table_name(table_name) if view_exists + if view_exists + results = sp_executesql %{ + SELECT c.COLUMN_NAME AS [name], c.COLUMN_DEFAULT AS [default] + FROM #{database}.INFORMATION_SCHEMA.COLUMNS c + WHERE c.TABLE_NAME = #{quote(view_tblnm)} + }.squish, "SCHEMA", [] + default_functions = results.each.with_object({}) {|row, out| out[row["name"]] = row["default"] }.compact + end + sql = column_definitions_sql(database, identifier) binds = [] @@ -402,13 +411,8 @@ def column_definitions(table_name) ci[:default_function] = begin default = ci[:default_value] if default.nil? && view_exists - default = select_value %{ - SELECT c.COLUMN_DEFAULT - FROM #{database}.INFORMATION_SCHEMA.COLUMNS c - WHERE - c.TABLE_NAME = '#{view_tblnm}' - AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}' - }.squish, "SCHEMA" + view_column = views_real_column_name(table_name, ci[:name]) + default = default_functions[view_column] if view_column.present? end case default when nil From 7bfce52b8a57635ef8fcae350f3cd239d739fc80 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 10 Aug 2023 15:15:12 +0100 Subject: [PATCH 1100/1412] Fix query cache filtering (#1075) --- test/support/core_ext/query_cache.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb index c695b2147..72d4779d9 100644 --- a/test/support/core_ext/query_cache.rb +++ b/test/support/core_ext/query_cache.rb @@ -22,7 +22,13 @@ module SqlIgnoredCache # compromising cache outside tests. def cache_sql(sql, name, binds) result = super - @query_cache.delete_if { |k, v| k =~ Regexp.union(IGNORED_SQL) } + + @query_cache.delete_if do |cache_key, _v| + # Query cache key generated by `sql` or `[sql, binds]`, so need to retrieve `sql` for both cases. + cache_key_sql = Array(cache_key).first + Regexp.union(IGNORED_SQL).match?(cache_key_sql) + end + result end end From 2c25eb34cff5cb0a63378f7a9539144afa225d70 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 15 Aug 2023 15:30:14 +0100 Subject: [PATCH 1101/1412] Fix EXPLAIN queries (#1076) --- .../connection_adapters/sqlserver/core_ext/explain.rb | 8 ++++---- .../connection_adapters/sqlserver/showplan.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) 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 a267909f5..4eea43e3d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -8,20 +8,20 @@ module Explain SQLSERVER_STATEMENT_PREFIX = "EXEC sp_executesql " SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ - def exec_explain(queries) + def exec_explain(queries, options = []) return super unless connection.adapter_name == "SQLServer" unprepared_queries = queries.map do |(sql, binds)| [unprepare_sqlserver_statement(sql, binds), binds] end - super(unprepared_queries) + super(unprepared_queries, options) end private - # This is somewhat hacky, but it should reliably reformat our prepared sql statment + # This is somewhat hacky, but it should reliably reformat our prepared sql statement # which uses sp_executesql to just the first argument, then unquote it. Likewise our - # `sp_executesql` method should substitude the @n args with the quoted values. + # `sp_executesql` method should substitute the @n args with the quoted values. def unprepare_sqlserver_statement(sql, binds) return sql unless sql.start_with?(SQLSERVER_STATEMENT_PREFIX) diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index b2a737518..e6c89120c 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -12,7 +12,7 @@ module Showplan OPTION_XML = "SHOWPLAN_XML" OPTIONS = [OPTION_ALL, OPTION_TEXT, OPTION_XML] - def explain(arel, binds = []) + def explain(arel, binds = [], options = []) sql = to_sql(arel) result = with_showplan_on { sp_executesql(sql, "EXPLAIN", binds) } printer = showplan_printer.new(result) From 72f553f294e82502c553f6a5c43101293d8ed149 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 16 Aug 2023 11:57:58 +0100 Subject: [PATCH 1102/1412] Fix auto populated ID after insert (#1077) --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- lib/active_record/connection_adapters/sqlserver_column.rb | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f62ee49a6..feefd4a3a 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -43,9 +43,9 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil, returning: nil) if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) - with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk) } + with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk, returning) } else - super(sql, name, binds, pk) + super(sql, name, binds, pk, returning) end end diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index ffaf60a2a..d9d2258de 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -17,6 +17,7 @@ def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_po def is_identity? is_identity end + alias_method :auto_incremented_by_db?, :is_identity? def is_primary? is_primary From be44633de0d286a8fd6f36be74c58d635bd11ff0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 17 Aug 2023 14:07:20 +0100 Subject: [PATCH 1103/1412] Translate exceptions (#1078) --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index feefd4a3a..f49a7f129 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -309,6 +309,7 @@ def do_execute(sql, name = "SQL") log(sql, name) { raw_connection_do(sql) } end + # TODO: Adapter should be refactored to use `with_raw_connection` to translate exceptions. def sp_executesql(sql, name, binds, options = {}) options[:ar_result] = true if options[:fetch] != :rows unless without_prepared_statement?(binds) @@ -316,6 +317,9 @@ def sp_executesql(sql, name, binds, options = {}) sql = sp_executesql_sql(sql, types, params, name) end raw_select sql, name, binds, options + rescue => original_exception + translated_exception = translate_exception_class(original_exception, sql, binds) + raise translated_exception end def sp_executesql_types_and_parameters(binds) From 7ca8c45c0769fc94dfc2543393b58a6595963aee Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 17 Aug 2023 14:09:05 +0100 Subject: [PATCH 1104/1412] Coerce sanitize test (#1079) --- test/cases/coerced_tests.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e7996f968..33eb9fc5d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1423,6 +1423,13 @@ def self.search_as_method(term) searchable_post.search_as_scope("20% _reduction_!").to_a end end + + # Use nvarchar string (N'') in assert + coerce_tests! :test_named_bind_with_literal_colons + def test_named_bind_with_literal_colons_coerced + assert_equal "TO_TIMESTAMP(N'2017/08/02 10:59:00', 'YYYY/MM/DD HH12:MI:SS')", bind("TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12\\:MI\\:SS')", date: "2017/08/02 10:59:00") + assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "TO_TIMESTAMP(:date, 'YYYY/MM/DD HH12:MI:SS')", date: "2017/08/02 10:59:00" } + end end class SchemaDumperTest < ActiveRecord::TestCase From 6cf056b9cf611c5940bf0870728ca3c680a039e5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 17 Aug 2023 15:51:29 +0100 Subject: [PATCH 1105/1412] Validate table name length when renaming (#1080) --- .../connection_adapters/sqlserver/schema_statements.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 50e06011b..19283ac30 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -125,7 +125,10 @@ def primary_keys_select(table_name) sp_executesql(sql, "SCHEMA", binds).map { |r| r["name"] } end - def rename_table(table_name, new_name) + def rename_table(table_name, new_name, **options) + validate_table_length!(new_name) unless options[:_uses_legacy_table_name] + schema_cache.clear_data_source_cache!(table_name.to_s) + schema_cache.clear_data_source_cache!(new_name.to_s) do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" rename_table_indexes(table_name, new_name) end From bfd77e55c60a278362e365abe5a77dbc23f4c443 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 29 Aug 2023 11:18:32 +0100 Subject: [PATCH 1106/1412] Coerce test --- test/cases/coerced_tests.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 33eb9fc5d..1c4d49c1b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2152,6 +2152,17 @@ def test_in_order_of_with_enums_keys_coerced Book.where(author_id: nil, name: nil).delete_all Book.connection.add_index(:books, [:author_id, :name], unique: true) end + + # 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_nil + def test_in_order_of_with_nil_coerced + Book.connection.remove_index(:books, column: [:author_id, :name]) + + original_test_in_order_of_with_nil + ensure + Book.where(author_id: nil, name: nil).delete_all + Book.connection.add_index(:books, [:author_id, :name], unique: true) + end end require "models/dashboard" From 4842d2e884346bf1a28bd36e91ac984843d2096b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 13 Sep 2023 14:54:48 +0100 Subject: [PATCH 1107/1412] Fixed adapter to pass SQL Server only tests (#1081) --- README.md | 8 +- Rakefile | 6 - .../sqlserver/database_statements.rb | 149 ++++++++--------- .../sqlserver/database_tasks.rb | 10 +- .../sqlserver/schema_statements.rb | 20 +-- .../connection_adapters/sqlserver_adapter.rb | 150 ++++++------------ .../tasks/sqlserver_database_tasks.rb | 7 +- test/cases/adapter_test_sqlserver.rb | 15 +- test/cases/column_test_sqlserver.rb | 12 +- test/cases/connection_test_sqlserver.rb | 9 +- test/cases/disconnected_test_sqlserver.rb | 12 +- test/cases/schema_dumper_test_sqlserver.rb | 4 +- test/config.yml | 1 - test/support/connection_reflection.rb | 10 +- 14 files changed, 178 insertions(+), 235 deletions(-) diff --git a/README.md b/README.md index d906c65e8..3359879eb 100644 --- a/README.md +++ b/README.md @@ -90,13 +90,16 @@ end #### Configure Connection -We currently conform to an unpublished and non-standard AbstractAdapter interface to configure connections made to the database. To do so, just implement the `configure_connection` method in an initializer like so. In this case below we are setting the `TEXTSIZE` to 64 megabytes. +The adapter conforms to the AbstractAdapter interface to configure connections. If you require additional connection +configuration then implement the `configure_connection` method in an initializer like so. In the following +example we are setting the `TEXTSIZE` to 64 megabytes. ```ruby module ActiveRecord module ConnectionAdapters class SQLServerAdapter < AbstractAdapter def configure_connection + super raw_connection_do "SET TEXTSIZE #{64.megabytes}" end end @@ -171,7 +174,8 @@ To then connect the application to your SQL Server instance edit the `config/dat ## Installation -The adapter has no strict gem dependencies outside of `ActiveRecord`. You will have to pick a connection mode, the default is dblib which uses the `TinyTDS` gem. Just bundle the gem and the adapter will use it. +The adapter has no strict gem dependencies outside of `ActiveRecord` and +[TinyTDS](https://github.com/rails-sqlserver/tiny_tds). ```ruby gem 'activerecord-sqlserver-adapter' diff --git a/Rakefile b/Rakefile index 93bc46719..3c6f58630 100644 --- a/Rakefile +++ b/Rakefile @@ -19,14 +19,8 @@ namespace :test do t.verbose = false end end - - task "dblib:env" do - ENV["ARCONN_MODE"] = "dblib" - end end -task "test:dblib" => "test:dblib:env" - namespace :profile do ["dblib"].each do |mode| namespace mode.to_sym do diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f49a7f129..95f2a8976 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -13,39 +13,58 @@ def write_query?(sql) # :nodoc: !READ_QUERY.match?(sql.b) end - def execute(sql, name = nil) + def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true) + result = nil sql = transform_query(sql) - if preventing_writes? && write_query?(sql) - raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" - end - materialize_transactions - mark_transaction_written_if_write(sql) + log(sql, name, async: async) do + with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| + handle = if id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name) { _execute(sql, conn) } + else + _execute(sql, conn) + end - if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) } - else - do_execute(sql, name) + result = handle.do + end end + + result end def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) + result = nil sql = transform_query(sql) - if preventing_writes? && write_query?(sql) - raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}" - end - materialize_transactions + check_if_write_query(sql) mark_transaction_written_if_write(sql) - sp_executesql(sql, name, binds, prepare: prepare, async: async) + unless without_prepared_statement?(binds) + types, params = sp_executesql_types_and_parameters(binds) + sql = sp_executesql_sql(sql, types, params, name) + end + + log(sql, name, binds, async: async) do + with_raw_connection do |conn| + begin + options = { ar_result: true } + + handle = _execute(sql, conn) + result = handle_to_names_and_values(handle, options) + ensure + finish_statement_handle(handle) + end + end + end + + result end def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil, returning: nil) if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) - with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk, returning) } + with_identity_insert_enabled(id_insert_table_name) { super } else - super(sql, name, binds, pk, returning) + super end end @@ -60,7 +79,7 @@ def exec_update(sql, name, binds) end def begin_db_transaction - do_execute "BEGIN TRANSACTION", "TRANSACTION" + execute "BEGIN TRANSACTION", "TRANSACTION" end def transaction_isolation_levels @@ -73,25 +92,25 @@ def begin_isolated_db_transaction(isolation) end def set_transaction_isolation_level(isolation_level) - do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION" + execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION" end def commit_db_transaction - do_execute "COMMIT TRANSACTION", "TRANSACTION" + execute "COMMIT TRANSACTION", "TRANSACTION" end def exec_rollback_db_transaction - do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION" + execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION" end include Savepoints def create_savepoint(name = current_savepoint_name) - do_execute "SAVE TRANSACTION #{name}", "TRANSACTION" + execute "SAVE TRANSACTION #{name}", "TRANSACTION" end def exec_rollback_to_savepoint(name = current_savepoint_name) - do_execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION" + execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION" end def release_savepoint(name = current_savepoint_name) @@ -164,27 +183,27 @@ def build_insert_sql(insert) # :nodoc: # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) - materialize_transactions - 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(", ") sql = "EXEC #{proc_name} #{vars}".strip - name = "Execute Procedure" - log(sql, name) do - case @connection_options[:mode] - when :dblib - result = ensure_established_connection! { dblib_execute(sql) } + + log(sql, "Execute Procedure") do + with_raw_connection do |conn| + result = _execute(sql, conn) options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc } + result.each(options) do |row| r = row.with_indifferent_access yield(r) if block_given? end + result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row } end end + end def with_identity_insert_enabled(table_name) @@ -198,8 +217,8 @@ def with_identity_insert_enabled(table_name) def use_database(database = nil) return if sqlserver_azure? - name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted - do_execute "USE #{name}" unless name.blank? + name = SQLServer::Utils.extract_identifiers(database || @connection_parameters[:database]).quoted + execute "USE #{name}" unless name.blank? end def user_options @@ -270,11 +289,12 @@ def sql_for_insert(sql, pk, binds, _returning) end sql = if pk && use_output_inserted? && !database_prefix_remote_server? - quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted table_name ||= get_table_name(sql) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted + quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted + id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted <<~SQL.squish DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); @@ -282,7 +302,9 @@ def sql_for_insert(sql, pk, binds, _returning) SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable SQL else - sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk}" + inserted_keys = Array(pk).map { |primary_key| " INSERTED.#{SQLServer::Utils.extract_identifiers(primary_key).quoted}" } + + sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT" + inserted_keys.join(",") end else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" @@ -293,29 +315,22 @@ def sql_for_insert(sql, pk, binds, _returning) # === SQLServer Specific ======================================== # def set_identity_insert(table_name, enable = true) - do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" + execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" rescue Exception raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}" end # === SQLServer Specific (Executing) ============================ # - def do_execute(sql, name = "SQL") - connect if @connection.nil? - - materialize_transactions - mark_transaction_written_if_write(sql) - - log(sql, name) { raw_connection_do(sql) } - end - # TODO: Adapter should be refactored to use `with_raw_connection` to translate exceptions. def sp_executesql(sql, name, binds, options = {}) options[:ar_result] = true if options[:fetch] != :rows + unless without_prepared_statement?(binds) types, params = sp_executesql_types_and_parameters(binds) sql = sp_executesql_sql(sql, types, params, name) end + raw_select sql, name, binds, options rescue => original_exception translated_exception = translate_exception_class(original_exception, sql, binds) @@ -373,11 +388,8 @@ def sp_executesql_sql(sql, types, params, name) end def raw_connection_do(sql) - case @connection_options[:mode] - when :dblib - result = ensure_established_connection! { dblib_execute(sql) } - result.do - end + result = ensure_established_connection! { dblib_execute(sql) } + result.do ensure @update_sql = false end @@ -436,45 +448,38 @@ def _raw_select(sql, options = {}) end def raw_connection_run(sql) - case @connection_options[:mode] - when :dblib - ensure_established_connection! { dblib_execute(sql) } - end - end - - def handle_more_results?(handle) - case @connection_options[:mode] - when :dblib - end + ensure_established_connection! { dblib_execute(sql) } end def handle_to_names_and_values(handle, options = {}) - case @connection_options[:mode] - when :dblib - handle_to_names_and_values_dblib(handle, options) - end - end - - def handle_to_names_and_values_dblib(handle, options = {}) query_options = {}.tap do |qo| qo[:timezone] = ActiveRecord.default_timezone || :utc qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash end results = handle.each(query_options) columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields + options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results end def finish_statement_handle(handle) - case @connection_options[:mode] - when :dblib - handle.cancel if handle - end + handle.cancel if handle handle end + # TODO: Rename + def _execute(sql, conn) + conn.execute(sql).tap do |result| + # TinyTDS returns false instead of raising an exception if connection fails. + # Getting around this by raising an exception ourselves while this PR + # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. + raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) + end + end + + # TODO: Remove def dblib_execute(sql) - @connection.execute(sql).tap do |result| + @raw_connection.execute(sql).tap do |result| # TinyTDS returns false instead of raising an exception if connection fails. # Getting around this by raising an exception ourselves while this PR # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. @@ -483,7 +488,7 @@ def dblib_execute(sql) end def ensure_established_connection! - raise TinyTds::Error, 'SQL Server client is not connected' unless @connection + raise TinyTds::Error, 'SQL Server client is not connected' unless @raw_connection yield end diff --git a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb index 63f33356f..1a22b23e6 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_tasks.rb @@ -8,13 +8,13 @@ def create_database(database, options = {}) name = SQLServer::Utils.extract_identifiers(database) db_options = create_database_options(options) edition_options = create_database_edition_options(options) - do_execute "CREATE DATABASE #{name} #{db_options} #{edition_options}" + execute "CREATE DATABASE #{name} #{db_options} #{edition_options}" end def drop_database(database) name = SQLServer::Utils.extract_identifiers(database) - do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" - do_execute "DROP DATABASE #{name}" + execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE" + execute "DROP DATABASE #{name}" end def current_database @@ -33,7 +33,7 @@ def collation def create_database_options(options = {}) keys = [:collate] - copts = @connection_options + copts = @connection_parameters options = { collate: copts[:collation] }.merge(options.symbolize_keys).select { |_, v| @@ -46,7 +46,7 @@ def create_database_options(options = {}) def create_database_edition_options(options = {}) keys = [:maxsize, :edition, :service_objective] - copts = @connection_options + copts = @connection_parameters edition_options = { maxsize: copts[:azure_maxsize], edition: copts[:azure_edition], diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 19283ac30..3129ee898 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -23,7 +23,7 @@ def drop_table(table_name, **options) pktable = fkdata["PKTABLE_NAME"] pkcolmn = fkdata["PKCOLUMN_NAME"] remove_foreign_key fktable, name: fkdata["FK_NAME"] - do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" + execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" end end if options[:if_exists] && version_year < 2016 @@ -129,7 +129,7 @@ def rename_table(table_name, new_name, **options) validate_table_length!(new_name) unless options[:_uses_legacy_table_name] schema_cache.clear_data_source_cache!(table_name.to_s) schema_cache.clear_data_source_cache!(new_name.to_s) - do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'" + execute "EXEC sp_rename '#{table_name}', '#{new_name}'" rename_table_indexes(table_name, new_name) end @@ -140,7 +140,7 @@ def remove_column(table_name, column_name, type = nil, **options) remove_check_constraints(table_name, column_name) remove_default_constraint(table_name, column_name) remove_indexes(table_name, column_name) - do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}" + execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}" end def change_column(table_name, column_name, type, options = {}) @@ -171,7 +171,7 @@ def change_column(table_name, column_name, type, options = {}) 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(', ')})" end - sql_commands.each { |c| do_execute(c) } + sql_commands.each { |c| execute(c) } clear_cache! end @@ -182,7 +182,7 @@ def change_column_default(table_name, column_name, default_or_changes) remove_default_constraint(table_name, column_name) default = extract_new_default_value(default_or_changes) - do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}" + execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}" clear_cache! end @@ -202,7 +202,7 @@ def rename_index(table_name, old_name, new_name) end def remove_index!(table_name, index_name) - do_execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" + execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end def foreign_keys(table_name) @@ -275,11 +275,11 @@ def change_column_null(table_name, column_name, allow_null, default = nil) column_id = SQLServer::Utils.extract_identifiers(column_name) column = column_for(table_name, column_name) if !allow_null.nil? && allow_null == false && !default.nil? - do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") + execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}" sql += " NOT NULL" if !allow_null.nil? && allow_null == false - do_execute sql + execute sql end def create_schema_dumper(options) @@ -531,7 +531,7 @@ def remove_columns_for_alter(table_name, *column_names, **options) def remove_check_constraints(table_name, column_name) constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", "SCHEMA" constraints.each do |constraint| - do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}" + execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}" end end @@ -540,7 +540,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| - do_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 diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6a2544100..6c7e3d6a5 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "tiny_tds" require "base64" require "active_record" require "arel_sqlserver" @@ -77,97 +78,38 @@ def dbconsole(config, options = {}) end def new_client(config) - case config[:mode] - when :dblib - require "tiny_tds" - dblib_connect(config) + TinyTds::Client.new(config) + rescue TinyTds::Error => error + if error.message.match(/database .* does not exist/i) + raise ActiveRecord::NoDatabaseError else - raise ArgumentError, "Unknown connection mode in #{config.inspect}." + raise end end - def dblib_connect(config) - TinyTds::Client.new( - dataserver: config[:dataserver], - host: config[:host], - port: config[:port], - username: config[:username], - password: config[:password], - database: config[:database], - tds_version: config[:tds_version] || "7.3", - appname: config_appname(config), - login_timeout: config_login_timeout(config), - timeout: config_timeout(config), - encoding: config_encoding(config), - azure: config[:azure], - contained: config[:contained] - ).tap do |client| - if config[:azure] - client.execute("SET ANSI_NULLS ON").do - client.execute("SET ANSI_NULL_DFLT_ON ON").do - client.execute("SET ANSI_PADDING ON").do - client.execute("SET ANSI_WARNINGS ON").do - else - client.execute("SET ANSI_DEFAULTS ON").do - end - client.execute("SET QUOTED_IDENTIFIER ON").do - client.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do - client.execute("SET IMPLICIT_TRANSACTIONS OFF").do - client.execute("SET TEXTSIZE 2147483647").do - client.execute("SET CONCAT_NULL_YIELDS_NULL ON").do - end - rescue TinyTds::Error => e - raise ActiveRecord::NoDatabaseError if e.message.match(/database .* does not exist/i) - raise e - end - - def config_appname(config) - if instance_methods.include?(:configure_application_name) - ActiveSupport::Deprecation.warn <<~MSG.squish - Configuring the application name used by TinyTDS by overriding the - `ActiveRecord::ConnectionAdapters::SQLServerAdapter#configure_application_name` - instance method is no longer supported. The application name should configured - using the `appname` setting in the `database.yml` file instead. Consult the - README for further information." - MSG - end - - config[:appname] || rails_application_name - end - def rails_application_name Rails.application.class.name.split("::").first rescue nil # Might not be in a Rails context so we fallback to `nil`. end - - def config_login_timeout(config) - config[:login_timeout].present? ? config[:login_timeout].to_i : nil - end - - def config_timeout(config) - config[:timeout].present? ? config[:timeout].to_i / 1000 : nil - end - - def config_encoding(config) - config[:encoding].present? ? config[:encoding] : nil - end end def initialize(...) super - @config = @config.symbolize_keys - @config.reverse_merge!(mode: :dblib) - @config[:mode] = @config[:mode].to_s.downcase.underscore.to_sym + @config[:tds_version] = "7.3" unless @config[:tds_version] + @config[:appname] = rails_application_name unless @config[:appname] + @config[:login_timeout] = @config[:login_timeout].present? ? @config[:login_timeout].to_i : nil + @config[:timeout] = @config[:timeout].present? ? @config[:timeout].to_i / 1000 : nil + @config[:encoding] = @config[:encoding].present? ? @config[:encoding] : nil - @connection_options ||= @config + @connection_parameters ||= @config end # === Abstract Adapter ========================================== # def arel_visitor - Arel::Visitors::SQLServer.new self + Arel::Visitors::SQLServer.new(self) end def valid_type?(type) @@ -175,13 +117,7 @@ def valid_type?(type) end def schema_creation - SQLServer::SchemaCreation.new self - end - - def self.database_exists?(config) - !!ActiveRecord::Base.sqlserver_connection(config) - rescue ActiveRecord::NoDatabaseError - false + SQLServer::SchemaCreation.new(self) end def supports_ddl_transactions? @@ -276,18 +212,22 @@ def supports_insert_conflict_target? false end + def return_value_after_insert?(column) # :nodoc: + column.is_primary? || column.is_identity? + end + def disable_referential_integrity tables = tables_with_referential_integrity - tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" } + tables.each { |t| execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" } yield ensure - tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" } + tables.each { |t| execute "ALTER TABLE #{quote_table_name(t)} CHECK CONSTRAINT ALL" } end # === Abstract Adapter (Connection Management) ================== # def active? - return false unless @connection + return false unless @raw_connection raw_connection_do "SELECT 1" true @@ -295,19 +235,20 @@ def active? false end - def reconnect! - super - disconnect! + def reconnect + @raw_connection.close rescue nil + @raw_connection = nil + @spid = nil + @collation = nil + connect end def disconnect! super - case @connection_options[:mode] - when :dblib - @connection.close rescue nil - end - @connection = nil + + @raw_connection.close rescue nil + @raw_connection = nil @spid = nil @collation = nil end @@ -319,7 +260,7 @@ def clear_cache!(...) def reset! reset_transaction - do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" + execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION" end # === Abstract Adapter (Misc Support) =========================== # @@ -360,7 +301,7 @@ def database_prefix_remote_server? end def database_prefix - @connection_options[:database_prefix] + @connection_parameters[:database_prefix] end def database_prefix_identifier(name) @@ -376,7 +317,7 @@ def version end def inspect - "#<#{self.class} version: #{version}, mode: #{@connection_options[:mode]}, azure: #{sqlserver_azure?.inspect}>" + "#<#{self.class} version: #{version}, azure: #{sqlserver_azure?.inspect}>" end def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil) @@ -517,7 +458,7 @@ def translate_exception(e, message:, sql:, binds:) # === SQLServer Specific (Connection Management) ================ # def connection_errors - @connection_errors ||= [].tap do |errors| + @raw_connection_errors ||= [].tap do |errors| errors << TinyTds::Error if defined?(TinyTds::Error) end end @@ -556,16 +497,25 @@ def sqlserver_version private def connect - @connection = self.class.new_client(@connection_options) - perform_connection_configuration + @raw_connection = self.class.new_client(@connection_parameters) end - def perform_connection_configuration - configure_connection_defaults - configure_connection if self.respond_to?(:configure_connection) - end + def configure_connection + if @config[:azure] + @raw_connection.execute("SET ANSI_NULLS ON").do + @raw_connection.execute("SET ANSI_NULL_DFLT_ON ON").do + @raw_connection.execute("SET ANSI_PADDING ON").do + @raw_connection.execute("SET ANSI_WARNINGS ON").do + else + @raw_connection.execute("SET ANSI_DEFAULTS ON").do + end + + @raw_connection.execute("SET QUOTED_IDENTIFIER ON").do + @raw_connection.execute("SET CURSOR_CLOSE_ON_COMMIT OFF").do + @raw_connection.execute("SET IMPLICIT_TRANSACTIONS OFF").do + @raw_connection.execute("SET TEXTSIZE 2147483647").do + @raw_connection.execute("SET CONCAT_NULL_YIELDS_NULL ON").do - def configure_connection_defaults @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first initialize_dateformatter diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 25d92c355..aa8f07f3d 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -10,8 +10,7 @@ module Tasks class SQLServerDatabaseTasks DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" - delegate :connection, :establish_connection, :clear_active_connections!, - to: ActiveRecord::Base + delegate :connection, :establish_connection, to: ActiveRecord::Base def self.using_database_configurations? true @@ -53,6 +52,10 @@ def purge create true end + def clear_active_connections! + ActiveRecord::Base.connection_handler.clear_active_connections! + end + def structure_dump(filename, extra_flags) server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}" server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port] diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index d31804403..34394732b 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -19,7 +19,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase string = connection.inspect _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} _(string).must_match %r{version\: \d.\d} - _(string).must_match %r{mode: dblib} _(string).must_match %r{azure: (true|false)} _(string).wont_match %r{host} _(string).wont_match %r{password} @@ -102,16 +101,18 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "test bad connection" do assert_raise ActiveRecord::NoDatabaseError do db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") - configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest") - ActiveRecord::Base.sqlserver_connection configuration + configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest") + + connection = ActiveRecord::Base.sqlserver_connection configuration + connection.exec_query("SELECT 1") end end it "test database exists returns false if database does not exist" do db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") - configuration = db_config.configuration_hash.merge(database: "inexistent_activerecord_unittest") + configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest") assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(configuration), - "expected database to not exist" + "expected database #{configuration[:database]} to not exist" end it "test database exists returns true when the database exists" do @@ -306,7 +307,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end describe "database statements" do - it "run the database consistency checker useroptions command" do + it "run the database consistency checker 'user_options' command" do skip "on azure" if connection_sqlserver_azure? keys = [:textsize, :language, :isolation_level, :dateformat] user_options = connection.user_options @@ -345,7 +346,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "tinyint", connection.type_to_sql(:integer, limit: 1) end - it "create bigints when limit is greateer than 4" do + it "create bigints when limit is greater than 4" do assert_equal "bigint", connection.type_to_sql(:integer, limit: 5) assert_equal "bigint", connection.type_to_sql(:integer, limit: 6) assert_equal "bigint", connection.type_to_sql(:integer, limit: 7) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index a5aafbbbf..8551357d9 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -277,7 +277,7 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "date" _(col.type).must_equal :date _(col.null).must_equal true - _(col.default).must_equal connection_dblib_73? ? Date.civil(1, 1, 1) : "0001-01-01" + _(col.default).must_equal connection_tds_73 ? Date.civil(1, 1, 1) : "0001-01-01" _(obj.date).must_equal Date.civil(1, 1, 1) _(col.default_function).must_be_nil type = connection.lookup_cast_type_from_column(col) @@ -357,7 +357,7 @@ def assert_obj_set_and_save(attribute, value) end it "datetime2" do - skip "datetime2 not supported in this protocol version" unless connection_dblib_73? + skip "datetime2 not supported in this protocol version" unless connection_tds_73 col = column("datetime2_7") _(col.sql_type).must_equal "datetime2(7)" _(col.type).must_equal :datetime @@ -422,7 +422,7 @@ def assert_obj_set_and_save(attribute, value) end it "datetimeoffset" do - skip "datetimeoffset not supported in this protocol version" unless connection_dblib_73? + skip "datetimeoffset not supported in this protocol version" unless connection_tds_73 col = column("datetimeoffset_7") _(col.sql_type).must_equal "datetimeoffset(7)" _(col.type).must_equal :datetimeoffset @@ -488,7 +488,7 @@ def assert_obj_set_and_save(attribute, value) end it "time(7)" do - skip "time() not supported in this protocol version" unless connection_dblib_73? + skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_7") _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time @@ -520,7 +520,7 @@ def assert_obj_set_and_save(attribute, value) end it "time(2)" do - skip "time() not supported in this protocol version" unless connection_dblib_73? + skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_2") _(col.sql_type).must_equal "time(2)" _(col.type).must_equal :time @@ -550,7 +550,7 @@ def assert_obj_set_and_save(attribute, value) end it "time using default precision" do - skip "time() not supported in this protocol version" unless connection_dblib_73? + skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_default") _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time diff --git a/test/cases/connection_test_sqlserver.rb b/test/cases/connection_test_sqlserver.rb index ddf9bbbca..1cb05cc3e 100644 --- a/test/cases/connection_test_sqlserver.rb +++ b/test/cases/connection_test_sqlserver.rb @@ -44,9 +44,9 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase assert connection.spid.nil? end - it "reset the connection" do + it "reset raw connection on disconnect!" do connection.disconnect! - _(connection.raw_connection).must_be_nil + _(connection.instance_variable_get(:@raw_connection)).must_be_nil end it "be able to disconnect and reconnect at will" do @@ -60,9 +60,6 @@ class ConnectionTestSQLServer < ActiveRecord::TestCase private def disconnect_raw_connection! - case connection_options[:mode] - when :dblib - connection.raw_connection.close rescue nil - end + connection.raw_connection.close rescue nil end end diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index d7ccd33f1..ccbc69de2 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -15,15 +15,13 @@ def setup ActiveRecord::Base.establish_connection(db_config) end - test "can't execute procedures while disconnected" do + test "execute procedure after disconnect reconnects" do @connection.execute_procedure :sp_tables, "sst_datatypes" @connection.disconnect! - assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do - @connection.execute_procedure :sp_tables, "sst_datatypes" - end + @connection.execute_procedure :sp_tables, "sst_datatypes" end - test "can't execute query while disconnected" do + test "execute query after disconnect reconnects" do sql = "SELECT count(*) from products WHERE id IN(@0, @1)" binds = [ ActiveRecord::Relation::QueryAttribute.new("id", 2, ActiveRecord::Type::BigInteger.new), @@ -32,8 +30,6 @@ def setup @connection.exec_query sql, "TEST", binds @connection.disconnect! - assert_raises(ActiveRecord::ConnectionNotEstablished, 'SQL Server client is not connected') do - @connection.exec_query sql, "TEST", binds - end + @connection.exec_query sql, "TEST", binds end end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 61de79232..13ba5b5ce 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -27,13 +27,13 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase # 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" - if connection_dblib_73? + if connection_tds_73 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 end assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0" - if connection_dblib_73? + if connection_tds_73 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" diff --git a/test/config.yml b/test/config.yml index c08e23b84..ef095168c 100644 --- a/test/config.yml +++ b/test/config.yml @@ -1,7 +1,6 @@ default_connection_info: &default_connection_info adapter: sqlserver - mode: <%= ENV['ARCONN_MODE'] || 'dblib' %> host: <%= ENV['ACTIVERECORD_UNITTEST_HOST'] || 'localhost' %> port: <%= ENV['ACTIVERECORD_UNITTEST_PORT'] %> database: activerecord_unittest diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index 154346470..a739f76ee 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -12,16 +12,10 @@ def connection end def connection_options - connection.instance_variable_get :@connection_options + connection.instance_variable_get :@connection_parameters end - def connection_dblib? - connection_options[:mode] == :dblib - end - - def connection_dblib_73? - return false unless connection_dblib? - + def connection_tds_73 rc = connection.raw_connection rc.respond_to?(:tds_73?) && rc.tds_73? end From 440c5da337a35246fdccdba4f98a7af789153679 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 13 Sep 2023 14:56:40 +0100 Subject: [PATCH 1108/1412] Updated to use Rails 7.1.0 beta1 release --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index d35a9b83a..2adc99526 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.alpha +7.1.0.beta1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 712c30e7a..d63505c42 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.1.0.alpha" + spec.add_dependency "activerecord", "~> 7.1.0.beta1" spec.add_dependency "tiny_tds" end From ebf14dcb87b1e166d47d060869e73a92e52e75e4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 13 Sep 2023 16:07:56 +0100 Subject: [PATCH 1109/1412] Accept nested functions in Dangerous Query Methods (#1082) See https://github.com/rails/rails/pull/44010 --- .../connection_adapters/sqlserver/quoting.rb | 4 ++-- test/cases/coerced_tests.rb | 23 ++++++++++++++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 2dafd40dd..8be406435 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -85,7 +85,7 @@ def column_name_with_order_matcher ( (?: # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) ) (?:\s+AS\s+(?:\w+|\[\w+\]))? ) @@ -98,7 +98,7 @@ def column_name_with_order_matcher ( (?: # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) ) (?:\s+ASC|\s+DESC)? (?:\s+NULLS\s+(?:FIRST|LAST))? diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1c4d49c1b..36b890b8e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1766,6 +1766,27 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids end + # Use LEN() vs length() function. + coerce_tests! %r{order: allows nested functions} + test "order: allows nested functions" do + ids_expected = Post.order(Arel.sql("author_id, len(trim(title))")).pluck(:id) + + # $DEBUG = true + ids = Post.order("author_id, len(trim(title))").pluck(:id) + + assert_equal ids_expected, ids + end + + # Use LEN() vs length() function. + coerce_tests! %r{pluck: allows nested functions} + test "pluck: allows nested functions" do + title_lengths_expected = Post.pluck(Arel.sql("len(trim(title))")) + + title_lengths = Post.pluck("len(trim(title))") + + assert_equal title_lengths_expected, title_lengths + end + test "order: allows string column names that are quoted" do ids_expected = Post.order(Arel.sql("id")).pluck(:id) @@ -2157,7 +2178,7 @@ def test_in_order_of_with_enums_keys_coerced coerce_tests! :test_in_order_of_with_nil def test_in_order_of_with_nil_coerced Book.connection.remove_index(:books, column: [:author_id, :name]) - + original_test_in_order_of_with_nil ensure Book.where(author_id: nil, name: nil).delete_all From bb343a96bd472d4956e323b8532885928b8373e4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 09:32:09 +0100 Subject: [PATCH 1110/1412] Allow column name with COLLATE as safe SQL string (#1083) See https://github.com/rails/rails/pull/44384 --- .../connection_adapters/sqlserver/quoting.rb | 1 + test/cases/coerced_tests.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 8be406435..ce70c7fbb 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -100,6 +100,7 @@ def column_name_with_order_matcher # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) ) + (?:\s+COLLATE\s+\w+)? (?:\s+ASC|\s+DESC)? (?:\s+NULLS\s+(?:FIRST|LAST))? ) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 36b890b8e..624acb895 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1850,6 +1850,18 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal titles_expected, titles end + + # Collation name should not be quoted. Hardcoded values for different adapters. + coerce_tests! %r{order: allows valid arguments with COLLATE} + 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 = Post.order(["author_id", %Q'title COLLATE #{collation_name} DESC']).pluck(:id) + + assert_equal ids_expected, ids + end end class ReservedWordTest < ActiveRecord::TestCase From 65149cd7ffcdbcc29ba84916c9270265217efc4a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 10:09:24 +0100 Subject: [PATCH 1111/1412] Coerced pretty print test (#1084) --- test/cases/coerced_tests.rb | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 624acb895..f22a9bedb 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -879,7 +879,7 @@ def test_in_batches_should_quote_batch_order_coerced end class EagerAssociationTest < ActiveRecord::TestCase - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! :test_count_with_include def test_count_with_include_coerced assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count @@ -1263,14 +1263,14 @@ def test_query_cached_even_when_types_are_reset_coerced require "models/post" class RelationTest < ActiveRecord::TestCase - # Use LEN vs LENGTH function. + # Use LEN() instead of LENGTH() function. coerce_tests! :test_reverse_order_with_function def test_reverse_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)")).reverse_order assert_equal topics(:second).title, topics.first.title end - # Use LEN vs LENGTH function. + # Use LEN() instead of LENGTH() function. coerce_tests! :test_reverse_order_with_function_other_predicates def test_reverse_order_with_function_other_predicates_coerced topics = Topic.order(Arel.sql("author_name, LEN(title), id")).reverse_order @@ -1339,6 +1339,14 @@ def test_relations_dont_load_all_records_in_inspect_coerced end end + # Find any limit via our expression. + coerce_tests! %r{relations don't load all records in #pretty_print} + def test_relations_dont_load_all_records_in_pretty_print_coerced + assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do + PP.pp Post.all, StringIO.new # avoid outputting. + end + end + # Order column must be in the GROUP clause. coerce_tests! :test_empty_complex_chained_relations def test_empty_complex_chained_relations_coerced @@ -1368,7 +1376,7 @@ def test_empty_complex_chained_relations_coerced assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).order(:id).take end - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! :test_reverse_arel_assoc_order_with_function def test_reverse_arel_assoc_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order @@ -1739,7 +1747,7 @@ def schema_dump_path class UnsafeRawSqlTest < ActiveRecord::TestCase fixtures :posts - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! %r{order: always allows Arel} test "order: always allows Arel" do titles = Post.order(Arel.sql("len(title)")).pluck(:title) @@ -1747,7 +1755,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_not_empty titles end - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! %r{pluck: always allows Arel} test "pluck: always allows Arel" do excepted_values = Post.includes(:comments).pluck(:title).map { |title| [title, title.size] } @@ -1756,7 +1764,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal excepted_values, values end - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! %r{order: allows valid Array arguments} test "order: allows valid Array arguments" do ids_expected = Post.order(Arel.sql("author_id, len(title)")).pluck(:id) @@ -1766,7 +1774,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids end - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! %r{order: allows nested functions} test "order: allows nested functions" do ids_expected = Post.order(Arel.sql("author_id, len(trim(title))")).pluck(:id) @@ -1777,7 +1785,7 @@ class UnsafeRawSqlTest < ActiveRecord::TestCase assert_equal ids_expected, ids end - # Use LEN() vs length() function. + # Use LEN() instead of LENGTH() function. coerce_tests! %r{pluck: allows nested functions} test "pluck: allows nested functions" do title_lengths_expected = Post.pluck(Arel.sql("len(trim(title))")) From 2965c1845c5b9e4df686b89ef99b504fc22f21c6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 10:12:02 +0100 Subject: [PATCH 1112/1412] Coerced test to use nvarchar --- test/cases/coerced_tests.rb | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f22a9bedb..2ece9d18b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1441,6 +1441,26 @@ def test_named_bind_with_literal_colons_coerced end class SchemaDumperTest < ActiveRecord::TestCase + # Use nvarchar string (N'') in assert + coerce_tests! :test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order + def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced + versions = %w{ 20100101010101 20100201010101 20100301010101 } + versions.shuffle.each do |v| + @schema_migration.create_version(v) + end + + schema_info = ActiveRecord::Base.connection.dump_schema_information + expected = <<~STR + INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("schema_migrations")} (version) VALUES + (N'20100301010101'), + (N'20100201010101'), + (N'20100101010101'); + STR + assert_equal expected.strip, schema_info + ensure + @schema_migration.delete_all_versions + end + # We have precision to 38. coerce_tests! :test_schema_dump_keeps_large_precision_integer_columns_as_decimal def test_schema_dump_keeps_large_precision_integer_columns_as_decimal_coerced From 4e9ad8ad0eb2c8d1c174a6a696fc67111ea5e6fa Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 11:11:28 +0100 Subject: [PATCH 1113/1412] Update coerced_tests.rb --- test/cases/coerced_tests.rb | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2ece9d18b..485e57637 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -854,22 +854,22 @@ def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced require "models/subscriber" class EachTest < ActiveRecord::TestCase # Quoting in tests does not cope with bracket quoting. - coerce_tests! :test_find_in_batches_should_quote_batch_order - def test_find_in_batches_should_quote_batch_order_coerced - Post.connection - assert_sql(/ORDER BY \[posts\]\.\[id\]/) do - Post.find_in_batches(:batch_size => 1) do |batch| - assert_kind_of Array, batch - assert_kind_of Post, batch.first - end + # TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged. + coerce_tests! :test_in_batches_no_subqueries_for_whole_tables_batching + def test_in_batches_no_subqueries_for_whole_tables_batching_coerced + c = Post.connection + quoted_posts_id = Regexp.escape(c.quote_table_name("posts.id")) + assert_sql(/DELETE FROM #{Regexp.escape(c.quote_table_name("posts"))} WHERE #{quoted_posts_id} > .+ AND #{quoted_posts_id} <=/i) do + Post.in_batches(of: 2).delete_all end end # Quoting in tests does not cope with bracket quoting. + # TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged. coerce_tests! :test_in_batches_should_quote_batch_order def test_in_batches_should_quote_batch_order_coerced Post.connection - assert_sql(/ORDER BY \[posts\]\.\[id\]/) do + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name('posts'))}\.#{Regexp.escape(c.quote_column_name('id'))}/) do Post.in_batches(of: 1) do |relation| assert_kind_of ActiveRecord::Relation, relation assert_kind_of Post, relation.first From 38e3cffb07bf1a992f20a6b294c3efb6cda25339 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 13:01:45 +0100 Subject: [PATCH 1114/1412] Test no longer need to be coerced --- test/cases/coerced_tests.rb | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 485e57637..4b21405f3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -96,8 +96,7 @@ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced ActiveRecord::Base.while_preventing_writes do # TinyTDS fail on encoding errors. - # But at least we can assert it fails in the client and not before when trying to - # match the query. + # But at least we can assert it fails in the client and not before when trying to match the query. assert_raises ActiveRecord::StatementInvalid do @connection.select_all("SELECT '\xC8'") end @@ -1396,15 +1395,6 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query end - - # Workaround for randomly failing test. Ordering of results not guaranteed. - # TODO: Remove coerced test when https://github.com/rails/rails/pull/44168 merged. - coerce_tests! :test_select_quotes_when_using_from_clause - def test_select_quotes_when_using_from_clause_coerced - quoted_join = ActiveRecord::Base.connection.quote_table_name("join") - selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join) - assert_equal Post.pluck(:id).sort, selected.sort - end end end From 7a100c4f328753542f1f894bca35aa4305736f57 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 13:34:19 +0100 Subject: [PATCH 1115/1412] Fix query logs tests (#1085) --- .../sqlserver/database_statements.rb | 1 - test/cases/coerced_tests.rb | 48 +++++++++++-------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 95f2a8976..8d5c1bbaa 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -15,7 +15,6 @@ def write_query?(sql) # :nodoc: def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true) result = nil - sql = transform_query(sql) log(sql, name, async: async) do with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4b21405f3..01b9fcf19 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -92,6 +92,7 @@ def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes end + # Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres. coerce_tests! :test_doesnt_error_when_a_select_query_has_encoding_errors def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced ActiveRecord::Base.while_preventing_writes do @@ -2218,42 +2219,51 @@ def test_in_order_of_with_nil_coerced require "models/dashboard" class QueryLogsTest < ActiveRecord::TestCase - # Same as original coerced test except our SQL ends with binding. - # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44053) - coerce_tests! :test_custom_basic_tags, :test_custom_proc_tags, :test_multiple_custom_tags, :test_custom_proc_context_tags - def test_custom_basic_tags_coerced - ActiveRecord::QueryLogs.tags = [ :application, { custom_string: "test content" } ] - - assert_sql(%r{/\*application:active_record,custom_string:test content\*/}) do + # SQL requires double single-quotes. + coerce_tests! :test_sql_commenter_format + def test_sql_commenter_format_coerced + ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + assert_sql(%r{/\*application=''active_record''\*/}) do Dashboard.first end end - def test_custom_proc_tags_coerced - ActiveRecord::QueryLogs.tags = [ :application, { custom_proc: -> { "test content" } } ] + # SQL requires double single-quotes. + coerce_tests! :test_sqlcommenter_format_value + def test_sqlcommenter_format_value_coerced + ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + + ActiveRecord::QueryLogs.tags = [ + :application, + { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } }, + ] - assert_sql(%r{/\*application:active_record,custom_proc:test content\*/}) do + assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do Dashboard.first end end - def test_multiple_custom_tags_coerced + # SQL requires double single-quotes. + coerce_tests! :test_sqlcommenter_format_value_string_coercible + def test_sqlcommenter_format_value_string_coercible_coerced + ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + ActiveRecord::QueryLogs.tags = [ :application, - { custom_proc: -> { "test content" }, another_proc: -> { "more test content" } }, + { custom_proc: -> { 1234 } }, ] - assert_sql(%r{/\*application:active_record,custom_proc:test content,another_proc:more test content\*/}) do + assert_sql(%r{custom_proc=''1234''\*/}) do Dashboard.first end end - def test_custom_proc_context_tags_coerced - ActiveSupport::ExecutionContext[:foo] = "bar" - ActiveRecord::QueryLogs.tags = [ :application, { custom_context_proc: ->(context) { context[:foo] } } ] - - assert_sql(%r{/\*application:active_record,custom_context_proc:bar\*/}) do - Dashboard.first + # 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 ] + assert_raises ActiveRecord::StatementInvalid do + ActiveRecord::Base.connection.execute "select 1 as '\xFF'" end end end From e6173d4bea2404b6a2a579de4f1df353bb321225 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 13:34:58 +0100 Subject: [PATCH 1116/1412] Coerce test --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 01b9fcf19..838bee8a4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -513,6 +513,9 @@ def test_distinct_count_all_with_custom_select_and_order_coerced # Leave it up to users to format selects/functions so HAVING works correctly. coerce_tests! :test_having_with_strong_parameters + + # SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server. + coerce_tests! :test_ids_with_includes_and_non_primary_key_order end module ActiveRecord From 32a9c8c34a96e2038db0ac427bea996a04c723ac Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 14:53:00 +0100 Subject: [PATCH 1117/1412] Removed coerced tests that are no longer in Rails --- test/cases/coerced_tests.rb | 34 +--------------------------------- 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 838bee8a4..0c960cd74 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -106,38 +106,6 @@ def test_doesnt_error_when_a_select_query_has_encoding_errors_coerced end end -module ActiveRecord - class AdapterPreventWritesLegacyTest < ActiveRecord::TestCase - # Fix randomly failing test. The loading of the model's schema was affecting the test. - coerce_tests! :test_errors_when_an_insert_query_is_called_while_preventing_writes - def test_errors_when_an_insert_query_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema!) - original_test_errors_when_an_insert_query_is_called_while_preventing_writes - end - - # Fix randomly failing test. The loading of the model's schema was affecting the test. - coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes - def test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema!) - original_test_errors_when_an_insert_query_prefixed_by_a_slash_star_comment_is_called_while_preventing_writes - end - - # Fix randomly failing test. The loading of the model's schema was affecting the test. - coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes - def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema!) - original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_is_called_while_preventing_writes - end - - # Fix randomly failing test. The loading of the model's schema was affecting the test. - coerce_tests! :test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes - def test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes_coerced - Subscriber.send(:load_schema!) - original_test_errors_when_an_insert_query_prefixed_by_a_double_dash_comment_containing_read_command_is_called_while_preventing_writes - end - end -end - module ActiveRecord class AdapterTestWithoutTransaction < ActiveRecord::TestCase # SQL Server does not allow truncation of tables that are referenced by foreign key @@ -513,7 +481,7 @@ def test_distinct_count_all_with_custom_select_and_order_coerced # Leave it up to users to format selects/functions so HAVING works correctly. coerce_tests! :test_having_with_strong_parameters - + # SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server. coerce_tests! :test_ids_with_includes_and_non_primary_key_order end From 1ece12bb62a54316e855d88b44c83a10ad43d3fa Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 15:35:51 +0100 Subject: [PATCH 1118/1412] Update coerced_tests.rb --- test/cases/coerced_tests.rb | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0c960cd74..2aa2ee05d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2103,6 +2103,35 @@ def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflectio end end +class PreloaderTest < ActiveRecord::TestCase + # SQL format for `order_id_constraint` different. + coerce_tests! :test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute + def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute_coerced + order = cpk_orders(:cpk_groceries_order_2) + _shop_id, order_id = order.id + order_agreements = Cpk::OrderAgreement.where(order_id: order_id).to_a + + assert_not_empty order_agreements + assert_equal order_agreements.sort, order.order_agreements.sort + + loaded_order = nil + sql = capture_sql do + loaded_order = Cpk::Order.where(id: order_id).includes(:order_agreements).to_a.first + end + + assert_equal 2, sql.size + preload_sql = sql.last + + c = Cpk::OrderAgreement.connection + order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id")) + order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/ + expectation = /SELECT.*WHERE.* #{order_id_constraint}/ + + assert_match(expectation, preload_sql) + assert_equal order_agreements.sort, loaded_order.order_agreements.sort + end +end + class BasePreventWritesTest < ActiveRecord::TestCase # SQL Server does not have query for release_savepoint coerce_tests! %r{an empty transaction does not raise if preventing writes} From a49ffff93eec0ded89437f8d913ee1104151442c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 15:36:41 +0100 Subject: [PATCH 1119/1412] In SQL Server only the first column added should have the `ADD` keyword (#1086) --- .../connection_adapters/sqlserver/schema_statements.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3129ee898..64c7484eb 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -256,6 +256,13 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) end end + # 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(', ')}" + end + def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map { |s| s = s.to_sql unless s.is_a?(String) From d248f4e58bf3afa71026a827b3a73ea9988ffec7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Sep 2023 15:57:56 +0100 Subject: [PATCH 1120/1412] Update coerced_tests.rb --- test/cases/coerced_tests.rb | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2aa2ee05d..1595fb621 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2104,7 +2104,7 @@ def test_has_many_through_has_and_belongs_to_many_with_has_many_source_reflectio end class PreloaderTest < ActiveRecord::TestCase - # SQL format for `order_id_constraint` different. + # Need to handle query parameters in SQL regex. coerce_tests! :test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attribute_coerced order = cpk_orders(:cpk_groceries_order_2) @@ -2130,6 +2130,30 @@ def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attr assert_match(expectation, preload_sql) assert_equal order_agreements.sort, loaded_order.order_agreements.sort end + + # Need to handle query parameters in SQL regex. + coerce_tests! :test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute + def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_coerced + order_agreement = cpk_order_agreements(:order_agreement_three) + order = cpk_orders(:cpk_groceries_order_2) + assert_equal order, order_agreement.order + + loaded_order_agreement = nil + sql = capture_sql do + loaded_order_agreement = Cpk::OrderAgreement.where(id: order_agreement.id).includes(:order).to_a.first + end + + assert_equal 2, sql.size + preload_sql = sql.last + + c = Cpk::Order.connection + order_id = Regexp.escape(c.quote_table_name("cpk_orders.id")) + order_constraint = /#{order_id} = @0.*@0 = \d+$/ + expectation = /SELECT.*WHERE.* #{order_constraint}/ + + assert_match(expectation, preload_sql) + assert_equal order, loaded_order_agreement.order + end end class BasePreventWritesTest < ActiveRecord::TestCase From 831712a65348b0f674c03d025157f29ea48facf6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 19 Sep 2023 14:27:13 +0100 Subject: [PATCH 1121/1412] Fix creation of stored procedures that contain insert statements (#1088) --- CHANGELOG.md | 4 ++++ .../sqlserver/database_statements.rb | 2 +- test/cases/migration_test_sqlserver.rb | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 501714c3b..f57bdab0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ - [#1073](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1073) Improve performance of view default function lookup +#### Fixed + +- [#1088](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1088) Fix creation of stored procedures that contain insert statements + ## v7.0.3.0 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 8d5c1bbaa..b518e06c8 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -426,7 +426,7 @@ def query_requires_identity_insert?(sql) end def insert_sql?(sql) - !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? + !(sql =~ /\A\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? end def identity_columns(table_name) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 132d69115..74b14eb18 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -115,4 +115,22 @@ class MigrationTestSQLServer < ActiveRecord::TestCase refute_includes schemas, { "name" => "some schema" } end end + + describe 'creating stored procedure' do + it 'stored procedure contains inserts are created successfully' do + sql = <<-SQL + CREATE OR ALTER PROCEDURE do_some_task + AS + IF NOT EXISTS(SELECT * FROM sys.objects WHERE type = 'U' AND name = 'SomeTableName') + BEGIN + CREATE TABLE SomeTableName (SomeNum int PRIMARY KEY CLUSTERED); + INSERT INTO SomeTableName(SomeNum) VALUES(1); + END + SQL + + assert_nothing_raised { connection.execute(sql) } + ensure + connection.execute("DROP PROCEDURE IF EXISTS dbo.do_some_task;") + end + end end From c2ac6d565e9267a3186a3a170999d27f4a1bd98c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 19 Sep 2023 14:43:43 +0100 Subject: [PATCH 1122/1412] When changing columns set date-time columns to datetime(6) by default (#1089) --- .../sqlserver/schema_statements.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 64c7484eb..813e34cc7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -146,6 +146,15 @@ def remove_column(table_name, column_name, type = nil, **options) def change_column(table_name, column_name, type, options = {}) sql_commands = [] indexes = [] + + if type == :datetime + # If no precision then default it to 6. + options[:precision] = 6 unless options.key?(:precision) + + # If there is precision then column must be of type 'datetime2'. + type = :datetime2 unless options[:precision].nil? + end + 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 @@ -153,24 +162,29 @@ def change_column(table_name, column_name, type, options = {}) else options[:default] end + if without_constraints || (column_object && column_object.type != type.to_sym) remove_default_constraint(table_name, column_name) indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) } remove_indexes(table_name, column_name) end + sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil? alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}" alter_command += " COLLATE #{options[:collation]}" if options[:collation].present? alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false sql_commands << alter_command + if without_constraints default = quote_default_expression(default, column_object || column_for(table_name, column_name)) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}" end + # 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(', ')})" end + sql_commands.each { |c| execute(c) } clear_cache! end @@ -232,6 +246,7 @@ def extract_foreign_key_action(action, fk_name) def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable + case type.to_s when "integer" case limit From 8d9b02a0dae9bea6b52dd161f370a499bf2096ce Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 19 Sep 2023 15:20:59 +0100 Subject: [PATCH 1123/1412] Cleanup for 7.1 branch --- CHANGELOG.md | 68 +--------------------------------------------------- 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f57bdab0b..abfa1a3cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,74 +2,8 @@ #### Changed -- [#1073](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1073) Improve performance of view default function lookup - -#### Fixed - -- [#1088](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1088) Fix creation of stored procedures that contain insert statements - -## v7.0.3.0 - -#### Fixed - -- [#1052](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1052) Ignore casing of VALUES clause when inserting records using SQL -- [#1053](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1053) Fix insertion of records to non-default schema table using raw SQL -- [#1059](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1059) Fix enums defined on string columns - -#### Changed - -- [#1057](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1057) Set precision 6 by default for timestamps. - -## v7.0.2.0 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.1.0...v7.0.2.0) - -#### Fixed - -- [#1049](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1049) Fix for inserting records into non-dbo schema table. - -## v7.0.1.0 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0...v7.0.1.0) - #### Fixed -- [#1029](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1029) Handle views defined in other databases. -- [#1033](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1033) Support proc default values in ruby 3.2. -- [#1020](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1020) Support using adapter as a stand-alone gem. -- [#1039](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1039) Fix hook method that allows custom connection configuration. - -#### Changed - -- [#1021](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1021) Freeze the SQL sent to instrumentation. - -## v7.0.0.0 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/v7.0.0.0.rc1...v7.0.0.0) - -#### Fixed - -- [#1002](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1002) Fix support for index types - -#### Changed - -- [#1004](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1004) Dump the precision for datetime columns following the new defaults. - -## v7.0.0.0.rc1 - -[Full changelog](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/compare/6-1-stable...v7.0.0.0.rc1) - -#### Changed - -- [#968](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/968) Define adapter type maps statically -- [#983](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/983) Optimize remove_columns to use a single SQL statement -- [#984](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/984) Better handle SQL queries with invalid encoding -- [#988](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/988) Raise `ActiveRecord::StatementInvalid` when `columns` is called with a non-existing table (***breaking change***) - #### Added -- [#972](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/972) Support `ActiveRecord::QueryLogs` -- [#981](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/981) Support `find_by` an encrypted attribute -- [#985](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/985) Support string returning clause for `ActiveRecord#insert_all` - -Please check [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/6-1-stable/CHANGELOG.md) for previous changes. +Please check [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-0-stable/CHANGELOG.md) for previous changes. From b0dbf8fd968f7ee76b7631e1e2af87ef9a332024 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 19 Sep 2023 16:45:19 +0100 Subject: [PATCH 1124/1412] Update calculate monkey-patch (#1090) --- .../sqlserver/core_ext/calculations.rb | 35 +++++++++++++++---- .../sqlserver/core_ext/finder_methods.rb | 12 +++++-- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index da0e37f80..34355ef53 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -8,18 +8,43 @@ module ConnectionAdapters module SQLServer module CoreExt module Calculations - # Same as original except we don't perform PostgreSQL hack that removes ordering. def calculate(operation, column_name) - return super unless klass.connection.adapter_name == "SQLServer" + if klass.connection.sqlserver? + _calculate(operation, column_name) + else + super + end + end + + private + + # Same as original `calculate` method except we don't perform PostgreSQL hack that removes ordering. + def _calculate(operation, column_name) + operation = operation.to_s.downcase + + if @none + case operation + when "count", "sum" + result = group_values.any? ? Hash.new : 0 + return @async ? Promise::Complete.new(result) : result + when "average", "minimum", "maximum" + result = group_values.any? ? Hash.new : nil + return @async ? Promise::Complete.new(result) : result + end + end if has_include?(column_name) relation = apply_join_dependency - if operation.to_s.downcase == "count" + if operation == "count" unless distinct_value || distinct_select?(column_name || select_for_count) relation.distinct! - relation.select_values = [klass.primary_key || table[Arel.star]] + relation.select_values = [ klass.primary_key || table[Arel.star] ] end + # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT + # Start of monkey-patch + # relation.order_values = [] if group_values.empty? + # End of monkey-patch end relation.calculate(operation, column_name) @@ -28,8 +53,6 @@ def calculate(operation, column_name) end end - private - def build_count_subquery(relation, column_name, distinct) return super unless klass.connection.adapter_name == "SQLServer" 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 03932d492..fe9271508 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 @@ -10,18 +10,26 @@ module CoreExt module FinderMethods private - # Same as original except we order by values in distinct select if present. def construct_relation_for_exists(conditions) - return super unless klass.connection.adapter_name == "SQLServer" + if klass.connection.sqlserver? + _construct_relation_for_exists(conditions) + else + super + end + end + # Same as original except we order by values in distinct select if present. + def _construct_relation_for_exists(conditions) conditions = sanitize_forbidden_attributes(conditions) if distinct_value && offset_value + # Start of monkey-patch if select_values.present? relation = order(*select_values).limit!(1) else relation = except(:order).limit!(1) end + # End of monkey-patch else relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1) end From f354e9841f87dbb707293fa988823cacd542f83c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 12:55:22 +0100 Subject: [PATCH 1125/1412] Fix typo --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1595fb621..64cbcd5c5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -839,7 +839,7 @@ def test_in_batches_no_subqueries_for_whole_tables_batching_coerced # TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged. coerce_tests! :test_in_batches_should_quote_batch_order def test_in_batches_should_quote_batch_order_coerced - Post.connection + c = Post.connection assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name('posts'))}\.#{Regexp.escape(c.quote_column_name('id'))}/) do Post.in_batches(of: 1) do |relation| assert_kind_of ActiveRecord::Relation, relation From 8c81e8369f42c4ea532abb0483bc150a337c9b7b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 14:27:49 +0100 Subject: [PATCH 1126/1412] Coerce test --- test/cases/coerced_tests.rb | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 64cbcd5c5..17564ab68 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -870,14 +870,6 @@ class FinderTest < ActiveRecord::TestCase coerce_tests! %r{doesn't have implicit ordering}, :test_find_doesnt_have_implicit_ordering - # Square brackets around column name - coerce_tests! :test_exists_does_not_select_columns_without_alias - def test_exists_does_not_select_columns_without_alias_coerced - assert_sql(/SELECT\s+1 AS one FROM \[topics\].*OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) do - Topic.exists? - end - end - # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced @@ -989,6 +981,15 @@ def test_implicit_order_for_model_without_primary_key_coerced NonPrimaryKey.implicit_order_column = old_implicit_order_column end + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key + def test_member_on_unloaded_relation_with_composite_primary_key_coerced + assert_sql(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + book = cpk_books(:cpk_great_author_first_book) + assert Cpk::Book.where(title: "The first book").member?(book) + end + end + # SQL Server is unable to use aliased SELECT in the HAVING clause. coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select end @@ -1066,7 +1067,7 @@ def test_a_bad_type_column_coerced def test_eager_load_belongs_to_primary_key_quoting_coerced Account.connection assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do - Account.all.merge!(:includes => :firm).find(1) + Account.all.merge!(includes: :firm).find(1) end end end From 1d278fff7ff75950e631a17e395be581721de008 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 14:30:47 +0100 Subject: [PATCH 1127/1412] Fixes for use clause (#1091) --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- .../connection_adapters/sqlserver/schema_statements.rb | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index b518e06c8..7efc1c8fa 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -4,7 +4,7 @@ module ActiveRecord module ConnectionAdapters module SQLServer module DatabaseStatements - READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor) # :nodoc: + READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback, :waitfor, :use) # :nodoc: private_constant :READ_QUERY def write_query?(sql) # :nodoc: @@ -217,7 +217,7 @@ def use_database(database = nil) return if sqlserver_azure? name = SQLServer::Utils.extract_identifiers(database || @connection_parameters[:database]).quoted - execute "USE #{name}" unless name.blank? + execute("USE #{name}", "SCHEMA") unless name.blank? end def user_options diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 813e34cc7..3f402bcf5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -118,10 +118,12 @@ def primary_keys_select(table_name) AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' ORDER BY KCU.ORDINAL_POSITION ASC }.gsub(/[[:space:]]/, " ") + binds = [] nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? + sp_executesql(sql, "SCHEMA", binds).map { |r| r["name"] } end From bbc2708b0292d1f72771500fdd7cbb6298ee5893 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 15:27:33 +0100 Subject: [PATCH 1128/1412] Test no longer needs to be coerced --- test/cases/coerced_tests.rb | 8 -------- 1 file changed, 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 17564ab68..9a739193a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -811,14 +811,6 @@ class DatabaseTasksDropAllTest < ActiveRecord::TestCase class DefaultScopingTest < ActiveRecord::TestCase # We are not doing order duplicate removal anymore. coerce_tests! :test_order_in_default_scope_should_not_prevail - - # Use our escaped format in assertion. - coerce_tests! :test_with_abstract_class_scope_should_be_executed_in_correct_context - def test_with_abstract_class_scope_should_be_executed_in_correct_context_coerced - vegetarian_pattern, gender_pattern = [/[lions].[is_vegetarian]/, /[lions].[gender]/] - assert_match vegetarian_pattern, Lion.all.to_sql - assert_match gender_pattern, Lion.female.to_sql - end end require "models/post" From 46bf67664b96f6f092f15f5157517e01d59acb55 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 15:44:53 +0100 Subject: [PATCH 1129/1412] Escape regexs (#1093) --- test/cases/coerced_tests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9a739193a..f1b6fe1b4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1252,7 +1252,7 @@ def test_reorder_with_take_coerced sql_log = capture_sql do 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.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}" end @@ -1264,7 +1264,7 @@ def test_reorder_with_first_coerced post = Post.order(:title).reorder(nil).first 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.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}" end From c3fab524845d1fe4462b7d0637eee06c88a6277b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Sep 2023 15:45:49 +0100 Subject: [PATCH 1130/1412] Coerce tests that require FETCH NEXT sql (#1092) --- test/cases/coerced_tests.rb | 77 +++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f1b6fe1b4..2a01d3c6d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -982,6 +982,83 @@ def test_member_on_unloaded_relation_with_composite_primary_key_coerced end end + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_column_prepends_query_constraints + def test_implicit_order_column_prepends_query_constraints_coerced + c = ClothingItem.connection + ClothingItem.implicit_order_column = "description" + quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) + quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) + quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description")) + + assert_sql(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_kind_of ClothingItem, ClothingItem.first + end + ensure + ClothingItem.implicit_order_column = nil + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! %r{#last for a model with composite query constraints} + test "#last for a model with composite query constraints coerced" do + c = ClothingItem.connection + quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) + quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) + + assert_sql(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_kind_of ClothingItem, ClothingItem.last + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! %r{#first for a model with composite query constraints} + test "#first for a model with composite query constraints coerced" do + c = ClothingItem.connection + quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) + quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) + + assert_sql(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_kind_of ClothingItem, ClothingItem.first + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_column_reorders_query_constraints + def test_implicit_order_column_reorders_query_constraints_coerced + c = ClothingItem.connection + ClothingItem.implicit_order_column = "color" + quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) + quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) + + assert_sql(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_kind_of ClothingItem, ClothingItem.first + end + ensure + ClothingItem.implicit_order_column = nil + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key + def test_include_on_unloaded_relation_with_composite_primary_key_coerced + assert_sql(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + book = cpk_books(:cpk_great_author_first_book) + assert Cpk::Book.where(title: "The first book").include?(book) + end + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_nth_to_last_with_order_uses_limit + def test_nth_to_last_with_order_uses_limit_coerced + c = Topic.connection + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + Topic.second_to_last + end + + assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + Topic.order(:updated_at).second_to_last + end + end + # SQL Server is unable to use aliased SELECT in the HAVING clause. coerce_tests! :test_include_on_unloaded_relation_with_having_referencing_aliased_select end From 702d21258c0783a0d5ed0a82b909b425aef1d2be Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 21 Sep 2023 11:25:56 +0100 Subject: [PATCH 1131/1412] Update coerced_tests.rb --- test/cases/coerced_tests.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2a01d3c6d..86b8bf29a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -850,6 +850,23 @@ def test_count_with_include_coerced # Use TOP (1) in scope vs limit 1. coerce_tests! %r{including association based on sql condition and no database column} + + # Table name not regex escaped in original test. Remove coerced test when https://github.com/rails/rails/pull/49345 merged. + coerce_tests! %r{preloading belongs_to association SQL} + test "preloading belongs_to association SQL coerced" do + blog_ids = [sharded_blogs(:sharded_blog_one).id, sharded_blogs(:sharded_blog_two).id] + posts = Sharded::BlogPost.where(blog_id: blog_ids).includes(:comments) + + sql = capture_sql do + comments_collection = posts.map(&:comments) + assert_equal 3, comments_collection.size + end.last + + c = Sharded::BlogPost.connection + quoted_blog_id = Regexp.escape(c.quote_table_name("sharded_comments.blog_id")) + quoted_blog_post_id = Regexp.escape(c.quote_table_name("sharded_comments.blog_post_id")) + assert_match(/WHERE #{quoted_blog_id} IN \(.+\) AND #{quoted_blog_post_id} IN \(.+\)/, sql) + end end require "models/topic" From d7d473ed167ab15de0770f0285b8b72ec2db753e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 25 Sep 2023 14:29:19 +0100 Subject: [PATCH 1132/1412] Run CI against Rails release instead of main branch --- Dockerfile.ci | 2 +- docker-compose.ci.yml | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 7f75f1a9e..0ded95afd 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_MAIN=1 bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 82c48d48a..cbc842619 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,7 +5,6 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_MAIN=1 build: context: . dockerfile: Dockerfile.ci From 19234ab0a948cd9a6a454fc3a829092db6eb02bb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 25 Sep 2023 15:52:38 +0100 Subject: [PATCH 1133/1412] Coerce tests that rely on upsert (#1094) --- test/cases/coerced_tests.rb | 68 ++++++++++++------------------------- 1 file changed, 21 insertions(+), 47 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 86b8bf29a..5cb942a80 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2379,55 +2379,37 @@ def test_invalid_encoding_query_coerced end end -# SQL Server does not support upsert yet -# TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44050) class InsertAllTest < ActiveRecord::TestCase - coerce_tests! :test_upsert_all_only_updates_the_column_provided_via_update_only - def test_upsert_all_only_updates_the_column_provided_via_update_only_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_only_updates_the_column_provided_via_update_only - end - end - - coerce_tests! :test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only - def test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_only_updates_the_list_of_columns_provided_via_update_only - end - end - - coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true - def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_true - end - end + # Same as original but using INSERTED.name as UPPER argument + coerce_tests! :test_insert_all_returns_requested_sql_fields + def test_insert_all_returns_requested_sql_fields_coerced + skip unless supports_insert_returning? - coerce_tests! :test_upsert_all_respects_created_at_precision_when_touched_implicitly - def test_upsert_all_respects_created_at_precision_when_touched_implicitly_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_respects_created_at_precision_when_touched_implicitly - end + 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 - coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden - def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden_coerced + # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 + coerce_tests! :test_insert_all_should_handle_empty_arrays + def test_insert_all_should_handle_empty_arrays_coerced assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_true_but_overridden + original_test_insert_all_should_handle_empty_arrays end end - coerce_tests! :test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false - def test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false_coerced + # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 + coerce_tests! :test_insert_all_and_upsert_all_with_aliased_attributes + def test_insert_all_and_upsert_all_with_aliased_attributes_coerced assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_does_not_implicitly_set_timestamps_on_create_when_model_record_timestamps_is_false + original_test_insert_all_and_upsert_all_with_aliased_attributes end end - coerce_tests! :test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden - def test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden_coerced + # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 + coerce_tests! :test_insert_all_and_upsert_all_with_sti + def test_insert_all_and_upsert_all_with_sti_coerced assert_raises(ArgumentError, /does not support upsert/) do - original_test_upsert_all_implicitly_sets_timestamps_on_create_when_model_record_timestamps_is_false_but_overridden + original_test_insert_all_and_upsert_all_with_sti end end end @@ -2451,17 +2433,6 @@ def test_disable_joins_through_with_enum_type_coerced end end -class InsertAllTest < ActiveRecord::TestCase - coerce_tests! :test_insert_all_returns_requested_sql_fields - # Same as original but using INSERTED.name as UPPER argument - 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") - end -end - class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052) # Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters @@ -2472,4 +2443,7 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption author = EncryptedAuthor.create(name: "a" * 4001) assert_not author.valid? end + + # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 + coerce_tests! %r{loading records with encrypted attributes defined on columns with default values} end From 38061fd7ab087786c260f7ac14c34121ea276cba Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 25 Sep 2023 15:52:52 +0100 Subject: [PATCH 1134/1412] Include the is_identity key in the tests (#1095) --- test/cases/coerced_tests.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5cb942a80..d26a053e5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2447,3 +2447,17 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 coerce_tests! %r{loading records with encrypted attributes defined on columns with default values} end + +module ActiveRecord + class Migration + class InvalidOptionsTest < ActiveRecord::TestCase + # Include the additional SQL Server migration options. + def invalid_add_column_option_exception_message(key) + default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"] + default_keys.concat([":is_identity"]) # SQL Server additional valid keys + + "Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}" + end + end + end +end From 711339e72f3b3062ab43e87fecdb3ca5fc02656d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 25 Sep 2023 16:25:43 +0100 Subject: [PATCH 1135/1412] Queries to find indexes are schema queries (#1096) --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3f402bcf5..eff05c7d9 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -44,7 +44,7 @@ def indexes(table_name) else name = index[:index_name] unique = index[:index_description] =~ /unique/ - where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}") + where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") orders = {} columns = [] From 2ccb0378bf90123511871eed2b0b460c836712c9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 26 Sep 2023 14:25:37 +0100 Subject: [PATCH 1136/1412] Coerced test --- test/cases/coerced_tests.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d26a053e5..1cd2eb60f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -45,6 +45,19 @@ def test_validate_uniqueness_by_default_database_collation_coerced assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count assert_equal 1, Topic.where(author_email_address: "David@loudthinking.com").count end + + # Need to explicitly set the WHERE clause to truthy. + coerce_tests! :test_partial_index + def test_partial_index_coerced + Topic.validates_uniqueness_of(:title) + @connection.add_index(:topics, :title, unique: true, where: "approved=1", name: :topics_index) + + t = Topic.create!(title: "abc") + t.author_name = "John" + assert_queries(1) do + t.valid? + end + end end require "models/event" From c571dc5f9484b6aa5bb404c387b20d14a273134f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 26 Sep 2023 14:42:18 +0100 Subject: [PATCH 1137/1412] Remove dependency on upsert in test --- test/cases/coerced_tests.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1cd2eb60f..60c24703f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2474,3 +2474,18 @@ def invalid_add_column_option_exception_message(key) end end end + +# SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert. +class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase + def thread_encrypting_and_decrypting(thread_label) + posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" } + + Thread.new do + posts.each.with_index do |article, index| + assert_encrypted_attribute article, :title, "Article #{index} (#{thread_label})" + article.decrypt + assert_not_encrypted_attribute article, :title, "Article #{index} (#{thread_label})" + end + end + end +end From c2bce850973aaf1ea534622d6139136c77aef6b0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 16:35:29 +0100 Subject: [PATCH 1138/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3359879eb..8eaa4b0d1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|----------------------|----------------|-------------------------------------------------------------------------------------------------| | n/a | `7.1.0` (unreleased) | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.0.3.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `7.0.4.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | From fa7d6cf7d1cc2491de28387895fd38b2f54073af Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 17:15:41 +0100 Subject: [PATCH 1139/1412] Fix foreign key tests (#1097) --- .../sqlserver/schema_statements.rb | 20 +++++++++++++------ test/cases/coerced_tests.rb | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index eff05c7d9..970abae3d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -224,17 +224,25 @@ def remove_index!(table_name, index_name) def foreign_keys(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema - fk_info.map do |row| - from_table = identifier.object - to_table = row["PKTABLE_NAME"] + + grouped_fk = fk_info.group_by { |row| row["FK_NAME"] }.values.each { |group| group.sort_by! { |row| row["KEY_SEQ"] } } + grouped_fk.map do |group| + row = group.first options = { name: row["FK_NAME"], - column: row["FKCOLUMN_NAME"], - primary_key: row["PKCOLUMN_NAME"], on_update: extract_foreign_key_action("update", row["FK_NAME"]), on_delete: extract_foreign_key_action("delete", row["FK_NAME"]) } - ForeignKeyDefinition.new from_table, to_table, options + + if group.one? + options[:column] = row["FKCOLUMN_NAME"] + options[:primary_key] = row["PKCOLUMN_NAME"] + else + options[:column] = group.map { |row| row["FKCOLUMN_NAME"] } + options[:primary_key] = group.map { |row| row["PKCOLUMN_NAME"] } + end + + ForeignKeyDefinition.new(identifier.object, row["PKTABLE_NAME"], options) end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 60c24703f..12bb8bdd1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1096,7 +1096,7 @@ def test_nth_to_last_with_order_uses_limit_coerced module ActiveRecord class Migration class ForeignKeyTest < ActiveRecord::TestCase - # We do not support :restrict. + # SQL Server does not support 'restrict' for 'on_update' or 'on_delete'. coerce_tests! :test_add_on_delete_restrict_foreign_key def test_add_on_delete_restrict_foreign_key_coerced assert_raises ArgumentError do From e49e656dcf752597a30691c42a0e62f96247595c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 17:20:17 +0100 Subject: [PATCH 1140/1412] Fix coerced test --- test/cases/coerced_tests.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 12bb8bdd1..95ae0adf1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -45,7 +45,9 @@ def test_validate_uniqueness_by_default_database_collation_coerced assert_equal 1, Topic.where(author_email_address: "david@loudthinking.com").count assert_equal 1, Topic.where(author_email_address: "David@loudthinking.com").count end +end +class UniquenessValidationWithIndexTest < ActiveRecord::TestCase # Need to explicitly set the WHERE clause to truthy. coerce_tests! :test_partial_index def test_partial_index_coerced From 03c7c3a554043ce2c29ec1a1d0e454335f561382 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 20:42:34 +0100 Subject: [PATCH 1141/1412] Update coerced test --- test/cases/coerced_tests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 95ae0adf1..d921ed009 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -648,11 +648,11 @@ def migrate(x) end }.new - ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, 100).migrate + ActiveRecord::Migrator.new(:up, [migration_a], @schema_migration, @internal_metadata, 100).migrate assert_column Person, :last_name, "migration_a should have created the last_name column on people" assert_nothing_raised do - ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, 101).migrate + ActiveRecord::Migrator.new(:up, [migration_b], @schema_migration, @internal_metadata, 101).migrate end ensure Person.reset_column_information From 37772487ed5e5ab2756b43d7d7832d758eaa2fc5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 20:50:46 +0100 Subject: [PATCH 1142/1412] Removed coerced test that no longer exists --- test/cases/coerced_tests.rb | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d921ed009..fed6ec488 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2270,20 +2270,6 @@ class BasePreventWritesTest < ActiveRecord::TestCase end end end - - class BasePreventWritesLegacyTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint - coerce_tests! %r{an empty transaction does not raise if preventing writes} - test "an empty transaction does not raise if preventing writes coerced" do - ActiveRecord::Base.connection_handler.while_preventing_writes do - assert_queries(1, ignore_none: true) do - Bird.transaction do - ActiveRecord::Base.connection.materialize_transactions - end - end - end - end - end end class MigratorTest < ActiveRecord::TestCase From ed474fb7847e4eefd077d7939beabf383401c94f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 22:04:04 +0100 Subject: [PATCH 1143/1412] Unique is expected to be a boolean (#1098) --- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 970abae3d..5e906e063 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -39,11 +39,11 @@ def indexes(table_name) data.reduce([]) do |indexes, index| index = index.with_indifferent_access - if index[:index_description] =~ /primary key/ + if index[:index_description].match?(/primary key/) indexes else name = index[:index_name] - unique = index[:index_description] =~ /unique/ + unique = index[:index_description].match?(/unique/) where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") orders = {} columns = [] From 867dbc4596cc0196712fcaf0ddb0519812926572 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 27 Sep 2023 22:05:10 +0100 Subject: [PATCH 1144/1412] Commented out test --- test/cases/coerced_tests.rb | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index fed6ec488..00ae75b35 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2153,14 +2153,15 @@ def test_where_in_binds_logging_include_attribute_names_coerced end end -class ActiveRecordSchemaTest < ActiveRecord::TestCase - # Workaround for randomly failing test. - coerce_tests! :test_has_primary_key - def test_has_primary_key_coerced - @schema_migration.reset_column_information - original_test_has_primary_key - end -end +# TODO: Method `reset_column_information` does not exist. Comment out the test for the time being. +# class ActiveRecordSchemaTest < ActiveRecord::TestCase +# # Workaround for randomly failing test. +# coerce_tests! :test_has_primary_key +# def test_has_primary_key_coerced +# @schema_migration.reset_column_information +# original_test_has_primary_key +# end +# end class ReloadModelsTest < ActiveRecord::TestCase # Skip test on Windows. The number of arguments passed to `IO.popen` in From 32632b9fe3f8ab04d63f153501f8b4842f9eb21a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 28 Sep 2023 14:50:09 +0100 Subject: [PATCH 1145/1412] Support common table expressions Fixes ActiveRecord::WithTest#test_common_table_expressions_are_unsupported --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 6c7e3d6a5..f8179f86f 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -188,6 +188,10 @@ def supports_optimizer_hints? true end + def supports_common_table_expressions? + true + end + def supports_lazy_transactions? true end From 7f662e7d8e3a6b8d660f569b3224692cd94cd37c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 13:39:20 +0100 Subject: [PATCH 1146/1412] Updated for Rails v7.1.0.rc2 (#1102) --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- .../sqlserver/database_statements.rb | 3 ++- test/cases/coerced_tests.rb | 27 ------------------- 4 files changed, 4 insertions(+), 30 deletions(-) diff --git a/VERSION b/VERSION index 2adc99526..68356638f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.beta1 +7.1.0.rc2 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index d63505c42..f0d10d457 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.1.0.beta1" + spec.add_dependency "activerecord", "~> 7.1.0.rc2" spec.add_dependency "tiny_tds" end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7efc1c8fa..217677ff4 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -308,7 +308,8 @@ def sql_for_insert(sql, pk, binds, _returning) else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" end - super + + [sql, binds] end # === SQLServer Specific ======================================== # diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 00ae75b35..690edd829 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2390,30 +2390,6 @@ def test_insert_all_returns_requested_sql_fields_coerced 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 - - # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 - coerce_tests! :test_insert_all_should_handle_empty_arrays - def test_insert_all_should_handle_empty_arrays_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_insert_all_should_handle_empty_arrays - end - end - - # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 - coerce_tests! :test_insert_all_and_upsert_all_with_aliased_attributes - def test_insert_all_and_upsert_all_with_aliased_attributes_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_insert_all_and_upsert_all_with_aliased_attributes - end - end - - # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 - coerce_tests! :test_insert_all_and_upsert_all_with_sti - def test_insert_all_and_upsert_all_with_sti_coerced - assert_raises(ArgumentError, /does not support upsert/) do - original_test_insert_all_and_upsert_all_with_sti - end - end end class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase @@ -2445,9 +2421,6 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption author = EncryptedAuthor.create(name: "a" * 4001) assert_not author.valid? end - - # SQL Server does not support upsert. Coerce can be removed after https://github.com/rails/rails/pull/49344 - coerce_tests! %r{loading records with encrypted attributes defined on columns with default values} end module ActiveRecord From 363567623296eaf14ac7c1a29590612817022ded Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 13:43:21 +0100 Subject: [PATCH 1147/1412] Created dumps for marshal tests (#1100) --- .../SQLServer/rails_7_1_topic.dump | Bin 0 -> 2572 bytes .../SQLServer/rails_7_1_topic_associations.dump | Bin 0 -> 3554 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump create mode 100644 test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump new file mode 100644 index 0000000000000000000000000000000000000000..e1967b46830d3e060a2bbc0cc92411ddf41f0b7a GIT binary patch literal 2572 zcmbVO-EP}96!v~#TZ$7WO@ER#XlD~>LC_LybD>3z zqTIU0uszD2U^jb#KF4nN7Q5*_N_PGRY()|X7I_Xoo}cfWqmS#F_&uG<=r&oViFnD3 zh_s12SaEBO?B`ZwnzZ*KD<|Tqj)fxR34cA^n@gVy+X!oKDx^UtT;(Erv(|0bWLzcH zuY0oEZTnVQC90v{>$b1{F-JK$CvEy#-e z=w)&rKlmRsc6T2=+WCB^+ipZUfep4aX>)69tMv_^h!$@pG7_zrPyOxfZNC&R$#JTU z<)wPiZLjua!p)SO0m1GfPgA2OqObr~Uoj$8Qe;re%cCyr>vfyXlU>G+YGjdSdEZeA!F8cG zM>S2l=K?=Ypn!NPT&H1>vBNz@}Ju!y>L~B zSL@-mUU*$?)m%Y`H(;%Mp||NCZQ;3tGHT7s=qaN8P z=O^onfi|MJfJGQKfomSPjXuC-+DJ4k+<$UgKvt7dfxz|x7D zcF0ZFDkoCsnVPb*6Tb;hGHdiyVEk_Y!5jkuwvz6=s`)cik*IvEm!UT!0{ux>9e+Y=Dj=moO0vb!;j?!lZ-Y?vR{lxx>!P z(x!zVd6Ygu|MUg?9R1t3=pXl+*`-7(Kx0S(!QGiNXU^q2-#PqERgrp6O=a|eY|>Ob zXIex`#~r+JYqcC?Rv1Ovhmn;taiU_85b~J6oF6WBp9$LxE9WKBq%)pm!kpjkwkt9& zlk#7BvfORgt+a_KhxJ~!eX}L$Ng-E%$Y)UD&+HVtV!~y@ zcpPhCOuls|c49P9S!~BLJ(KBZFH)0ybK|*|){2y=)GuVD){_&lw*K#(R}aVknwClJ zrAkG&T|Mm`L!#4dZwyqL8D=4t9|;?4d!^E=QmHg8hfTWGZLcTXSVW0?>rP4SA-AI6 zkR|!n^YlY};eXgXIQZM(c-OCMxqt-d3|qhubywO z%gIz}%X9Id+uk0?l_e{XltD7Zw zG36Rou{_4mxm7G&VE;JH51DnktFJ-lp0@ZXA<@!YQrVVeEt{;O~L|9r0q z(?&Lp|E~yXP^aGfaGTZ?Im+?%)Ps+ZW0j@?h*Rld%%@gpL&&o)A3qa%CNv>E938k`8A*+e`M?jeyAY2_N8G*3qYV1MeqtE47gL`jd-2Vx}CWVeP4 zyT6}ERitt9$;CUZE7G}!ai>TxisdV=qcPWYQm0mDz*^Zy4E79pV%+=C#WdUu!mS{@ zA%FE>+En4~dbmUP!guL!FE)Ptj}5v`1G=fm;Wf;0Aa&qBsD+!d;qMB_I(0yIH4xBk zMLxfVUZ>NkZ00J%y8*p*Jx$I+Xv*UdI7K_B8>hxBV4Vf?u5ViSvHbSDz9I^{!x(+ud2tjfa=$WCy$R+l130l7id;Cld^MPL&XHU8?~!nPv8FKI*}@S!+%4pzuJbSg9u?rPDi5~g1w zTQo}zgjk|@9MDhc9enO8^6;weHz+ayIOib5p}I&<;Xl2n$e*vF^GK;g@bp4mK<{5q z(W3kpcF9_}t-_nt@K!H;JK3$cf)3w-weEpFpog@D&wD7NHa_p;^8wwbAJKh9?&W?I z4K4@TSFW91T1nofXg;75mGXo+q#1O*+cXwKo+TD!xagPXWyfhBuc*A}mxOe*ngH&6 zz>TPZ8C=_=HF7uapKKt8N(+Ahi_mNWM;^G1evHeMmLM$Le{owtwy6^X1d9G!0=_0F zr$w1`Q0cB=sWh2)$Q?(OGpRC@%-M3s-+(8^YBd*V|97E5pFt;VVSYXHeB2@TFU8nk z&VXMPN$#Q&V2>HgwPhA`Yq&q*RxHSP3-?MWowGABhr2f^E|aJMhf*}|4%xY~!X`jD zsq!RoiXl7#*R#B_ATmiZ;L%w?9$fAf4lHdpsnA+!6Cid7k3>^~rFlbrfzY<-B!hie zGmF6c25-=$19ER^cZjSp9QSDpP)^gu(;~0iPTiMWn|lhY83=-qXD3(7js4a@DwJ@30aN> zTk_g4kZh9A2~XUSZTPViSNa8Xoq2|>=c+JsxPU0Kd8pkW+jDQV@SxjnST_Xwu`>LQ zehWpe;sAgrC2!Y3U^(OSeZLJhD%CKK?s zy4u&CEF_L{flBx{MOw>l7p9&F%N+!({z8oC@B(Rp6ZAVw>{<+qaJc3^=9pyAkNWj~ zC2}cKWPerX)q4?Yt@->>ey|)yl;ddKFRD;37VfNSc`6cLK#Kf+b>rvi7ox=tKwgv& zWN%=aAKb2(wt2*jjPmZO$e(`c`X%XQMaU6;)`*cykyXqiri1bsW;O#6?sRBQ;NoYb@Kzd fM9V9rl%H`_21*`+3ojYS6C57`k;P(ul`Q=au~(%q literal 0 HcmV?d00001 From 940059d1b79a7d4a0234a9bc81b11c34a46bd699 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 15:49:30 +0100 Subject: [PATCH 1148/1412] Enable identity insert on same connection (#1101) --- .../sqlserver/database_statements.rb | 52 +++++++++---------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 217677ff4..9887aed8e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -18,13 +18,11 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac log(sql, name, async: async) do with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| - handle = if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name) { _execute(sql, conn) } + result = if id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name, conn) { _execute(sql, conn, perform_do: true) } else - _execute(sql, conn) + _execute(sql, conn, perform_do: true) end - - result = handle.do end end @@ -48,8 +46,16 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa begin options = { ar_result: true } - handle = _execute(sql, conn) - result = handle_to_names_and_values(handle, options) + # TODO: Look into refactoring this. + if id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name, conn) do + handle = _execute(sql, conn) + result = handle_to_names_and_values(handle, options) + end + else + handle = _execute(sql, conn) + result = handle_to_names_and_values(handle, options) + end ensure finish_statement_handle(handle) end @@ -59,14 +65,6 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa result end - def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil, returning: nil) - if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds) - with_identity_insert_enabled(id_insert_table_name) { super } - else - super - end - end - def exec_delete(sql, name, binds) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" super(sql, name, binds).rows.first.first @@ -205,12 +203,12 @@ def execute_procedure(proc_name, *variables) end - def with_identity_insert_enabled(table_name) + def with_identity_insert_enabled(table_name, conn) table_name = quote_table_name(table_name) - set_identity_insert(table_name, true) + set_identity_insert(table_name, conn, true) yield ensure - set_identity_insert(table_name, false) + set_identity_insert(table_name, conn, false) end def use_database(database = nil) @@ -314,8 +312,8 @@ def sql_for_insert(sql, pk, binds, _returning) # === SQLServer Specific ======================================== # - def set_identity_insert(table_name, enable = true) - execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}" + def set_identity_insert(table_name, conn, enable) + _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}" end @@ -413,10 +411,6 @@ def exclude_output_inserted_table_name?(table_name, sql) self.class.exclude_output_inserted_table_names[table_name] end - def exec_insert_requires_identity?(sql, _pk, _binds) - query_requires_identity_insert?(sql) - end - def query_requires_identity_insert?(sql) return false unless insert_sql?(sql) @@ -468,13 +462,15 @@ def finish_statement_handle(handle) end # TODO: Rename - def _execute(sql, conn) - conn.execute(sql).tap do |result| + def _execute(sql, conn, perform_do: false) + result = conn.execute(sql).tap do |_result| # TinyTDS returns false instead of raising an exception if connection fails. - # Getting around this by raising an exception ourselves while this PR + # Getting around this by raising an exception ourselves while PR # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. - raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) + raise TinyTds::Error, "failed to execute statement" if _result.is_a?(FalseClass) end + + perform_do ? result.do : result end # TODO: Remove From 221363d5b9e0bfbb7859f866ea2dee5dfc96e4e1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 15:49:55 +0100 Subject: [PATCH 1149/1412] Added method to build change column default definition (#1103) --- .../connection_adapters/sqlserver/schema_statements.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5e906e063..4c255f0cf 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -221,6 +221,14 @@ def remove_index!(table_name, index_name) execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end + def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc: + column = column_for(table_name, column_name) + return unless column + + default = extract_new_default_value(default_or_changes) + ChangeColumnDefaultDefinition.new(column, default) + end + def foreign_keys(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema From 603eb15774a4f9094839c104f8460df13336036f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 16:10:35 +0100 Subject: [PATCH 1150/1412] Coerce test --- test/cases/coerced_tests.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 690edd829..3d62e4df1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -662,6 +662,32 @@ def migrate(x) end end +module ActiveRecord + class Migration + class CompatibilityTest < ActiveRecord::TestCase + # Error message depends on the database adapter. + coerce_tests! :test_create_table_on_7_0 + 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 migrate(x) + create_table @@long_table_name + end + }.new + + error = assert_raises(StandardError) do + ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate + end + assert_match(/The identifier that starts with '#{long_table_name[0..-2]}' is too long/i, error.message) + ensure + connection.drop_table(long_table_name) rescue nil + end + end + end +end + class CoreTest < ActiveRecord::TestCase # I think fixtures are using the wrong time zone and the `:first` # `topics`.`bonus_time` attribute of 2005-01-30t15:28:00.00+01:00 is From c2ade4e4ca970392c3e11fd75a865ce298637eb5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 16:22:24 +0100 Subject: [PATCH 1151/1412] Test no longer needs to be coerced --- test/cases/coerced_tests.rb | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d62e4df1..48c9ec563 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1180,15 +1180,6 @@ def test_has_one_through_executes_limited_query_coerced require "models/company" class InheritanceTest < ActiveRecord::TestCase - # Rails test required inserting to a identity column. - coerce_tests! :test_a_bad_type_column - def test_a_bad_type_column_coerced - Company.connection.with_identity_insert_enabled("companies") do - Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')" - end - assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) } - end - # Use Square brackets around column name coerce_tests! :test_eager_load_belongs_to_primary_key_quoting def test_eager_load_belongs_to_primary_key_quoting_coerced From 119c77e7dad16037c56451b0f05f428843a7be6b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 16:27:10 +0100 Subject: [PATCH 1152/1412] Test no longer needs to be coerced --- test/cases/coerced_tests.rb | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 48c9ec563..0a4011df3 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1178,18 +1178,6 @@ def test_has_one_through_executes_limited_query_coerced end end -require "models/company" -class InheritanceTest < ActiveRecord::TestCase - # Use Square brackets around column name - coerce_tests! :test_eager_load_belongs_to_primary_key_quoting - def test_eager_load_belongs_to_primary_key_quoting_coerced - Account.connection - assert_sql(/\[companies\]\.\[id\] = @0.* @0 = 1/) do - Account.all.merge!(includes: :firm).find(1) - end - end -end - class LeftOuterJoinAssociationTest < ActiveRecord::TestCase # Uses || operator in SQL. Just trust core gets value out of this test. coerce_tests! :test_does_not_override_select From 201e8b43906eb817f7b184ce3a703388f1ccf7f3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 16:36:18 +0100 Subject: [PATCH 1153/1412] Coerce test --- test/cases/coerced_tests.rb | 43 ++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0a4011df3..aaa2963e1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1596,16 +1596,57 @@ class TestAdapterWithInvalidConnection < ActiveRecord::TestCase require "models/topic" class TransactionTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint + # SQL Server does not have query for release_savepoint. coerce_tests! :test_releasing_named_savepoints def test_releasing_named_savepoints_coerced Topic.transaction do + Topic.connection.materialize_transactions + Topic.connection.create_savepoint("another") Topic.connection.release_savepoint("another") # We do not have a notion of releasing, so this does nothing vs raise an error. Topic.connection.release_savepoint("another") end end + + # SQL Server does not have query for release_savepoint. + coerce_tests! :test_nested_transactions_after_disable_lazy_transactions + def test_nested_transactions_after_disable_lazy_transactions_coerced + Topic.connection.disable_lazy_transactions! + + capture_sql do + # RealTransaction (begin..commit) + Topic.transaction(requires_new: true) do + # ResetParentTransaction (no queries) + Topic.transaction(requires_new: true) do + Topic.delete_all + # SavepointTransaction (savepoint..release) + Topic.transaction(requires_new: true) do + # ResetParentTransaction (no queries) + Topic.transaction(requires_new: true) do + # no-op + end + end + end + Topic.delete_all + end + end + + actual_queries = ActiveRecord::SQLCounter.log_all + + expected_queries = [ + /BEGIN/i, + /DELETE/i, + /^SAVE TRANSACTION/i, + /DELETE/i, + /COMMIT/i, + ] + + assert_equal expected_queries.size, actual_queries.size + expected_queries.zip(actual_queries) do |expected, actual| + assert_match expected, actual + end + end end require "models/tag" From c09a82b9848b2a13473670306781514ca2800d14 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Oct 2023 16:43:12 +0100 Subject: [PATCH 1154/1412] Coerce test --- test/cases/coerced_tests.rb | 40 ++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index aaa2963e1..542014a35 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1601,7 +1601,7 @@ class TransactionTest < ActiveRecord::TestCase def test_releasing_named_savepoints_coerced Topic.transaction do Topic.connection.materialize_transactions - + Topic.connection.create_savepoint("another") Topic.connection.release_savepoint("another") # We do not have a notion of releasing, so this does nothing vs raise an error. @@ -1647,6 +1647,44 @@ def test_nested_transactions_after_disable_lazy_transactions_coerced assert_match expected, actual end end + + # SQL Server does not have query for release_savepoint. + coerce_tests! :test_nested_transactions_skip_excess_savepoints + def test_nested_transactions_skip_excess_savepoints_coerced + capture_sql do + # RealTransaction (begin..commit) + Topic.transaction(requires_new: true) do + # ResetParentTransaction (no queries) + Topic.transaction(requires_new: true) do + Topic.delete_all + # SavepointTransaction (savepoint..release) + Topic.transaction(requires_new: true) do + # ResetParentTransaction (no queries) + Topic.transaction(requires_new: true) do + Topic.delete_all + end + end + end + Topic.delete_all + end + end + + actual_queries = ActiveRecord::SQLCounter.log_all + + expected_queries = [ + /BEGIN/i, + /DELETE/i, + /^SAVE TRANSACTION/i, + /DELETE/i, + /DELETE/i, + /COMMIT/i, + ] + + assert_equal expected_queries.size, actual_queries.size + expected_queries.zip(actual_queries) do |expected, actual| + assert_match expected, actual + end + end end require "models/tag" From a596a6ee825815e6e6ed6cefccf94bd5d594c63f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 3 Oct 2023 13:42:20 +0100 Subject: [PATCH 1155/1412] Fix/coerce compatibility tests (#1104) --- .../sqlserver/schema_statements.rb | 9 ++- test/cases/coerced_tests.rb | 69 ++++++++++++++++++- 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 4c255f0cf..2ab28ef19 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -310,15 +310,18 @@ def update_table_definition(table_name, base) SQLServer::Table.new(table_name, base) end - def change_column_null(table_name, column_name, allow_null, default = nil) + def change_column_null(table_name, column_name, null, default = nil) + validate_change_column_null_argument!(null) + table_id = SQLServer::Utils.extract_identifiers(table_name) column_id = SQLServer::Utils.extract_identifiers(column_name) column = column_for(table_name, column_name) - if !allow_null.nil? && allow_null == false && !default.nil? + if !null.nil? && null == false && !default.nil? execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL") end sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}" - sql += " NOT NULL" if !allow_null.nil? && allow_null == false + sql += " NOT NULL" if !null.nil? && null == false + execute sql end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 542014a35..22318d234 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -680,10 +680,77 @@ def migrate(x) error = assert_raises(StandardError) do ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate end - assert_match(/The identifier that starts with '#{long_table_name[0..-2]}' is too long/i, error.message) + 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 end + + # Error message depends on the database adapter. + coerce_tests! :test_rename_table_on_7_0 + def test_rename_table_on_7_0_coerced + long_table_name = "a" * (connection.table_name_length + 1) + connection.create_table(:more_testings) + + migration = Class.new(ActiveRecord::Migration[7.0]) { + @@long_table_name = long_table_name + def version; 100 end + def migrate(x) + rename_table :more_testings, @@long_table_name + end + }.new + + error = assert_raises(StandardError) do + ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate + end + assert_match(/The new name '#{long_table_name[0...-1]}' is already in use as a object name and would cause a duplicate that is not permitted/i, error.message) + ensure + connection.drop_table(:more_testings) rescue nil + connection.drop_table(long_table_name) rescue nil + 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) + + migration = Class.new(ActiveRecord::Migration[7.0]) { + @@long_index_name = long_index_name + def migrate(x) + add_column :testings, :very_long_column_name_to_test_with, :string + add_index :testings, [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name + end + }.new + + 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) + 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) + + migration = Class.new(ActiveRecord::Migration[7.0]) { + @@long_index_name = long_index_name + def migrate(x) + create_table :more_testings do |t| + t.integer :foo + t.integer :bar + t.integer :very_long_column_name_to_test_with + t.index [:foo, :bar, :very_long_column_name_to_test_with], name: @@long_index_name + end + end + }.new + + 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) + ensure + connection.drop_table :more_testings rescue nil + end end end end From cfdfa118826ed98d7ea6814fdd2b85be3be96257 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 3 Oct 2023 16:30:31 +0100 Subject: [PATCH 1156/1412] Use IN sql instead of OR sql for query constraints (#1105) --- .../sqlserver/core_ext/preloader.rb | 18 +++++++++++++++--- test/cases/coerced_tests.rb | 19 +------------------ 2 files changed, 16 insertions(+), 21 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index 9fb736795..db258bd7e 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -8,10 +8,22 @@ module SQLServer module CoreExt module LoaderQuery def load_records_for_keys(keys, &block) - return super unless scope.connection.adapter_name == "SQLServer" + return super unless scope.connection.sqlserver? - keys.each_slice(in_clause_length).flat_map do |slice| - scope.where(association_key_name => slice).load(&block).records + if association_key_name.is_a?(Array) + query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new } + + keys.each_with_object(query_constraints) do |values_set, constraints| + association_key_name.zip(values_set).each do |key_name, value| + constraints[key_name] << value + end + end + + scope.where(query_constraints).load(&block) + else + keys.each_slice(in_clause_length).flat_map do |slice| + scope.where(association_key_name => slice).load(&block).records + end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 22318d234..8195f8d95 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -956,25 +956,8 @@ def test_count_with_include_coerced assert_equal 3, authors(:david).posts_with_comments.where("LEN(comments.body) > 15").references(:comments).count end - # Use TOP (1) in scope vs limit 1. + # The raw SQL in the scope uses `limit 1`. coerce_tests! %r{including association based on sql condition and no database column} - - # Table name not regex escaped in original test. Remove coerced test when https://github.com/rails/rails/pull/49345 merged. - coerce_tests! %r{preloading belongs_to association SQL} - test "preloading belongs_to association SQL coerced" do - blog_ids = [sharded_blogs(:sharded_blog_one).id, sharded_blogs(:sharded_blog_two).id] - posts = Sharded::BlogPost.where(blog_id: blog_ids).includes(:comments) - - sql = capture_sql do - comments_collection = posts.map(&:comments) - assert_equal 3, comments_collection.size - end.last - - c = Sharded::BlogPost.connection - quoted_blog_id = Regexp.escape(c.quote_table_name("sharded_comments.blog_id")) - quoted_blog_post_id = Regexp.escape(c.quote_table_name("sharded_comments.blog_post_id")) - assert_match(/WHERE #{quoted_blog_id} IN \(.+\) AND #{quoted_blog_post_id} IN \(.+\)/, sql) - end end require "models/topic" From d82e40323b59b434714d86788a9b8f8d007d8227 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 5 Oct 2023 17:08:44 +0100 Subject: [PATCH 1157/1412] Updated for Rails v7.1.0 --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 68356638f..a3fcc7121 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.rc2 +7.1.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index f0d10d457..0abb37147 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.1.0.rc2" + spec.add_dependency "activerecord", "~> 7.1.0" spec.add_dependency "tiny_tds" end From 459817ff0cd4782972e06690e2c73fa7abda607c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 5 Oct 2023 17:15:55 +0100 Subject: [PATCH 1158/1412] Fix insert SQL with returning columns (#1106) --- .../connection_adapters/sqlserver/database_statements.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9887aed8e..7de16e54b 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -279,7 +279,7 @@ def newsequentialid_function protected - def sql_for_insert(sql, pk, binds, _returning) + def sql_for_insert(sql, pk, binds, returning) if pk.nil? table_name = query_requires_identity_insert?(sql) pk = primary_key(table_name) @@ -299,9 +299,12 @@ def sql_for_insert(sql, pk, binds, _returning) SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable SQL else - inserted_keys = Array(pk).map { |primary_key| " INSERTED.#{SQLServer::Utils.extract_identifiers(primary_key).quoted}" } + returning_columns = returning || Array(pk) - sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT" + inserted_keys.join(",") + 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(",") + end end else "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident" From 8cd903b91101749aba4a62e1eec9c8e10cb96d11 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 5 Oct 2023 19:55:44 +0100 Subject: [PATCH 1159/1412] Fix "ambiguous first argument" warning --- test/cases/coerced_tests.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 8195f8d95..7a278260b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1863,8 +1863,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. From ca76dc025b368ff10995d10b55dc4a7cde005014 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 5 Oct 2023 19:57:18 +0100 Subject: [PATCH 1160/1412] Correctly set SQL if no returning columns Fixes "EagerAssociationTest::test_string_id_column_joins" test. --- .../connection_adapters/sqlserver/database_statements.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7de16e54b..de5732f40 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -304,6 +304,8 @@ def sql_for_insert(sql, pk, binds, returning) 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 From 18c01f755d40014382512346c51818bd15233e4e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 9 Oct 2023 10:46:39 +0200 Subject: [PATCH 1161/1412] Savepoint queries should be internally executed so that they don't clear the query cache (#1107) --- .../sqlserver/database_statements.rb | 13 ---------- .../sqlserver/savepoints.rb | 24 +++++++++++++++++++ .../connection_adapters/sqlserver_adapter.rb | 4 +++- 3 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/savepoints.rb diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index de5732f40..9d1f75885 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -100,19 +100,6 @@ def exec_rollback_db_transaction execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION" end - include Savepoints - - def create_savepoint(name = current_savepoint_name) - execute "SAVE TRANSACTION #{name}", "TRANSACTION" - end - - def exec_rollback_to_savepoint(name = current_savepoint_name) - execute "ROLLBACK TRANSACTION #{name}", "TRANSACTION" - end - - def release_savepoint(name = current_savepoint_name) - end - def case_sensitive_comparison(attribute, value) column = column_for_attribute(attribute) diff --git a/lib/active_record/connection_adapters/sqlserver/savepoints.rb b/lib/active_record/connection_adapters/sqlserver/savepoints.rb new file mode 100644 index 000000000..e1bc69c7d --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/savepoints.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module Savepoints + def current_savepoint_name + current_transaction.savepoint_name + end + + def create_savepoint(name = current_savepoint_name) + internal_execute("SAVE TRANSACTION #{name}", "TRANSACTION") + end + + def exec_rollback_to_savepoint(name = current_savepoint_name) + internal_execute("ROLLBACK TRANSACTION #{name}", "TRANSACTION") + end + + def release_savepoint(_name) + end + end + end + end +end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f8179f86f..f9552ad1c 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -17,6 +17,7 @@ require "active_record/connection_adapters/sqlserver/database_limits" require "active_record/connection_adapters/sqlserver/database_statements" require "active_record/connection_adapters/sqlserver/database_tasks" +require "active_record/connection_adapters/sqlserver/savepoints" require "active_record/connection_adapters/sqlserver/transaction" require "active_record/connection_adapters/sqlserver/errors" require "active_record/connection_adapters/sqlserver/schema_creation" @@ -40,7 +41,8 @@ class SQLServerAdapter < AbstractAdapter SQLServer::Showplan, SQLServer::SchemaStatements, SQLServer::DatabaseLimits, - SQLServer::DatabaseTasks + SQLServer::DatabaseTasks, + SQLServer::Savepoints ADAPTER_NAME = "SQLServer".freeze From b88b1403a966e884810891694fb5539ae6c90083 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 9 Oct 2023 11:30:50 +0100 Subject: [PATCH 1162/1412] Table name truncated when renaming table --- test/cases/coerced_tests.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7a278260b..75b526090 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -685,7 +685,7 @@ def migrate(x) connection.drop_table(long_table_name) rescue nil end - # Error message depends on the database adapter. + # 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). coerce_tests! :test_rename_table_on_7_0 def test_rename_table_on_7_0_coerced long_table_name = "a" * (connection.table_name_length + 1) @@ -699,13 +699,13 @@ def migrate(x) end }.new - error = assert_raises(StandardError) do - ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate - end - assert_match(/The new name '#{long_table_name[0...-1]}' is already in use as a object name and would cause a duplicate that is not permitted/i, error.message) + ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate + assert connection.table_exists?(long_table_name[0...-1]) + 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) rescue nil + connection.drop_table(long_table_name[0...-1]) rescue nil end # SQL Server has a different maximum index name length. From d19559879c791dbbcd119589cfc33450e2b669cf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 9 Oct 2023 15:28:13 +0200 Subject: [PATCH 1163/1412] Fix materialized transaction queries (#1108) --- .../sqlserver/database_statements.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9d1f75885..74aea0e8e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -76,7 +76,7 @@ def exec_update(sql, name, binds) end def begin_db_transaction - execute "BEGIN TRANSACTION", "TRANSACTION" + internal_execute("BEGIN TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false) end def transaction_isolation_levels @@ -84,20 +84,20 @@ def transaction_isolation_levels end def begin_isolated_db_transaction(isolation) - set_transaction_isolation_level transaction_isolation_levels.fetch(isolation) + set_transaction_isolation_level(transaction_isolation_levels.fetch(isolation)) begin_db_transaction end def set_transaction_isolation_level(isolation_level) - execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION" + internal_execute("SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION", allow_retry: true, materialize_transactions: false) end def commit_db_transaction - execute "COMMIT TRANSACTION", "TRANSACTION" + internal_execute("COMMIT TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true) end def exec_rollback_db_transaction - execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION" + internal_execute("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true) end def case_sensitive_comparison(attribute, value) From 137788e3f2559d4463f94967b0ea30c0cabfe8d7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 9 Oct 2023 15:34:36 +0200 Subject: [PATCH 1164/1412] Temporarily coerce all SerializedAttributeTest tests (#1109) --- test/cases/coerced_tests.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 75b526090..4d4ff1b7d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2585,3 +2585,8 @@ def thread_encrypting_and_decrypting(thread_label) end end end + +# TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1 +class SerializedAttributeTest < ActiveRecord::TestCase + coerce_all_tests! +end From d1a3b1dd1977adff67e216c14d22efe23ec7869f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 9 Oct 2023 16:52:07 +0200 Subject: [PATCH 1165/1412] Added method to build change column definition (#1110) --- .../connection_adapters/sqlserver/schema_statements.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 2ab28ef19..684082856 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -221,6 +221,12 @@ def remove_index!(table_name, index_name) execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}" end + def build_change_column_definition(table_name, column_name, type, **options) # :nodoc: + td = create_table_definition(table_name) + cd = td.new_column_definition(column_name, type, **options) + ChangeColumnDefinition.new(cd, column_name) + end + def build_change_column_default_definition(table_name, column_name, default_or_changes) # :nodoc: column = column_for(table_name, column_name) return unless column From cf3a15f97a53d47aa536cef30ee9a40ceb6bcfa5 Mon Sep 17 00:00:00 2001 From: Alexandre Overtus Date: Wed, 11 Oct 2023 11:19:08 -0400 Subject: [PATCH 1166/1412] Allow AbstractAdapter to detect sqlserver connection (#1111) Allow AbstractAdapter to detect sqlserver connection Co-authored-by: Alexandre Overtus <3729387+aovertus@users.noreply.github.com> --- .../sqlserver/core_ext/abstract_adapter.rb | 20 +++++++++++++++++++ .../sqlserver/transaction.rb | 10 ++++------ .../connection_adapters/sqlserver_adapter.rb | 3 ++- 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb new file mode 100644 index 000000000..90b1bead1 --- /dev/null +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/abstract_adapter.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module ActiveRecord + module ConnectionAdapters + module SQLServer + module CoreExt + module AbstractAdapter + def sqlserver? + false + end + end + end + end + end +end + +ActiveSupport.on_load(:active_record) do + mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AbstractAdapter + ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(mod) +end diff --git a/lib/active_record/connection_adapters/sqlserver/transaction.rb b/lib/active_record/connection_adapters/sqlserver/transaction.rb index 86f4aa777..b2677dc30 100644 --- a/lib/active_record/connection_adapters/sqlserver/transaction.rb +++ b/lib/active_record/connection_adapters/sqlserver/transaction.rb @@ -5,14 +5,12 @@ module ActiveRecord module ConnectionAdapters module SQLServerTransaction - private + delegate :sqlserver?, to: :connection, prefix: true - def sqlserver? - connection.respond_to?(:sqlserver?) && connection.sqlserver? - end + private def current_isolation_level - return unless sqlserver? + return unless connection_sqlserver? level = connection.user_options_isolation_level # When READ_COMMITTED_SNAPSHOT is set to ON, @@ -50,7 +48,7 @@ def rollback private def reset_starting_isolation_level - if sqlserver? && starting_isolation_level + if connection_sqlserver? && starting_isolation_level connection.set_transaction_isolation_level(starting_isolation_level) end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index f9552ad1c..8f8915cac 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -12,6 +12,7 @@ require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods" require "active_record/connection_adapters/sqlserver/core_ext/finder_methods" require "active_record/connection_adapters/sqlserver/core_ext/preloader" +require "active_record/connection_adapters/sqlserver/core_ext/abstract_adapter" require "active_record/connection_adapters/sqlserver/version" require "active_record/connection_adapters/sqlserver/type" require "active_record/connection_adapters/sqlserver/database_limits" @@ -100,7 +101,7 @@ def initialize(...) super @config[:tds_version] = "7.3" unless @config[:tds_version] - @config[:appname] = rails_application_name unless @config[:appname] + @config[:appname] = self.class.rails_application_name unless @config[:appname] @config[:login_timeout] = @config[:login_timeout].present? ? @config[:login_timeout].to_i : nil @config[:timeout] = @config[:timeout].present? ? @config[:timeout].to_i / 1000 : nil @config[:encoding] = @config[:encoding].present? ? @config[:encoding] : nil From 8c4c4bcbe80ab3415c1859f70ae0cabd20b68e80 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 10:39:42 +0100 Subject: [PATCH 1167/1412] Update to Rails 7.1.1. --- .gitignore | 1 + activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 27b05d95f..f7d974a26 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ coverage/* .flooignore .floo .byebug_history +tmp/* diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 0abb37147..f158dca18 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.1.0" + spec.add_dependency "activerecord", "~> 7.1.1" spec.add_dependency "tiny_tds" end From 13efaae8520db3651ecba8091b80f3c870e5c79d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 10:42:15 +0100 Subject: [PATCH 1168/1412] Fix issue with default view value not being found because of case sensitivity (#1113) (cherry picked from commit 0871ed87aa19b95a9b2d23e082088b965aaf893a) --- .../sqlserver/schema_statements.rb | 4 +-- test/cases/view_test_sqlserver.rb | 36 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 test/cases/view_test_sqlserver.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 684082856..53693f383 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -425,7 +425,7 @@ def column_definitions(table_name) if view_exists results = sp_executesql %{ - SELECT c.COLUMN_NAME AS [name], c.COLUMN_DEFAULT AS [default] + SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default] FROM #{database}.INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = #{quote(view_tblnm)} }.squish, "SCHEMA", [] @@ -463,7 +463,7 @@ def column_definitions(table_name) ci[:default_function] = begin default = ci[:default_value] if default.nil? && view_exists - view_column = views_real_column_name(table_name, ci[:name]) + view_column = views_real_column_name(table_name, ci[:name]).downcase default = default_functions[view_column] if view_column.present? end case default diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb new file mode 100644 index 000000000..82985f37e --- /dev/null +++ b/test/cases/view_test_sqlserver.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class ViewTestSQLServer < ActiveRecord::TestCase + let(:connection) { ActiveRecord::Base.connection } + + describe 'view with default values' do + before do + connection.drop_table :view_casing_table rescue nil + 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 + end + + connection.execute("DROP VIEW IF EXISTS view_casing_table_view;") + connection.execute("CREATE VIEW view_casing_table_view AS SELECT id AS id, default_falsey AS falsey, default_truthy AS truthy FROM view_casing_table") + end + + it "default values are correct when column casing used in tables and views are different" do + klass = Class.new(ActiveRecord::Base) do + self.table_name = "view_casing_table_view" + end + + obj = klass.new + assert_equal false, obj.falsey + assert_equal true, obj.truthy + assert_equal 0, klass.count + + obj.save! + assert_equal false, obj.falsey + assert_equal true, obj.truthy + assert_equal 1, klass.count + end + end +end From 6bf4ca72c92e696724108d92eeb71fd672fa86fc Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 11:45:13 +0100 Subject: [PATCH 1169/1412] Implement message-pack methods on Data class (#1115) --- .../connection_adapters/sqlserver/type/data.rb | 10 ++++++++++ test/cases/coerced_tests.rb | 13 +++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/type/data.rb b/lib/active_record/connection_adapters/sqlserver/type/data.rb index 1cf241450..90104d8ad 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/data.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/data.rb @@ -36,6 +36,16 @@ def eql?(other) self.class == other.class && value == other.value end alias :== :eql? + + def self.from_msgpack_ext(string) + 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" + end end end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4d4ff1b7d..7cdaa0d99 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2586,6 +2586,19 @@ def thread_encrypting_and_decrypting(thread_label) end end +# Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack +# can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects. +class ActiveRecordMessagePackTest < ActiveRecord::TestCase + private + def serializer + @serializer ||= ::MessagePack::Factory.new.tap do |factory| + ActiveRecord::MessagePack::Extensions.install(factory) + ActiveSupport::MessagePack::Extensions.install(factory) + ActiveSupport::MessagePack::Extensions.install_unregistered_type_fallback(factory) + end + end +end + # TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1 class SerializedAttributeTest < ActiveRecord::TestCase coerce_all_tests! From d9c3c44d6cb4df65ad7e2ed0a65f562345c590ba Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 11:53:46 +0100 Subject: [PATCH 1170/1412] Loaded replies before marshalling dump (#1116) To create the dumps with associates use: ActiveRecord::Marshalling.format_version = 6.1 topic = Topic.find(1) topic.replies.load File.binwrite(marshal_fixture_path("rails_6_1_topic_associations"), Marshal.dump(topic)) ActiveRecord::Marshalling.format_version = 7.1 topic = Topic.find(1) topic.replies.load File.binwrite(marshal_fixture_path("rails_7_1_topic_associations"), Marshal.dump(topic)) --- .../rails_6_1_topic_associations.dump | Bin 2876 -> 5784 bytes .../rails_7_1_topic_associations.dump | Bin 3554 -> 1246 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump b/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump index d1700d3d7ea148ef55f42808091a7a6d579a3cc8..9278beeeeb5c1f151505ce678a9cbed4cc2f2d70 100644 GIT binary patch literal 5784 zcmbVQ*>W4l6$O$MHiDaE+9b8nuq;QV5Y_y$b0;jXIJSv@}AdxKwg~FJp*EqZ7otkn(5{CoqKPebGm<0P^?r_ zJ-2y*&52;z)*yRo0u4dnC72OL~lLiO>7%gWf$Em3jVnv0BNy ztsKkUt|@kTZ1~Ag#_T|KZ;41WL?}zFM84=sDzLM3B)nKU4cS(KbfSJwI!zHqZVqGg zn_?S@$z9_fW1GtNC45r;dQ0>o8H9}O-P_ue!I2CYt6>gf8}32XkK}<2ifop8_%t`f z42w-s*c6`B51!j~BX=54f$$HoP-$2HUtmS~sOi;MsUZ(s-@97OB!9kQ1?jiEE5r9i zQh|#x!gEeSrY)OKa&y&6DRLuE=6JEDc&WsvXoi=om6vKAxnjFP7_FED=zcz}nEe7D zdf#`ONX@l?S1K%I6}G;pZQXWFViW0hEDT=?H~PPHZ?vRTCFUlCS_N+2v@5W$S@qS zy{v7YX=&T!skjw&T>sGZ4_2G1n=D3)so5VGRjtfE4BRM^zN7pR>*rxTX2qJ@mD4l- zUV411^RHfx6+c%#VpF(Z+r(H~=V1K>a)7dy+VWbF&$?44z!saK2u*qcD{(vKXNODk zbF>0q8_JJEM-LqF^`iC_gI6<|$EdtsP7l_;z5fEfX}`2v<-{$1VZ@u&%+hdyYGua5 zz5@yfliajmFZ1$!R@7fupK>$qk01K4<3Idum)F+bd+*xyYcJ}Vwh7OYg0u#X(UoeY z+*H`RzNz5UjW>05oV$AU>dGB)Bv-@=%3iLtM8CAUx>`!=<34+BJU|H)02o~1xu45c zy~w5<=V;Aw>UlOzVcgIFgFT{mP*Be+RvkC+Oi(kv1GmMaCw7E)CI9WW-~LXq*C)+m zS)kX)>)TtK%8R>x0br$(#B4k^hyy!Cs_`i>&uJn>VRPyO^TsbJWw2un`We9wdZ+i!3+btijj0MSe>0(*^!gjW2jt^MHe| z_{;ca^Pmx2v&>TLyPttWzt5#DxRgPD?wBaK8Dz010&FlR!SQsD-A%HXj6y8u(?OFQ zwv&uY&=}*2dKZo0*X~4r=^#)sQ0#;RYZC)zn*beMlVWts@24>Mm;ao9e0=qrzosxq zl_8vtbpcM}Uh7Fa51!KwD<}LN9xqbC&Cv|a(E`0f%XF5`)2nomF41MWLT}PFx?#~x zi#BbtF>uSx&|QmmEZViGX3;)s1t@VkrO4#w`{SHo`~a~@Ddv2#f|r@}13x_zyshQ-QWCJ-$|j?xmv zcyXLzi`0^B5qr^4w1mewDxeICkSK*DJ0*mfohGftkQt+BLQFnIU4(dms5WR$6Nd7= zzRk{Pp>^b{IQ06?sG^+3ieVI}zJ$Cy3k$&^Y(nrkrad-l&SvL_!<{`fXE8M^R!_M zK7dkmlJbH4^t^^>9$M_eurK|9kEEw*7Z$`SI76PGs9CUX69p#dIR{p0@XfaoM8FX)~0U zZeC5B-%OjINeS`S>P3DLxqF>os^@_!@BSdI(~Z>lIFZxMv^imIT2EcsOq)ThIJeMk z(_AAvZPpApN;kBXc1-dR?W7&!j6%COe4@n2bd3jkA!^i{%r_Vy3K%ZBune-PHxIKb5E( zD8hFQ)XW39ab8cL9>>^gs058S)0C3Eei_HSu&H{|LNicbL*_UcSZskD;c3=_Kn2vx zmH~PkGgDR#GHpzjasi)wUpsdJ#M$#i6GR9e z1Wn^1flXYx6mYeoFF-mX>};|kV7c>UMt|wT7`nTy5T^J?ioI%f31g$tcXp*`_-7X2 zeP~V&$-dEwM5DK2bL)Y=)v4(>aDTNjWzuNQi$hn#G%aL1`0x=LUi^$#%5IdwKpD zrtyI8A`{Raok+1O-xrvBu?&V+Ze@@obov-;LX$bM-@rMfrNf@;5u68%z)EEW?$c)E zBb=(WAgX8hJzJj!2T^t!vFqW8s3wolJA)A$$$&g>D}=^e37!;zUUgeWLQ9xJQUbU` zM1)Y#=+YsYL$$ZPS%<*H&euKFs z)2ev(i<71^oQFK18pfsp{tWjqQ zIaiPRioN|^fH4Bgp@SQVG+{e>%4tA>7>Y$|n?&=ruH-fF$Gw_K3D16I^X%^}p8W%L pdG?QZ`IGh{5{IbFX;STWC?n%xg46tg#p@RT#NxlU_;1|I{{aEn)Rh1L delta 1168 zcmaJ=&2Jk;6t|spon1TriXA6S)0%BdQ6XXZYGDVdFIBNv!KvK}q#Rb`^~4>r-dSd6 zQ>+T57cQI#BO#9b3ppZz=-`XWgMkC_yYet(>n|zW%d^M=MnqE;NJevAUteFo)bQ+jC}NaT1$4T+eVhkr@M$8XqI1~s z>{Xg1QXH|u6Qgw`OCrwFAQai0loz_0#r)HZVTk7sOQ@MpiE)CWDjzl53*f9fh?(k> zSyHqBH`YXo43oEVb~D7p6&nY%NY%^hd;RgnCr^JoeScA3GfT$*OL1$UP(J;<52kQ$ z>to}MlV4c{EPYbG3zm=P2Q*5VXB!83SkiJx+aVoZ{uAngY*=IycF1|yJv*p;*MtVV z3N7L8<#PIiC3AI!w0Ny>bYM-NF1ZC#Um!h1ZnW-TqR5-;JFuyLUjDNAoVq?dcfDNI z-<*481#ZF?+6DcGamzaM+Q#h-&%^thFlqjy#!a#&NOy(we6k+xRB-|xlYaDav4D&4 zE?hzzVAe07*=U!+fve!?pUmy*LR|l5?v~Ay``2!pt_AsAg6UqE4|p(Q2STzS?`06; zOgTu&H}I0X0YdT#k7+YPdP2_9pIjRvEyvuAL)vYFcTVanmWXMR3bOO?!H~# z)9-W#ot)7Eoa`tlXyEH<_uj@3KHjf})}EAfCdh{dnFCw;tF?9gtUF!8y&m<3ef(qI zV-~Rq78)oK9O5fY_2K$<3qvQ8PpO;*hinF>9$H_tyVw}XLlxwWfgc5bXguiIXPKB0*J^8uKA>u(ftlO4l5=qEuLd1FbpbU~TQCS>f!O^*Si< z=b5z~RB-{y!IJ%EcHg`=GrrURgzZF@Je&7_JdE8Q&n;}dO}G`iw&uEER%9iydp}t) z=bWobJ56?r-ler(NREh#Tjn&*mDRU6vRO&Td=o$Cb`U%0sT zGr4SolW&S^%|t4N0VIBXnrXgb7c7%1$M}sDf&n7MX--ScRE6>7$M=^^t(d|%S(gms zGpcD156OxaHH+N?bnFE$y%3?P6U=b<3I~B7hSRBGz&6ovNaEAm5wV0XSvbHGQYx15 z3M%76;I1-SFa$3JUviD`m}p4+E(JreJGqjgu2O?EDx+R6>I2+Xhmem991U(K-&8b+ zhI|x_P_&JrF))q`TIp0V+Vn?m2+_<7j*K(2{~-jw4I#S0H_D`dHfnP%bingG=N(-! zhdP31%*pB6_I=-O!(+ZIrP8!bhiZ4PxS(pC-nazNZCaK}u2>`CJa%^!D+|slNUPhU zW^(sx!N1lFwEiC40vk%IpsKVTQ@Hb92mBg$zy@X@Ol9zHUn^Oc-Nz&Y12xN2+UVN2 zco-lr2*Je=<_qEJF+Mu>(R6kcbgq&e=xtr*w>x`2x8^-O>h#PlSlSEFwl+&kTyBv* z?~y_5I%Z8TVNXQf?x`a`I~p)B+Xx^-koEy=Z9cXUnQR0!3(}53w;*N&(aDL)j0R+f z>>~02k^Mh(k{lrN2$92tIDCsdhUW<)Pr<~d-iG!WBF{Ipf8iT>@ej$ZQMNXJG_v&* DO+8iC literal 3554 zcmbVP>uwvz6=s`)cik*IvEm!UT!0{ux>9e+Y=Dj=moO0vb!;j?!lZ-Y?vR{lxx>!P z(x!zVd6Ygu|MUg?9R1t3=pXl+*`-7(Kx0S(!QGiNXU^q2-#PqERgrp6O=a|eY|>Ob zXIex`#~r+JYqcC?Rv1Ovhmn;taiU_85b~J6oF6WBp9$LxE9WKBq%)pm!kpjkwkt9& zlk#7BvfORgt+a_KhxJ~!eX}L$Ng-E%$Y)UD&+HVtV!~y@ zcpPhCOuls|c49P9S!~BLJ(KBZFH)0ybK|*|){2y=)GuVD){_&lw*K#(R}aVknwClJ zrAkG&T|Mm`L!#4dZwyqL8D=4t9|;?4d!^E=QmHg8hfTWGZLcTXSVW0?>rP4SA-AI6 zkR|!n^YlY};eXgXIQZM(c-OCMxqt-d3|qhubywO z%gIz}%X9Id+uk0?l_e{XltD7Zw zG36Rou{_4mxm7G&VE;JH51DnktFJ-lp0@ZXA<@!YQrVVeEt{;O~L|9r0q z(?&Lp|E~yXP^aGfaGTZ?Im+?%)Ps+ZW0j@?h*Rld%%@gpL&&o)A3qa%CNv>E938k`8A*+e`M?jeyAY2_N8G*3qYV1MeqtE47gL`jd-2Vx}CWVeP4 zyT6}ERitt9$;CUZE7G}!ai>TxisdV=qcPWYQm0mDz*^Zy4E79pV%+=C#WdUu!mS{@ zA%FE>+En4~dbmUP!guL!FE)Ptj}5v`1G=fm;Wf;0Aa&qBsD+!d;qMB_I(0yIH4xBk zMLxfVUZ>NkZ00J%y8*p*Jx$I+Xv*UdI7K_B8>hxBV4Vf?u5ViSvHbSDz9I^{!x(+ud2tjfa=$WCy$R+l130l7id;Cld^MPL&XHU8?~!nPv8FKI*}@S!+%4pzuJbSg9u?rPDi5~g1w zTQo}zgjk|@9MDhc9enO8^6;weHz+ayIOib5p}I&<;Xl2n$e*vF^GK;g@bp4mK<{5q z(W3kpcF9_}t-_nt@K!H;JK3$cf)3w-weEpFpog@D&wD7NHa_p;^8wwbAJKh9?&W?I z4K4@TSFW91T1nofXg;75mGXo+q#1O*+cXwKo+TD!xagPXWyfhBuc*A}mxOe*ngH&6 zz>TPZ8C=_=HF7uapKKt8N(+Ahi_mNWM;^G1evHeMmLM$Le{owtwy6^X1d9G!0=_0F zr$w1`Q0cB=sWh2)$Q?(OGpRC@%-M3s-+(8^YBd*V|97E5pFt;VVSYXHeB2@TFU8nk z&VXMPN$#Q&V2>HgwPhA`Yq&q*RxHSP3-?MWowGABhr2f^E|aJMhf*}|4%xY~!X`jD zsq!RoiXl7#*R#B_ATmiZ;L%w?9$fAf4lHdpsnA+!6Cid7k3>^~rFlbrfzY<-B!hie zGmF6c25-=$19ER^cZjSp9QSDpP)^gu(;~0iPTiMWn|lhY83=-qXD3(7js4a@DwJ@30aN> zTk_g4kZh9A2~XUSZTPViSNa8Xoq2|>=c+JsxPU0Kd8pkW+jDQV@SxjnST_Xwu`>LQ zehWpe;sAgrC2!Y3U^(OSeZLJhD%CKK?s zy4u&CEF_L{flBx{MOw>l7p9&F%N+!({z8oC@B(Rp6ZAVw>{<+qaJc3^=9pyAkNWj~ zC2}cKWPerX)q4?Yt@->>ey|)yl;ddKFRD;37VfNSc`6cLK#Kf+b>rvi7ox=tKwgv& zWN%=aAKb2(wt2*jjPmZO$e(`c`X%XQMaU6;)`*cykyXqiri1bsW;O#6?sRBQ;NoYb@Kzd fM9V9rl%H`_21*`+3ojYS6C57`k;P(ul`Q=au~(%q From 7d35c576de545245f14f007bd61b09d44064f553 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 14:03:16 +0100 Subject: [PATCH 1171/1412] Need to set explicitly set JSON attribute as such for testing(#1118) --- test/cases/coerced_tests.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7cdaa0d99..e47b0dd83 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2599,6 +2599,11 @@ def serializer end end +class StoreTest < ActiveRecord::TestCase + # Set the attribute as JSON type for the `StoreTest#saved changes tracking for accessors with json column` test. + Admin::User.attribute :json_options, ActiveRecord::Type::SQLServer::Json.new +end + # TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1 class SerializedAttributeTest < ActiveRecord::TestCase coerce_all_tests! From 46d304ad307f5bd0912e402352980b4c75e1844a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Oct 2023 16:39:55 +0100 Subject: [PATCH 1172/1412] Initialise the schema cache when checking the database version (#1120) --- .../connection_adapters/sqlserver_adapter.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 8f8915cac..96a32c411 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -243,7 +243,7 @@ def active? end def reconnect - @raw_connection.close rescue nil + @raw_connection&.close rescue nil @raw_connection = nil @spid = nil @collation = nil @@ -254,7 +254,7 @@ def reconnect def disconnect! super - @raw_connection.close rescue nil + @raw_connection&.close rescue nil @raw_connection = nil @spid = nil @collation = nil @@ -338,6 +338,12 @@ def get_database_version # :nodoc: version_year end + def check_version # :nodoc: + if schema_cache.database_version < 2012 + raise "Your version of SQL Server (#{database_version}) is too old. SQL Server Active Record supports 2012 or higher." + end + end + class << self protected From a9a07774e1948d7a0080ef94a35930ea5758d413 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 18 Oct 2023 19:29:38 +0100 Subject: [PATCH 1173/1412] Removed legacy connection handling (#1121) --- .../sqlserver/database_statements.rb | 70 ++++--------------- .../sqlserver/schema_statements.rb | 12 ++-- .../connection_adapters/sqlserver/showplan.rb | 4 +- .../connection_adapters/sqlserver_adapter.rb | 9 +-- 4 files changed, 24 insertions(+), 71 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 74aea0e8e..3858090b8 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -19,9 +19,9 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac log(sql, name, async: async) do with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, conn) { _execute(sql, conn, perform_do: true) } + with_identity_insert_enabled(id_insert_table_name, conn) { internal_raw_execute(sql, conn, perform_do: true) } else - _execute(sql, conn, perform_do: true) + internal_raw_execute(sql, conn, perform_do: true) end end end @@ -49,11 +49,11 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa # TODO: Look into refactoring this. if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name, conn) do - handle = _execute(sql, conn) + handle = internal_raw_execute(sql, conn) result = handle_to_names_and_values(handle, options) end else - handle = _execute(sql, conn) + handle = internal_raw_execute(sql, conn) result = handle_to_names_and_values(handle, options) end ensure @@ -176,7 +176,7 @@ def execute_procedure(proc_name, *variables) log(sql, "Execute Procedure") do with_raw_connection do |conn| - result = _execute(sql, conn) + result = internal_raw_execute(sql, conn) options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc } result.each(options) do |row| @@ -305,28 +305,13 @@ def sql_for_insert(sql, pk, binds, returning) # === SQLServer Specific ======================================== # def set_identity_insert(table_name, conn, enable) - _execute("SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}", conn , perform_do: true) + 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}" end # === SQLServer Specific (Executing) ============================ # - # TODO: Adapter should be refactored to use `with_raw_connection` to translate exceptions. - def sp_executesql(sql, name, binds, options = {}) - options[:ar_result] = true if options[:fetch] != :rows - - unless without_prepared_statement?(binds) - types, params = sp_executesql_types_and_parameters(binds) - sql = sp_executesql_sql(sql, types, params, name) - end - - raw_select sql, name, binds, options - rescue => original_exception - translated_exception = translate_exception_class(original_exception, sql, binds) - raise translated_exception - end - def sp_executesql_types_and_parameters(binds) types, params = [], [] binds.each_with_index do |attr, index| @@ -377,13 +362,6 @@ def sp_executesql_sql(sql, types, params, name) sql.freeze end - def raw_connection_do(sql) - result = ensure_established_connection! { dblib_execute(sql) } - result.do - ensure - @update_sql = false - end - # === SQLServer Specific (Identity Inserts) ===================== # def use_output_inserted? @@ -422,21 +400,14 @@ def identity_columns(table_name) # === SQLServer Specific (Selecting) ============================ # - def raw_select(sql, name = "SQL", binds = [], options = {}) - log(sql, name, binds, async: options[:async]) { _raw_select(sql, options) } - end + def _raw_select(sql, conn, options = {}) + handle = internal_raw_execute(sql, conn) - def _raw_select(sql, options = {}) - handle = raw_connection_run(sql) handle_to_names_and_values(handle, options) ensure finish_statement_handle(handle) end - def raw_connection_run(sql) - ensure_established_connection! { dblib_execute(sql) } - end - def handle_to_names_and_values(handle, options = {}) query_options = {}.tap do |qo| qo[:timezone] = ActiveRecord.default_timezone || :utc @@ -453,33 +424,16 @@ def finish_statement_handle(handle) handle end - # TODO: Rename - def _execute(sql, conn, perform_do: false) + # TinyTDS returns false instead of raising an exception if connection fails. + # Getting around this by raising an exception ourselves while PR + # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. + def internal_raw_execute(sql, conn, perform_do: false) result = conn.execute(sql).tap do |_result| - # TinyTDS returns false instead of raising an exception if connection fails. - # Getting around this by raising an exception ourselves while PR - # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. raise TinyTds::Error, "failed to execute statement" if _result.is_a?(FalseClass) end perform_do ? result.do : result end - - # TODO: Remove - def dblib_execute(sql) - @raw_connection.execute(sql).tap do |result| - # TinyTDS returns false instead of raising an exception if connection fails. - # Getting around this by raising an exception ourselves while this PR - # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. - raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) - end - end - - def ensure_established_connection! - raise TinyTds::Error, 'SQL Server client is not connected' unless @raw_connection - - yield - end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 53693f383..17a3016c6 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -124,7 +124,7 @@ def primary_keys_select(table_name) binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? - sp_executesql(sql, "SCHEMA", binds).map { |r| r["name"] } + internal_exec_query(sql, "SCHEMA", binds).map { |row| row["name"] } end def rename_table(table_name, new_name, **options) @@ -424,12 +424,13 @@ def column_definitions(table_name) view_tblnm = view_table_name(table_name) if view_exists if view_exists - results = sp_executesql %{ + sql = <<~SQL SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default] FROM #{database}.INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = #{quote(view_tblnm)} - }.squish, "SCHEMA", [] - default_functions = results.each.with_object({}) {|row, out| out[row["name"]] = row["default"] }.compact + SQL + results = internal_exec_query(sql, "SCHEMA") + default_functions = results.each.with_object({}) { |row, out| out[row["name"]] = row["default"] }.compact end sql = column_definitions_sql(database, identifier) @@ -438,7 +439,8 @@ def column_definitions(table_name) nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? - results = sp_executesql(sql, "SCHEMA", binds) + results = internal_exec_query(sql, "SCHEMA", binds) + columns = results.map do |ci| ci = ci.symbolize_keys ci[:_type] = ci[:type] diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index e6c89120c..734495f8a 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -14,7 +14,7 @@ module Showplan def explain(arel, binds = [], options = []) sql = to_sql(arel) - result = with_showplan_on { sp_executesql(sql, "EXPLAIN", binds) } + result = with_showplan_on { internal_exec_query(sql, "EXPLAIN", binds) } printer = showplan_printer.new(result) printer.pp end @@ -30,7 +30,7 @@ def with_showplan_on def set_showplan_option(enable = true) sql = "SET #{showplan_option} #{enable ? 'ON' : 'OFF'}" - raw_connection_do(sql) + raw_execute(sql, "SCHEMA") rescue Exception raise ActiveRecordError, "#{showplan_option} could not be turned #{enable ? 'ON' : 'OFF'}, perhaps you do not have SHOWPLAN permissions?" end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 96a32c411..fdf21bea4 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -234,10 +234,7 @@ def disable_referential_integrity # === Abstract Adapter (Connection Management) ================== # def active? - return false unless @raw_connection - - raw_connection_do "SELECT 1" - true + @raw_connection&.active? rescue *connection_errors false end @@ -504,7 +501,7 @@ def version_year end def sqlserver_version - @sqlserver_version ||= _raw_select("SELECT @@version", fetch: :rows).first.first.to_s + @sqlserver_version ||= _raw_select("SELECT @@version", @raw_connection, fetch: :rows).first.first.to_s end private @@ -529,7 +526,7 @@ def configure_connection @raw_connection.execute("SET TEXTSIZE 2147483647").do @raw_connection.execute("SET CONCAT_NULL_YIELDS_NULL ON").do - @spid = _raw_select("SELECT @@SPID", fetch: :rows).first.first + @spid = _raw_select("SELECT @@SPID", @raw_connection, fetch: :rows).first.first initialize_dateformatter use_database From a6b37349bdabff333179c9a272fb4959d037f36f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Oct 2023 11:43:00 +0100 Subject: [PATCH 1174/1412] Cleanup SQLite databases created by test suite --- .gitignore | 1 + test/cases/helper_sqlserver.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/.gitignore b/.gitignore index f7d974a26..7b49cb5e8 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ coverage/* .floo .byebug_history tmp/* +test/storage/test.sqlite3* diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4435eb95b..4ec42d945 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -27,6 +27,7 @@ class TestCase < ActiveSupport::TestCase setup :ensure_clean_rails_env setup :remove_backtrace_silencers + setup :cleanup_sqlite_databases private @@ -37,6 +38,12 @@ def ensure_clean_rails_env def remove_backtrace_silencers Rails.backtrace_cleaner.remove_silencers! end + + # Cleanup the SQLite database created by previous runs of the Rails ActiveRecord test suite. + def cleanup_sqlite_databases + sqlite_db_dir = File.join(ARTest::SQLServer.test_root_sqlserver, "db") + Dir.glob("#{sqlite_db_dir}/**").each { |f| File.delete(f) } + end def host_windows? RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ From a923a68cc7125f71bfeb917c46b0d7daa0e3494a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Oct 2023 12:12:18 +0100 Subject: [PATCH 1175/1412] Fix redefined method warnings (#1123) --- .gitignore | 2 +- test/cases/coerced_tests.rb | 9 ++++++++- test/cases/disconnected_test_sqlserver.rb | 1 + test/cases/helper_sqlserver.rb | 2 +- test/cases/rake_test_sqlserver.rb | 2 +- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 7b49cb5e8..6365e37d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ nbproject/ -debug.log +debug.log* .DS_Store pkg/ doc/ diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e47b0dd83..3fc5ce909 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -778,6 +778,7 @@ module ConnectionAdapters module ActiveRecord # The original module is hardcoded for PostgreSQL/SQLite/MySQL tests. module DatabaseTasksSetupper + undef_method :setup def setup @sqlserver_tasks = Class.new do @@ -800,6 +801,7 @@ def structure_load(*); end $stderr, @original_stderr = StringIO.new, $stderr end + undef_method :with_stubbed_new def with_stubbed_new ActiveRecord::Tasks::SQLServerDatabaseTasks.stub(:new, @sqlserver_tasks) do yield @@ -1953,8 +1955,9 @@ def with_marshable_time_defaults end # We need to give the full path for this to work. + undef_method :schema_dump_path def schema_dump_path - File.join ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml" + File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml") end end end @@ -2306,6 +2309,7 @@ class ReloadModelsTest < ActiveRecord::TestCase class MarshalSerializationTest < ActiveRecord::TestCase private + undef_method :marshal_fixture_path def marshal_fixture_path(file_name) File.expand_path( "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump", @@ -2561,6 +2565,7 @@ module ActiveRecord class Migration class InvalidOptionsTest < ActiveRecord::TestCase # Include the additional SQL Server migration options. + undef_method :invalid_add_column_option_exception_message def invalid_add_column_option_exception_message(key) default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"] default_keys.concat([":is_identity"]) # SQL Server additional valid keys @@ -2573,6 +2578,7 @@ def invalid_add_column_option_exception_message(key) # SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert. class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase + undef_method :thread_encrypting_and_decrypting def thread_encrypting_and_decrypting(thread_label) posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" } @@ -2590,6 +2596,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| ActiveRecord::MessagePack::Extensions.install(factory) diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index ccbc69de2..2936cf93f 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -5,6 +5,7 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase self.use_transactional_tests = false + undef_method :setup def setup @connection = ActiveRecord::Base.connection end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4ec42d945..6abd297c7 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -38,7 +38,7 @@ def ensure_clean_rails_env def remove_backtrace_silencers Rails.backtrace_cleaner.remove_silencers! end - + # Cleanup the SQLite database created by previous runs of the Rails ActiveRecord test suite. def cleanup_sqlite_databases sqlite_db_dir = File.join(ARTest::SQLServer.test_root_sqlserver, "db") diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 0cecf65e9..145bc7119 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -177,7 +177,7 @@ class SQLServerRakeSchemaCacheDumpLoadTest < SQLServerRakeTest quietly { db_tasks.dump_schema_cache connection, filename } filedata = File.read(filename) - schema_cache = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(filedata) : YAML.load(filedata) + _schema_cache = YAML.respond_to?(:unsafe_load) ? YAML.unsafe_load(filedata) : YAML.load(filedata) col_id, col_name = connection.schema_cache.columns("users") From 58cd06883d5799808ca626c74a5f262b5ed734ab Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Oct 2023 15:44:37 +0100 Subject: [PATCH 1176/1412] Coerce tests that are not testing SQL Server but using SQLite to test non-specific adapter functionality (#1124) --- test/cases/coerced_tests.rb | 44 ++++++++++++++++++++++++++++++++++ test/cases/helper_sqlserver.rb | 7 ------ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3fc5ce909..e60cd3c05 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1605,6 +1605,9 @@ def test_schema_dump_includes_decimal_options_coerced output = dump_all_table_schema([/^[^n]/]) assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output end + + # Tests are not about a specific adapter. + coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config end class SchemaDumperDefaultsTest < ActiveRecord::TestCase @@ -2611,6 +2614,47 @@ class StoreTest < ActiveRecord::TestCase Admin::User.attribute :json_options, ActiveRecord::Type::SQLServer::Json.new end +class TestDatabasesTest < ActiveRecord::TestCase + # Tests are not about a specific adapter. + coerce_all_tests! +end + +module ActiveRecord + module ConnectionAdapters + class ConnectionHandlersShardingDbTest < ActiveRecord::TestCase + # Tests are not about a specific adapter. + coerce_all_tests! + end + end +end + +module ActiveRecord + module ConnectionAdapters + class ConnectionSwappingNestedTest < ActiveRecord::TestCase + # Tests are not about a specific adapter. + coerce_all_tests! + end + end +end + +module ActiveRecord + module ConnectionAdapters + class ConnectionHandlersMultiDbTest < ActiveRecord::TestCase + # Tests are not about a specific adapter. + coerce_tests! :test_switching_connections_via_handler + end + end +end + +module ActiveRecord + module ConnectionAdapters + class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase + # Tests are not about a specific adapter. + coerce_all_tests! + end + end +end + # TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1 class SerializedAttributeTest < ActiveRecord::TestCase coerce_all_tests! diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 6abd297c7..4435eb95b 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -27,7 +27,6 @@ class TestCase < ActiveSupport::TestCase setup :ensure_clean_rails_env setup :remove_backtrace_silencers - setup :cleanup_sqlite_databases private @@ -39,12 +38,6 @@ def remove_backtrace_silencers Rails.backtrace_cleaner.remove_silencers! end - # Cleanup the SQLite database created by previous runs of the Rails ActiveRecord test suite. - def cleanup_sqlite_databases - sqlite_db_dir = File.join(ARTest::SQLServer.test_root_sqlserver, "db") - Dir.glob("#{sqlite_db_dir}/**").each { |f| File.delete(f) } - end - def host_windows? RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ end From e1e4bf8a0fb6ae3aba257f1e6339e92f967b3a0e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Oct 2023 15:54:05 +0100 Subject: [PATCH 1177/1412] Remove Ruby 2.7.8 from CI (#1125) --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 193bb6df4..d5dacad34 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,6 @@ jobs: fail-fast: false matrix: ruby: - - 2.7.8 - 3.0.6 - 3.1.4 - 3.2.2 From bd59ee431f632c4b3d487576fc9c217d0fa4dcb0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Nov 2023 11:45:08 +0000 Subject: [PATCH 1178/1412] Fix view issue with default column value not found (#1126) --- .../sqlserver/schema_statements.rb | 2 +- test/cases/view_test_sqlserver.rb | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 17a3016c6..0675d6f91 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -659,7 +659,7 @@ def views_real_column_name(table_name, column_name) view_definition = view_information(table_name)[:VIEW_DEFINITION] return column_name unless view_definition - match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im) + match_data = view_definition.match(/CREATE\s+VIEW.*AS\s+SELECT.*\W([\w-]*)\s+AS\s+#{column_name}/im) match_data ? match_data[1] : column_name end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 82985f37e..5fd5c973c 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -11,10 +11,18 @@ class ViewTestSQLServer < ActiveRecord::TestCase 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: false, default: "abc" end connection.execute("DROP VIEW IF EXISTS view_casing_table_view;") - connection.execute("CREATE VIEW view_casing_table_view AS SELECT id AS id, default_falsey AS falsey, default_truthy AS truthy FROM view_casing_table") + connection.execute <<-SQL + CREATE VIEW view_casing_table_view AS + SELECT id AS id, + default_falsey AS falsey, + default_truthy AS truthy, + default_string AS s + FROM view_casing_table + SQL end it "default values are correct when column casing used in tables and views are different" do @@ -25,11 +33,13 @@ class ViewTestSQLServer < ActiveRecord::TestCase obj = klass.new assert_equal false, obj.falsey assert_equal true, obj.truthy + assert_equal "abc", obj.s assert_equal 0, klass.count obj.save! assert_equal false, obj.falsey assert_equal true, obj.truthy + assert_equal "abc", obj.s assert_equal 1, klass.count end end From b38a6ccd0bdef52ee31a298a7deb7c8a9055e7d7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Nov 2023 14:56:43 +0000 Subject: [PATCH 1179/1412] Release v7.1.0.beta1 --- CHANGELOG.md | 2 +- README.md | 20 ++++++++++---------- VERSION | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index abfa1a3cb..59a59981a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.0.beta1 #### Changed diff --git a/README.md b/README.md index 8eaa4b0d1..22d86f2bf 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,16 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|----------------------|----------------|-------------------------------------------------------------------------------------------------| -| n/a | `7.1.0` (unreleased) | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.0.4.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| +| `v7.1.0.beta1` | `7.1.0` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.4.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | For older versions, please check their stable branches. diff --git a/VERSION b/VERSION index a3fcc7121..2adc99526 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0 +7.1.0.beta1 From 2bb87e350e3c8f539285ce58dc7ae00d3f5008a7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Nov 2023 16:06:36 +0000 Subject: [PATCH 1180/1412] Remove tests that no longer need to be coerced (#1127) --- test/cases/coerced_tests.rb | 59 ------------------------------------- 1 file changed, 59 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index e60cd3c05..35adc8a37 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -923,34 +923,6 @@ class DefaultScopingTest < ActiveRecord::TestCase coerce_tests! :test_order_in_default_scope_should_not_prevail end -require "models/post" -require "models/subscriber" -class EachTest < ActiveRecord::TestCase - # Quoting in tests does not cope with bracket quoting. - # TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged. - coerce_tests! :test_in_batches_no_subqueries_for_whole_tables_batching - def test_in_batches_no_subqueries_for_whole_tables_batching_coerced - c = Post.connection - quoted_posts_id = Regexp.escape(c.quote_table_name("posts.id")) - assert_sql(/DELETE FROM #{Regexp.escape(c.quote_table_name("posts"))} WHERE #{quoted_posts_id} > .+ AND #{quoted_posts_id} <=/i) do - Post.in_batches(of: 2).delete_all - end - end - - # Quoting in tests does not cope with bracket quoting. - # TODO: Remove coerced test when https://github.com/rails/rails/pull/49269 merged. - coerce_tests! :test_in_batches_should_quote_batch_order - def test_in_batches_should_quote_batch_order_coerced - c = Post.connection - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name('posts'))}\.#{Regexp.escape(c.quote_column_name('id'))}/) do - Post.in_batches(of: 1) do |relation| - assert_kind_of ActiveRecord::Relation, relation - assert_kind_of Post, relation.first - end - end - end -end - class EagerAssociationTest < ActiveRecord::TestCase # Use LEN() instead of LENGTH() function. coerce_tests! :test_count_with_include @@ -2533,37 +2505,6 @@ def test_insert_all_returns_requested_sql_fields_coerced end end -class HasOneThroughDisableJoinsAssociationsTest < ActiveRecord::TestCase - # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44051) - coerce_tests! :test_disable_joins_through_with_enum_type - def test_disable_joins_through_with_enum_type_coerced - joins = capture_sql { @member.club } - no_joins = capture_sql { @member.club_without_joins } - - assert_equal 1, joins.size - assert_equal 2, no_joins.size - - assert_match(/INNER JOIN/, joins.first) - no_joins.each do |nj| - assert_no_match(/INNER JOIN/, nj) - end - - assert_match(/\[memberships\]\.\[type\]/, no_joins.first) - end -end - -class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::EncryptionTestCase - # TODO: Remove coerce after Rails 7.1.0 (see https://github.com/rails/rails/pull/44052) - # Same as original but SQL Server string is varchar(4000), not varchar(255) as other adapters. Produce invalid strings with 4001 characters - coerce_tests! %r{validate column sizes} - test "validate column sizes coerced" do - assert EncryptedAuthor.new(name: "jorge").valid? - assert_not EncryptedAuthor.new(name: "a" * 4001).valid? - author = EncryptedAuthor.create(name: "a" * 4001) - assert_not author.valid? - end -end - module ActiveRecord class Migration class InvalidOptionsTest < ActiveRecord::TestCase From 24dd55774ad0bc27740d950aef905f173ee6a9d0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 6 Nov 2023 16:07:40 +0000 Subject: [PATCH 1181/1412] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22d86f2bf..c15c12369 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `v7.1.0.beta1` | `7.1.0` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `v7.1.0.beta1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.4.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | From f163e444433aca4c853a3f473fe2148e6734b698 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 7 Nov 2023 10:26:47 +0000 Subject: [PATCH 1182/1412] Do not coerce the SerializedAttributeTest tests (#1128) --- test/cases/coerced_tests.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 35adc8a37..52fac5070 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2595,8 +2595,3 @@ class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase end end end - -# TODO: Need to uncoerce the 'SerializedAttributeTest' tests before releasing adapter for Rails 7.1 -class SerializedAttributeTest < ActiveRecord::TestCase - coerce_all_tests! -end From d1122070b124bcdafac05ee9472447d3879f8761 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 12:10:21 +0000 Subject: [PATCH 1183/1412] Reset connections after READ COMMITTED isolation test (#1132) --- test/cases/transaction_test_sqlserver.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index f48bb2db1..9f1e0ae63 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -53,17 +53,10 @@ class TransactionTestSQLServer < ActiveRecord::TestCase end describe "when READ_COMMITTED_SNAPSHOT is set" do - before do + it "should use READ COMMITTED as an isolation level" do connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION ON" connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE" - end - - after do - connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF" - connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" - end - it "should use READ COMMITTED as an isolation level" do _(connection.user_options_isolation_level).must_match "read committed snapshot" Ship.transaction(isolation: :serializable) do @@ -74,6 +67,12 @@ class TransactionTestSQLServer < ActiveRecord::TestCase # "READ COMMITTED", and that no exception was raised (it's reported back # by SQL Server as "read committed snapshot"). _(connection.user_options_isolation_level).must_match "read committed snapshot" + ensure + connection.execute "ALTER DATABASE [#{connection.current_database}] SET ALLOW_SNAPSHOT_ISOLATION OFF" + connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" + + # Reset all connections. Otherwise, the next test may fail with error 'DBPROCESS is dead or not enabled'. Not sure why. + ActiveRecord::Base.connection_handler.clear_all_connections! end end From 1ed00c3a556eb299e1c4c7c562d574487497da57 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 12:32:59 +0000 Subject: [PATCH 1184/1412] Update RUNNING_UNIT_TESTS.md --- RUNNING_UNIT_TESTS.md | 34 ++++++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/RUNNING_UNIT_TESTS.md b/RUNNING_UNIT_TESTS.md index 6089d06c0..a4907d0ad 100644 --- a/RUNNING_UNIT_TESTS.md +++ b/RUNNING_UNIT_TESTS.md @@ -13,12 +13,12 @@ Default testing uses DBLIB with TinyTDS. * Setup two databases in SQL Server, [activerecord_unittest] and [activerecord_unittest2] * Create a [rails] user with an empty password and give it a [db_owner] role to both DBs. Some tests require a server role of [sysadmin] too. More details below with DDL SQL examples. -* $ bundle install -* $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' +* `bundle install` +* `bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net'` Focusing tests. Use the `ONLY_` env vars to run either ours or the ActiveRecord cases. Use the `TEST_FILES` env variants to focus on specific test(s), use commas for multiple cases. Note, you have to use different env vars to focus only on ours or a core ActiveRecord case. There may be failures when focusing on an ActiveRecord case since our coereced test files is not loaded in this scenerio. -``` +```console $ bundle exec rake test ONLY_SQLSERVER=1 $ bundle exec rake test ONLY_ACTIVERECORD=1 @@ -49,13 +49,13 @@ GO The tests of this adapter depend on the existence of the Rails which are automatically cloned for you with bundler. However you can clone Rails from git://github.com/rails/rails.git and set the `RAILS_SOURCE` environment variable so bundler will use another local path instead. -``` +```console $ git clone git://github.com/rails-sqlserver/activerecord-sqlserver-adapter.git ``` Suggest just letting bundler do all the work and assuming there is a git tag for the Rails version, you can set `RAILS_VERSION` before bundling. -``` +```console $ export RAILS_VERSION='4.2.0' $ bundle install ``` @@ -65,20 +65,20 @@ $ bundle install Please consult the `test/config.yml` file which is used to parse the configuration options for the DB connections when running tests. This file has overrides for any connection mode that you can set using simple environment variables. Assuming you are using FreeTDS 0.91 and above -``` +```console $ export ACTIVERECORD_UNITTEST_HOST='my.db.net' # Defaults to localhost $ export ACTIVERECORD_UNITTEST_PORT='1533' # Defaults to 1433 ``` If you have FreeTDS installed and/or want to use a named dataserver in your freetds.conf file -``` +```console $ export ACTIVERECORD_UNITTEST_DATASERVER='mydbname' ``` These can be passed down to rake too. -``` +```console $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' ``` @@ -87,16 +87,30 @@ $ bundle exec rake test ACTIVERECORD_UNITTEST_HOST='my.db.net' Now with that out of the way you can run "bundle install" to hook everything up. Our tests use bundler to setup the load paths correctly. The default mode is DBLIB using TinyTDS. It is important to use bundle exec so we can wire up the ActiveRecord test libs correctly. -``` +```console $ bundle exec rake test ``` ## Testing Options - By default, Bundler will download the Rails git repo and use the git tag that matches the dependency version in our gemspec. If you want to test another version of Rails, you can either temporarily change the :tag for Rails in the Gemfile. Likewise, you can clone the Rails repo your self to another directory and use the `RAILS_SOURCE` environment variable. +```console +$ RAILS_SOURCE='/vagrant/rails' bundle exec rake test +``` + +If you want to use a specific seed for the tests use the `TESTOPTS` env variable like: + +```console +$ bundle exec rake test TESTOPTS="--seed=45250" +``` + +And to make the tests fail-fast use: + +```console +$ bundle exec rake test TESTOPTS="-f" +``` ## Troubleshooting From b87b8ce812e56cd7745b11b2b18ccdbed7618b66 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 12:19:22 +0000 Subject: [PATCH 1185/1412] Fix matching view's real column name (#1133) --- .../sqlserver/schema_statements.rb | 9 ++++++--- test/cases/view_test_sqlserver.rb | 16 ++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 0675d6f91..e319bbc54 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -640,17 +640,19 @@ def view_information(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) 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" + if view_info view_info = view_info.with_indifferent_access 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 + 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 end + view_info end end @@ -659,7 +661,8 @@ def views_real_column_name(table_name, column_name) view_definition = view_information(table_name)[:VIEW_DEFINITION] return column_name unless view_definition - match_data = view_definition.match(/CREATE\s+VIEW.*AS\s+SELECT.*\W([\w-]*)\s+AS\s+#{column_name}/im) + # 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 ? match_data[1] : column_name end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 5fd5c973c..b371bd0b1 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -9,18 +9,20 @@ class ViewTestSQLServer < ActiveRecord::TestCase before do connection.drop_table :view_casing_table rescue nil 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: 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;") connection.execute <<-SQL CREATE VIEW view_casing_table_view AS SELECT id AS id, - default_falsey AS falsey, - default_truthy AS truthy, - default_string AS s + default_falsey AS falsey, + default_truthy AS truthy, + default_string_null AS s_null, + default_string AS s FROM view_casing_table SQL end @@ -34,12 +36,14 @@ 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_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_equal 1, klass.count end end From 9b86c74ef1ca0dc9fec8ca7230e092f486bc44b1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 12:50:16 +0000 Subject: [PATCH 1186/1412] Updated adapter versions --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c15c12369..4c0fcf15f 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,10 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `v7.1.0.beta1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.0.4.0` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.2.1` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | Active | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `7.1.0.beta1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | | `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | From 64f06122b23a6c9fb1ec4b51f53e9bf58ec1f03e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 14:11:09 +0000 Subject: [PATCH 1187/1412] Remove old commented out test (#1130) --- test/cases/coerced_tests.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 52fac5070..f2967c0aa 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2265,16 +2265,6 @@ def test_where_in_binds_logging_include_attribute_names_coerced end end -# TODO: Method `reset_column_information` does not exist. Comment out the test for the time being. -# class ActiveRecordSchemaTest < ActiveRecord::TestCase -# # Workaround for randomly failing test. -# coerce_tests! :test_has_primary_key -# def test_has_primary_key_coerced -# @schema_migration.reset_column_information -# original_test_has_primary_key -# end -# end - 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. From 1712b830ef9547f6176752d373862eccd2315029 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 14:21:33 +0000 Subject: [PATCH 1188/1412] Remove old code --- test/support/sql_counter_sqlserver.rb | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb index acf51208e..7144a4fd7 100644 --- a/test/support/sql_counter_sqlserver.rb +++ b/test/support/sql_counter_sqlserver.rb @@ -10,20 +10,5 @@ def capture_sql_ss ActiveRecord::SQLCounter.log.dup end end - - # TODO: Delete the code below after all Rails 6.1 tests passing. - # - # ignored_sql = [ - # /INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im, - # /sys.columns/i, - # /SELECT @@version/, - # /SELECT @@TRANCOUNT/, - # /(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/, - # /SELECT CAST\(.* AS .*\) AS value/, - # /SELECT DATABASEPROPERTYEX/im - # ] - # - # sqlcounter = ObjectSpace.each_object(ActiveRecord::SQLCounter).to_a.first - # sqlcounter.instance_variable_set :@ignore, Regexp.union(ignored_sql.push(sqlcounter.ignore)) end end From 90b73f9e27dfc47e57d2ee704f436acb9fe7ef60 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Nov 2023 14:21:44 +0000 Subject: [PATCH 1189/1412] Fix warning message --- test/cases/transaction_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 9f1e0ae63..02e4af92d 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -72,7 +72,7 @@ class TransactionTestSQLServer < ActiveRecord::TestCase connection.execute "ALTER DATABASE [#{connection.current_database}] SET READ_COMMITTED_SNAPSHOT OFF WITH ROLLBACK IMMEDIATE" # Reset all connections. Otherwise, the next test may fail with error 'DBPROCESS is dead or not enabled'. Not sure why. - ActiveRecord::Base.connection_handler.clear_all_connections! + ActiveRecord::Base.connection_handler.clear_all_connections!(:all) end end From 788290bd7294478aca597bae3e7da33f1ad4b8b6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Nov 2023 11:09:47 +0000 Subject: [PATCH 1190/1412] Refactor and cleanup code (#1134) --- .../sqlserver/database_statements.rb | 31 +++++++++---------- .../connection_adapters/sqlserver_adapter.rb | 4 +-- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3858090b8..1ad593bfe 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -43,21 +43,12 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa log(sql, name, binds, async: async) do with_raw_connection do |conn| - begin - options = { ar_result: true } - - # TODO: Look into refactoring this. - if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, conn) do - handle = internal_raw_execute(sql, conn) - result = handle_to_names_and_values(handle, options) - end - else - handle = internal_raw_execute(sql, conn) - result = handle_to_names_and_values(handle, options) + if id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name, conn) do + result = internal_exec_sql_query(sql, conn) end - ensure - finish_statement_handle(handle) + else + result = internal_exec_sql_query(sql, conn) end end end @@ -65,6 +56,13 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa result end + def internal_exec_sql_query(sql, conn) + handle = internal_raw_execute(sql, conn) + handle_to_names_and_values(handle, ar_result: true) + ensure + finish_statement_handle(handle) + end + def exec_delete(sql, name, binds) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" super(sql, name, binds).rows.first.first @@ -400,10 +398,9 @@ def identity_columns(table_name) # === SQLServer Specific (Selecting) ============================ # - def _raw_select(sql, conn, options = {}) + def _raw_select(sql, conn) handle = internal_raw_execute(sql, conn) - - handle_to_names_and_values(handle, options) + handle_to_names_and_values(handle, fetch: :rows) ensure finish_statement_handle(handle) end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index fdf21bea4..b3cbe29f8 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -501,7 +501,7 @@ def version_year end def sqlserver_version - @sqlserver_version ||= _raw_select("SELECT @@version", @raw_connection, fetch: :rows).first.first.to_s + @sqlserver_version ||= _raw_select("SELECT @@version", @raw_connection).first.first.to_s end private @@ -526,7 +526,7 @@ def configure_connection @raw_connection.execute("SET TEXTSIZE 2147483647").do @raw_connection.execute("SET CONCAT_NULL_YIELDS_NULL ON").do - @spid = _raw_select("SELECT @@SPID", @raw_connection, fetch: :rows).first.first + @spid = _raw_select("SELECT @@SPID", @raw_connection).first.first initialize_dateformatter use_database From fa7e95d57010a013d27eaaeb126e00e71d9177e2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Nov 2023 11:51:11 +0000 Subject: [PATCH 1191/1412] Reset database connections after tests --- test/cases/transaction_test_sqlserver.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cases/transaction_test_sqlserver.rb b/test/cases/transaction_test_sqlserver.rb index 02e4af92d..c098e4c82 100644 --- a/test/cases/transaction_test_sqlserver.rb +++ b/test/cases/transaction_test_sqlserver.rb @@ -42,6 +42,9 @@ class TransactionTestSQLServer < ActiveRecord::TestCase after_level = connection.user_options_isolation_level _(in_level).must_match %r{serializable}i _(after_level).must_match %r{read committed}i + ensure + # Reset all connections. Otherwise, the next test may fail with error 'DBPROCESS is dead or not enabled'. Not sure why. + ActiveRecord::Base.connection_handler.clear_all_connections!(:all) end it "can use an isolation level and reverts back to starting isolation level under exceptions" do @@ -50,6 +53,9 @@ class TransactionTestSQLServer < ActiveRecord::TestCase Ship.transaction(isolation: :serializable) { Ship.create! } }).must_raise(ActiveRecord::RecordInvalid) _(connection.user_options_isolation_level).must_match %r{read committed}i + ensure + # Reset all connections. Otherwise, the next test may fail with error 'DBPROCESS is dead or not enabled'. Not sure why. + ActiveRecord::Base.connection_handler.clear_all_connections!(:all) end describe "when READ_COMMITTED_SNAPSHOT is set" do From 9dc8d7aa17cbc7a9a80176fddb9a0c34f7f3d8ea Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Nov 2023 15:48:22 +0000 Subject: [PATCH 1192/1412] Release v7.1.0.rc1 (#1135) --- CHANGELOG.md | 41 ++++++++++++++++++++++++++++++++++++++--- README.md | 2 +- VERSION | 2 +- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59a59981a..1e3f7ba6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,44 @@ -## v7.1.0.beta1 +## v7.1.0.rc1 + +#### Added + +* Rails 7.1 Support + + The adapter supports new Rails 7.1 features such as composite primary keys. See the + [Rails 7.1 release notes](https://guides.rubyonrails.org/7_1_release_notes.html) for more information. #### Changed -#### Fixed +* Configure Connection -#### Added + If you require additional connection configuration you now need to call `super` within the `configure_connection` + method so that the default configuration is also applied. + + v7.1.x adapter: + ```ruby + module ActiveRecord + module ConnectionAdapters + class SQLServerAdapter < AbstractAdapter + def configure_connection + super + raw_connection_do "SET TEXTSIZE #{64.megabytes}" + end + end + end + end + ``` + + v7.0.x adapter: + ```ruby + module ActiveRecord + module ConnectionAdapters + class SQLServerAdapter < AbstractAdapter + def configure_connection + raw_connection_do "SET TEXTSIZE #{64.megabytes}" + end + end + end + end + ``` Please check [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-0-stable/CHANGELOG.md) for previous changes. diff --git a/README.md b/README.md index 4c0fcf15f..f13ba2440 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `7.1.0.beta1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.0.rc1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | diff --git a/VERSION b/VERSION index 2adc99526..b104ec350 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.beta1 +7.1.0.rc1 From 87fd749f02f586581d5c22c3e344088404437260 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Nov 2023 11:29:23 +0000 Subject: [PATCH 1193/1412] Reintroduce visit_Arel_Nodes_HomogeneousIn monkey-patch (#1137) --- .../tasks/sqlserver_database_tasks.rb | 2 +- lib/arel/visitors/sqlserver.rb | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index aa8f07f3d..ab87b365f 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -53,7 +53,7 @@ def purge end def clear_active_connections! - ActiveRecord::Base.connection_handler.clear_active_connections! + ActiveRecord::Base.connection_handler.clear_active_connections!(:all) end def structure_dump(filename, extra_flags) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 74019cc9a..6e3b3785b 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -64,6 +64,38 @@ def visit_Arel_Nodes_Grouping(o, collector) super end + def visit_Arel_Nodes_HomogeneousIn(o, collector) + collector.preparable = false + + visit o.left, collector + + if o.type == :in + collector << " IN (" + else + collector << " NOT IN (" + end + + values = o.casted_values + + # Monkey-patch start. + column_name = o.attribute.name + column_type = o.attribute.relation.type_for_attribute(column_name) + column_type = column_type.cast_type if column_type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) # Use cast_type on encrypted attributes. Don't encrypt them again + + if values.empty? + collector << @connection.quote(nil) + elsif @connection.prepared_statements && !column_type.serialized? + # Add query attribute bindings rather than just values. + attrs = values.map { |value| ActiveRecord::Relation::QueryAttribute.new(column_name, value, column_type) } + collector.add_binds(attrs, &bind_block) + else + collector.add_binds(values, o.proc_for_binds, &bind_block) + end + # Monkey-patch end. + + collector << ")" + end + def visit_Arel_Nodes_SelectStatement(o, collector) @select_statement = o distinct_One_As_One_Is_So_Not_Fetch o From 86a9c0e2c6172fbd15df53c2a812663d5ac88a92 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Nov 2023 12:14:34 +0000 Subject: [PATCH 1194/1412] Prevent marking broken connections as verified (#1136) --- CHANGELOG.md | 6 ++++++ .../connection_adapters/sqlserver/database_statements.rb | 3 +++ 2 files changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e3f7ba6b..7d5559242 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Added + +- [#1136](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1136) Prevent marking broken connections as verified + ## v7.1.0.rc1 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 1ad593bfe..9e2c522bb 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -23,6 +23,7 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac else internal_raw_execute(sql, conn, perform_do: true) end + verified! end end @@ -50,6 +51,7 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa else result = internal_exec_sql_query(sql, conn) end + verified! end end @@ -175,6 +177,7 @@ def execute_procedure(proc_name, *variables) log(sql, "Execute Procedure") do with_raw_connection do |conn| result = internal_raw_execute(sql, conn) + verified! options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc } result.each(options) do |row| From 9387b5b0507156a2e2bb60de6c13c55dccd93dce Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Nov 2023 12:15:11 +0000 Subject: [PATCH 1195/1412] Cache quoted names (#1138) --- .../connection_adapters/sqlserver/quoting.rb | 26 +++++++++++-------- .../connection_adapters/sqlserver/utils.rb | 12 +++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index ce70c7fbb..54f65ca54 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -4,18 +4,18 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Quoting - QUOTED_TRUE = "1".freeze - QUOTED_FALSE = "0".freeze - QUOTED_STRING_PREFIX = "N".freeze + QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc: + QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc: 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) @@ -34,7 +34,11 @@ def quote_string_single_national(s) end def quote_column_name(name) - SQLServer::Utils.extract_identifiers(name).quoted + QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted + end + + def quote_table_name(name) + QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted end def quote_default_expression(value, column) @@ -47,7 +51,7 @@ def quote_default_expression(value, column) end def quoted_true - QUOTED_TRUE + '1' end def unquoted_true @@ -55,7 +59,7 @@ def unquoted_true end def quoted_false - QUOTED_FALSE + '0' end def unquoted_false @@ -117,7 +121,7 @@ def quote(value) when ActiveRecord::Type::SQLServer::Data value.quoted when String, ActiveSupport::Multibyte::Chars - "#{QUOTED_STRING_PREFIX}#{super}" + "N#{super}" else super end diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index d5f87097e..002847919 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -6,13 +6,9 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Utils - QUOTED_STRING_PREFIX = "N" - # Value object to return identifiers from SQL Server names http://bit.ly/1CZ3EiL # Inspired from Rails PostgreSQL::Name adapter object in their own Utils. - # class Name - SEPARATOR = "." UNQUOTED_SCANNER = /\]?\./ QUOTED_SCANNER = /\A\[.*?\]\./ QUOTED_CHECKER = /\A\[/ @@ -42,7 +38,7 @@ def server_quoted end def fully_qualified_database_quoted - [server_quoted, database_quoted].compact.join(SEPARATOR) + [server_quoted, database_quoted].compact.join('.') end def fully_qualified? @@ -69,7 +65,7 @@ def to_s end def quoted - parts.map { |p| quote(p) if p }.join SEPARATOR + parts.map { |p| quote(p) if p }.join('.') end def quoted_raw @@ -132,7 +128,7 @@ def parts extend self def quote_string(s) - s.to_s.gsub /\'/, "''" + s.to_s.gsub(/\'/, "''") end def quote_string_single(s) @@ -140,7 +136,7 @@ def quote_string_single(s) end def quote_string_single_national(s) - "#{QUOTED_STRING_PREFIX}'#{quote_string(s)}'" + "N'#{quote_string(s)}'" end def quoted_raw(name) From fd9bd5147ad04380767f974d426390c2dc9d3252 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Nov 2023 12:16:25 +0000 Subject: [PATCH 1196/1412] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d5559242..4d8ab04be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Added - [#1136](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1136) Prevent marking broken connections as verified +- [#1138](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1138) Cache quoted names ## v7.1.0.rc1 From 3a907105b62be7df271bfd90fbc9b0d73f33f2df Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 Nov 2023 13:16:31 +0000 Subject: [PATCH 1197/1412] Release v7.1.0.rc2 (#1139) --- CHANGELOG.md | 2 +- README.md | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d8ab04be..54b6c630e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.0.rc2 #### Added diff --git a/README.md b/README.md index f13ba2440..4bd5a1dc3 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `7.1.0.rc1` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.0.rc2` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | diff --git a/VERSION b/VERSION index b104ec350..68356638f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.rc1 +7.1.0.rc2 From f3993f3321f436bcd261f96c93624d79b02ffe2c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 Nov 2023 12:36:16 +0000 Subject: [PATCH 1198/1412] Added support for check constraints (#1141) --- CHANGELOG.md | 6 ++ .../sqlserver/schema_statements.rb | 23 ++++++++ .../connection_adapters/sqlserver_adapter.rb | 4 ++ test/cases/coerced_tests.rb | 58 +++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54b6c630e..2db759135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Added + +- [#1141](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1141) Added support for check constraints. + ## v7.1.0.rc2 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e319bbc54..876bac06d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -267,6 +267,29 @@ def extract_foreign_key_action(action, fk_name) end end + def check_constraints(table_name) + sql = <<~SQL + select chk.name AS 'name', + chk.definition AS 'expression' + from sys.check_constraints chk + inner join sys.tables st on chk.parent_object_id = st.object_id + where + st.name = '#{table_name}' + SQL + + chk_info = internal_exec_query(sql, "SCHEMA") + + chk_info.map do |row| + options = { + name: row["name"] + } + expression = row["expression"] + expression = expression[1..-2] if expression.start_with?("(") && expression.end_with?(")") + + CheckConstraintDefinition.new(table_name, expression, options) + end + end + def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) limit = nil unless type_limitable diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index b3cbe29f8..1e272d913 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -171,6 +171,10 @@ def supports_datetime_with_precision? true end + def supports_check_constraints? + true + end + def supports_json? version_year >= 2016 end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f2967c0aa..1eec0d02d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1580,6 +1580,13 @@ def test_schema_dump_includes_decimal_options_coerced # Tests are not about a specific adapter. coerce_tests! :test_do_not_dump_foreign_keys_when_bypassed_by_config + + # 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 + assert_equal 't.check_constraint "[price]>[discounted_price]", name: "products_price_check"', constraint_definition + end end class SchemaDumperDefaultsTest < ActiveRecord::TestCase @@ -2585,3 +2592,54 @@ class ConnectionHandlersMultiPoolConfigTest < ActiveRecord::TestCase end end end + +module ActiveRecord + class Migration + class CheckConstraintTest < ActiveRecord::TestCase + # SQL Server formats the check constraint expression differently. + coerce_tests! :test_check_constraints + def test_check_constraints_coerced + check_constraints = @connection.check_constraints("products") + assert_equal 1, check_constraints.size + + constraint = check_constraints.first + assert_equal "products", constraint.table_name + assert_equal "products_price_check", constraint.name + assert_equal "[price]>[discounted_price]", constraint.expression + end + + # SQL Server formats the check constraint expression differently. + coerce_tests! :test_add_check_constraint + def test_add_check_constraint_coerced + @connection.add_check_constraint :trades, "quantity > 0" + + check_constraints = @connection.check_constraints("trades") + assert_equal 1, check_constraints.size + + constraint = check_constraints.first + assert_equal "trades", constraint.table_name + assert_equal "chk_rails_2189e9f96c", constraint.name + assert_equal "[quantity]>(0)", constraint.expression + end + + # SQL Server formats the check constraint expression differently. + coerce_tests! :test_remove_check_constraint + def test_remove_check_constraint_coerced + @connection.add_check_constraint :trades, "price > 0", name: "price_check" + @connection.add_check_constraint :trades, "quantity > 0", name: "quantity_check" + + assert_equal 2, @connection.check_constraints("trades").size + @connection.remove_check_constraint :trades, name: "quantity_check" + assert_equal 1, @connection.check_constraints("trades").size + + constraint = @connection.check_constraints("trades").first + assert_equal "trades", constraint.table_name + assert_equal "price_check", constraint.name + assert_equal "[price]>(0)", constraint.expression + + @connection.remove_check_constraint :trades, name: :price_check # name as a symbol + assert_empty @connection.check_constraints("trades") + end + end + end +end From 34a5df5c354b4967be98eae3b4e5d7f04d7265af Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 21 Nov 2023 11:26:49 +0000 Subject: [PATCH 1199/1412] Release v7.1.0 --- CHANGELOG.md | 2 +- README.md | 20 ++++++++++---------- VERSION | 2 +- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2db759135..c574a5469 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.0 #### Added diff --git a/README.md b/README.md index 4bd5a1dc3..af67b7de4 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,16 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `7.1.0.rc2` | `7.1.x` | In development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|---------|--------------------------------------------------------------------------------------------------| +| `7.1.0` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | For older versions, please check their stable branches. diff --git a/VERSION b/VERSION index 68356638f..a3fcc7121 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0.rc2 +7.1.0 From c7d2eab3b1cbe7b3dee4fc1f9b6237749d8d9923 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 8 Jan 2024 14:10:22 +0000 Subject: [PATCH 1200/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index af67b7de4..cb052a5ce 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ module ActiveRecord class SQLServerAdapter < AbstractAdapter def configure_connection super - raw_connection_do "SET TEXTSIZE #{64.megabytes}" + @raw_connection.execute("SET TEXTSIZE #{64.megabytes}").do end end end From 7e07687e763029b5f80b0f53e2948ec07c652d66 Mon Sep 17 00:00:00 2001 From: Kiyotaka Sasaya Date: Tue, 9 Jan 2024 00:01:03 +0900 Subject: [PATCH 1201/1412] Ensure correct order of COLLATE and NOT NULL in CREATE TABLE statements (#1145) --- CHANGELOG.md | 6 ++ .../sqlserver/schema_creation.rb | 6 +- test/cases/active_schema_test_sqlserver.rb | 63 +++++++++++-------- 3 files changed, 46 insertions(+), 29 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c574a5469..83d510304 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1145](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1145) Ensure correct order of COLLATE and NOT NULL in CREATE TABLE statements + ## v7.1.0 #### Added diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 95710ae8e..f6d0e400a 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -51,12 +51,12 @@ def visit_CreateIndexDefinition(o) def add_column_options!(sql, options) sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) - if options[:null] == false - sql << " NOT NULL" - end if options[:collation].present? sql << " COLLATE #{options[:collation]}" end + if options[:null] == false + sql << " NOT NULL" + end if options[:is_identity] == true sql << " IDENTITY(1,1)" end diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb index 6d38f5820..35585cfe5 100644 --- a/test/cases/active_schema_test_sqlserver.rb +++ b/test/cases/active_schema_test_sqlserver.rb @@ -3,43 +3,44 @@ require "cases/helper_sqlserver" class ActiveSchemaTestSQLServer < ActiveRecord::TestCase - before do - connection.create_table :schema_test_table, force: true, id: false do |t| - t.column :foo, :string, limit: 100 - t.column :state, :string + describe "indexes" do + + before do + connection.create_table :schema_test_table, force: true, id: false do |t| + t.column :foo, :string, limit: 100 + t.column :state, :string + end end - end - after do - connection.drop_table :schema_test_table rescue nil - end + after do + connection.drop_table :schema_test_table rescue nil + end - it 'default index' do - assert_sql('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do - connection.add_index :schema_test_table, "foo" + it 'default index' do + assert_sql('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do + connection.add_index :schema_test_table, "foo" + end end - end - it 'unique index' do - assert_sql('CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do - connection.add_index :schema_test_table, "foo", unique: true + it 'unique index' do + assert_sql('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 - end - it 'where condition on index' do - assert_sql("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'" + it 'where condition on index' do + assert_sql("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 - end - it 'if index does not exist' do - assert_sql("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 + it 'if index does not exist' do + assert_sql("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 - end - describe "index types" do it 'clustered index' do assert_sql('CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') do connection.add_index :schema_test_table, "foo", type: :clustered @@ -52,4 +53,14 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase end end end + + 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| + t.text :not_null_text_with_collation, null: false, collation: "Latin1_General_CS_AS" + end + end + ensure + connection.drop_table :not_null_with_collation_table rescue nil + end end From 31c5fb98eb8d26f9170a8e982253db9bd242b46f Mon Sep 17 00:00:00 2001 From: Kiyotaka Sasaya Date: Tue, 9 Jan 2024 00:36:24 +0900 Subject: [PATCH 1202/1412] Fix precision handling for datetimeoffset migration (#1143) --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 10 +++++ test/cases/active_schema_test_sqlserver.rb | 45 ++++++++++++++++--- test/cases/column_test_sqlserver.rb | 5 ++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d510304..4b391aa57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#1145](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1145) Ensure correct order of COLLATE and NOT NULL in CREATE TABLE statements +- [#1143](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1143) Fix precision handling for datetimeoffset migration ## v7.1.0 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 876bac06d..76bba0d25 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -313,6 +313,16 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) end end column_type_sql + when "datetimeoffset" + column_type_sql = super + if precision + if (0..7) === precision + column_type_sql << "(#{precision})" + else + raise(ActiveRecordError, "The datetimeoffset type has precision of #{precision}. The allowed range of precision is from 0 to 7.") + end + end + column_type_sql else super end diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb index 35585cfe5..75784ff38 100644 --- a/test/cases/active_schema_test_sqlserver.rb +++ b/test/cases/active_schema_test_sqlserver.rb @@ -4,7 +4,6 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase describe "indexes" do - before do connection.create_table :schema_test_table, force: true, id: false do |t| t.column :foo, :string, limit: 100 @@ -54,13 +53,45 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase end end - 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| - t.text :not_null_text_with_collation, null: false, collation: "Latin1_General_CS_AS" + 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| + t.text :not_null_text_with_collation, null: false, collation: "Latin1_General_CS_AS" + end + end + ensure + connection.drop_table :not_null_with_collation_table rescue nil + end + end + + 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 + t.datetimeoffset :precision_5, precision: 5 + t.datetimeoffset :precision_7, precision: 7 + end + end + + columns = connection.columns("datetimeoffset_precisions") + + assert_equal columns.find { |column| column.name == "precision_default" }.precision, 7 + 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 + end + + 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 end - ensure - connection.drop_table :not_null_with_collation_table rescue nil end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index 8551357d9..dfbd42e47 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -435,13 +435,15 @@ def assert_obj_set_and_save(attribute, value) _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil - # Can save 100 nanosecond precisoins and return again. + + # Can save 100 nanosecond precisions and return again. obj.datetimeoffset_7 = Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456755) _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.save! _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" obj.reload _(obj.datetimeoffset_7).must_equal Time.new(2010, 4, 1, 12, 34, 56, +18000).change(nsec: 123456800), "Nanoseconds were <#{obj.datetimeoffset_7.nsec}> vs <123456800>" + # Maintains the timezone time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetimeoffset_7 = time @@ -449,6 +451,7 @@ def assert_obj_set_and_save(attribute, value) obj.save! _(obj.datetimeoffset_7).must_equal time _(obj.reload.datetimeoffset_7).must_equal time + # With other precisions. time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column("datetimeoffset_3") From 1bf0e4c36ee5c86bc50a18ed5faa28f9d85c8daf Mon Sep 17 00:00:00 2001 From: Kiyotaka Sasaya Date: Tue, 9 Jan 2024 01:13:21 +0900 Subject: [PATCH 1203/1412] Fix precision handling in time migration (#1144) --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 12 +++++++- test/cases/active_schema_test_sqlserver.rb | 30 +++++++++++++++++++ test/cases/coerced_tests.rb | 3 ++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b391aa57..ebe56ee98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Fixed - [#1145](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1145) Ensure correct order of COLLATE and NOT NULL in CREATE TABLE statements +- [#1144](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1144) Fix precision handling in time migration - [#1143](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1143) Fix precision handling for datetimeoffset migration ## v7.1.0 diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 76bba0d25..a0b43d1b3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -303,6 +303,16 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) 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 + column_type_sql = type.to_s + if precision + if (0..7) === precision + column_type_sql << "(#{precision})" + else + raise(ActiveRecordError, "The time type has precision of #{precision}. The allowed range of precision is from 0 to 7") + end + end + column_type_sql when "datetime2" column_type_sql = super if precision @@ -319,7 +329,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) if (0..7) === precision column_type_sql << "(#{precision})" else - raise(ActiveRecordError, "The datetimeoffset type has precision of #{precision}. The allowed range of precision is from 0 to 7.") + raise(ActiveRecordError, "The datetimeoffset type has precision of #{precision}. The allowed range of precision is from 0 to 7") end end column_type_sql diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb index 75784ff38..a3ceedec1 100644 --- a/test/cases/active_schema_test_sqlserver.rb +++ b/test/cases/active_schema_test_sqlserver.rb @@ -94,4 +94,34 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase connection.drop_table :datetimeoffset_precisions rescue nil end end + + 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 + t.time :precision_5, precision: 5 + t.time :precision_7, precision: 7 + end + end + + columns = connection.columns("time_precisions") + + assert_equal columns.find { |column| column.name == "precision_default" }.precision, 7 + 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 + end + + 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 + end + end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1eec0d02d..315a2ac45 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1832,6 +1832,9 @@ def test_time_precision_is_truncated_on_assignment_coerced # SQL Server uses default precision for time. coerce_tests! :test_no_time_precision_isnt_truncated_on_assignment + + # SQL Server accepts precision of 7 for time. + coerce_tests! :test_invalid_time_precision_raises_error end class DefaultNumbersTest < ActiveRecord::TestCase From 48c89b199fb4249726f2441c9db77ae95e6404f8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 8 Jan 2024 16:22:19 +0000 Subject: [PATCH 1204/1412] Release v7.1.1 --- CHANGELOG.md | 2 +- README.md | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebe56ee98..6f30e604a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.1 #### Fixed diff --git a/README.md b/README.md index cb052a5ce..2c116bb63 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|---------|--------------------------------------------------------------------------------------------------| -| `7.1.0` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.1` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | diff --git a/VERSION b/VERSION index a3fcc7121..21c8c7b46 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.0 +7.1.1 From c75651b86a28f3428c0e918ba2acf6478a5ca9fd Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Jan 2024 19:13:52 +0000 Subject: [PATCH 1205/1412] Fix Preloader to not generate a query for already loaded association with query_constraints (#1150) --- .../connection_adapters/sqlserver/core_ext/preloader.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index db258bd7e..046edb3ea 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -10,6 +10,8 @@ module LoaderQuery def load_records_for_keys(keys, &block) return super unless scope.connection.sqlserver? + return [] if keys.empty? + if association_key_name.is_a?(Array) query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new } From c5710d9b0e5741d600e32859b4fc771f04d00999 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 19 Jan 2024 09:56:06 +0000 Subject: [PATCH 1206/1412] Added Dev Container CLI to run the tests (#1149) --- .devcontainer/Dockerfile | 30 ++++++++++++++++++++++ .devcontainer/boot.sh | 22 ++++++++++++++++ .devcontainer/devcontainer.json | 38 +++++++++++++++++++++++++++ .devcontainer/docker-compose.yml | 35 +++++++++++++++++++++++++ README.md | 44 +++++++++++++++++++++++++++++--- RUNNING_UNIT_TESTS.md | 7 +++-- 6 files changed, 169 insertions(+), 7 deletions(-) create mode 100644 .devcontainer/Dockerfile create mode 100755 .devcontainer/boot.sh create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose.yml diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..8c67b380d --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,30 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.191.1/containers/ruby/.devcontainer/base.Dockerfile + +# [Choice] Ruby version: 3, 3.0, 2, 2.7, 2.6 +ARG VARIANT="3" +FROM mcr.microsoft.com/devcontainers/ruby:${VARIANT} + +# TinyTDS +RUN apt-get -y install libc6-dev \ + && wget http://www.freetds.org/files/stable/freetds-1.1.32.tar.gz \ + && tar -xzf freetds-1.1.32.tar.gz \ + && cd freetds-1.1.32 \ + && ./configure --prefix=/usr/local --with-tdsver=7.3 \ + && make \ + && make install + +# Install the SQL Server command-line tools +RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/trusted.gpg.d/microsoft.asc \ + && curl https://packages.microsoft.com/config/ubuntu/22.04/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list \ + && apt-get update \ + && ACCEPT_EULA=Y apt-get install -y mssql-tools18 unixodbc-dev \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> ~/.bashrc \ + && echo 'export PATH="$PATH:/opt/mssql-tools18/bin"' >> /root/.bashrc + +# Add the SQL Server main Gemfile and install the gems. +RUN mkdir -p /tmp/activerecord-sqlserver-adapter +COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ +RUN cd /tmp/activerecord-sqlserver-adapter \ + && bundle install \ + && rm -rf /tmp/activerecord-sqlserver-adapter +RUN chown -R vscode:vscode /usr/local/rvm diff --git a/.devcontainer/boot.sh b/.devcontainer/boot.sh new file mode 100755 index 000000000..8b545cb63 --- /dev/null +++ b/.devcontainer/boot.sh @@ -0,0 +1,22 @@ +sudo chown -R vscode:vscode /usr/local/bundle + +# Wait for 5 seconds to make sure SQL Server came up. +sleep 5 + +# Setup test databases and users. +/opt/mssql-tools18/bin/sqlcmd -C -S sqlserver -U sa -P "MSSQLadmin!" < Date: Mon, 22 Jan 2024 11:32:45 +0000 Subject: [PATCH 1207/1412] FROM subquery should work if order provided (#1151) --- CHANGELOG.md | 6 ++++++ lib/arel/visitors/sqlserver.rb | 9 ++++----- test/cases/fetch_test_sqlserver.rb | 19 +++++++++++++++++++ 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f30e604a..4f011a2a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1151](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1151) FROM subquery should work if order provided + ## v7.1.1 #### Fixed diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 6e3b3785b..fe56bbef1 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -213,7 +213,7 @@ def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {}) def visit_Orders_And_Let_Fetch_Happen(o, collector) make_Fetch_Possible_And_Deterministic o - unless o.orders.empty? + if o.orders.any? collector << " ORDER BY " len = o.orders.length - 1 o.orders.each_with_index { |x, i| @@ -261,15 +261,14 @@ def select_statement_lock? def make_Fetch_Possible_And_Deterministic(o) return if o.limit.nil? && o.offset.nil? + return if o.orders.any? t = table_From_Statement o pk = primary_Key_From_Table t return unless pk - if o.orders.empty? - # Prefer deterministic vs a simple `(SELECT NULL)` expr. - o.orders = [pk.asc] - end + # Prefer deterministic vs a simple `(SELECT NULL)` expr. + o.orders = [pk.asc] end def distinct_One_As_One_Is_So_Not_Fetch(o) diff --git a/test/cases/fetch_test_sqlserver.rb b/test/cases/fetch_test_sqlserver.rb index 6a55dec7d..2b55bd25a 100755 --- a/test/cases/fetch_test_sqlserver.rb +++ b/test/cases/fetch_test_sqlserver.rb @@ -42,6 +42,25 @@ class FetchTestSqlserver < ActiveRecord::TestCase end end + describe "FROM subquery" do + let(:from_sql) { "(SELECT [books].* FROM [books]) [books]" } + + it "SQL generated correctly for FROM subquery if order provided" do + 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 + end + + it "exception thrown if FROM subquery is provided without an order" do + query = Book.from(from_sql).limit(5) + + assert_raise(ActiveRecord::StatementInvalid) do + query.to_sql + end + end + end + protected def create_10_books From b715fc9cfe2e54be3732a3304ec23953dfc80dec Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Jan 2024 14:59:51 +0000 Subject: [PATCH 1208/1412] Allow connections from local to SQL Server container --- .devcontainer/docker-compose.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 19c09fdf2..df424bcc1 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -27,6 +27,8 @@ services: restart: unless-stopped networks: - default + ports: + - "1433:1433" environment: MSSQL_SA_PASSWORD: MSSQLadmin! ACCEPT_EULA: Y From 652397276d0b4ad58542163033ccfe450a173483 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 23 Jan 2024 10:51:12 +0000 Subject: [PATCH 1209/1412] Release v7.1.2 --- CHANGELOG.md | 2 +- README.md | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f011a2a1..8b0c41290 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.2 #### Fixed diff --git a/README.md b/README.md index 95c7e54b4..457f3ec0d 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|---------|--------------------------------------------------------------------------------------------------| -| `7.1.1` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.2` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | diff --git a/VERSION b/VERSION index 21c8c7b46..a8a188756 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.1 +7.1.2 From 9ef538741e690bd6bbbcb87142d344de227a0c7b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 23 Jan 2024 11:59:02 +0000 Subject: [PATCH 1210/1412] Use data volume container --- .devcontainer/docker-compose.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index df424bcc1..51d97fe65 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -27,6 +27,8 @@ services: restart: unless-stopped networks: - default + volumes: + - sqlserver-data:/var/opt/mssql ports: - "1433:1433" environment: @@ -35,3 +37,6 @@ services: networks: default: + +volumes: + sqlserver-data: From f34bfc0506fca13cba3ad5d27afe6f1cb81fc8a7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 23 Jan 2024 11:59:17 +0000 Subject: [PATCH 1211/1412] Wait longer to make sure database is up --- .devcontainer/boot.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/boot.sh b/.devcontainer/boot.sh index 8b545cb63..83e81f691 100755 --- a/.devcontainer/boot.sh +++ b/.devcontainer/boot.sh @@ -1,7 +1,7 @@ sudo chown -R vscode:vscode /usr/local/bundle -# Wait for 5 seconds to make sure SQL Server came up. -sleep 5 +# Wait for 10 seconds to make sure SQL Server came up. +sleep 10 # Setup test databases and users. /opt/mssql-tools18/bin/sqlcmd -C -S sqlserver -U sa -P "MSSQLadmin!" < Date: Tue, 13 Feb 2024 16:42:40 +0000 Subject: [PATCH 1212/1412] Fixed directory name --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 457f3ec0d..4a255ee31 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ utilize the [`.devcontainer`](https://github.com/rails-sqlserver/activerecord-sq ```bash $ npm install -g @devcontainers/cli -$ cd rails +$ cd activerecord-sqlserver-adapter $ devcontainer up --workspace-folder . $ devcontainer exec --workspace-folder . bash ``` From 446bca407335d38afaa4fd1f3e5bfeac8b3f3d54 Mon Sep 17 00:00:00 2001 From: Jon Hinson Date: Wed, 14 Feb 2024 03:15:16 -0600 Subject: [PATCH 1213/1412] Fix Composite Key Inserts with Triggers (#1152) --- CHANGELOG.md | 6 ++++++ .../sqlserver/database_statements.rb | 8 ++++---- test/cases/trigger_test_sqlserver.rb | 10 ++++++++++ test/models/sqlserver/trigger.rb | 4 ++++ test/schema/sqlserver_specific_schema.rb | 17 +++++++++++++++++ 5 files changed, 41 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b0c41290..fb9c36917 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Unreleased + +#### Fixed + +- [#1152](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1152) Fix Composite Key Inserts with Triggers + ## v7.1.2 #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9e2c522bb..31f17033f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -278,13 +278,13 @@ def sql_for_insert(sql, pk, binds, returning) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted - quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted + quoted_pk = Array(pk).map { |subkey| SQLServer::Utils.extract_identifiers(subkey).quoted } id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted <<~SQL.squish - DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type}); - #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"} - SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable + DECLARE @ssaIdInsertTable table (#{quoted_pk.map { |subkey| "#{subkey} #{id_sql_type}"}.join(", ") }); + #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{ quoted_pk.map { |subkey| "INSERTED.#{subkey}" }.join(", ") } INTO @ssaIdInsertTable"} + SELECT #{quoted_pk.map {|subkey| "CAST(#{subkey} AS #{id_sql_type}) #{subkey}"}.join(", ")} FROM @ssaIdInsertTable SQL else returning_columns = returning || Array(pk) diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index 8af51e68a..e530b4840 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -28,4 +28,14 @@ class SQLServerTriggerTest < ActiveRecord::TestCase _(obj.id).must_be :present? _(obj.id.to_s).must_equal SSTestTriggerHistory.first.id_source end + + it "can insert into a table with composite pk with output inserted - with a true setting for table name" do + exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger"] = true + assert SSTestTriggerHistory.all.empty? + obj = SSTestTriggerCompositePk.create! pk_col_one: 123, pk_col_two: 42, event_name: "test trigger" + _(obj.event_name).must_equal "test trigger" + _(obj.pk_col_one).must_equal 123 + _(obj.pk_col_two).must_equal 42 + _(obj.pk_col_one.to_s).must_equal SSTestTriggerHistory.first.id_source + end end diff --git a/test/models/sqlserver/trigger.rb b/test/models/sqlserver/trigger.rb index 3293cb6eb..bbdd66827 100644 --- a/test/models/sqlserver/trigger.rb +++ b/test/models/sqlserver/trigger.rb @@ -7,3 +7,7 @@ class SSTestTrigger < ActiveRecord::Base class SSTestTriggerUuid < ActiveRecord::Base self.table_name = "sst_table_with_uuid_trigger" end + +class SSTestTriggerCompositePk < ActiveRecord::Base + self.table_name = "sst_table_with_composite_pk_trigger" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index f0262f410..8982a2170 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -232,6 +232,23 @@ SELECT id AS id_source, event_name FROM INSERTED SQL + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_composite_pk_trigger') DROP TABLE sst_table_with_composite_pk_trigger" + execute <<-SQL + CREATE TABLE sst_table_with_composite_pk_trigger( + pk_col_one int NOT NULL, + pk_col_two int NOT NULL, + event_name nvarchar(255), + CONSTRAINT PK_sst_table_with_composite_pk_trigger PRIMARY KEY (pk_col_one, pk_col_two) + ) + SQL + execute <<-SQL + CREATE TRIGGER sst_table_with_composite_pk_trigger_t ON sst_table_with_composite_pk_trigger + FOR INSERT + AS + INSERT INTO sst_table_with_trigger_history (id_source, event_name) + SELECT pk_col_one AS id_source, event_name FROM INSERTED + SQL + # Another schema. create_table :sst_schema_columns, force: true do |t| From 042cc1e828ecfb3216c138252d10f7e060340cc2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 15 Feb 2024 09:59:21 +0000 Subject: [PATCH 1214/1412] Release v7.1.3 --- CHANGELOG.md | 2 +- README.md | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fb9c36917..6a37d2971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v7.1.3 #### Fixed diff --git a/README.md b/README.md index 4a255ee31..b7ffee7c7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|---------|--------------------------------------------------------------------------------------------------| -| `7.1.2` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.3` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | diff --git a/VERSION b/VERSION index a8a188756..1996c5044 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.2 +7.1.3 From cb146526d02db666840a9c65d2c3d5f7d7aef77a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Feb 2024 11:38:47 +0000 Subject: [PATCH 1215/1412] Updated main branch for Rails edge development --- Dockerfile.ci | 2 +- README.md | 21 +++++++++++---------- docker-compose.ci.yml | 1 + 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..7f75f1a9e 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_MAIN=1 bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/README.md b/README.md index b7ffee7c7..1e4b9cbf0 100644 --- a/README.md +++ b/README.md @@ -11,16 +11,17 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|---------------|---------|--------------------------------------------------------------------------------------------------| -| `7.1.3` | `7.1.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| +| unreleased | edge | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.3` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | For older versions, please check their stable branches. diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbc842619..82c48d48a 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_MAIN=1 build: context: . dockerfile: Dockerfile.ci From f41d74782486cd4fe5550aa967e1d0d41f39a5c2 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Feb 2024 11:39:32 +0000 Subject: [PATCH 1216/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e4b9cbf0..f80b772e7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| unreleased | edge | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| Unreleased | Edge | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.1.3` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | From 481afa6162ea55ce59a0625f45dcd24c58273043 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Feb 2024 11:45:27 +0000 Subject: [PATCH 1217/1412] Fix CI --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index 1996c5044..2031da2af 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.1.3 +7.2.0.alpha diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index f158dca18..6d63dcdf3 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.1.1" + spec.add_dependency "activerecord", "~> 7.2.0.alpha" spec.add_dependency "tiny_tds" end From c0088ce4193a6b70d89ed4d4123bf8b388e92cc0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 2 Mar 2024 20:29:46 +0000 Subject: [PATCH 1218/1412] Only support Ruby v3.1+ (#1153) --- .github/workflows/ci.yml | 2 +- CHANGELOG.md | 76 +------------------------- activerecord-sqlserver-adapter.gemspec | 2 +- 3 files changed, 5 insertions(+), 75 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d5dacad34..5f409874a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: fail-fast: false matrix: ruby: - - 3.0.6 - 3.1.4 - 3.2.2 + - 3.3.0 steps: - name: Checkout code diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a37d2971..213a453e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,77 +1,7 @@ -## v7.1.3 - -#### Fixed - -- [#1152](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1152) Fix Composite Key Inserts with Triggers - -## v7.1.2 - -#### Fixed - -- [#1151](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1151) FROM subquery should work if order provided - -## v7.1.1 - -#### Fixed - -- [#1145](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1145) Ensure correct order of COLLATE and NOT NULL in CREATE TABLE statements -- [#1144](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1144) Fix precision handling in time migration -- [#1143](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1143) Fix precision handling for datetimeoffset migration - -## v7.1.0 - -#### Added - -- [#1141](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1141) Added support for check constraints. - -## v7.1.0.rc2 - -#### Added - -- [#1136](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1136) Prevent marking broken connections as verified -- [#1138](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1138) Cache quoted names - -## v7.1.0.rc1 - -#### Added - -* Rails 7.1 Support - - The adapter supports new Rails 7.1 features such as composite primary keys. See the - [Rails 7.1 release notes](https://guides.rubyonrails.org/7_1_release_notes.html) for more information. +## Unreleased #### Changed -* Configure Connection - - If you require additional connection configuration you now need to call `super` within the `configure_connection` - method so that the default configuration is also applied. - - v7.1.x adapter: - ```ruby - module ActiveRecord - module ConnectionAdapters - class SQLServerAdapter < AbstractAdapter - def configure_connection - super - raw_connection_do "SET TEXTSIZE #{64.megabytes}" - end - end - end - end - ``` - - v7.0.x adapter: - ```ruby - module ActiveRecord - module ConnectionAdapters - class SQLServerAdapter < AbstractAdapter - def configure_connection - raw_connection_do "SET TEXTSIZE #{64.megabytes}" - end - end - end - end - ``` +- [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ -Please check [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-0-stable/CHANGELOG.md) for previous changes. +Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 6d63dcdf3..3d603b673 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.version = version - spec.required_ruby_version = ">= 2.7.0" + spec.required_ruby_version = ">= 3.1.0" spec.license = "MIT" spec.authors = ["Ken Collins", "Anna Carey", "Will Bond", "Murray Steele", "Shawn Balestracci", "Joe Rafaniello", "Tom Ward", "Aidan Haran"] From bba549fa0e1f7799486043ec745df5c78ea1ab89 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 14 Mar 2024 14:36:30 +0000 Subject: [PATCH 1219/1412] Register the adapter and lease connections --- .../sqlserver/core_ext/attribute_methods.rb | 2 +- .../sqlserver/core_ext/calculations.rb | 4 ++-- .../sqlserver/core_ext/explain.rb | 2 +- .../sqlserver/core_ext/finder_methods.rb | 2 +- .../connection_adapters/sqlserver_adapter.rb | 3 ++- lib/active_record/sqlserver_base.rb | 13 ----------- test/cases/adapter_test_sqlserver.rb | 2 +- test/cases/coerced_tests.rb | 22 +++++++++---------- test/cases/disconnected_test_sqlserver.rb | 2 +- test/cases/json_test_sqlserver.rb | 2 +- test/cases/lateral_test_sqlserver.rb | 4 ++-- test/cases/primary_keys_test_sqlserver.rb | 4 ++-- test/cases/schema_dumper_test_sqlserver.rb | 4 ++-- test/cases/specific_schema_test_sqlserver.rb | 4 ++-- test/cases/view_test_sqlserver.rb | 2 +- test/support/connection_reflection.rb | 2 +- test/support/test_in_memory_oltp.rb | 4 ++-- 17 files changed, 33 insertions(+), 45 deletions(-) delete mode 100644 lib/active_record/sqlserver_base.rb 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 af0e6a4e7..9cf0a7959 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 @@ -10,7 +10,7 @@ module AttributeMethods private def attributes_for_update(attribute_names) - return super unless self.class.connection.adapter_name == "SQLServer" + return super unless self.class.lease_connection.adapter_name == "SQLServer" super.reject do |name| column = self.class.columns_hash[name] diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 34355ef53..b35a719e6 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -9,7 +9,7 @@ module SQLServer module CoreExt module Calculations def calculate(operation, column_name) - if klass.connection.sqlserver? + if klass.lease_connection.sqlserver? _calculate(operation, column_name) else super @@ -54,7 +54,7 @@ def _calculate(operation, column_name) end def build_count_subquery(relation, column_name, distinct) - return super unless klass.connection.adapter_name == "SQLServer" + return super unless klass.lease_connection.adapter_name == "SQLServer" super(relation.unscope(:order), column_name, distinct) 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 4eea43e3d..6f17fa249 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -9,7 +9,7 @@ module Explain SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries, options = []) - return super unless connection.adapter_name == "SQLServer" + return super unless lease_connection.adapter_name == "SQLServer" unprepared_queries = queries.map do |(sql, binds)| [unprepare_sqlserver_statement(sql, binds), binds] 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 fe9271508..86b866078 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 @@ -11,7 +11,7 @@ module FinderMethods private def construct_relation_for_exists(conditions) - if klass.connection.sqlserver? + if klass.lease_connection.sqlserver? _construct_relation_for_exists(conditions) else super diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 1e272d913..bffaad63a 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -29,12 +29,13 @@ require "active_record/connection_adapters/sqlserver/table_definition" require "active_record/connection_adapters/sqlserver/quoting" require "active_record/connection_adapters/sqlserver/utils" -require "active_record/sqlserver_base" require "active_record/connection_adapters/sqlserver_column" require "active_record/tasks/sqlserver_database_tasks" module ActiveRecord module ConnectionAdapters + register "sqlserver", "ActiveRecord::ConnectionAdapters::SQLServerAdapter", "active_record/connection_adapters/sqlserver_adapter" + class SQLServerAdapter < AbstractAdapter include SQLServer::Version, SQLServer::Quoting, diff --git a/lib/active_record/sqlserver_base.rb b/lib/active_record/sqlserver_base.rb deleted file mode 100644 index 9dbcbc623..000000000 --- a/lib/active_record/sqlserver_base.rb +++ /dev/null @@ -1,13 +0,0 @@ -# frozen_string_literal: true - -module ActiveRecord - module ConnectionHandling - def sqlserver_adapter_class - ConnectionAdapters::SQLServerAdapter - end - - def sqlserver_connection(config) #:nodoc: - sqlserver_adapter_class.new(config) - end - end -end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 34394732b..f02e9e706 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -508,7 +508,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase describe "block writes to a database" do def setup - @conn = ActiveRecord::Base.connection + @conn = ActiveRecord::Base.lease_connection end def test_errors_when_an_insert_query_is_called_while_preventing_writes diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 315a2ac45..9445bb06b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -197,7 +197,7 @@ class BasicsTest < ActiveRecord::TestCase # Use square brackets as SQL Server escaped character coerce_tests! :test_column_names_are_escaped def test_column_names_are_escaped_coerced - conn = ActiveRecord::Base.connection + conn = ActiveRecord::Base.lease_connection assert_equal "[t]]]", conn.quote_column_name("t]") end @@ -236,7 +236,7 @@ def test_update_date_time_attributes_with_default_timezone_local ActiveRecord::Base.while_preventing_writes do assert_queries(1, ignore_none: true) do Bird.transaction do - ActiveRecord::Base.connection.materialize_transactions + ActiveRecord::Base.lease_connection.materialize_transactions end end end @@ -1542,9 +1542,9 @@ def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_rega @schema_migration.create_version(v) end - schema_info = ActiveRecord::Base.connection.dump_schema_information + schema_info = ActiveRecord::Base.lease_connection.dump_schema_information expected = <<~STR - INSERT INTO #{ActiveRecord::Base.connection.quote_table_name("schema_migrations")} (version) VALUES + INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES (N'20100301010101'), (N'20100201010101'), (N'20100101010101'); @@ -1602,7 +1602,7 @@ class SchemaDumperDefaultsCoerceTest < ActiveRecord::TestCase include SchemaDumpingHelper setup do - @connection = ActiveRecord::Base.connection + @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" @@ -2213,15 +2213,15 @@ def test_insert_all_coerced Task.cache { Task.insert({ starting: Time.now }) } end - assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do Task.cache { Task.insert_all!([{ starting: Time.now }]) } end - assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do Task.cache { Task.insert!({ starting: Time.now }) } end - assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do + assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do Task.cache { Task.insert_all!([{ starting: Time.now }]) } end @@ -2287,7 +2287,7 @@ class MarshalSerializationTest < ActiveRecord::TestCase undef_method :marshal_fixture_path def marshal_fixture_path(file_name) File.expand_path( - "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.connection.adapter_name}/#{file_name}.dump", + "support/marshal_compatibility_fixtures/#{ActiveRecord::Base.lease_connection.adapter_name}/#{file_name}.dump", ARTest::SQLServer.test_root_sqlserver ) end @@ -2379,7 +2379,7 @@ class BasePreventWritesTest < ActiveRecord::TestCase ActiveRecord::Base.while_preventing_writes do assert_queries(1, ignore_none: true) do Bird.transaction do - ActiveRecord::Base.connection.materialize_transactions + ActiveRecord::Base.lease_connection.materialize_transactions end end end @@ -2489,7 +2489,7 @@ def test_sqlcommenter_format_value_string_coercible_coerced def test_invalid_encoding_query_coerced ActiveRecord::QueryLogs.tags = [ :application ] assert_raises ActiveRecord::StatementInvalid do - ActiveRecord::Base.connection.execute "select 1 as '\xFF'" + ActiveRecord::Base.lease_connection.execute "select 1 as '\xFF'" end end end diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 2936cf93f..382ca51b2 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -7,7 +7,7 @@ class TestDisconnectedAdapter < ActiveRecord::TestCase undef_method :setup def setup - @connection = ActiveRecord::Base.connection + @connection = ActiveRecord::Base.lease_connection end teardown do diff --git a/test/cases/json_test_sqlserver.rb b/test/cases/json_test_sqlserver.rb index a863868a8..501210a12 100644 --- a/test/cases/json_test_sqlserver.rb +++ b/test/cases/json_test_sqlserver.rb @@ -2,7 +2,7 @@ require "cases/helper_sqlserver" -if ActiveRecord::Base.connection.supports_json? +if ActiveRecord::Base.lease_connection.supports_json? class JsonTestSQLServer < ActiveRecord::TestCase before do @o1 = SSTestDatatypeMigrationJson.create! json_col: { "a" => "a", "b" => "b", "c" => "c" } diff --git a/test/cases/lateral_test_sqlserver.rb b/test/cases/lateral_test_sqlserver.rb index 17aea3c9b..fb1660110 100644 --- a/test/cases/lateral_test_sqlserver.rb +++ b/test/cases/lateral_test_sqlserver.rb @@ -16,7 +16,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase eq = Arel::Nodes::Equality.new(one, one) sql = author.project(Arel.star).where(author[:name].matches("David")).outer_join(subselect.lateral.as("bar")).on(eq).to_sql - results = ActiveRecord::Base.connection.exec_query sql + results = ActiveRecord::Base.lease_connection.exec_query sql assert_equal sql, "SELECT * FROM [authors] OUTER APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'" assert_equal results.length, 1 end @@ -27,7 +27,7 @@ class LateralTestSQLServer < ActiveRecord::TestCase subselect = post.project(Arel.star).take(1).where(post[:author_id].eq(author[:id])).where(post[:id].eq(42)) sql = author.project(Arel.star).where(author[:name].matches("David")).join(subselect.lateral.as("bar")).to_sql - results = ActiveRecord::Base.connection.exec_query sql + results = ActiveRecord::Base.lease_connection.exec_query sql assert_equal sql, "SELECT * FROM [authors] CROSS APPLY (SELECT * FROM [posts] WHERE [posts].[author_id] = [authors].[id] AND [posts].[id] = 42 ORDER BY [posts].[id] ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) AS bar WHERE [authors].[name] LIKE N'David'" assert_equal results.length, 0 diff --git a/test/cases/primary_keys_test_sqlserver.rb b/test/cases/primary_keys_test_sqlserver.rb index 433c17de0..8edf3dcfe 100644 --- a/test/cases/primary_keys_test_sqlserver.rb +++ b/test/cases/primary_keys_test_sqlserver.rb @@ -12,7 +12,7 @@ class Barcode < ActiveRecord::Base end setup do - @connection = ActiveRecord::Base.connection + @connection = ActiveRecord::Base.lease_connection @connection.create_table(:barcodes, primary_key: "code", id: :uuid, force: true) end @@ -50,7 +50,7 @@ class Widget < ActiveRecord::Base end setup do - @connection = ActiveRecord::Base.connection + @connection = ActiveRecord::Base.lease_connection end teardown do diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 13ba5b5ce..f6ae6b2c3 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -5,7 +5,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase before { all_tables } - let(:all_tables) { ActiveRecord::Base.connection.tables } + let(:all_tables) { ActiveRecord::Base.lease_connection.tables } let(:schema) { @generated_schema } it "sst_datatypes" do @@ -166,7 +166,7 @@ def generate_schema_for_table(*table_names) stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.lease_connection, stream) @generated_schema = stream.string yield @generated_schema if block_given? @schema_lines = Hash.new diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 6148f0874..c4590ce81 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -159,14 +159,14 @@ def quoted_id it "returns a new id via connection newid_function" do acceptable_uuid = ActiveRecord::ConnectionAdapters::SQLServer::Type::Uuid::ACCEPTABLE_UUID - db_uuid = ActiveRecord::Base.connection.newid_function + db_uuid = ActiveRecord::Base.lease_connection.newid_function _(db_uuid).must_match(acceptable_uuid) end # with similar table definition in two schemas it "returns the correct primary columns" do - connection = ActiveRecord::Base.connection + connection = ActiveRecord::Base.lease_connection assert_equal "field_1", connection.columns("test.sst_schema_test_mulitple_schema").detect(&:is_primary?).name assert_equal "field_2", connection.columns("test2.sst_schema_test_mulitple_schema").detect(&:is_primary?).name end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index b371bd0b1..73ddf006a 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -3,7 +3,7 @@ require "cases/helper_sqlserver" class ViewTestSQLServer < ActiveRecord::TestCase - let(:connection) { ActiveRecord::Base.connection } + let(:connection) { ActiveRecord::Base.lease_connection } describe 'view with default values' do before do diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index a739f76ee..b7fe15ce2 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -8,7 +8,7 @@ module ConnectionReflection included { extend ConnectionReflection } def connection - ActiveRecord::Base.connection + ActiveRecord::Base.lease_connection end def connection_options diff --git a/test/support/test_in_memory_oltp.rb b/test/support/test_in_memory_oltp.rb index 88c918c92..2b5bb0c19 100644 --- a/test/support/test_in_memory_oltp.rb +++ b/test/support/test_in_memory_oltp.rb @@ -8,10 +8,10 @@ ARTest.connect - if ActiveRecord::Base.connection.supports_in_memory_oltp? + if ActiveRecord::Base.lease_connection.supports_in_memory_oltp? puts "Configuring In-Memory OLTP..." inmem_file = ARTest::SQLServer.test_root_sqlserver, "schema", "enable-in-memory-oltp.sql" inmem_sql = File.read File.join(inmem_file) - ActiveRecord::Base.connection.execute(inmem_sql) + ActiveRecord::Base.lease_connection.execute(inmem_sql) end end From 9e2117b37eb41250d6eba077e6ea14cd73408c36 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 14:46:56 +0100 Subject: [PATCH 1220/1412] Fix EXPLAIN tests See https://github.com/rails/rails/pull/50482 --- test/cases/showplan_test_sqlserver.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 74bf9ffa9..3bf9e1538 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -8,32 +8,32 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase describe "Unprepare previously prepared SQL" do it "from simple statement" do - plan = Car.where(id: 1).explain + plan = Car.where(id: 1).explain.inspect _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] = 1" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end it "from multiline statement" do - plan = Car.where("\n id = 1 \n").explain + plan = Car.where("\n id = 1 \n").explain.inspect _(plan).must_include "SELECT [cars].* FROM [cars] WHERE (\n id = 1 \n)" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end it "from prepared statement" do - plan = Car.where(name: ",").limit(1).explain + plan = Car.where(name: ",").limit(1).explain.inspect _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[name]" _(plan).must_include "TOP EXPRESSION", "make sure we do not showplan the sp_executesql" _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end it "from array condition using index" do - plan = Car.where(id: [1, 2]).explain + plan = Car.where(id: [1, 2]).explain.inspect _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end it "from array condition" do - plan = Car.where(name: ["honda", "zyke"]).explain + plan = Car.where(name: ["honda", "zyke"]).explain.inspect _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end @@ -42,7 +42,7 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase describe "With SHOWPLAN_TEXT option" do it "use simple table printer" do with_showplan_option("SHOWPLAN_TEXT") do - plan = Car.where(id: 1).explain + plan = Car.where(id: 1).explain.inspect _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id]" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end @@ -52,7 +52,7 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase describe "With SHOWPLAN_XML option" do it "show formatted xml" do with_showplan_option("SHOWPLAN_XML") do - plan = Car.where(id: 1).explain + plan = Car.where(id: 1).explain.inspect _(plan).must_include "ShowPlanXML" end end From dea78990fb8a2aa09bd816d2ed0c9e067f1fed4d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 14:51:06 +0100 Subject: [PATCH 1221/1412] Eliminate uses of lease_connection (#1157) --- .../sqlserver/core_ext/attribute_methods.rb | 10 ++++--- .../sqlserver/core_ext/calculations.rb | 17 +++++++----- .../sqlserver/core_ext/explain.rb | 11 +++++--- .../sqlserver/core_ext/finder_methods.rb | 10 ++++--- .../sqlserver/core_ext/preloader.rb | 26 ++++++++++--------- .../sqlserver/database_statements.rb | 2 +- test/cases/adapter_test_sqlserver.rb | 4 +-- test/cases/coerced_tests.rb | 20 +++++++------- 8 files changed, 56 insertions(+), 44 deletions(-) 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 9cf0a7959..639874a81 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 @@ -10,11 +10,13 @@ module AttributeMethods private def attributes_for_update(attribute_names) - return super unless self.class.lease_connection.adapter_name == "SQLServer" + self.class.with_connection do |connection| + return super(attribute_names) unless connection.sqlserver? - super.reject do |name| - column = self.class.columns_hash[name] - column && column.respond_to?(:is_identity?) && column.is_identity? + super(attribute_names).reject do |name| + column = self.class.columns_hash[name] + column && column.respond_to?(:is_identity?) && column.is_identity? + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index b35a719e6..54af006b7 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -9,10 +9,12 @@ module SQLServer module CoreExt module Calculations def calculate(operation, column_name) - if klass.lease_connection.sqlserver? - _calculate(operation, column_name) - else - super + klass.with_connection do |connection| + if connection.sqlserver? + _calculate(operation, column_name) + else + super + end end end @@ -54,9 +56,10 @@ def _calculate(operation, column_name) end def build_count_subquery(relation, column_name, distinct) - return super unless klass.lease_connection.adapter_name == "SQLServer" - - super(relation.unscope(:order), column_name, distinct) + klass.with_connection do |connection| + relation = relation.unscope(:order) if connection.sqlserver? + super(relation, column_name, distinct) + end 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 6f17fa249..d650d6d66 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb @@ -9,12 +9,15 @@ module Explain SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/ def exec_explain(queries, options = []) - return super unless lease_connection.adapter_name == "SQLServer" + with_connection do |connection| + return super(queries, options) unless connection.sqlserver? - unprepared_queries = queries.map do |(sql, binds)| - [unprepare_sqlserver_statement(sql, binds), binds] + unprepared_queries = queries.map do |(sql, binds)| + [unprepare_sqlserver_statement(sql, binds), binds] + end + + super(unprepared_queries, options) end - super(unprepared_queries, options) end private 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 86b866078..6016369c2 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 @@ -11,10 +11,12 @@ module FinderMethods private def construct_relation_for_exists(conditions) - if klass.lease_connection.sqlserver? - _construct_relation_for_exists(conditions) - else - super + klass.with_connection do |connection| + if connection.sqlserver? + _construct_relation_for_exists(conditions) + else + super + end end end diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb index 046edb3ea..d7bc7b9e4 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb @@ -8,23 +8,25 @@ module SQLServer module CoreExt module LoaderQuery def load_records_for_keys(keys, &block) - return super unless scope.connection.sqlserver? + scope.with_connection do |connection| + return super unless connection.sqlserver? - return [] if keys.empty? + return [] if keys.empty? - if association_key_name.is_a?(Array) - query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new } + if association_key_name.is_a?(Array) + query_constraints = Hash.new { |hsh, key| hsh[key] = Set.new } - keys.each_with_object(query_constraints) do |values_set, constraints| - association_key_name.zip(values_set).each do |key_name, value| - constraints[key_name] << value + keys.each_with_object(query_constraints) do |values_set, constraints| + association_key_name.zip(values_set).each do |key_name, value| + constraints[key_name] << value + end end - end - scope.where(query_constraints).load(&block) - else - keys.each_slice(in_clause_length).flat_map do |slice| - scope.where(association_key_name => slice).load(&block).records + scope.where(query_constraints).load(&block) + else + keys.each_slice(in_clause_length).flat_map do |slice| + scope.where(association_key_name => slice).load(&block).records + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 31f17033f..f7abe9e2d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -30,7 +30,7 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac result end - def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false) + def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) result = nil sql = transform_query(sql) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index f02e9e706..5c392513b 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -61,8 +61,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "test table existence across database schemas" do - arunit_connection = Topic.connection - arunit2_connection = College.connection + arunit_connection = Topic.lease_connection + arunit2_connection = College.lease_connection arunit_database = arunit_connection.pool.db_config.database arunit2_database = arunit2_connection.pool.db_config.database diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9445bb06b..74f2142fa 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1018,7 +1018,7 @@ def test_implicit_order_column_is_configurable_coerced assert_equal topics(:fifth), Topic.first assert_equal topics(:third), Topic.last - c = Topic.connection + c = Topic.lease_connection assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { Topic.last } @@ -1032,7 +1032,7 @@ def test_implicit_order_set_to_primary_key_coerced old_implicit_order_column = Topic.implicit_order_column Topic.implicit_order_column = "id" - c = Topic.connection + c = Topic.lease_connection assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { Topic.last } @@ -1046,7 +1046,7 @@ def test_implicit_order_for_model_without_primary_key_coerced old_implicit_order_column = NonPrimaryKey.implicit_order_column NonPrimaryKey.implicit_order_column = "created_at" - c = NonPrimaryKey.connection + c = NonPrimaryKey.lease_connection assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { NonPrimaryKey.last @@ -1067,7 +1067,7 @@ def test_member_on_unloaded_relation_with_composite_primary_key_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_implicit_order_column_prepends_query_constraints def test_implicit_order_column_prepends_query_constraints_coerced - c = ClothingItem.connection + c = ClothingItem.lease_connection ClothingItem.implicit_order_column = "description" quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) @@ -1083,7 +1083,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! %r{#last for a model with composite query constraints} test "#last for a model with composite query constraints coerced" do - c = ClothingItem.connection + c = ClothingItem.lease_connection quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) @@ -1095,7 +1095,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! %r{#first for a model with composite query constraints} test "#first for a model with composite query constraints coerced" do - c = ClothingItem.connection + c = ClothingItem.lease_connection quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) @@ -1107,7 +1107,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_implicit_order_column_reorders_query_constraints def test_implicit_order_column_reorders_query_constraints_coerced - c = ClothingItem.connection + c = ClothingItem.lease_connection ClothingItem.implicit_order_column = "color" quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) @@ -1131,7 +1131,7 @@ def test_include_on_unloaded_relation_with_composite_primary_key_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_nth_to_last_with_order_uses_limit def test_nth_to_last_with_order_uses_limit_coerced - c = Topic.connection + c = Topic.lease_connection assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do Topic.second_to_last end @@ -2338,7 +2338,7 @@ def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attr assert_equal 2, sql.size preload_sql = sql.last - c = Cpk::OrderAgreement.connection + c = Cpk::OrderAgreement.lease_connection order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id")) order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/ expectation = /SELECT.*WHERE.* #{order_id_constraint}/ @@ -2362,7 +2362,7 @@ def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_ assert_equal 2, sql.size preload_sql = sql.last - c = Cpk::Order.connection + c = Cpk::Order.lease_connection order_id = Regexp.escape(c.quote_table_name("cpk_orders.id")) order_constraint = /#{order_id} = @0.*@0 = \d+$/ expectation = /SELECT.*WHERE.* #{order_constraint}/ From f58963b7ed587902a90ea81fb4101d21e448e9c7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 14:51:27 +0100 Subject: [PATCH 1222/1412] Rename assert_queries test (#1158) --- test/cases/coerced_tests.rb | 18 +++++++++--------- .../eager_load_too_many_ids_test_sqlserver.rb | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 74f2142fa..d2070c97a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -56,7 +56,7 @@ def test_partial_index_coerced t = Topic.create!(title: "abc") t.author_name = "John" - assert_queries(1) do + assert_queries_count(1) do t.valid? end end @@ -234,7 +234,7 @@ def test_update_date_time_attributes_with_default_timezone_local coerce_tests! %r{an empty transaction does not raise if preventing writes} test "an empty transaction does not raise if preventing writes coerced" do ActiveRecord::Base.while_preventing_writes do - assert_queries(1, ignore_none: true) do + assert_queries_count(1, ignore_none: true) do Bird.transaction do ActiveRecord::Base.lease_connection.materialize_transactions end @@ -490,8 +490,8 @@ def test_limit_with_offset_is_kept_coerced coerce_tests! :test_distinct_count_all_with_custom_select_and_order def test_distinct_count_all_with_custom_select_and_order_coerced accounts = Account.distinct.select("credit_limit % 10 AS the_limit").order(Arel.sql("credit_limit % 10")) - assert_queries(1) { assert_equal 3, accounts.count(:all) } - assert_queries(1) { assert_equal 3, accounts.load.size } + assert_queries_count(1) { assert_equal 3, accounts.count(:all) } + assert_queries_count(1) { assert_equal 3, accounts.load.size } end # Leave it up to users to format selects/functions so HAVING works correctly. @@ -1324,7 +1324,7 @@ def test_create_without_primary_key_no_extra_query_coerced self.table_name = "dashboards" end klass.create! # warmup schema cache - assert_queries(2, ignore_none: true) { klass.create! } + assert_queries_count(2, ignore_none: true) { klass.create! } end end @@ -1354,7 +1354,7 @@ def test_query_cached_even_when_types_are_reset_coerced Task.initialize_find_by_cache Task.define_attribute_methods - assert_queries(1, ignore_none: true) do + assert_queries_count(1, ignore_none: true) do Task.find(1) end @@ -1454,11 +1454,11 @@ def test_relations_dont_load_all_records_in_pretty_print_coerced def test_empty_complex_chained_relations_coerced posts = Post.select("comments_count").where("id is not null").group("author_id", "id").where("legacy_comments_count > 0") - assert_queries(1) { assert_equal false, posts.empty? } + assert_queries_count(1) { assert_equal false, posts.empty? } assert_not_predicate posts, :loaded? no_posts = posts.where(title: "") - assert_queries(1) { assert_equal true, no_posts.empty? } + assert_queries_count(1) { assert_equal true, no_posts.empty? } assert_not_predicate no_posts, :loaded? end @@ -2377,7 +2377,7 @@ class BasePreventWritesTest < ActiveRecord::TestCase coerce_tests! %r{an empty transaction does not raise if preventing writes} test "an empty transaction does not raise if preventing writes coerced" do ActiveRecord::Base.while_preventing_writes do - assert_queries(1, ignore_none: true) do + assert_queries_count(1, ignore_none: true) do Bird.transaction do ActiveRecord::Base.lease_connection.materialize_transactions end diff --git a/test/cases/eager_load_too_many_ids_test_sqlserver.rb b/test/cases/eager_load_too_many_ids_test_sqlserver.rb index 96eaa6cf9..72ba24625 100644 --- a/test/cases/eager_load_too_many_ids_test_sqlserver.rb +++ b/test/cases/eager_load_too_many_ids_test_sqlserver.rb @@ -11,7 +11,7 @@ def test_batch_preloading_too_many_ids # We Monkey patch Preloader to work with batches of 10_000 records. # Expect: N Books queries + Citation query expected_query_count = (Citation.count / in_clause_length.to_f).ceil + 1 - assert_queries(expected_query_count) do + assert_queries_count(expected_query_count) do Citation.preload(:reference_of).to_a.size end end From 3f03e48cfe4b70777cdd8467201617afce0c2426 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 15:00:02 +0100 Subject: [PATCH 1223/1412] Fixed option flag in assert_queries_count calls --- test/cases/coerced_tests.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d2070c97a..841d12280 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -234,7 +234,7 @@ def test_update_date_time_attributes_with_default_timezone_local coerce_tests! %r{an empty transaction does not raise if preventing writes} test "an empty transaction does not raise if preventing writes coerced" do ActiveRecord::Base.while_preventing_writes do - assert_queries_count(1, ignore_none: true) do + assert_queries_count(1, include_schema: true) do Bird.transaction do ActiveRecord::Base.lease_connection.materialize_transactions end @@ -1324,7 +1324,7 @@ def test_create_without_primary_key_no_extra_query_coerced self.table_name = "dashboards" end klass.create! # warmup schema cache - assert_queries_count(2, ignore_none: true) { klass.create! } + assert_queries_count(2, include_schema: true) { klass.create! } end end @@ -1354,7 +1354,7 @@ def test_query_cached_even_when_types_are_reset_coerced Task.initialize_find_by_cache Task.define_attribute_methods - assert_queries_count(1, ignore_none: true) do + assert_queries_count(1, include_schema: true) do Task.find(1) end @@ -2377,7 +2377,7 @@ class BasePreventWritesTest < ActiveRecord::TestCase coerce_tests! %r{an empty transaction does not raise if preventing writes} test "an empty transaction does not raise if preventing writes coerced" do ActiveRecord::Base.while_preventing_writes do - assert_queries_count(1, ignore_none: true) do + assert_queries_count(1, include_schema: true) do Bird.transaction do ActiveRecord::Base.lease_connection.materialize_transactions end From c85e862a1841f4db4e8fd3cc989b95cdb983d67d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 15:08:16 +0100 Subject: [PATCH 1224/1412] Method `assert_sql` changed to `assert_queries_match` See https://github.com/rails/rails/pull/50373 --- test/cases/active_schema_test_sqlserver.rb | 12 ++-- test/cases/coerced_tests.rb | 68 +++++++++---------- test/cases/index_test_sqlserver.rb | 8 +-- test/cases/optimizer_hints_test_sqlserver.rb | 18 ++--- .../pessimistic_locking_test_sqlserver.rb | 2 +- test/cases/specific_schema_test_sqlserver.rb | 14 ++-- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/test/cases/active_schema_test_sqlserver.rb b/test/cases/active_schema_test_sqlserver.rb index a3ceedec1..078f36966 100644 --- a/test/cases/active_schema_test_sqlserver.rb +++ b/test/cases/active_schema_test_sqlserver.rb @@ -16,38 +16,38 @@ class ActiveSchemaTestSQLServer < ActiveRecord::TestCase end it 'default index' do - assert_sql('CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') 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_sql('CREATE UNIQUE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') 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 - assert_sql("CREATE INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo]) WHERE state = 'active'") 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 - assert_sql("IF NOT EXISTS (SELECT name FROM sysindexes WHERE name = 'index_schema_test_table_on_foo') " \ + 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_sql('CREATE CLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') 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_sql('CREATE NONCLUSTERED INDEX [index_schema_test_table_on_foo] ON [schema_test_table] ([foo])') 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 diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 841d12280..7365bc7aa 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -260,7 +260,7 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced def test_belongs_to_coerced client = Client.find(3) first_firm = companies(:first_firm) - assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do assert_equal first_firm, client.firm assert_equal first_firm.name, client.firm.name end @@ -323,7 +323,7 @@ def assert_bind_params_to_sql_coerced(prepared:) authors = Author.where(id: [1, 2, 3, nil]) assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } + assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } # prepared_statements: true # @@ -338,7 +338,7 @@ def assert_bind_params_to_sql_coerced(prepared:) authors = Author.where(id: [1, 2, 3, 9223372036854775808]) assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_sql(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } + assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } end end end @@ -947,9 +947,9 @@ class FinderTest < ActiveRecord::TestCase # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced - assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries } - assert_sql(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries } + assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } + assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries } + assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries } end # This fails only when run in the full test suite task. Just taking it out of the mix. @@ -980,7 +980,7 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_match def test_include_on_unloaded_relation_with_match_coerced - assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do assert_equal true, Customer.where(name: "David").include?(customers(:david)) end end @@ -988,7 +988,7 @@ def test_include_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_without_match def test_include_on_unloaded_relation_without_match_coerced - assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do assert_equal false, Customer.where(name: "David").include?(customers(:mary)) end end @@ -996,7 +996,7 @@ def test_include_on_unloaded_relation_without_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_match def test_member_on_unloaded_relation_with_match_coerced - assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do assert_equal true, Customer.where(name: "David").member?(customers(:david)) end end @@ -1004,7 +1004,7 @@ def test_member_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_without_match def test_member_on_unloaded_relation_without_match_coerced - assert_sql(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do assert_equal false, Customer.where(name: "David").member?(customers(:mary)) end end @@ -1019,7 +1019,7 @@ def test_implicit_order_column_is_configurable_coerced assert_equal topics(:third), Topic.last c = Topic.lease_connection - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { Topic.last } ensure @@ -1033,7 +1033,7 @@ def test_implicit_order_set_to_primary_key_coerced Topic.implicit_order_column = "id" c = Topic.lease_connection - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { Topic.last } ensure @@ -1048,7 +1048,7 @@ def test_implicit_order_for_model_without_primary_key_coerced c = NonPrimaryKey.lease_connection - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { NonPrimaryKey.last } ensure @@ -1058,7 +1058,7 @@ def test_implicit_order_for_model_without_primary_key_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key def test_member_on_unloaded_relation_with_composite_primary_key_coerced - assert_sql(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").member?(book) end @@ -1073,7 +1073,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description")) - assert_sql(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1087,7 +1087,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_sql(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do assert_kind_of ClothingItem, ClothingItem.last end end @@ -1099,7 +1099,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_sql(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do assert_kind_of ClothingItem, ClothingItem.first end end @@ -1112,7 +1112,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_sql(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1122,7 +1122,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key def test_include_on_unloaded_relation_with_composite_primary_key_coerced - assert_sql(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").include?(book) end @@ -1132,11 +1132,11 @@ def test_include_on_unloaded_relation_with_composite_primary_key_coerced coerce_tests! :test_nth_to_last_with_order_uses_limit def test_nth_to_last_with_order_uses_limit_coerced c = Topic.lease_connection - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do Topic.second_to_last end - assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do Topic.order(:updated_at).second_to_last end end @@ -1184,7 +1184,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_has_one_coerced firm = companies(:first_firm) first_account = Account.find(1) - assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do assert_equal first_account, firm.account assert_equal first_account.credit_limit, firm.account.credit_limit end @@ -1196,7 +1196,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase coerce_tests! :test_has_one_through_executes_limited_query def test_has_one_through_executes_limited_query_coerced boring_club = clubs(:boring_club) - assert_sql(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do assert_equal boring_club, @member.general_club end end @@ -1257,7 +1257,7 @@ def test_update_all_doesnt_ignore_order_coerced _(david.id).must_equal 1 _(mary.id).must_equal 2 _(david.name).wont_equal mary.name - assert_sql(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do + assert_queries_match(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do Author.where("[id] > 1").order(:id).update_all(name: "Test") end _(david.reload.name).must_equal "David" @@ -1436,7 +1436,7 @@ def test_having_with_binds_for_both_where_and_having # Find any limit via our expression. coerce_tests! %r{relations don't load all records in #inspect} def test_relations_dont_load_all_records_in_inspect_coerced - assert_sql(/NEXT @0 ROWS.*@0 = \d+/) do + assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do Post.all.inspect end end @@ -1444,7 +1444,7 @@ def test_relations_dont_load_all_records_in_inspect_coerced # Find any limit via our expression. coerce_tests! %r{relations don't load all records in #pretty_print} def test_relations_dont_load_all_records_in_pretty_print_coerced - assert_sql(/FETCH NEXT @(\d) ROWS ONLY/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do PP.pp Post.all, StringIO.new # avoid outputting. end end @@ -1516,11 +1516,11 @@ def self.search_as_method(term) } end - assert_sql(/LIKE N'20!% !_reduction!_!!'/) do + assert_queries_match(/LIKE N'20!% !_reduction!_!!'/) do searchable_post.search_as_method("20% _reduction_!").to_a end - assert_sql(/LIKE N'20!% !_reduction!_!!'/) do + assert_queries_match(/LIKE N'20!% !_reduction!_!!'/) do searchable_post.search_as_scope("20% _reduction_!").to_a end end @@ -2125,13 +2125,13 @@ def test_merge_doesnt_duplicate_same_clauses_coerced non_mary_and_bob = Author.where.not(id: [mary, bob]) author_id = Author.connection.quote_table_name("authors.id") - assert_sql(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do + assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob) end only_david = Author.where("#{author_id} IN (?)", david) - assert_sql(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do + assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do assert_equal [david], only_david.merge(only_david) end end @@ -2253,7 +2253,7 @@ def test_eager_loading_too_many_ids_coerced # Perform test citation_count = Citation.count - assert_sql(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do + assert_queries_match(/WHERE \[citations\]\.\[id\] IN \(0, 1/) do assert_equal citation_count, Citation.eager_load(:citations).offset(0).size end end @@ -2449,7 +2449,7 @@ class QueryLogsTest < ActiveRecord::TestCase coerce_tests! :test_sql_commenter_format def test_sql_commenter_format_coerced ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) - assert_sql(%r{/\*application=''active_record''\*/}) do + assert_queries_match(%r{/\*application=''active_record''\*/}) do Dashboard.first end end @@ -2464,7 +2464,7 @@ def test_sqlcommenter_format_value_coerced { tracestate: "congo=t61rcWkgMzE,rojo=00f067aa0ba902b7", custom_proc: -> { "Joe's Shack" } }, ] - assert_sql(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do + assert_queries_match(%r{custom_proc=''Joe%27s%20Shack'',tracestate=''congo%3Dt61rcWkgMzE%2Crojo%3D00f067aa0ba902b7''\*/}) do Dashboard.first end end @@ -2479,7 +2479,7 @@ def test_sqlcommenter_format_value_string_coercible_coerced { custom_proc: -> { 1234 } }, ] - assert_sql(%r{custom_proc=''1234''\*/}) do + assert_queries_match(%r{custom_proc=''1234''\*/}) do Dashboard.first end end diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index b66e3acd2..75651c0ff 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -19,22 +19,22 @@ class IndexTestSQLServer < ActiveRecord::TestCase end it "add index with order" do - assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do + assert_queries_match(/CREATE.*INDEX.*\(\[last_name\] DESC\)/i) do connection.add_index "testings", ["last_name"], order: { last_name: :desc } connection.remove_index "testings", ["last_name"] end - assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\]\)/i) do + 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.remove_index "testings", ["last_name", "first_name"] end - assert_sql(/CREATE.*INDEX.*\(\[last_name\] DESC, \[first_name\] ASC\)/i) do + 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.remove_index "testings", ["last_name", "first_name"] end end it "add index with where" do - assert_sql(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do + assert_queries_match(/CREATE.*INDEX.*\(\[last_name\]\) WHERE \[first_name\] = N'john doe'/i) do connection.add_index "testings", "last_name", where: "[first_name] = N'john doe'" connection.remove_index "testings", "last_name" end diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index ba0438c46..f40a3441b 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -7,13 +7,13 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase fixtures :companies it "apply optimizations" do - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("HASH GROUP") companies = companies.distinct.select("firm_id") assert_includes companies.explain, "| Hash Match | Aggregate |" end - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do companies = Company.optimizer_hints("ORDER GROUP") companies = companies.distinct.select("firm_id") assert_includes companies.explain, "| Stream Aggregate | Aggregate |" @@ -21,7 +21,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "apply multiple optimizations" do - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do companies = Company.optimizer_hints("HASH GROUP", "FAST 1") companies = companies.distinct.select("firm_id") assert_includes companies.explain, "| Hash Match | Flow Distinct |" @@ -29,7 +29,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "support subqueries" do - assert_sql(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do + assert_queries_match(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do companies = Company.optimizer_hints("MAXDOP 2") companies = companies.select(:id).where(firm_id: [0, 1]).limit(3) assert_equal 3, companies.count @@ -37,25 +37,25 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "sanitize values" do - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("OPTION (HASH GROUP)") companies = companies.distinct.select("firm_id") companies.to_a end - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("OPTION(HASH GROUP)") companies = companies.distinct.select("firm_id") companies.to_a end - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(TABLE HINT \(\[companies\], INDEX\(1\)\)\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(TABLE HINT \(\[companies\], INDEX\(1\)\)\)\z}) do companies = Company.optimizer_hints("OPTION(TABLE HINT ([companies], INDEX(1)))") companies = companies.distinct.select("firm_id") companies.to_a end - assert_sql(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do + assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("Option(HASH GROUP)") companies = companies.distinct.select("firm_id") companies.to_a @@ -63,7 +63,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "skip optimization after unscope" do - assert_sql("SELECT DISTINCT [companies].[firm_id] FROM [companies]") do + assert_queries_match("SELECT DISTINCT [companies].[firm_id] FROM [companies]") do companies = Company.optimizer_hints("HASH GROUP") companies = companies.distinct.select("firm_id") companies.unscope(:optimizer_hints).load diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index a8dbbcdb5..e0f592e6f 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -88,7 +88,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase it "copes with eager loading un-locked paginated" do eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ - assert_sql(eager_ids_sql, loader_sql) do + assert_queries_match(eager_ids_sql, loader_sql) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a _(people[0].first_name).must_equal "Thing_10" _(people[1].first_name).must_equal "Thing_11" diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index c4590ce81..6d80c8392 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -93,7 +93,7 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase it "use primary key for row table order in pagination sql" do sql = /ORDER BY \[sst_natural_pk_data\]\.\[legacy_id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ - assert_sql(sql) { SSTestNaturalPkData.limit(5).offset(5).load } + assert_queries_match(sql) { SSTestNaturalPkData.limit(5).offset(5).load } end # Special quoted column @@ -112,16 +112,16 @@ def quoted_id end end # Using ActiveRecord's quoted_id feature for objects. - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } # Using our custom char type data. type = ActiveRecord::Type::SQLServer::Char data = ActiveRecord::Type::SQLServer::Data - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } # Taking care of everything. - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first } - assert_sql(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first } + assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first } end it "can update and hence properly quoted non-national char/varchar columns" do From 610143268f38f7eabebcd9d1e5091063df229057 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 18 Apr 2024 20:46:11 +0100 Subject: [PATCH 1225/1412] Don't require an active connection for table and column quoting (#1159) See https://github.com/rails/rails/pull/51174 --- .../sqlserver/database_statements.rb | 8 +- .../connection_adapters/sqlserver/quoting.rb | 90 +++++++++---------- 2 files changed, 48 insertions(+), 50 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f7abe9e2d..0b6f95ec2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -153,10 +153,10 @@ def build_insert_sql(insert) # :nodoc: if returning = insert.send(:insert_all).returning returning_sql = if returning.is_a?(String) - returning - else - returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") - end + returning + else + returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + end sql << " OUTPUT #{returning_sql}" end diff --git a/lib/active_record/connection_adapters/sqlserver/quoting.rb b/lib/active_record/connection_adapters/sqlserver/quoting.rb index 54f65ca54..3fe889bd5 100644 --- a/lib/active_record/connection_adapters/sqlserver/quoting.rb +++ b/lib/active_record/connection_adapters/sqlserver/quoting.rb @@ -4,9 +4,53 @@ module ActiveRecord module ConnectionAdapters module SQLServer module Quoting + extend ActiveSupport::Concern + QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc: QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc: + module ClassMethods + def column_name_matcher + / + \A + ( + (?: + # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) + ) + (?:\s+AS\s+(?:\w+|\[\w+\]))? + ) + (?:\s*,\s*\g<1>)* + \z + /ix + end + + def column_name_with_order_matcher + / + \A + ( + (?: + # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) + ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) + ) + (?:\s+COLLATE\s+\w+)? + (?:\s+ASC|\s+DESC)? + (?:\s+NULLS\s+(?:FIRST|LAST))? + ) + (?:\s*,\s*\g<1>)* + \z + /ix + end + + def quote_column_name(name) + QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted + end + + def quote_table_name(name) + QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted + end + end + def fetch_type_metadata(sql_type, sqlserver_options = {}) cast_type = lookup_cast_type(sql_type) @@ -33,14 +77,6 @@ def quote_string_single_national(s) SQLServer::Utils.quote_string_single_national(s) end - def quote_column_name(name) - QUOTED_COLUMN_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted - end - - def quote_table_name(name) - QUOTED_TABLE_NAMES[name] ||= SQLServer::Utils.extract_identifiers(name).quoted - end - 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?('()') @@ -76,44 +112,6 @@ def quoted_date(value) end end - def column_name_matcher - COLUMN_NAME - end - - def column_name_with_order_matcher - COLUMN_NAME_WITH_ORDER - end - - COLUMN_NAME = / - \A - ( - (?: - # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) - ) - (?:\s+AS\s+(?:\w+|\[\w+\]))? - ) - (?:\s*,\s*\g<1>)* - \z - /ix - - COLUMN_NAME_WITH_ORDER = / - \A - ( - (?: - # [database_name].[database_owner].[table_name].[column_name] | function(one or no argument) - ((?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\]) | \w+\((?:|\g<2>)\)) - ) - (?:\s+COLLATE\s+\w+)? - (?:\s+ASC|\s+DESC)? - (?:\s+NULLS\s+(?:FIRST|LAST))? - ) - (?:\s*,\s*\g<1>)* - \z - /ix - - private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER - def quote(value) case value when Type::Binary::Data From bbc3bcf8fb49c9391e49dc3e982210283bd1a6cf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 20 Apr 2024 20:17:20 +0100 Subject: [PATCH 1226/1412] Allow place-holder condition to be a query parameter (#1160) --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 0b6f95ec2..3b9efbf3d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -325,7 +325,7 @@ def sp_executesql_types_and_parameters(binds) end def sp_executesql_sql_type(attr) - return "nvarchar(max)".freeze if attr.is_a?(Symbol) + return "nvarchar(max)".freeze if attr.is_a?(Symbol) || attr.is_a?(String) return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) case value = attr.value_for_database @@ -337,7 +337,7 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - return quote(attr) if attr.is_a?(Symbol) + return quote(attr) if attr.is_a?(Symbol) || attr.is_a?(String) case value = attr.value_for_database when Type::Binary::Data, From 8b030aca25fe7d13639000a6f4ffad2802b218c1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 20 Apr 2024 20:20:40 +0100 Subject: [PATCH 1227/1412] Replace connection with lease_connection --- lib/arel/visitors/sqlserver.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 22 +++++------ test/cases/coerced_tests.rb | 58 ++++++++++++++-------------- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index fe56bbef1..9016522cb 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -129,7 +129,7 @@ def visit_Arel_Table(o, collector) # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 table_name = begin - if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server? + if o.class.engine.lease_connection.respond_to?(:sqlserver?) && o.class.engine.lease_connection.database_prefix_remote_server? remote_server_table_name(o) else quote_table_name(o.name) @@ -316,7 +316,7 @@ def primary_Key_From_Table(t) def remote_server_table_name(o) ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( - "#{o.class.engine.connection.database_prefix}#{o.name}" + "#{o.class.engine.lease_connection.database_prefix}#{o.name}" ).quoted end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 5c392513b..46fed2566 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -31,7 +31,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "raises invalid statement error for bad SQL" do - assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.update("UPDATE XXX") } + assert_raise(ActiveRecord::StatementInvalid) { Topic.lease_connection.update("UPDATE XXX") } end it "is has our adapter_name" do @@ -285,23 +285,23 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "NOT ALLOW by default the deletion of a referenced parent" do - SSTestHasPk.connection.disable_referential_integrity {} + SSTestHasPk.lease_connection.disable_referential_integrity {} assert_raise(ActiveRecord::StatementInvalid) { @parent.destroy } end it "ALLOW deletion of referenced parent using #disable_referential_integrity block" do - SSTestHasPk.connection.disable_referential_integrity { @parent.destroy } + SSTestHasPk.lease_connection.disable_referential_integrity { @parent.destroy } end it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do assert_raise(ActiveRecord::StatementInvalid) do - SSTestHasPk.connection.disable_referential_integrity {} + SSTestHasPk.lease_connection.disable_referential_integrity {} @parent.destroy end end it "not disable referential integrity for the same table twice" do - tables = SSTestHasPk.connection.tables_with_referential_integrity + tables = SSTestHasPk.lease_connection.tables_with_referential_integrity assert_equal tables.size, tables.uniq.size end end @@ -396,7 +396,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "allows connection#view_information to work across databases when using qualified object names" do # College is defined in activerecord_unittest2 database. - view_info = College.connection.send(:view_information, "[activerecord_unittest].[dbo].[sst_customers_view]") + view_info = College.lease_connection.send(:view_information, "[activerecord_unittest].[dbo].[sst_customers_view]") assert_equal("sst_customers_view", view_info["TABLE_NAME"]) assert_match(/CREATE VIEW sst_customers_view/, view_info["VIEW_DEFINITION"]) end @@ -407,8 +407,8 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "allow the connection#view_table_name method to return true table_name for the view for other connections" do - assert_equal "customers", College.connection.send(:view_table_name, "[activerecord_unittest].[dbo].[sst_customers_view]") - assert_equal "topics", College.connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." + assert_equal "customers", College.lease_connection.send(:view_table_name, "[activerecord_unittest].[dbo].[sst_customers_view]") + assert_equal "topics", College.lease_connection.send(:view_table_name, "topics"), "No view here, the same table name should come back." end # With same column names @@ -434,7 +434,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "respond true to data_source_exists?" do - assert SSTestCustomersView.connection.data_source_exists?(SSTestCustomersView.table_name) + assert SSTestCustomersView.lease_connection.data_source_exists?(SSTestCustomersView.table_name) end # With aliased column names @@ -462,7 +462,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "respond true to data_source_exists?" do - assert SSTestStringDefaultsView.connection.data_source_exists?(SSTestStringDefaultsView.table_name) + assert SSTestStringDefaultsView.lease_connection.data_source_exists?(SSTestStringDefaultsView.table_name) end # That have more than 4000 chars for their defintion @@ -554,7 +554,7 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end it 'records can be inserted using SQL' do - Alien.connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')") + Alien.lease_connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')") end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 7365bc7aa..66bee9fe9 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -657,7 +657,7 @@ def migrate(x) ensure Person.reset_column_information if Person.column_names.include?("last_name") - Person.connection.remove_column("people", "last_name") + Person.lease_connection.remove_column("people", "last_name") end end end @@ -1334,7 +1334,7 @@ class QueryCacheTest < ActiveRecord::TestCase coerce_tests! :test_cache_does_not_wrap_results_in_arrays def test_cache_does_not_wrap_results_in_arrays_coerced Task.cache do - assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + assert_equal 2, Task.lease_connection.select_value("SELECT count(*) AS count_all FROM tasks") end end @@ -1493,7 +1493,7 @@ class RelationTest < ActiveRecord::TestCase coerce_tests! :test_does_not_duplicate_optimizer_hints_on_merge def test_does_not_duplicate_optimizer_hints_on_merge_coerced - escaped_table = Post.connection.quote_table_name("posts") + escaped_table = Post.lease_connection.quote_table_name("posts") expected = "SELECT #{escaped_table}.* FROM #{escaped_table} OPTION (OMGHINT)" query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query @@ -1634,19 +1634,19 @@ class TransactionTest < ActiveRecord::TestCase coerce_tests! :test_releasing_named_savepoints def test_releasing_named_savepoints_coerced Topic.transaction do - Topic.connection.materialize_transactions + Topic.lease_connection.materialize_transactions - Topic.connection.create_savepoint("another") - Topic.connection.release_savepoint("another") + Topic.lease_connection.create_savepoint("another") + Topic.lease_connection.release_savepoint("another") # We do not have a notion of releasing, so this does nothing vs raise an error. - Topic.connection.release_savepoint("another") + Topic.lease_connection.release_savepoint("another") end end # SQL Server does not have query for release_savepoint. coerce_tests! :test_nested_transactions_after_disable_lazy_transactions def test_nested_transactions_after_disable_lazy_transactions_coerced - Topic.connection.disable_lazy_transactions! + Topic.lease_connection.disable_lazy_transactions! capture_sql do # RealTransaction (begin..commit) @@ -1889,12 +1889,12 @@ class StatementCacheTest < 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_statement_cache_values_differ def test_statement_cache_values_differ_coerced - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) original_test_statement_cache_values_differ ensure Book.where(author_id: nil, name: 'my book').delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end end end @@ -2124,7 +2124,7 @@ def test_merge_doesnt_duplicate_same_clauses_coerced non_mary_and_bob = Author.where.not(id: [mary, bob]) - author_id = Author.connection.quote_table_name("authors.id") + author_id = Author.lease_connection.quote_table_name("authors.id") assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob) end @@ -2151,56 +2151,56 @@ 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.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) send(:'original_enums are distinct per class') ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! %r{creating new objects with enum scopes} test "creating new objects with enum scopes coerced" do - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) send(:'original_creating new objects with enum scopes') ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # 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 inheritable} test "enums are inheritable coerced" do - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) send(:'original_enums are inheritable') ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! %r{declare multiple enums at a time} test "declare multiple enums at a time coerced" do - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) send(:'original_declare multiple enums at a time') ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! %r{serializable\? with large number label} test "serializable? with large number label coerced" do - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) send(:'original_serializable\? with large number label') ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end end @@ -2401,45 +2401,45 @@ 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 def test_in_order_of_with_enums_values_coerced - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) original_test_in_order_of_with_enums_values ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # 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_string_column def test_in_order_of_with_string_column_coerced - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) original_test_in_order_of_with_string_column ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # 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_keys def test_in_order_of_with_enums_keys_coerced - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) original_test_in_order_of_with_enums_keys ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end # 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_nil def test_in_order_of_with_nil_coerced - Book.connection.remove_index(:books, column: [:author_id, :name]) + Book.lease_connection.remove_index(:books, column: [:author_id, :name]) original_test_in_order_of_with_nil ensure Book.where(author_id: nil, name: nil).delete_all - Book.connection.add_index(:books, [:author_id, :name], unique: true) + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end end From d7d82bf869987db7a6d2fd33f78523379540382d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Apr 2024 08:59:49 +0100 Subject: [PATCH 1228/1412] Allow integer place-holder condition to be a query parameter (#1161) --- .../sqlserver/database_statements.rb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 3b9efbf3d..38a887e00 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -325,11 +325,15 @@ def sp_executesql_types_and_parameters(binds) end def sp_executesql_sql_type(attr) - return "nvarchar(max)".freeze if attr.is_a?(Symbol) || attr.is_a?(String) - return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) + return attr.type.sqlserver_type if attr.respond_to?(:type) && attr.type.respond_to?(:sqlserver_type) - case value = attr.value_for_database - when Numeric + value = if attr.is_a?(Symbol) || attr.is_a?(String) || attr.is_a?(Numeric) + attr + else + attr.value_for_database + end + + if value.is_a?(Numeric) value > 2_147_483_647 ? "bigint".freeze : "int".freeze else "nvarchar(max)".freeze @@ -337,11 +341,10 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - return quote(attr) if attr.is_a?(Symbol) || attr.is_a?(String) + return quote(attr) if attr.is_a?(Symbol) || attr.is_a?(String) || attr.is_a?(Numeric) case value = attr.value_for_database - when Type::Binary::Data, - ActiveRecord::Type::SQLServer::Data + when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data quote(value) else quote(type_cast(value)) From 37be69cd88a8c12d7885a2cfc4fe5729ca2f4d77 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Apr 2024 09:27:25 +0100 Subject: [PATCH 1229/1412] Replace connection with lease_connection --- .../tasks/sqlserver_database_tasks.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index ab87b365f..a33ebb83d 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -10,7 +10,7 @@ module Tasks class SQLServerDatabaseTasks DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" - delegate :connection, :establish_connection, to: ActiveRecord::Base + delegate :lease_connection, :establish_connection, to: ActiveRecord::Base def self.using_database_configurations? true @@ -23,7 +23,7 @@ def initialize(configuration) def create(master_established = false) establish_master_connection unless master_established - connection.create_database configuration.database, configuration_hash.merge(collation: default_collation) + lease_connection.create_database configuration.database, configuration_hash.merge(collation: default_collation) establish_connection configuration rescue ActiveRecord::StatementInvalid => e if /database .* already exists/i === e.message @@ -35,15 +35,15 @@ def create(master_established = false) def drop establish_master_connection - connection.drop_database configuration.database + lease_connection.drop_database configuration.database end def charset - connection.charset + lease_connection.charset end def collation - connection.collation + lease_connection.collation end def purge @@ -67,9 +67,9 @@ def structure_dump(filename, extra_flags) "-P #{Shellwords.escape(configuration_hash[:password])}", "-o #{Shellwords.escape(filename)}", ] - table_args = connection.tables.map { |t| Shellwords.escape(t) } + table_args = lease_connection.tables.map { |t| Shellwords.escape(t) } command.concat(table_args) - view_args = connection.views.map { |v| Shellwords.escape(v) } + view_args = lease_connection.views.map { |v| Shellwords.escape(v) } command.concat(view_args) raise "Error dumping database" unless Kernel.system(command.join(" ")) @@ -83,7 +83,7 @@ def structure_dump(filename, extra_flags) end def structure_load(filename, extra_flags) - connection.execute File.read(filename) + lease_connection.execute File.read(filename) end private From f2f1c80b90d5f7aa7507a3e9b653dc53388d8653 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Apr 2024 19:08:08 +0100 Subject: [PATCH 1230/1412] Query cache is now a Store class instead of a Hash (#1162) --- test/support/core_ext/query_cache.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/support/core_ext/query_cache.rb b/test/support/core_ext/query_cache.rb index 72d4779d9..0679c42aa 100644 --- a/test/support/core_ext/query_cache.rb +++ b/test/support/core_ext/query_cache.rb @@ -22,8 +22,8 @@ module SqlIgnoredCache # compromising cache outside tests. def cache_sql(sql, name, binds) result = super - - @query_cache.delete_if do |cache_key, _v| + + @query_cache.instance_variable_get(:@map).delete_if do |cache_key, _v| # Query cache key generated by `sql` or `[sql, binds]`, so need to retrieve `sql` for both cases. cache_key_sql = Array(cache_key).first Regexp.union(IGNORED_SQL).match?(cache_key_sql) From 73382f37301df06775ee2464d713f4dbf7891976 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 22 Apr 2024 19:17:18 +0100 Subject: [PATCH 1231/1412] Fix tests --- test/cases/schema_dumper_test_sqlserver.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index f6ae6b2c3..97e48d557 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -166,7 +166,8 @@ def generate_schema_for_table(*table_names) stream = StringIO.new ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.lease_connection, stream) + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection_pool, stream) + @generated_schema = stream.string yield @generated_schema if block_given? @schema_lines = Hash.new From 76508601f4fd06f830e2fc5e2a899b1d340366a7 Mon Sep 17 00:00:00 2001 From: vividmuimui <1803598+vividmuimui@users.noreply.github.com> Date: Thu, 25 Apr 2024 02:53:59 +0900 Subject: [PATCH 1232/1412] Fix composite primary key with different data type with triggers (#1164) --- README.md | 4 ++++ .../sqlserver/database_statements.rb | 20 ++++++++++++++----- test/cases/trigger_test_sqlserver.rb | 10 ++++++++++ test/models/sqlserver/trigger.rb | 4 ++++ test/schema/sqlserver_specific_schema.rb | 17 ++++++++++++++++ 5 files changed, 50 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index f80b772e7..271e21495 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,10 @@ adapter.exclude_output_inserted_table_names['my_table_name'] = true # Explicitly set the data type for the temporary key table. adapter.exclude_output_inserted_table_names['my_uuid_table_name'] = 'uniqueidentifier' + + +# Explicitly set data types when data type is different for composite primary keys. +adapter.exclude_output_inserted_table_names['my_composite_pk_table_name'] = { pk_col_one: "uniqueidentifier", pk_col_two: "int" } ``` diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 38a887e00..946a84ad3 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -278,13 +278,17 @@ def sql_for_insert(sql, pk, binds, returning) exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql) if exclude_output_inserted - quoted_pk = Array(pk).map { |subkey| SQLServer::Utils.extract_identifiers(subkey).quoted } + 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 - id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? "bigint" : exclude_output_inserted <<~SQL.squish - DECLARE @ssaIdInsertTable table (#{quoted_pk.map { |subkey| "#{subkey} #{id_sql_type}"}.join(", ") }); - #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/i), " OUTPUT #{ quoted_pk.map { |subkey| "INSERTED.#{subkey}" }.join(", ") } INTO @ssaIdInsertTable"} - SELECT #{quoted_pk.map {|subkey| "CAST(#{subkey} AS #{id_sql_type}) #{subkey}"}.join(", ")} FROM @ssaIdInsertTable + 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) @@ -385,6 +389,12 @@ def exclude_output_inserted_table_name?(table_name, sql) self.class.exclude_output_inserted_table_names[table_name] end + def exclude_output_inserted_id_sql_type(pk, exclude_output_inserted) + return "bigint" if exclude_output_inserted.is_a?(TrueClass) + return exclude_output_inserted[pk.to_sym] if exclude_output_inserted.is_a?(Hash) + exclude_output_inserted + end + def query_requires_identity_insert?(sql) return false unless insert_sql?(sql) diff --git a/test/cases/trigger_test_sqlserver.rb b/test/cases/trigger_test_sqlserver.rb index e530b4840..e964e0d96 100644 --- a/test/cases/trigger_test_sqlserver.rb +++ b/test/cases/trigger_test_sqlserver.rb @@ -38,4 +38,14 @@ class SQLServerTriggerTest < ActiveRecord::TestCase _(obj.pk_col_two).must_equal 42 _(obj.pk_col_one.to_s).must_equal SSTestTriggerHistory.first.id_source 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" } + assert SSTestTriggerHistory.all.empty? + obj = SSTestTriggerCompositePkWithDefferentDataType.create! pk_col_two: 123, event_name: "test trigger" + _(obj.event_name).must_equal "test trigger" + _(obj.pk_col_one).must_be :present? + _(obj.pk_col_two).must_equal 123 + _(obj.pk_col_one.to_s).must_equal SSTestTriggerHistory.first.id_source + end end diff --git a/test/models/sqlserver/trigger.rb b/test/models/sqlserver/trigger.rb index bbdd66827..c668f1f2a 100644 --- a/test/models/sqlserver/trigger.rb +++ b/test/models/sqlserver/trigger.rb @@ -11,3 +11,7 @@ class SSTestTriggerUuid < ActiveRecord::Base class SSTestTriggerCompositePk < ActiveRecord::Base self.table_name = "sst_table_with_composite_pk_trigger" end + +class SSTestTriggerCompositePkWithDefferentDataType < ActiveRecord::Base + self.table_name = "sst_table_with_composite_pk_trigger_with_different_data_type" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 8982a2170..1bef7d0e1 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -249,6 +249,23 @@ SELECT pk_col_one AS id_source, event_name FROM INSERTED SQL + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_composite_pk_trigger_with_different_data_type') DROP TABLE sst_table_with_composite_pk_trigger_with_different_data_type" + execute <<-SQL + CREATE TABLE sst_table_with_composite_pk_trigger_with_different_data_type( + pk_col_one uniqueidentifier DEFAULT NEWID(), + pk_col_two int NOT NULL, + event_name nvarchar(255), + CONSTRAINT PK_sst_table_with_composite_pk_trigger_with_different_data_type PRIMARY KEY (pk_col_one, pk_col_two) + ) + SQL + execute <<-SQL + CREATE TRIGGER sst_table_with_composite_pk_trigger_with_different_data_type_t ON sst_table_with_composite_pk_trigger_with_different_data_type + FOR INSERT + AS + INSERT INTO sst_table_with_trigger_history (id_source, event_name) + SELECT pk_col_one AS id_source, event_name FROM INSERTED + SQL + # Another schema. create_table :sst_schema_columns, force: true do |t| From 83cd6f0d5490a096f1681c3cf39ed1ca19f637ee Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 Apr 2024 19:40:58 +0100 Subject: [PATCH 1233/1412] Fix tests --- test/cases/pessimistic_locking_test_sqlserver.rb | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index e0f592e6f..b2ef25dec 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_sql %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_sql %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_sql %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_sql %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_sql %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_sql %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 @@ -88,7 +88,8 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase it "copes with eager loading un-locked paginated" do eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ - assert_queries_match(eager_ids_sql, loader_sql) do + + assert_queries_match(/#{eager_ids_sql}|#{loader_sql}/) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a _(people[0].first_name).must_equal "Thing_10" _(people[1].first_name).must_equal "Thing_11" From 0569c315202eb1ba67915a71c5929a98c344fa53 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 Apr 2024 20:50:24 +0100 Subject: [PATCH 1234/1412] Check that 2 queries are matched --- test/cases/pessimistic_locking_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/pessimistic_locking_test_sqlserver.rb b/test/cases/pessimistic_locking_test_sqlserver.rb index b2ef25dec..ecb1553d8 100644 --- a/test/cases/pessimistic_locking_test_sqlserver.rb +++ b/test/cases/pessimistic_locking_test_sqlserver.rb @@ -89,7 +89,7 @@ class PessimisticLockingTestSQLServer < ActiveRecord::TestCase eager_ids_sql = /SELECT\s+DISTINCT \[people\].\[id\] FROM \[people\] WITH\(UPDLOCK\) LEFT OUTER JOIN \[readers\] WITH\(UPDLOCK\)\s+ON \[readers\].\[person_id\] = \[people\].\[id\]\s+ORDER BY \[people\].\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/ loader_sql = /SELECT.*FROM \[people\] WITH\(UPDLOCK\).*WHERE \[people\]\.\[id\] IN/ - assert_queries_match(/#{eager_ids_sql}|#{loader_sql}/) do + assert_queries_match(/#{eager_ids_sql}|#{loader_sql}/, count: 2) do people = Person.lock(true).limit(5).offset(10).includes(:readers).references(:readers).to_a _(people[0].first_name).must_equal "Thing_10" _(people[1].first_name).must_equal "Thing_11" From b6f48afed4511834f6a5939f7fb727bb7f29c4f7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 1 May 2024 15:28:19 +0100 Subject: [PATCH 1235/1412] Updated test cases relying on capture_sql (#1165) --- test/cases/coerced_tests.rb | 36 +++------------------------ test/cases/helper_sqlserver.rb | 2 -- test/support/sql_counter_sqlserver.rb | 14 ----------- 3 files changed, 4 insertions(+), 48 deletions(-) delete mode 100644 test/support/sql_counter_sqlserver.rb diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 66bee9fe9..1cea29b68 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -473,7 +473,7 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced # Match SQL Server limit implementation coerce_tests! :test_limit_is_kept def test_limit_is_kept_coerced - queries = capture_sql_ss { Account.limit(1).count } + queries = capture_sql { Account.limit(1).count } assert_equal 1, queries.length assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first) end @@ -481,7 +481,7 @@ def test_limit_is_kept_coerced # Match SQL Server limit implementation coerce_tests! :test_limit_with_offset_is_kept def test_limit_with_offset_is_kept_coerced - queries = capture_sql_ss { Account.limit(1).offset(1).count } + queries = capture_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first) end @@ -1337,30 +1337,6 @@ def test_cache_does_not_wrap_results_in_arrays_coerced assert_equal 2, Task.lease_connection.select_value("SELECT count(*) AS count_all FROM tasks") end end - - # Same as original test except that we expect one query to be performed to retrieve the table's primary key - # and we don't call `reload_type_map` because SQL Server adapter doesn't support it. - # When we generate the SQL for the `find` it includes ordering on the primary key. If we reset the column - # information then the primary key needs to be retrieved from the database again to generate the SQL causing the - # original test's `assert_no_queries` assertion to fail. Assert that the query was to get the primary key. - coerce_tests! :test_query_cached_even_when_types_are_reset - def test_query_cached_even_when_types_are_reset_coerced - Task.cache do - # Warm the cache - Task.find(1) - - # Clear places where type information is cached - Task.reset_column_information - Task.initialize_find_by_cache - Task.define_attribute_methods - - assert_queries_count(1, include_schema: true) do - Task.find(1) - end - - assert_includes ActiveRecord::SQLCounter.log_all.first, "TC.CONSTRAINT_TYPE = N''PRIMARY KEY''" - end - end end require "models/post" @@ -1648,7 +1624,7 @@ def test_releasing_named_savepoints_coerced def test_nested_transactions_after_disable_lazy_transactions_coerced Topic.lease_connection.disable_lazy_transactions! - capture_sql do + actual_queries = capture_sql(include_schema: true) do # RealTransaction (begin..commit) Topic.transaction(requires_new: true) do # ResetParentTransaction (no queries) @@ -1666,8 +1642,6 @@ def test_nested_transactions_after_disable_lazy_transactions_coerced end end - actual_queries = ActiveRecord::SQLCounter.log_all - expected_queries = [ /BEGIN/i, /DELETE/i, @@ -1685,7 +1659,7 @@ def test_nested_transactions_after_disable_lazy_transactions_coerced # SQL Server does not have query for release_savepoint. coerce_tests! :test_nested_transactions_skip_excess_savepoints def test_nested_transactions_skip_excess_savepoints_coerced - capture_sql do + actual_queries = capture_sql(include_schema: true) do # RealTransaction (begin..commit) Topic.transaction(requires_new: true) do # ResetParentTransaction (no queries) @@ -1703,8 +1677,6 @@ def test_nested_transactions_skip_excess_savepoints_coerced end end - actual_queries = ActiveRecord::SQLCounter.log_all - expected_queries = [ /BEGIN/i, /DELETE/i, diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4435eb95b..097330370 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -10,7 +10,6 @@ require "cases/helper" require "support/load_schema_sqlserver" require "support/coerceable_test_sqlserver" -require "support/sql_counter_sqlserver" require "support/connection_reflection" require "mocha/minitest" @@ -20,7 +19,6 @@ class TestCase < ActiveSupport::TestCase include ARTest::SQLServer::CoerceableTest, ARTest::SQLServer::ConnectionReflection, - ARTest::SQLServer::SqlCounterSqlserver, ActiveSupport::Testing::Stream let(:logger) { ActiveRecord::Base.logger } diff --git a/test/support/sql_counter_sqlserver.rb b/test/support/sql_counter_sqlserver.rb deleted file mode 100644 index 7144a4fd7..000000000 --- a/test/support/sql_counter_sqlserver.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -module ARTest - module SQLServer - module SqlCounterSqlserver - # Only return the log vs. log_all - def capture_sql_ss - ActiveRecord::SQLCounter.clear_log - yield - ActiveRecord::SQLCounter.log.dup - end - end - end -end From e7c4f6d797eb03393fd909ac30672965c10127bc Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 2 May 2024 19:02:38 +0100 Subject: [PATCH 1236/1412] Allow basic attribute types to be query parameters (#1166) --- .../sqlserver/database_statements.rb | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 946a84ad3..f219ce20f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -331,11 +331,7 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) return attr.type.sqlserver_type if attr.respond_to?(:type) && attr.type.respond_to?(:sqlserver_type) - value = if attr.is_a?(Symbol) || attr.is_a?(String) || attr.is_a?(Numeric) - attr - else - attr.value_for_database - end + value = basic_attribute_type?(attr) ? attr : attr.value_for_database if value.is_a?(Numeric) value > 2_147_483_647 ? "bigint".freeze : "int".freeze @@ -345,7 +341,7 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - return quote(attr) if attr.is_a?(Symbol) || attr.is_a?(String) || attr.is_a?(Numeric) + return quote(attr) if basic_attribute_type?(attr) case value = attr.value_for_database when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data @@ -355,6 +351,16 @@ def sp_executesql_sql_param(attr) end end + def basic_attribute_type?(type) + type.is_a?(Symbol) || + type.is_a?(String) || + type.is_a?(Numeric) || + type.is_a?(Time) || + type.is_a?(TrueClass) || + type.is_a?(FalseClass) || + type.is_a?(NilClass) + end + def sp_executesql_sql(sql, types, params, name) if name == "EXPLAIN" params.each.with_index do |param, index| From 984901991f40a4b9960cab28b788cbbe74b46435 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 May 2024 21:32:31 +0100 Subject: [PATCH 1237/1412] Fixed tests (#1167) --- .../sqlserver/core_ext/calculations.rb | 2 +- test/cases/coerced_tests.rb | 44 +++++++++++++++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 54af006b7..abc690744 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -41,7 +41,7 @@ def _calculate(operation, column_name) if operation == "count" unless distinct_value || distinct_select?(column_name || select_for_count) relation.distinct! - relation.select_values = [ klass.primary_key || table[Arel.star] ] + relation.select_values = Array(klass.primary_key || table[Arel.star]) end # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT # Start of monkey-patch diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1cea29b68..80d309de5 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -243,6 +243,25 @@ def test_update_date_time_attributes_with_default_timezone_local end end +class HasManyThroughAssociationsTest < ActiveRecord::TestCase + # SQL Server does not have query for release_savepoint + coerce_tests! :test_associate_existing + def test_associate_existing_coerced + post = posts(:thinking) + person = people(:david) + + assert_queries_count(2) do + post.people << person + end + + assert_queries_count(1) do + assert_includes post.people, person + end + + assert_includes post.reload.people.reload, person + end +end + class BelongsToAssociationsTest < ActiveRecord::TestCase # Since @client.firm is a single first/top, and we use FETCH the order clause is used. coerce_tests! :test_belongs_to_does_not_use_order_by @@ -1492,11 +1511,11 @@ def self.search_as_method(term) } end - assert_queries_match(/LIKE N'20!% !_reduction!_!!'/) do + assert_queries_match(/LIKE @0/) do searchable_post.search_as_method("20% _reduction_!").to_a end - assert_queries_match(/LIKE N'20!% !_reduction!_!!'/) do + assert_queries_match(/LIKE @0/) do searchable_post.search_as_scope("20% _reduction_!").to_a end end @@ -2103,7 +2122,7 @@ def test_merge_doesnt_duplicate_same_clauses_coerced only_david = Author.where("#{author_id} IN (?)", david) - assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(1\)\)\z/) do + assert_queries_match(/WHERE \(#{Regexp.escape(author_id)} IN \(@\d\)\)/) do assert_equal [david], only_david.merge(only_david) end end @@ -2456,6 +2475,25 @@ def test_sqlcommenter_format_value_string_coercible_coerced end end + # SQL requires double single-quotes. + coerce_tests! :test_sqlcommenter_format_allows_string_keys + def test_sqlcommenter_format_allows_string_keys_coerced + ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + + ActiveRecord::QueryLogs.tags = [ + :application, + { + "string" => "value", + 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 + Dashboard.first + end + end + # Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres. coerce_tests! :test_invalid_encoding_query def test_invalid_encoding_query_coerced From 95b0c8eccfed8f9ee55edc34614a59bf3926313b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 May 2024 20:57:20 +0100 Subject: [PATCH 1238/1412] When asserting queries performed, insert placeholder query for release save-point (#1168) --- .../sqlserver/savepoints.rb | 2 + test/cases/coerced_tests.rb | 38 +++---------------- test/cases/helper_sqlserver.rb | 4 +- test/support/query_assertions.rb | 29 ++++++++++++++ 4 files changed, 39 insertions(+), 34 deletions(-) create mode 100644 test/support/query_assertions.rb diff --git a/lib/active_record/connection_adapters/sqlserver/savepoints.rb b/lib/active_record/connection_adapters/sqlserver/savepoints.rb index e1bc69c7d..915cd07bb 100644 --- a/lib/active_record/connection_adapters/sqlserver/savepoints.rb +++ b/lib/active_record/connection_adapters/sqlserver/savepoints.rb @@ -16,6 +16,8 @@ def exec_rollback_to_savepoint(name = current_savepoint_name) internal_execute("ROLLBACK TRANSACTION #{name}", "TRANSACTION") end + # SQL Server does require save-points to be explicitly released. + # See https://stackoverflow.com/questions/3101312/sql-server-2008-no-release-savepoint-for-current-transaction def release_savepoint(_name) end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 80d309de5..aabf4ffbf 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -243,25 +243,6 @@ def test_update_date_time_attributes_with_default_timezone_local end end -class HasManyThroughAssociationsTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint - coerce_tests! :test_associate_existing - def test_associate_existing_coerced - post = posts(:thinking) - person = people(:david) - - assert_queries_count(2) do - post.people << person - end - - assert_queries_count(1) do - assert_includes post.people, person - end - - assert_includes post.reload.people.reload, person - end -end - class BelongsToAssociationsTest < ActiveRecord::TestCase # Since @client.firm is a single first/top, and we use FETCH the order clause is used. coerce_tests! :test_belongs_to_does_not_use_order_by @@ -1335,18 +1316,6 @@ def test_registering_new_handlers_for_association_coerced end end -class PrimaryKeysTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint - coerce_tests! :test_create_without_primary_key_no_extra_query - def test_create_without_primary_key_no_extra_query_coerced - klass = Class.new(ActiveRecord::Base) do - self.table_name = "dashboards" - end - klass.create! # warmup schema cache - assert_queries_count(2, include_schema: true) { klass.create! } - end -end - require "models/task" class QueryCacheTest < ActiveRecord::TestCase # SQL Server adapter not in list of supported adapters in original test. @@ -1633,8 +1602,11 @@ def test_releasing_named_savepoints_coerced Topic.lease_connection.create_savepoint("another") Topic.lease_connection.release_savepoint("another") - # We do not have a notion of releasing, so this does nothing vs raise an error. - Topic.lease_connection.release_savepoint("another") + + # We do not have a notion of releasing, so this does nothing and doesn't raise an error. + assert_nothing_raised do + Topic.lease_connection.release_savepoint("another") + end end end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 097330370..4c1315ff1 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -11,6 +11,7 @@ require "support/load_schema_sqlserver" require "support/coerceable_test_sqlserver" require "support/connection_reflection" +require "support/query_assertions" require "mocha/minitest" module ActiveRecord @@ -19,7 +20,8 @@ class TestCase < ActiveSupport::TestCase include ARTest::SQLServer::CoerceableTest, ARTest::SQLServer::ConnectionReflection, - ActiveSupport::Testing::Stream + ActiveSupport::Testing::Stream, + ARTest::SQLServer::QueryAssertions let(:logger) { ActiveRecord::Base.logger } diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb new file mode 100644 index 000000000..1fde7aad2 --- /dev/null +++ b/test/support/query_assertions.rb @@ -0,0 +1,29 @@ +module ARTest + module SQLServer + module QueryAssertions + def assert_queries_count(count = nil, include_schema: false, &block) + ActiveRecord::Base.lease_connection.materialize_transactions + + counter = ActiveRecord::Assertions::QueryAssertions::SQLCounter.new + ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do + result = _assert_nothing_raised_or_warn("assert_queries_count", &block) + queries = include_schema ? counter.log_all : counter.log + + # Start of monkey-patch + # Rails tests expect a save-point to be released at the end of the test. SQL Server does not release + # save-points and so the number of queries will be off by one. This monkey patch adds a placeholder query + # to the end of the queries array to account for the missing save-point release. + queries.append "/* release savepoint placeholder for testing */" if queries.first =~ /SAVE TRANSACTION \S+/ + # End of monkey-patch + + 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")}"}" + end + result + end + end + end + end +end From b64b4f6bb3fc70ee523fc65542bde0aa45b3bddb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 10 May 2024 14:37:18 +0100 Subject: [PATCH 1239/1412] Fix tests --- test/cases/optimizer_hints_test_sqlserver.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index f40a3441b..97752f36c 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -10,13 +10,13 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("HASH GROUP") companies = companies.distinct.select("firm_id") - assert_includes companies.explain, "| Hash Match | Aggregate |" + assert_includes companies.explain.inspect, "| Hash Match | Aggregate |" end assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(ORDER GROUP\)\z}) do companies = Company.optimizer_hints("ORDER GROUP") companies = companies.distinct.select("firm_id") - assert_includes companies.explain, "| Stream Aggregate | Aggregate |" + assert_includes companies.explain.inspect, "| Stream Aggregate | Aggregate |" end end @@ -24,7 +24,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP, FAST 1\)\z}) do companies = Company.optimizer_hints("HASH GROUP", "FAST 1") companies = companies.distinct.select("firm_id") - assert_includes companies.explain, "| Hash Match | Flow Distinct |" + assert_includes companies.explain.inspect, "| Hash Match | Flow Distinct |" end end From e7880d0ea88c0f398b195ac7adb0c78e9d43891c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 13 May 2024 11:17:22 +0100 Subject: [PATCH 1240/1412] Add row_count field to sql.active_record notification (#1169) --- .../sqlserver/database_statements.rb | 34 +++++++++---------- test/cases/coerced_tests.rb | 22 ++++++++++++ 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index f219ce20f..c2463eb92 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,8 +14,6 @@ def write_query?(sql) # :nodoc: end def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true) - result = nil - log(sql, name, async: async) do with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) @@ -24,14 +22,12 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac internal_raw_execute(sql, conn, perform_do: true) end verified! + result end end - - result end def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) - result = nil sql = transform_query(sql) check_if_write_query(sql) @@ -42,20 +38,21 @@ def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: fa sql = sp_executesql_sql(sql, types, params, name) end - log(sql, name, binds, async: async) do + log(sql, name, binds, async: async) do |notification_payload| with_raw_connection do |conn| - if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, conn) do - result = internal_exec_sql_query(sql, conn) - end - else - result = internal_exec_sql_query(sql, conn) - end + result = if id_insert_table_name = query_requires_identity_insert?(sql) + with_identity_insert_enabled(id_insert_table_name, conn) do + internal_exec_sql_query(sql, conn) + end + else + internal_exec_sql_query(sql, conn) + end + verified! + notification_payload[:row_count] = result.count + result end end - - result end def internal_exec_sql_query(sql, conn) @@ -174,7 +171,7 @@ def execute_procedure(proc_name, *variables) end.join(", ") sql = "EXEC #{proc_name} #{vars}".strip - log(sql, "Execute Procedure") do + log(sql, "Execute Procedure") do |notification_payload| with_raw_connection do |conn| result = internal_raw_execute(sql, conn) verified! @@ -185,10 +182,11 @@ def execute_procedure(proc_name, *variables) yield(r) if block_given? end - result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row } + result = result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row } + notification_payload[:row_count] = result.count + result end end - end def with_identity_insert_enabled(table_name, conn) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index aabf4ffbf..04c258cd1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -351,6 +351,28 @@ def test_payload_name_on_load_coerced Book.send(:load_schema!) original_test_payload_name_on_load end + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! :test_payload_row_count_on_select_all + def test_payload_row_count_on_select_all_coerced + connection.remove_index(:books, column: [:author_id, :name]) + + original_test_payload_row_count_on_select_all + ensure + Book.where(author_id: nil, name: 'row count book 1').delete_all + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) + end + + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! :test_payload_row_count_on_raw_sql + def test_payload_row_count_on_raw_sql_coerced + connection.remove_index(:books, column: [:author_id, :name]) + + original_test_payload_row_count_on_raw_sql + ensure + Book.where(author_id: nil, name: 'row count book 3').delete_all + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) + end end end From b6257ad929af4773f41019bef84728ea362ae5c0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 13 May 2024 11:17:34 +0100 Subject: [PATCH 1241/1412] Fix test bad connection test (#1170) --- test/cases/adapter_test_sqlserver.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 46fed2566..b963d09d5 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -102,8 +102,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_raise ActiveRecord::NoDatabaseError do db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") configuration = db_config.configuration_hash.merge(database: "nonexistent_activerecord_unittest") - - connection = ActiveRecord::Base.sqlserver_connection configuration + connection = ActiveRecord::ConnectionAdapters::SQLServerAdapter.new(configuration) connection.exec_query("SELECT 1") end end From ae733fd403c7ad1e7547df0a98b34f213ece5879 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 May 2024 20:14:04 +0100 Subject: [PATCH 1242/1412] Return row-count for pluck (#1171) --- .../sqlserver/database_statements.rb | 3 ++- test/cases/coerced_tests.rb | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index c2463eb92..e1259792c 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,7 +14,7 @@ def write_query?(sql) # :nodoc: end def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true) - log(sql, name, async: async) do + log(sql, name, async: async) do |notification_payload| with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| result = if id_insert_table_name = query_requires_identity_insert?(sql) with_identity_insert_enabled(id_insert_table_name, conn) { internal_raw_execute(sql, conn, perform_do: true) } @@ -22,6 +22,7 @@ def raw_execute(sql, name, async: false, allow_retry: false, materialize_transac internal_raw_execute(sql, conn, perform_do: true) end verified! + notification_payload[:row_count] = result result end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 04c258cd1..9b850d295 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -363,6 +363,17 @@ def test_payload_row_count_on_select_all_coerced Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. + coerce_tests! :test_payload_row_count_on_pluck + def test_payload_row_count_on_pluck_coerced + connection.remove_index(:books, column: [:author_id, :name]) + + original_test_payload_row_count_on_pluck + ensure + Book.where(author_id: nil, name: 'row count book 2').delete_all + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) + end + # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! :test_payload_row_count_on_raw_sql def test_payload_row_count_on_raw_sql_coerced From 21cf4717465325ee0d556b97d55fac4f99af4bf8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 May 2024 20:32:56 +0100 Subject: [PATCH 1243/1412] Support any Rails branch in CI (#1172) --- Dockerfile.ci | 2 +- Gemfile | 4 ++-- docker-compose.ci.yml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 7f75f1a9e..f9437e97d 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_MAIN=1 bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/Gemfile b/Gemfile index 0d0761ba0..1f0913b9a 100644 --- a/Gemfile +++ b/Gemfile @@ -16,8 +16,8 @@ gem "msgpack", ">= 1.7.0" if ENV["RAILS_SOURCE"] gemspec path: ENV["RAILS_SOURCE"] -elsif ENV["RAILS_MAIN"] - gem "rails", github: "rails/rails", branch: 'main' +elsif ENV["RAILS_BRANCH"] + gem "rails", github: "rails/rails", branch: ENV["RAILS_BRANCH"] else # Need to get rails source because the gem doesn't include tests version = ENV["RAILS_VERSION"] || begin diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 82c48d48a..957b5ec9b 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,7 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_MAIN=1 + - RAILS_BRANCH=7-2-stable build: context: . dockerfile: Dockerfile.ci From e8eb9a7032f92ee151cd6bae3af0743da594958e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 14 May 2024 20:35:53 +0100 Subject: [PATCH 1244/1412] Fix enum deprecation message --- test/cases/enum_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/enum_test_sqlserver.rb b/test/cases/enum_test_sqlserver.rb index 210a4aa7f..713056f85 100644 --- a/test/cases/enum_test_sqlserver.rb +++ b/test/cases/enum_test_sqlserver.rb @@ -13,7 +13,7 @@ class EnumTestSQLServer < ActiveRecord::TestCase Class.new(ActiveRecord::Base) do self.table_name = 'sst_datatypes' - enum col_name => { alpha: "A", beta: "B" } + enum col_name, { alpha: "A", beta: "B" } end end From 4669b1a52619de64da482dcc56b88c6194b7389f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 May 2024 11:55:48 +0100 Subject: [PATCH 1245/1412] Removed tests that no longer need to be coerced (#1173) --- test/cases/coerced_tests.rb | 26 -------------------------- 1 file changed, 26 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9b850d295..98a39f41c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -229,18 +229,6 @@ def test_update_date_time_attributes_with_default_timezone_local end end end - - # SQL Server does not have query for release_savepoint - coerce_tests! %r{an empty transaction does not raise if preventing writes} - test "an empty transaction does not raise if preventing writes coerced" do - ActiveRecord::Base.while_preventing_writes do - assert_queries_count(1, include_schema: true) do - Bird.transaction do - ActiveRecord::Base.lease_connection.materialize_transactions - end - end - end - end end class BelongsToAssociationsTest < ActiveRecord::TestCase @@ -2368,20 +2356,6 @@ def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_ end end -class BasePreventWritesTest < ActiveRecord::TestCase - # SQL Server does not have query for release_savepoint - coerce_tests! %r{an empty transaction does not raise if preventing writes} - test "an empty transaction does not raise if preventing writes coerced" do - ActiveRecord::Base.while_preventing_writes do - assert_queries_count(1, include_schema: true) do - Bird.transaction do - ActiveRecord::Base.lease_connection.materialize_transactions - end - end - end - end -end - 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/ From 26b81cf2657732853933aab080b511dd1158eb95 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 15 May 2024 11:57:00 +0100 Subject: [PATCH 1246/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 271e21495..c10a0dcf4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| Unreleased | Edge | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| Unreleased | `7.2.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.1.3` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | From d48c790e3a4cdadd9ca7aeb7b4cc1e48393c9754 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 May 2024 10:36:56 +0100 Subject: [PATCH 1247/1412] Fix for text showplan (#1174) --- .../connection_adapters/sqlserver/database_statements.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index e1259792c..0c7243ad1 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -432,7 +432,11 @@ def handle_to_names_and_values(handle, options = {}) qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash end results = handle.each(query_options) - columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields + + columns = handle.fields + # If query returns multiple result sets, only return the columns of the last one. + columns = columns.last if columns.any? && columns.all? { |e| e.is_a?(Array) } + columns = columns.map(&:downcase) if lowercase_schema_reflection options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results end From 86851d336b3486bbd4f7b49c5dcabb78523324f0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 May 2024 10:54:54 +0100 Subject: [PATCH 1248/1412] Ensure pre-7.1 migrations use legacy index names when using `rename_table` (#1175) --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a0b43d1b3..1b968420d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -132,7 +132,7 @@ def rename_table(table_name, new_name, **options) schema_cache.clear_data_source_cache!(table_name.to_s) schema_cache.clear_data_source_cache!(new_name.to_s) execute "EXEC sp_rename '#{table_name}', '#{new_name}'" - rename_table_indexes(table_name, new_name) + rename_table_indexes(table_name, new_name, **options) end def remove_column(table_name, column_name, type = nil, **options) From 636c38fc78055642e0179f3abcc2a852098a0973 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 May 2024 16:39:23 +0100 Subject: [PATCH 1249/1412] Updated error message in test to include sqlserver (#1176) --- test/cases/coerced_tests.rb | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 98a39f41c..d05fd890d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2635,3 +2635,21 @@ def test_remove_check_constraint_coerced end end end + +module ActiveRecord + module ConnectionAdapters + class PoolConfig + class ResolverTest < ActiveRecord::TestCase + # SQL Server was not included in the list of available adapters in the error message. + coerce_tests! :test_url_invalid_adapter + def test_url_invalid_adapter_coerced + error = assert_raises(AdapterNotFound) do + Base.connection_handler.establish_connection "ridiculous://foo?encoding=utf8" + end + + assert_match "Database configuration specifies nonexistent 'ridiculous' adapter. Available adapters are: abstract, fake, mysql2, postgresql, sqlite3, sqlserver, trilogy. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile if it's not in the list of available adapters.", error.message + end + end + end + end +end From ac5e91bd48f167355b6671152b9ba89689653696 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 May 2024 17:12:48 +0100 Subject: [PATCH 1250/1412] Update minitest (#1177) --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 1f0913b9a..eacb820b8 100644 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,7 @@ gem "pg", ">= 0.18.0" gem "sqlite3", "~> 1.4" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" -gem "minitest", ">= 5.15.0", "< 5.16" +gem "minitest", ">= 5.15.0", "< 5.22.0" gem "msgpack", ">= 1.7.0" if ENV["RAILS_SOURCE"] From b19c728742cdb6dc11a2f4b830f33a97738b0532 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 16 May 2024 18:22:56 +0100 Subject: [PATCH 1251/1412] Fix test --- test/cases/migration_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/migration_test_sqlserver.rb b/test/cases/migration_test_sqlserver.rb index 74b14eb18..0ffe20861 100644 --- a/test/cases/migration_test_sqlserver.rb +++ b/test/cases/migration_test_sqlserver.rb @@ -20,7 +20,7 @@ class MigrationTestSQLServer < ActiveRecord::TestCase it "not create a tables if error in migrations" do begin migrations_dir = File.join ARTest::SQLServer.migrations_root, "transaction_table" - quietly { ActiveRecord::MigrationContext.new(migrations_dir, ActiveRecord::SchemaMigration).up } + quietly { ActiveRecord::MigrationContext.new(migrations_dir).up } rescue Exception => e assert_match %r|this and all later migrations canceled|, e.message end From af26fc9a8df9eadf4adf602b3600bfd34127a404 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 17 May 2024 11:50:21 +0100 Subject: [PATCH 1252/1412] Support encrypting binary columns (#1178) --- .../connection_adapters/sqlserver/database_statements.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 0c7243ad1..741acb21e 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -328,7 +328,13 @@ def sp_executesql_types_and_parameters(binds) end def sp_executesql_sql_type(attr) - return attr.type.sqlserver_type if attr.respond_to?(:type) && attr.type.respond_to?(:sqlserver_type) + if attr.respond_to?(:type) + return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) + + if attr.type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) && attr.type.instance_variable_get(:@cast_type).respond_to?(:sqlserver_type) + return attr.type.instance_variable_get(:@cast_type).sqlserver_type + end + end value = basic_attribute_type?(attr) ? attr : attr.value_for_database From a3475a2410916e1e3f2dcaaab57e2622d7f78571 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 17 May 2024 19:14:54 +0100 Subject: [PATCH 1253/1412] Add savepoint placeholder for each savepoint created (#1179) --- test/support/query_assertions.rb | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb index 1fde7aad2..accb11f09 100644 --- a/test/support/query_assertions.rb +++ b/test/support/query_assertions.rb @@ -10,10 +10,7 @@ def assert_queries_count(count = nil, include_schema: false, &block) queries = include_schema ? counter.log_all : counter.log # Start of monkey-patch - # Rails tests expect a save-point to be released at the end of the test. SQL Server does not release - # save-points and so the number of queries will be off by one. This monkey patch adds a placeholder query - # to the end of the queries array to account for the missing save-point release. - queries.append "/* release savepoint placeholder for testing */" if queries.first =~ /SAVE TRANSACTION \S+/ + queries = include_release_savepoint_placeholder_queries(queries) # End of monkey-patch if count @@ -24,6 +21,29 @@ def assert_queries_count(count = nil, include_schema: false, &block) result end end + + private + + # Rails tests expect a save-point to be created and released. SQL Server does not release + # save-points and so the number of queries will be off. This monkey patch adds a placeholder queries + # to replace the missing save-point releases. + def include_release_savepoint_placeholder_queries(queries) + grouped_queries = [[]] + + queries.each do |query| + if query =~ /SAVE TRANSACTION \S+/ + grouped_queries << [query] + else + grouped_queries.last << query + end + end + + grouped_queries.each do |group| + group.append "/* release savepoint placeholder for testing */" if group.first =~ /SAVE TRANSACTION \S+/ + end + + grouped_queries.flatten + end end end end From b211d4feaca74f7cc99daaf14efedf221a42f823 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 19 May 2024 18:04:48 +0100 Subject: [PATCH 1254/1412] Fix for the Rails test schema including decimal greater than 38 precision (#1181) --- test/cases/helper_sqlserver.rb | 1 + test/support/table_definition_sqlserver.rb | 24 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/support/table_definition_sqlserver.rb diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4c1315ff1..1bd78c82f 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -7,6 +7,7 @@ require "support/core_ext/query_cache" require "support/minitest_sqlserver" require "support/test_in_memory_oltp" +require "support/table_definition_sqlserver" require "cases/helper" require "support/load_schema_sqlserver" require "support/coerceable_test_sqlserver" diff --git a/test/support/table_definition_sqlserver.rb b/test/support/table_definition_sqlserver.rb new file mode 100644 index 000000000..7be97923a --- /dev/null +++ b/test/support/table_definition_sqlserver.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module ActiveRecord + module ConnectionAdapters + module SQLServer + class TableDefinition < ::ActiveRecord::ConnectionAdapters::TableDefinition + # SQL Server supports precision of 38 for decimal columns. In Rails the test schema includes a column + # with a precision of 55. This is a problem for SQL Server 2008. This method will override the default + # decimal method to limit the precision to 38 for the :atoms_in_universe column. + # See https://github.com/rails/rails/pull/51826/files#diff-2a57b61bbf9ee2c23938fc571d403799f68b4b530d65e2cde219a429bbf10af5L876 + def decimal(*names, **options) + throw "This 'decimal' method should only be used in a test environment." unless defined?(ActiveSupport::TestCase) + + names.each do |name| + options_for_name = options.dup + options_for_name[:precision] = 38 if name == :atoms_in_universe && options_for_name[:precision].to_i == 55 + + column(name, :decimal, **options_for_name) + end + end + end + end + end +end From a86d2ef286a0622d386dc9699e1ac5642f46eaa3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 19 May 2024 18:05:31 +0100 Subject: [PATCH 1255/1412] Coerce tests to check for subclasses rather than actual classes (#1180) --- test/cases/coerced_tests.rb | 31 +++++++++++++++++++++++++++++++ test/cases/helper_sqlserver.rb | 9 +++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d05fd890d..a69a52308 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2653,3 +2653,34 @@ def test_url_invalid_adapter_coerced end end end + +module ActiveRecord + class TableMetadataTest < ActiveSupport::TestCase + # Adapter returns an object that is subclass of what is expected in the original test. + coerce_tests! %r{#associated_table creates the right type caster for joined table with different association name} + def associated_table_creates_the_right_type_caster_for_joined_table_with_different_association_name_coerced + base_table_metadata = TableMetadata.new(AuditRequiredDeveloper, Arel::Table.new("developers")) + + associated_table_metadata = base_table_metadata.associated_table("audit_logs") + + assert associated_table_metadata.arel_table.type_for_attribute(:message).is_a?(ActiveRecord::Type::String) + end + end +end + +module ActiveRecord + module TypeCaster + class ConnectionTest < ActiveSupport::TestCase + # Adapter returns an object that is subclass of what is expected in the original test. + coerce_tests! %r{#type_for_attribute is not aware of custom types} + def type_for_attribute_is_not_aware_of_custom_types_coerced + type_caster = Connection.new(AttributedDeveloper, "developers") + + type = type_caster.type_for_attribute(:name) + + assert_not_equal DeveloperName, type.class + assert type.is_a?(ActiveRecord::Type::String) + end + end + end +end diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 1bd78c82f..c42e9c7c0 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -15,12 +15,17 @@ require "support/query_assertions" require "mocha/minitest" +module ActiveSupport + class TestCase < ::Minitest::Test + include ARTest::SQLServer::CoerceableTest + end +end + module ActiveRecord class TestCase < ActiveSupport::TestCase SQLServer = ActiveRecord::ConnectionAdapters::SQLServer - include ARTest::SQLServer::CoerceableTest, - ARTest::SQLServer::ConnectionReflection, + include ARTest::SQLServer::ConnectionReflection, ActiveSupport::Testing::Stream, ARTest::SQLServer::QueryAssertions From fb541578e95c6d528c946d1c8e44ca0970474438 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 19 May 2024 18:06:04 +0100 Subject: [PATCH 1256/1412] Unpin minitest version to 5.21 (#1182) --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index eacb820b8..100d9b03d 100644 --- a/Gemfile +++ b/Gemfile @@ -11,7 +11,7 @@ gem "pg", ">= 0.18.0" gem "sqlite3", "~> 1.4" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" -gem "minitest", ">= 5.15.0", "< 5.22.0" +gem "minitest", ">= 5.15.0" gem "msgpack", ">= 1.7.0" if ENV["RAILS_SOURCE"] From f3b83d0fc894a3499b9d9af2bf88314f27c450eb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 May 2024 12:15:53 +0100 Subject: [PATCH 1257/1412] Coerced some explain tests (#1183) --- test/cases/coerced_tests.rb | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a69a52308..2f31184ba 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2684,3 +2684,30 @@ def type_for_attribute_is_not_aware_of_custom_types_coerced end 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 + def test_relation_explain_with_first_coerced + expected_query = capture_sql { + Car.all.first + }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] + message = Car.all.explain.first + assert_match(/^EXPLAIN/, message) + assert_match(expected_query, message) + end + + # Expected query slightly different from because of 'sp_executesql' and query parameters. + coerce_tests! :test_relation_explain_with_last + 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) + assert_match(expected_query, message) + end +end From f38562bbf85dfcff5c6c7cb32ecb7a80251efbe7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 20 May 2024 13:15:07 +0100 Subject: [PATCH 1258/1412] Coerce query assertion test (#1184) --- test/cases/coerced_tests.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2f31184ba..01e76100f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2711,3 +2711,26 @@ def test_relation_explain_with_last_coerced assert_match(expected_query, message) end end + +module ActiveRecord + module Assertions + class QueryAssertionsTest < ActiveSupport::TestCase + # Query slightly different in original test. + coerce_tests! :test_assert_queries_match + def test_assert_queries_match_coerced + assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 1) { Post.first } + assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i) { Post.first } + + error = assert_raises(Minitest::Assertion) { + assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 2) { Post.first } + } + assert_match(/1 instead of 2 queries/, error.message) + + error = assert_raises(Minitest::Assertion) { + assert_queries_match(/ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, count: 0) { Post.first } + } + assert_match(/1 instead of 0 queries/, error.message) + end + end + end +end From 204bc98921813fe14457d6eeb6ff4f27921f9af7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 21 May 2024 10:19:02 +0100 Subject: [PATCH 1259/1412] Updated test to match Rails original (#1185) --- test/cases/coerced_tests.rb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 01e76100f..c4359096d 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2197,18 +2197,6 @@ def test_insert_all_coerced Task.cache { Task.insert({ starting: Time.now }) } end - assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do - Task.cache { Task.insert_all!([{ starting: Time.now }]) } - end - - assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do - Task.cache { Task.insert!({ starting: Time.now }) } - end - - assert_called(ActiveRecord::Base.lease_connection, :clear_query_cache, times: 2) do - Task.cache { Task.insert_all!([{ starting: Time.now }]) } - end - assert_raises(ArgumentError, /does not support upsert/) do Task.cache { Task.upsert({ starting: Time.now }) } end @@ -2216,6 +2204,16 @@ def test_insert_all_coerced assert_raises(ArgumentError, /does not support upsert/) do Task.cache { Task.upsert_all([{ starting: Time.now }]) } end + + Task.cache do + assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do + Task.insert_all!([ starting: Time.now ]) + end + + assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do + Task.insert!({ starting: Time.now }) + end + end end end From 634bc2a483994a6143c84698ff25c1591444a702 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 23 May 2024 19:34:31 +0100 Subject: [PATCH 1260/1412] Eliminate missed lease_connection calls (#1187) --- .../tasks/sqlserver_database_tasks.rb | 70 ++++++++++--------- lib/arel/visitors/sqlserver.rb | 22 +++--- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index a33ebb83d..bbae97e7c 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -10,7 +10,7 @@ module Tasks class SQLServerDatabaseTasks DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" - delegate :lease_connection, :establish_connection, to: ActiveRecord::Base + delegate :with_connection, :establish_connection, to: ActiveRecord::Base def self.using_database_configurations? true @@ -23,8 +23,10 @@ def initialize(configuration) def create(master_established = false) establish_master_connection unless master_established - lease_connection.create_database configuration.database, configuration_hash.merge(collation: default_collation) - establish_connection configuration + with_connection do |connection| + connection.create_database(configuration.database, configuration_hash.merge(collation: default_collation)) + end + establish_connection(configuration) rescue ActiveRecord::StatementInvalid => e if /database .* already exists/i === e.message raise DatabaseAlreadyExists @@ -35,15 +37,15 @@ def create(master_established = false) def drop establish_master_connection - lease_connection.drop_database configuration.database + with_connection { |connection| connection.drop_database(configuration.database) } end def charset - lease_connection.charset + with_connection { |connection| connection.charset } end def collation - lease_connection.collation + with_connection { |connection| connection.collation } end def purge @@ -56,34 +58,38 @@ def clear_active_connections! ActiveRecord::Base.connection_handler.clear_active_connections!(:all) end - def structure_dump(filename, extra_flags) - server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}" - server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port] - command = [ - "defncopy-ttds", - server_arg, - "-D #{Shellwords.escape(configuration_hash[:database])}", - "-U #{Shellwords.escape(configuration_hash[:username])}", - "-P #{Shellwords.escape(configuration_hash[:password])}", - "-o #{Shellwords.escape(filename)}", - ] - table_args = lease_connection.tables.map { |t| Shellwords.escape(t) } - command.concat(table_args) - view_args = lease_connection.views.map { |v| Shellwords.escape(v) } - command.concat(view_args) - raise "Error dumping database" unless Kernel.system(command.join(" ")) - - 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!(/text\(\d+\)/, "text") # Fix text(16) column defs - File.open(filename, "w") { |file| file.puts dump } + def structure_dump(filename, _extra_flags) + with_connection do |connection| + server_arg = "-S #{Shellwords.escape(configuration_hash[:host])}" + server_arg += ":#{Shellwords.escape(configuration_hash[:port])}" if configuration_hash[:port] + command = [ + "defncopy-ttds", + server_arg, + "-D #{Shellwords.escape(configuration_hash[:database])}", + "-U #{Shellwords.escape(configuration_hash[:username])}", + "-P #{Shellwords.escape(configuration_hash[:password])}", + "-o #{Shellwords.escape(filename)}", + ] + table_args = connection.tables.map { |t| Shellwords.escape(t) } + command.concat(table_args) + view_args = connection.views.map { |v| Shellwords.escape(v) } + command.concat(view_args) + raise "Error dumping database" unless Kernel.system(command.join(" ")) + + 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!(/text\(\d+\)/, "text") # Fix text(16) column defs + File.open(filename, "w") { |file| file.puts dump } + end end - def structure_load(filename, extra_flags) - lease_connection.execute File.read(filename) + def structure_load(filename, _extra_flags) + with_connection do |connection| + connection.execute File.read(filename) + end end private diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 9016522cb..8392eb102 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -129,10 +129,12 @@ def visit_Arel_Table(o, collector) # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450 table_name = begin - if o.class.engine.lease_connection.respond_to?(:sqlserver?) && o.class.engine.lease_connection.database_prefix_remote_server? - remote_server_table_name(o) - else - quote_table_name(o.name) + o.class.engine.with_connection do |connection| + if connection.respond_to?(:sqlserver?) && connection.database_prefix_remote_server? + remote_server_table_name(o) + else + quote_table_name(o.name) + end end rescue Exception quote_table_name(o.name) @@ -315,14 +317,16 @@ def primary_Key_From_Table(t) end def remote_server_table_name(o) - ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( - "#{o.class.engine.lease_connection.database_prefix}#{o.name}" - ).quoted + o.class.engine.with_connection do |connection| + ActiveRecord::ConnectionAdapters::SQLServer::Utils.extract_identifiers( + "#{connection.database_prefix}#{o.name}" + ).quoted + end end - # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer + # Need to remove ordering from sub-queries unless TOP/OFFSET also used. Otherwise, SQLServer # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables, - # subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified." + # sub-queries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified." def remove_invalid_ordering_from_select_statement(node) return unless Arel::Nodes::SelectStatement === node From f73591fe617ae152fad8c2c8798c34809cb3c1da Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 24 May 2024 20:53:37 +0100 Subject: [PATCH 1261/1412] Syntax for "WITH RECURSIVE" is slightly different in SQL Server (#1188) --- lib/arel/visitors/sqlserver.rb | 5 +++++ test/cases/coerced_tests.rb | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 8392eb102..3dfe35bbe 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -203,6 +203,11 @@ def collect_optimizer_hints(o, collector) collector end + def visit_Arel_Nodes_WithRecursive(o, collector) + collector << "WITH " + collect_ctes(o.children, collector) + end + # SQLServer ToSql/Visitor (Additions) def visit_Arel_Nodes_SelectStatement_SQLServer_Lock(collector, options = {}) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c4359096d..4e18c5f92 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2732,3 +2732,26 @@ def test_assert_queries_match_coerced end end end + +module ActiveRecord + class WithTest < ActiveRecord::TestCase + # SQL contains just 'WITH' instead of 'WITH RECURSIVE' as expected by the original test. + coerce_tests! :test_with_recursive + def test_with_recursive_coerced + top_companies = Company.where(firm_id: nil).to_a + child_companies = Company.where(firm_id: top_companies).to_a + top_companies_and_children = (top_companies.map(&:id) + child_companies.map(&:id)).sort + + 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"), + ] + ).from("top_companies_and_children AS companies") + + assert_equal top_companies_and_children, relation.order(:id).pluck(:id) + assert_match "WITH ", relation.to_sql + end + end +end + From c3b65adf3a9348df18576822dacd13ea7e941d06 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 5 Jun 2024 18:36:38 +0100 Subject: [PATCH 1262/1412] Update CI to latest Ruby versions (#1191) --- .github/workflows/ci.yml | 6 +++--- Gemfile | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5f409874a..940d35462 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,9 +14,9 @@ jobs: fail-fast: false matrix: ruby: - - 3.1.4 - - 3.2.2 - - 3.3.0 + - 3.1.6 + - 3.2.4 + - 3.3.2 steps: - name: Checkout code diff --git a/Gemfile b/Gemfile index 100d9b03d..0f435985e 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ gemspec gem "bcrypt" gem "pg", ">= 0.18.0" -gem "sqlite3", "~> 1.4" +gem "sqlite3", ">= 1.6.6" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" gem "minitest", ">= 5.15.0" From c3e51fc4b34c32230d7e31efead0cce10e391868 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 5 Jun 2024 19:30:08 +0100 Subject: [PATCH 1263/1412] Updates for Rails 7.2.0.beta2 release (#1190) --- Dockerfile.ci | 2 +- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- docker-compose.ci.yml | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index f9437e97d..0ded95afd 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/VERSION b/VERSION index 2031da2af..de4dd5c4e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.0.alpha +7.2.0.beta2 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 3d603b673..3e71c7422 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.2.0.alpha" + spec.add_dependency "activerecord", "~> 7.2.0.beta2" spec.add_dependency "tiny_tds" end diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 957b5ec9b..cbc842619 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,7 +5,6 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_BRANCH=7-2-stable build: context: . dockerfile: Dockerfile.ci From 20d1c84497cfd30e3595d6e31636ae7e87704d03 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 5 Jun 2024 19:40:08 +0100 Subject: [PATCH 1264/1412] Update Freetds to match CI (#1192) --- .devcontainer/Dockerfile | 6 +++--- test/cases/rake_test_sqlserver.rb | 12 ++++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 8c67b380d..116b439f1 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -6,9 +6,9 @@ FROM mcr.microsoft.com/devcontainers/ruby:${VARIANT} # TinyTDS RUN apt-get -y install libc6-dev \ - && wget http://www.freetds.org/files/stable/freetds-1.1.32.tar.gz \ - && tar -xzf freetds-1.1.32.tar.gz \ - && cd freetds-1.1.32 \ + && wget http://www.freetds.org/files/stable/freetds-1.4.14.tar.gz \ + && tar -xzf freetds-1.4.14.tar.gz \ + && cd freetds-1.4.14 \ && ./configure --prefix=/usr/local --with-tdsver=7.3 \ && make \ && make install diff --git a/test/cases/rake_test_sqlserver.rb b/test/cases/rake_test_sqlserver.rb index 145bc7119..ea1074d76 100644 --- a/test/cases/rake_test_sqlserver.rb +++ b/test/cases/rake_test_sqlserver.rb @@ -138,18 +138,22 @@ class SQLServerRakeStructureDumpLoadTest < SQLServerRakeTest it "dumps structure and accounts for defncopy oddities" do skip "debug defncopy on windows later" if host_windows? + quietly { db_tasks.structure_dump configuration, filename } + _(filedata).wont_match %r{\AUSE.*\z} _(filedata).wont_match %r{\AGO.*\z} - _(filedata).must_match %r{email\s+nvarchar\(4000\)} - _(filedata).must_match %r{background1\s+nvarchar\(max\)} - _(filedata).must_match %r{background2\s+text\s+} + _(filedata).must_match %r{\[email\]\s+nvarchar\(4000\)} + _(filedata).must_match %r{\[background1\]\s+nvarchar\(max\)} + _(filedata).must_match %r{\[background2\]\s+text\s+} end it "can load dumped structure" do skip "debug defncopy on windows later" if host_windows? + quietly { db_tasks.structure_dump configuration, filename } - _(filedata).must_match %r{CREATE TABLE dbo\.users} + + _(filedata).must_match %r{CREATE TABLE \[dbo\]\.\[users\]} db_tasks.purge(configuration) _(connection.tables).wont_include "users" db_tasks.load_schema db_config, :sql, filename From ccb357bdfdae3747d0ee02f9fb90b309be5a6450 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 8 Jun 2024 18:00:23 +0100 Subject: [PATCH 1265/1412] Coerce test with trigger to autopopulate primary key on insert (#1186) --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4e18c5f92..21d419c9b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1267,6 +1267,9 @@ def test_update_coerced assert_not_predicate topic, :approved? assert_equal "The First Topic", topic.title end + + # In SQL Server it's not possible to set the primary key column using a trigger and to get it then to return. + coerce_tests! :test_model_with_no_auto_populated_fields_still_returns_primary_key_after_insert end require "models/author" From 331ca6b0eeb6b99d347becf5a23e8b617430095e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 8 Jun 2024 18:09:35 +0100 Subject: [PATCH 1266/1412] Fix update-all for composite key (#1189) --- lib/arel/visitors/sqlserver.rb | 42 +++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 3dfe35bbe..b4cef188d 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -30,10 +30,46 @@ def visit_Arel_Nodes_Concat(o, collector) end def visit_Arel_Nodes_UpdateStatement(o, collector) - if o.orders.any? && o.limit.nil? - o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) + if has_join_and_composite_primary_key?(o) + update_statement_using_join(o, collector) + else + o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? + + super end - super + end + + def visit_Arel_Nodes_DeleteStatement(o, collector) + if has_join_and_composite_primary_key?(o) + delete_statement_using_join(o, collector) + else + super + end + end + + def has_join_and_composite_primary_key?(o) + has_join_sources?(o) && o.relation.left.instance_variable_get(:@klass).composite_primary_key? + end + + def delete_statement_using_join(o, collector) + collector.retryable = false + + collector << "DELETE " + visit o.relation.left, collector + collector << " FROM " + visit o.relation, collector + collect_nodes_for o.wheres, collector, " WHERE ", " AND " + end + + def update_statement_using_join(o, collector) + collector.retryable = false + + collector << "UPDATE " + visit o.relation.left, collector + collect_nodes_for o.values, collector, " SET " + collector << " FROM " + visit o.relation, collector + collect_nodes_for o.wheres, collector, " WHERE ", " AND " end def visit_Arel_Nodes_Lock(o, collector) From 34e585211ede0758583aa1e73f96c244f560a452 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 9 Jun 2024 17:12:16 +0100 Subject: [PATCH 1267/1412] Add assertions to tests missing any assertions (#1193) --- test/cases/adapter_test_sqlserver.rb | 12 +++++++++--- test/cases/disconnected_test_sqlserver.rb | 10 ++++++++-- test/cases/index_test_sqlserver.rb | 6 ++++-- test/cases/scratchpad_test_sqlserver.rb | 8 -------- test/cases/specific_schema_test_sqlserver.rb | 8 ++++++-- 5 files changed, 27 insertions(+), 17 deletions(-) delete mode 100644 test/cases/scratchpad_test_sqlserver.rb diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index b963d09d5..2686267ff 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -289,7 +289,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "ALLOW deletion of referenced parent using #disable_referential_integrity block" do - SSTestHasPk.lease_connection.disable_referential_integrity { @parent.destroy } + assert_difference("SSTestHasPk.count", -1) do + SSTestHasPk.lease_connection.disable_referential_integrity { @parent.destroy } + end end it "again NOT ALLOW deletion of referenced parent after #disable_referential_integrity block" do @@ -549,11 +551,15 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes describe 'table is in non-dbo schema' do it "records can be created successfully" do - Alien.create!(name: 'Trisolarans') + assert_difference("Alien.count", 1) do + Alien.create!(name: 'Trisolarans') + end end it 'records can be inserted using SQL' do - Alien.lease_connection.exec_insert("insert into [test].[aliens] (id, name) VALUES(1, 'Trisolarans'), (2, 'Xenomorph')") + 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 diff --git a/test/cases/disconnected_test_sqlserver.rb b/test/cases/disconnected_test_sqlserver.rb index 382ca51b2..d3e89fbff 100644 --- a/test/cases/disconnected_test_sqlserver.rb +++ b/test/cases/disconnected_test_sqlserver.rb @@ -19,7 +19,10 @@ def setup test "execute procedure after disconnect reconnects" do @connection.execute_procedure :sp_tables, "sst_datatypes" @connection.disconnect! - @connection.execute_procedure :sp_tables, "sst_datatypes" + + assert_nothing_raised do + @connection.execute_procedure :sp_tables, "sst_datatypes" + end end test "execute query after disconnect reconnects" do @@ -31,6 +34,9 @@ def setup @connection.exec_query sql, "TEST", binds @connection.disconnect! - @connection.exec_query sql, "TEST", binds + + assert_nothing_raised do + @connection.exec_query sql, "TEST", binds + end end end diff --git a/test/cases/index_test_sqlserver.rb b/test/cases/index_test_sqlserver.rb index 75651c0ff..09acec01f 100644 --- a/test/cases/index_test_sqlserver.rb +++ b/test/cases/index_test_sqlserver.rb @@ -41,7 +41,9 @@ class IndexTestSQLServer < ActiveRecord::TestCase end it "add index with expression" do - connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])" - connection.add_index "testings", "first_name_upper" + assert_nothing_raised do + connection.execute "ALTER TABLE [testings] ADD [first_name_upper] AS UPPER([first_name])" + connection.add_index "testings", "first_name_upper" + end end end diff --git a/test/cases/scratchpad_test_sqlserver.rb b/test/cases/scratchpad_test_sqlserver.rb deleted file mode 100644 index ca1f72961..000000000 --- a/test/cases/scratchpad_test_sqlserver.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -require "cases/helper_sqlserver" - -class ScratchpadTestSQLServer < ActiveRecord::TestCase - it "helps debug things" do - end -end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 6d80c8392..61232e4ec 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -6,8 +6,12 @@ class SpecificSchemaTestSQLServer < ActiveRecord::TestCase after { SSTestEdgeSchema.delete_all } it "handle dollar symbols" do - SSTestDollarTableName.create! - SSTestDollarTableName.limit(20).offset(1) + assert_difference("SSTestDollarTableName.count", 1) do + SSTestDollarTableName.create! + end + assert_nothing_raised do + SSTestDollarTableName.limit(20).offset(1) + end end it "models can use tinyint pk tables" do From 09484cfd574819fd6152567d7c8b214aaa303cca Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 9 Jun 2024 17:19:01 +0100 Subject: [PATCH 1268/1412] Update ci.yml --- .github/workflows/ci.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 940d35462..f7c8ba082 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,6 +1,12 @@ name: CI -on: [push, pull_request] +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + schedule: + - cron: '0 18 * * *' jobs: test: From b0d3726b7d31411a04ed4dbd3b77b50b2cf6d93d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Jun 2024 19:15:51 +0100 Subject: [PATCH 1269/1412] Fix missing assertion warning (#1195) --- test/cases/execute_procedure_test_sqlserver.rb | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/test/cases/execute_procedure_test_sqlserver.rb b/test/cases/execute_procedure_test_sqlserver.rb index 9fa8df0df..d7f58965c 100644 --- a/test/cases/execute_procedure_test_sqlserver.rb +++ b/test/cases/execute_procedure_test_sqlserver.rb @@ -42,12 +42,16 @@ class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase assert_equal date_base.change(usec: 0), date_proc.change(usec: 0) end + def transaction_with_procedure_and_return + ActiveRecord::Base.transaction do + connection.execute_procedure("my_getutcdate") + return + end + end + it 'test deprecation with transaction return when executing procedure' do - assert_deprecated(ActiveRecord.deprecator) do - ActiveRecord::Base.transaction do - connection.execute_procedure("my_getutcdate") - return - end + assert_not_deprecated(ActiveRecord.deprecator) do + transaction_with_procedure_and_return end end end From 88d8e06045ec67f11b8de2c8b786527c4c723656 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 18 Jun 2024 14:34:44 +0100 Subject: [PATCH 1270/1412] Run CI against 7-2-stable branch until Rails release --- Dockerfile.ci | 2 +- docker-compose.ci.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..f9437e97d 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbc842619..957b5ec9b 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_BRANCH=7-2-stable build: context: . dockerfile: Dockerfile.ci From b6fdbebcc46f75f5bca9b2ba2b35e6b04e76df7d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 23 Jun 2024 11:13:26 +0100 Subject: [PATCH 1271/1412] Use default inspect for database adapter (#1196) --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 4 ---- test/cases/adapter_test_sqlserver.rb | 2 -- 2 files changed, 6 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index bffaad63a..3f3e72808 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -325,10 +325,6 @@ def version self.class::VERSION end - def inspect - "#<#{self.class} version: #{version}, azure: #{sqlserver_azure?.inspect}>" - end - def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil) result = from_clause + join_clause + where_clause + having_clause result << offset if offset diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 2686267ff..230043548 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -18,8 +18,6 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "has basic and non-sensitive information in the adapters inspect method" do string = connection.inspect _(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter} - _(string).must_match %r{version\: \d.\d} - _(string).must_match %r{azure: (true|false)} _(string).wont_match %r{host} _(string).wont_match %r{password} _(string).wont_match %r{username} From 1beb276cce652559505d2c1529071c48d8ed0bb4 Mon Sep 17 00:00:00 2001 From: Jonah George Date: Tue, 2 Jul 2024 04:27:15 -0700 Subject: [PATCH 1272/1412] Remove ActiveRecord::Relation#calculate patch (#1198) --- .../sqlserver/core_ext/calculations.rb | 47 ------------------- 1 file changed, 47 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index abc690744..b1a272d1d 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -8,53 +8,6 @@ module ConnectionAdapters module SQLServer module CoreExt module Calculations - def calculate(operation, column_name) - klass.with_connection do |connection| - if connection.sqlserver? - _calculate(operation, column_name) - else - super - end - end - end - - private - - # Same as original `calculate` method except we don't perform PostgreSQL hack that removes ordering. - def _calculate(operation, column_name) - operation = operation.to_s.downcase - - if @none - case operation - when "count", "sum" - result = group_values.any? ? Hash.new : 0 - return @async ? Promise::Complete.new(result) : result - when "average", "minimum", "maximum" - result = group_values.any? ? Hash.new : nil - return @async ? Promise::Complete.new(result) : result - end - end - - if has_include?(column_name) - relation = apply_join_dependency - - if operation == "count" - unless distinct_value || distinct_select?(column_name || select_for_count) - relation.distinct! - relation.select_values = Array(klass.primary_key || table[Arel.star]) - end - # PostgreSQL: ORDER BY expressions must appear in SELECT list when using DISTINCT - # Start of monkey-patch - # relation.order_values = [] if group_values.empty? - # End of monkey-patch - end - - relation.calculate(operation, column_name) - else - perform_calculation(operation, column_name) - end - end - def build_count_subquery(relation, column_name, distinct) klass.with_connection do |connection| relation = relation.unscope(:order) if connection.sqlserver? From d6109b9e234abe078cc7c4e9d026d9171b358ecf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 4 Jul 2024 14:53:09 +0100 Subject: [PATCH 1273/1412] Private keyword removed by mistake --- .../connection_adapters/sqlserver/core_ext/calculations.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index b1a272d1d..8357fdbe4 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -8,6 +8,9 @@ module ConnectionAdapters module SQLServer module CoreExt module Calculations + + private + def build_count_subquery(relation, column_name, distinct) klass.with_connection do |connection| relation = relation.unscope(:order) if connection.sqlserver? From 9ead256ceca243088aed465a876247773a56554a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 4 Jul 2024 15:18:58 +0100 Subject: [PATCH 1274/1412] Updated version numbers --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c10a0dcf4..85e3817a3 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ Interested in older versions? We follow a rational versioning policy that tracks | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| | Unreleased | `7.2.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.1.3` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.5.1` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `7.1.4` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.7` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | From b9b0912ea879ddb808f9de8c8646415747bd637e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 4 Jul 2024 19:07:03 +0100 Subject: [PATCH 1275/1412] Removed Appveyor status --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 85e3817a3..97ec2fd57 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,6 @@ # ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. * [![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml) - CI -* [![Build Status](https://ci.appveyor.com/api/projects/status/mtgbx8f57vr7k2qa/branch/master?svg=true)](https://ci.appveyor.com/project/rails-sqlserver/activerecord-sqlserver-adapter/branch/master) - Appveyor * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version * [![Gitter chat](https://img.shields.io/badge/%E2%8A%AA%20GITTER%20-JOIN%20CHAT%20%E2%86%92-brightgreen.svg?style=flat)](https://gitter.im/rails-sqlserver/activerecord-sqlserver-adapter) - Community From 252c4d609f40bd3537e00b82073ed41659a50cd0 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 15 Jul 2024 15:48:29 +0100 Subject: [PATCH 1276/1412] Fix table name typo (#1204) --- test/cases/specific_schema_test_sqlserver.rb | 4 ++-- test/schema/sqlserver_specific_schema.rb | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index 61232e4ec..bfdce3617 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -171,7 +171,7 @@ def quoted_id it "returns the correct primary columns" do connection = ActiveRecord::Base.lease_connection - assert_equal "field_1", connection.columns("test.sst_schema_test_mulitple_schema").detect(&:is_primary?).name - assert_equal "field_2", connection.columns("test2.sst_schema_test_mulitple_schema").detect(&:is_primary?).name + assert_equal "field_1", connection.columns("test.sst_schema_test_multiple_schema").detect(&:is_primary?).name + assert_equal "field_2", connection.columns("test2.sst_schema_test_multiple_schema").detect(&:is_primary?).name end end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 1bef7d0e1..3ae18f53c 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -304,17 +304,17 @@ ) NATURALPKTABLESQLINOTHERSCHEMA - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_mulitple_schema' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_test_mulitple_schema" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_multiple_schema' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_test_multiple_schema" execute <<-SCHEMATESTMULTIPLESCHEMA - CREATE TABLE test.sst_schema_test_mulitple_schema( + CREATE TABLE test.sst_schema_test_multiple_schema( field_1 int NOT NULL PRIMARY KEY, field_2 int, ) SCHEMATESTMULTIPLESCHEMA execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test2') EXEC sp_executesql N'CREATE SCHEMA test2'" - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_mulitple_schema' and TABLE_SCHEMA = 'test2') DROP TABLE test2.sst_schema_test_mulitple_schema" + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_multiple_schema' and TABLE_SCHEMA = 'test2') DROP TABLE test2.sst_schema_test_multiple_schema" execute <<-SCHEMATESTMULTIPLESCHEMA - CREATE TABLE test2.sst_schema_test_mulitple_schema( + CREATE TABLE test2.sst_schema_test_multiple_schema( field_1 int, field_2 int NOT NULL PRIMARY KEY, ) From 6d75c85c7dea0e5bc5a2ebdbfc336469d2c5e88b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 15 Jul 2024 16:07:37 +0100 Subject: [PATCH 1277/1412] Update for Rails 7.2.0.beta3 (#1205) --- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index de4dd5c4e..f987ae52f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.0.beta2 +7.2.0.beta1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 3e71c7422..53193d205 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.2.0.beta2" + spec.add_dependency "activerecord", "~> 7.2.0.beta3" spec.add_dependency "tiny_tds" end From 17dc03cbf8cc03e48f2614572ca0a2f16b1a093e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 15 Jul 2024 19:14:21 +0100 Subject: [PATCH 1278/1412] Update CHANGELOG.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 213a453e3..65d04331d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,12 @@ ## Unreleased +#### Added + +- [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns + #### Changed - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ +- [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. From 0b61b94c466af7a5f98dc57fc498b4071be7f960 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 16 Jul 2024 14:48:46 +0100 Subject: [PATCH 1279/1412] Support non-dbo schemas in schema dumper (#1201) --- CHANGELOG.md | 1 + .../sqlserver/schema_dumper.rb | 11 +++++++ .../sqlserver/schema_statements.rb | 31 +++++++++++++++---- test/cases/schema_dumper_test_sqlserver.rb | 27 +++++++++++++--- 4 files changed, 60 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d04331d..7cf70720c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ #### Added +- [#1201](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1201) Support non-dbo schemas in schema dumper. - [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index 9da6eef6f..d5bb1b348 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -39,6 +39,17 @@ def schema_collation(column) def default_primary_key?(column) super && column.is_identity? end + + def schemas(stream) + schema_names = @connection.schema_names + + if schema_names.any? + schema_names.sort.each do |name| + stream.puts " create_schema #{name.inspect}" + end + stream.puts + end + end end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 1b968420d..7cbb7a436 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -393,19 +393,37 @@ def drop_schema(schema_name) execute "DROP SCHEMA [#{schema_name}]" end + # 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') + SQL + + query_values(sql, "SCHEMA") + end + private def data_source_sql(name = nil, type: nil) - scope = quoted_scope name, type: type + scope = quoted_scope(name, type: type) - 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 #{table_name}" + sql = "SELECT " + sql += " CASE" + sql += " WHEN #{table_schema} = 'dbo' THEN #{table_name}" + sql += " ELSE CONCAT(#{table_schema}, '.', #{table_name})" + sql += " END" sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)" sql += " WHERE TABLE_CATALOG = #{table_catalog}" - sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" + sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}" if scope[:schema] sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name] sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type] sql += " ORDER BY #{table_name}" @@ -414,9 +432,10 @@ def data_source_sql(name = nil, type: nil) def quoted_scope(name = nil, type: nil) identifier = SQLServer::Utils.extract_identifiers(name) + {}.tap do |scope| scope[:database] = identifier.database if identifier.database - scope[:schema] = identifier.schema || "dbo" + scope[:schema] = identifier.schema || "dbo" if name.present? scope[:name] = identifier.object if identifier.object scope[:type] = type if type end diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 97e48d557..a3c82d87f 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "cases/helper_sqlserver" +require "stringio" class SchemaDumperTestSQLServer < ActiveRecord::TestCase before { all_tables } @@ -141,7 +142,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase it "honor nonstandard primary keys" do generate_schema_for_table("movies") do |output| match = output.match(%r{create_table "movies"(.*)do}) - assert_not_nil(match, "nonstandardpk table not found") + 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" end end @@ -159,15 +160,31 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase _(output.scan('t.integer "unique_field"').length).must_equal(1) end + it "schemas are dumped and tables names only include non-default schema" do + stream = StringIO.new + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection_pool, stream) + generated_schema = stream.string + + # Only generate non-default schemas. Default schema is 'dbo'. + assert_not_includes generated_schema, 'create_schema "dbo"' + assert_includes generated_schema, 'create_schema "test"' + assert_includes generated_schema, 'create_schema "test2"' + + # Only non-default schemas should be included in table names. Default schema is 'dbo'. + assert_includes generated_schema, 'create_table "accounts"' + assert_includes generated_schema, 'create_table "test.aliens"' + assert_includes generated_schema, 'create_table "test2.sst_schema_test_multiple_schema"' + end + private def generate_schema_for_table(*table_names) - require "stringio" + previous_ignore_tables = ActiveRecord::SchemaDumper.ignore_tables + ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names stream = StringIO.new - ActiveRecord::SchemaDumper.ignore_tables = all_tables - table_names ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection_pool, stream) - + @generated_schema = stream.string yield @generated_schema if block_given? @schema_lines = Hash.new @@ -178,6 +195,8 @@ def generate_schema_for_table(*table_names) @schema_lines[Regexp.last_match[1]] = SchemaLine.new(line) end @generated_schema + ensure + ActiveRecord::SchemaDumper.ignore_tables = previous_ignore_tables end def line(column_name) From 8849ef05e538155d1e07398a129c7fc728e9811d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 Jul 2024 09:48:15 +0100 Subject: [PATCH 1280/1412] Support table names containing spaces (#1206) --- .../sqlserver/schema_statements.rb | 18 ++++--- test/cases/adapter_test_sqlserver.rb | 8 ++++ test/cases/schema_test_sqlserver.rb | 48 ++++++++++++++++++- test/models/sqlserver/table_with_spaces.rb | 5 ++ test/schema/sqlserver_specific_schema.rb | 4 ++ 5 files changed, 76 insertions(+), 7 deletions(-) create mode 100644 test/models/sqlserver/table_with_spaces.rb diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 7cbb7a436..84a16ea9e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -673,12 +673,18 @@ def get_table_name(sql) # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc. def get_raw_table_name(sql) - case sql - when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i - Regexp.last_match[3] || Regexp.last_match[4] - when /FROM\s+([^\(\s]+)\s*/i - Regexp.last_match[1] - end + s = sql.gsub(/^\s*EXEC sp_executesql N'/i, "") + + if s.match?(/^\s*INSERT INTO.*/i) + s.split(/INSERT INTO/i)[1] + .split(/OUTPUT INSERTED/i)[0] + .split(/(DEFAULT)?\s+VALUES/i)[0] + .match(/\s*([^(]*)/i)[0] + elsif s.match?(/^\s*UPDATE\s+.*/i) + s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] + else + s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] + end.strip end def default_constraint_name(table_name, column_name) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 230043548..572f416e8 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -561,6 +561,14 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end + describe 'table names contains spaces' do + it 'records can be created successfully' do + assert_difference("TableWithSpaces.count", 1) do + TableWithSpaces.create!(name: 'Bob') + end + end + end + describe "exec_insert" do it 'values clause should be case-insensitive' do assert_difference("Post.count", 4) do diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index fc119fe27..1751af622 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -39,7 +39,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal 1, columns.select { |c| c.is_identity? }.size end - it "return correct varchar and nvarchar column limit length when table is in non dbo schema" do + it "return correct varchar and nvarchar column limit length when table is in non-dbo schema" do columns = connection.columns("test.sst_schema_columns") assert_equal 255, columns.find { |c| c.name == "name" }.limit @@ -48,4 +48,50 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal 1000, columns.find { |c| c.name == "n_description" }.limit end end + + describe "parsing table name from raw SQL" 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 + + it do + assert_equal "sst_schema_columns", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM sst_schema_columns") + end + + it do + assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES]") + end + + it do + assert_equal "[WITH - SPACES$DOLLAR]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES$DOLLAR]") + end + end + + 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 + + it do + assert_equal "lock_without_defaults", connection.send(:get_raw_table_name, "INSERT INTO lock_without_defaults(title) VALUES('title1')") + end + + it do + assert_equal "json_data_type", connection.send(:get_raw_table_name, "insert into json_data_type (payload) VALUES ('null')") + end + + it do + assert_equal "[auto_increments]", connection.send(:get_raw_table_name, "INSERT INTO [auto_increments] OUTPUT INSERTED.[id] DEFAULT VALUES") + end + + it do + assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [WITH - SPACES] ([external_id]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 bigint', @0 = 10") + end + + it do + assert_equal "[test].[aliens]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 varchar(255)', @0 = 'Trisolarans'") + end + end + end end diff --git a/test/models/sqlserver/table_with_spaces.rb b/test/models/sqlserver/table_with_spaces.rb new file mode 100644 index 000000000..d5f07ec4a --- /dev/null +++ b/test/models/sqlserver/table_with_spaces.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class TableWithSpaces < ActiveRecord::Base + self.table_name = "A Table With Spaces" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 3ae18f53c..136b6c1a5 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -151,6 +151,10 @@ SELECT GETUTCDATE() utcdate SQL + create_table 'A Table With Spaces', force: true do |t| + t.string :name + end + # Constraints create_table(:sst_has_fks, force: true) do |t| From b62e362fab76bd4b34db5fdb402d2e12e87e0bbb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 25 Jul 2024 09:51:12 +0100 Subject: [PATCH 1281/1412] Remove change already in v7.1 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cf70720c..65d04331d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,6 @@ #### Added -- [#1201](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1201) Support non-dbo schemas in schema dumper. - [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns #### Changed From 4b8da98922d2dad8f451d31b30aa2de384ed5bc0 Mon Sep 17 00:00:00 2001 From: Jesse vB <64111866+jvon1904@users.noreply.github.com> Date: Fri, 26 Jul 2024 16:30:55 -0400 Subject: [PATCH 1282/1412] Exclude "guest" schmea in schema dumper (#1208) Co-authored-by: Jesse vonBergen --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 84a16ea9e..4a71f754e 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -400,7 +400,7 @@ def schema_names FROM sys.schemas WHERE name NOT LIKE 'db_%' AND - name NOT IN ('INFORMATION_SCHEMA', 'sys') + name NOT IN ('INFORMATION_SCHEMA', 'sys', 'guest') SQL query_values(sql, "SCHEMA") diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index a3c82d87f..405f84c93 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -167,6 +167,10 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase # Only generate non-default schemas. Default schema is 'dbo'. assert_not_includes generated_schema, 'create_schema "dbo"' + assert_not_includes generated_schema, 'create_schema "db_owner"' + assert_not_includes generated_schema, 'create_schema "INFORMATION_SCHEMA"' + assert_not_includes generated_schema, 'create_schema "sys"' + assert_not_includes generated_schema, 'create_schema "guest"' assert_includes generated_schema, 'create_schema "test"' assert_includes generated_schema, 'create_schema "test2"' From 01f59c25137e759b987e3ed13f29a825d7891dcd Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 1 Aug 2024 16:25:21 +0100 Subject: [PATCH 1283/1412] Handle blank SQL when parsing table name (#1210) --- .../connection_adapters/sqlserver/schema_statements.rb | 2 ++ test/cases/schema_test_sqlserver.rb | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 4a71f754e..f85806478 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -673,6 +673,8 @@ def get_table_name(sql) # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc. def get_raw_table_name(sql) + return if sql.blank? + s = sql.gsub(/^\s*EXEC sp_executesql N'/i, "") if s.match?(/^\s*INSERT INTO.*/i) diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 1751af622..255c58711 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -66,6 +66,10 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it do assert_equal "[WITH - SPACES$DOLLAR]", connection.send(:get_raw_table_name, "SELECT id FROM [WITH - SPACES$DOLLAR]") end + + it do + assert_nil connection.send(:get_raw_table_name, nil) + end end describe 'INSERT statements' do From c77b8cbf991a0d3e5393d88788047f7b1a7922b7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 1 Aug 2024 16:25:53 +0100 Subject: [PATCH 1284/1412] Update docker compose command --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f7c8ba082..a1d3b0c80 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,7 +29,7 @@ jobs: uses: actions/checkout@v2 - name: Build docker images - run: docker-compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} + run: docker compose build --build-arg TARGET_VERSION=${{ matrix.ruby }} - name: Run tests - run: docker-compose run ci + run: docker compose run ci From 923b9faa11a727713ec1876c45ecc917e7e342b6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 7 Aug 2024 15:42:18 +0100 Subject: [PATCH 1285/1412] Run CI again 7.2.0.rc1 --- Dockerfile.ci | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- docker-compose.ci.yml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index f9437e97d..0ded95afd 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_BRANCH=7-2-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 53193d205..39cbf0b23 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.2.0.beta3" + spec.add_dependency "activerecord", "~> 7.2.0.rc1" spec.add_dependency "tiny_tds" end diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 957b5ec9b..cbc842619 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,7 +5,6 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_BRANCH=7-2-stable build: context: . dockerfile: Dockerfile.ci From 299b8372b1f6c40c6d6db4c9b15b207529ff5315 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 12 Aug 2024 15:34:28 +0100 Subject: [PATCH 1286/1412] Release v7.2.0 --- README.md | 55 +++++++++++++++++++------- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 97ec2fd57..2b8baf293 100644 --- a/README.md +++ b/README.md @@ -8,21 +8,25 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails version. We also have stable branches for each major/minor release of ActiveRecord. +Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version +of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you +are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails +version. We also have stable branches for each major/minor release of ActiveRecord. For older versions, please check +their stable branches. | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| Unreleased | `7.2.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.1.4` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.7` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.3.0` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.3` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.1` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.6` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.18` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.8` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | - -For older versions, please check their stable branches. +| `7.2.x` | `7.2.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.x` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | + +See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. #### Native Data Type Support @@ -167,14 +171,35 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X ## New Rails Applications -When creating a new Rails application you can specify that you want to use the SQL Server adapter using the `database` option: +When creating a new Rails application you need to perform the following steps to connect a Rails application to a +SQL Server instance. +1. Create new Rails application, the database defaults to `sqlite`. + +```bash +rails new my_app ``` -rails new my_app --database=sqlserver + +2. Update the Gemfile to install the adapter instead of the SQLite adapter. Remove the `sqlite3` gem from the Gemfile. + +```ruby +gem 'activerecord-sqlserver-adapter' ``` -To then connect the application to your SQL Server instance edit the `config/database.yml` file with the username, password and host of your SQL Server instance. +3. Connect the application to your SQL Server instance by editing the `config/database.yml` file with the username, +password and host of your SQL Server instance. +Example: + +```yaml +development: + adapter: sqlserver + host: 'localhost' + port: 1433 + database: my_app_development + username: 'frank_castle' + password: 'secret' +``` ## Installation diff --git a/VERSION b/VERSION index f987ae52f..0ee843cc6 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.0.beta1 +7.2.0 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 39cbf0b23..47d6f0f13 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.2.0.rc1" + spec.add_dependency "activerecord", "~> 7.2.0" spec.add_dependency "tiny_tds" end From acb632f2154f9e564dd6b770e463e7b420b5c90e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 13 Aug 2024 12:23:56 +0100 Subject: [PATCH 1287/1412] Rails 8 --- Dockerfile.ci | 2 +- README.md | 3 ++- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- docker-compose.ci.yml | 1 + 5 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..74f636508 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=main bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/README.md b/README.md index 2b8baf293..5fd887d25 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ their stable branches. | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `7.2.x` | `7.2.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| Unreleased | `8.0.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.x` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.x` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | diff --git a/VERSION b/VERSION index 0ee843cc6..60145e843 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.2.0 +8.0.0.alpha1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 47d6f0f13..4348bed4a 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 7.2.0" + spec.add_dependency "activerecord", "~> 8.0.0" spec.add_dependency "tiny_tds" end diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index cbc842619..f339681e7 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_BRANCH=main build: context: . dockerfile: Dockerfile.ci From d7266730e4cd8a989257558651179ea49f03b58e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 13 Aug 2024 13:58:41 +0100 Subject: [PATCH 1288/1412] Fix CI (#1212) --- .devcontainer/Dockerfile | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 116b439f1..6d4c1a2af 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/t RUN mkdir -p /tmp/activerecord-sqlserver-adapter COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ RUN cd /tmp/activerecord-sqlserver-adapter \ - && bundle install \ + && RAILS_BRANCH=main bundle install \ && rm -rf /tmp/activerecord-sqlserver-adapter RUN chown -R vscode:vscode /usr/local/rvm diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 4348bed4a..1ef711091 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.0.0" + spec.add_dependency "activerecord", "~> 8.0.0.alpha" spec.add_dependency "tiny_tds" end From 8b1c0055cb68992c419837abe96fd8adfedfab95 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 15 Aug 2024 14:19:38 +0100 Subject: [PATCH 1289/1412] Fixes before abstract adapter refactor (#1214) --- Gemfile | 2 + .../sqlserver/core_ext/calculations.rb | 4 +- .../sqlserver/core_ext/finder_methods.rb | 2 +- .../sqlserver/schema_statements.rb | 184 ++++++++++-------- test/cases/coerced_tests.rb | 10 +- 5 files changed, 113 insertions(+), 89 deletions(-) diff --git a/Gemfile b/Gemfile index 0f435985e..341d6786b 100644 --- a/Gemfile +++ b/Gemfile @@ -18,6 +18,8 @@ if ENV["RAILS_SOURCE"] gemspec path: ENV["RAILS_SOURCE"] elsif ENV["RAILS_BRANCH"] gem "rails", github: "rails/rails", branch: ENV["RAILS_BRANCH"] +elsif ENV["RAILS_COMMIT"] + gem "rails", github: "rails/rails", ref: ENV["RAILS_COMMIT"] else # Need to get rails source because the gem doesn't include tests version = ENV["RAILS_VERSION"] || begin diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb index 8357fdbe4..5fafea101 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb @@ -10,9 +10,9 @@ module CoreExt module Calculations private - + def build_count_subquery(relation, column_name, distinct) - klass.with_connection do |connection| + model.with_connection do |connection| relation = relation.unscope(:order) if connection.sqlserver? super(relation, column_name, distinct) end 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 6016369c2..db2d06708 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 @@ -11,7 +11,7 @@ module FinderMethods private def construct_relation_for_exists(conditions) - klass.with_connection do |connection| + model.with_connection do |connection| if connection.sqlserver? _construct_relation_for_exists(conditions) else diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index f85806478..a82a73df5 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -37,18 +37,16 @@ def indexes(table_name) data = select("EXEC sp_helpindex #{quote(table_name)}", "SCHEMA") rescue [] data.reduce([]) do |indexes, index| - index = index.with_indifferent_access - - 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/) + 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?("(-)") @@ -480,16 +478,15 @@ def initialize_native_database_types end def column_definitions(table_name) - identifier = database_prefix_identifier(table_name) - database = identifier.fully_qualified_database_quoted - view_exists = view_exists?(table_name) - view_tblnm = view_table_name(table_name) if view_exists + identifier = database_prefix_identifier(table_name) + database = identifier.fully_qualified_database_quoted + view_exists = view_exists?(table_name) if view_exists sql = <<~SQL SELECT LOWER(c.COLUMN_NAME) AS [name], c.COLUMN_DEFAULT AS [default] FROM #{database}.INFORMATION_SCHEMA.COLUMNS c - WHERE c.TABLE_NAME = #{quote(view_tblnm)} + WHERE c.TABLE_NAME = #{quote(view_table_name(table_name))} SQL results = internal_exec_query(sql, "SCHEMA") default_functions = results.each.with_object({}) { |row, out| out[row["name"]] = row["default"] }.compact @@ -498,71 +495,93 @@ def column_definitions(table_name) sql = column_definitions_sql(database, identifier) binds = [] - nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128 + nv128 = SQLServer::Type::UnicodeVarchar.new(limit: 128) binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128) binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank? + results = internal_exec_query(sql, "SCHEMA", binds) + raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty? columns = results.map do |ci| - ci = ci.symbolize_keys - ci[:_type] = ci[:type] - ci[:table_name] = view_tblnm || table_name - ci[:type] = case ci[:type] - when /^bit|image|text|ntext|datetime$/ - ci[:type] - when /^datetime2|datetimeoffset$/i - "#{ci[:type]}(#{ci[:datetime_precision]})" - when /^time$/i - "#{ci[:type]}(#{ci[:datetime_precision]})" - when /^numeric|decimal$/i - "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})" - when /^float|real$/i - "#{ci[:type]}" - when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/ - ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})" - else - ci[:type] - end - ci[:default_value], - ci[:default_function] = begin - default = ci[:default_value] - if default.nil? && view_exists - view_column = views_real_column_name(table_name, ci[:name]).downcase - default = default_functions[view_column] if view_column.present? - end - case default - when nil - [nil, nil] - when /\A\((\w+\(\))\)\Z/ - default_function = Regexp.last_match[1] - [nil, default_function] - when /\A\(N'(.*)'\)\Z/m - string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1]) - [string_literal, nil] - when /CREATE DEFAULT/mi - [nil, nil] - else - type = case ci[:type] - when /smallint|int|bigint/ then ci[:_type] - else ci[:type] - end - value = default.match(/\A\((.*)\)\Z/m)[1] - value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA") - [value, nil] - end + col = { + 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"] + } + + col[:table_name] = 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'] + else + col[:is_identity] = ci['is_identity'].to_i == 1 end - ci[:null] = ci[:is_nullable].to_i == 1 - ci.delete(:is_nullable) - ci[:is_primary] = ci[:is_primary].to_i == 1 - ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class) - ci + + col end - # Since Rails 7, it's expected that all adapter raise error when table doesn't exists. - # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those). - # Raise error if the method return an empty array - columns.tap do |result| - raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty? + columns + end + + def default_value_and_function(default:, name:, type:, original_type:, view_exists:, table_name:, default_functions:) + if default.nil? && view_exists + view_column = views_real_column_name(table_name, name).downcase + default = default_functions[view_column] if view_column.present? + end + + case default + when nil + [nil, nil] + when /\A\((\w+\(\))\)\Z/ + default_function = Regexp.last_match[1] + [nil, default_function] + when /\A\(N'(.*)'\)\Z/m + string_literal = SQLServer::Utils.unquote_string(Regexp.last_match[1]) + [string_literal, nil] + when /CREATE DEFAULT/mi + [nil, nil] + else + type = case type + 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] + end + end + + def column_type(ci:) + case ci['type'] + when /^bit|image|text|ntext|datetime$/ + ci['type'] + when /^datetime2|datetimeoffset$/i + "#{ci['type']}(#{ci['datetime_precision']})" + when /^time$/i + "#{ci['type']}(#{ci['datetime_precision']})" + when /^numeric|decimal$/i + "#{ci['type']}(#{ci['numeric_precision']},#{ci['numeric_scale']})" + when /^float|real$/i + "#{ci['type']}" + when /^char|nchar|varchar|nvarchar|binary|varbinary|bigint|int|smallint$/ + ci['length'].to_i == -1 ? "#{ci['type']}(max)" : "#{ci['type']}(#{ci['length']})" + else + ci['type'] end end @@ -701,25 +720,26 @@ def lowercase_schema_reflection_sql(node) def view_table_name(table_name) view_info = view_information(table_name) - view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name + view_info.present? ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name end def view_information(table_name) @view_information ||= {} + @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]" - view_info = select_one "SELECT * FROM #{information_query_table} WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA" - - if view_info - view_info = view_info.with_indifferent_access - 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 + + 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 end end @@ -728,8 +748,8 @@ def view_information(table_name) end def views_real_column_name(table_name, column_name) - view_definition = view_information(table_name)[:VIEW_DEFINITION] - return column_name unless 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) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 21d419c9b..44c7d2211 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2419,7 +2419,9 @@ class QueryLogsTest < ActiveRecord::TestCase # SQL requires double single-quotes. coerce_tests! :test_sql_commenter_format def test_sql_commenter_format_coerced - ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter + ActiveRecord::QueryLogs.tags = [:application] + assert_queries_match(%r{/\*application=''active_record''\*/}) do Dashboard.first end @@ -2428,7 +2430,7 @@ def test_sql_commenter_format_coerced # SQL requires double single-quotes. coerce_tests! :test_sqlcommenter_format_value def test_sqlcommenter_format_value_coerced - ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter ActiveRecord::QueryLogs.tags = [ :application, @@ -2443,7 +2445,7 @@ def test_sqlcommenter_format_value_coerced # SQL requires double single-quotes. coerce_tests! :test_sqlcommenter_format_value_string_coercible def test_sqlcommenter_format_value_string_coercible_coerced - ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter ActiveRecord::QueryLogs.tags = [ :application, @@ -2458,7 +2460,7 @@ def test_sqlcommenter_format_value_string_coercible_coerced # SQL requires double single-quotes. coerce_tests! :test_sqlcommenter_format_allows_string_keys def test_sqlcommenter_format_allows_string_keys_coerced - ActiveRecord::QueryLogs.update_formatter(:sqlcommenter) + ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter ActiveRecord::QueryLogs.tags = [ :application, From edddc2b7f42dd240e4e6d035b2f38829c74ed89f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 23 Aug 2024 16:16:13 +0100 Subject: [PATCH 1290/1412] Fix mismatched foreign key errors (#1215) --- CHANGELOG.md | 4 +++ .../connection_adapters/sqlserver_adapter.rb | 30 +++++++------------ test/cases/adapter_test_sqlserver.rb | 22 ++++++++++++++ 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 65d04331d..99be32efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,4 +9,8 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter +#### Fixed + +- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors + Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 3f3e72808..dacf11cdc 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -431,36 +431,28 @@ def type_map TYPE_MAP end - def translate_exception(e, message:, sql:, binds:) + def translate_exception(exception, message:, sql:, binds:) case message when /(SQL Server client is not connected)|(failed to execute statement)/i - ConnectionNotEstablished.new(message) + ConnectionNotEstablished.new(message, connection_pool: @pool) when /(cannot insert duplicate key .* with unique index) | (violation of (unique|primary) key constraint)/i - RecordNotUnique.new(message, sql: sql, binds: binds) + RecordNotUnique.new(message, sql: sql, binds: binds, connection_pool: @pool) when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i - InvalidForeignKey.new(message, sql: sql, binds: binds) + InvalidForeignKey.new(message, sql: sql, binds: binds, connection_pool: @pool) when /has been chosen as the deadlock victim/i - DeadlockVictim.new(message, sql: sql, binds: binds) + DeadlockVictim.new(message, sql: sql, binds: binds, connection_pool: @pool) when /database .* does not exist/i - NoDatabaseError.new(message) + NoDatabaseError.new(message, connection_pool: @pool) when /data would be truncated/ - ValueTooLong.new(message, sql: sql, binds: binds) + ValueTooLong.new(message, sql: sql, binds: binds, connection_pool: @pool) when /connection timed out/ - StatementTimeout.new(message, sql: sql, binds: binds) + StatementTimeout.new(message, sql: sql, binds: binds, connection_pool: @pool) when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/ - pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2) - MismatchedForeignKey.new( - self, - message: message, - table: fk_id.schema, - foreign_key: fk_id.object, - target_table: pk_id.schema, - primary_key: pk_id.object - ) + MismatchedForeignKey.new(message: message, connection_pool: @pool) when /Cannot insert the value NULL into column.*does not allow nulls/ - NotNullViolation.new(message, sql: sql, binds: binds) + NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool) when /Arithmetic overflow error/ - RangeError.new(message, sql: sql, binds: binds) + RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool) else super end diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 572f416e8..3c5e0ec80 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -580,4 +580,26 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes end end end + + describe "mismatched foreign keys error" do + 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 + @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 '.*'/, + 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 + end + end end From 512f334dc2668863308b0e831fefcd26446cb60b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 31 Aug 2024 21:40:58 +0100 Subject: [PATCH 1291/1412] Refactor adapter interface to match abstract adapter (#1216) --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 74 +++++++++---------- .../connection_adapters/sqlserver/showplan.rb | 1 + test/cases/coerced_tests.rb | 8 -- 4 files changed, 35 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 99be32efb..e257b0dff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter +- [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 741acb21e..38bd54525 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -13,47 +13,39 @@ def write_query?(sql) # :nodoc: !READ_QUERY.match?(sql.b) end - def raw_execute(sql, name, async: false, allow_retry: false, materialize_transactions: true) - log(sql, name, async: async) do |notification_payload| - with_raw_connection(allow_retry: allow_retry, materialize_transactions: materialize_transactions) do |conn| - result = if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, conn) { internal_raw_execute(sql, conn, perform_do: true) } - else - internal_raw_execute(sql, conn, perform_do: true) - end - verified! - notification_payload[:row_count] = result - result - end + def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) + result = if 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 + + verified! + notification_payload[:row_count] = result.count + result + end + + def cast_result(raw_result) + if raw_result.columns.empty? + ActiveRecord::Result.empty + else + ActiveRecord::Result.new(raw_result.columns, raw_result.rows) end end - def internal_exec_query(sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false) - sql = transform_query(sql) - - check_if_write_query(sql) - mark_transaction_written_if_write(sql) + def affected_rows(raw_result) + raw_result.first['AffectedRows'] + end - unless without_prepared_statement?(binds) + def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false) + unless binds.nil? || binds.empty? types, params = sp_executesql_types_and_parameters(binds) sql = sp_executesql_sql(sql, types, params, name) end - log(sql, name, binds, async: async) do |notification_payload| - with_raw_connection do |conn| - result = if id_insert_table_name = query_requires_identity_insert?(sql) - with_identity_insert_enabled(id_insert_table_name, conn) do - internal_exec_sql_query(sql, conn) - end - else - internal_exec_sql_query(sql, conn) - end - - verified! - notification_payload[:row_count] = result.count - result - end - end + super end def internal_exec_sql_query(sql, conn) @@ -63,14 +55,14 @@ def internal_exec_sql_query(sql, conn) finish_statement_handle(handle) end - def exec_delete(sql, name, binds) + def exec_delete(sql, name = nil, binds = []) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" - super(sql, name, binds).rows.first.first + super(sql, name, binds) end - def exec_update(sql, name, binds) + def exec_update(sql, name = nil, binds = []) sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows" - super(sql, name, binds).rows.first.first + super(sql, name, binds) end def begin_db_transaction @@ -378,6 +370,7 @@ def sp_executesql_sql(sql, types, params, name) sql = "EXEC sp_executesql #{quote(sql)}" sql += ", #{types}, #{params}" unless params.empty? end + sql.freeze end @@ -455,10 +448,9 @@ def finish_statement_handle(handle) # TinyTDS returns false instead of raising an exception if connection fails. # Getting around this by raising an exception ourselves while PR # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. - def internal_raw_execute(sql, conn, perform_do: false) - result = conn.execute(sql).tap do |_result| - raise TinyTds::Error, "failed to execute statement" if _result.is_a?(FalseClass) - end + def internal_raw_execute(sql, raw_connection, perform_do: false) + result = raw_connection.execute(sql) + raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) perform_do ? result.do : result end diff --git a/lib/active_record/connection_adapters/sqlserver/showplan.rb b/lib/active_record/connection_adapters/sqlserver/showplan.rb index 734495f8a..a93a839f4 100644 --- a/lib/active_record/connection_adapters/sqlserver/showplan.rb +++ b/lib/active_record/connection_adapters/sqlserver/showplan.rb @@ -16,6 +16,7 @@ def explain(arel, binds = [], options = []) sql = to_sql(arel) result = with_showplan_on { internal_exec_query(sql, "EXPLAIN", binds) } printer = showplan_printer.new(result) + printer.pp end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 44c7d2211..26ff4d15a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2250,14 +2250,6 @@ class LogSubscriberTest < ActiveRecord::TestCase def test_verbose_query_logs_coerced original_test_verbose_query_logs end - - # Bindings logged slightly differently. - coerce_tests! :test_where_in_binds_logging_include_attribute_names - def test_where_in_binds_logging_include_attribute_names_coerced - Developer.where(id: [1, 2, 3, 4, 5]).load - wait - assert_match(%{@0 = 1, @1 = 2, @2 = 3, @3 = 4, @4 = 5 [["id", nil], ["id", nil], ["id", nil], ["id", nil], ["id", nil]]}, @logger.logged(:debug).last) - end end class ReloadModelsTest < ActiveRecord::TestCase From b59f40df4025f1681a51362408c73131b55867cb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Sep 2024 15:08:03 +0100 Subject: [PATCH 1292/1412] Fix test --- test/cases/adapter_test_sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 3c5e0ec80..a968ab24c 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -542,7 +542,7 @@ def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes @conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')") ActiveRecord::Base.while_preventing_writes do - assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'") + assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'").count end end end From 2a60a217750cdb2bf9d0d77651eae7572ec331ea Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 2 Sep 2024 18:14:20 +0100 Subject: [PATCH 1293/1412] Fix serialized encrypted attributes (#1218) --- .../connection_adapters/sqlserver/database_statements.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 38bd54525..7a825101d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -321,10 +321,13 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) if attr.respond_to?(:type) - return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type) + type = attr.type.is_a?(ActiveRecord::Normalization::NormalizedValueType) ? attr.type.cast_type : attr.type + type = type.subtype if type.serialized? - if attr.type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) && attr.type.instance_variable_get(:@cast_type).respond_to?(:sqlserver_type) - return attr.type.instance_variable_get(:@cast_type).sqlserver_type + return type.sqlserver_type if type.respond_to?(:sqlserver_type) + + if type.is_a?(ActiveRecord::Encryption::EncryptedAttributeType) && type.instance_variable_get(:@cast_type).respond_to?(:sqlserver_type) + return type.instance_variable_get(:@cast_type).sqlserver_type end end From fb4512d115eb0ea7acbe7fa058b9c09f01fbc050 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 4 Sep 2024 10:09:52 +0100 Subject: [PATCH 1294/1412] Only query for view table name if table is a view (#1219) --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index a82a73df5..75cd62df8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -513,7 +513,7 @@ def column_definitions(table_name) length: ci["length"] } - col[:table_name] = view_table_name(table_name) || table_name + 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'], From ee5a900b1ac62d286fc06ca053a4dd25f60120bd Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 4 Sep 2024 10:14:35 +0100 Subject: [PATCH 1295/1412] Run CI once a week --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1d3b0c80..76b16bb71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ main ] schedule: - - cron: '0 18 * * *' + - cron: '0 4 * * 1' jobs: test: From 28857df0cf6e17feb71abbc9c7ec80e3db4cf45b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 4 Sep 2024 10:38:26 +0100 Subject: [PATCH 1296/1412] Updated CI Rubies --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76b16bb71..24f65697b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,9 +20,9 @@ jobs: fail-fast: false matrix: ruby: + - 3.3.4 + - 3.2.5 - 3.1.6 - - 3.2.4 - - 3.3.2 steps: - name: Checkout code From 3194885d8f7438b52b8daf00a986b3b80c8dbb15 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 23 Sep 2024 12:00:54 +0100 Subject: [PATCH 1297/1412] Switch Github runners to Ubuntu 20.04 (#1222) --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 24f65697b..c2f9aba86 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: jobs: test: name: Run test suite - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. env: COMPOSE_FILE: docker-compose.ci.yml From a2d6078e1c4b3162317fd545c5e6352076f789bf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 23 Sep 2024 13:46:50 +0100 Subject: [PATCH 1298/1412] Allow drop_table to accept an array of table names (#1223) --- .../sqlserver/schema_statements.rb | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 75cd62df8..3313c9d57 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -14,22 +14,24 @@ def create_table(table_name, **options) res end - def drop_table(table_name, **options) - # Mimic CASCADE option as best we can. - if options[:force] == :cascade - execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata| - fktable = fkdata["FKTABLE_NAME"] - fkcolmn = fkdata["FKCOLUMN_NAME"] - pktable = fkdata["PKTABLE_NAME"] - pkcolmn = fkdata["PKCOLUMN_NAME"] - remove_foreign_key fktable, name: fkdata["FK_NAME"] - execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" + def drop_table(*table_names, **options) + table_names.each do |table_name| + # Mimic CASCADE option as best we can. + if options[:force] == :cascade + execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata| + fktable = fkdata["FKTABLE_NAME"] + fkcolmn = fkdata["FKCOLUMN_NAME"] + pktable = fkdata["PKTABLE_NAME"] + pkcolmn = fkdata["PKCOLUMN_NAME"] + remove_foreign_key fktable, name: fkdata["FK_NAME"] + execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )" + end + end + if options[:if_exists] && version_year < 2016 + execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA" + else + super end - end - if options[:if_exists] && version_year < 2016 - execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA" - else - super end end From 0761353e1d7e21c6eb2882f8703141b3d61bb9d8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 27 Sep 2024 15:41:49 +0100 Subject: [PATCH 1299/1412] Drop support to Ruby 3.1 (#1225) --- .github/workflows/ci.yml | 1 - activerecord-sqlserver-adapter.gemspec | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c2f9aba86..bbdc3646c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,7 +22,6 @@ jobs: ruby: - 3.3.4 - 3.2.5 - - 3.1.6 steps: - name: Checkout code diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 1ef711091..437aa4f34 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |spec| spec.platform = Gem::Platform::RUBY spec.version = version - spec.required_ruby_version = ">= 3.1.0" + 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"] From 78c2079a2cb3b10c43c10945890523a1c645bed6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 27 Sep 2024 20:24:38 +0100 Subject: [PATCH 1300/1412] Keep ordering in count subquery (#1227) --- .../sqlserver/core_ext/calculations.rb | 29 ------------------- .../connection_adapters/sqlserver_adapter.rb | 2 -- 2 files changed, 31 deletions(-) delete mode 100644 lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb deleted file mode 100644 index 5fafea101..000000000 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +++ /dev/null @@ -1,29 +0,0 @@ -# frozen_string_literal: true - -require "active_record/relation" -require "active_record/version" - -module ActiveRecord - module ConnectionAdapters - module SQLServer - module CoreExt - module Calculations - - private - - def build_count_subquery(relation, column_name, distinct) - model.with_connection do |connection| - relation = relation.unscope(:order) if connection.sqlserver? - super(relation, column_name, distinct) - end - end - end - end - end - end -end - -ActiveSupport.on_load(:active_record) do - mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Calculations - ActiveRecord::Relation.include(mod) -end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index dacf11cdc..314eacce1 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -4,9 +4,7 @@ require "base64" require "active_record" require "arel_sqlserver" -require "active_record/connection_adapters/abstract_adapter" require "active_record/connection_adapters/sqlserver/core_ext/active_record" -require "active_record/connection_adapters/sqlserver/core_ext/calculations" require "active_record/connection_adapters/sqlserver/core_ext/explain" require "active_record/connection_adapters/sqlserver/core_ext/explain_subscriber" require "active_record/connection_adapters/sqlserver/core_ext/attribute_methods" From 8bd31c28422b675a3d50958f2b60cd8e66600ad6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 29 Sep 2024 12:42:05 +0100 Subject: [PATCH 1301/1412] Handle insert returning using symbol (#1226) --- .../sqlserver/database_statements.rb | 2 +- test/cases/coerced_tests.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 7a825101d..97e66c155 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -145,7 +145,7 @@ def build_insert_sql(insert) # :nodoc: returning_sql = if returning.is_a?(String) returning else - returning.map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") end sql << " OUTPUT #{returning_sql}" end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 26ff4d15a..a5a171203 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2487,6 +2487,17 @@ def test_insert_all_returns_requested_sql_fields_coerced 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. + coerce_tests! :test_insert_with_type_casting_and_serialize_is_consistent + def test_insert_with_type_casting_and_serialize_is_consistent_coerced + connection.remove_index(:books, column: [:author_id, :name]) + + original_test_insert_with_type_casting_and_serialize_is_consistent + ensure + Book.where(author_id: nil, name: '["Array"]').delete_all + Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) + end end module ActiveRecord From 931746730bb904c0b502c0aedb842e87c49f4a12 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 30 Sep 2024 11:26:39 +0100 Subject: [PATCH 1302/1412] Enable identity insert on view's base table (#1228) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 3 +++ test/cases/view_test_sqlserver.rb | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e257b0dff..bbe193fa0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,5 +13,6 @@ #### Fixed - [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors +- [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 97e66c155..2113057dc 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -15,6 +15,9 @@ def write_query?(sql) # :nodoc: def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) result = if id_insert_table_name = query_requires_identity_insert?(sql) + # If the table name is a view, we need to get the base table name for enabling identity insert. + id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) + with_identity_insert_enabled(id_insert_table_name, raw_connection) do internal_exec_sql_query(sql, raw_connection) end diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 73ddf006a..84bfd80e1 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -47,4 +47,12 @@ class ViewTestSQLServer < ActiveRecord::TestCase assert_equal 1, klass.count end end + + describe 'identity insert' do + it "identity insert works with views" do + assert_difference("SSTestCustomersView.count", 1) do + SSTestCustomersView.create!(id: 5, name: "Bob") + end + end + end end From 7b66df0e6786d896f3958d3082200fb94b0e8aaa Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 30 Sep 2024 11:44:07 +0100 Subject: [PATCH 1303/1412] Update CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbe193fa0..2b94bf643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ - [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter - [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter +- [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1 #### Fixed From e3aba95b0bba50b9c17bf605039ae9afa6ee1a32 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 30 Sep 2024 14:30:13 +0100 Subject: [PATCH 1304/1412] Coerce test (#1229) --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a5a171203..a39927864 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -520,6 +520,9 @@ def test_distinct_count_all_with_custom_select_and_order_coerced # SELECT columns must be in the GROUP clause. Since since `ids` only selects the primary key you cannot perform this query in SQL Server. coerce_tests! :test_ids_with_includes_and_non_primary_key_order + + # To limit the results in SQL Server we use `FETCH NEXT @0 ROWS ONLY` instead of `LIMIT @0`. To use `FETCH NEXT` an order must be provided. + coerce_tests! :test_no_order_by_when_counting_all end module ActiveRecord From 98499231aa28f8fea5a96b1b317e3cf1f6aabc40 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 1 Oct 2024 10:33:12 +0100 Subject: [PATCH 1305/1412] Updated supported versions --- README.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 5fd887d25..d2d5e7acb 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ActiveRecord SQL Server Adapter. For SQL Server 2012 And Higher. +# ActiveRecord SQL Server Adapter * [![CI](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml/badge.svg)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/actions/workflows/ci.yml) - CI * [![Gem Version](http://img.shields.io/gem/v/activerecord-sqlserver-adapter.svg)](https://rubygems.org/gems/activerecord-sqlserver-adapter) - Gem Version @@ -8,26 +8,27 @@ The SQL Server adapter for ActiveRecord using SQL Server 2012 or higher. -Interested in older versions? We follow a rational versioning policy that tracks Rails. That means that our 7.x version -of the adapter is only for the latest 7.x version of Rails. If you need the adapter for SQL Server 2008 or 2005, you -are still in the right spot. Just install the latest 3.2.x to 4.1.x version of the adapter that matches your Rails -version. We also have stable branches for each major/minor release of ActiveRecord. For older versions, please check -their stable branches. +We follow a rational versioning policy that tracks Rails. That means that our 7.x version of the adapter is only +for the latest 7.x version of Rails. We also have stable branches for each major/minor release of ActiveRecord. + +We support the versions of the adapter that are in the Rails [Bug Fixes](https://rubyonrails.org/maintenance) +maintenance group. + +See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| | Unreleased | `8.0.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.x` | `7.0.x` | Active | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.x` | `6.1.x` | Active | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | | `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | | `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | | `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | | `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | -See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. #### Native Data Type Support From 6844c31357659b8ad80705c4af35584b434b195d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 7 Oct 2024 14:01:28 +0100 Subject: [PATCH 1306/1412] Run CI --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index d2d5e7acb..927084bd0 100644 --- a/README.md +++ b/README.md @@ -264,7 +264,6 @@ Many many people have contributed. If you do not see your name here and it shoul You can see an up-to-date list of contributors here: http://github.com/rails-sqlserver/activerecord-sqlserver-adapter/contributors - ## License ActiveRecord SQL Server Adapter is released under the [MIT License](https://opensource.org/licenses/MIT). From ee8eba9e5edd61428a17916a6b3d2c9058690ac7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 7 Oct 2024 14:07:06 +0100 Subject: [PATCH 1307/1412] Fix for PredicateBuilderTest (#1239) --- test/cases/coerced_tests.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a39927864..9ad625757 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1340,6 +1340,12 @@ def test_registering_new_handlers_coerced 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 end + + private + + def topic_title + Topic.lease_connection.quote_table_name("topics.title") + end end end From 68d1c0f4c822323f032cdba0f2ec7844259238c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Z=C3=BCrcher?= Date: Wed, 9 Oct 2024 15:00:22 +0200 Subject: [PATCH 1308/1412] Allow INSERT statements with SELECT notation (#1238) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 1 + test/cases/schema_test_sqlserver.rb | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b94bf643..165989f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,5 +15,6 @@ - [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors - [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table +- [#1238](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1238) Allow INSERT statements with SELECT notation Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 3313c9d57..33aa461ad 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -702,6 +702,7 @@ def get_raw_table_name(sql) s.split(/INSERT INTO/i)[1] .split(/OUTPUT INSERTED/i)[0] .split(/(DEFAULT)?\s+VALUES/i)[0] + .split(/\bSELECT\b(?![^\[]*\])/i)[0] .match(/\s*([^(]*)/i)[0] elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 255c58711..f9dbde8e6 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -96,6 +96,10 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it do assert_equal "[test].[aliens]", connection.send(:get_raw_table_name, "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name]) OUTPUT INSERTED.[id] VALUES (@0)', N'@0 varchar(255)', @0 = 'Trisolarans'") end + + it do + assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]") + end end end end From 7dcff7381cc4c2f5f964fbfe95f067327c702be3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 10 Oct 2024 15:56:09 +0100 Subject: [PATCH 1309/1412] Updated sample code for connection configuration (#1242) --- README.md | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 927084bd0..33739ffad 100644 --- a/README.md +++ b/README.md @@ -105,16 +105,14 @@ configuration then implement the `configure_connection` method in an initializer example we are setting the `TEXTSIZE` to 64 megabytes. ```ruby -module ActiveRecord - module ConnectionAdapters - class SQLServerAdapter < AbstractAdapter - def configure_connection - super - @raw_connection.execute("SET TEXTSIZE #{64.megabytes}").do - end +ActiveRecord::ConnectionAdapters::SQLServerAdapter.prepend( + Module.new do + def configure_connection + super + @raw_connection.execute("SET TEXTSIZE #{64.megabytes}").do end end -end +) ``` #### Configure Application Name From 19de909db491f428bfff603243eaf5fec2e2f73a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 15 Oct 2024 16:54:09 +0100 Subject: [PATCH 1310/1412] Fix typo --- test/cases/adapter_test_sqlserver.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a968ab24c..ba781fc94 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -464,13 +464,13 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert SSTestStringDefaultsView.lease_connection.data_source_exists?(SSTestStringDefaultsView.table_name) end - # That have more than 4000 chars for their defintion + # That have more than 4000 chars for their definition - it "cope with null returned for the defintion" do + it "cope with null returned for the definition" do assert_nothing_raised() { SSTestStringDefaultsBigView.columns } end - it "using alternate view defintion still be able to find real default" do + 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 end From c137deb9aec6e4c6971d2c5905463fca23a999ee Mon Sep 17 00:00:00 2001 From: Andreas Sippl Date: Wed, 16 Oct 2024 10:25:46 +0200 Subject: [PATCH 1311/1412] Fix queries with date and date-time placeholder conditions (#1246) --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 14 ++++---------- test/cases/adapter_test_sqlserver.rb | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 165989f36..cd6249da0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,5 +16,6 @@ - [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors - [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table - [#1238](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1238) Allow INSERT statements with SELECT notation +- [#1246](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1246) Fix queries with date and date-time placeholder conditions Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 2113057dc..9e36d61aa 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -334,7 +334,7 @@ def sp_executesql_sql_type(attr) end end - value = basic_attribute_type?(attr) ? attr : attr.value_for_database + value = active_model_attribute?(attr) ? attr.value_for_database : attr if value.is_a?(Numeric) value > 2_147_483_647 ? "bigint".freeze : "int".freeze @@ -344,7 +344,7 @@ def sp_executesql_sql_type(attr) end def sp_executesql_sql_param(attr) - return quote(attr) if basic_attribute_type?(attr) + return quote(attr) unless active_model_attribute?(attr) case value = attr.value_for_database when Type::Binary::Data, ActiveRecord::Type::SQLServer::Data @@ -354,14 +354,8 @@ def sp_executesql_sql_param(attr) end end - def basic_attribute_type?(type) - type.is_a?(Symbol) || - type.is_a?(String) || - type.is_a?(Numeric) || - type.is_a?(Time) || - type.is_a?(TrueClass) || - type.is_a?(FalseClass) || - type.is_a?(NilClass) + def active_model_attribute?(type) + type.is_a?(::ActiveModel::Attribute) end def sp_executesql_sql(sql, types, params, name) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index ba781fc94..28a310c51 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -602,4 +602,18 @@ def setup @conn.execute("ALTER TABLE engines DROP COLUMN old_car_id") rescue nil end end + + describe "placeholder conditions" do + it 'using time placeholder' do + assert_equal Task.where("starting < ?", Time.now).count, 1 + end + + it 'using date placeholder' do + assert_equal Task.where("starting < ?", Date.today).count, 1 + end + + it 'using date-time placeholder' do + assert_equal Task.where("starting < ?", DateTime.current).count, 1 + end + end end From 3ad671be2d3f9ea6d774808270435a605658d8da Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 21 Oct 2024 09:55:43 +0100 Subject: [PATCH 1312/1412] Update activerecord-sqlserver-adapter.gemspec --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 437aa4f34..94badc3b2 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.0.0.alpha" + spec.add_dependency "activerecord", "~> 8.0.0.rc1" spec.add_dependency "tiny_tds" end From dd0d3f994aaf1f119ec6a6eef137b0d36ae7c367 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 21 Oct 2024 10:50:02 +0100 Subject: [PATCH 1313/1412] Fix CI for Rails 8.0.0.rc1 (#1251) --- .devcontainer/Dockerfile | 2 +- Dockerfile.ci | 2 +- docker-compose.ci.yml | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6d4c1a2af..116b439f1 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/t RUN mkdir -p /tmp/activerecord-sqlserver-adapter COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ RUN cd /tmp/activerecord-sqlserver-adapter \ - && RAILS_BRANCH=main bundle install \ + && bundle install \ && rm -rf /tmp/activerecord-sqlserver-adapter RUN chown -R vscode:vscode /usr/local/rvm diff --git a/Dockerfile.ci b/Dockerfile.ci index 74f636508..0ded95afd 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_BRANCH=main bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index f339681e7..cbc842619 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -5,7 +5,6 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_BRANCH=main build: context: . dockerfile: Dockerfile.ci From f47c372045e1e2f62f37feb00cecf8b4f52dbfea Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 21 Oct 2024 14:01:10 +0100 Subject: [PATCH 1314/1412] Fix tests for Rails 8.0.0.rc1 (#1252) --- test/cases/coerced_tests.rb | 41 +++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9ad625757..1ffa0694c 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1341,6 +1341,14 @@ 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 end + # Same as original test except string has `N` prefix to indicate unicode string. + coerce_tests! :test_registering_new_handlers_for_joins + def test_registering_new_handlers_for_joins_coerced + Reply.belongs_to :regexp_topic, -> { where(title: /rails/) }, class_name: "Topic", foreign_key: "parent_id" + + assert_match %r{#{Regexp.escape(quote_table_name("regexp_topic.title"))} ~ N'rails'}i, Reply.joins(:regexp_topic).references(Arel.sql("regexp_topic")).to_sql + end + private def topic_title @@ -2177,17 +2185,6 @@ class EnumTest < ActiveRecord::TestCase Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end - # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. - coerce_tests! %r{declare multiple enums at a time} - test "declare multiple enums at a time coerced" do - Book.lease_connection.remove_index(:books, column: [:author_id, :name]) - - send(:'original_declare multiple enums at a time') - ensure - Book.where(author_id: nil, name: nil).delete_all - Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) - end - # Need to remove index as SQL Server considers NULLs on a unique-index to be equal unlike PostgreSQL/MySQL/SQLite. coerce_tests! %r{serializable\? with large number label} test "serializable? with large number label coerced" do @@ -2669,6 +2666,28 @@ def test_url_invalid_adapter_coerced end end +module ActiveRecord + module ConnectionAdapters + class RegistrationIsolatedTest < ActiveRecord::TestCase + # SQL Server was not included in the list of available adapters in the error message. + coerce_tests! %r{resolve raises if the adapter is using the pre 7.2 adapter registration API} + def resolve_raises_if_the_adapter_is_using_the_pre_7_2_adapter_registration_API + exception = assert_raises(ActiveRecord::AdapterNotFound) do + ActiveRecord::ConnectionAdapters.resolve("fake_legacy") + end + + assert_equal( + "Database configuration specifies nonexistent 'ridiculous' adapter. Available adapters are: abstract, fake, mysql2, postgresql, sqlite3, sqlserver, trilogy. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile if it's not in the list of available adapters.", + exception.message + ) + ensure + ActiveRecord::ConnectionAdapters.instance_variable_get(:@adapters).delete("fake_legacy") + end + end + end +end + + module ActiveRecord class TableMetadataTest < ActiveSupport::TestCase # Adapter returns an object that is subclass of what is expected in the original test. From 8bf060f7ad4b37b94e42c574c2866ef12c6276c5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 22 Oct 2024 14:36:18 +0100 Subject: [PATCH 1315/1412] Binary basic columns should be limitable (#1249) --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 2 +- test/cases/schema_dumper_test_sqlserver.rb | 68 ++++++++++--------- test/schema/sqlserver_specific_schema.rb | 1 + 4 files changed, 38 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd6249da0..c629526c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,5 +17,6 @@ - [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table - [#1238](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1238) Allow INSERT statements with SELECT notation - [#1246](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1246) Fix queries with date and date-time placeholder conditions +- [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 33aa461ad..ba4541a52 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -291,7 +291,7 @@ def check_constraints(table_name) end def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) - type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s) + type_limitable = %w(string integer float char nchar varchar nvarchar binary_basic).include?(type.to_s) limit = nil unless type_limitable case type.to_s diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 405f84c93..6e7140ddf 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -93,39 +93,41 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase assert_line :binary_col, type: "binary" # Our type methods. - _(columns["real_col"].sql_type).must_equal "real" - _(columns["money_col"].sql_type).must_equal "money" - _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" - _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" - _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" - _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" - _(columns["char_col"].sql_type).must_equal "char(1)" - _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" - _(columns["text_basic_col"].sql_type).must_equal "text" - _(columns["nchar_col"].sql_type).must_equal "nchar(1)" - _(columns["ntext_col"].sql_type).must_equal "ntext" - _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" - _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" - _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" - _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" - _(columns["json_col"].sql_type).must_equal "nvarchar(max)" - - assert_line :real_col, type: "real" - assert_line :money_col, type: "money", precision: 19, scale: 4 - assert_line :smalldatetime_col, type: "smalldatetime" - assert_line :datetime2_col, type: "datetime", precision: 7 - assert_line :datetimeoffset, type: "datetimeoffset", precision: 7 - assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4 - assert_line :char_col, type: "char", limit: 1 - assert_line :varchar_col, type: "varchar" - assert_line :text_basic_col, type: "text_basic" - assert_line :nchar_col, type: "nchar", limit: 1 - assert_line :ntext_col, type: "ntext" - assert_line :binary_basic_col, type: "binary_basic", limit: 1 - assert_line :varbinary_col, type: "varbinary" - assert_line :uuid_col, type: "uuid" - assert_line :sstimestamp_col, type: "ss_timestamp", null: false - assert_line :json_col, type: "text" + _(columns["real_col"].sql_type).must_equal "real" + _(columns["money_col"].sql_type).must_equal "money" + _(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime" + _(columns["datetime2_col"].sql_type).must_equal "datetime2(7)" + _(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)" + _(columns["smallmoney_col"].sql_type).must_equal "smallmoney" + _(columns["char_col"].sql_type).must_equal "char(1)" + _(columns["varchar_col"].sql_type).must_equal "varchar(8000)" + _(columns["text_basic_col"].sql_type).must_equal "text" + _(columns["nchar_col"].sql_type).must_equal "nchar(1)" + _(columns["ntext_col"].sql_type).must_equal "ntext" + _(columns["binary_basic_col"].sql_type).must_equal "binary(1)" + _(columns["binary_basic_16_col"].sql_type).must_equal "binary(16)" + _(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)" + _(columns["uuid_col"].sql_type).must_equal "uniqueidentifier" + _(columns["sstimestamp_col"].sql_type).must_equal "timestamp" + _(columns["json_col"].sql_type).must_equal "nvarchar(max)" + + assert_line :real_col, type: "real" + assert_line :money_col, type: "money", precision: 19, scale: 4 + assert_line :smalldatetime_col, type: "smalldatetime" + assert_line :datetime2_col, type: "datetime", precision: 7 + assert_line :datetimeoffset, type: "datetimeoffset", precision: 7 + assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4 + assert_line :char_col, type: "char", limit: 1 + assert_line :varchar_col, type: "varchar" + assert_line :text_basic_col, type: "text_basic" + assert_line :nchar_col, type: "nchar", limit: 1 + assert_line :ntext_col, type: "ntext" + assert_line :binary_basic_col, type: "binary_basic", limit: 1 + assert_line :binary_basic_16_col, type: "binary_basic", limit: 16 + assert_line :varbinary_col, type: "varbinary" + assert_line :uuid_col, type: "uuid" + assert_line :sstimestamp_col, type: "ss_timestamp", null: false + assert_line :json_col, type: "text" end it "dump column collation" do diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 136b6c1a5..a5160f791 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -33,6 +33,7 @@ t.nchar :nchar_col t.ntext :ntext_col t.binary_basic :binary_basic_col + t.binary_basic :binary_basic_16_col, limit: 16 t.varbinary :varbinary_col t.uuid :uuid_col t.ss_timestamp :sstimestamp_col From 34df30931cfbee1bdc19bb5562a996c495db256a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 14:10:29 +0000 Subject: [PATCH 1316/1412] Fixed the ordering of optimizer hints in the generated SQL (#1255) --- lib/arel/visitors/sqlserver.rb | 14 ++++++-------- test/cases/optimizer_hints_test_sqlserver.rb | 9 +++++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index b4cef188d..5890b7fc3 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -134,26 +134,24 @@ def visit_Arel_Nodes_HomogeneousIn(o, collector) def visit_Arel_Nodes_SelectStatement(o, collector) @select_statement = o + optimizer_hints = nil distinct_One_As_One_Is_So_Not_Fetch o if o.with collector = visit o.with, collector collector << " " end - collector = o.cores.inject(collector) { |c, x| - visit_Arel_Nodes_SelectCore(x, c) - } + collector = o.cores.inject(collector) do |collect, core| + optimizer_hints = core.optimizer_hints if core.optimizer_hints + visit_Arel_Nodes_SelectCore(core, collect) + end collector = visit_Orders_And_Let_Fetch_Happen o, collector collector = visit_Make_Fetch_Happen o, collector + collector = maybe_visit optimizer_hints, collector collector ensure @select_statement = nil end - def visit_Arel_Nodes_SelectCore(o, collector) - collector = super - maybe_visit o.optimizer_hints, collector - end - def visit_Arel_Nodes_OptimizerHints(o, collector) hints = o.expr.map { |v| sanitize_as_option_clause(v) }.join(", ") collector << "OPTION (#{hints})" diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index 97752f36c..46f13ac0d 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -36,6 +36,15 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end end + + it "support order" do + assert_queries_match(%r{\ASELECT .+ FROM .+ ORDER .+ OPTION .+\z}) do + companies = Company.optimizer_hints("LABEL='FindCompanies'") + companies = companies.order(:id) + companies.to_a + end + end + it "sanitize values" do assert_queries_match(%r{\ASELECT .+ FROM .+ OPTION \(HASH GROUP\)\z}) do companies = Company.optimizer_hints("OPTION (HASH GROUP)") From e45857c23d9e87783257d71c77fd9f30af3f27e4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 14:10:53 +0000 Subject: [PATCH 1317/1412] Cleanup --- test/cases/optimizer_hints_test_sqlserver.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index 46f13ac0d..b1aab6820 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -36,7 +36,6 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end end - it "support order" do assert_queries_match(%r{\ASELECT .+ FROM .+ ORDER .+ OPTION .+\z}) do companies = Company.optimizer_hints("LABEL='FindCompanies'") From f471cd497416e8ca86a94bb44ee20ef38f3ac0e8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 14:19:44 +0000 Subject: [PATCH 1318/1412] Rails v8.0.0 (#1257) --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 94badc3b2..d9fdebdf1 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.0.0.rc1" + spec.add_dependency "activerecord", "~> 8.0.0" spec.add_dependency "tiny_tds" end From 27c193c0ca2f805a9cb170a732bc72e5b9e43440 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 19:07:54 +0000 Subject: [PATCH 1319/1412] Fix casting of invalid time values (#1258) --- lib/active_record/connection_adapters/sqlserver/type/time.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/type/time.rb b/lib/active_record/connection_adapters/sqlserver/type/time.rb index bdc8bbc86..3fdf44523 100644 --- a/lib/active_record/connection_adapters/sqlserver/type/time.rb +++ b/lib/active_record/connection_adapters/sqlserver/type/time.rb @@ -36,9 +36,10 @@ def quoted(value) def cast_value(value) value = super - return if value.blank? - value = value.change year: 2000, month: 01, day: 01 + return value unless value.is_a?(::Time) + + value = value.change(year: 2000, month: 01, day: 01) apply_seconds_precision(value) end From 2acaa4315601752056ff701ea821b293ec5d5518 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 8 Nov 2024 19:14:45 +0000 Subject: [PATCH 1320/1412] Cleanup changelog --- CHANGELOG.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c629526c9..742f5885d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,7 @@ ## Unreleased -#### Added - -- [#1178](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1178) Support encrypting binary columns - #### Changed -- [#1153](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1153) Only support Ruby v3.1+ -- [#1196](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1196) Use default inspect for database adapter - [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter - [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1 @@ -17,6 +11,5 @@ - [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table - [#1238](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1238) Allow INSERT statements with SELECT notation - [#1246](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1246) Fix queries with date and date-time placeholder conditions -- [#1249](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1249) Binary basic columns should be limitable -Please check [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-1-stable/CHANGELOG.md) for previous changes. +Please check [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-2-stable/CHANGELOG.md) for previous changes. From 519d4310681d55666f70d55a7b55dff7d020f34d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 11 Nov 2024 09:04:15 +0000 Subject: [PATCH 1321/1412] Release v8.0.0 (#1259) --- CHANGELOG.md | 5 +---- README.md | 2 +- VERSION | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 742f5885d..7700e9882 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v8.0.0 #### Changed @@ -8,8 +8,5 @@ #### Fixed - [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors -- [#1228](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1228) Enable identity insert on view's base table -- [#1238](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1238) Allow INSERT statements with SELECT notation -- [#1246](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1246) Fix queries with date and date-time placeholder conditions Please check [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-2-stable/CHANGELOG.md) for previous changes. diff --git a/README.md b/README.md index 33739ffad..1343132b9 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| Unreleased | `8.0.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.0.0` | `8.0.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | diff --git a/VERSION b/VERSION index 60145e843..ae9a76b92 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.0.alpha1 +8.0.0 From bbd315e0ade9ed8fe020b476c64921cc7d95724a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 11 Nov 2024 09:12:23 +0000 Subject: [PATCH 1322/1412] Typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1343132b9..29e2ed32b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `8.0.0` | `8.0.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.0.x` | `8.0.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | From 13ad9cd7d92fd28e3bc1d95cfc8d0e615200fae8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 20 Nov 2024 15:03:42 +0000 Subject: [PATCH 1323/1412] Rename Docker compose file (#1261) --- .devcontainer/{docker-compose.yml => compose.yaml} | 0 .devcontainer/devcontainer.json | 2 +- .github/workflows/ci.yml | 2 +- docker-compose.ci.yml => compose.ci.yaml | 0 4 files changed, 2 insertions(+), 2 deletions(-) rename .devcontainer/{docker-compose.yml => compose.yaml} (100%) rename docker-compose.ci.yml => compose.ci.yaml (100%) diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/compose.yaml similarity index 100% rename from .devcontainer/docker-compose.yml rename to .devcontainer/compose.yaml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 57e124ac5..d70e3e5f1 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,7 +1,7 @@ // For format details, see https://aka.ms/devcontainer.json. { "name": "ActiveRecord SQL Server Adapter project development", - "dockerComposeFile": "docker-compose.yml", + "dockerComposeFile": "compose.yaml", "service": "activerecord-sqlserver-adapter", "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bbdc3646c..5e3eeaf64 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. env: - COMPOSE_FILE: docker-compose.ci.yml + COMPOSE_FILE: compose.ci.yaml strategy: fail-fast: false diff --git a/docker-compose.ci.yml b/compose.ci.yaml similarity index 100% rename from docker-compose.ci.yml rename to compose.ci.yaml From 2ba0c8ab6066add994e9d744c3b5d8e681d686ce Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 23 Nov 2024 20:40:48 +0000 Subject: [PATCH 1324/1412] Fix distinct alias when multiple databases used (#1262) --- CHANGELOG.md | 12 +++++++++--- .../sqlserver/schema_statements.rb | 10 +++++++--- test/cases/adapter_test_sqlserver.rb | 9 +++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7700e9882..56cb70868 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,12 +1,18 @@ +## Unreleased + +#### Fixed + +- [#1262](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1262) Fix distinct alias when multiple databases used. + ## v8.0.0 #### Changed -- [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter -- [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1 +- [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter. +- [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1. #### Fixed -- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors +- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors. Please check [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-2-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index ba4541a52..1cc1a1b67 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -347,12 +347,16 @@ def add_timestamps(table_name, **options) def columns_for_distinct(columns, orders) order_columns = orders.reject(&:blank?).map { |s| - s = s.to_sql unless s.is_a?(String) + s = visitor.compile(s) unless s.is_a?(String) s.gsub(/\s+(?:ASC|DESC)\b/i, "") .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "") - }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" } + } + .reject(&:blank?) + .reject { |s| columns.include?(s) } - (order_columns << super).join(", ") + order_columns_aliased = order_columns.map.with_index { |column, i| "#{column} AS alias_#{i}" } + + (order_columns_aliased << super).join(", ") end def update_table_definition(table_name, base) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 28a310c51..96cc70bf0 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -616,4 +616,13 @@ def setup assert_equal Task.where("starting < ?", DateTime.current).count, 1 end end + + describe "distinct select query" do + it "generated SQL does not contain unnecessary alias projection" do + sqls = capture_sql do + Post.includes(:comments).joins(:comments).first + end + assert_no_match(/AS alias_0/, sqls.first) + end + end end From 6f3b0de0ee04754eb4402405cbad3cd72a6eb8d4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 12:16:29 +0000 Subject: [PATCH 1325/1412] Release v8.0.1 (#1263) --- .github/workflows/ci.yml | 4 ++-- CHANGELOG.md | 2 +- VERSION | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5e3eeaf64..3294d4a6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,8 +20,8 @@ jobs: fail-fast: false matrix: ruby: - - 3.3.4 - - 3.2.5 + - 3.3.6 + - 3.2.6 steps: - name: Checkout code diff --git a/CHANGELOG.md b/CHANGELOG.md index 56cb70868..c74508b03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v8.0.1 #### Fixed diff --git a/VERSION b/VERSION index ae9a76b92..cd1d2e94f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.0 +8.0.1 From b3b0c7d89a8fd3172da77bcf2fa5362165756db4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 12:33:15 +0000 Subject: [PATCH 1326/1412] Rails 8.1 branch --- CHANGELOG.md | 18 ++---------------- Dockerfile.ci | 2 +- README.md | 25 +++++++++++++------------ VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- compose.ci.yaml | 1 + 6 files changed, 19 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c74508b03..69928efed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,4 @@ -## v8.0.1 +## Unreleased -#### Fixed -- [#1262](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1262) Fix distinct alias when multiple databases used. - -## v8.0.0 - -#### Changed - -- [#1216](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1216) Refactor adapter interface to match abstract adapter. -- [#1225](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1225) Drop support to Ruby 3.1. - -#### Fixed - -- [#1215](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1215) Fix mismatched foreign key errors. - -Please check [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/7-2-stable/CHANGELOG.md) for previous changes. +Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/Dockerfile.ci b/Dockerfile.ci index 0ded95afd..74f636508 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=main bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/README.md b/README.md index 29e2ed32b..0285ae324 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,19 @@ maintenance group. See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|---------------|----------------|-------------------------------------------------------------------------------------------------| -| `8.0.x` | `8.0.x` | Active | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | -| `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|----------------|----------------------------------------------------------------------------------------------------| +| Unreleased | `8.1.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | +| `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | +| `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | #### Native Data Type Support diff --git a/VERSION b/VERSION index cd1d2e94f..b351275b1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.0.1 +8.1.0.alpha1 diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index d9fdebdf1..3a8933c0e 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.0.0" + spec.add_dependency "activerecord", "~> 8.1.0" spec.add_dependency "tiny_tds" end diff --git a/compose.ci.yaml b/compose.ci.yaml index cbc842619..f339681e7 100644 --- a/compose.ci.yaml +++ b/compose.ci.yaml @@ -5,6 +5,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver + - RAILS_BRANCH=main build: context: . dockerfile: Dockerfile.ci From 9503ec6c7c6e880664f7022c90524c5be66c5ad9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 12:41:59 +0000 Subject: [PATCH 1327/1412] Update activerecord-sqlserver-adapter.gemspec --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 3a8933c0e..d9fdebdf1 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.1.0" + spec.add_dependency "activerecord", "~> 8.0.0" spec.add_dependency "tiny_tds" end From 871689f8e294ca6cca1e05b4c7a178cfe453c70c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 15:02:07 +0000 Subject: [PATCH 1328/1412] Fix CI (#1266) --- .devcontainer/Dockerfile | 2 +- VERSION | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 116b439f1..6d4c1a2af 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/t RUN mkdir -p /tmp/activerecord-sqlserver-adapter COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ RUN cd /tmp/activerecord-sqlserver-adapter \ - && bundle install \ + && RAILS_BRANCH=main bundle install \ && rm -rf /tmp/activerecord-sqlserver-adapter RUN chown -R vscode:vscode /usr/local/rvm diff --git a/VERSION b/VERSION index b351275b1..cd8590d8d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.0.alpha1 +8.1.0.alpha diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index d9fdebdf1..118af47c1 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -27,6 +27,6 @@ Gem::Specification.new do |spec| spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.0.0" + spec.add_dependency "activerecord", "~> 8.1.0.alpha" spec.add_dependency "tiny_tds" end From 0afebac4aa1f353e74d63b8bbef6367acf4cb12d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 8 Dec 2024 18:09:44 +0000 Subject: [PATCH 1329/1412] Fix CI for Rails 8.1 (#1267) --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 314eacce1..304501834 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -3,6 +3,7 @@ require "tiny_tds" require "base64" require "active_record" +require "active_record/connection_adapters/statement_pool" require "arel_sqlserver" require "active_record/connection_adapters/sqlserver/core_ext/active_record" require "active_record/connection_adapters/sqlserver/core_ext/explain" From 9f8e99448d7a949a370e3316c10ebc2e4a2b6708 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 10 Dec 2024 11:59:08 +0000 Subject: [PATCH 1330/1412] Update coerced_tests.rb --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1ffa0694c..6e9f59127 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1489,6 +1489,9 @@ def test_reverse_arel_assoc_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order assert_equal topics(:second).title, topics.first.title end + + # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting expression column. + coerce_tests! %r{runs queries when using pick with expression column and empty IN} end module ActiveRecord From 6ddb0357d8765b1f241923bd53ed989616cf004f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 11 Dec 2024 11:18:31 +0000 Subject: [PATCH 1331/1412] Moved coercion to correct class --- test/cases/coerced_tests.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6e9f59127..5bb7bbd80 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1489,9 +1489,6 @@ def test_reverse_arel_assoc_order_with_function_coerced topics = Topic.order(Arel.sql("LEN(title)") => :asc).reverse_order assert_equal topics(:second).title, topics.first.title end - - # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting expression column. - coerce_tests! %r{runs queries when using pick with expression column and empty IN} end module ActiveRecord @@ -1506,6 +1503,9 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql assert_equal expected, query end + + # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting expression column. + coerce_tests! %r{runs queries when using pick with expression column and empty IN} end end From ecd3486ca60f7c3e84d25f746563036254121315 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 11 Dec 2024 11:39:33 +0000 Subject: [PATCH 1332/1412] Removed tests that no longer need to be coerced --- test/cases/coerced_tests.rb | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5bb7bbd80..0bdd9b2a0 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2200,35 +2200,6 @@ class EnumTest < ActiveRecord::TestCase end end -require "models/task" -class QueryCacheExpiryTest < ActiveRecord::TestCase - # SQL Server does not support skipping or upserting duplicates. - coerce_tests! :test_insert_all - def test_insert_all_coerced - assert_raises(ArgumentError, /does not support skipping duplicates/) do - Task.cache { Task.insert({ starting: Time.now }) } - end - - assert_raises(ArgumentError, /does not support upsert/) do - Task.cache { Task.upsert({ starting: Time.now }) } - end - - assert_raises(ArgumentError, /does not support upsert/) do - Task.cache { Task.upsert_all([{ starting: Time.now }]) } - end - - Task.cache do - assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do - Task.insert_all!([ starting: Time.now ]) - end - - assert_called(ActiveRecord::Base.connection_pool.query_cache, :clear, times: 1) do - Task.insert!({ starting: Time.now }) - end - end - end -end - require "models/citation" class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase fixtures :citations From a1bc1453b03d9a859d279333aa917d2b484c2540 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 11 Dec 2024 18:28:42 +0000 Subject: [PATCH 1333/1412] Add affected_rows to sql.active_record (#1268) --- .../sqlserver/database_statements.rb | 48 ++++++++++++------- test/cases/coerced_tests.rb | 7 +++ 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 9e36d61aa..fe286a268 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,18 +14,19 @@ def write_query?(sql) # :nodoc: end def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) - result = if id_insert_table_name = query_requires_identity_insert?(sql) - # If the table name is a view, we need to get the base table name for enabling identity insert. - id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name) + 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) - 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 + 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 notification_payload[:row_count] = result.count result end @@ -38,8 +39,18 @@ def cast_result(raw_result) end end + # Returns the affected rows from results. def affected_rows(raw_result) - raw_result.first['AffectedRows'] + 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 end def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false) @@ -53,7 +64,9 @@ def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow def internal_exec_sql_query(sql, conn) handle = internal_raw_execute(sql, conn) - handle_to_names_and_values(handle, ar_result: true) + results = handle_to_names_and_values(handle, ar_result: true) + + return results, affected_rows_from_results_or_handle(results, handle) ensure finish_statement_handle(handle) end @@ -432,12 +445,15 @@ def handle_to_names_and_values(handle, options = {}) end results = handle.each(query_options) - columns = handle.fields - # If query returns multiple result sets, only return the columns of the last one. - columns = columns.last if columns.any? && columns.all? { |e| e.is_a?(Array) } - columns = columns.map(&:downcase) if lowercase_schema_reflection + if options[:ar_result] + columns = handle.fields + columns = columns.last if columns.any? && columns.all? { |e| e.is_a?(Array) } # If query returns multiple result sets, only return the columns of the last one. + columns = columns.map(&:downcase) if lowercase_schema_reflection - options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results + ActiveRecord::Result.new(columns, results) + else + results + end end def finish_statement_handle(handle) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0bdd9b2a0..2bb8bee1f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -372,6 +372,13 @@ def test_payload_row_count_on_raw_sql_coerced Book.where(author_id: nil, name: 'row count book 3').delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end + + # Fix randomly failing test. The loading of the model's schema was affecting the test. + coerce_tests! :test_payload_affected_rows + def test_payload_affected_rows_coerced + Book.send(:load_schema!) + original_test_payload_affected_rows + end end end From 1d1ea09c2934efaf4a77fea2cede3ab1e67ee938 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 12 Dec 2024 11:21:23 +0000 Subject: [PATCH 1334/1412] Fix parsing of raw table name from SQL with extra parentheses --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/schema_test_sqlserver.rb | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 1cc1a1b67..b390b6cb1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -711,7 +711,7 @@ def get_raw_table_name(sql) elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] else - s.match(/FROM\s+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] + s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] end.strip end diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index f9dbde8e6..124c2d6dc 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -101,5 +101,11 @@ class SchemaTestSQLServer < ActiveRecord::TestCase assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "INSERT INTO [with].[select notation] SELECT * FROM [table_name]") end end + + describe 'CREATE VIEW statements' do + it do + assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))") + end + end end end From a06eeb467597c0f67f19fdcf66dfdd8016ce733b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 16 Dec 2024 09:31:34 +0000 Subject: [PATCH 1335/1412] SQL Server does not support 'restrict' for 'on_update' or 'on_delete' --- test/cases/coerced_tests.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 2bb8bee1f..9e1519196 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1190,6 +1190,9 @@ def test_add_on_delete_restrict_foreign_key_coerced end end + # SQL Server does not support 'restrict' for 'on_update' or 'on_delete'. + coerce_tests! :test_remove_foreign_key_with_restrict_action + # Error message depends on the database adapter. coerce_tests! :test_add_foreign_key_with_if_not_exists_not_set def test_add_foreign_key_with_if_not_exists_not_set_coerced From 6e75574b946d3206fac44bc110aab44eac0d1439 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Wed, 18 Dec 2024 17:38:19 +0100 Subject: [PATCH 1336/1412] TinyTDS v3+ is now required (#1273) --- CHANGELOG.md | 4 ++++ activerecord-sqlserver-adapter.gemspec | 2 +- .../connection_adapters/sqlserver/database_statements.rb | 5 ----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69928efed..856e2a85b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,8 @@ ## Unreleased +#### Changed + +- [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. + Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 118af47c1..11b377608 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -28,5 +28,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "activerecord", "~> 8.1.0.alpha" - spec.add_dependency "tiny_tds" + spec.add_dependency "tiny_tds", "~> 3" end diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index fe286a268..8c99de570 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -461,13 +461,8 @@ def finish_statement_handle(handle) handle end - # TinyTDS returns false instead of raising an exception if connection fails. - # Getting around this by raising an exception ourselves while PR - # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released. def internal_raw_execute(sql, raw_connection, perform_do: false) result = raw_connection.execute(sql) - raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass) - perform_do ? result.do : result end end From 2c9aad100215bc3c0bac06b001b00bd6b2cfa157 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 19 Dec 2024 10:19:36 +0000 Subject: [PATCH 1337/1412] Coerce test (#1274) --- test/cases/coerced_tests.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 9e1519196..d1fd4e806 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -383,6 +383,22 @@ def test_payload_affected_rows_coerced end class CalculationsTest < ActiveRecord::TestCase + # SELECT columns must be in the GROUP clause. + coerce_tests! :test_should_count_with_group_by_qualified_name_on_loaded + def test_should_count_with_group_by_qualified_name_on_loaded_coerced + accounts = Account.group("accounts.id").select("accounts.id") + + expected = { 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5 => 1, 6 => 1 } + + assert_not_predicate accounts, :loaded? + assert_equal expected, accounts.count + + accounts.load + + assert_predicate accounts, :loaded? + assert_equal expected, accounts.count(:id) + end + # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_offset_is_kept def test_offset_is_kept_coerced From fb20aa5887ea8312d28a965cf5fe2a27812f952f Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Fri, 20 Dec 2024 20:14:10 +0100 Subject: [PATCH 1338/1412] Remove special handling for older TDS connections (#1275) `tiny_tds` v3 will raise an exception when using a TDS version older than v7.3 for the server connections. This is mostly to enforce that people do no longer use `tiny_tds` with ancient SQL servers (and report issues). But therefore, we can remove a couple of special cases in the `sqlserver-adapter` tests where they behaved differently if ran against older versions. --- test/cases/column_test_sqlserver.rb | 7 +------ test/cases/schema_dumper_test_sqlserver.rb | 16 ++++++---------- test/support/connection_reflection.rb | 5 ----- 3 files changed, 7 insertions(+), 21 deletions(-) diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index dfbd42e47..e717a5532 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -277,7 +277,7 @@ def assert_obj_set_and_save(attribute, value) _(col.sql_type).must_equal "date" _(col.type).must_equal :date _(col.null).must_equal true - _(col.default).must_equal connection_tds_73 ? Date.civil(1, 1, 1) : "0001-01-01" + _(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 = connection.lookup_cast_type_from_column(col) @@ -357,7 +357,6 @@ def assert_obj_set_and_save(attribute, value) end it "datetime2" do - skip "datetime2 not supported in this protocol version" unless connection_tds_73 col = column("datetime2_7") _(col.sql_type).must_equal "datetime2(7)" _(col.type).must_equal :datetime @@ -422,7 +421,6 @@ def assert_obj_set_and_save(attribute, value) end it "datetimeoffset" do - skip "datetimeoffset not supported in this protocol version" unless connection_tds_73 col = column("datetimeoffset_7") _(col.sql_type).must_equal "datetimeoffset(7)" _(col.type).must_equal :datetimeoffset @@ -491,7 +489,6 @@ def assert_obj_set_and_save(attribute, value) end it "time(7)" do - skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_7") _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time @@ -523,7 +520,6 @@ def assert_obj_set_and_save(attribute, value) end it "time(2)" do - skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_2") _(col.sql_type).must_equal "time(2)" _(col.type).must_equal :time @@ -553,7 +549,6 @@ def assert_obj_set_and_save(attribute, value) end it "time using default precision" do - skip "time() not supported in this protocol version" unless connection_tds_73 col = column("time_default") _(col.sql_type).must_equal "time(7)" _(col.type).must_equal :time diff --git a/test/cases/schema_dumper_test_sqlserver.rb b/test/cases/schema_dumper_test_sqlserver.rb index 6e7140ddf..9e02d4b26 100644 --- a/test/cases/schema_dumper_test_sqlserver.rb +++ b/test/cases/schema_dumper_test_sqlserver.rb @@ -28,17 +28,13 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase # 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" - if connection_tds_73 - 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 - end + 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" - if connection_tds_73 - 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" - end + 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" diff --git a/test/support/connection_reflection.rb b/test/support/connection_reflection.rb index b7fe15ce2..d41605ef5 100644 --- a/test/support/connection_reflection.rb +++ b/test/support/connection_reflection.rb @@ -15,11 +15,6 @@ def connection_options connection.instance_variable_get :@connection_parameters end - def connection_tds_73 - rc = connection.raw_connection - rc.respond_to?(:tds_73?) && rc.tds_73? - end - def connection_sqlserver_azure? connection.sqlserver_azure? end From 4196d90410ae4136d2023452a2fd2273473898d7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 1 Jan 2025 20:42:16 +0000 Subject: [PATCH 1339/1412] Rename test (#1282) --- test/cases/coerced_tests.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index d1fd4e806..060d5a30a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1569,16 +1569,16 @@ def test_named_bind_with_literal_colons_coerced class SchemaDumperTest < ActiveRecord::TestCase # Use nvarchar string (N'') in assert - coerce_tests! :test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order - def test_dump_schema_information_outputs_lexically_reverse_ordered_versions_regardless_of_database_order_coerced + 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.shuffle.each do |v| @schema_migration.create_version(v) end - schema_info = ActiveRecord::Base.lease_connection.dump_schema_information + schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions expected = <<~STR - INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES + INSERT INTO #{quote_table_name("schema_migrations")} (version) VALUES (N'20100301010101'), (N'20100201010101'), (N'20100101010101'); From d17094d32eb3eef5c0ef5a0d2b97af02bb868cfa Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 5 Jan 2025 16:38:34 +0000 Subject: [PATCH 1340/1412] Remove unused method (#1284) --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 7 ------- 1 file changed, 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 304501834..746db1a57 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -324,13 +324,6 @@ def version self.class::VERSION end - def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil) - result = from_clause + join_clause + where_clause + having_clause - result << offset if offset - result << limit if limit - result - end - def get_database_version # :nodoc: version_year end From c7f4cf5ca2d2e5b1d9f0420111f3fe3ed864acb0 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Thu, 9 Jan 2025 16:09:04 +0100 Subject: [PATCH 1341/1412] Limit `tiny_tds` to v3 (#1289) --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 11b377608..071a2ceb9 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -28,5 +28,5 @@ Gem::Specification.new do |spec| spec.require_paths = ["lib"] spec.add_dependency "activerecord", "~> 8.1.0.alpha" - spec.add_dependency "tiny_tds", "~> 3" + spec.add_dependency "tiny_tds", "~> 3.0" end From c8ea8ce506465105c9336f94067245fe0862e279 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Jan 2025 15:58:30 +0000 Subject: [PATCH 1342/1412] Migrate ActiveRecord::Normalization to Active Model (#1290) --- .../connection_adapters/sqlserver/database_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 8c99de570..7857b6eae 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -337,7 +337,7 @@ def sp_executesql_types_and_parameters(binds) def sp_executesql_sql_type(attr) if attr.respond_to?(:type) - type = attr.type.is_a?(ActiveRecord::Normalization::NormalizedValueType) ? attr.type.cast_type : attr.type + type = attr.type.is_a?(ActiveModel::Attributes::Normalization::NormalizedValueType) ? attr.type.cast_type : attr.type type = type.subtype if type.serialized? return type.sqlserver_type if type.respond_to?(:sqlserver_type) From 5e3eb2660c9bf8691e3b4f39c58844c769a37a6a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 9 Jan 2025 16:04:34 +0000 Subject: [PATCH 1343/1412] Added Ruby 3.4.1 to the CI matrix (#1291) --- .github/workflows/ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3294d4a6d..7eb8439a4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,7 @@ jobs: fail-fast: false matrix: ruby: + - 3.4.1 - 3.3.6 - 3.2.6 From 43737b8caa77d329ab25eb9edd9998ccd7c1a6bf Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Feb 2025 13:41:41 +0000 Subject: [PATCH 1344/1412] Include cast type in column (#1292) --- .../sqlserver/schema_creation.rb | 2 +- .../sqlserver/schema_statements.rb | 5 +- test/cases/coerced_tests.rb | 17 ++-- test/cases/column_test_sqlserver.rb | 82 +++++++++---------- 4 files changed, 57 insertions(+), 49 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index f6d0e400a..21479662f 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -50,7 +50,7 @@ def visit_CreateIndexDefinition(o) end def add_column_options!(sql, options) - sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) + sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options) if options[:collation].present? sql << " COLLATE #{options[:collation]}" end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index b390b6cb1..781b0f0ed 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -70,8 +70,10 @@ def columns(table_name) column_definitions(table_name).map do |ci| sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options + new_column( ci[:name], + lookup_cast_type(ci[:type]), ci[:default_value], sql_type_metadata, ci[:null], @@ -83,9 +85,10 @@ def columns(table_name) end end - def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + def new_column(name, cast_type, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) SQLServer::Column.new( name, + cast_type, default, sql_type_metadata, null, diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 060d5a30a..58d760341 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -570,8 +570,8 @@ def test_create_table_with_defaults_coerce five = columns.detect { |c| c.name == "five" } assert_equal "hello", one.default - assert_equal true, connection.lookup_cast_type_from_column(two).deserialize(two.default) - assert_equal false, connection.lookup_cast_type_from_column(three).deserialize(three.default) + assert_equal true, two.fetch_cast_type(connection).deserialize(two.default) + assert_equal false, three.fetch_cast_type(connection).deserialize(three.default) assert_equal 1, four.default assert_equal "hello", five.default end @@ -1971,12 +1971,17 @@ def with_marshable_time_defaults # Revert changes @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present? end - - # We need to give the full path for this to work. - undef_method :schema_dump_path - def schema_dump_path + + # We need to give the full paths for this to work. + undef_method :schema_dump_5_1_path + def schema_dump_5_1_path File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_5_1.yml") end + + undef_method :schema_dump_8_0_path + def schema_dump_8_0_path + File.join(ARTest::SQLServer.root_activerecord, "test/assets/schema_dump_8_0.yml") + end end end end diff --git a/test/cases/column_test_sqlserver.rb b/test/cases/column_test_sqlserver.rb index e717a5532..5da2ab4e4 100644 --- a/test/cases/column_test_sqlserver.rb +++ b/test/cases/column_test_sqlserver.rb @@ -42,7 +42,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.bigint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::BigInteger _(type.limit).must_equal 8 assert_obj_set_and_save :bigint, -9_223_372_036_854_775_808 @@ -57,7 +57,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.int).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Integer _(type.limit).must_equal 4 assert_obj_set_and_save :int, -2_147_483_648 @@ -72,7 +72,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.smallint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallInteger _(type.limit).must_equal 2 assert_obj_set_and_save :smallint, -32_768 @@ -87,7 +87,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 42 _(obj.tinyint).must_equal 42 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::TinyInteger _(type.limit).must_equal 1 assert_obj_set_and_save :tinyint, 0 @@ -102,7 +102,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal true _(obj.bit).must_equal true _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Boolean _(type.limit).must_be_nil obj.bit = 0 @@ -123,7 +123,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("12345.01") _(obj.decimal_9_2).must_equal BigDecimal("12345.01") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 9 @@ -140,7 +140,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("1234567.89") _(obj.decimal_16_4).must_equal BigDecimal("1234567.89") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type.precision).must_equal 16 _(type.scale).must_equal 4 obj.decimal_16_4 = "1234567.8901001" @@ -158,7 +158,7 @@ def assert_obj_set_and_save(attribute, value) _(obj.numeric_18_0).must_equal BigDecimal("191") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DecimalWithoutScale _(type.limit).must_be_nil _(type.precision).must_equal 18 @@ -179,7 +179,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("12345678901234567890.01") _(obj.numeric_36_2).must_equal BigDecimal("12345678901234567890.01") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Decimal _(type.limit).must_be_nil _(type.precision).must_equal 36 @@ -198,7 +198,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("4.20") _(obj.money).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Money _(type.limit).must_be_nil _(type.precision).must_equal 19 @@ -217,7 +217,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal BigDecimal("4.20") _(obj.smallmoney).must_equal BigDecimal("4.20") _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallMoney _(type.limit).must_be_nil _(type.precision).must_equal 10 @@ -240,7 +240,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal 123.00000001 _(obj.float).must_equal 123.00000001 _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Float _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -259,7 +259,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Real _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -280,7 +280,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Date _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -319,7 +319,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DateTime _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -365,14 +365,14 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DateTime2 _(type.limit).must_be_nil _(type.precision).must_equal 7 _(type.scale).must_be_nil obj.save! _(obj).must_equal obj.class.where(datetime2_7: time).first - # Can save 100 nanosecond precisoins and return again. + # Can save 100 nanosecond precisions and return again. time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456755, 1000) time2 = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456800, 1000) obj.datetime2_7 = time @@ -383,7 +383,7 @@ def assert_obj_set_and_save(attribute, value) _(obj.datetime2_7).must_equal time2, "Nanoseconds were <#{obj.datetime2_7.nsec}> vs <123456800>" _(obj).must_equal obj.class.where(datetime2_7: time).first _(obj).must_equal obj.class.where(datetime2_7: time2).first - # Can save small fraction nanosecond precisoins and return again. + # Can save small fraction nanosecond precisions and return again. time = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15020, 1000) time2 = Time.utc 2008, 6, 21, 13, 30, 0, Rational(15000, 1000) obj.datetime2_7 = time @@ -395,7 +395,7 @@ def assert_obj_set_and_save(attribute, value) # datetime2_3 time = Time.utc 9999, 12, 31, 23, 59, 59, Rational(123456789, 1000) col = column("datetime2_3") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 3 + _(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 @@ -403,7 +403,7 @@ def assert_obj_set_and_save(attribute, value) _(obj).must_equal obj.class.where(datetime2_3: time).first # datetime2_1 col = column("datetime2_1") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 1 + _(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 @@ -411,7 +411,7 @@ def assert_obj_set_and_save(attribute, value) _(obj).must_equal obj.class.where(datetime2_1: time).first # datetime2_0 col = column("datetime2_0") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 0 + _(col.fetch_cast_type(connection).precision).must_equal 0 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>" @@ -428,7 +428,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::DateTimeOffset _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -453,13 +453,13 @@ def assert_obj_set_and_save(attribute, value) # With other precisions. time = ActiveSupport::TimeZone["America/Los_Angeles"].local 2010, 12, 31, 23, 59, 59, Rational(123456755, 1000) col = column("datetimeoffset_3") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 3 + _(col.fetch_cast_type(connection).precision).must_equal 3 obj.datetimeoffset_3 = time _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" obj.save! _(obj.datetimeoffset_3).must_equal time.change(nsec: 123000000), "Nanoseconds were <#{obj.datetimeoffset_3.nsec}> vs <123000000>" col = column("datetime2_1") - _(connection.lookup_cast_type_from_column(col).precision).must_equal 1 + _(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! @@ -474,7 +474,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::SmallDateTime _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -495,7 +495,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -526,7 +526,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 2 @@ -555,7 +555,7 @@ def assert_obj_set_and_save(attribute, value) _(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 = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Time _(type.limit).must_be_nil _(type.precision).must_equal 7 @@ -589,7 +589,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "1234567890" _(obj.char_10).must_equal "1234567890" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Char _(type.limit).must_equal 10 _(type.precision).must_be_nil @@ -609,7 +609,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test varchar_50" _(obj.varchar_50).must_equal "test varchar_50" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Varchar _(type.limit).must_equal 50 _(type.precision).must_be_nil @@ -626,7 +626,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test varchar_max" _(obj.varchar_max).must_equal "test varchar_max" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::VarcharMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -643,7 +643,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test text" _(obj.text).must_equal "test text" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Text _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -662,7 +662,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "12345678åå" _(obj.nchar_10).must_equal "12345678åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeChar _(type.limit).must_equal 10 _(type.precision).must_be_nil @@ -682,7 +682,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test nvarchar_50 åå" _(obj.nvarchar_50).must_equal "test nvarchar_50 åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeVarchar _(type.limit).must_equal 50 _(type.precision).must_be_nil @@ -699,7 +699,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test nvarchar_max åå" _(obj.nvarchar_max).must_equal "test nvarchar_max åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeVarcharMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -716,7 +716,7 @@ def assert_obj_set_and_save(attribute, value) _(col.default).must_equal "test ntext åå" _(obj.ntext).must_equal "test ntext åå" _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::UnicodeText _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -737,7 +737,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Binary _(type.limit).must_equal 49 _(type.precision).must_be_nil @@ -758,7 +758,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Varbinary _(type.limit).must_equal 49 _(type.precision).must_be_nil @@ -779,7 +779,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::VarbinaryMax _(type.limit).must_equal 2_147_483_647 _(type.precision).must_be_nil @@ -798,7 +798,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_equal "newid()" - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Uuid _(type.limit).must_be_nil _(type.precision).must_be_nil @@ -821,7 +821,7 @@ def assert_obj_set_and_save(attribute, value) _(col.null).must_equal true _(col.default).must_be_nil _(col.default_function).must_be_nil - type = connection.lookup_cast_type_from_column(col) + type = col.fetch_cast_type(connection) _(type).must_be_instance_of Type::Timestamp _(type.limit).must_be_nil _(type.precision).must_be_nil From f3c4d3307d53ba8709c222e4abe835713f3f40dd Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Feb 2025 14:57:58 +0000 Subject: [PATCH 1345/1412] Support joins in update_all (#1293) --- lib/arel/visitors/sqlserver.rb | 63 +++++++++++++++++++++++++++------- test/cases/coerced_tests.rb | 27 +++++++++------ 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 5890b7fc3..a9e8609ba 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -29,9 +29,56 @@ def visit_Arel_Nodes_Concat(o, collector) visit o.right, collector end + # Same as SQLite and PostgreSQL. def visit_Arel_Nodes_UpdateStatement(o, collector) - if has_join_and_composite_primary_key?(o) - update_statement_using_join(o, collector) + collector.retryable = false + o = prepare_update_statement(o) + + collector << "UPDATE " + + # UPDATE with JOIN is in the form of: + # + # UPDATE t1 + # SET .. + # FROM t2 + # WHERE t1.join_id = t2.join_id + # + # Or if more than one join is present: + # + # UPDATE t1 + # SET .. + # FROM t2 + # JOIN t3 ON t2.join_id = t3.join_id + # WHERE t1.join_id = t2.join_id + if has_join_sources?(o) + visit o.relation.left, collector + collect_nodes_for o.values, collector, " SET " + collector << " FROM " + first_join, *remaining_joins = o.relation.right + visit first_join.left, collector + + if remaining_joins && !remaining_joins.empty? + collector << " " + remaining_joins.each do |join| + visit join, collector + end + end + + collect_nodes_for [first_join.right.expr] + o.wheres, collector, " WHERE ", " AND " + else + collector = visit o.relation, collector + collect_nodes_for o.values, collector, " SET " + collect_nodes_for o.wheres, collector, " WHERE ", " AND " + end + + collect_nodes_for o.orders, collector, " ORDER BY " + maybe_visit o.limit, collector + end + + # Same as PostgreSQL except we need to add limit if using subquery. + def prepare_update_statement(o) + if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) + o else o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? @@ -39,6 +86,7 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) end end + def visit_Arel_Nodes_DeleteStatement(o, collector) if has_join_and_composite_primary_key?(o) delete_statement_using_join(o, collector) @@ -61,17 +109,6 @@ def delete_statement_using_join(o, collector) collect_nodes_for o.wheres, collector, " WHERE ", " AND " end - def update_statement_using_join(o, collector) - collector.retryable = false - - collector << "UPDATE " - visit o.relation.left, collector - collect_nodes_for o.values, collector, " SET " - collector << " FROM " - visit o.relation, collector - collect_nodes_for o.wheres, collector, " WHERE ", " AND " - end - def visit_Arel_Nodes_Lock(o, collector) o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/ collector << " " diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 58d760341..4da87a0cf 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1303,18 +1303,25 @@ def test_update_coerced require "models/author" class UpdateAllTest < ActiveRecord::TestCase - # Rails test required updating a identity column. + # Regular expression slightly different. coerce_tests! :test_update_all_doesnt_ignore_order def test_update_all_doesnt_ignore_order_coerced - david, mary = authors(:david), authors(:mary) - _(david.id).must_equal 1 - _(mary.id).must_equal 2 - _(david.name).wont_equal mary.name - assert_queries_match(/UPDATE.*\(SELECT \[authors\].\[id\] FROM \[authors\].*ORDER BY \[authors\].\[id\]/i) do - Author.where("[id] > 1").order(:id).update_all(name: "Test") + assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error + test_update_with_order_succeeds = lambda do |order| + Author.order(order).update_all("id = id + 1") + rescue ActiveRecord::ActiveRecordError + false + end + + if test_update_with_order_succeeds.call("id DESC") + # test that this wasn't a fluke and using an incorrect order results in an exception + assert_not test_update_with_order_succeeds.call("id ASC") + else + # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead + assert_queries_match(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC.*\)/i) do + test_update_with_order_succeeds.call("id DESC") + end end - _(david.reload.name).must_equal "David" - _(mary.reload.name).must_equal "Test" end # SELECT columns must be in the GROUP clause. @@ -1971,7 +1978,7 @@ def with_marshable_time_defaults # Revert changes @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present? end - + # We need to give the full paths for this to work. undef_method :schema_dump_5_1_path def schema_dump_5_1_path From b26d642dd117d8060eee6a887a77c9a31d50a460 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Feb 2025 15:02:57 +0000 Subject: [PATCH 1346/1412] Exist queries are retryable (#1294) --- .../sqlserver/core_ext/finder_methods.rb | 2 +- test/cases/coerced_tests.rb | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 db2d06708..8c5fc8a85 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 @@ -33,7 +33,7 @@ def _construct_relation_for_exists(conditions) end # End of monkey-patch else - relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1) + relation = except(:select, :distinct, :order)._select!(Arel.sql(::ActiveRecord::FinderMethods::ONE_AS_ONE, retryable: true)).limit!(1) end case conditions diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 4da87a0cf..0b30b7d3e 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2803,3 +2803,14 @@ def test_with_recursive_coerced end end +module ActiveRecord + class AdapterConnectionTest < ActiveRecord::TestCase + # Original method defined for core adapters. + undef_method :raw_transaction_open? + def raw_transaction_open?(connection) + connection.instance_variable_get(:@raw_connection).query("SELECT @@trancount").to_a[0][0] > 0 + rescue + false + end + end +end From c45f7c4b79d9de428cd203fd8c6edc32f17de5b6 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 11 Feb 2025 11:06:21 +0000 Subject: [PATCH 1347/1412] Fix for checking if raw connection transaction open (#1296) --- test/cases/coerced_tests.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0b30b7d3e..b9b511433 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2805,10 +2805,11 @@ def test_with_recursive_coerced module ActiveRecord class AdapterConnectionTest < ActiveRecord::TestCase - # Original method defined for core adapters. + # Original method only handled the core adapters. undef_method :raw_transaction_open? def raw_transaction_open?(connection) - connection.instance_variable_get(:@raw_connection).query("SELECT @@trancount").to_a[0][0] > 0 + transaction_count = connection.instance_variable_get(:@raw_connection).execute("SELECT @@TRANCOUNT AS TRANSACTION_COUNT").first["TRANSACTION_COUNT"] + transaction_count > 0 rescue false end From 6f89d229fae05b497f68537023d8e7bebb9c2505 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 12 Feb 2025 12:22:09 +0000 Subject: [PATCH 1348/1412] Coerce test (#1297) --- test/cases/coerced_tests.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index b9b511433..1de193222 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1537,8 +1537,11 @@ def test_does_not_duplicate_optimizer_hints_on_merge_coerced assert_equal expected, query end - # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting expression column. - coerce_tests! %r{runs queries when using pick with expression column and empty IN} + # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting non-aggregate expression column. + coerce_tests! %r{no queries when using pick with non-aggregate expression and empty IN} + + # Order column must be in the GROUP clause. However, with implicit ordering we can't test this when selecting aggregate expression column. + coerce_tests! %r{runs queries when using pick with aggregate expression despite empty IN} end end From 6e287b8933976c55f5b5d430a297ab1347b39007 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 12 Feb 2025 14:10:47 +0000 Subject: [PATCH 1349/1412] Coerce test as cast type in SQL Server is varchar (#1298) --- test/cases/coerced_tests.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1de193222..6c5016bd9 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1961,6 +1961,20 @@ def test_marshal_dump_and_load_via_disk_coerced end end + # 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 + def test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one + cache = load_bound_reflection(schema_dump_8_0_path) + + assert_no_queries do + columns = cache.columns_hash("courses") + assert_equal 3, columns.size + cast_type = columns["name"].fetch_cast_type(@connection) + assert_not_nil cast_type, "expected cast_type to be present" + assert_equal :varchar, cast_type.type + end + end + private def with_marshable_time_defaults From 01239aa8cad3cf372e99faf2336e2d48734967c7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 12 Feb 2025 14:41:33 +0000 Subject: [PATCH 1350/1412] Remove workarounds for old Rubies (#1299) --- test/cases/coerced_tests.rb | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 6c5016bd9..517775446 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1948,19 +1948,6 @@ 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/ - # Ruby 2.5 and 2.6 have issues to marshal Time before 1900. 2012.sql has one column with default value 1753 - coerce_tests! :test_marshal_dump_and_load_with_gzip, :test_marshal_dump_and_load_via_disk - - # Tests fail on Windows AppVeyor CI with 'Permission denied' error when renaming file during `File.atomic_write` call. - unless RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ - def test_marshal_dump_and_load_with_gzip_coerced - with_marshable_time_defaults { original_test_marshal_dump_and_load_with_gzip } - end - def test_marshal_dump_and_load_via_disk_coerced - with_marshable_time_defaults { original_test_marshal_dump_and_load_via_disk } - end - end - # 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 def test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one @@ -1977,25 +1964,6 @@ def test_yaml_load_8_0_dump_without_cast_type_still_get_the_right_one private - def with_marshable_time_defaults - # Detect problems - if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.7") - column = @connection.columns(:sst_datatypes).find { |c| c.name == "datetime" } - current_default = column.default if column.default.is_a?(Time) && column.default.year < 1900 - end - - # Correct problems - if current_default.present? - @connection.change_column_default(:sst_datatypes, :datetime, current_default.dup.change(year: 1900)) - end - - # Run original test - yield - ensure - # Revert changes - @connection.change_column_default(:sst_datatypes, :datetime, current_default) if current_default.present? - end - # We need to give the full paths for this to work. undef_method :schema_dump_5_1_path def schema_dump_5_1_path From 84831e68a5248283b7424e70dce52ed0337b2461 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 13 Feb 2025 09:46:32 +0000 Subject: [PATCH 1351/1412] Use `standardrb` for code formatting (#1300) --- .github/workflows/ci.yml | 24 + Gemfile | 14 +- Guardfile | 4 +- Rakefile | 2 +- activerecord-sqlserver-adapter.gemspec | 27 +- compose.ci.yaml | 7 + .../sqlserver/core_ext/attribute_methods.rb | 2 +- .../sqlserver/core_ext/explain.rb | 3 +- .../sqlserver/core_ext/finder_methods.rb | 8 +- .../sqlserver/database_statements.rb | 133 +++-- .../sqlserver/database_tasks.rb | 7 +- .../connection_adapters/sqlserver/quoting.rb | 14 +- .../sqlserver/schema_statements.rb | 248 ++++----- .../connection_adapters/sqlserver/showplan.rb | 10 +- .../sqlserver/showplan/printer_table.rb | 4 +- .../sqlserver/sql_type_metadata.rb | 9 +- .../sqlserver/type/data.rb | 6 +- .../sqlserver/type/date.rb | 6 +- .../sqlserver/type/datetime.rb | 7 +- .../sqlserver/type/smalldatetime.rb | 2 +- .../sqlserver/type/time.rb | 10 +- .../sqlserver/type/time_value_fractional.rb | 2 +- .../sqlserver/type/uuid.rb | 2 - .../connection_adapters/sqlserver/utils.rb | 22 +- .../connection_adapters/sqlserver_adapter.rb | 126 +++-- .../connection_adapters/sqlserver_column.rb | 11 +- .../tasks/sqlserver_database_tasks.rb | 10 +- lib/arel/visitors/sqlserver.rb | 28 +- test/cases/active_schema_test_sqlserver.rb | 68 ++- test/cases/adapter_test_sqlserver.rb | 123 +++-- test/cases/coerced_tests.rb | 271 ++++----- test/cases/column_test_sqlserver.rb | 513 +++++++++--------- test/cases/connection_test_sqlserver.rb | 26 +- test/cases/enum_test_sqlserver.rb | 17 +- .../cases/execute_procedure_test_sqlserver.rb | 2 +- test/cases/fetch_test_sqlserver.rb | 2 +- test/cases/helper_sqlserver.rb | 6 +- test/cases/index_test_sqlserver.rb | 14 +- test/cases/json_test_sqlserver.rb | 16 +- test/cases/lateral_test_sqlserver.rb | 4 +- test/cases/migration_test_sqlserver.rb | 24 +- .../pessimistic_locking_test_sqlserver.rb | 12 +- test/cases/primary_keys_test_sqlserver.rb | 8 +- test/cases/rake_test_sqlserver.rb | 22 +- test/cases/schema_dumper_test_sqlserver.rb | 218 ++++---- test/cases/schema_test_sqlserver.rb | 14 +- test/cases/transaction_test_sqlserver.rb | 14 +- test/cases/trigger_test_sqlserver.rb | 2 +- test/cases/utils_test_sqlserver.rb | 6 +- test/cases/view_test_sqlserver.rb | 22 +- ...ate_clients_and_change_column_collation.rb | 4 +- test/models/sqlserver/edge_schema.rb | 4 +- test/schema/sqlserver_specific_schema.rb | 86 +-- test/support/coerceable_test_sqlserver.rb | 20 +- test/support/query_assertions.rb | 6 +- test/support/rake_helpers.rb | 16 +- 56 files changed, 1204 insertions(+), 1084 deletions(-) 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 From 9559629657d475d6a5ee642886e1e6a8ac27538d Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 15 Feb 2025 17:07:45 +0000 Subject: [PATCH 1352/1412] Only generate an UPDATE/FROM if the first join is an INNER JOIN https://github.com/rails/rails/pull/54530 --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 68c7cb3ee..97da8bbfe 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -79,7 +79,7 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) # Same as PostgreSQL except we need to add limit if using subquery. def prepare_update_statement(o) - if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) + if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && (o.relation.right.first.is_a?(Arel::Nodes::InnerJoin)) o else o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? From 8d4c6bc79d953e657a4c3e502c64087139545914 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 15 Feb 2025 17:09:14 +0000 Subject: [PATCH 1353/1412] Add support for `INDEX INCLUDE` (#1301) --- CHANGELOG.md | 4 +++ .../sqlserver/schema_creation.rb | 7 ++++ .../sqlserver/schema_statements.rb | 32 ++++++++++++++++++- .../connection_adapters/sqlserver_adapter.rb | 4 +++ 4 files changed, 46 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 856e2a85b..8c8b5e367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +#### Added + +- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. + #### Changed - [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 21479662f..866d2fab3 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -6,6 +6,8 @@ module SQLServer class SchemaCreation < SchemaCreation private + delegate :quoted_include_columns_for_index, to: :@conn + def supports_index_using? false end @@ -44,11 +46,16 @@ def visit_CreateIndexDefinition(o) sql << "INDEX" sql << "#{quote_column_name(index.name)} ON #{quote_table_name(index.table)}" sql << "(#{quoted_columns(index)})" + sql << "INCLUDE (#{quoted_include_columns(index.include)})" if supports_index_include? && index.include sql << "WHERE #{index.where}" if index.where sql.join(" ") end + def quoted_include_columns(o) + (String === o) ? o : quoted_include_columns_for_index(o) + end + def add_column_options!(sql, options) sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options) if options[:collation].present? diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 9627a4fc5..c8382cc92 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -49,6 +49,7 @@ def indexes(table_name) name = index["index_name"] unique = index["index_description"].match?(/unique/) where = select_value("SELECT [filter_definition] FROM sys.indexes WHERE name = #{quote(name)}", "SCHEMA") + include_columns = index_include_columns(table_name, name) orders = {} columns = [] @@ -63,11 +64,31 @@ def indexes(table_name) columns << column end - indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders) + indexes << IndexDefinition.new(table_name, name, unique, columns, where: where, orders: orders, include: include_columns.presence) end end end + def index_include_columns(table_name, index_name) + sql = <<~SQL + SELECT + ic.index_id, + c.name AS column_name + FROM + sys.indexes i + JOIN + sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id + JOIN + sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id + WHERE + i.object_id = OBJECT_ID('#{table_name}') + AND i.name = '#{index_name}' + AND ic.is_included_column = 1; + SQL + + select_all(sql, "SCHEMA").map { |row| row["column_name"] } + end + def columns(table_name) return [] if table_name.blank? @@ -421,6 +442,15 @@ def schema_names query_values(sql, "SCHEMA") end + def quoted_include_columns_for_index(column_names) # :nodoc: + return quote_column_name(column_names) if column_names.is_a?(Symbol) + + quoted_columns = column_names.each_with_object({}) do |name, result| + result[name.to_sym] = quote_column_name(name).dup + end + add_options_for_index_columns(quoted_columns).values.join(", ") + end + private def data_source_sql(name = nil, type: nil) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 39d0927fb..170f3286b 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -143,6 +143,10 @@ def supports_partial_index? true end + def supports_index_include? + true + end + def supports_expression_index? false end From 8286984f474b7035d8c27f6f9612967447e2f4a4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 15 Feb 2025 17:11:18 +0000 Subject: [PATCH 1354/1412] Formatting --- lib/arel/visitors/sqlserver.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 97da8bbfe..69fad62fa 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -79,7 +79,7 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) # Same as PostgreSQL except we need to add limit if using subquery. def prepare_update_statement(o) - if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && (o.relation.right.first.is_a?(Arel::Nodes::InnerJoin)) + if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && o.relation.right.first.is_a?(Arel::Nodes::InnerJoin) o else o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? From df2630fc82769835a432e5ae970e23c40495d208 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 19 Feb 2025 11:14:54 +0000 Subject: [PATCH 1355/1412] Run CI everyday --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e82c9161..d59408e7b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ main ] schedule: - - cron: '0 4 * * 1' + - cron: '0 4 * * *' jobs: test: From 900e69cbd08726ec90c128815eb17aacf6a025be Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 19 Feb 2025 13:47:20 +0000 Subject: [PATCH 1356/1412] Fix warning about symbol to string conversion being frozen in future (#1302) --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c8382cc92..5f1e0f6dd 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -336,7 +336,7 @@ def type_to_sql(type, limit: nil, precision: nil, scale: nil, **) 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 - column_type_sql = type.to_s + column_type_sql = type.to_s.dup if precision if (0..7) === precision column_type_sql << "(#{precision})" From 5832c040fe0fbfbc700a49686300a056e55d2796 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 19 Feb 2025 14:18:05 +0000 Subject: [PATCH 1357/1412] Fix randomly failing test --- test/cases/coerced_tests.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cd7a9d499..11d980311 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -389,7 +389,7 @@ def test_payload_row_count_on_raw_sql_coerced # Fix randomly failing test. The loading of the model's schema was affecting the test. coerce_tests! :test_payload_affected_rows def test_payload_affected_rows_coerced - Book.send(:load_schema!) + Book.create!(name: "TEMP RECORD TO RUN SCHEMA QUERIES").destroy! original_test_payload_affected_rows end end From 212fb0ab48143b8fc4121c655f14f35c92f2fd5e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 21 Feb 2025 20:54:45 +0000 Subject: [PATCH 1358/1412] Update all subquery fixes (#1303) --- lib/arel/visitors/sqlserver.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 69fad62fa..e9e9e9357 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -57,16 +57,23 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) collect_nodes_for o.values, collector, " SET " collector << " FROM " first_join, *remaining_joins = o.relation.right - visit first_join.left, collector + from_items = remaining_joins.extract! do |join| + join.right.expr.right.relation == o.relation.left + end + + from_where = [first_join.left] + from_items.map(&:left) + collect_nodes_for from_where, collector, " ", ", " if remaining_joins && !remaining_joins.empty? collector << " " remaining_joins.each do |join| visit join, collector + collector << " " end end - collect_nodes_for [first_join.right.expr] + o.wheres, collector, " WHERE ", " AND " + from_where = [first_join.right.expr] + from_items.map { |i| i.right.expr } + collect_nodes_for from_where + o.wheres, collector, " WHERE ", " AND " else collector = visit o.relation, collector collect_nodes_for o.values, collector, " SET " @@ -77,9 +84,13 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) maybe_visit o.limit, collector end - # Same as PostgreSQL except we need to add limit if using subquery. + # Same as PostgreSQL and SQLite except we need to add limit if using subquery. def prepare_update_statement(o) - if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && o.relation.right.first.is_a?(Arel::Nodes::InnerJoin) + if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && + # The dialect isn't flexible enough to allow anything other than a inner join + # for the first join: + # UPDATE table SET .. FROM joined_table WHERE ... + (o.relation.right.all? { |join| join.is_a?(Arel::Nodes::InnerJoin) || join.right.expr.right.relation != o.relation.left }) o else o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? From b963e6f0437c7dd28b4c5daa4488d57165724301 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 1 Mar 2025 14:15:16 +0000 Subject: [PATCH 1359/1412] Fix affected rows count when lowercase schema reflection enabled (#1306) --- .../sqlserver/database_statements.rb | 3 ++- test/cases/adapter_test_sqlserver.rb | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 217df0026..d5dc267ea 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -43,7 +43,8 @@ def cast_result(raw_result) # Returns the affected rows from results. def affected_rows(raw_result) - raw_result&.first&.fetch("AffectedRows", nil) + column_name = lowercase_schema_reflection ? "affectedrows" : "AffectedRows" + raw_result&.first&.fetch(column_name, nil) end # Returns the affected rows from results or handle. diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 9eb781185..a5403775a 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -7,6 +7,7 @@ require "models/subscriber" require "models/minimalistic" require "models/college" +require "models/discount" class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks @@ -189,6 +190,24 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "Favorite number?", SSTestUppered.last.column1 assert SSTestUppered.columns_hash["column2"] end + + it "destroys model with no associations" do + connection.lowercase_schema_reflection = true + + assert_nothing_raised do + discount = Discount.create! + discount.destroy! + end + end + + it "destroys model with association" do + connection.lowercase_schema_reflection = true + + assert_nothing_raised do + post = Post.create!(title: "Setup", body: "Record to be deleted") + post.destroy! + end + end end describe "identity inserts" do From f1fea825a26b664be847607c92370ff2bb204d4c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 1 Mar 2025 14:25:30 +0000 Subject: [PATCH 1360/1412] Reporting an Issue --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 0285ae324..f6062772d 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,12 @@ The adapter has no strict gem dependencies outside of `ActiveRecord` and gem 'activerecord-sqlserver-adapter' ``` +## Reporting an Issue + +Having a way to reproduce your issue will help people confirm, investigate, and ultimately fix your issue. You +can do this by providing an executable test case. To make this process easier, we have prepared a bug report template +for you to use as a starting point at [How to report a bug](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/wiki/How-to-report-a-bug). + ## Contributing Please contribute to the project by submitting bug fixes and features. To make sure your fix/feature has From 6f9c242d74939b3b16e0a1f7eaec78def7214ab9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 10 Mar 2025 10:59:56 +0000 Subject: [PATCH 1361/1412] Fix retrieval of temporary table's column information (#1308) --- CHANGELOG.md | 4 ++++ .../sqlserver/schema_statements.rb | 22 ++++++++++++++----- .../connection_adapters/sqlserver/utils.rb | 4 ++++ test/cases/temporary_table_test_sqlserver.rb | 19 ++++++++++++++++ 4 files changed, 43 insertions(+), 6 deletions(-) create mode 100644 test/cases/temporary_table_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8b5e367..47847fbb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Unreleased +#### Fixed + +- [#1308](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1308) Fix retrieval of temporary table's column information. + #### Added - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5f1e0f6dd..140cc89e1 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -631,11 +631,21 @@ def column_type(ci:) end def column_definitions_sql(database, identifier) - object_name = prepared_statements ? "@0" : quote(identifier.object) - schema_name = if identifier.schema.blank? - "schema_name()" + schema_name = "schema_name()" + + if prepared_statements + object_name = "@0" + schema_name = "@1" if identifier.schema.present? else - prepared_statements ? "@1" : quote(identifier.schema) + object_name = quote(identifier.object) + schema_name = quote(identifier.schema) if identifier.schema.present? + end + + object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name + + if identifier.temporary_table? + database = "TEMPDB" + object_id_arg = "CONCAT('#{database}','..',#{object_name})" end %{ @@ -691,7 +701,7 @@ def column_definitions_sql(database, identifier) AND k.unique_index_id = ic.index_id AND c.column_id = ic.column_id WHERE - o.name = #{object_name} + o.Object_ID = Object_ID(#{object_id_arg}) AND s.name = #{schema_name} ORDER BY c.column_id @@ -713,7 +723,7 @@ def remove_check_constraints(table_name, column_name) end def remove_default_constraint(table_name, column_name) - # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case. + # If there are foreign keys in this table, we could still get back a 2D array, so flatten just in case. execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row| row["constraint_type"] == "DEFAULT on column #{column_name}" end.each do |row| diff --git a/lib/active_record/connection_adapters/sqlserver/utils.rb b/lib/active_record/connection_adapters/sqlserver/utils.rb index baf237495..77ec1d734 100644 --- a/lib/active_record/connection_adapters/sqlserver/utils.rb +++ b/lib/active_record/connection_adapters/sqlserver/utils.rb @@ -81,6 +81,10 @@ def hash parts.hash end + def temporary_table? + object.start_with?("#") + end + protected def parse_raw_name diff --git a/test/cases/temporary_table_test_sqlserver.rb b/test/cases/temporary_table_test_sqlserver.rb new file mode 100644 index 000000000..0ab808a70 --- /dev/null +++ b/test/cases/temporary_table_test_sqlserver.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TemporaryTableSQLServer < ActiveRecord::TestCase + def test_insert_into_temporary_table + ActiveRecord::Base.with_connection do |conn| + conn.exec_query("CREATE TABLE #temp_users (id INT IDENTITY(1,1), name NVARCHAR(100))") + + result = conn.exec_query("SELECT * FROM #temp_users") + assert_equal 0, result.count + + conn.exec_query("INSERT INTO #temp_users (name) VALUES ('John'), ('Doe')") + + result = conn.exec_query("SELECT * FROM #temp_users") + assert_equal 2, result.count + end + end +end From 3b69b66f9b104618d3cff552a80e24982e80ca6c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 12 Mar 2025 11:05:56 +0000 Subject: [PATCH 1362/1412] Coerce tests that use LIMIT in SQL (#1311) --- test/cases/coerced_tests.rb | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 11d980311..3d56cf48f 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1101,8 +1101,8 @@ def test_member_on_unloaded_relation_without_match_coerced end # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. - coerce_tests! :test_implicit_order_column_is_configurable - def test_implicit_order_column_is_configurable_coerced + coerce_tests! :test_implicit_order_column_is_configurable_with_a_single_value + def test_implicit_order_column_is_configurable_with_a_single_value_coerced old_implicit_order_column = Topic.implicit_order_column Topic.implicit_order_column = "title" @@ -1117,6 +1117,32 @@ def test_implicit_order_column_is_configurable_coerced Topic.implicit_order_column = old_implicit_order_column end + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_implicit_order_column_is_configurable_with_multiple_values + def test_implicit_order_column_is_configurable_with_multiple_values_coerced + old_implicit_order_column = Topic.implicit_order_column + Topic.implicit_order_column = ["title", "author_name"] + + assert_queries_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.title"))} DESC, #{Regexp.escape(quote_table_name("topics.author_name"))} DESC, #{Regexp.escape(quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + Topic.last + } + ensure + Topic.implicit_order_column = old_implicit_order_column + end + + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. + coerce_tests! :test_ordering_does_not_append_primary_keys_or_query_constraints_if_passed_an_implicit_order_column_array_ending_in_nil + def test_ordering_does_not_append_primary_keys_or_query_constraints_if_passed_an_implicit_order_column_array_ending_in_nil_coerced + old_implicit_order_column = Topic.implicit_order_column + Topic.implicit_order_column = ["author_name", nil] + + assert_queries_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.author_name"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + Topic.last + } + ensure + Topic.implicit_order_column = old_implicit_order_column + end + # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_implicit_order_set_to_primary_key def test_implicit_order_set_to_primary_key_coerced From 8739eea70fd9615f16a8f2d6071577e448ad4132 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 12 Mar 2025 11:07:10 +0000 Subject: [PATCH 1363/1412] Removing from changelog as fix already in v8 release. --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47847fbb0..8c8b5e367 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,5 @@ ## Unreleased -#### Fixed - -- [#1308](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1308) Fix retrieval of temporary table's column information. - #### Added - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. From aa0d8ef1598767be5d6f3cc95a9e8a933e67be2a Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Thu, 20 Mar 2025 14:40:15 +0100 Subject: [PATCH 1364/1412] Support `insert_all` and `upsert_all` using `MERGE` (#1312) --- CHANGELOG.md | 1 + README.md | 16 ++ .../sqlserver/database_statements.rb | 150 ++++++++++++++++-- .../sqlserver/schema_statements.rb | 2 + .../connection_adapters/sqlserver_adapter.rb | 4 +- test/cases/adapter_test_sqlserver.rb | 12 +- test/cases/coerced_tests.rb | 18 +++ test/cases/schema_test_sqlserver.rb | 18 +++ 8 files changed, 208 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c8b5e367..a4884550c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ #### Added - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. +- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all` #### Changed diff --git a/README.md b/README.md index f6062772d..294bcd990 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,22 @@ ActiveRecord::ConnectionAdapters::SQLServerAdapter.showplan_option = 'SHOWPLAN_X ``` **NOTE:** The method we utilize to make SHOWPLANs work is very brittle to complex SQL. There is no getting around this as we have to deconstruct an already prepared statement for the sp_executesql method. If you find that explain breaks your app, simple disable it. Do not open a github issue unless you have a patch. Please [consult the Rails guides](http://guides.rubyonrails.org/active_record_querying.html#running-explain) for more info. +#### `insert_all` / `upsert_all` support + +`insert_all` and `upsert_all` on other database system like MySQL, SQlite or PostgreSQL use a clause with their `INSERT` statement to either skip duplicates (`ON DUPLICATE KEY IGNORE`) or to update the existing record (`ON DUPLICATE KEY UPDATE`). Microsoft SQL Server does not offer these clauses, so the support for these two options is implemented slightly different. + +Behind the scenes, we execute a `MERGE` query, which joins your data that you want to insert or update into the table existing on the server. The emphasis here is "JOINING", so we also need to remove any duplicates that might make the `JOIN` operation fail, e.g. something like this: + +```ruby +Book.insert_all [ + { id: 200, author_id: 8, name: "Refactoring" }, + { id: 200, author_id: 8, name: "Refactoring" } +] +``` + +The removal of duplicates happens during the SQL query. + +Because of this implementation, if you pass `on_duplicate` to `upsert_all`, make sure to assign your value to `target.[column_name]` (e.g. `target.status = GREATEST(target.status, 1)`). To access the values that you want to upsert, use `source.[column_name]`. ## New Rails Applications diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index d5dc267ea..92e91665d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -154,20 +154,53 @@ def default_insert_value(column) private :default_insert_value def build_insert_sql(insert) # :nodoc: - sql = "INSERT #{insert.into}" + # Use regular insert if not skipping/updating duplicates. + return build_sql_for_regular_insert(insert:) unless insert.skip_duplicates? || insert.update_duplicates? + + insert_all = insert.send(:insert_all) + columns_with_uniqueness_constraints = get_columns_with_uniqueness_constraints(insert_all:, insert:) - returning = insert.send(:insert_all).returning + # If we do not have any columns that might have conflicting values just execute a regular insert, else use merge. + if columns_with_uniqueness_constraints.flatten.empty? + build_sql_for_regular_insert(insert:) + else + build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_constraints:) + end + end - if returning - returning_sql = if returning.is_a?(String) - returning + def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_constraints:) # :nodoc: + sql = <<~SQL + MERGE INTO #{insert.model.quoted_table_name} WITH (UPDLOCK, HOLDLOCK) AS target + USING ( + SELECT * + FROM ( + SELECT #{insert.send(:columns_list)}, #{partition_by_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:)} + FROM (#{insert.values_list}) + AS t1 (#{insert.send(:columns_list)}) + ) AS ranked_source + WHERE #{is_first_record_across_all_uniqueness_constraints(columns_with_uniqueness_constraints:)} + ) AS source + ON (#{joining_on_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:)}) + SQL + + if insert.update_duplicates? + sql << " WHEN MATCHED THEN UPDATE SET " + + if insert.raw_update_sql? + sql << insert.raw_update_sql else - Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ") + if insert.record_timestamps? + sql << build_sql_for_recording_timestamps_when_updating(insert:) + end + + sql << insert.updatable_columns.map { |column| "target.#{quote_column_name(column)}=source.#{quote_column_name(column)}" }.join(",") end - sql << " OUTPUT #{returning_sql}" end + sql << " WHEN NOT MATCHED BY TARGET THEN" + sql << " INSERT (#{insert.send(:columns_list)}) VALUES (#{insert_all.keys_including_timestamps.map { |column| "source.#{quote_column_name(column)}" }.join(", ")})" + sql << build_sql_for_returning(insert:, insert_all: insert.send(:insert_all)) + sql << ";" - sql << " #{insert.values_list}" sql end @@ -418,11 +451,18 @@ 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 + if id_column && ( + sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i || + sql =~ /^\s*MERGE INTO.+THEN INSERT \([^)]*\b(#{id_column.name})\b,?[^)]*\)/im + ) + SQLServer::Utils.extract_identifiers(raw_table_name).quoted + else + false + end end def insert_sql?(sql) - !(sql =~ /\A\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil? + !(sql =~ /\A\s*(INSERT|EXEC sp_executesql N'INSERT|MERGE INTO.+THEN INSERT)/im).nil? end def identity_columns(table_name) @@ -465,6 +505,96 @@ def internal_raw_execute(sql, raw_connection, perform_do: false) result = raw_connection.execute(sql) perform_do ? result.do : result end + + # === SQLServer Specific (insert_all / upsert_all support) ===================== # + def build_sql_for_returning(insert:, insert_all:) + return "" unless insert_all.returning + + returning_values_sql = if insert_all.returning.is_a?(String) + insert_all.returning + else + Array(insert_all.returning).map do |attribute| + if insert.model.attribute_alias?(attribute) + "INSERTED.#{quote_column_name(insert.model.attribute_alias(attribute))} AS #{quote_column_name(attribute)}" + else + "INSERTED.#{quote_column_name(attribute)}" + end + end.join(",") + end + + " OUTPUT #{returning_values_sql}" + end + private :build_sql_for_returning + + def get_columns_with_uniqueness_constraints(insert_all:, insert:) + if (unique_by = insert_all.unique_by) + [unique_by.columns] + else + # Compare against every unique constraint (primary key included). + # Discard constraints that are not fully included on insert.keys. Prevents invalid queries. + # Example: ignore unique index for columns ["name"] if insert keys is ["description"] + (insert_all.send(:unique_indexes).map(&:columns) + [insert_all.primary_keys]).select do |columns| + columns.to_set.subset?(insert.keys) + end + end + end + private :get_columns_with_uniqueness_constraints + + def build_sql_for_regular_insert(insert:) + sql = "INSERT #{insert.into}" + sql << build_sql_for_returning(insert:, insert_all: insert.send(:insert_all)) + sql << " #{insert.values_list}" + sql + end + private :build_sql_for_regular_insert + + # why is the "PARTITION BY" clause needed? + # in every DBMS system, insert_all / upsert_all is usually implemented with INSERT, that allows to define what happens + # when duplicates are found (SKIP OR UPDATE) + # by default rows are considered to be unique by every unique index on the table + # but since we have to use MERGE in MSSQL, which in return is a JOIN, we have to perform the "de-duplication" ourselves + # otherwise the "JOIN" clause would complain about non-unique values and being unable to JOIN the two tables + # this works easiest by using PARTITION and make sure that any record + # we are trying to insert is "the first one seen across all the potential columns with uniqueness constraints" + def partition_by_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:) + columns_with_uniqueness_constraints.map.with_index do |group_of_columns_with_uniqueness_constraints, index| + <<~PARTITION_BY + ROW_NUMBER() OVER ( + PARTITION BY #{group_of_columns_with_uniqueness_constraints.map { |column| quote_column_name(column) }.join(",")} + ORDER BY #{group_of_columns_with_uniqueness_constraints.map { |column| "#{quote_column_name(column)} DESC" }.join(",")} + ) AS rn_#{index} + PARTITION_BY + end.join(", ") + end + private :partition_by_columns_with_uniqueness_constraints + + def is_first_record_across_all_uniqueness_constraints(columns_with_uniqueness_constraints:) + columns_with_uniqueness_constraints.map.with_index do |group_of_columns_with_uniqueness_constraints, index| + "rn_#{index} = 1" + end.join(" AND ") + end + private :is_first_record_across_all_uniqueness_constraints + + def joining_on_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:) + columns_with_uniqueness_constraints.map do |columns| + columns.map do |column| + "target.#{quote_column_name(column)} = source.#{quote_column_name(column)}" + end.join(" AND ") + end.join(") OR (") + end + private :joining_on_columns_with_uniqueness_constraints + + # normally, generating the CASE SQL is done entirely by Rails + # and you would just hook into "touch_model_timestamps_unless" to add your database-specific instructions + # however, since we need to have "target." for the assignment, we also generate the CASE switch ourselves + def build_sql_for_recording_timestamps_when_updating(insert:) + insert.model.timestamp_attributes_for_update_in_model.filter_map do |column_name| + if insert.send(:touch_timestamp_attribute?, column_name) + "target.#{quote_column_name(column_name)}=CASE WHEN (#{insert.updatable_columns.map { |column| "(COALESCE(target.#{quote_column_name(column)}, 'NULL') = COALESCE(source.#{quote_column_name(column)}, 'NULL'))" }.join(" AND ")}) THEN target.#{quote_column_name(column_name)} ELSE #{high_precision_current_timestamp} END," + end + end.join + end + private :build_sql_for_recording_timestamps_when_updating end end end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 140cc89e1..e6b1b8798 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -759,6 +759,8 @@ def get_raw_table_name(sql) .match(/\s*([^(]*)/i)[0] elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] + elsif s.match?(/^\s*MERGE INTO.*/i) + s.match(/^\s*MERGE\s+INTO\s+(\[?[a-z_ -]+\]?\.?\[?[a-z_ -]+\]?)\s+(AS|WITH|USING)/i)[1] else s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] end.strip diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 170f3286b..3eea37906 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -216,11 +216,11 @@ def supports_insert_returning? end def supports_insert_on_duplicate_skip? - false + true end def supports_insert_on_duplicate_update? - false + true end def supports_insert_conflict_target? diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index a5403775a..3ae39d394 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -13,6 +13,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } + let(:basic_merge_sql) { "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name]" } let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" } @@ -91,6 +92,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase it "return unquoted table name object from basic INSERT UPDATE and SELECT statements" do assert_equal "funny_jokes", connection.send(:get_table_name, basic_insert_sql) + assert_equal "ships", connection.send(:get_table_name, basic_merge_sql) assert_equal "customers", connection.send(:get_table_name, basic_update_sql) assert_equal "customers", connection.send(:get_table_name, basic_select_sql) end @@ -219,6 +221,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_unquoted_sp = "EXEC sp_executesql N'INSERT INTO funny_jokes (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Knock knock'" @identity_insert_sql_unordered_sp = "EXEC sp_executesql N'INSERT INTO [funny_jokes] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Knock knock', @1 = 420" + @identity_merge_sql = "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name] WHEN NOT MATCHED BY TARGET THEN INSERT ([id], [name]) VALUES (source.[id], source.[name]) OUTPUT INSERTED.[id]" + @identity_merge_sql_unquoted = "MERGE INTO ships WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT id, name, ROW_NUMBER() OVER ( PARTITION BY id ORDER BY id DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 (id, name) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.id = source.id) WHEN MATCHED THEN UPDATE SET target.name = source.name WHEN NOT MATCHED BY TARGET THEN INSERT (id, name) VALUES (source.id, source.name) OUTPUT INSERTED.id" + @identity_merge_sql_unordered = "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [name], [id], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([name], [id]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name] WHEN NOT MATCHED BY TARGET THEN INSERT ([name], [id]) VALUES (source.[name], source.[id]) OUTPUT INSERTED.[id]" + @identity_insert_sql_non_dbo = "INSERT INTO [test].[aliens] ([id],[name]) VALUES(420,'Mork')" @identity_insert_sql_non_dbo_unquoted = "INSERT INTO test.aliens ([id],[name]) VALUES(420,'Mork')" @identity_insert_sql_non_dbo_unordered = "INSERT INTO [test].[aliens] ([name],[id]) VALUES('Mork',420)" @@ -235,6 +241,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase 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 "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql) + assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_unquoted) + assert_equal "[ships]", connection.send(:query_requires_identity_insert?, @identity_merge_sql_unordered) + 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) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered) @@ -244,7 +254,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase end it "return false to #query_requires_identity_insert? for normal SQL" do - [basic_insert_sql, basic_update_sql, basic_select_sql].each do |sql| + [basic_insert_sql, basic_merge_sql, basic_update_sql, basic_select_sql].each do |sql| assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}" end end diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 3d56cf48f..1cdd9eff8 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2552,6 +2552,24 @@ def test_insert_with_type_casting_and_serialize_is_consistent_coerced Book.where(author_id: nil, name: '["Array"]').delete_all Book.lease_connection.add_index(:books, [:author_id, :name], unique: true) end + + # Same as original but using target.status for assignment and CASE instead of GREATEST for operator + coerce_tests! :test_upsert_all_updates_using_provided_sql + def test_upsert_all_updates_using_provided_sql_coerced + Book.upsert_all( + [{id: 1, status: 1}, {id: 2, status: 1}], + on_duplicate: Arel.sql(<<~SQL + target.status = CASE + WHEN target.status > 1 THEN target.status + ELSE 1 + END + SQL + ) + ) + + assert_equal "published", Book.find(1).status + assert_equal "written", Book.find(2).status + end end module ActiveRecord diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index 7057a9ca6..fb9eb1cc0 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -102,6 +102,24 @@ class SchemaTestSQLServer < ActiveRecord::TestCase end end + describe "MERGE statements" do + it do + assert_equal "[dashboards]", connection.send(:get_raw_table_name, "MERGE INTO [dashboards] AS target") + end + + it do + assert_equal "lock_without_defaults", connection.send(:get_raw_table_name, "MERGE INTO lock_without_defaults AS target") + end + + it do + assert_equal "[WITH - SPACES]", connection.send(:get_raw_table_name, "MERGE INTO [WITH - SPACES] AS target") + end + + it do + assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "MERGE INTO [with].[select notation] AS target") + end + end + describe "CREATE VIEW statements" do it do assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))") From b3208b457f50c81e7459afeecb148f102153f794 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 20 Mar 2025 14:25:52 +0000 Subject: [PATCH 1365/1412] Test no longer need to be coerced (#1316) --- test/cases/coerced_tests.rb | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 1cdd9eff8..520980672 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2587,22 +2587,6 @@ def invalid_add_column_option_exception_message(key) end end -# SQL Server does not support upsert. Removed dependency on `insert_all` that uses upsert. -class ActiveRecord::Encryption::ConcurrencyTest < ActiveRecord::EncryptionTestCase - undef_method :thread_encrypting_and_decrypting - def thread_encrypting_and_decrypting(thread_label) - posts = 100.times.collect { |index| EncryptedPost.create! title: "Article #{index} (#{thread_label})", body: "Body #{index} (#{thread_label})" } - - Thread.new do - posts.each.with_index do |article, index| - assert_encrypted_attribute article, :title, "Article #{index} (#{thread_label})" - article.decrypt - assert_not_encrypted_attribute article, :title, "Article #{index} (#{thread_label})" - end - end - end -end - # Need to use `install_unregistered_type_fallback` instead of `install_unregistered_type_error` so that message-pack # can read and write `ActiveRecord::ConnectionAdapters::SQLServer::Type::Data` objects. class ActiveRecordMessagePackTest < ActiveRecord::TestCase From 2e239391c0acd462387ffb5452aa64e3f5f871c3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 20 Mar 2025 16:16:32 +0000 Subject: [PATCH 1366/1412] Correctly retrieve the SQL Server database version (#1313) --- CHANGELOG.md | 3 +++ .../connection_adapters/sqlserver_adapter.rb | 7 ++----- test/cases/helper_sqlserver.rb | 8 ++++++++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4884550c..48a4daac0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,5 +9,8 @@ - [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. +#### Fixed + +- [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version. Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 3eea37906..a06bc957e 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -486,19 +486,16 @@ def initialize_dateformatter end def version_year - @version_year ||= begin + @version_year ||= if /vNext/.match?(sqlserver_version) 2016 else /SQL Server (\d+)/.match(sqlserver_version).to_a.last.to_s.to_i end - rescue - 2016 - end end def sqlserver_version - @sqlserver_version ||= _raw_select("SELECT @@version", @raw_connection).first.first.to_s + @sqlserver_version ||= execute("SELECT @@version", "SCHEMA").rows.first.first.to_s end private diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 411d4c08d..4be7683b8 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -15,6 +15,14 @@ require "support/query_assertions" require "mocha/minitest" +Minitest.after_run do + puts "\n\n" + puts "=" * 80 + puts ActiveRecord::Base.lease_connection.send(:sqlserver_version) + puts "\nSQL Server Version Year: #{ActiveRecord::Base.lease_connection.get_database_version}" + puts "=" * 80 +end + module ActiveSupport class TestCase < ::Minitest::Test include ARTest::SQLServer::CoerceableTest From f69c849fdb621cc9fe15942e362e019094f27326 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Sun, 23 Mar 2025 10:38:09 +0100 Subject: [PATCH 1367/1412] Reverse order of values when upserting (#1317) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48a4daac0..fabc1048a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. - [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all` +- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting #### Changed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 92e91665d..addc593d3 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -169,6 +169,8 @@ def build_insert_sql(insert) # :nodoc: end def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_constraints:) # :nodoc: + insert_all.inserts.reverse! if insert.update_duplicates? + sql = <<~SQL MERGE INTO #{insert.model.quoted_table_name} WITH (UPDLOCK, HOLDLOCK) AS target USING ( From 39736830d41ecb7217b4e67cba885dde67d44321 Mon Sep 17 00:00:00 2001 From: Andy Pfister Date: Mon, 24 Mar 2025 13:53:39 +0100 Subject: [PATCH 1368/1412] Fix upserting with mixed data types (#1320) --- CHANGELOG.md | 1 + .../sqlserver/database_statements.rb | 2 +- test/cases/insert_all_test_sqlserver.rb | 20 +++++++++++++++++++ test/models/sqlserver/recurring_task.rb | 3 +++ test/schema/sqlserver_specific_schema.rb | 8 ++++++++ 5 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 test/cases/insert_all_test_sqlserver.rb create mode 100644 test/models/sqlserver/recurring_task.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index fabc1048a..bcddc4efb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,5 +13,6 @@ #### Fixed - [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version. +- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index addc593d3..30296d1a9 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -592,7 +592,7 @@ def joining_on_columns_with_uniqueness_constraints(columns_with_uniqueness_const def build_sql_for_recording_timestamps_when_updating(insert:) insert.model.timestamp_attributes_for_update_in_model.filter_map do |column_name| if insert.send(:touch_timestamp_attribute?, column_name) - "target.#{quote_column_name(column_name)}=CASE WHEN (#{insert.updatable_columns.map { |column| "(COALESCE(target.#{quote_column_name(column)}, 'NULL') = COALESCE(source.#{quote_column_name(column)}, 'NULL'))" }.join(" AND ")}) THEN target.#{quote_column_name(column_name)} ELSE #{high_precision_current_timestamp} END," + "target.#{quote_column_name(column_name)}=CASE WHEN (#{insert.updatable_columns.map { |column| "(source.#{quote_column_name(column)} = target.#{quote_column_name(column)} OR (source.#{quote_column_name(column)} IS NULL AND target.#{quote_column_name(column)} IS NULL))" }.join(" AND ")}) THEN target.#{quote_column_name(column_name)} ELSE #{high_precision_current_timestamp} END," end end.join end diff --git a/test/cases/insert_all_test_sqlserver.rb b/test/cases/insert_all_test_sqlserver.rb new file mode 100644 index 000000000..ee17d5500 --- /dev/null +++ b/test/cases/insert_all_test_sqlserver.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "models/sqlserver/recurring_task" + +class InsertAllTestSQLServer < ActiveRecord::TestCase + test "upsert_all recording of timestamps works with mixed datatypes" do + task = RecurringTask.create!( + key: "abcdef", + priority: 5 + ) + + RecurringTask.upsert_all([{ + id: task.id, + priority: nil + }]) + + assert_not_equal task.updated_at, RecurringTask.find(task.id).updated_at + end +end diff --git a/test/models/sqlserver/recurring_task.rb b/test/models/sqlserver/recurring_task.rb new file mode 100644 index 000000000..aecf7c462 --- /dev/null +++ b/test/models/sqlserver/recurring_task.rb @@ -0,0 +1,3 @@ +class RecurringTask < ActiveRecord::Base + self.table_name = "recurring_tasks" +end diff --git a/test/schema/sqlserver_specific_schema.rb b/test/schema/sqlserver_specific_schema.rb index 367ae2bf8..68770d59a 100644 --- a/test/schema/sqlserver_specific_schema.rb +++ b/test/schema/sqlserver_specific_schema.rb @@ -372,4 +372,12 @@ name varchar(255) ) TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL + + create_table "recurring_tasks", force: true do |t| + t.string :key + t.integer :priority, default: 0 + + t.datetime2 :created_at + t.datetime2 :updated_at + end end From 048734dfc3e6787e1076379963bb881e589ac2b9 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 3 Apr 2025 19:40:12 +0100 Subject: [PATCH 1369/1412] Use a self-join for UPDATE with outer joins (#1323) --- lib/arel/visitors/sqlserver.rb | 54 +++++++++++----------------------- 1 file changed, 17 insertions(+), 37 deletions(-) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index e9e9e9357..606f8185f 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -42,57 +42,37 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) # # UPDATE t1 # SET .. - # FROM t2 - # WHERE t1.join_id = t2.join_id - # - # Or if more than one join is present: - # - # UPDATE t1 - # SET .. - # FROM t2 - # JOIN t3 ON t2.join_id = t3.join_id - # WHERE t1.join_id = t2.join_id + # FROM t1 JOIN t2 ON t2.join_id = t1.join_id .. + # WHERE .. if has_join_sources?(o) - visit o.relation.left, collector + collector = visit o.relation.left, collector collect_nodes_for o.values, collector, " SET " collector << " FROM " - first_join, *remaining_joins = o.relation.right - from_items = remaining_joins.extract! do |join| - join.right.expr.right.relation == o.relation.left - end - - from_where = [first_join.left] + from_items.map(&:left) - collect_nodes_for from_where, collector, " ", ", " - - if remaining_joins && !remaining_joins.empty? - collector << " " - remaining_joins.each do |join| - visit join, collector - collector << " " - end - end - - from_where = [first_join.right.expr] + from_items.map { |i| i.right.expr } - collect_nodes_for from_where + o.wheres, collector, " WHERE ", " AND " + collector = inject_join o.relation.right, collector, " " else collector = visit o.relation, collector collect_nodes_for o.values, collector, " SET " - collect_nodes_for o.wheres, collector, " WHERE ", " AND " end + collect_nodes_for o.wheres, collector, " WHERE ", " AND " collect_nodes_for o.orders, collector, " ORDER BY " maybe_visit o.limit, collector end - # Same as PostgreSQL and SQLite except we need to add limit if using subquery. + # Similar to PostgreSQL and SQLite. def prepare_update_statement(o) - if has_join_sources?(o) && !has_limit_or_offset_or_orders?(o) && !has_group_by_and_having?(o) && - # The dialect isn't flexible enough to allow anything other than a inner join - # for the first join: - # UPDATE table SET .. FROM joined_table WHERE ... - (o.relation.right.all? { |join| join.is_a?(Arel::Nodes::InnerJoin) || join.right.expr.right.relation != o.relation.left }) - o + if o.key && has_join_sources?(o) && !has_group_by_and_having?(o) && !has_limit_or_offset_or_orders?(o) + # Join clauses cannot reference the target table, so alias the + # updated table, place the entire relation in the FROM clause, and + # add a self-join (which requires the primary key) + stmt = o.clone + + stmt.relation, stmt.wheres = o.relation.clone, o.wheres.clone + stmt.relation.right = [stmt.relation.left, *stmt.relation.right] + # Don't need to use alias + stmt else + # If using subquery, we need to add limit o.limit = Nodes::Limit.new(9_223_372_036_854_775_807) if o.orders.any? && o.limit.nil? super From da6238cb062469351b1af636e8738926c9730169 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 4 Apr 2025 16:52:29 +0100 Subject: [PATCH 1370/1412] Redefine isolation level method as MSSSQL resets isolation after transaction (#1324) --- test/cases/coerced_tests.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 520980672..0d4891a96 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1842,6 +1842,22 @@ class TransactionIsolationTest < ActiveRecord::TestCase # I really need some help understanding this one. coerce_tests! %r{repeatable read} + + private + + # Need to handle the resetting of the isolation level in the adapter by `SQLServerRealTransaction#commit`. + # MySQL & PostgreSQL do not reset the connection and SQLite does support transaction isolation. After that we + # can assert the number of expected isolation level events. + undef_method :assert_begin_isolation_level_event + def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED") + isolation_events = events.select { _1.match(/SET TRANSACTION ISOLATION LEVEL/) } + + index_of_reset_starting_isolation_level_event = isolation_events.index("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") + assert index_of_reset_starting_isolation_level_event.present? + isolation_events.delete_at(index_of_reset_starting_isolation_level_event) + + assert_equal 1, isolation_events.select { _1.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) }.size + end end class ViewWithPrimaryKeyTest < ActiveRecord::TestCase From 3d341c599282b8c335dbc9de40655cdd793a3b12 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 17 Apr 2025 08:11:54 +0100 Subject: [PATCH 1371/1412] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d59408e7b..2a08f5989 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ main ] schedule: - - cron: '0 4 * * *' + - cron: '0 4 * * 7' jobs: test: From 25aacf221fdad167e2e77638c80e677cca5ce31c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 19 Apr 2025 20:53:58 +0100 Subject: [PATCH 1372/1412] Fix cron schedule --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a08f5989..a7f72eb74 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: pull_request: branches: [ main ] schedule: - - cron: '0 4 * * 7' + - cron: '0 4 * * 0' jobs: test: From ab5c5c112a79a0f39cc8961b15435e71748a0cc8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 25 Apr 2025 19:13:49 +0100 Subject: [PATCH 1373/1412] Use latest Ubuntu image on CI (#1325) --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7f72eb74..ee0b1428b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ on: jobs: test: name: Run test suite - runs-on: ubuntu-20.04 # TODO: Change back to 'ubuntu-latest' when https://github.com/microsoft/mssql-docker/issues/899 resolved. + runs-on: ubuntu-latest env: COMPOSE_FILE: compose.ci.yaml @@ -37,7 +37,7 @@ jobs: 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. + runs-on: ubuntu-latest env: COMPOSE_FILE: compose.ci.yaml From 6feabc248b5494edd52ba0daf3804130e3b890c4 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 25 Apr 2025 20:16:03 +0100 Subject: [PATCH 1374/1412] Move AbstractAdapter#valid_type? to a class method (#1326) --- README.md | 2 +- .../sqlserver/schema_statements.rb | 40 ------------------- .../connection_adapters/sqlserver_adapter.rb | 38 ++++++++++++++++++ test/cases/coerced_tests.rb | 4 +- 4 files changed, 41 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 294bcd990..5e5586b22 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions #### Native Data Type Support -We support every data type supported by FreeTDS. All simplified Rails types in migrations will correspond to a matching SQL Server national (unicode) data type. Always check the `initialize_native_database_types` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver/schema_statements.rb) for an updated list. +We support every data type supported by FreeTDS. All simplified Rails types in migrations will correspond to a matching SQL Server national (unicode) data type. Always check the `NATIVE_DATABASE_TYPES` [(here)](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/master/lib/active_record/connection_adapters/sqlserver_adapter.rb) for an updated list. The following types (`date`, `datetime2`, `datetimeoffset`, `time`) all require TDS version `7.3` with TinyTDS. We recommend using FreeTDS 1.0 or higher which default to using `TDSVER` to `7.3`. The adapter also sets TinyTDS's `tds_version` to this as well if non is specified. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index e6b1b8798..5cc910919 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -4,10 +4,6 @@ module ActiveRecord module ConnectionAdapters module SQLServer module SchemaStatements - def native_database_types - @native_database_types ||= initialize_native_database_types.freeze - end - def create_table(table_name, **options) res = super clear_cache! @@ -488,42 +484,6 @@ def quoted_scope(name = nil, type: nil) # === SQLServer Specific ======================================== # - 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)"} - } - end - def column_definitions(table_name) identifier = database_prefix_identifier(table_name) database = identifier.fully_qualified_database_quoted diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index a06bc957e..70eab807d 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -62,6 +62,40 @@ class SQLServerAdapter < AbstractAdapter self.use_output_inserted = true self.exclude_output_inserted_table_names = Concurrent::Map.new { false } + 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)"} + } + class << self def dbconsole(config, options = {}) sqlserver_config = config.configuration_hash @@ -95,6 +129,10 @@ def rails_application_name rescue nil # Might not be in a Rails context so we fallback to `nil`. end + + def native_database_types # :nodoc: + NATIVE_DATABASE_TYPES + end end def initialize(...) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 0d4891a96..5f5e99107 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1850,13 +1850,13 @@ class TransactionIsolationTest < ActiveRecord::TestCase # can assert the number of expected isolation level events. undef_method :assert_begin_isolation_level_event def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED") - isolation_events = events.select { _1.match(/SET TRANSACTION ISOLATION LEVEL/) } + isolation_events = events.select { |event| event.match(/SET TRANSACTION ISOLATION LEVEL/) } index_of_reset_starting_isolation_level_event = isolation_events.index("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") assert index_of_reset_starting_isolation_level_event.present? isolation_events.delete_at(index_of_reset_starting_isolation_level_event) - assert_equal 1, isolation_events.select { _1.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) }.size + assert_equal 1, isolation_events.count { |event| event.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) } end end From 1c5fb1fdd3dfca2e3bbe8e4fab060b9475b3abe1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 12 May 2025 10:44:48 +0100 Subject: [PATCH 1375/1412] Fix multiple nil identity columns for merge insert (#1327) --- CHANGELOG.md | 9 +++--- .../sqlserver/database_statements.rb | 31 ++++++++++++++++++- 2 files changed, 35 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcddc4efb..3b0050259 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ #### Added -- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for INDEX INCLUDE. -- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all` -- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting +- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for `INDEX INCLUDE`. +- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`. +- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting. #### Changed @@ -13,6 +13,7 @@ #### Fixed - [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version. -- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting +- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting. +- [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert. Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 30296d1a9..bac9e5441 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -177,7 +177,7 @@ def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_con SELECT * FROM ( SELECT #{insert.send(:columns_list)}, #{partition_by_columns_with_uniqueness_constraints(columns_with_uniqueness_constraints:)} - FROM (#{insert.values_list}) + FROM (#{merge_insert_values_list(insert:, insert_all:)}) AS t1 (#{insert.send(:columns_list)}) ) AS ranked_source WHERE #{is_first_record_across_all_uniqueness_constraints(columns_with_uniqueness_constraints:)} @@ -206,6 +206,35 @@ def build_sql_for_merge_insert(insert:, insert_all:, columns_with_uniqueness_con sql end + # For `nil` identity columns we need to ensure that the values do not match so that they are all inserted. + # Method is a combination of `ActiveRecord::InsertAll#values_list` and `ActiveRecord::ConnectionAdapters::SQLServer::DatabaseStatements#default_insert_value`. + def merge_insert_values_list(insert:, insert_all:) + connection = insert.send(:connection) + identity_index = 0 + + types = insert.send(:extract_types_from_columns_on, insert.model.table_name, keys: insert.keys_including_timestamps) + + values_list = insert_all.map_key_with_value do |key, value| + if Arel::Nodes::SqlLiteral === value + value + elsif insert.primary_keys.include?(key) && value.nil? + column = insert.send(:column_from_key, key) + + if column.is_identity? + identity_index += 1 + table_name = quote(quote_table_name(column.table_name)) + Arel.sql("IDENT_CURRENT(#{table_name}) + (IDENT_INCR(#{table_name}) * #{identity_index})") + else + connection.default_insert_value(column) + end + else + ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value)) + end + end + + connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list)) + end + # === SQLServer Specific ======================================== # def execute_procedure(proc_name, *variables) From 663aaecac89eec7684405900614758453dd413d3 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 May 2025 11:16:43 +0100 Subject: [PATCH 1376/1412] Enable identity insert on view's base table for fixtures (#1335) --- .../sqlserver/database_statements.rb | 12 ++++++------ test/cases/view_test_sqlserver.rb | 10 ++++++++-- test/fixtures/sst_customers_view.yml | 6 ++++++ 3 files changed, 20 insertions(+), 8 deletions(-) create mode 100644 test/fixtures/sst_customers_view.yml diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index bac9e5441..4aee1e1a2 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -17,9 +17,6 @@ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notif id_insert_table_name = query_requires_identity_insert?(sql) 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 @@ -264,11 +261,14 @@ def execute_procedure(proc_name, *variables) end def with_identity_insert_enabled(table_name, conn) - table_name = quote_table_name(table_name) - set_identity_insert(table_name, conn, true) + # If the table name is a view, we need to get the base table name for enabling identity insert. + table_name = view_table_name(table_name) if view_exists?(table_name) + quoted_table_name = quote_table_name(table_name) + + set_identity_insert(quoted_table_name, conn, true) yield ensure - set_identity_insert(table_name, conn, false) + set_identity_insert(quoted_table_name, conn, false) end def use_database(database = nil) diff --git a/test/cases/view_test_sqlserver.rb b/test/cases/view_test_sqlserver.rb index 88d195750..ed5d2303c 100644 --- a/test/cases/view_test_sqlserver.rb +++ b/test/cases/view_test_sqlserver.rb @@ -53,10 +53,16 @@ class ViewTestSQLServer < ActiveRecord::TestCase end describe "identity insert" do - it "identity insert works with views" do - assert_difference("SSTestCustomersView.count", 1) do + it "creates table record through a view" do + assert_difference("SSTestCustomersView.count", 2) do SSTestCustomersView.create!(id: 5, name: "Bob") + SSTestCustomersView.create!(id: 6, name: "Tim") end end + + it "creates table records through a view using fixtures" do + ActiveRecord::FixtureSet.create_fixtures(File.join(ARTest::SQLServer.test_root_sqlserver, "fixtures"), ["sst_customers_view"]) + assert_equal SSTestCustomersView.all.count, 2 + end end end diff --git a/test/fixtures/sst_customers_view.yml b/test/fixtures/sst_customers_view.yml new file mode 100644 index 000000000..668ba3763 --- /dev/null +++ b/test/fixtures/sst_customers_view.yml @@ -0,0 +1,6 @@ +david: + name: "David" + balance: 2,004 +aidan: + name: "Aidan" + balance: 10,191 From 9cf4eaa3517240add52f7fad3f7f9bc86fc62da8 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 20 May 2025 14:48:53 +0100 Subject: [PATCH 1377/1412] Add affected_rows to ActiveRecord::Result (#1336) --- .../sqlserver/database_statements.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 4aee1e1a2..ab7ddf88f 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -30,12 +30,11 @@ def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notif result end - def cast_result(raw_result) - if raw_result.columns.empty? - ActiveRecord::Result.empty - else - ActiveRecord::Result.new(raw_result.columns, raw_result.rows) - end + # Method `perform_query` already returns an `ActiveRecord::Result` so we have nothing to cast here. This is + # different to the MySQL/PostgreSQL adapters where the raw result is converted to `ActiveRecord::Result` in + # `cast_result`. + def cast_result(result) + result end # Returns the affected rows from results. @@ -521,7 +520,7 @@ def handle_to_names_and_values(handle, options = {}) columns = columns.last if columns.any? && columns.all? { |e| e.is_a?(Array) } # If query returns multiple result sets, only return the columns of the last one. columns = columns.map(&:downcase) if lowercase_schema_reflection - ActiveRecord::Result.new(columns, results) + ActiveRecord::Result.new(columns, results, affected_rows: handle.affected_rows) else results end From 0414f69a037aa71fb79447311c9a0b19f9bc103f Mon Sep 17 00:00:00 2001 From: Justin Dell Date: Tue, 10 Jun 2025 04:11:20 -0500 Subject: [PATCH 1378/1412] Fix `insert_all`/`upsert_all` for table names containing numbers (#1338) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/schema_statements.rb | 2 +- test/cases/schema_test_sqlserver.rb | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b0050259..b75dd73b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,5 +15,6 @@ - [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version. - [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting. - [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert. +- [#1338](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1338) Fix `insert_all`/`upsert_all` for table names containing numbers. Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 5cc910919..dbe4148a8 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -720,7 +720,7 @@ def get_raw_table_name(sql) elsif s.match?(/^\s*UPDATE\s+.*/i) s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] elsif s.match?(/^\s*MERGE INTO.*/i) - s.match(/^\s*MERGE\s+INTO\s+(\[?[a-z_ -]+\]?\.?\[?[a-z_ -]+\]?)\s+(AS|WITH|USING)/i)[1] + s.match(/^\s*MERGE\s+INTO\s+(\[?[a-z0-9_ -]+\]?\.?\[?[a-z0-9_ -]+\]?)\s+(AS|WITH|USING)/i)[1] else s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] end.strip diff --git a/test/cases/schema_test_sqlserver.rb b/test/cases/schema_test_sqlserver.rb index fb9eb1cc0..fc645cf7f 100644 --- a/test/cases/schema_test_sqlserver.rb +++ b/test/cases/schema_test_sqlserver.rb @@ -118,6 +118,10 @@ class SchemaTestSQLServer < ActiveRecord::TestCase it do assert_equal "[with].[select notation]", connection.send(:get_raw_table_name, "MERGE INTO [with].[select notation] AS target") end + + it do + assert_equal "[with_numbers_1234]", connection.send(:get_raw_table_name, "MERGE INTO [with_numbers_1234] AS target") + end end describe "CREATE VIEW statements" do From a5b84098521ffbe00f2c1356302c8261752d4580 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 13 Jun 2025 16:10:38 +0100 Subject: [PATCH 1379/1412] Support more Azure services by changing language source (#1343) --- CHANGELOG.md | 1 + .../connection_adapters/sqlserver/database_statements.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b75dd73b0..b04046870 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ #### Changed - [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. +- [#1343](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1343) Support more Azure services by changing language source. #### Fixed diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index ab7ddf88f..72290ab1d 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -296,7 +296,7 @@ def user_options def user_options_dateformat if sqlserver_azure? - select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID", "SCHEMA" + select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [name] = @@LANGUAGE", "SCHEMA" else user_options["dateformat"] end From 766b219f6688d428dcbb0c4f24c32e8682cb21f1 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 14 Jun 2025 15:06:06 +0100 Subject: [PATCH 1380/1412] Fix tests --- test/cases/coerced_tests.rb | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 5f5e99107..c8c148b3a 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -1845,18 +1845,21 @@ class TransactionIsolationTest < ActiveRecord::TestCase private - # Need to handle the resetting of the isolation level in the adapter by `SQLServerRealTransaction#commit`. - # MySQL & PostgreSQL do not reset the connection and SQLite does support transaction isolation. After that we - # can assert the number of expected isolation level events. + # Need to handle the resetting of the isolation level in the adapter by `SQLServerRealTransaction#commit` for each + # connection pool. After the resetting events have been removed we can assert the number of expected isolation level + # events. This workaround assumes that the `count` also matches the number of connection pools used in the test. + # Note: MySQL & PostgreSQL do not reset the connection and SQLite does support transaction isolation. undef_method :assert_begin_isolation_level_event - def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED") + def assert_begin_isolation_level_event(events, isolation: "READ COMMITTED", count: 1) isolation_events = events.select { |event| event.match(/SET TRANSACTION ISOLATION LEVEL/) } - index_of_reset_starting_isolation_level_event = isolation_events.index("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") - assert index_of_reset_starting_isolation_level_event.present? - isolation_events.delete_at(index_of_reset_starting_isolation_level_event) + count.times do + index_of_reset_starting_isolation_level_event = isolation_events.index("SET TRANSACTION ISOLATION LEVEL READ COMMITTED") + assert index_of_reset_starting_isolation_level_event.present? + isolation_events.delete_at(index_of_reset_starting_isolation_level_event) + end - assert_equal 1, isolation_events.count { |event| event.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) } + assert_equal count, isolation_events.count { |event| event.match(/SET TRANSACTION ISOLATION LEVEL #{isolation}/) } end end From 6b5ae0f995308aa2eeb9dcd24c2b61a4648b83bc Mon Sep 17 00:00:00 2001 From: b-nik <32591851+b-nik@users.noreply.github.com> Date: Mon, 23 Jun 2025 16:58:13 +0300 Subject: [PATCH 1381/1412] Maintain index options during `change_column` operations (#1345) --- CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 6 +- .../change_column_index_test_sqlserver.rb | 108 ++++++++++++++++++ 3 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 test/cases/change_column_index_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index b04046870..169c88648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,5 +17,6 @@ - [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting. - [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert. - [#1338](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1338) Fix `insert_all`/`upsert_all` for table names containing numbers. +- [#1345](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1345) Maintain index options during `change_column` operations. Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index dbe4148a8..c1fd12c61 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -210,12 +210,14 @@ def change_column(table_name, column_name, type, options = {}) sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}" end + sql_commands.each { |c| execute(c) } + # 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(", ")})" + create_index_def = CreateIndexDefinition.new(index) + execute schema_creation.accept(create_index_def) end - sql_commands.each { |c| execute(c) } clear_cache! end diff --git a/test/cases/change_column_index_test_sqlserver.rb b/test/cases/change_column_index_test_sqlserver.rb new file mode 100644 index 000000000..08206a961 --- /dev/null +++ b/test/cases/change_column_index_test_sqlserver.rb @@ -0,0 +1,108 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class ChangeColumnIndexTestSqlServer < ActiveRecord::TestCase + class CreateClientsWithUniqueIndex < ActiveRecord::Migration[8.0] + def up + create_table :clients do |t| + t.string :name, limit: 15 + end + add_index :clients, :name, unique: true + end + + def down + drop_table :clients + end + end + + class CreateBlogPostsWithMultipleIndexesOnTheSameColumn < ActiveRecord::Migration[8.0] + def up + create_table :blog_posts do |t| + t.string :title, limit: 15 + t.string :subtitle + end + add_index :blog_posts, :title, unique: true, where: "([blog_posts].[title] IS NOT NULL)", name: "custom_index_name" + add_index :blog_posts, [:title, :subtitle], unique: true + end + + def down + drop_table :blog_posts + end + end + + class ChangeClientsNameLength < ActiveRecord::Migration[8.0] + def up + change_column :clients, :name, :string, limit: 30 + end + end + + class ChangeBlogPostsTitleLength < ActiveRecord::Migration[8.0] + def up + change_column :blog_posts, :title, :string, limit: 30 + end + end + + before do + @old_verbose = ActiveRecord::Migration.verbose + ActiveRecord::Migration.verbose = false + + CreateClientsWithUniqueIndex.new.up + CreateBlogPostsWithMultipleIndexesOnTheSameColumn.new.up + end + + after do + CreateClientsWithUniqueIndex.new.down + CreateBlogPostsWithMultipleIndexesOnTheSameColumn.new.down + + ActiveRecord::Migration.verbose = @old_verbose + end + + def test_index_uniqueness_is_maintained_after_column_change + indexes = ActiveRecord::Base.connection.indexes("clients") + columns = ActiveRecord::Base.connection.columns("clients") + assert_equal columns.find { |column| column.name == "name" }.limit, 15 + assert_equal indexes.size, 1 + assert_equal indexes.first.name, "index_clients_on_name" + assert indexes.first.unique + + ChangeClientsNameLength.new.up + + indexes = ActiveRecord::Base.connection.indexes("clients") + columns = ActiveRecord::Base.connection.columns("clients") + assert_equal columns.find { |column| column.name == "name" }.limit, 30 + assert_equal indexes.size, 1 + assert_equal indexes.first.name, "index_clients_on_name" + assert indexes.first.unique + end + + def test_multiple_index_options_are_maintained_after_column_change + indexes = ActiveRecord::Base.connection.indexes("blog_posts") + columns = ActiveRecord::Base.connection.columns("blog_posts") + assert_equal columns.find { |column| column.name == "title" }.limit, 15 + assert_equal indexes.size, 2 + + index_1 = indexes.find { |index| index.columns == ["title"] } + assert_equal index_1.name, "custom_index_name" + assert_equal index_1.where, "([blog_posts].[title] IS NOT NULL)" + assert index_1.unique + + index_2 = indexes.find { |index| index.columns == ["title", "subtitle"] } + assert index_2.unique + + ChangeBlogPostsTitleLength.new.up + + indexes = ActiveRecord::Base.connection.indexes("blog_posts") + columns = ActiveRecord::Base.connection.columns("blog_posts") + assert_equal columns.find { |column| column.name == "title" }.limit, 30 + assert_equal indexes.size, 2 + + index_1 = indexes.find { |index| index.columns == ["title"] } + assert_equal index_1.name, "custom_index_name" + assert_equal index_1.where, "([blog_posts].[title] IS NOT NULL)" + assert index_1.unique + + index_2 = indexes.find { |index| index.columns == ["title", "subtitle"] } + assert index_2.unique + end +end From 531a7a99bbecceb985fa7393f59fdbec394dbbec Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 7 Jul 2025 10:36:41 +0100 Subject: [PATCH 1382/1412] Fix annotate comments to propagate to update_all Ref: https://github.com/rails/rails/pull/55269 --- lib/arel/visitors/sqlserver.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/arel/visitors/sqlserver.rb b/lib/arel/visitors/sqlserver.rb index 606f8185f..fcf275d86 100644 --- a/lib/arel/visitors/sqlserver.rb +++ b/lib/arel/visitors/sqlserver.rb @@ -57,6 +57,7 @@ def visit_Arel_Nodes_UpdateStatement(o, collector) collect_nodes_for o.wheres, collector, " WHERE ", " AND " collect_nodes_for o.orders, collector, " ORDER BY " maybe_visit o.limit, collector + maybe_visit o.comment, collector end # Similar to PostgreSQL and SQLite. From 882842cf30113c72d09e82ef93b6bcfd88281491 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 8 Jul 2025 13:01:18 +0100 Subject: [PATCH 1383/1412] Foreign keys are expected in reverse order (#1349) --- .../sqlserver/schema_statements.rb | 2 +- test/cases/coerced_tests.rb | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index c1fd12c61..996f81e91 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -269,7 +269,7 @@ def foreign_keys(table_name) identifier = SQLServer::Utils.extract_identifiers(table_name) fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema - grouped_fk = fk_info.group_by { |row| row["FK_NAME"] }.values.each { |group| group.sort_by! { |row| row["KEY_SEQ"] } } + grouped_fk = fk_info.group_by { |row| row["FK_NAME"] }.values.each { |group| group.sort_by! { |row| row["KEY_SEQ"] } }.reverse grouped_fk.map do |group| row = group.first options = { diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index c8c148b3a..cf9e26db4 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -838,6 +838,34 @@ def migrate(x) nil end end + + # Foreign key count is the same as PostgreSQL/SQLite. + coerce_tests! :test_remove_foreign_key_on_8_0 + def test_remove_foreign_key_on_8_0_coerced + connection.create_table(:sub_testings) do |t| + t.references :testing, foreign_key: true, type: :bigint + t.references :experiment, foreign_key: {to_table: :testings}, type: :bigint + end + + migration = Class.new(ActiveRecord::Migration[8.0]) do + def up + change_table(:sub_testings) do |t| + t.remove_foreign_key :testings + t.remove_foreign_key :testings, column: :experiment_id + end + end + end + + assert_raise(StandardError, match: /Table 'sub_testings' has no foreign key for testings$/) { + ActiveRecord::Migrator.new(:up, [migration], @schema_migration, @internal_metadata).migrate + } + + foreign_keys = @connection.foreign_keys("sub_testings") + assert_equal 2, foreign_keys.size + ensure + connection.drop_table(:sub_testings, if_exists: true) + ActiveRecord::Base.clear_cache! + end end end end From eca7ac1f6028c0b96bb74bfedb88bd5c823d452c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 8 Jul 2025 13:10:44 +0100 Subject: [PATCH 1384/1412] Clean ActiveRecord backtraces as if they are in the source (#1350) --- test/cases/helper_sqlserver.rb | 1 + test/support/core_ext/backtrace_cleaner.rb | 36 ++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 test/support/core_ext/backtrace_cleaner.rb diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 4be7683b8..95edca121 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -5,6 +5,7 @@ Bundler.require :default, :development require "pry" require "support/core_ext/query_cache" +require "support/core_ext/backtrace_cleaner" require "support/minitest_sqlserver" require "support/test_in_memory_oltp" require "support/table_definition_sqlserver" diff --git a/test/support/core_ext/backtrace_cleaner.rb b/test/support/core_ext/backtrace_cleaner.rb new file mode 100644 index 000000000..54e2b3b8e --- /dev/null +++ b/test/support/core_ext/backtrace_cleaner.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +# Need to handle `ActiveRecord` lines like they are in the source rather than in the Rails gem. +module SQLServer + module BacktraceCleaner + extend ActiveSupport::Concern + + private + + def add_gem_filter + gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) } + return if gems_paths.empty? + + gems_regexp = %r{\A(#{gems_paths.join("|")})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)} + gems_result = '\3 (\4) \5' + + add_filter do |line| + if line.match?(/activerecord/) + line + else + line.sub(gems_regexp, gems_result) + end + end + end + + def add_gem_silencer + add_silencer do |line| + ActiveSupport::BacktraceCleaner::FORMATTED_GEMS_PATTERN.match?(line) && !/activerecord/.match?(line) + end + end + end +end + +ActiveSupport.on_load(:active_record) do + ActiveSupport::BacktraceCleaner.prepend(SQLServer::BacktraceCleaner) +end From bc72b1d2378fb6c0815ebf402cbf8291ef38332b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 14 Jul 2025 11:03:37 +0100 Subject: [PATCH 1385/1412] Update error message Ref: https://github.com/rails/rails/pull/55313 --- .../connection_adapters/sqlserver/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 996f81e91..bb48f5cc7 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -241,7 +241,7 @@ def rename_column(table_name, column_name, new_column_name) end def rename_index(table_name, old_name, new_name) - raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters" if new_name.length > index_name_length + raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long (#{new_name.length} characters); the limit is #{index_name_length} characters" if new_name.length > index_name_length identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}") execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX" From 7d96477b5d2d4a07e7f46680876c4e87114447d5 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 14 Jul 2025 11:04:08 +0100 Subject: [PATCH 1386/1412] Coerce tests to match SQL for limit (#1351) --- test/cases/coerced_tests.rb | 62 +++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index cf9e26db4..caee1e15b 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2898,3 +2898,65 @@ def raw_transaction_open?(connection) end end end + +class EachTest < ActiveRecord::TestCase + # Match SQL Server limit implementation. + coerce_tests! :test_in_batches_executes_range_queries_when_unconstrained + def test_in_batches_executes_range_queries_when_unconstrained_coerced + quoted_posts_id = Regexp.escape(quote_table_name("posts.id")) + + relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET @\S ROWS FETCH NEXT @\S ROWS ONLY/i, count: 6) do + assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 1) do + Post.in_batches(of: 2).to_a + end + end + + assert_queries_match(/WHERE #{quoted_posts_id} > .+ AND #{quoted_posts_id} <= .+/i) do + relations.each { |relation| assert_kind_of Post, relation.first } + end + end + + # Match SQL Server limit implementation. + coerce_tests! :test_in_batches_executes_in_queries_when_unconstrained_and_opted_out_of_ranges + def test_in_batches_executes_in_queries_when_unconstrained_and_opted_out_of_ranges_coerced + quoted_posts_id = Regexp.escape(quote_table_name("posts.id")) + + relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 6) do + Post.in_batches(of: 2, use_ranges: false).to_a + end + + assert_queries_match(/#{quoted_posts_id} IN \(.+\)/i) do + relations.each { |relation| assert_kind_of Post, relation.first } + end + end + + # Match SQL Server limit implementation. + coerce_tests! :test_in_batches_executes_in_queries_when_constrained + def test_in_batches_executes_in_queries_when_constrained_coerced + quoted_posts_id = Regexp.escape(quote_table_name("posts.id")) + + relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 3) do + Post.where("id < ?", 5).in_batches(of: 2).to_a + end + + assert_queries_match(/#{quoted_posts_id} IN \(.+\)/i) do + relations.each { |relation| assert_kind_of Post, relation.first } + end + end + + # Match SQL Server limit implementation. + coerce_tests! :test_in_batches_executes_range_queries_when_constrained_and_opted_in_into_ranges + def test_in_batches_executes_range_queries_when_constrained_and_opted_in_into_ranges_coerced + quoted_posts_id = Regexp.escape(quote_table_name("posts.id")) + + relations = assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET @\S ROWS FETCH NEXT @\S ROWS ONLY/i, count: 3) do + assert_queries_match(/ORDER BY #{quoted_posts_id} ASC OFFSET 0 ROWS FETCH NEXT @\S ROWS ONLY/i, count: 1) do + Post.where("id < ?", 5).in_batches(of: 2, use_ranges: true).to_a + end + end + + assert_queries_match(/#{quoted_posts_id} > .+ AND #{quoted_posts_id} <= .+/i) do + relations.each { |relation| assert_kind_of Post, relation.first } + end + end +end From 0adea5b20516661b510a7e571f3170152484fc16 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 3 Aug 2025 12:17:56 +0100 Subject: [PATCH 1387/1412] Fix CI (#1354) --- Gemfile | 4 ++-- compose.ci.yaml | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index f2a7b8885..cbb406565 100644 --- a/Gemfile +++ b/Gemfile @@ -7,8 +7,8 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } gemspec gem "bcrypt" -gem "pg", ">= 0.18.0" -gem "sqlite3", ">= 1.6.6" +gem "pg", "1.5.9" +gem "sqlite3", ">= 2.1" gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] gem "benchmark-ips" gem "minitest", ">= 5.15.0" diff --git a/compose.ci.yaml b/compose.ci.yaml index 1e02f057d..8242db9b8 100644 --- a/compose.ci.yaml +++ b/compose.ci.yaml @@ -1,4 +1,3 @@ -version: "2.2" services: sqlserver: image: ghcr.io/rails-sqlserver/mssql-server-linux-rails From 09db3c16ce0f4957a0c206f159516c35f3da7edc Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Aug 2025 14:58:48 +0100 Subject: [PATCH 1388/1412] Support cross database inserts (#1357) --- .github/workflows/ci.yml | 1 + CHANGELOG.md | 1 + .../sqlserver/schema_statements.rb | 9 ++-- test/cases/adapter_test_sqlserver.rb | 45 ++++++++++++++----- test/cases/temp_test_sqlserver.rb | 9 ++++ 5 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 test/cases/temp_test_sqlserver.rb diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee0b1428b..9c6e9e41b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,7 @@ jobs: test: name: Run test suite runs-on: ubuntu-latest + timeout-minutes: 10 env: COMPOSE_FILE: compose.ci.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 169c88648..050b62f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,5 +18,6 @@ - [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert. - [#1338](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1338) Fix `insert_all`/`upsert_all` for table names containing numbers. - [#1345](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1345) Maintain index options during `change_column` operations. +- [#1357](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index bb48f5cc7..44201c922 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -593,6 +593,7 @@ def column_type(ci:) end def column_definitions_sql(database, identifier) + database = "TEMPDB" if identifier.temporary_table? schema_name = "schema_name()" if prepared_statements @@ -603,12 +604,8 @@ def column_definitions_sql(database, identifier) schema_name = quote(identifier.schema) if identifier.schema.present? end - object_id_arg = identifier.schema.present? ? "CONCAT(#{schema_name},'.',#{object_name})" : object_name - - if identifier.temporary_table? - database = "TEMPDB" - object_id_arg = "CONCAT('#{database}','..',#{object_name})" - end + object_id_arg = identifier.schema.present? ? "CONCAT('.',#{schema_name},'.',#{object_name})" : "CONCAT('..',#{object_name})" + object_id_arg = "CONCAT('#{database}',#{object_id_arg})" %{ SELECT diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index 3ae39d394..b6582ebcf 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -7,11 +7,18 @@ require "models/subscriber" require "models/minimalistic" require "models/college" +require "models/dog" +require "models/other_dog" require "models/discount" class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks + let(:arunit_connection) { Topic.lease_connection } + let(:arunit2_connection) { College.lease_connection } + let(:arunit_database) { arunit_connection.pool.db_config.database } + let(:arunit2_database) { arunit2_connection.pool.db_config.database } + let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } let(:basic_merge_sql) { "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name]" } let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" } @@ -51,8 +58,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert Topic.table_exists?, "Topics table name of 'dbo.topics' should return true for exists." # Test when database and owner included in table name. - db_config = ActiveRecord::Base.configurations.configs_for(env_name: "arunit", name: "primary") - Topic.table_name = "#{db_config.database}.dbo.topics" + Topic.table_name = "#{arunit_database}.dbo.topics" assert Topic.table_exists?, "Topics table name of '[DATABASE].dbo.topics' should return true for exists." ensure Topic.table_name = "topics" @@ -110,13 +116,13 @@ 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 @@ -231,6 +237,9 @@ class AdapterTestSQLServer < ActiveRecord::TestCase @identity_insert_sql_non_dbo_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([id],[name]) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unquoted_sp = "EXEC sp_executesql N'INSERT INTO test.aliens (id, name) VALUES (@0, @1)', N'@0 int, @1 nvarchar(255)', @0 = 420, @1 = N'Mork'" @identity_insert_sql_non_dbo_unordered_sp = "EXEC sp_executesql N'INSERT INTO [test].[aliens] ([name],[id]) VALUES (@0, @1)', N'@0 nvarchar(255), @1 int', @0 = N'Mork', @1 = 420" + + @non_identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs SELECT * FROM #{arunit_database}.dbo.dogs" + @identity_insert_sql_cross_database = "INSERT INTO #{arunit2_database}.dbo.dogs(id) SELECT id FROM #{arunit_database}.dbo.dogs" end it "return quoted table_name to #query_requires_identity_insert? when INSERT sql contains id column" do @@ -251,20 +260,32 @@ class AdapterTestSQLServer < ActiveRecord::TestCase assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unquoted_sp) assert_equal "[test].[aliens]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_non_dbo_unordered_sp) + + assert_equal "[#{arunit2_database}].[dbo].[dogs]", connection.send(:query_requires_identity_insert?, @identity_insert_sql_cross_database) end it "return false to #query_requires_identity_insert? for normal SQL" do - [basic_insert_sql, basic_merge_sql, basic_update_sql, basic_select_sql].each do |sql| + [basic_insert_sql, basic_merge_sql, basic_update_sql, basic_select_sql, @non_identity_insert_sql_cross_database].each do |sql| assert !connection.send(:query_requires_identity_insert?, sql), "SQL was #{sql}" end end - it "find identity column using #identity_columns" do + it "find identity column" do task_id_column = Task.columns_hash["id"] assert_equal task_id_column.name, connection.send(:identity_columns, Task.table_name).first.name assert_equal task_id_column.sql_type, connection.send(:identity_columns, Task.table_name).first.sql_type end + it "find identity column cross database" do + id_column = Dog.columns_hash["id"] + assert_equal id_column.name, arunit2_connection.send(:identity_columns, Dog.table_name).first.name + assert_equal id_column.sql_type, arunit2_connection.send(:identity_columns, Dog.table_name).first.sql_type + + id_column = OtherDog.columns_hash["id"] + assert_equal id_column.name, arunit_connection.send(:identity_columns, OtherDog.table_name).first.name + assert_equal id_column.sql_type, arunit_connection.send(:identity_columns, OtherDog.table_name).first.sql_type + end + it "return an empty array when calling #identity_columns for a table_name with no identity" do _(connection.send(:identity_columns, Subscriber.table_name)).must_equal [] end @@ -455,8 +476,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 @@ -482,8 +503,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 @@ -495,7 +516,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 @@ -510,7 +531,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase 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 diff --git a/test/cases/temp_test_sqlserver.rb b/test/cases/temp_test_sqlserver.rb new file mode 100644 index 000000000..c9fae9490 --- /dev/null +++ b/test/cases/temp_test_sqlserver.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" + +class TempTestSQLServer < ActiveRecord::TestCase + # it "assert true" do + # assert true + # end +end From 940c49d2e7e372c62a2f1b72f6b5ba5b46bd266c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 15 Aug 2025 14:59:44 +0100 Subject: [PATCH 1389/1412] Standardrb --- test/cases/adapter_test_sqlserver.rb | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/test/cases/adapter_test_sqlserver.rb b/test/cases/adapter_test_sqlserver.rb index b6582ebcf..6d5c87d61 100644 --- a/test/cases/adapter_test_sqlserver.rb +++ b/test/cases/adapter_test_sqlserver.rb @@ -14,10 +14,10 @@ class AdapterTestSQLServer < ActiveRecord::TestCase fixtures :tasks - let(:arunit_connection) { Topic.lease_connection } + let(:arunit_connection) { Topic.lease_connection } let(:arunit2_connection) { College.lease_connection } - let(:arunit_database) { arunit_connection.pool.db_config.database } - let(:arunit2_database) { arunit2_connection.pool.db_config.database } + let(:arunit_database) { arunit_connection.pool.db_config.database } + let(:arunit2_database) { arunit2_connection.pool.db_config.database } let(:basic_insert_sql) { "INSERT INTO [funny_jokes] ([name]) VALUES('Knock knock')" } let(:basic_merge_sql) { "MERGE INTO [ships] WITH (UPDLOCK, HOLDLOCK) AS target USING ( SELECT * FROM ( SELECT [id], [name], ROW_NUMBER() OVER ( PARTITION BY [id] ORDER BY [id] DESC ) AS rn_0 FROM ( VALUES (101, N'RSS Sir David Attenborough') ) AS t1 ([id], [name]) ) AS ranked_source WHERE rn_0 = 1 ) AS source ON (target.[id] = source.[id]) WHEN MATCHED THEN UPDATE SET target.[name] = source.[name]" } @@ -116,13 +116,13 @@ 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 @@ -476,8 +476,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 @@ -503,8 +503,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 @@ -516,7 +516,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 @@ -531,7 +531,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase 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 From 0c779df3a1e954e57d38c46f9c677df09f702253 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 1 Sep 2025 11:32:20 +0100 Subject: [PATCH 1390/1412] InsertAll fixes Refactoring required after https://github.com/rails/rails/commit/ec032b10ff701c5f0425d2cffa0f0b69b2bda6b1 --- .../connection_adapters/sqlserver/database_statements.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 72290ab1d..096bff4ff 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -208,13 +208,13 @@ def merge_insert_values_list(insert:, insert_all:) connection = insert.send(:connection) identity_index = 0 - types = insert.send(:extract_types_from_columns_on, insert.model.table_name, keys: insert.keys_including_timestamps) + types = insert.send(:extract_types_for, insert.keys_including_timestamps) values_list = insert_all.map_key_with_value do |key, value| if Arel::Nodes::SqlLiteral === value value elsif insert.primary_keys.include?(key) && value.nil? - column = insert.send(:column_from_key, key) + column = insert.model.columns_hash[key] if column.is_identity? identity_index += 1 From 6cc4db608b20ffb851b5c2b0fdb0f874a5bf2274 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 1 Sep 2025 11:39:15 +0100 Subject: [PATCH 1391/1412] Add `ActiveRecord::CheckViolation` error class for check constraint violations. Ref: https://github.com/rails/rails/commit/2cffe069f26c95c14ba1136b45e504696b8c4e97 --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 70eab807d..ae96199f5 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -495,6 +495,8 @@ def translate_exception(exception, message:, sql:, binds:) NotNullViolation.new(message, sql: sql, binds: binds, connection_pool: @pool) when /Arithmetic overflow error/ RangeError.new(message, sql: sql, binds: binds, connection_pool: @pool) + when /statement conflicted with the CHECK constraint/ + CheckViolation.new(message, sql: sql, binds: binds, connection_pool: @pool) else super end From 1b90779b154bd0b496eb85a4aa193f02d834b924 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 8 Sep 2025 14:03:28 +0100 Subject: [PATCH 1392/1412] Rails v8.1.0.beta1 released --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 5cbe0a3a7..4da3cda1f 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -26,6 +26,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.1.0.alpha" + spec.add_dependency "activerecord", "~> 8.1.0.beta1" spec.add_dependency "tiny_tds", "~> 3.0" end From 3972fda1fdd8cac5a11c9e8f66530c4d8b977e01 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 8 Sep 2025 14:04:01 +0100 Subject: [PATCH 1393/1412] Call verified! if connection active --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index ae96199f5..0d275f974 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -280,7 +280,10 @@ def disable_referential_integrity # === Abstract Adapter (Connection Management) ================== # def active? - @raw_connection&.active? + if @raw_connection&.active? + verified! + true + end rescue *connection_errors false end From d3306ad7446476514859ee548d8250f1cd7d876b Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Mon, 15 Sep 2025 09:54:50 +0100 Subject: [PATCH 1394/1412] Standardrb fixes --- .../connection_adapters/sqlserver/schema_statements.rb | 4 ++-- test/support/query_assertions.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index 44201c922..ff9eaa5de 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -717,11 +717,11 @@ def get_raw_table_name(sql) .split(/\bSELECT\b(?![^\[]*\])/i)[0] .match(/\s*([^(]*)/i)[0] elsif s.match?(/^\s*UPDATE\s+.*/i) - s.match(/UPDATE\s+([^\(\s]+)\s*/i)[1] + s.match(/UPDATE\s+([^(\s]+)\s*/i)[1] elsif s.match?(/^\s*MERGE INTO.*/i) s.match(/^\s*MERGE\s+INTO\s+(\[?[a-z0-9_ -]+\]?\.?\[?[a-z0-9_ -]+\]?)\s+(AS|WITH|USING)/i)[1] else - s.match(/FROM[\s|\(]+((\[[^\(\]]+\])|[^\(\s]+)\s*/i)[1] + s.match(/FROM[\s|(]+((\[[^(\]]+\])|[^(\s]+)\s*/i)[1] end.strip end diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb index e01c67bc4..2abd98159 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.#{"\nQueries:\n#{queries.join("\n")}" unless queries.empty?}" end result end From 4c373e404faf981f8f1a7fc2039a61e4d4ddd750 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 24 Sep 2025 13:21:44 +0100 Subject: [PATCH 1395/1412] Rails 7.1 is no longer supported --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5e5586b22..fca8a2489 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions | Unreleased | `8.1.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | | `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | -| `7.1.x` | `7.1.x` | Active | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | | `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | From d12bfe3bdcd5f991cab370d6333bb1236595b42c Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 28 Sep 2025 11:32:08 +0100 Subject: [PATCH 1396/1412] Do not modify frozen string --- lib/active_record/connection_adapters/sqlserver_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index 0d275f974..c33c0bb67 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -107,7 +107,7 @@ def dbconsole(config, options = {}) if sqlserver_config[:host] host_arg = "tcp:#{sqlserver_config[:host]}" - host_arg << ",#{sqlserver_config[:port]}" if sqlserver_config[:port] + host_arg += ",#{sqlserver_config[:port]}" if sqlserver_config[:port] args += ["-S", host_arg] end From 8217d47c61896977ee015253b52a947afdef7354 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 28 Sep 2025 11:33:39 +0100 Subject: [PATCH 1397/1412] Modify namespace of ActiveRecord explain class Ref: https://github.com/rails/rails/pull/55736 --- .../sqlserver/core_ext/explain_subscriber.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb index f151cad74..80dd6a2cf 100644 --- a/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +++ b/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb @@ -5,6 +5,6 @@ ActiveSupport.on_load(:active_record) do silence_warnings do # Already defined in Rails - ActiveRecord::ExplainSubscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i + ActiveRecord::ExplainRegistry::Subscriber::EXPLAINED_SQLS = /(select|update|delete|insert)\b/i end end From c698edd3db809ffb69039cad352026099cace33f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Oct 2025 14:11:18 +0100 Subject: [PATCH 1398/1412] Coerce tests (#1368) --- test/cases/coerced_tests.rb | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index caee1e15b..f3ec9de32 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2959,4 +2959,30 @@ def test_in_batches_executes_range_queries_when_constrained_and_opted_in_into_ra relations.each { |relation| assert_kind_of Post, relation.first } end end + + # Match SQL Server SQL format. + coerce_tests! :test_in_batches_should_unscope_cursor_after_pluck + def test_in_batches_should_unscope_cursor_after_pluck_coerced + all_ids = Post.limit(2).pluck(:id) + found_ids = [] + # only a single clause on id (i.e. not 'id IN (?,?) AND id = ?', but only 'id = ?') + assert_queries_match(/WHERE #{Regexp.escape(quote_table_name("posts.id"))} = \S+ ORDER BY/) do + Post.where(id: all_ids).in_batches(of: 1) do |relation| + found_ids << relation.pick(:id) + end + end + assert_equal all_ids.sort, found_ids + end + + # Match SQL Server SQL format. + coerce_tests! :test_in_batches_loaded_should_unscope_cursor_after_pluck + def test_in_batches_loaded_should_unscope_cursor_after_pluck_coerced + all_ids = Post.limit(2).pluck(:id) + # only a single clause on id (i.e. not 'id IN (?,?) AND id = ?', but only 'id = ?') + assert_queries_match(/WHERE #{Regexp.escape(quote_table_name("posts.id"))} = \S+;/) do + Post.where(id: all_ids).in_batches(of: 1, load: true) do |relation| + relation.delete_all + end + end + end end From a7272455c0cc05786f275a3ed1e986dfe8548930 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Wed, 8 Oct 2025 16:31:12 +0100 Subject: [PATCH 1399/1412] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 050b62f36..4ff2dfdbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,11 +4,11 @@ - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for `INDEX INCLUDE`. - [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`. -- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting. #### Changed - [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. +- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting. - [#1343](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1343) Support more Azure services by changing language source. #### Fixed From dfdb318c10b9a21c3fb036c1720d312c155ddacb Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 18 Oct 2025 17:21:20 +0100 Subject: [PATCH 1400/1412] Added support for computed columns (#1367) --- CHANGELOG.md | 1 + README.md | 13 ++ .../sqlserver/schema_creation.rb | 27 +++-- .../sqlserver/schema_dumper.rb | 25 ++-- .../sqlserver/schema_statements.rb | 71 ++++++----- .../sqlserver/table_definition.rb | 4 +- .../connection_adapters/sqlserver_adapter.rb | 4 + .../connection_adapters/sqlserver_column.rb | 15 ++- test/cases/coerced_tests.rb | 2 +- test/cases/virtual_column_test_sqlserver.rb | 113 ++++++++++++++++++ 10 files changed, 221 insertions(+), 54 deletions(-) create mode 100644 test/cases/virtual_column_test_sqlserver.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ff2dfdbb..810a557b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for `INDEX INCLUDE`. - [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`. +- [#1367](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1367) Added support for computed columns. #### Changed diff --git a/README.md b/README.md index fca8a2489..0aa011417 100644 --- a/README.md +++ b/README.md @@ -186,6 +186,19 @@ The removal of duplicates happens during the SQL query. Because of this implementation, if you pass `on_duplicate` to `upsert_all`, make sure to assign your value to `target.[column_name]` (e.g. `target.status = GREATEST(target.status, 1)`). To access the values that you want to upsert, use `source.[column_name]`. +#### Computed Columns + +The adapter supports computed columns. They can either be virtual `stored: false` (default) and persisted `stored: true`. You can create a computed column in a migration like so: + +```ruby +create_table :users do |t| + t.string :name + t.virtual :lower_name, as: "LOWER(name)", stored: false + t.virtual :upper_name, as: "UPPER(name)", stored: true + t.virtual :name_length, as: "LEN(name)" +end +``` + ## New Rails Applications When creating a new Rails application you need to perform the following steps to connect a Rails application to a diff --git a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb index 866d2fab3..c7661a47d 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_creation.rb @@ -12,6 +12,12 @@ def supports_index_using? false end + def visit_ColumnDefinition(o) + column_sql = super + column_sql = column_sql.sub(" #{o.sql_type}", "") if o.options[:as].present? + column_sql + end + def visit_TableDefinition(o) if_not_exists = o.if_not_exists @@ -58,18 +64,17 @@ def quoted_include_columns(o) def add_column_options!(sql, options) sql << " DEFAULT #{quote_default_expression_for_column_definition(options[:default], options[:column])}" if options_include_default?(options) - if options[:collation].present? - sql << " COLLATE #{options[:collation]}" - end - if options[:null] == false - sql << " NOT NULL" - end - if options[:is_identity] == true - sql << " IDENTITY(1,1)" - end - if options[:primary_key] == true - sql << " PRIMARY KEY" + + sql << " COLLATE #{options[:collation]}" if options[:collation].present? + sql << " NOT NULL" if options[:null] == false + sql << " IDENTITY(1,1)" if options[:is_identity] == true + sql << " PRIMARY KEY" if options[:primary_key] == true + + if (as = options[:as]) + sql << " AS #{as}" + sql << " PERSISTED" if options[:stored] end + sql end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb index d5bb1b348..775754b5b 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb @@ -4,22 +4,31 @@ module ActiveRecord module ConnectionAdapters module SQLServer class SchemaDumper < ConnectionAdapters::SchemaDumper - SQLSEVER_NO_LIMIT_TYPES = [ - "text", - "ntext", - "varchar(max)", - "nvarchar(max)", - "varbinary(max)" - ].freeze + SQLSERVER_NO_LIMIT_TYPES = %w[text ntext varchar(max) nvarchar(max) varbinary(max)].freeze private + def prepare_column_options(column) + spec = super + + if @connection.supports_virtual_columns? && column.virtual? + spec[:as] = extract_expression_for_virtual_column(column) + spec[:stored] = column.virtual_stored? + end + + spec + end + + def extract_expression_for_virtual_column(column) + column.default_function.inspect + end + def explicit_primary_key_default?(column) column.type == :integer && !column.is_identity? end def schema_limit(column) - return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type) + return if SQLSERVER_NO_LIMIT_TYPES.include?(column.sql_type) super end diff --git a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb index ff9eaa5de..d5a28f438 100644 --- a/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/schema_statements.rb @@ -88,38 +88,47 @@ def index_include_columns(table_name, index_name) def columns(table_name) return [] if table_name.blank? - column_definitions(table_name).map do |ci| - sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name - sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options - - new_column( - ci[:name], - lookup_cast_type(ci[:type]), - ci[:default_value], - sql_type_metadata, - ci[:null], - ci[:default_function], - ci[:collation], - nil, - sqlserver_options - ) + definitions = column_definitions(table_name) + definitions.map do |field| + new_column_from_field(table_name, field, definitions) end end - def new_column(name, cast_type, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {}) + def new_column_from_field(_table_name, field, _definitions) + sqlserver_options = field.slice(:ordinal_position, :is_primary, :is_identity, :table_name) + sql_type_metadata = fetch_type_metadata(field[:type], sqlserver_options) + generated_type = extract_generated_type(field) + + default_function = if generated_type.present? + field[:computed_formula] + else + field[:default_function] + end + SQLServer::Column.new( - name, - cast_type, - default, + field[:name], + lookup_cast_type(field[:type]), + field[:default_value], sql_type_metadata, - null, + field[:null], default_function, - collation: collation, - comment: comment, + collation: field[:collation], + comment: nil, + generated_type: generated_type, **sqlserver_options ) end + def extract_generated_type(field) + if field[:is_computed] + if field[:is_persisted] + :stored + else + :virtual + end + end + end + def primary_keys(table_name) primaries = primary_keys_select(table_name) primaries.present? ? primaries : identity_columns(table_name).map(&:name) @@ -512,15 +521,7 @@ def column_definitions(table_name) raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if results.empty? results.map do |ci| - col = { - 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"] - } + col = ci.slice("name", "numeric_scale", "numeric_precision", "datetime_precision", "collation", "ordinal_position", "length", "is_computed", "is_persisted", "computed_formula").symbolize_keys col[:table_name] = view_exists ? view_table_name(table_name) : table_name col[:type] = column_type(ci: ci) @@ -640,7 +641,10 @@ def column_definitions_sql(database, identifier) WHEN ic.object_id IS NOT NULL THEN 1 END AS [is_primary], - c.is_identity AS [is_identity] + c.is_identity AS [is_identity], + c.is_computed AS [is_computed], + cc.is_persisted AS [is_persisted], + cc.definition AS [computed_formula] FROM #{database}.sys.columns c INNER JOIN #{database}.sys.objects o ON c.object_id = o.object_id @@ -659,6 +663,9 @@ def column_definitions_sql(database, identifier) ON k.parent_object_id = ic.object_id AND k.unique_index_id = ic.index_id AND c.column_id = ic.column_id + LEFT OUTER JOIN #{database}.sys.computed_columns cc + ON c.object_id = cc.object_id + AND c.column_id = cc.column_id WHERE o.Object_ID = Object_ID(#{object_id_arg}) AND s.name = #{schema_name} diff --git a/lib/active_record/connection_adapters/sqlserver/table_definition.rb b/lib/active_record/connection_adapters/sqlserver/table_definition.rb index 5800ef0ca..e68a17785 100644 --- a/lib/active_record/connection_adapters/sqlserver/table_definition.rb +++ b/lib/active_record/connection_adapters/sqlserver/table_definition.rb @@ -109,6 +109,8 @@ def new_column_definition(name, type, **options) type = :datetime2 unless options[:precision].nil? when :primary_key options[:is_identity] = true + when :virtual + type = options[:type] end super @@ -117,7 +119,7 @@ def new_column_definition(name, type, **options) private def valid_column_definition_options - super + [:is_identity] + super + [:is_identity, :as, :stored] end end diff --git a/lib/active_record/connection_adapters/sqlserver_adapter.rb b/lib/active_record/connection_adapters/sqlserver_adapter.rb index c33c0bb67..9f8810ed1 100644 --- a/lib/active_record/connection_adapters/sqlserver_adapter.rb +++ b/lib/active_record/connection_adapters/sqlserver_adapter.rb @@ -265,6 +265,10 @@ def supports_insert_conflict_target? false end + def supports_virtual_columns? + true + end + def return_value_after_insert?(column) # :nodoc: column.is_primary? || column.is_identity? end diff --git a/lib/active_record/connection_adapters/sqlserver_column.rb b/lib/active_record/connection_adapters/sqlserver_column.rb index 86d106750..d30ba7444 100644 --- a/lib/active_record/connection_adapters/sqlserver_column.rb +++ b/lib/active_record/connection_adapters/sqlserver_column.rb @@ -6,12 +6,13 @@ module SQLServer class Column < ConnectionAdapters::Column delegate :is_identity, :is_primary, :table_name, :ordinal_position, to: :sql_type_metadata - def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, **) + def initialize(*, is_identity: nil, is_primary: nil, table_name: nil, ordinal_position: nil, generated_type: nil, **) super @is_identity = is_identity @is_primary = is_primary @table_name = table_name @ordinal_position = ordinal_position + @generated_type = generated_type end def is_identity? @@ -31,6 +32,18 @@ def case_sensitive? collation&.match(/_CS/) end + def virtual? + @generated_type.present? + end + + def virtual_stored? + @generated_type == :stored + end + + def has_default? + super && !virtual? + end + def init_with(coder) @is_identity = coder["is_identity"] @is_primary = coder["is_primary"] diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index f3ec9de32..a7429f7d1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2626,7 +2626,7 @@ class InvalidOptionsTest < ActiveRecord::TestCase undef_method :invalid_add_column_option_exception_message def invalid_add_column_option_exception_message(key) default_keys = [":limit", ":precision", ":scale", ":default", ":null", ":collation", ":comment", ":primary_key", ":if_exists", ":if_not_exists"] - default_keys.concat([":is_identity"]) # SQL Server additional valid keys + default_keys.concat([":is_identity", ":as", ":stored"]) # SQL Server additional valid keys "Unknown key: :#{key}. Valid keys are: #{default_keys.join(", ")}" end diff --git a/test/cases/virtual_column_test_sqlserver.rb b/test/cases/virtual_column_test_sqlserver.rb new file mode 100644 index 000000000..44d740552 --- /dev/null +++ b/test/cases/virtual_column_test_sqlserver.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +require "cases/helper_sqlserver" +require "support/schema_dumping_helper" + +class VirtualColumnTestSQLServer < ActiveRecord::TestCase + include SchemaDumpingHelper + + class VirtualColumn < ActiveRecord::Base + end + + def setup + @connection = ActiveRecord::Base.lease_connection + @connection.create_table :virtual_columns, force: true do |t| + t.string :name + t.virtual :upper_name, as: "UPPER(name)", stored: true + t.virtual :lower_name, as: "LOWER(name)", stored: false + t.virtual :octet_name, as: "LEN(name)" + t.virtual :mutated_name, as: "REPLACE(name, 'l', 'L')" + t.integer :column1 + end + VirtualColumn.create(name: "Rails", column1: 10) + end + + def teardown + @connection.drop_table :virtual_columns, if_exists: true + VirtualColumn.reset_column_information + end + + def test_virtual_column_with_full_inserts + partial_inserts_was = VirtualColumn.partial_inserts + VirtualColumn.partial_inserts = false + assert_nothing_raised do + VirtualColumn.create!(name: "Rails") + end + ensure + VirtualColumn.partial_inserts = partial_inserts_was + end + + def test_stored_column + column = VirtualColumn.columns_hash["upper_name"] + assert_predicate column, :virtual? + assert_predicate column, :virtual_stored? + assert_equal "RAILS", VirtualColumn.take.upper_name + end + + def test_explicit_virtual_column + column = VirtualColumn.columns_hash["lower_name"] + assert_predicate column, :virtual? + assert_not_predicate column, :virtual_stored? + assert_equal "rails", VirtualColumn.take.lower_name + end + + def test_implicit_virtual_column + column = VirtualColumn.columns_hash["octet_name"] + assert_predicate column, :virtual? + assert_not_predicate column, :virtual_stored? + assert_equal 5, VirtualColumn.take.octet_name + end + + def test_virtual_column_with_comma_in_definition + column = VirtualColumn.columns_hash["mutated_name"] + assert_predicate column, :virtual? + assert_not_predicate column, :virtual_stored? + assert_not_nil column.default_function + assert_equal "RaiLs", VirtualColumn.take.mutated_name + end + + def test_change_table_with_stored_generated_column + @connection.change_table :virtual_columns do |t| + t.virtual :decr_column1, as: "column1 - 1", stored: true + end + VirtualColumn.reset_column_information + column = VirtualColumn.columns_hash["decr_column1"] + assert_predicate column, :virtual? + assert_predicate column, :virtual_stored? + assert_equal 9, VirtualColumn.take.decr_column1 + end + + def test_change_table_with_explicit_virtual_generated_column + @connection.change_table :virtual_columns do |t| + t.virtual :incr_column1, as: "column1 + 1", stored: false + end + VirtualColumn.reset_column_information + column = VirtualColumn.columns_hash["incr_column1"] + assert_predicate column, :virtual? + assert_not_predicate column, :virtual_stored? + assert_equal 11, VirtualColumn.take.incr_column1 + end + + def test_change_table_with_implicit_virtual_generated_column + @connection.change_table :virtual_columns do |t| + t.virtual :sqr_column1, as: "power(column1, 2)" + end + VirtualColumn.reset_column_information + column = VirtualColumn.columns_hash["sqr_column1"] + assert_predicate column, :virtual? + assert_not_predicate column, :virtual_stored? + assert_equal 100, VirtualColumn.take.sqr_column1 + end + + def test_schema_dumping + output = dump_table_schema("virtual_columns") + assert_match(/t\.virtual\s+"lower_name",\s+as: "\(lower\(\[name\]\)\)", stored: false$/i, output) + assert_match(/t\.virtual\s+"upper_name",\s+as: "\(upper\(\[name\]\)\)", stored: true$/i, output) + assert_match(/t\.virtual\s+"octet_name",\s+as: "\(len\(\[name\]\)\)", stored: false$/i, output) + end + + def test_build_fixture_sql + fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :virtual_columns).first + assert_equal 2, fixtures.size + end +end From f0f30fd662f7f0c886ea34d584f916fe981b340a Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 18 Oct 2025 17:27:50 +0100 Subject: [PATCH 1401/1412] Updated CI for Rails 8-1-stable branch --- .devcontainer/Dockerfile | 2 +- Dockerfile.ci | 2 +- activerecord-sqlserver-adapter.gemspec | 2 +- compose.ci.yaml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 6d4c1a2af..5068bd219 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/t RUN mkdir -p /tmp/activerecord-sqlserver-adapter COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ RUN cd /tmp/activerecord-sqlserver-adapter \ - && RAILS_BRANCH=main bundle install \ + && RAILS_BRANCH=8-1-stable bundle install \ && rm -rf /tmp/activerecord-sqlserver-adapter RUN chown -R vscode:vscode /usr/local/rvm diff --git a/Dockerfile.ci b/Dockerfile.ci index 74f636508..222008702 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_BRANCH=main bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=8-1-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index 4da3cda1f..bad85d912 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -26,6 +26,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.1.0.beta1" + spec.add_dependency "activerecord", "~> 8.1.0.rc1" spec.add_dependency "tiny_tds", "~> 3.0" end diff --git a/compose.ci.yaml b/compose.ci.yaml index 8242db9b8..6997c249b 100644 --- a/compose.ci.yaml +++ b/compose.ci.yaml @@ -4,7 +4,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_BRANCH=main + - RAILS_BRANCH=8-1-stable build: context: . dockerfile: Dockerfile.ci @@ -13,7 +13,7 @@ services: - "sqlserver" standardrb: environment: - - RAILS_BRANCH=main + - RAILS_BRANCH=8-1-stable build: context: . dockerfile: Dockerfile.ci From d9694ab2116635bf6cbd986c822b828381ec485e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sat, 18 Oct 2025 20:35:26 +0100 Subject: [PATCH 1402/1412] Coerce test --- test/cases/coerced_tests.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a7429f7d1..80e0de565 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2883,6 +2883,23 @@ def test_with_recursive_coerced assert_equal top_companies_and_children, relation.order(:id).pluck(:id) assert_match "WITH ", relation.to_sql end + + # Use truthy condition instead of "WHERE true". + # https://github.com/rails/rails/pull/55938 + coerce_tests! :test_with_when_hash_with_multiple_elements_of_different_type_is_passed_as_an_argument + def test_with_when_hash_with_multiple_elements_of_different_type_is_passed_as_an_argument_coerced + # standard:disable Style/HashSyntax + cte_options = { + posts_with_tags: Post.arel_table.project(Arel.star).where(Post.arel_table[:tags_count].gt(0)), + posts_with_tags_and_truthy: Arel.sql("SELECT * FROM posts_with_tags WHERE 1=1"), + posts_with_tags_and_comments: Arel.sql("SELECT * FROM posts_with_tags_and_truthy WHERE tags_count > ?", 0), + "posts_with_tags_and_multiple_comments" => Post.where("legacy_comments_count > 1").from("posts_with_tags_and_comments AS posts") + } + # standard:enable Style/HashSyntax + relation = Post.with(cte_options).from("posts_with_tags_and_multiple_comments AS posts") + + assert_equal POSTS_WITH_TAGS_AND_MULTIPLE_COMMENTS, relation.order(:id).pluck(:id) + end end end From ce32333a37100c46fa3140047d33864a1700407e Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Sun, 19 Oct 2025 10:24:07 +0100 Subject: [PATCH 1403/1412] Test no longer needs to be coerced --- test/cases/coerced_tests.rb | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index 80e0de565..a7429f7d1 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -2883,23 +2883,6 @@ def test_with_recursive_coerced assert_equal top_companies_and_children, relation.order(:id).pluck(:id) assert_match "WITH ", relation.to_sql end - - # Use truthy condition instead of "WHERE true". - # https://github.com/rails/rails/pull/55938 - coerce_tests! :test_with_when_hash_with_multiple_elements_of_different_type_is_passed_as_an_argument - def test_with_when_hash_with_multiple_elements_of_different_type_is_passed_as_an_argument_coerced - # standard:disable Style/HashSyntax - cte_options = { - posts_with_tags: Post.arel_table.project(Arel.star).where(Post.arel_table[:tags_count].gt(0)), - posts_with_tags_and_truthy: Arel.sql("SELECT * FROM posts_with_tags WHERE 1=1"), - posts_with_tags_and_comments: Arel.sql("SELECT * FROM posts_with_tags_and_truthy WHERE tags_count > ?", 0), - "posts_with_tags_and_multiple_comments" => Post.where("legacy_comments_count > 1").from("posts_with_tags_and_comments AS posts") - } - # standard:enable Style/HashSyntax - relation = Post.with(cte_options).from("posts_with_tags_and_multiple_comments AS posts") - - assert_equal POSTS_WITH_TAGS_AND_MULTIPLE_COMMENTS, relation.order(:id).pluck(:id) - end end end From 8a585f12b8cf080e8fb6b56a9cadb0d358bf9623 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Thu, 6 Nov 2025 19:34:59 +0000 Subject: [PATCH 1404/1412] Support for 7.2 has ended --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0aa011417..4b13af660 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions |-----------------|---------------|----------------|----------------------------------------------------------------------------------------------------| | Unreleased | `8.1.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | -| `7.2.x` | `7.2.x` | Active | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | +| `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | | `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | | `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | From 1495a22e27a490f1cab6d057f5df37a9d91f0a8f Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 19:38:23 +0000 Subject: [PATCH 1405/1412] Fixed query logging so that filter parameters are respected (#1372) Co-authored-by: Matt Taylor <70293171+mtaylor-vailsys@users.noreply.github.com> --- .../sqlserver/database_statements.rb | 14 +- test/cases/coerced_tests.rb | 209 +++--------------- test/cases/optimizer_hints_test_sqlserver.rb | 2 +- test/cases/showplan_test_sqlserver.rb | 4 +- test/cases/specific_schema_test_sqlserver.rb | 12 +- test/support/query_assertions.rb | 22 ++ 6 files changed, 66 insertions(+), 197 deletions(-) diff --git a/lib/active_record/connection_adapters/sqlserver/database_statements.rb b/lib/active_record/connection_adapters/sqlserver/database_statements.rb index 096bff4ff..daf635d63 100644 --- a/lib/active_record/connection_adapters/sqlserver/database_statements.rb +++ b/lib/active_record/connection_adapters/sqlserver/database_statements.rb @@ -14,6 +14,11 @@ def write_query?(sql) # :nodoc: end def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) + unless binds.nil? || binds.empty? + types, params = sp_executesql_types_and_parameters(binds) + sql = sp_executesql_sql(sql, types, params, notification_payload[:name]) + end + id_insert_table_name = query_requires_identity_insert?(sql) result, affected_rows = if id_insert_table_name @@ -48,15 +53,6 @@ def affected_rows_from_results_or_handle(raw_result, handle) 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) - unless binds.nil? || binds.empty? - types, params = sp_executesql_types_and_parameters(binds) - sql = sp_executesql_sql(sql, types, params, name) - end - - super - end - def internal_exec_sql_query(sql, conn) handle = internal_raw_execute(sql, conn) results = handle_to_names_and_values(handle, ar_result: true) diff --git a/test/cases/coerced_tests.rb b/test/cases/coerced_tests.rb index a7429f7d1..0615ff254 100644 --- a/test/cases/coerced_tests.rb +++ b/test/cases/coerced_tests.rb @@ -261,7 +261,7 @@ def test_belongs_to_with_primary_key_joins_on_correct_column_coerced def test_belongs_to_coerced client = Client.find(3) first_firm = companies(:first_firm) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_and_values_match(/FETCH NEXT @3 ROWS ONLY/, ["Firm", "Agency", 1, 1]) do assert_equal first_firm, client.firm assert_equal first_firm.name, client.firm.name end @@ -270,21 +270,6 @@ def test_belongs_to_coerced module ActiveRecord class BindParameterTest < ActiveRecord::TestCase - # Same as original coerced test except log is found using `EXEC sp_executesql` wrapper. - coerce_tests! :test_binds_are_logged - def test_binds_are_logged_coerced - sub = Arel::Nodes::BindParam.new(1) - binds = [Relation::QueryAttribute.new("id", 1, Type::Value.new)] - sql = "select * from topics where id = #{sub.to_sql}" - - @connection.exec_query(sql, "SQL", binds) - - logged_sql = "EXEC sp_executesql N'#{sql}', N'#{sub.to_sql} int', #{sub.to_sql} = 1" - message = @subscriber.calls.find { |args| args[4][:sql] == logged_sql } - - assert_equal binds, message[4][:binds] - end - # SQL Server adapter does not use a statement cache as query plans are already reused using `EXEC sp_executesql`. coerce_tests! :test_statement_cache coerce_tests! :test_statement_cache_with_query_cache @@ -292,55 +277,6 @@ def test_binds_are_logged_coerced coerce_tests! :test_statement_cache_with_find_by coerce_tests! :test_statement_cache_with_in_clause coerce_tests! :test_statement_cache_with_sql_string_literal - - # Same as original coerced test except prepared statements include `EXEC sp_executesql` wrapper. - coerce_tests! :test_bind_params_to_sql_with_prepared_statements, :test_bind_params_to_sql_with_unprepared_statements - def test_bind_params_to_sql_with_prepared_statements_coerced - assert_bind_params_to_sql_coerced(prepared: true) - end - - def test_bind_params_to_sql_with_unprepared_statements_coerced - @connection.unprepared_statement do - assert_bind_params_to_sql_coerced(prepared: false) - end - end - - private - - def assert_bind_params_to_sql_coerced(prepared:) - table = Author.quoted_table_name - pk = "#{table}.#{Author.quoted_primary_key}" - - # prepared_statements: true - # - # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2) OR [authors].[id] IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 - # - # prepared_statements: false - # - # SELECT [authors].* FROM [authors] WHERE ([authors].[id] IN (1, 2, 3) OR [authors].[id] IS NULL) - # - sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)" - sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE (#{pk} IN (#{bind_params(1..3)}) OR #{pk} IS NULL)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" - - authors = Author.where(id: [1, 2, 3, nil]) - assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } - - # prepared_statements: true - # - # EXEC sp_executesql N'SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (@0, @1, @2)', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3 - # - # prepared_statements: false - # - # SELECT [authors].* FROM [authors] WHERE [authors].[id] IN (1, 2, 3) - # - sql_unprepared = "SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})" - sql_prepared = "EXEC sp_executesql N'SELECT #{table}.* FROM #{table} WHERE #{pk} IN (#{bind_params(1..3)})', N'@0 bigint, @1 bigint, @2 bigint', @0 = 1, @1 = 2, @2 = 3" - - authors = Author.where(id: [1, 2, 3, 9223372036854775808]) - assert_equal sql_unprepared, @connection.to_sql(authors.arel) - assert_queries_match(prepared ? sql_prepared : sql_unprepared) { assert_equal 3, authors.length } - end end end @@ -531,7 +467,7 @@ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar_coerced def test_limit_is_kept_coerced queries = capture_sql { Account.limit(1).count } assert_equal 1, queries.length - assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/, queries.first) + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, queries.first) end # Match SQL Server limit implementation @@ -539,7 +475,7 @@ def test_limit_is_kept_coerced def test_limit_with_offset_is_kept_coerced queries = capture_sql { Account.limit(1).offset(1).count } assert_equal 1, queries.length - assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY.*@0 = 1, @1 = 1/, queries.first) + assert_match(/ORDER BY \[accounts\]\.\[id\] ASC OFFSET @0 ROWS FETCH NEXT @1 ROWS ONLY/, queries.first) end # SQL Server needs an alias for the calculated column @@ -1066,9 +1002,9 @@ class FinderTest < ActiveRecord::TestCase # Assert SQL Server limit implementation coerce_tests! :test_take_and_first_and_last_with_integer_should_use_sql_limit def test_take_and_first_and_last_with_integer_should_use_sql_limit_coerced - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 3/) { Topic.take(3).entries } - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 2/) { Topic.first(2).entries } - assert_queries_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.* @0 = 5/) { Topic.last(5).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [3]) { Topic.take(3).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [2]) { Topic.first(2).entries } + assert_queries_and_values_match(/OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/, [5]) { Topic.last(5).entries } end # This fails only when run in the full test suite task. Just taking it out of the mix. @@ -1099,7 +1035,7 @@ def test_condition_local_time_interpolation_with_default_timezone_utc_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_match def test_include_on_unloaded_relation_with_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal true, Customer.where(name: "David").include?(customers(:david)) end end @@ -1107,7 +1043,7 @@ def test_include_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_without_match def test_include_on_unloaded_relation_without_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal false, Customer.where(name: "David").include?(customers(:mary)) end end @@ -1115,7 +1051,7 @@ def test_include_on_unloaded_relation_without_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_match def test_member_on_unloaded_relation_with_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal true, Customer.where(name: "David").member?(customers(:david)) end end @@ -1123,7 +1059,7 @@ def test_member_on_unloaded_relation_with_match_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_without_match def test_member_on_unloaded_relation_without_match_coerced - assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY.*@2 = 1/) do + assert_queries_match(/1 AS one.*FETCH NEXT @2 ROWS ONLY/) do assert_equal false, Customer.where(name: "David").member?(customers(:mary)) end end @@ -1138,7 +1074,7 @@ def test_implicit_order_column_is_configurable_with_a_single_value_coerced assert_equal topics(:third), Topic.last c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_and_values_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.title"))} DESC, #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) { Topic.last } ensure @@ -1151,7 +1087,7 @@ def test_implicit_order_column_is_configurable_with_multiple_values_coerced old_implicit_order_column = Topic.implicit_order_column Topic.implicit_order_column = ["title", "author_name"] - assert_queries_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.title"))} DESC, #{Regexp.escape(quote_table_name("topics.author_name"))} DESC, #{Regexp.escape(quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_and_values_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.title"))} DESC, #{Regexp.escape(quote_table_name("topics.author_name"))} DESC, #{Regexp.escape(quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) { Topic.last } ensure @@ -1164,7 +1100,7 @@ def test_ordering_does_not_append_primary_keys_or_query_constraints_if_passed_an old_implicit_order_column = Topic.implicit_order_column Topic.implicit_order_column = ["author_name", nil] - assert_queries_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.author_name"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_and_values_match(/ORDER BY #{Regexp.escape(quote_table_name("topics.author_name"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) { Topic.last } ensure @@ -1178,7 +1114,7 @@ def test_implicit_order_set_to_primary_key_coerced Topic.implicit_order_column = "id" c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_and_values_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) { Topic.last } ensure @@ -1193,7 +1129,7 @@ def test_implicit_order_for_model_without_primary_key_coerced c = NonPrimaryKey.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY.*@0 = 1/i) { + assert_queries_and_values_match(/ORDER BY #{Regexp.escape(c.quote_table_name("non_primary_keys.created_at"))} DESC OFFSET 0 ROWS FETCH NEXT @0 ROWS ONLY/i, [1]) { NonPrimaryKey.last } ensure @@ -1203,7 +1139,7 @@ def test_implicit_order_for_model_without_primary_key_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_member_on_unloaded_relation_with_composite_primary_key def test_member_on_unloaded_relation_with_composite_primary_key_coerced - assert_queries_match(/1 AS one.* FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.* FETCH NEXT @3 ROWS ONLY/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").member?(book) end @@ -1218,7 +1154,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) quoted_descrption = Regexp.escape(c.quote_table_name("clothing_items.description")) - assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_descrption} ASC, #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1232,7 +1168,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} DESC, #{quoted_color} DESC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.last end end @@ -1244,7 +1180,7 @@ def test_implicit_order_column_prepends_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_type} ASC, #{quoted_color} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end end @@ -1257,7 +1193,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced quoted_type = Regexp.escape(c.quote_table_name("clothing_items.clothing_type")) quoted_color = Regexp.escape(c.quote_table_name("clothing_items.color")) - assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/i) do + assert_queries_match(/ORDER BY #{quoted_color} ASC, #{quoted_type} ASC OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/i) do assert_kind_of ClothingItem, ClothingItem.first end ensure @@ -1267,7 +1203,7 @@ def test_implicit_order_column_reorders_query_constraints_coerced # Check for `FETCH NEXT x ROWS` rather then `LIMIT`. coerce_tests! :test_include_on_unloaded_relation_with_composite_primary_key def test_include_on_unloaded_relation_with_composite_primary_key_coerced - assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1/) do + assert_queries_match(/1 AS one.*OFFSET 0 ROWS FETCH NEXT @(\d) ROWS ONLY/) do book = cpk_books(:cpk_great_author_first_book) assert Cpk::Book.where(title: "The first book").include?(book) end @@ -1277,11 +1213,11 @@ def test_include_on_unloaded_relation_with_composite_primary_key_coerced coerce_tests! :test_nth_to_last_with_order_uses_limit def test_nth_to_last_with_order_uses_limit_coerced c = Topic.lease_connection - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.id"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do Topic.second_to_last end - assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY.*@\1 = 1.*@\2 = 1/i) do + assert_queries_match(/ORDER BY #{Regexp.escape(c.quote_table_name("topics.updated_at"))} DESC OFFSET @(\d) ROWS FETCH NEXT @(\d) ROWS ONLY/i) do Topic.order(:updated_at).second_to_last end end @@ -1332,7 +1268,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase def test_has_one_coerced firm = companies(:first_firm) first_account = Account.find(1) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do assert_equal first_account, firm.account assert_equal first_account.credit_limit, firm.account.credit_limit end @@ -1344,7 +1280,7 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase coerce_tests! :test_has_one_through_executes_limited_query def test_has_one_through_executes_limited_query_coerced boring_club = clubs(:boring_club) - assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY(.)*@\1 = 1/) do + assert_queries_match(/FETCH NEXT @(\d) ROWS ONLY/) do assert_equal boring_club, @member.general_club end end @@ -1565,7 +1501,7 @@ def test_having_with_binds_for_both_where_and_having # Find any limit via our expression. coerce_tests! %r{relations don't load all records in #inspect} def test_relations_dont_load_all_records_in_inspect_coerced - assert_queries_match(/NEXT @0 ROWS.*@0 = \d+/) do + assert_queries_match(/NEXT @0 ROWS/) do Post.all.inspect end end @@ -1678,7 +1614,7 @@ def test_dump_schema_versions_outputs_lexically_reverse_ordered_versions_regardl schema_info = ActiveRecord::Base.lease_connection.dump_schema_versions expected = <<~STR - INSERT INTO #{quote_table_name("schema_migrations")} (version) VALUES + INSERT INTO #{ActiveRecord::Base.lease_connection.quote_table_name("schema_migrations")} (version) VALUES (N'20100301010101'), (N'20100201010101'), (N'20100101010101'); @@ -2257,7 +2193,7 @@ def test_merge_doesnt_duplicate_same_clauses_coerced non_mary_and_bob = Author.where.not(id: [mary, bob]) author_id = Author.lease_connection.quote_table_name("authors.id") - assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)'/) do + assert_queries_match(/WHERE #{Regexp.escape(author_id)} NOT IN \((@\d), \g<1>\)/) do assert_equal [david], non_mary_and_bob.merge(non_mary_and_bob) end @@ -2420,7 +2356,7 @@ def test_preloads_has_many_on_model_with_a_composite_primary_key_through_id_attr c = Cpk::OrderAgreement.lease_connection order_id_column = Regexp.escape(c.quote_table_name("cpk_order_agreements.order_id")) - order_id_constraint = /#{order_id_column} = @0.*@0 = \d+$/ + order_id_constraint = /#{order_id_column} = @0$/ expectation = /SELECT.*WHERE.* #{order_id_constraint}/ assert_match(expectation, preload_sql) @@ -2444,7 +2380,7 @@ def test_preloads_belongs_to_a_composite_primary_key_model_through_id_attribute_ c = Cpk::Order.lease_connection order_id = Regexp.escape(c.quote_table_name("cpk_orders.id")) - order_constraint = /#{order_id} = @0.*@0 = \d+$/ + order_constraint = /#{order_id} = @0$/ expectation = /SELECT.*WHERE.* #{order_constraint}/ assert_match(expectation, preload_sql) @@ -2509,66 +2445,6 @@ def test_in_order_of_with_nil_coerced end class QueryLogsTest < ActiveRecord::TestCase - # SQL requires double single-quotes. - coerce_tests! :test_sql_commenter_format - def test_sql_commenter_format_coerced - ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter - ActiveRecord::QueryLogs.tags = [:application] - - assert_queries_match(%r{/\*application=''active_record''\*/}) do - Dashboard.first - end - end - - # SQL requires double single-quotes. - coerce_tests! :test_sqlcommenter_format_value - def test_sqlcommenter_format_value_coerced - ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter - - ActiveRecord::QueryLogs.tags = [ - :application, - {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 - Dashboard.first - end - end - - # SQL requires double single-quotes. - coerce_tests! :test_sqlcommenter_format_value_string_coercible - def test_sqlcommenter_format_value_string_coercible_coerced - ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter - - ActiveRecord::QueryLogs.tags = [ - :application, - {custom_proc: -> { 1234 }} - ] - - assert_queries_match(%r{custom_proc=''1234''\*/}) do - Dashboard.first - end - end - - # SQL requires double single-quotes. - coerce_tests! :test_sqlcommenter_format_allows_string_keys - def test_sqlcommenter_format_allows_string_keys_coerced - ActiveRecord::QueryLogs.tags_formatter = :sqlcommenter - - ActiveRecord::QueryLogs.tags = [ - :application, - { - "string" => "value", - :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 - Dashboard.first - end - end - # Invalid character encoding causes `ActiveRecord::StatementInvalid` error similar to Postgres. coerce_tests! :test_invalid_encoding_query def test_invalid_encoding_query_coerced @@ -2816,31 +2692,6 @@ def type_for_attribute_is_not_aware_of_custom_types_coerced end end -class ExplainTest < ActiveRecord::TestCase - # Expected query slightly different from because of 'sp_executesql' and query parameters. - coerce_tests! :test_relation_explain_with_first - def test_relation_explain_with_first_coerced - expected_query = capture_sql { - Car.all.first - }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] - message = Car.all.explain.first - assert_match(/^EXPLAIN/, message) - assert_match(expected_query, message) - end - - # Expected query slightly different from because of 'sp_executesql' and query parameters. - coerce_tests! :test_relation_explain_with_last - def test_relation_explain_with_last_coerced - expected_query = capture_sql { - Car.all.last - }.first[/EXEC sp_executesql N'(.*?) NEXT/, 1] - message = Car.all.explain.last - - assert_match(/^EXPLAIN/, message) - assert_match(expected_query, message) - end -end - module ActiveRecord module Assertions class QueryAssertionsTest < ActiveSupport::TestCase diff --git a/test/cases/optimizer_hints_test_sqlserver.rb b/test/cases/optimizer_hints_test_sqlserver.rb index b1aab6820..fde5e9144 100644 --- a/test/cases/optimizer_hints_test_sqlserver.rb +++ b/test/cases/optimizer_hints_test_sqlserver.rb @@ -29,7 +29,7 @@ class OptimizerHitsTestSQLServer < ActiveRecord::TestCase end it "support subqueries" do - assert_queries_match(%r{.*'SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)'.*}) do + assert_queries_match(%r{SELECT COUNT\(count_column\) FROM \(SELECT .*\) subquery_for_count OPTION \(MAXDOP 2\)}) do companies = Company.optimizer_hints("MAXDOP 2") companies = companies.select(:id).where(firm_id: [0, 1]).limit(3) assert_equal 3, companies.count diff --git a/test/cases/showplan_test_sqlserver.rb b/test/cases/showplan_test_sqlserver.rb index 3bf9e1538..6dfde5386 100644 --- a/test/cases/showplan_test_sqlserver.rb +++ b/test/cases/showplan_test_sqlserver.rb @@ -28,13 +28,13 @@ class ShowplanTestSQLServer < ActiveRecord::TestCase it "from array condition using index" do plan = Car.where(id: [1, 2]).explain.inspect - _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (1, 2)" + _(plan).must_include "SELECT [cars].* FROM [cars] WHERE [cars].[id] IN (@0, @1)" _(plan).must_include "Clustered Index Seek", "make sure we do not showplan the sp_executesql" end it "from array condition" do plan = Car.where(name: ["honda", "zyke"]).explain.inspect - _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (N'honda', N'zyke')" + _(plan).must_include " SELECT [cars].* FROM [cars] WHERE [cars].[name] IN (@0, @1)" _(plan).must_include "Clustered Index Scan", "make sure we do not showplan the sp_executesql" end end diff --git a/test/cases/specific_schema_test_sqlserver.rb b/test/cases/specific_schema_test_sqlserver.rb index bfdce3617..0829012e5 100644 --- a/test/cases/specific_schema_test_sqlserver.rb +++ b/test/cases/specific_schema_test_sqlserver.rb @@ -116,16 +116,16 @@ def quoted_id end end # Using ActiveRecord's quoted_id feature for objects. - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: value.new).first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: value.new).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: value.new).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: value.new).first } # Using our custom char type data. type = ActiveRecord::Type::SQLServer::Char data = ActiveRecord::Type::SQLServer::Data - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: data.new("T", type.new)).first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: data.new("T", type.new)).first } # Taking care of everything. - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(char_col: "T").first } - assert_queries_match(/@0 = 'T'/) { SSTestDatatypeMigration.where(varchar_col: "T").first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(char_col: "T").first } + assert_queries_and_values_match(/.*/, ["'T'", 1]) { SSTestDatatypeMigration.where(varchar_col: "T").first } end it "can update and hence properly quoted non-national char/varchar columns" do diff --git a/test/support/query_assertions.rb b/test/support/query_assertions.rb index 2abd98159..78dfce02d 100644 --- a/test/support/query_assertions.rb +++ b/test/support/query_assertions.rb @@ -22,6 +22,28 @@ def assert_queries_count(count = nil, include_schema: false, &block) end end + def assert_queries_and_values_match(match, bound_values = [], count: nil, &block) + ActiveRecord::Base.lease_connection.materialize_transactions + + counter = ActiveRecord::Assertions::QueryAssertions::SQLCounter.new + ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do + result = _assert_nothing_raised_or_warn("assert_queries_match", &block) + queries = counter.log_full + matched_queries = queries.select do |query, values| + values = values.map { |v| v.respond_to?(:quoted) ? v.quoted : v } + match === query && bound_values === values + end + + if count + assert_equal count, matched_queries.size, "#{matched_queries.size} instead of #{count} queries were executed.#{"\nQueries:\n#{counter.log.join("\n")}" unless count.log.empty?}" + else + assert_operator matched_queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{"\nQueries:\n#{counter.log.join("\n")}" unless counter.log.empty?}" + end + + result + end + end + private # Rails tests expect a save-point to be created and released. SQL Server does not release From 181c2c9021ccdae3fec7aa6a4b5db1de1f292a12 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 19:38:48 +0000 Subject: [PATCH 1406/1412] Update Rubies in CI (#1369) --- .github/workflows/ci.yml | 6 +++--- test/cases/helper_sqlserver.rb | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c6e9e41b..36f6f9aa4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,9 @@ jobs: fail-fast: false matrix: ruby: - - 3.4.1 - - 3.3.6 - - 3.2.6 + - 3.4.7 + - 3.3.9 + - 3.2.9 steps: - name: Checkout code diff --git a/test/cases/helper_sqlserver.rb b/test/cases/helper_sqlserver.rb index 95edca121..64c433cf3 100644 --- a/test/cases/helper_sqlserver.rb +++ b/test/cases/helper_sqlserver.rb @@ -17,6 +17,9 @@ require "mocha/minitest" Minitest.after_run do + puts "\n\n" + puts "=" * 80 + puts "Ruby Version: #{RUBY_VERSION}p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" puts "\n\n" puts "=" * 80 puts ActiveRecord::Base.lease_connection.send(:sqlserver_version) From 79aebf61e394d75c0df79b4c7d7c276d65836b51 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 19:58:32 +0000 Subject: [PATCH 1407/1412] Release v8.1.0 (#1375) --- CHANGELOG.md | 2 +- VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 810a557b3..38287646c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -## Unreleased +## v8.1.0 #### Added diff --git a/VERSION b/VERSION index cd8590d8d..8104cabd3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.0.alpha +8.1.0 From 2f2031a45982fd74950b70303df6cca413780478 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 19:59:58 +0000 Subject: [PATCH 1408/1412] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b13af660..a03f3f380 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions | Adapter Version | Rails Version | Support | Branch | |-----------------|---------------|----------------|----------------------------------------------------------------------------------------------------| -| Unreleased | `8.1.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.1.x` | `8.1.x` | Active [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | | `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | | `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | | `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | From de540c65a9e51d4ba40967d3ba43d8d4bc102655 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 20:07:57 +0000 Subject: [PATCH 1409/1412] Prepare for v8.2 development --- .devcontainer/Dockerfile | 2 +- Dockerfile.ci | 2 +- README.md | 27 ++++++++++++++------------- VERSION | 2 +- compose.ci.yaml | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 5068bd219..6d4c1a2af 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -25,6 +25,6 @@ RUN curl https://packages.microsoft.com/keys/microsoft.asc | sudo tee /etc/apt/t RUN mkdir -p /tmp/activerecord-sqlserver-adapter COPY Gemfile VERSION activerecord-sqlserver-adapter.gemspec /tmp/activerecord-sqlserver-adapter/ RUN cd /tmp/activerecord-sqlserver-adapter \ - && RAILS_BRANCH=8-1-stable bundle install \ + && RAILS_BRANCH=main bundle install \ && rm -rf /tmp/activerecord-sqlserver-adapter RUN chown -R vscode:vscode /usr/local/rvm diff --git a/Dockerfile.ci b/Dockerfile.ci index 222008702..74f636508 100644 --- a/Dockerfile.ci +++ b/Dockerfile.ci @@ -9,6 +9,6 @@ WORKDIR $WORKDIR COPY . $WORKDIR -RUN RAILS_BRANCH=8-1-stable bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 +RUN RAILS_BRANCH=main bundle install --jobs `expr $(cat /proc/cpuinfo | grep -c "cpu cores") - 1` --retry 3 CMD ["sh"] diff --git a/README.md b/README.md index a03f3f380..4e1ec395a 100644 --- a/README.md +++ b/README.md @@ -16,19 +16,20 @@ maintenance group. See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|---------------|----------------|----------------------------------------------------------------------------------------------------| -| `8.1.x` | `8.1.x` | Active [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | -| `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | -| `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| +| Unreleased | `8.2.x` | In Development [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.1.x` | `8.1.x` | Active [8-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-1-stable) | +| `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | +| `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | +| `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | #### Native Data Type Support diff --git a/VERSION b/VERSION index 8104cabd3..69640086a 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -8.1.0 +8.2.0.alpha diff --git a/compose.ci.yaml b/compose.ci.yaml index 6997c249b..8242db9b8 100644 --- a/compose.ci.yaml +++ b/compose.ci.yaml @@ -4,7 +4,7 @@ services: ci: environment: - ACTIVERECORD_UNITTEST_HOST=sqlserver - - RAILS_BRANCH=8-1-stable + - RAILS_BRANCH=main build: context: . dockerfile: Dockerfile.ci @@ -13,7 +13,7 @@ services: - "sqlserver" standardrb: environment: - - RAILS_BRANCH=8-1-stable + - RAILS_BRANCH=main build: context: . dockerfile: Dockerfile.ci From aadb38e3095acc605a7b689f22f44b5eb434bbcd Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 20:09:54 +0000 Subject: [PATCH 1410/1412] Update activerecord-sqlserver-adapter.gemspec --- activerecord-sqlserver-adapter.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord-sqlserver-adapter.gemspec b/activerecord-sqlserver-adapter.gemspec index bad85d912..c28b3da1c 100644 --- a/activerecord-sqlserver-adapter.gemspec +++ b/activerecord-sqlserver-adapter.gemspec @@ -26,6 +26,6 @@ Gem::Specification.new do |spec| spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } spec.require_paths = ["lib"] - spec.add_dependency "activerecord", "~> 8.1.0.rc1" + spec.add_dependency "activerecord", "~> 8.2.0.alpha" spec.add_dependency "tiny_tds", "~> 3.0" end From e988fd4fdce627e906f2aa3eb980667178cac9a7 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Fri, 7 Nov 2025 20:27:20 +0000 Subject: [PATCH 1411/1412] Fixed typo in table --- README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 4e1ec395a..e9a712787 100644 --- a/README.md +++ b/README.md @@ -16,20 +16,20 @@ maintenance group. See [Rubygems](https://rubygems.org/gems/activerecord-sqlserver-adapter/versions) for the latest version of the adapter for each Rails release. -| Adapter Version | Rails Version | Support | Branch | -|-----------------|---------------|--------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| -| Unreleased | `8.2.x` | In Development [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | -| `8.1.x` | `8.1.x` | Active [8-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-1-stable) | -| `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | -| `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | -| `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | -| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | -| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | -| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | -| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | -| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | -| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | -| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | +| Adapter Version | Rails Version | Support | Branch | +|-----------------|---------------|----------------|----------------------------------------------------------------------------------------------------| +| Unreleased | `8.2.x` | In Development | [main](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/main) | +| `8.1.x` | `8.1.x` | Active | [8-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-1-stable) | +| `8.0.x` | `8.0.x` | Active | [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/8-0-stable) | +| `7.2.x` | `7.2.x` | Ended | [7-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-2-stable) | +| `7.1.x` | `7.1.x` | Ended | [7-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-1-stable) | +| `7.0.x` | `7.0.x` | Ended | [7-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/7-0-stable) | +| `6.1.x` | `6.1.x` | Ended | [6-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-1-stable) | +| `6.0.x` | `6.0.x` | Ended | [6-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/6-0-stable) | +| `5.2.x` | `5.2.x` | Ended | [5-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-2-stable) | +| `5.1.x` | `5.1.x` | Ended | [5-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/5-1-stable) | +| `4.2.x` | `4.2.x` | Ended | [4-2-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-2-stable) | +| `4.1.x` | `4.1.x` | Ended | [4-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/tree/4-1-stable) | #### Native Data Type Support From 321f2be4afddb09837de2248f89707352a43afd7 Mon Sep 17 00:00:00 2001 From: Justin Dell Date: Wed, 12 Nov 2025 14:36:50 -0600 Subject: [PATCH 1412/1412] Fixed SQL Server database tasks (#1377) --- CHANGELOG.md | 19 ++----------------- .../tasks/sqlserver_database_tasks.rb | 2 +- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 38287646c..d345aff8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,9 @@ -## v8.1.0 +## Unreleased #### Added -- [#1301](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1301) Add support for `INDEX INCLUDE`. -- [#1312](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1312) Add support for `insert_all` and `upsert_all`. -- [#1367](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1367) Added support for computed columns. - #### Changed -- [#1273](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1273) TinyTDS v3+ is now required. -- [#1317](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1317) Reverse order of values when upserting. -- [#1343](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1343) Support more Azure services by changing language source. - #### Fixed -- [#1313](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1313) Correctly retrieve the SQL Server database version. -- [#1320](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1320) Fix SQL statement to calculate `updated_at` when upserting. -- [#1327](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1327) Fix multiple `nil` identity columns for merge insert. -- [#1338](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1338) Fix `insert_all`/`upsert_all` for table names containing numbers. -- [#1345](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1345) Maintain index options during `change_column` operations. -- [#1357](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/1357) Support cross database inserts. - -Please check [8-0-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-0-stable/CHANGELOG.md) for previous changes. +Please check [8-1-stable](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/blob/8-1-stable/CHANGELOG.md) for previous changes. diff --git a/lib/active_record/tasks/sqlserver_database_tasks.rb b/lib/active_record/tasks/sqlserver_database_tasks.rb index 8b18b89bd..62ca56733 100644 --- a/lib/active_record/tasks/sqlserver_database_tasks.rb +++ b/lib/active_record/tasks/sqlserver_database_tasks.rb @@ -7,7 +7,7 @@ module ActiveRecord module Tasks - class SQLServerDatabaseTasks + class SQLServerDatabaseTasks < AbstractTasks DEFAULT_COLLATION = "SQL_Latin1_General_CP1_CI_AS" delegate :with_connection, :establish_connection, to: ActiveRecord::Base