Skip to content
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

Rails 6 support #690

Merged
merged 102 commits into from
May 7, 2020
Merged
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
f2233d7
Update ruby versions supported by Rails 6
wpolicarpo Mar 19, 2019
c43f5a9
Update activerecord version to 6
wpolicarpo Mar 19, 2019
027ecb3
Fix Rails 6 migrations
wpolicarpo Mar 20, 2019
6ead58b
Remove `table` and `column` which are no longer passed to `case_sensi…
kamipo Jan 11, 2019
2dc497b
Remove `id_value` argument which is no longer passed to `sql_for_insert`
kamipo Jan 11, 2019
1057a81
Fix constants missing in 6.0.0.rc1
auroranockert May 24, 2019
0f76d92
Fix concatenate syntax
auroranockert Jul 5, 2019
3be1cb8
Adapt to changed method signature in Rails 6.0.
robotex82 Nov 4, 2019
848e668
Merge pull request #703 from jensnockert/concat-sql-syntax
wpolicarpo Feb 25, 2020
21b98bb
Merge branch 'master' into 6-0-dev
wpolicarpo Mar 25, 2020
f91b626
Merge pull request #711 from robotex82/feature/rails-6-0
wpolicarpo Mar 25, 2020
915fd79
Update version Rails 6.0.2.2
aidanharan Mar 25, 2020
98505fe
Merge pull request #747 from aidanharan/update-to-rails-6
wpolicarpo Mar 26, 2020
e24ec67
Fix method signature
aidanharan Mar 26, 2020
7a1552d
Updated comment
aidanharan Mar 26, 2020
3106e81
Merge pull request #748 from aidanharan/fix-rake-test
wpolicarpo Mar 30, 2020
ee65b45
Fix for table from statement join
aidanharan Mar 30, 2020
ae1bccd
Merge pull request #750 from aidanharan/fix-table-from-statement-join
wpolicarpo Mar 31, 2020
a25eddb
Sanitize forbidden attributes for exists relation
aidanharan Mar 31, 2020
ef99277
Merge pull request #751 from aidanharan/exists-relation-sanitize-forb…
wpolicarpo Mar 31, 2020
2e00595
Added method to get database version
aidanharan Apr 1, 2020
9841e46
Merge pull request #752 from aidanharan/database-version-fix
wpolicarpo Apr 1, 2020
400314a
Use abstract controllers combine multi statements method
aidanharan Apr 1, 2020
d173241
Merge pull request #754 from aidanharan/use-default-combine-multi-sta…
wpolicarpo Apr 1, 2020
ebfd121
Add/remove foreign keys when truncating during tests
aidanharan Apr 2, 2020
694c1e5
Use https protocol for Github
aidanharan Apr 2, 2020
b10e2b2
Merge pull request #756 from aidanharan/gemfile-github-https
wpolicarpo Apr 2, 2020
3d3faf8
Subqueries cannot include ordering unless TOP/LIMIT also specified
aidanharan Apr 6, 2020
1300331
Merge pull request #758 from aidanharan/in-clause-no-ordering
wpolicarpo Apr 7, 2020
8b48e5f
Merge pull request #755 from aidanharan/fix-truncation-tests
wpolicarpo Apr 7, 2020
2360741
Store table name on columns
aidanharan Apr 7, 2020
579730f
Merge pull request #759 from aidanharan/table-name-required-on-column…
wpolicarpo Apr 8, 2020
ca78a17
Fixed migration test
aidanharan Apr 8, 2020
b73de76
Updated unsafe raw sql tests to match tests in Rails 6
aidanharan Apr 8, 2020
f89a893
Merge pull request #761 from aidanharan/fix-migration-context-initial…
wpolicarpo Apr 8, 2020
9200fa9
Fix missing parent class
aidanharan Apr 9, 2020
1ae4253
Merge branch 'master' into 6-0-dev
wpolicarpo Apr 9, 2020
dd698f1
Merge pull request #764 from aidanharan/fix-coerced-tests
wpolicarpo Apr 9, 2020
7ee2122
Fix sanitization matchers
aidanharan Apr 9, 2020
5185b3c
Merge pull request #762 from aidanharan/unsafe-raw-sql-fix-tests
wpolicarpo Apr 14, 2020
e872d91
Changed query cached test
aidanharan Apr 14, 2020
d4340fa
Added method deprecations
aidanharan Apr 14, 2020
04fb60e
Merge pull request #767 from aidanharan/query-cache-types-reset
wpolicarpo Apr 14, 2020
ddc6c60
Merge pull request #768 from aidanharan/database-limits-deprecations
wpolicarpo Apr 14, 2020
b3492a8
Fixed tests
aidanharan Apr 15, 2020
16c3949
Merge pull request #771 from aidanharan/fully-qualified-identifier-tests
wpolicarpo Apr 15, 2020
b9b5178
Add ability to block writes to a database
aidanharan Apr 14, 2020
841ed90
Merge pull request #769 from aidanharan/readonly-mode
wpolicarpo Apr 15, 2020
355fb34
Moved coerced tests to correct classes
aidanharan Apr 15, 2020
88596fc
Coerce tests that require changing identity column
aidanharan Apr 15, 2020
7589e90
Fix showplan test
aidanharan Apr 15, 2020
fa7cdad
Merge pull request #772 from aidanharan/update-all-tests
wpolicarpo Apr 15, 2020
33c1c38
Merge pull request #773 from aidanharan/show-plantest
wpolicarpo Apr 15, 2020
86506cf
Install PostgreSQL gem
aidanharan Apr 15, 2020
3aa7abb
Skip test if database case insensitive
aidanharan Apr 20, 2020
61e6833
Merge pull request #777 from aidanharan/skip-case-sensitive-test
wpolicarpo Apr 20, 2020
54ce1b6
Fix asserted SQL
aidanharan Apr 20, 2020
e67f033
Merge pull request #778 from aidanharan/limit-one-row-sql
wpolicarpo Apr 21, 2020
c256c33
Wrap original test with code to add/remove index
aidanharan Apr 16, 2020
6dcc1b7
Merge pull request #776 from aidanharan/unique-index-fixes
wpolicarpo Apr 21, 2020
420153a
added if_not_exists option with workaround
YehudaGold Apr 22, 2020
c96d91d
Coerce test as adapter doesnt use statement cache
aidanharan Apr 23, 2020
764aa3d
Updated test to match test in Rails
aidanharan Apr 23, 2020
ab38770
Merge pull request #781 from aidanharan/bind-parameter-statement-cache
wpolicarpo Apr 24, 2020
bf473c4
Merge pull request #779 from YehudaGold/add-if-not-exists
wpolicarpo Apr 24, 2020
c319b82
Merge branch 'master' into 6-0-dev
wpolicarpo Apr 24, 2020
4b30617
fix update_attributes deprecated method usage
YehudaGold Apr 24, 2020
cfc41e9
Merge pull request #774 from aidanharan/postgresql-gem
wpolicarpo Apr 24, 2020
ef35bcc
Merge pull request #784 from YehudaGold/update-attributes-deprecated
wpolicarpo Apr 24, 2020
20f91a8
Merge pull request #782 from aidanharan/update-query-cache-test
wpolicarpo Apr 24, 2020
3706bc5
fixes test_default_decimal_number to assert against number.like other…
YehudaGold Apr 26, 2020
4d3255d
Merge pull request #785 from YehudaGold/coerce-default-decimal-test
wpolicarpo Apr 26, 2020
0f92db2
Fix `EagerLoadingTooManyIdsTest#test_preloading_too_many_ids`
kamipo Apr 27, 2020
7026eb5
Merge pull request #786 from kamipo/fix_test_preloading_too_many_ids
wpolicarpo Apr 27, 2020
9061a7a
Raise ActiveRecord::InvalidForeignKey for foreign-key constraint viol…
aidanharan Apr 27, 2020
0c35cc2
Merge pull request #787 from aidanharan/foreign-key-violation-delete
wpolicarpo Apr 27, 2020
6aa992d
Coerce tests as SQL Server does not return string value for updated_at
aidanharan Apr 28, 2020
14d4619
Rails 6: Clean coerced tests (#793)
YehudaGold Apr 29, 2020
0fac1db
Merge pull request #790 from aidanharan/cache-key-no-string-value
wpolicarpo Apr 29, 2020
0f9779e
Rails 6: Coerce time no precision test (#791)
aidanharan Apr 29, 2020
967c93e
Take and first use implicit ordering on identifier column (#789)
aidanharan Apr 29, 2020
a07552c
Rails 6: Support lazy transactions (#780)
aidanharan Apr 29, 2020
9b71375
Coerce test an empty transaction does not raise if preventing writes.…
YehudaGold Apr 29, 2020
fe348fa
Rails 6: add database_exists? (#796)
YehudaGold Apr 30, 2020
d16d958
Rails 6: Use same sqlite3 and pg versions as Rails 6.0.2.2 (#794)
aidanharan Apr 30, 2020
3eba185
Updates for bulk insert/upsert (#795)
aidanharan Apr 30, 2020
6bdfc09
Fix randomly failing test (#797)
aidanharan Apr 30, 2020
66db048
Rails 6: Coerce eagerload too many IDs test (#798)
aidanharan May 5, 2020
ee8af23
Fix randomly failing tests by loading models schema
aidanharan May 6, 2020
b1e02f9
Rails 6: Log subscriber workaround (#799)
aidanharan May 6, 2020
8ab0006
Merge pull request #800 from aidanharan/random-failing-test-load-schema
wpolicarpo May 6, 2020
a72acbc
Add metadata to the gemspec
wpolicarpo May 7, 2020
6a6650b
Update README for Rails 6
wpolicarpo May 7, 2020
708570d
Workaround for randomly failing test
aidanharan May 7, 2020
0c8cdc8
Coerce predicate builder tests
aidanharan May 7, 2020
d18f933
Merge pull request #802 from aidanharan/predicate-build-coerced-tests
wpolicarpo May 7, 2020
6064f93
Merge pull request #801 from aidanharan/schema-randomly-failing-test
wpolicarpo May 7, 2020
499a6c7
Remove invalid order from FROM subquery
aidanharan May 7, 2020
14a94fd
Merge pull request #803 from aidanharan/remove-ordering-frm-subqueries
wpolicarpo May 7, 2020
01a7946
Update appveyor matrix
wpolicarpo May 7, 2020
e72371f
Update tiny_tds version for appveyor
wpolicarpo May 7, 2020
17b2053
Cleanup appveyor configuration
wpolicarpo May 7, 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
4 changes: 0 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -15,10 +15,6 @@ script:
- docker-compose run ci
matrix:
include:
- name: 2.3.8
env: TARGET_VERSION=2.3.8
- name: 2.4.10
env: TARGET_VERSION=2.4.10
- name: 2.5.8
env: TARGET_VERSION=2.5.8
- name: 2.6.6
13 changes: 10 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
# frozen_string_literal: true

require 'openssl'
source 'https://rubygems.org'

git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gemspec

gem 'sqlite3', '~> 1.3.6'
gem "sqlite3", "~> 1.4"
gem "pg", ">= 0.18.0"

gem 'bcrypt'
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

@@ -13,7 +20,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'
@@ -33,7 +40,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']
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5.2.0
6.0.0.rc1
39 changes: 25 additions & 14 deletions activerecord-sqlserver-adapter.gemspec
Original file line number Diff line number Diff line change
@@ -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 = ['[email protected]', '[email protected]']
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 = ["[email protected]", "[email protected]"]
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', '~> 5.2.0'
spec.add_dependency 'tiny_tds'
spec.require_paths = ["lib"]

spec.add_dependency "activerecord", "~> 6.0.0"
spec.add_dependency "tiny_tds"
end
37 changes: 20 additions & 17 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
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.0
clone_depth: 5
skip_tags: true
matrix:
fast_finish: true
- SET TINYTDS_VERSION=2.1.3.pre

install:
- ps: Update-AppveyorBuild -Version "$(Get-Content $env:appveyor_build_folder\VERSION).$env:appveyor_build_number"
- ruby --version
- gem --version
- 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=
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"
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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))
Original file line number Diff line number Diff line change
@@ -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

Original file line number Diff line number Diff line change
@@ -2,8 +2,20 @@ 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

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
@@ -12,6 +24,12 @@ 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

materialize_transactions

sp_executesql(sql, name, binds, prepare: prepare)
end

@@ -71,9 +89,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
@@ -89,12 +109,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
@@ -107,11 +127,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))
@@ -122,9 +137,22 @@ 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)
materialize_transactions

vars = if variables.any? && variables.first.is_a?(Hash)
variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
else
@@ -221,18 +249,20 @@ def newsequentialid_function

protected

def sql_for_insert(sql, pk, id_value, 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)
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.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
@@ -257,6 +287,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

36 changes: 36 additions & 0 deletions lib/active_record/connection_adapters/sqlserver/quoting.rb
Original file line number Diff line number Diff line change
@@ -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

17 changes: 14 additions & 3 deletions lib/active_record/connection_adapters/sqlserver/schema_creation.rb
Original file line number Diff line number Diff line change
@@ -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)
@@ -34,7 +45,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
Original file line number Diff line number Diff line change
@@ -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
@@ -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

@@ -237,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
@@ -557,7 +556,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
50 changes: 40 additions & 10 deletions lib/active_record/connection_adapters/sqlserver_adapter.rb
Original file line number Diff line number Diff line change
@@ -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'
@@ -80,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
@@ -144,10 +151,30 @@ def supports_savepoints?
true
end

def supports_lazy_transactions?
true
end

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" }
@@ -196,7 +223,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
@@ -256,6 +283,9 @@ def combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [],
result
end

def get_database_version # :nodoc:
version_year
end

protected

@@ -325,18 +355,18 @@ 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)
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(
@@ -348,9 +378,9 @@ def translate_exception(e, message)
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
10 changes: 7 additions & 3 deletions lib/active_record/connection_adapters/sqlserver_column.rb
Original file line number Diff line number Diff line change
@@ -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, table_name, default_function, 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
6 changes: 6 additions & 0 deletions lib/active_record/sqlserver_base.rb
Original file line number Diff line number Diff line change
@@ -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
48 changes: 38 additions & 10 deletions lib/arel/visitors/sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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)
@@ -31,7 +37,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

@@ -52,12 +58,17 @@ 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
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 +106,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 +117,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,29 +128,38 @@ 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

def collect_in_clause(left, right, collector)
if Array === right
right.each { |node| remove_invalid_ordering_from_select_statement(node) }
else
remove_invalid_ordering_from_select_statement(right)
end

super
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]
collector << " " if options[:space]
end
collector
end

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 << " 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
@@ -196,7 +216,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

@@ -213,6 +233,14 @@ def remote_server_table_name o
).quoted
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 remove_invalid_ordering_from_select_statement(node)
return unless Arel::Nodes::SelectStatement === node

node.orders = [] unless node.offset || node.limit
end
end
end
end
8 changes: 4 additions & 4 deletions test/appveyor/dbsetup.ps1
Original file line number Diff line number Diff line change
@@ -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) {
64 changes: 63 additions & 1 deletion test/cases/adapter_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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}
@@ -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
@@ -429,5 +448,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

678 changes: 490 additions & 188 deletions test/cases/coerced_tests.rb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions test/cases/fully_qualified_identifier_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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
34 changes: 34 additions & 0 deletions test/cases/in_clause_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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
4 changes: 2 additions & 2 deletions test/cases/migration_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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
@@ -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"
4 changes: 2 additions & 2 deletions test/cases/showplan_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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

17 changes: 12 additions & 5 deletions test/support/coerceable_test_sqlserver.rb
Original file line number Diff line number Diff line change
@@ -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