activerecord-sqlserver-adapter 5.2.1 → 6.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|