activerecord-sqlserver-adapter 5.2.1 → 6.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.github/issue_template.md +23 -0
- data/.travis.yml +6 -8
- data/CHANGELOG.md +22 -32
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +42 -41
- data/README.md +9 -30
- data/RUNNING_UNIT_TESTS.md +3 -0
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +25 -14
- data/appveyor.yml +24 -17
- data/docker-compose.ci.yml +7 -5
- data/guides/RELEASING.md +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +4 -1
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +55 -14
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +38 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -3
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +93 -70
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +42 -40
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -2
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +5 -2
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +7 -6
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -2
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +44 -10
- data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -3
- data/lib/active_record/sqlserver_base.rb +8 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -0
- data/lib/activerecord-sqlserver-adapter.rb +2 -0
- data/lib/arel/visitors/sqlserver.rb +40 -10
- data/lib/arel_sqlserver.rb +2 -0
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/adapter_test_sqlserver.rb +65 -1
- data/test/cases/change_column_null_test_sqlserver.rb +2 -0
- data/test/cases/coerced_tests.rb +644 -187
- data/test/cases/column_test_sqlserver.rb +2 -1
- data/test/cases/connection_test_sqlserver.rb +2 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +2 -0
- data/test/cases/fetch_test_sqlserver.rb +2 -0
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +4 -2
- data/test/cases/helper_sqlserver.rb +2 -0
- data/test/cases/in_clause_test_sqlserver.rb +36 -0
- data/test/cases/index_test_sqlserver.rb +2 -0
- data/test/cases/json_test_sqlserver.rb +2 -0
- data/test/cases/migration_test_sqlserver.rb +4 -2
- data/test/cases/order_test_sqlserver.rb +2 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +2 -0
- data/test/cases/rake_test_sqlserver.rb +2 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +3 -1
- data/test/cases/schema_test_sqlserver.rb +2 -0
- data/test/cases/scratchpad_test_sqlserver.rb +2 -0
- data/test/cases/showplan_test_sqlserver.rb +4 -2
- data/test/cases/specific_schema_test_sqlserver.rb +2 -0
- data/test/cases/transaction_test_sqlserver.rb +2 -1
- data/test/cases/trigger_test_sqlserver.rb +2 -1
- data/test/cases/utils_test_sqlserver.rb +2 -0
- data/test/cases/uuid_test_sqlserver.rb +2 -1
- data/test/debug.rb +2 -0
- data/test/migrations/create_clients_and_change_column_null.rb +2 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -0
- data/test/models/sqlserver/booking.rb +2 -0
- data/test/models/sqlserver/customers_view.rb +2 -0
- data/test/models/sqlserver/datatype.rb +2 -0
- data/test/models/sqlserver/datatype_migration.rb +2 -0
- data/test/models/sqlserver/dollar_table_name.rb +2 -0
- data/test/models/sqlserver/edge_schema.rb +2 -0
- data/test/models/sqlserver/fk_has_fk.rb +2 -0
- data/test/models/sqlserver/fk_has_pk.rb +2 -0
- data/test/models/sqlserver/natural_pk_data.rb +2 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +2 -0
- data/test/models/sqlserver/no_pk_data.rb +2 -0
- data/test/models/sqlserver/object_default.rb +2 -0
- data/test/models/sqlserver/quoted_table.rb +2 -0
- data/test/models/sqlserver/quoted_view_1.rb +2 -0
- data/test/models/sqlserver/quoted_view_2.rb +2 -0
- data/test/models/sqlserver/sst_memory.rb +2 -0
- data/test/models/sqlserver/string_default.rb +2 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +2 -0
- data/test/models/sqlserver/string_defaults_view.rb +2 -0
- data/test/models/sqlserver/tinyint_pk.rb +2 -0
- data/test/models/sqlserver/trigger.rb +2 -0
- data/test/models/sqlserver/trigger_history.rb +2 -0
- data/test/models/sqlserver/upper.rb +2 -0
- data/test/models/sqlserver/uppered.rb +2 -0
- data/test/models/sqlserver/uuid.rb +2 -0
- data/test/schema/sqlserver_specific_schema.rb +2 -0
- data/test/support/coerceable_test_sqlserver.rb +14 -5
- data/test/support/connection_reflection.rb +2 -0
- data/test/support/core_ext/query_cache.rb +3 -0
- data/test/support/load_schema_sqlserver.rb +2 -0
- data/test/support/minitest_sqlserver.rb +2 -0
- data/test/support/paths_sqlserver.rb +2 -0
- data/test/support/rake_helpers.rb +1 -0
- data/test/support/sql_counter_sqlserver.rb +3 -0
- data/test/support/test_in_memory_oltp.rb +2 -0
- metadata +18 -9
data/docker-compose.ci.yml
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
version: "2.2"
|
2
2
|
services:
|
3
|
-
|
3
|
+
sqlserver:
|
4
4
|
image: metaskills/mssql-server-linux-rails
|
5
5
|
ci:
|
6
6
|
environment:
|
7
|
-
- ACTIVERECORD_UNITTEST_HOST=
|
8
|
-
build:
|
9
|
-
|
7
|
+
- ACTIVERECORD_UNITTEST_HOST=sqlserver
|
8
|
+
build:
|
9
|
+
context: .
|
10
|
+
dockerfile: Dockerfile.ci
|
11
|
+
command: wait-for sqlserver:1433 -- bundle exec rake test
|
10
12
|
depends_on:
|
11
|
-
- "
|
13
|
+
- "sqlserver"
|
data/guides/RELEASING.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Releasing
|
2
|
+
|
3
|
+
## Building locally
|
4
|
+
|
5
|
+
If you want to build the gem to test it locally run `bundle exec rake build`.
|
6
|
+
|
7
|
+
This command will build the gem in `pkg/activerecord-sqlserver-adapter-A.B.C.gem`, where `A.B.C` is the version in `VERSION` file.
|
8
|
+
|
9
|
+
## Releasing to RubyGems
|
10
|
+
|
11
|
+
Run `bundle exec rake release` to build the gem locally and push the `gem` file to RubyGems.
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLServer
|
4
6
|
module CoreExt
|
5
7
|
module Explain
|
6
8
|
|
7
|
-
SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '
|
9
|
+
SQLSERVER_STATEMENT_PREFIX = 'EXEC sp_executesql '
|
8
10
|
SQLSERVER_STATEMENT_REGEXP = /N'(.+)', N'(.+)', (.+)/
|
9
11
|
|
10
12
|
def exec_explain(queries)
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record/relation'
|
2
4
|
require 'active_record/version'
|
3
5
|
|
@@ -11,13 +13,13 @@ module ActiveRecord
|
|
11
13
|
|
12
14
|
# Same as original except we order by values in distinct select if present.
|
13
15
|
def construct_relation_for_exists(conditions)
|
14
|
-
|
15
|
-
relation = limit!(1)
|
16
|
+
conditions = sanitize_forbidden_attributes(conditions)
|
16
17
|
|
18
|
+
if distinct_value && offset_value
|
17
19
|
if select_values.present?
|
18
|
-
relation =
|
20
|
+
relation = order(*select_values).limit!(1)
|
19
21
|
else
|
20
|
-
relation =
|
22
|
+
relation = except(:order).limit!(1)
|
21
23
|
end
|
22
24
|
else
|
23
25
|
relation = except(:select, :distinct, :order)._select!(::ActiveRecord::FinderMethods::ONE_AS_ONE).limit!(1)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record/associations/preloader"
|
4
|
+
|
5
|
+
module ActiveRecord
|
6
|
+
module ConnectionAdapters
|
7
|
+
module SQLServer
|
8
|
+
module CoreExt
|
9
|
+
module Preloader
|
10
|
+
private
|
11
|
+
|
12
|
+
def records_for(ids)
|
13
|
+
ids.each_slice(in_clause_length).flat_map do |slice|
|
14
|
+
scope.where(association_key_name => slice).load do |record|
|
15
|
+
# Processing only the first owner
|
16
|
+
# because the record is modified but not an owner
|
17
|
+
owner = owners_by_key[convert_key(record[association_key_name])].first
|
18
|
+
association = owner.association(reflection.name)
|
19
|
+
association.set_inverse_instance(record)
|
20
|
+
end.records
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def in_clause_length
|
25
|
+
10_000
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
ActiveSupport.on_load(:active_record) do
|
34
|
+
mod = ActiveRecord::ConnectionAdapters::SQLServer::CoreExt::Preloader
|
35
|
+
ActiveRecord::Associations::Preloader::Association.prepend(mod)
|
36
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_record/relation'
|
2
4
|
require 'active_record/version'
|
3
5
|
|
@@ -9,7 +11,8 @@ module ActiveRecord
|
|
9
11
|
|
10
12
|
private
|
11
13
|
|
12
|
-
# Copy of original from Rails master.
|
14
|
+
# Copy of original from Rails master.
|
15
|
+
# This patch can be removed when adapter supports Rails version greater than 6.0.2.2
|
13
16
|
def table_name_matches?(from)
|
14
17
|
table_name = Regexp.escape(table.name)
|
15
18
|
quoted_table_name = Regexp.escape(connection.quote_table_name(table.name))
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLServer
|
@@ -9,10 +11,12 @@ module ActiveRecord
|
|
9
11
|
def column_name_length
|
10
12
|
128
|
11
13
|
end
|
14
|
+
deprecate :column_name_length
|
12
15
|
|
13
16
|
def table_name_length
|
14
17
|
128
|
15
18
|
end
|
19
|
+
deprecate :table_name_length
|
16
20
|
|
17
21
|
def index_name_length
|
18
22
|
128
|
@@ -21,14 +25,17 @@ module ActiveRecord
|
|
21
25
|
def columns_per_table
|
22
26
|
1024
|
23
27
|
end
|
28
|
+
deprecate :columns_per_table
|
24
29
|
|
25
30
|
def indexes_per_table
|
26
31
|
999
|
27
32
|
end
|
33
|
+
deprecate :indexes_per_table
|
28
34
|
|
29
35
|
def columns_per_multicolumn_index
|
30
36
|
16
|
31
37
|
end
|
38
|
+
deprecate :columns_per_multicolumn_index
|
32
39
|
|
33
40
|
def in_clause_length
|
34
41
|
10_000
|
@@ -37,10 +44,12 @@ module ActiveRecord
|
|
37
44
|
def sql_query_length
|
38
45
|
65_536 * 4_096
|
39
46
|
end
|
47
|
+
deprecate :sql_query_length
|
40
48
|
|
41
49
|
def joins_per_query
|
42
50
|
256
|
43
51
|
end
|
52
|
+
deprecate :joins_per_query
|
44
53
|
|
45
54
|
private
|
46
55
|
|
@@ -1,9 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLServer
|
4
6
|
module DatabaseStatements
|
7
|
+
READ_QUERY = ActiveRecord::ConnectionAdapters::AbstractAdapter.build_read_query_regexp(:begin, :commit, :dbcc, :explain, :save, :select, :set, :rollback) # :nodoc:
|
8
|
+
private_constant :READ_QUERY
|
9
|
+
|
10
|
+
def write_query?(sql) # :nodoc:
|
11
|
+
!READ_QUERY.match?(sql)
|
12
|
+
end
|
5
13
|
|
6
14
|
def execute(sql, name = nil)
|
15
|
+
if preventing_writes? && write_query?(sql)
|
16
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
17
|
+
end
|
18
|
+
|
19
|
+
materialize_transactions
|
20
|
+
|
7
21
|
if id_insert_table_name = query_requires_identity_insert?(sql)
|
8
22
|
with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
|
9
23
|
else
|
@@ -12,6 +26,12 @@ module ActiveRecord
|
|
12
26
|
end
|
13
27
|
|
14
28
|
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
|
29
|
+
if preventing_writes? && write_query?(sql)
|
30
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
31
|
+
end
|
32
|
+
|
33
|
+
materialize_transactions
|
34
|
+
|
15
35
|
sp_executesql(sql, name, binds, prepare: prepare)
|
16
36
|
end
|
17
37
|
|
@@ -71,9 +91,11 @@ module ActiveRecord
|
|
71
91
|
def release_savepoint(name = current_savepoint_name)
|
72
92
|
end
|
73
93
|
|
74
|
-
def case_sensitive_comparison(
|
94
|
+
def case_sensitive_comparison(attribute, value)
|
95
|
+
column = column_for_attribute(attribute)
|
96
|
+
|
75
97
|
if column.collation && !column.case_sensitive?
|
76
|
-
|
98
|
+
attribute.eq(Arel::Nodes::Bin.new(value))
|
77
99
|
else
|
78
100
|
super
|
79
101
|
end
|
@@ -89,12 +111,12 @@ module ActiveRecord
|
|
89
111
|
end
|
90
112
|
end
|
91
113
|
|
92
|
-
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}"
|
93
|
-
|
114
|
+
table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}" }
|
115
|
+
total_sqls = Array.wrap(table_deletes + fixture_inserts)
|
94
116
|
|
95
117
|
disable_referential_integrity do
|
96
118
|
transaction(requires_new: true) do
|
97
|
-
|
119
|
+
total_sqls.each do |sql|
|
98
120
|
execute sql, "Fixtures Load"
|
99
121
|
yield if block_given?
|
100
122
|
end
|
@@ -107,11 +129,6 @@ module ActiveRecord
|
|
107
129
|
end
|
108
130
|
private :can_perform_case_insensitive_comparison_for?
|
109
131
|
|
110
|
-
def combine_multi_statements(total_sql)
|
111
|
-
total_sql
|
112
|
-
end
|
113
|
-
private :combine_multi_statements
|
114
|
-
|
115
132
|
def default_insert_value(column)
|
116
133
|
if column.is_identity?
|
117
134
|
table_name = quote(quote_table_name(column.table_name))
|
@@ -122,9 +139,22 @@ module ActiveRecord
|
|
122
139
|
end
|
123
140
|
private :default_insert_value
|
124
141
|
|
142
|
+
def build_insert_sql(insert) # :nodoc:
|
143
|
+
sql = +"INSERT #{insert.into}"
|
144
|
+
|
145
|
+
if returning = insert.send(:insert_all).returning
|
146
|
+
sql << " OUTPUT " << returning.map {|column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
|
147
|
+
end
|
148
|
+
|
149
|
+
sql << " #{insert.values_list}"
|
150
|
+
sql
|
151
|
+
end
|
152
|
+
|
125
153
|
# === SQLServer Specific ======================================== #
|
126
154
|
|
127
155
|
def execute_procedure(proc_name, *variables)
|
156
|
+
materialize_transactions
|
157
|
+
|
128
158
|
vars = if variables.any? && variables.first.is_a?(Hash)
|
129
159
|
variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
|
130
160
|
else
|
@@ -221,18 +251,20 @@ module ActiveRecord
|
|
221
251
|
|
222
252
|
protected
|
223
253
|
|
224
|
-
def sql_for_insert(sql, pk,
|
254
|
+
def sql_for_insert(sql, pk, binds)
|
225
255
|
if pk.nil?
|
226
256
|
table_name = query_requires_identity_insert?(sql)
|
227
257
|
pk = primary_key(table_name)
|
228
258
|
end
|
259
|
+
|
229
260
|
sql = if pk && use_output_inserted? && !database_prefix_remote_server?
|
230
261
|
quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
|
231
262
|
table_name ||= get_table_name(sql)
|
232
263
|
exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
|
264
|
+
|
233
265
|
if exclude_output_inserted
|
234
266
|
id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted
|
235
|
-
|
267
|
+
<<~SQL.squish
|
236
268
|
DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
|
237
269
|
#{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
|
238
270
|
SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable
|
@@ -257,6 +289,8 @@ module ActiveRecord
|
|
257
289
|
# === SQLServer Specific (Executing) ============================ #
|
258
290
|
|
259
291
|
def do_execute(sql, name = 'SQL')
|
292
|
+
materialize_transactions
|
293
|
+
|
260
294
|
log(sql, name) { raw_connection_do(sql) }
|
261
295
|
end
|
262
296
|
|
@@ -310,7 +344,7 @@ module ActiveRecord
|
|
310
344
|
types = quote(types.join(', '))
|
311
345
|
params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp.
|
312
346
|
sql = "EXEC sp_executesql #{quote(sql)}"
|
313
|
-
sql
|
347
|
+
sql += ", #{types}, #{params}" unless params.empty?
|
314
348
|
end
|
315
349
|
sql
|
316
350
|
end
|
@@ -318,7 +352,14 @@ module ActiveRecord
|
|
318
352
|
def raw_connection_do(sql)
|
319
353
|
case @connection_options[:mode]
|
320
354
|
when :dblib
|
321
|
-
@connection.execute(sql)
|
355
|
+
result = @connection.execute(sql)
|
356
|
+
|
357
|
+
# TinyTDS returns false instead of raising an exception if connection fails.
|
358
|
+
# Getting around this by raising an exception ourselves while this PR
|
359
|
+
# https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
|
360
|
+
raise TinyTds::Error, 'failed to execute statement' if result.is_a?(FalseClass)
|
361
|
+
|
362
|
+
result.do
|
322
363
|
end
|
323
364
|
ensure
|
324
365
|
@update_sql = false
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLServer
|
@@ -68,6 +70,42 @@ module ActiveRecord
|
|
68
70
|
end
|
69
71
|
end
|
70
72
|
|
73
|
+
def column_name_matcher
|
74
|
+
COLUMN_NAME
|
75
|
+
end
|
76
|
+
|
77
|
+
def column_name_with_order_matcher
|
78
|
+
COLUMN_NAME_WITH_ORDER
|
79
|
+
end
|
80
|
+
|
81
|
+
COLUMN_NAME = /
|
82
|
+
\A
|
83
|
+
(
|
84
|
+
(?:
|
85
|
+
# [table_name].[column_name] | function(one or no argument)
|
86
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
87
|
+
)
|
88
|
+
(?:\s+AS\s+(?:\w+|\[\w+\]))?
|
89
|
+
)
|
90
|
+
(?:\s*,\s*\g<1>)*
|
91
|
+
\z
|
92
|
+
/ix
|
93
|
+
|
94
|
+
COLUMN_NAME_WITH_ORDER = /
|
95
|
+
\A
|
96
|
+
(
|
97
|
+
(?:
|
98
|
+
# [table_name].[column_name] | function(one or no argument)
|
99
|
+
((?:\w+\.|\[\w+\]\.)?(?:\w+|\[\w+\])) | \w+\((?:|\g<2>)\)
|
100
|
+
)
|
101
|
+
(?:\s+ASC|\s+DESC)?
|
102
|
+
(?:\s+NULLS\s+(?:FIRST|LAST))?
|
103
|
+
)
|
104
|
+
(?:\s*,\s*\g<1>)*
|
105
|
+
\z
|
106
|
+
/ix
|
107
|
+
|
108
|
+
private_constant :COLUMN_NAME, :COLUMN_NAME_WITH_ORDER
|
71
109
|
|
72
110
|
private
|
73
111
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
module SQLServer
|
@@ -6,15 +8,26 @@ module ActiveRecord
|
|
6
8
|
private
|
7
9
|
|
8
10
|
def visit_TableDefinition(o)
|
11
|
+
if_not_exists = o.if_not_exists
|
12
|
+
|
9
13
|
if o.as
|
10
14
|
table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
|
11
15
|
query = o.as.respond_to?(:to_sql) ? o.as.to_sql : o.as
|
12
16
|
projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
|
13
|
-
|
17
|
+
sql = "SELECT #{projections} INTO #{table_name} FROM #{source}"
|
14
18
|
else
|
15
19
|
o.instance_variable_set :@as, nil
|
16
|
-
|
20
|
+
o.instance_variable_set :@if_not_exists, false
|
21
|
+
sql = super
|
17
22
|
end
|
23
|
+
|
24
|
+
if if_not_exists
|
25
|
+
o.instance_variable_set :@if_not_exists, true
|
26
|
+
table_name = o.temporary ? "##{o.name}" : o.name
|
27
|
+
sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='#{table_name}' and xtype='U') #{sql}"
|
28
|
+
end
|
29
|
+
|
30
|
+
sql
|
18
31
|
end
|
19
32
|
|
20
33
|
def add_column_options!(sql, options)
|
@@ -34,7 +47,7 @@ module ActiveRecord
|
|
34
47
|
def action_sql(action, dependency)
|
35
48
|
case dependency
|
36
49
|
when :restrict
|
37
|
-
raise ArgumentError,
|
50
|
+
raise ArgumentError, <<~MSG.squish
|
38
51
|
'#{dependency}' is not supported for :on_update or :on_delete.
|
39
52
|
Supported values are: :nullify, :cascade
|
40
53
|
MSG
|