From 364edcb47b58e7526d88d0c59df32d511f088f20 Mon Sep 17 00:00:00 2001 From: Aidan Haran Date: Tue, 27 Apr 2021 13:43:06 +0100 Subject: [PATCH] Refactored to use new_client connection pattern --- 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