Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
b4f558e
testing
serkaniyigun Oct 13, 2019
a78b6eb
sql_for_insert fix
serkaniyigun Oct 13, 2019
ec9d711
Use rails backtrace in tests
takanamito Jan 12, 2020
046f820
Prepare for Minitest6
takanamito Jan 12, 2020
1aafcc6
DateTime also returns true for .acts_like?(:date) so it must be check…
ablignaut Jan 13, 2020
9172586
corrected syntax for DEPRECATED warnings in test/cases
daveomcd Nov 7, 2019
3ba9811
corrected syntax for DEPRECATED warnings in test/cases
daveomcd Nov 7, 2019
83a607c
removed byebug
daveomcd Feb 4, 2020
4ce8c53
Merge commit 'pullrequests/daveomcd/correcting-tests' into 6-0-dev
Feb 20, 2020
3ff8afa
Merge commit 'pullrequests/ablignaut/master' into 6-0-dev
Feb 20, 2020
fe26f9a
various changes
nemesit Feb 20, 2020
8d2b737
fix column_initialization, translate_exception,...
Feb 21, 2020
c97d602
fixed tests
Feb 21, 2020
31af51a
Version -> 6.0.2.1
Feb 21, 2020
0aec9e8
various changes
Feb 24, 2020
319abc4
Merge pull request #718 from takanamito/fix-ci
wpolicarpo Feb 25, 2020
339a255
Fix minitest expection calls
aidanharan Feb 25, 2020
2776e4a
Merge pull request #730 from aidanharan/fix-coerced-minitest-calls
wpolicarpo Feb 26, 2020
ff79a68
Calculate should not remove ordering for MSSQL
aidanharan Feb 26, 2020
501248c
Merge pull request #733 from aidanharan/fix-calculation-tests
wpolicarpo Feb 26, 2020
c57ab31
Order by selected items when using distinct exists
aidanharan Feb 27, 2020
825e8f2
Merge pull request #735 from aidanharan/fix-finder-distinct-select-ex…
wpolicarpo Feb 27, 2020
5cbc494
Default precision for 'time' column type is 7
aidanharan Feb 27, 2020
0a0ee51
Merge pull request #737 from aidanharan/time-with-default-precision
wpolicarpo Feb 28, 2020
d846bc1
Set default time precision when registering time type
aidanharan Mar 19, 2020
8747adc
Adapter does not use prepared statement cache
aidanharan Mar 23, 2020
7372fc8
Merge pull request #744 from aidanharan/coerce-statement-cache-tests
wpolicarpo Mar 25, 2020
75ebf70
Merge pull request #743 from aidanharan/time-with-default-precision-r…
wpolicarpo Mar 25, 2020
72737e8
Quoted table names containing square brackets need to be regex escaped
aidanharan Mar 24, 2020
20b0f60
Merge pull request #745 from aidanharan/fix-ar-relations-tests
wpolicarpo Mar 25, 2020
e7d45c5
Merge branch 'master' of github.com:rails-sqlserver/activerecord-sqls…
nemesit Mar 26, 2020
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
1 change: 1 addition & 0 deletions .ruby-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2.6.4
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
ARG TARGET_VERSION=2.6.3

FROM wpolicarpo/activerecord-sqlserver-adapter:${TARGET_VERSION}
FROM nemesit/activerecord-sqlserver-adapter:${TARGET_VERSION}

ENV WORKDIR /activerecord-sqlserver-adapter

Expand Down
22 changes: 12 additions & 10 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ require 'openssl'
source 'https://rubygems.org'
gemspec

gem 'sqlite3', '~> 1.3.6'
gem 'sqlite3', '~> 1.4.2'
gem 'bcrypt'
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

Expand Down Expand Up @@ -33,25 +33,27 @@ else
ver
end
end
gem 'rails', git: "git://github.com/rails/rails.git", tag: "v#{version}"

gem 'rails', git: "https://github.com/rails/rails.git", tag: "v6.0.2.1"
# gem 'rails', git: "git://github.com/rails/rails.git", tag: "v#{version}"
end

if ENV['AREL']
gem 'arel', path: ENV['AREL']
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
#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', git: 'https://github.com/nemesit/tiny_tds.git'
#end
end

group :development do
gem 'byebug'
gem 'byebug', platform: [:mri, :mingw, :x64_mingw]
gem 'mocha'
gem 'minitest-spec-rails'
end
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6.0.0.beta1
6.0.2.1
2 changes: 1 addition & 1 deletion activerecord-sqlserver-adapter.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -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.2.1'
spec.add_dependency 'tiny_tds'
end
2 changes: 1 addition & 1 deletion docker-compose.ci.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: "2.2"
services:
database:
image: metaskills/mssql-server-linux-rails
image: nemesit/mssql-server-linux-rails
ci:
environment:
- ACTIVERECORD_UNITTEST_HOST=database
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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|(?<!FROM)\s)(?:\b#{table_name}\b|#{quoted_table_name})(?!\.)/i.match?(from.to_s)
end
end
end
end
end
end

ActiveSupport.on_load(:active_record) do
ActiveRecord::Relation.include(ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::QueryMethods)
end
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 5 additions & 4 deletions lib/active_record/connection_adapters/sqlserver/quoting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 13 additions & 4 deletions lib/active_record/connection_adapters/sqlserver_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
require 'active_record'
require 'arel_sqlserver'
require 'active_record/connection_adapters/abstract_adapter'
require 'active_record/connection_adapters/statement_pool'
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/version'
require 'active_record/connection_adapters/sqlserver/type'
require 'active_record/connection_adapters/sqlserver/database_limits'
Expand Down Expand Up @@ -40,6 +43,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
Expand Down Expand Up @@ -296,8 +302,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
Expand All @@ -321,15 +326,15 @@ 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)
when /conflicted with the foreign key constraint/i
InvalidForeignKey.new(message)
when /has been chosen as the deadlock victim/i
DeadlockVictim.new(message)
when /database .* does not exist/i
when /'doesnotexist'((?!').)*does not exist/
NoDatabaseError.new(message)
when /data would be truncated/
ValueTooLong.new(message)
Expand Down Expand Up @@ -447,6 +452,10 @@ def version_year
def sqlserver_version
@sqlserver_version ||= _raw_select('SELECT @@version', fetch: :rows).first.first.to_s
end

def build_statement_pool
ActiveRecord::ConnectionAdapters::StatementPool.new(self.class.type_cast_config_to_integer(@config[:statement_limit]))
end
end
end
end
9 changes: 7 additions & 2 deletions lib/active_record/connection_adapters/sqlserver_column.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,14 @@ 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 = {})
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)
@table_name = table_name
super(name, default, sql_type_metadata, null, default_function, collation: collation, comment: comment, **sqlserver_options)
end

def table_name
@table_name
end

def is_identity?
Expand Down
16 changes: 15 additions & 1 deletion lib/arel/visitors/sqlserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ class SQLServer < Arel::Visitors::ToSql
FETCH0 = " FETCH FIRST (SELECT 0) "
ROWS_ONLY = " ROWS ONLY"

WHERE = ' WHERE '
SPACE = ' '
COMMA = ', '
GROUP_BY = ' GROUP BY '
ORDER_BY = ' ORDER BY '
WINDOW = ' WINDOW '
AND = ' AND '
DISTINCT = 'DISTINCT'

private

Expand All @@ -22,6 +30,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)
Expand Down Expand Up @@ -196,7 +210,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

Expand Down
36 changes: 18 additions & 18 deletions test/cases/adapter_test_sqlserver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
Loading