activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +15 -0
- data/CHANGELOG.md +60 -0
- data/Gemfile +45 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +5 -5
- data/README.md +193 -0
- data/RUNNING_UNIT_TESTS.md +95 -0
- data/Rakefile +48 -0
- data/activerecord-sqlserver-adapter.gemspec +28 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +5 -15
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +130 -151
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -25
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +39 -78
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +71 -47
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -30
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +112 -108
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +52 -7
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +52 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/castable.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +39 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +14 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +37 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +59 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +118 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +133 -198
- data/lib/active_record/connection_adapters/sqlserver_column.rb +15 -86
- data/lib/active_record/sqlserver_base.rb +2 -0
- data/lib/arel/visitors/sqlserver.rb +120 -393
- data/lib/{arel/arel_sqlserver.rb → arel_sqlserver.rb} +1 -3
- data/test/cases/adapter_test_sqlserver.rb +420 -0
- data/test/cases/coerced_tests.rb +642 -0
- data/test/cases/column_test_sqlserver.rb +703 -0
- data/test/cases/connection_test_sqlserver.rb +216 -0
- data/test/cases/database_statements_test_sqlserver.rb +57 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +38 -0
- data/test/cases/helper_sqlserver.rb +36 -0
- data/test/cases/migration_test_sqlserver.rb +66 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +175 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +9 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +118 -0
- data/test/cases/transaction_test_sqlserver.rb +61 -0
- data/test/cases/utils_test_sqlserver.rb +91 -0
- data/test/cases/uuid_test_sqlserver.rb +41 -0
- data/test/config.yml +35 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +3 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +3 -0
- data/test/models/sqlserver/fk_has_pk.rb +3 -0
- data/test/models/sqlserver/natural_pk_data.rb +4 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
- data/test/models/sqlserver/no_pk_data.rb +3 -0
- data/test/models/sqlserver/quoted_table.rb +7 -0
- data/test/models/sqlserver/quoted_view_1.rb +3 -0
- data/test/models/sqlserver/quoted_view_2.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
- data/test/models/sqlserver/string_defaults_view.rb +3 -0
- data/test/models/sqlserver/tinyint_pk.rb +3 -0
- data/test/models/sqlserver/upper.rb +3 -0
- data/test/models/sqlserver/uppered.rb +3 -0
- data/test/models/sqlserver/uuid.rb +3 -0
- data/test/schema/datatypes/2012.sql +64 -0
- data/test/schema/sqlserver_specific_schema.rb +181 -0
- data/test/support/coerceable_test_sqlserver.rb +45 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +48 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +32 -0
- metadata +271 -21
- data/CHANGELOG +0 -39
- data/VERSION +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +0 -17
- data/lib/active_record/sqlserver_test_case.rb +0 -17
- data/lib/arel/nodes_sqlserver.rb +0 -14
- data/lib/arel/select_manager_sqlserver.rb +0 -62
data/Rakefile
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require_relative 'test/support/paths_sqlserver'
|
4
|
+
require_relative 'test/support/rake_helpers'
|
5
|
+
|
6
|
+
task test: ['test:dblib']
|
7
|
+
task default: [:test]
|
8
|
+
|
9
|
+
namespace :test do
|
10
|
+
|
11
|
+
%w(dblib odbc).each do |mode|
|
12
|
+
|
13
|
+
Rake::TestTask.new(mode) do |t|
|
14
|
+
t.libs = ARTest::SQLServer.test_load_paths
|
15
|
+
t.test_files = test_files
|
16
|
+
t.warning = !!ENV['WARNING']
|
17
|
+
t.verbose = true
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
task 'dblib:env' do
|
23
|
+
ENV['ARCONN'] = 'dblib'
|
24
|
+
end
|
25
|
+
|
26
|
+
task 'odbc:env' do
|
27
|
+
ENV['ARCONN'] = 'odbc'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
task 'test:dblib' => 'test:dblib:env'
|
33
|
+
task 'test:odbc' => 'test:odbc:env'
|
34
|
+
|
35
|
+
namespace :profile do
|
36
|
+
['dblib', 'odbc'].each do |mode|
|
37
|
+
namespace mode.to_sym do
|
38
|
+
Dir.glob('test/profile/*_profile_case.rb').sort.each do |test_file|
|
39
|
+
profile_case = File.basename(test_file).sub('_profile_case.rb', '')
|
40
|
+
Rake::TestTask.new(profile_case) do |t|
|
41
|
+
t.libs = ARTest::SQLServer.test_load_paths
|
42
|
+
t.test_files = [test_file]
|
43
|
+
t.verbose = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "active_record/connection_adapters/sqlserver/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'activerecord-sqlserver-adapter'
|
7
|
+
spec.version = ActiveRecord::ConnectionAdapters::SQLServer::Version::VERSION
|
8
|
+
spec.platform = Gem::Platform::RUBY
|
9
|
+
spec.authors = ['Ken Collins', 'Anna Carey', 'Will Bond', 'Murray Steele', 'Shawn Balestracci', 'Joe Rafaniello', 'Tom Ward']
|
10
|
+
spec.email = ['ken@metaskills.net', 'will@wbond.net']
|
11
|
+
spec.homepage = 'http://github.com/rails-sqlserver/activerecord-sqlserver-adapter'
|
12
|
+
spec.summary = 'ActiveRecord SQL Server Adapter.'
|
13
|
+
spec.description = spec.summary
|
14
|
+
spec.files = `git ls-files -z`.split("\x0")
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
spec.add_dependency 'activerecord', '~> 4.2.0'
|
19
|
+
spec.add_development_dependency 'bundler'
|
20
|
+
spec.add_development_dependency 'guard-minitest'
|
21
|
+
spec.add_development_dependency 'minitest', '< 5.3.4' # PENDING: [Rails5.x] Remove test order constraint.
|
22
|
+
spec.add_development_dependency 'minitest-focus'
|
23
|
+
spec.add_development_dependency 'minitest-spec-rails'
|
24
|
+
spec.add_development_dependency 'mocha'
|
25
|
+
spec.add_development_dependency 'nokogiri'
|
26
|
+
spec.add_development_dependency 'pry'
|
27
|
+
spec.add_development_dependency 'rake'
|
28
|
+
end
|
@@ -1,17 +1,13 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
module
|
3
|
+
module SQLServer
|
4
4
|
module CoreExt
|
5
5
|
module ActiveRecord
|
6
|
-
extend ActiveSupport::Concern
|
7
6
|
|
8
|
-
|
9
|
-
class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns
|
10
|
-
self.coerced_sqlserver_date_columns = Set.new
|
11
|
-
self.coerced_sqlserver_time_columns = Set.new
|
12
|
-
end
|
7
|
+
extend ActiveSupport::Concern
|
13
8
|
|
14
9
|
module ClassMethods
|
10
|
+
|
15
11
|
def execute_procedure(proc_name, *variables)
|
16
12
|
if connection.respond_to?(:execute_procedure)
|
17
13
|
connection.execute_procedure(proc_name, *variables)
|
@@ -20,18 +16,12 @@ module ActiveRecord
|
|
20
16
|
end
|
21
17
|
end
|
22
18
|
|
23
|
-
def coerce_sqlserver_date(*attributes)
|
24
|
-
self.coerced_sqlserver_date_columns += attributes.map(&:to_s)
|
25
|
-
end
|
26
|
-
|
27
|
-
def coerce_sqlserver_time(*attributes)
|
28
|
-
self.coerced_sqlserver_time_columns += attributes.map(&:to_s)
|
29
|
-
end
|
30
19
|
end
|
20
|
+
|
31
21
|
end
|
32
22
|
end
|
33
23
|
end
|
34
24
|
end
|
35
25
|
end
|
36
26
|
|
37
|
-
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::
|
27
|
+
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ActiveRecord
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'active_record/attribute_methods'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
module ConnectionAdapters
|
5
|
+
module SQLServer
|
6
|
+
module CoreExt
|
7
|
+
module AttributeMethods
|
8
|
+
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def attributes_for_update(attribute_names)
|
13
|
+
super.reject do |name|
|
14
|
+
column = self.class.columns_hash[name]
|
15
|
+
column && column.respond_to?(:is_identity?) && column.is_identity?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::AttributeMethods
|
@@ -1,8 +1,9 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
module
|
3
|
+
module SQLServer
|
4
4
|
module CoreExt
|
5
5
|
module Explain
|
6
|
+
|
6
7
|
SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '
|
7
8
|
SQLSERVER_PARAM_MATCHER = /@\d+ =/
|
8
9
|
|
@@ -15,7 +16,7 @@ module ActiveRecord
|
|
15
16
|
|
16
17
|
# This is somewhat hacky, but it should reliably reformat our prepared sql statment
|
17
18
|
# which uses sp_executesql to just the first argument, then unquote it. Likewise our
|
18
|
-
#
|
19
|
+
# `sp_executesql` method should substitude the @n args withe the quoted values.
|
19
20
|
def unprepare_sqlserver_statement(sql)
|
20
21
|
if sql.starts_with?(SQLSERVER_STATEMENT_PREFIX)
|
21
22
|
executesql = sql.from(SQLSERVER_STATEMENT_PREFIX.length)
|
@@ -28,11 +29,12 @@ module ActiveRecord
|
|
28
29
|
sql
|
29
30
|
end
|
30
31
|
end
|
32
|
+
|
31
33
|
end
|
32
34
|
end
|
33
35
|
end
|
34
36
|
end
|
35
37
|
end
|
36
38
|
|
37
|
-
ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::
|
38
|
-
ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::
|
39
|
+
ActiveRecord::Base.extend ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain
|
40
|
+
ActiveRecord::Relation.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Explain
|
@@ -1,28 +1,34 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
module
|
3
|
+
module SQLServer
|
4
4
|
module CoreExt
|
5
5
|
module ODBC
|
6
|
+
|
6
7
|
module Statement
|
8
|
+
|
7
9
|
def finished?
|
8
10
|
connected?
|
9
11
|
false
|
10
12
|
rescue ::ODBC::Error
|
11
13
|
true
|
12
14
|
end
|
15
|
+
|
13
16
|
end
|
14
17
|
|
15
18
|
module Database
|
19
|
+
|
16
20
|
def run_block(*args)
|
17
21
|
yield sth = run(*args)
|
18
22
|
sth.drop
|
19
23
|
end
|
24
|
+
|
20
25
|
end
|
26
|
+
|
21
27
|
end
|
22
28
|
end
|
23
29
|
end
|
24
30
|
end
|
25
31
|
end
|
26
32
|
|
27
|
-
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::
|
28
|
-
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::
|
33
|
+
ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Statement
|
34
|
+
ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::ODBC::Database
|
@@ -1,7 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
module
|
3
|
+
module SQLServer
|
4
4
|
module DatabaseLimits
|
5
|
+
|
5
6
|
def table_alias_length
|
6
7
|
128
|
7
8
|
end
|
@@ -41,6 +42,7 @@ module ActiveRecord
|
|
41
42
|
def joins_per_query
|
42
43
|
256
|
43
44
|
end
|
45
|
+
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionAdapters
|
3
|
-
module
|
3
|
+
module SQLServer
|
4
4
|
module DatabaseStatements
|
5
|
+
|
5
6
|
def select_rows(sql, name = nil, binds = [])
|
6
|
-
|
7
|
+
sp_executesql sql, name, binds, fetch: :rows
|
7
8
|
end
|
8
9
|
|
9
10
|
def execute(sql, name = nil)
|
@@ -15,19 +16,17 @@ module ActiveRecord
|
|
15
16
|
end
|
16
17
|
|
17
18
|
def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
|
18
|
-
|
19
|
-
with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
|
20
|
-
elsif update_sql?(sql)
|
21
|
-
sql = strip_ident_from_update(sql)
|
22
|
-
do_exec_query(sql, name, binds)
|
23
|
-
else
|
24
|
-
do_exec_query(sql, name, binds)
|
25
|
-
end
|
19
|
+
sp_executesql(sql, name, binds)
|
26
20
|
end
|
27
21
|
|
28
|
-
# The abstract adapter ignores the last two parameters also
|
29
22
|
def exec_insert(sql, name, binds, _pk = nil, _sequence_name = nil)
|
30
|
-
|
23
|
+
id_insert = binds_have_identity_column?(binds)
|
24
|
+
id_table = table_name_from_binds(binds) if id_insert
|
25
|
+
if id_insert && id_table
|
26
|
+
with_identity_insert_enabled(id_table) { exec_query(sql, name, binds) }
|
27
|
+
else
|
28
|
+
exec_query(sql, name, binds)
|
29
|
+
end
|
31
30
|
end
|
32
31
|
|
33
32
|
def exec_delete(sql, name, binds)
|
@@ -48,35 +47,44 @@ module ActiveRecord
|
|
48
47
|
do_execute 'BEGIN TRANSACTION'
|
49
48
|
end
|
50
49
|
|
51
|
-
def
|
52
|
-
|
50
|
+
def transaction_isolation_levels
|
51
|
+
super.merge snapshot: "SNAPSHOT"
|
53
52
|
end
|
54
53
|
|
55
|
-
def
|
56
|
-
|
54
|
+
def begin_isolated_db_transaction(isolation)
|
55
|
+
set_transaction_isolation_level transaction_isolation_levels.fetch(isolation)
|
56
|
+
begin_db_transaction
|
57
57
|
end
|
58
58
|
|
59
|
-
def
|
60
|
-
|
59
|
+
def set_transaction_isolation_level(isolation_level)
|
60
|
+
do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
|
61
|
+
begin_db_transaction
|
61
62
|
end
|
62
63
|
|
63
|
-
def
|
64
|
+
def commit_db_transaction
|
65
|
+
do_execute 'COMMIT TRANSACTION'
|
64
66
|
end
|
65
67
|
|
66
|
-
def
|
67
|
-
|
68
|
+
def rollback_db_transaction
|
69
|
+
do_execute 'ROLLBACK TRANSACTION'
|
68
70
|
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
+
include Savepoints
|
73
|
+
|
74
|
+
def create_savepoint(name = current_savepoint_name)
|
75
|
+
do_execute "SAVE TRANSACTION #{name}"
|
72
76
|
end
|
73
77
|
|
74
|
-
def
|
75
|
-
|
78
|
+
def rollback_to_savepoint(name = current_savepoint_name)
|
79
|
+
do_execute "ROLLBACK TRANSACTION #{name}"
|
76
80
|
end
|
77
81
|
|
78
|
-
def
|
79
|
-
|
82
|
+
def release_savepoint(name = current_savepoint_name)
|
83
|
+
end
|
84
|
+
|
85
|
+
def case_sensitive_modifier(node, table_attribute)
|
86
|
+
node = Arel::Nodes.build_quoted node, table_attribute
|
87
|
+
Arel::Nodes::Bin.new(node)
|
80
88
|
end
|
81
89
|
|
82
90
|
# === SQLServer Specific ======================================== #
|
@@ -114,15 +122,23 @@ module ActiveRecord
|
|
114
122
|
end
|
115
123
|
end
|
116
124
|
|
125
|
+
def with_identity_insert_enabled(table_name)
|
126
|
+
table_name = quote_table_name(table_name_or_views_table_name(table_name))
|
127
|
+
set_identity_insert(table_name, true)
|
128
|
+
yield
|
129
|
+
ensure
|
130
|
+
set_identity_insert(table_name, false)
|
131
|
+
end
|
132
|
+
|
117
133
|
def use_database(database = nil)
|
118
134
|
return if sqlserver_azure?
|
119
|
-
database
|
120
|
-
do_execute "USE #{
|
135
|
+
name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted
|
136
|
+
do_execute "USE #{name}" unless name.blank?
|
121
137
|
end
|
122
138
|
|
123
139
|
def user_options
|
124
140
|
return {} if sqlserver_azure?
|
125
|
-
select_rows('
|
141
|
+
select_rows('dbcc useroptions', 'SCHEMA').reduce(HashWithIndifferentAccess.new) do |values, row|
|
126
142
|
if row.instance_of? Hash
|
127
143
|
set_option = row.values[0].gsub(/\s+/, '_')
|
128
144
|
user_value = row.values[1]
|
@@ -135,7 +151,6 @@ module ActiveRecord
|
|
135
151
|
end
|
136
152
|
end
|
137
153
|
|
138
|
-
# TODO: Rails 4 now supports isolation levels
|
139
154
|
def user_options_dateformat
|
140
155
|
if sqlserver_azure?
|
141
156
|
select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
|
@@ -169,19 +184,6 @@ module ActiveRecord
|
|
169
184
|
end
|
170
185
|
end
|
171
186
|
|
172
|
-
def run_with_isolation_level(isolation_level)
|
173
|
-
unless valid_isolation_levels.include?(isolation_level.upcase)
|
174
|
-
raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}."
|
175
|
-
end
|
176
|
-
initial_isolation_level = user_options_isolation_level || 'READ COMMITTED'
|
177
|
-
do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
|
178
|
-
begin
|
179
|
-
yield
|
180
|
-
ensure
|
181
|
-
do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
|
182
|
-
end if block_given?
|
183
|
-
end
|
184
|
-
|
185
187
|
def newid_function
|
186
188
|
select_value 'SELECT NEWID()'
|
187
189
|
end
|
@@ -190,51 +192,6 @@ module ActiveRecord
|
|
190
192
|
select_value 'SELECT NEWSEQUENTIALID()'
|
191
193
|
end
|
192
194
|
|
193
|
-
def activity_stats
|
194
|
-
select_all %|
|
195
|
-
SELECT
|
196
|
-
[session_id] = s.session_id,
|
197
|
-
[user_process] = CONVERT(CHAR(1), s.is_user_process),
|
198
|
-
[login] = s.login_name,
|
199
|
-
[database] = ISNULL(db_name(r.database_id), N''),
|
200
|
-
[task_state] = ISNULL(t.task_state, N''),
|
201
|
-
[command] = ISNULL(r.command, N''),
|
202
|
-
[application] = ISNULL(s.program_name, N''),
|
203
|
-
[wait_time_ms] = ISNULL(w.wait_duration_ms, 0),
|
204
|
-
[wait_type] = ISNULL(w.wait_type, N''),
|
205
|
-
[wait_resource] = ISNULL(w.resource_description, N''),
|
206
|
-
[blocked_by] = ISNULL(CONVERT (varchar, w.blocking_session_id), ''),
|
207
|
-
[head_blocker] =
|
208
|
-
CASE
|
209
|
-
-- session has an active request, is blocked, but is blocking others
|
210
|
-
WHEN r2.session_id IS NOT NULL AND r.blocking_session_id = 0 THEN '1'
|
211
|
-
-- session is idle but has an open tran and is blocking others
|
212
|
-
WHEN r.session_id IS NULL THEN '1'
|
213
|
-
ELSE ''
|
214
|
-
END,
|
215
|
-
[total_cpu_ms] = s.cpu_time,
|
216
|
-
[total_physical_io_mb] = (s.reads + s.writes) * 8 / 1024,
|
217
|
-
[memory_use_kb] = s.memory_usage * 8192 / 1024,
|
218
|
-
[open_transactions] = ISNULL(r.open_transaction_count,0),
|
219
|
-
[login_time] = s.login_time,
|
220
|
-
[last_request_start_time] = s.last_request_start_time,
|
221
|
-
[host_name] = ISNULL(s.host_name, N''),
|
222
|
-
[net_address] = ISNULL(c.client_net_address, N''),
|
223
|
-
[execution_context_id] = ISNULL(t.exec_context_id, 0),
|
224
|
-
[request_id] = ISNULL(r.request_id, 0),
|
225
|
-
[workload_group] = N''
|
226
|
-
FROM sys.dm_exec_sessions s LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id)
|
227
|
-
LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id)
|
228
|
-
LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id AND r.request_id = t.request_id)
|
229
|
-
LEFT OUTER JOIN
|
230
|
-
(SELECT *, ROW_NUMBER() OVER (PARTITION BY waiting_task_address ORDER BY wait_duration_ms DESC) AS row_num
|
231
|
-
FROM sys.dm_os_waiting_tasks
|
232
|
-
) w ON (t.task_address = w.waiting_task_address) AND w.row_num = 1
|
233
|
-
LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id)
|
234
|
-
WHERE db_name(r.database_id) = '#{current_database}'
|
235
|
-
ORDER BY s.session_id|
|
236
|
-
end
|
237
|
-
|
238
195
|
# === SQLServer Specific (Rake/Test Helpers) ==================== #
|
239
196
|
|
240
197
|
def recreate_database
|
@@ -244,21 +201,25 @@ module ActiveRecord
|
|
244
201
|
end
|
245
202
|
|
246
203
|
def recreate_database!(database = nil)
|
247
|
-
|
248
|
-
database ||= current_db
|
249
|
-
this_db = database.to_s == current_db
|
250
|
-
do_execute 'USE master' if this_db
|
204
|
+
database ||= current_database
|
251
205
|
drop_database(database)
|
252
206
|
create_database(database)
|
253
207
|
ensure
|
254
|
-
use_database(
|
208
|
+
use_database(database)
|
255
209
|
end
|
256
210
|
|
257
211
|
def drop_database(database)
|
258
212
|
retry_count = 0
|
259
213
|
max_retries = 1
|
214
|
+
name = SQLServer::Utils.extract_identifiers(database)
|
260
215
|
begin
|
261
|
-
do_execute "
|
216
|
+
do_execute "
|
217
|
+
USE master
|
218
|
+
IF EXISTS (
|
219
|
+
SELECT * FROM [sys].[databases]
|
220
|
+
WHERE name = #{quote(name.object)}
|
221
|
+
) DROP DATABASE #{name}
|
222
|
+
".squish
|
262
223
|
rescue ActiveRecord::StatementInvalid => err
|
263
224
|
if err.message =~ /because it is currently in use/i
|
264
225
|
raise if retry_count >= max_retries
|
@@ -273,12 +234,19 @@ module ActiveRecord
|
|
273
234
|
end
|
274
235
|
end
|
275
236
|
|
276
|
-
def create_database(database,
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
237
|
+
def create_database(database, options = {})
|
238
|
+
name = SQLServer::Utils.extract_identifiers(database)
|
239
|
+
options = {collation: @connection_options[:collation]}.merge!(options.symbolize_keys)
|
240
|
+
options = options.select { |_, v| v.present? }
|
241
|
+
option_string = options.inject("") do |memo, (key, value)|
|
242
|
+
memo += case key
|
243
|
+
when :collation
|
244
|
+
" COLLATE #{value}"
|
245
|
+
else
|
246
|
+
""
|
247
|
+
end
|
281
248
|
end
|
249
|
+
do_execute "CREATE DATABASE #{name}#{option_string}"
|
282
250
|
end
|
283
251
|
|
284
252
|
def current_database
|
@@ -289,6 +257,7 @@ module ActiveRecord
|
|
289
257
|
select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
|
290
258
|
end
|
291
259
|
|
260
|
+
|
292
261
|
protected
|
293
262
|
|
294
263
|
def select(sql, name = nil, binds = [])
|
@@ -296,71 +265,82 @@ module ActiveRecord
|
|
296
265
|
end
|
297
266
|
|
298
267
|
def sql_for_insert(sql, pk, id_value, sequence_name, binds)
|
299
|
-
sql =
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
268
|
+
sql = if pk
|
269
|
+
quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
270
|
+
sql.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
|
271
|
+
else
|
272
|
+
"#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
|
273
|
+
end
|
305
274
|
super
|
306
275
|
end
|
307
276
|
|
308
277
|
# === SQLServer Specific ======================================== #
|
309
278
|
|
310
|
-
def
|
311
|
-
|
279
|
+
def binds_have_identity_column?(binds)
|
280
|
+
binds.any? do |column_value|
|
281
|
+
column, value = column_value
|
282
|
+
SQLServerColumn === column && column.is_identity?
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
def table_name_from_binds(binds)
|
287
|
+
binds.detect { |column_value|
|
288
|
+
column, value = column_value
|
289
|
+
SQLServerColumn === column
|
290
|
+
}.try(:first).try(:table_name)
|
291
|
+
end
|
292
|
+
|
293
|
+
def set_identity_insert(table_name, enable = true)
|
294
|
+
do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
|
295
|
+
rescue Exception
|
296
|
+
raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
|
312
297
|
end
|
313
298
|
|
314
299
|
# === SQLServer Specific (Executing) ============================ #
|
315
300
|
|
316
301
|
def do_execute(sql, name = 'SQL')
|
317
|
-
log(sql, name)
|
318
|
-
with_sqlserver_error_handling { raw_connection_do(sql) }
|
319
|
-
end
|
302
|
+
log(sql, name) { raw_connection_do(sql) }
|
320
303
|
end
|
321
304
|
|
322
|
-
def
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
305
|
+
def sp_executesql(sql, name, binds, options = {})
|
306
|
+
options[:ar_result] = true if options[:fetch] != :rows
|
307
|
+
types, params = sp_executesql_types_and_parameters(binds)
|
308
|
+
sql = sp_executesql_sql(sql, types, params, name)
|
309
|
+
raw_select sql, name, binds, options
|
310
|
+
end
|
328
311
|
|
329
|
-
|
330
|
-
|
331
|
-
params = []
|
312
|
+
def sp_executesql_types_and_parameters(binds)
|
313
|
+
types, params = [], []
|
332
314
|
binds.each_with_index do |(column, value), index|
|
333
|
-
|
334
|
-
|
335
|
-
v = value
|
336
|
-
names_and_types << if ar_column
|
337
|
-
if column.is_integer? && value.present?
|
338
|
-
v = value.to_i
|
339
|
-
# Reset the casted value to the bind as required by Rails 4.1
|
340
|
-
binds[index] = [column, v]
|
341
|
-
end
|
342
|
-
"@#{index} #{column.sql_type_for_statement}"
|
343
|
-
elsif column.acts_like?(:string)
|
344
|
-
"@#{index} nvarchar(max)"
|
345
|
-
elsif column.is_a?(Fixnum)
|
346
|
-
v = value.to_i
|
347
|
-
"@#{index} int"
|
348
|
-
else
|
349
|
-
raise 'Unknown bind columns. We can account for this.'
|
350
|
-
end
|
351
|
-
quoted_value = ar_column ? quote(v, column) : quote(v, nil)
|
352
|
-
params << (explaining ? quoted_value : "@#{index} = #{quoted_value}")
|
315
|
+
types << "@#{index} #{sp_executesql_sql_type(column, value)}"
|
316
|
+
params << quote(value, column)
|
353
317
|
end
|
354
|
-
|
355
|
-
|
318
|
+
[types, params]
|
319
|
+
end
|
320
|
+
|
321
|
+
def sp_executesql_sql_type(column, value)
|
322
|
+
return column.sql_type_for_statement if SQLServerColumn === column
|
323
|
+
if value.is_a?(Numeric)
|
324
|
+
'int'
|
325
|
+
# We can do more here later.
|
326
|
+
else
|
327
|
+
'nvarchar(max)'
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def sp_executesql_sql(sql, types, params, name)
|
332
|
+
if name == 'EXPLAIN'
|
333
|
+
params.each.with_index do |param, index|
|
356
334
|
substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
|
357
335
|
sql.sub! substitute_at_finder, param
|
358
336
|
end
|
359
337
|
else
|
338
|
+
types = quote(types.join(', '))
|
339
|
+
params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp.
|
360
340
|
sql = "EXEC sp_executesql #{quote(sql)}"
|
361
|
-
sql << ", #{
|
341
|
+
sql << ", #{types}, #{params}" unless params.empty?
|
362
342
|
end
|
363
|
-
|
343
|
+
sql
|
364
344
|
end
|
365
345
|
|
366
346
|
def raw_connection_do(sql)
|
@@ -388,13 +368,11 @@ module ActiveRecord
|
|
388
368
|
end
|
389
369
|
|
390
370
|
def raw_connection_run(sql)
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
|
397
|
-
end
|
371
|
+
case @connection_options[:mode]
|
372
|
+
when :dblib
|
373
|
+
@connection.execute(sql)
|
374
|
+
when :odbc
|
375
|
+
block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
|
398
376
|
end
|
399
377
|
end
|
400
378
|
|
@@ -450,6 +428,7 @@ module ActiveRecord
|
|
450
428
|
end
|
451
429
|
handle
|
452
430
|
end
|
431
|
+
|
453
432
|
end
|
454
433
|
end
|
455
434
|
end
|