Skip to content

Rails 6.1: Refactored to use new_client connection pattern #917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

#### Changed

- ...
- [#917](https://github.com/rails-sqlserver/activerecord-sqlserver-adapter/pull/917) Refactored to use new_client connection pattern

#### Added

Expand Down
147 changes: 81 additions & 66 deletions lib/active_record/connection_adapters/sqlserver_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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 ========================================== #
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
24 changes: 9 additions & 15 deletions lib/active_record/sqlserver_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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