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
@@ -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 Type
|
5
7
|
class Varbinary < Binary
|
6
8
|
|
7
|
-
def initialize(
|
9
|
+
def initialize(**args)
|
8
10
|
super
|
9
11
|
@limit = 8000 if @limit.to_i == 0
|
10
12
|
end
|
@@ -14,8 +16,9 @@ module ActiveRecord
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def sqlserver_type
|
17
|
-
'varbinary'.
|
18
|
-
type
|
19
|
+
'varbinary'.yield_self do |type|
|
20
|
+
type += "(#{limit})" if limit
|
21
|
+
type
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
@@ -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 Type
|
5
7
|
class VarbinaryMax < Varbinary
|
6
8
|
|
7
|
-
def initialize(
|
9
|
+
def initialize(**args)
|
8
10
|
super
|
9
11
|
@limit = 2_147_483_647
|
10
12
|
end
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def sqlserver_type
|
17
|
-
|
19
|
+
"varbinary(max)"
|
18
20
|
end
|
19
21
|
|
20
22
|
end
|
@@ -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 Type
|
5
7
|
class Varchar < Char
|
6
8
|
|
7
|
-
def initialize(
|
9
|
+
def initialize(**args)
|
8
10
|
super
|
9
11
|
@limit = 8000 if @limit.to_i == 0
|
10
12
|
end
|
@@ -14,8 +16,9 @@ module ActiveRecord
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def sqlserver_type
|
17
|
-
'varchar'.
|
18
|
-
type
|
19
|
+
'varchar'.yield_self do |type|
|
20
|
+
type += "(#{limit})" if limit
|
21
|
+
type
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
@@ -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 Type
|
5
7
|
class VarcharMax < Varchar
|
6
8
|
|
7
|
-
def initialize(
|
9
|
+
def initialize(**args)
|
8
10
|
super
|
9
11
|
@limit = 2_147_483_647
|
10
12
|
end
|
@@ -14,7 +16,7 @@ module ActiveRecord
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def sqlserver_type
|
17
|
-
|
19
|
+
"varchar(max)"
|
18
20
|
end
|
19
21
|
|
20
22
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'base64'
|
2
4
|
require 'active_record'
|
3
5
|
require 'arel_sqlserver'
|
@@ -9,6 +11,7 @@ require 'active_record/connection_adapters/sqlserver/core_ext/explain_subscriber
|
|
9
11
|
require 'active_record/connection_adapters/sqlserver/core_ext/attribute_methods'
|
10
12
|
require 'active_record/connection_adapters/sqlserver/core_ext/finder_methods'
|
11
13
|
require 'active_record/connection_adapters/sqlserver/core_ext/query_methods'
|
14
|
+
require 'active_record/connection_adapters/sqlserver/core_ext/preloader'
|
12
15
|
require 'active_record/connection_adapters/sqlserver/version'
|
13
16
|
require 'active_record/connection_adapters/sqlserver/type'
|
14
17
|
require 'active_record/connection_adapters/sqlserver/database_limits'
|
@@ -80,6 +83,12 @@ module ActiveRecord
|
|
80
83
|
SQLServer::SchemaCreation.new self
|
81
84
|
end
|
82
85
|
|
86
|
+
def self.database_exists?(config)
|
87
|
+
!!ActiveRecord::Base.sqlserver_connection(config)
|
88
|
+
rescue ActiveRecord::NoDatabaseError
|
89
|
+
false
|
90
|
+
end
|
91
|
+
|
83
92
|
def supports_ddl_transactions?
|
84
93
|
true
|
85
94
|
end
|
@@ -144,10 +153,30 @@ module ActiveRecord
|
|
144
153
|
true
|
145
154
|
end
|
146
155
|
|
156
|
+
def supports_lazy_transactions?
|
157
|
+
true
|
158
|
+
end
|
159
|
+
|
147
160
|
def supports_in_memory_oltp?
|
148
161
|
@version_year >= 2014
|
149
162
|
end
|
150
163
|
|
164
|
+
def supports_insert_returning?
|
165
|
+
true
|
166
|
+
end
|
167
|
+
|
168
|
+
def supports_insert_on_duplicate_skip?
|
169
|
+
false
|
170
|
+
end
|
171
|
+
|
172
|
+
def supports_insert_on_duplicate_update?
|
173
|
+
false
|
174
|
+
end
|
175
|
+
|
176
|
+
def supports_insert_conflict_target?
|
177
|
+
false
|
178
|
+
end
|
179
|
+
|
151
180
|
def disable_referential_integrity
|
152
181
|
tables = tables_with_referential_integrity
|
153
182
|
tables.each { |t| do_execute "ALTER TABLE #{quote_table_name(t)} NOCHECK CONSTRAINT ALL" }
|
@@ -196,7 +225,7 @@ module ActiveRecord
|
|
196
225
|
# === Abstract Adapter (Misc Support) =========================== #
|
197
226
|
|
198
227
|
def tables_with_referential_integrity
|
199
|
-
schemas_and_tables = select_rows
|
228
|
+
schemas_and_tables = select_rows <<~SQL.squish
|
200
229
|
SELECT DISTINCT s.name, o.name
|
201
230
|
FROM sys.foreign_keys i
|
202
231
|
INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
|
@@ -256,6 +285,9 @@ module ActiveRecord
|
|
256
285
|
result
|
257
286
|
end
|
258
287
|
|
288
|
+
def get_database_version # :nodoc:
|
289
|
+
version_year
|
290
|
+
end
|
259
291
|
|
260
292
|
protected
|
261
293
|
|
@@ -325,18 +357,20 @@ module ActiveRecord
|
|
325
357
|
m.register_type 'timestamp', SQLServer::Type::Timestamp.new
|
326
358
|
end
|
327
359
|
|
328
|
-
def translate_exception(e, message)
|
360
|
+
def translate_exception(e, message:, sql:, binds:)
|
329
361
|
case message
|
330
362
|
when /(cannot insert duplicate key .* with unique index) | (violation of unique key constraint)/i
|
331
|
-
RecordNotUnique.new(message)
|
332
|
-
when /conflicted with the foreign key constraint/i
|
333
|
-
InvalidForeignKey.new(message)
|
363
|
+
RecordNotUnique.new(message, sql: sql, binds: binds)
|
364
|
+
when /(conflicted with the foreign key constraint) | (The DELETE statement conflicted with the REFERENCE constraint)/i
|
365
|
+
InvalidForeignKey.new(message, sql: sql, binds: binds)
|
334
366
|
when /has been chosen as the deadlock victim/i
|
335
|
-
DeadlockVictim.new(message)
|
367
|
+
DeadlockVictim.new(message, sql: sql, binds: binds)
|
336
368
|
when /database .* does not exist/i
|
337
|
-
NoDatabaseError.new(message)
|
369
|
+
NoDatabaseError.new(message, sql: sql, binds: binds)
|
338
370
|
when /data would be truncated/
|
339
|
-
ValueTooLong.new(message)
|
371
|
+
ValueTooLong.new(message, sql: sql, binds: binds)
|
372
|
+
when /connection timed out/
|
373
|
+
StatementTimeout.new(message, sql: sql, binds: binds)
|
340
374
|
when /Column '(.*)' is not the same data type as referencing column '(.*)' in foreign key/
|
341
375
|
pk_id, fk_id = SQLServer::Utils.extract_identifiers($1), SQLServer::Utils.extract_identifiers($2)
|
342
376
|
MismatchedForeignKey.new(
|
@@ -348,9 +382,9 @@ module ActiveRecord
|
|
348
382
|
primary_key: pk_id.object
|
349
383
|
)
|
350
384
|
when /Cannot insert the value NULL into column.*does not allow nulls/
|
351
|
-
NotNullViolation.new(message)
|
385
|
+
NotNullViolation.new(message, sql: sql, binds: binds)
|
352
386
|
when /Arithmetic overflow error/
|
353
|
-
RangeError.new(message)
|
387
|
+
RangeError.new(message, sql: sql, binds: binds)
|
354
388
|
else
|
355
389
|
super
|
356
390
|
end
|
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionAdapters
|
3
5
|
class SQLServerColumn < Column
|
4
6
|
|
5
|
-
def initialize(name, default, sql_type_metadata = nil, null = true,
|
6
|
-
@sqlserver_options = sqlserver_options
|
7
|
-
super
|
7
|
+
def initialize(name, default, sql_type_metadata = nil, null = true, default_function = nil, collation: nil, comment: nil, **sqlserver_options)
|
8
|
+
@sqlserver_options = sqlserver_options
|
9
|
+
super
|
8
10
|
end
|
9
11
|
|
10
12
|
def is_identity?
|
@@ -15,6 +17,10 @@ module ActiveRecord
|
|
15
17
|
@sqlserver_options[:is_primary]
|
16
18
|
end
|
17
19
|
|
20
|
+
def table_name
|
21
|
+
@sqlserver_options[:table_name]
|
22
|
+
end
|
23
|
+
|
18
24
|
def is_utf8?
|
19
25
|
sql_type =~ /nvarchar|ntext|nchar/i
|
20
26
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module ConnectionHandling
|
3
5
|
def sqlserver_connection(config) #:nodoc:
|
@@ -11,6 +13,12 @@ module ActiveRecord
|
|
11
13
|
raise ArgumentError, "Unknown connection mode in #{config.inspect}."
|
12
14
|
end
|
13
15
|
ConnectionAdapters::SQLServerAdapter.new(nil, nil, config.merge(mode: mode))
|
16
|
+
rescue TinyTds::Error => e
|
17
|
+
if e.message.match(/database .* does not exist/i)
|
18
|
+
raise ActiveRecord::NoDatabaseError
|
19
|
+
else
|
20
|
+
raise
|
21
|
+
end
|
14
22
|
end
|
15
23
|
end
|
16
24
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Arel
|
2
4
|
module Visitors
|
3
5
|
class SQLServer < Arel::Visitors::ToSql
|
@@ -22,6 +24,12 @@ module Arel
|
|
22
24
|
collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} "
|
23
25
|
end
|
24
26
|
|
27
|
+
def visit_Arel_Nodes_Concat(o, collector)
|
28
|
+
visit o.left, collector
|
29
|
+
collector << " + "
|
30
|
+
visit o.right, collector
|
31
|
+
end
|
32
|
+
|
25
33
|
def visit_Arel_Nodes_UpdateStatement(o, a)
|
26
34
|
if o.orders.any? && o.limit.nil?
|
27
35
|
o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
|
@@ -31,7 +39,7 @@ module Arel
|
|
31
39
|
|
32
40
|
def visit_Arel_Nodes_Lock o, collector
|
33
41
|
o.expr = Arel.sql('WITH(UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/
|
34
|
-
collector <<
|
42
|
+
collector << " "
|
35
43
|
visit o.expr, collector
|
36
44
|
end
|
37
45
|
|
@@ -52,12 +60,17 @@ module Arel
|
|
52
60
|
end
|
53
61
|
end
|
54
62
|
|
63
|
+
def visit_Arel_Nodes_Grouping(o, collector)
|
64
|
+
remove_invalid_ordering_from_select_statement(o.expr)
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
55
68
|
def visit_Arel_Nodes_SelectStatement o, collector
|
56
69
|
@select_statement = o
|
57
70
|
distinct_One_As_One_Is_So_Not_Fetch o
|
58
71
|
if o.with
|
59
72
|
collector = visit o.with, collector
|
60
|
-
collector <<
|
73
|
+
collector << " "
|
61
74
|
end
|
62
75
|
collector = o.cores.inject(collector) { |c,x|
|
63
76
|
visit_Arel_Nodes_SelectCore(x, c)
|
@@ -95,7 +108,7 @@ module Arel
|
|
95
108
|
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector
|
96
109
|
end
|
97
110
|
if o.right.any?
|
98
|
-
collector <<
|
111
|
+
collector << " " if o.left
|
99
112
|
collector = inject_join o.right, collector, ' '
|
100
113
|
end
|
101
114
|
collector
|
@@ -106,7 +119,7 @@ module Arel
|
|
106
119
|
collector = visit o.left, collector
|
107
120
|
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
|
108
121
|
if o.right
|
109
|
-
collector <<
|
122
|
+
collector << " "
|
110
123
|
visit(o.right, collector)
|
111
124
|
else
|
112
125
|
collector
|
@@ -117,16 +130,26 @@ module Arel
|
|
117
130
|
collector << "LEFT OUTER JOIN "
|
118
131
|
collector = visit o.left, collector
|
119
132
|
collector = visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, space: true
|
120
|
-
collector <<
|
133
|
+
collector << " "
|
121
134
|
visit o.right, collector
|
122
135
|
end
|
123
136
|
|
137
|
+
def collect_in_clause(left, right, collector)
|
138
|
+
if Array === right
|
139
|
+
right.each { |node| remove_invalid_ordering_from_select_statement(node) }
|
140
|
+
else
|
141
|
+
remove_invalid_ordering_from_select_statement(right)
|
142
|
+
end
|
143
|
+
|
144
|
+
super
|
145
|
+
end
|
146
|
+
|
124
147
|
# SQLServer ToSql/Visitor (Additions)
|
125
148
|
|
126
149
|
def visit_Arel_Nodes_SelectStatement_SQLServer_Lock collector, options = {}
|
127
150
|
if select_statement_lock?
|
128
151
|
collector = visit @select_statement.lock, collector
|
129
|
-
collector <<
|
152
|
+
collector << " " if options[:space]
|
130
153
|
end
|
131
154
|
collector
|
132
155
|
end
|
@@ -134,12 +157,11 @@ module Arel
|
|
134
157
|
def visit_Orders_And_Let_Fetch_Happen o, collector
|
135
158
|
make_Fetch_Possible_And_Deterministic o
|
136
159
|
unless o.orders.empty?
|
137
|
-
collector <<
|
138
|
-
collector << ORDER_BY
|
160
|
+
collector << " ORDER BY "
|
139
161
|
len = o.orders.length - 1
|
140
162
|
o.orders.each_with_index { |x, i|
|
141
163
|
collector = visit(x, collector)
|
142
|
-
collector <<
|
164
|
+
collector << ", " unless len == i
|
143
165
|
}
|
144
166
|
end
|
145
167
|
collector
|
@@ -196,7 +218,7 @@ module Arel
|
|
196
218
|
elsif Arel::Nodes::SqlLiteral === core.from
|
197
219
|
Arel::Table.new(core.from)
|
198
220
|
elsif Arel::Nodes::JoinSource === core.source
|
199
|
-
Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
|
221
|
+
Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left.left
|
200
222
|
end
|
201
223
|
end
|
202
224
|
|
@@ -213,6 +235,14 @@ module Arel
|
|
213
235
|
).quoted
|
214
236
|
end
|
215
237
|
|
238
|
+
# Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer
|
239
|
+
# returns error "The ORDER BY clause is invalid in views, inline functions, derived tables,
|
240
|
+
# subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified."
|
241
|
+
def remove_invalid_ordering_from_select_statement(node)
|
242
|
+
return unless Arel::Nodes::SelectStatement === node
|
243
|
+
|
244
|
+
node.orders = [] unless node.offset || node.limit
|
245
|
+
end
|
216
246
|
end
|
217
247
|
end
|
218
248
|
end
|
data/lib/arel_sqlserver.rb
CHANGED
data/test/appveyor/dbsetup.ps1
CHANGED
@@ -5,15 +5,15 @@ Write-Output "Setting up..."
|
|
5
5
|
|
6
6
|
Write-Output "Setting variables..."
|
7
7
|
$serverName = $env:COMPUTERNAME
|
8
|
-
$
|
8
|
+
$instanceNames = @('SQL2014')
|
9
9
|
$smo = 'Microsoft.SqlServer.Management.Smo.'
|
10
10
|
$wmi = new-object ($smo + 'Wmi.ManagedComputer')
|
11
11
|
|
12
12
|
Write-Output "Configure Instances..."
|
13
|
-
foreach ($
|
14
|
-
Write-Output "Instance $
|
13
|
+
foreach ($instanceName in $instanceNames) {
|
14
|
+
Write-Output "Instance $instanceName ..."
|
15
15
|
Write-Output "Enable TCP/IP and port 1433..."
|
16
|
-
$uri = "ManagedComputer[@Name='$serverName']/ServerInstance[@Name='$
|
16
|
+
$uri = "ManagedComputer[@Name='$serverName']/ServerInstance[@Name='$instanceName']/ServerProtocol[@Name='Tcp']"
|
17
17
|
$tcp = $wmi.GetSmoObject($uri)
|
18
18
|
$tcp.IsEnabled = $true
|
19
19
|
foreach ($ipAddress in $Tcp.IPAddresses) {
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cases/helper_sqlserver'
|
2
4
|
require 'models/topic'
|
3
5
|
require 'models/task'
|
@@ -13,7 +15,7 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
13
15
|
let(:basic_update_sql) { "UPDATE [customers] SET [address_street] = NULL WHERE [id] = 2" }
|
14
16
|
let(:basic_select_sql) { "SELECT * FROM [customers] WHERE ([customers].[id] = 1)" }
|
15
17
|
|
16
|
-
it 'has basic and non-
|
18
|
+
it 'has basic and non-sensitive information in the adapters inspect method' do
|
17
19
|
string = connection.inspect
|
18
20
|
_(string).must_match %r{ActiveRecord::ConnectionAdapters::SQLServerAdapter}
|
19
21
|
_(string).must_match %r{version\: \d.\d}
|
@@ -65,6 +67,25 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
65
67
|
assert_equal 'customers', connection.send(:get_table_name, basic_select_sql)
|
66
68
|
end
|
67
69
|
|
70
|
+
it 'test bad connection' do
|
71
|
+
assert_raise ActiveRecord::NoDatabaseError do
|
72
|
+
config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest')
|
73
|
+
ActiveRecord::Base.sqlserver_connection config
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'test database exists returns false if database does not exist' do
|
78
|
+
config = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest')
|
79
|
+
assert_not ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config),
|
80
|
+
'expected database to not exist'
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'test database exists returns true when the database exists' do
|
84
|
+
config = ActiveRecord::Base.configurations['arunit']
|
85
|
+
assert ActiveRecord::ConnectionAdapters::SQLServerAdapter.database_exists?(config),
|
86
|
+
"expected database #{config[:database]} to exist"
|
87
|
+
end
|
88
|
+
|
68
89
|
describe 'with different language' do
|
69
90
|
|
70
91
|
before do
|
@@ -429,5 +450,48 @@ class AdapterTestSQLServer < ActiveRecord::TestCase
|
|
429
450
|
end
|
430
451
|
end
|
431
452
|
|
453
|
+
describe 'block writes to a database' do
|
454
|
+
def setup
|
455
|
+
@conn = ActiveRecord::Base.connection
|
456
|
+
@connection_handler = ActiveRecord::Base.connection_handler
|
457
|
+
end
|
458
|
+
|
459
|
+
def test_errors_when_an_insert_query_is_called_while_preventing_writes
|
460
|
+
assert_raises(ActiveRecord::ReadOnlyError) do
|
461
|
+
@connection_handler.while_preventing_writes do
|
462
|
+
@conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
def test_errors_when_an_update_query_is_called_while_preventing_writes
|
468
|
+
@conn.insert("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
|
469
|
+
|
470
|
+
assert_raises(ActiveRecord::ReadOnlyError) do
|
471
|
+
@connection_handler.while_preventing_writes do
|
472
|
+
@conn.update("UPDATE [subscribers] SET [subscribers].[name] = 'Aidan' WHERE [subscribers].[nick] = 'aido'")
|
473
|
+
end
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
def test_errors_when_a_delete_query_is_called_while_preventing_writes
|
478
|
+
@conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
|
479
|
+
|
480
|
+
assert_raises(ActiveRecord::ReadOnlyError) do
|
481
|
+
@connection_handler.while_preventing_writes do
|
482
|
+
@conn.execute("DELETE FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
|
483
|
+
end
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def test_doesnt_error_when_a_select_query_is_called_while_preventing_writes
|
488
|
+
@conn.execute("INSERT INTO [subscribers] ([nick]) VALUES ('aido')")
|
489
|
+
|
490
|
+
@connection_handler.while_preventing_writes do
|
491
|
+
assert_equal 1, @conn.execute("SELECT * FROM [subscribers] WHERE [subscribers].[nick] = 'aido'")
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
432
496
|
end
|
433
497
|
|