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.
Files changed (146) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.travis.yml +6 -8
  5. data/CHANGELOG.md +22 -32
  6. data/{Dockerfile → Dockerfile.ci} +1 -1
  7. data/Gemfile +42 -41
  8. data/README.md +9 -30
  9. data/RUNNING_UNIT_TESTS.md +3 -0
  10. data/Rakefile +2 -0
  11. data/VERSION +1 -1
  12. data/activerecord-sqlserver-adapter.gemspec +25 -14
  13. data/appveyor.yml +24 -17
  14. data/docker-compose.ci.yml +7 -5
  15. data/guides/RELEASING.md +11 -0
  16. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -0
  17. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +2 -0
  18. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +2 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -1
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +6 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +4 -1
  24. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  25. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +55 -14
  26. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +2 -0
  27. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -0
  28. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +38 -0
  29. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -3
  30. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +2 -0
  31. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +93 -70
  32. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +2 -0
  33. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -0
  34. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +2 -0
  35. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -0
  36. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +42 -40
  37. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +3 -1
  38. data/lib/active_record/connection_adapters/sqlserver/type.rb +2 -0
  39. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -1
  40. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -2
  41. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -1
  42. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +5 -2
  43. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -0
  44. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -1
  45. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +7 -6
  46. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -0
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -0
  48. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -1
  50. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -1
  51. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -2
  53. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -1
  54. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -2
  56. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -1
  57. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -0
  58. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -1
  59. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +5 -4
  60. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +2 -0
  61. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -1
  62. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -1
  63. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -2
  64. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -0
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -1
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -3
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +3 -1
  69. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -3
  70. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -2
  71. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -2
  73. data/lib/active_record/connection_adapters/sqlserver/utils.rb +2 -0
  74. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -0
  75. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +44 -10
  76. data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -3
  77. data/lib/active_record/sqlserver_base.rb +8 -0
  78. data/lib/active_record/tasks/sqlserver_database_tasks.rb +2 -0
  79. data/lib/activerecord-sqlserver-adapter.rb +2 -0
  80. data/lib/arel/visitors/sqlserver.rb +40 -10
  81. data/lib/arel_sqlserver.rb +2 -0
  82. data/test/appveyor/dbsetup.ps1 +4 -4
  83. data/test/cases/adapter_test_sqlserver.rb +65 -1
  84. data/test/cases/change_column_null_test_sqlserver.rb +2 -0
  85. data/test/cases/coerced_tests.rb +644 -187
  86. data/test/cases/column_test_sqlserver.rb +2 -1
  87. data/test/cases/connection_test_sqlserver.rb +2 -0
  88. data/test/cases/execute_procedure_test_sqlserver.rb +2 -0
  89. data/test/cases/fetch_test_sqlserver.rb +2 -0
  90. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +4 -2
  91. data/test/cases/helper_sqlserver.rb +2 -0
  92. data/test/cases/in_clause_test_sqlserver.rb +36 -0
  93. data/test/cases/index_test_sqlserver.rb +2 -0
  94. data/test/cases/json_test_sqlserver.rb +2 -0
  95. data/test/cases/migration_test_sqlserver.rb +4 -2
  96. data/test/cases/order_test_sqlserver.rb +2 -0
  97. data/test/cases/pessimistic_locking_test_sqlserver.rb +2 -0
  98. data/test/cases/rake_test_sqlserver.rb +2 -0
  99. data/test/cases/schema_dumper_test_sqlserver.rb +3 -1
  100. data/test/cases/schema_test_sqlserver.rb +2 -0
  101. data/test/cases/scratchpad_test_sqlserver.rb +2 -0
  102. data/test/cases/showplan_test_sqlserver.rb +4 -2
  103. data/test/cases/specific_schema_test_sqlserver.rb +2 -0
  104. data/test/cases/transaction_test_sqlserver.rb +2 -1
  105. data/test/cases/trigger_test_sqlserver.rb +2 -1
  106. data/test/cases/utils_test_sqlserver.rb +2 -0
  107. data/test/cases/uuid_test_sqlserver.rb +2 -1
  108. data/test/debug.rb +2 -0
  109. data/test/migrations/create_clients_and_change_column_null.rb +2 -0
  110. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -0
  111. data/test/models/sqlserver/booking.rb +2 -0
  112. data/test/models/sqlserver/customers_view.rb +2 -0
  113. data/test/models/sqlserver/datatype.rb +2 -0
  114. data/test/models/sqlserver/datatype_migration.rb +2 -0
  115. data/test/models/sqlserver/dollar_table_name.rb +2 -0
  116. data/test/models/sqlserver/edge_schema.rb +2 -0
  117. data/test/models/sqlserver/fk_has_fk.rb +2 -0
  118. data/test/models/sqlserver/fk_has_pk.rb +2 -0
  119. data/test/models/sqlserver/natural_pk_data.rb +2 -0
  120. data/test/models/sqlserver/natural_pk_int_data.rb +2 -0
  121. data/test/models/sqlserver/no_pk_data.rb +2 -0
  122. data/test/models/sqlserver/object_default.rb +2 -0
  123. data/test/models/sqlserver/quoted_table.rb +2 -0
  124. data/test/models/sqlserver/quoted_view_1.rb +2 -0
  125. data/test/models/sqlserver/quoted_view_2.rb +2 -0
  126. data/test/models/sqlserver/sst_memory.rb +2 -0
  127. data/test/models/sqlserver/string_default.rb +2 -0
  128. data/test/models/sqlserver/string_defaults_big_view.rb +2 -0
  129. data/test/models/sqlserver/string_defaults_view.rb +2 -0
  130. data/test/models/sqlserver/tinyint_pk.rb +2 -0
  131. data/test/models/sqlserver/trigger.rb +2 -0
  132. data/test/models/sqlserver/trigger_history.rb +2 -0
  133. data/test/models/sqlserver/upper.rb +2 -0
  134. data/test/models/sqlserver/uppered.rb +2 -0
  135. data/test/models/sqlserver/uuid.rb +2 -0
  136. data/test/schema/sqlserver_specific_schema.rb +2 -0
  137. data/test/support/coerceable_test_sqlserver.rb +14 -5
  138. data/test/support/connection_reflection.rb +2 -0
  139. data/test/support/core_ext/query_cache.rb +3 -0
  140. data/test/support/load_schema_sqlserver.rb +2 -0
  141. data/test/support/minitest_sqlserver.rb +2 -0
  142. data/test/support/paths_sqlserver.rb +2 -0
  143. data/test/support/rake_helpers.rb +1 -0
  144. data/test/support/sql_counter_sqlserver.rb +3 -0
  145. data/test/support/test_in_memory_oltp.rb +2 -0
  146. 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(*args)
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'.tap do |type|
18
- type << "(#{limit})" if limit
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(*args)
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
- 'varbinary(max)'.freeze
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(*args)
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'.tap do |type|
18
- type << "(#{limit})" if limit
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(*args)
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
- 'varchar(max)'.freeze
19
+ "varchar(max)"
18
20
  end
19
21
 
20
22
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'strscan'
2
4
 
3
5
  module ActiveRecord
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
@@ -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 <<-SQL.strip_heredoc
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, table_name = nil, default_function = nil, collation = nil, comment = nil, sqlserver_options = {})
6
- @sqlserver_options = sqlserver_options || {}
7
- super(name, default, sql_type_metadata, null, table_name, default_function, collation, comment: comment)
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
  require 'active_record/tasks/database_tasks'
2
4
  require 'shellwords'
3
5
  require 'ipaddr'
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record/connection_adapters/sqlserver_adapter'
@@ -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 << SPACE
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 << SPACE
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 << SPACE if o.left
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 << SPACE
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 << SPACE
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 << SPACE if options[:space]
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 << SPACE
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 << COMMA unless len == i
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
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'arel'
2
4
  require 'arel/visitors/sqlserver'
@@ -5,15 +5,15 @@ Write-Output "Setting up..."
5
5
 
6
6
  Write-Output "Setting variables..."
7
7
  $serverName = $env:COMPUTERNAME
8
- $instances = @('SQL2012SP1', 'SQL2014')
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 ($instance in $instances) {
14
- Write-Output "Instance $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='$instance']/ServerProtocol[@Name='Tcp']"
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-senstive information in the adpaters inspect method' do
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