activerecord-sqlserver-adapter 5.2.1 → 6.0.0

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 (149) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.gitignore +1 -0
  5. data/.rubocop.yml +29 -0
  6. data/.travis.yml +6 -8
  7. data/CHANGELOG.md +38 -24
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +48 -41
  10. data/Guardfile +9 -8
  11. data/README.md +9 -30
  12. data/RUNNING_UNIT_TESTS.md +3 -0
  13. data/Rakefile +14 -16
  14. data/VERSION +1 -1
  15. data/activerecord-sqlserver-adapter.gemspec +25 -14
  16. data/appveyor.yml +24 -17
  17. data/docker-compose.ci.yml +7 -5
  18. data/guides/RELEASING.md +11 -0
  19. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +3 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +8 -7
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +6 -4
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
  29. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
  30. data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
  31. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +46 -8
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +190 -164
  35. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
  36. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
  37. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
  38. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -2
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
  41. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
  42. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
  43. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
  44. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
  45. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +2 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
  47. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
  48. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
  51. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  52. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  54. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  55. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  56. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  58. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  59. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  60. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  61. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  62. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  63. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  64. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  70. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  71. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  73. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  75. data/lib/active_record/connection_adapters/sqlserver/type.rb +37 -35
  76. data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -11
  77. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  78. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +128 -92
  79. data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
  80. data/lib/active_record/sqlserver_base.rb +9 -1
  81. data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
  82. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  83. data/lib/arel/visitors/sqlserver.rb +58 -24
  84. data/lib/arel_sqlserver.rb +4 -2
  85. data/test/appveyor/dbsetup.ps1 +4 -4
  86. data/test/cases/adapter_test_sqlserver.rb +214 -171
  87. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  88. data/test/cases/coerced_tests.rb +631 -356
  89. data/test/cases/column_test_sqlserver.rb +283 -284
  90. data/test/cases/connection_test_sqlserver.rb +17 -20
  91. data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
  92. data/test/cases/fetch_test_sqlserver.rb +16 -22
  93. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  94. data/test/cases/helper_sqlserver.rb +15 -15
  95. data/test/cases/in_clause_test_sqlserver.rb +36 -0
  96. data/test/cases/index_test_sqlserver.rb +15 -15
  97. data/test/cases/json_test_sqlserver.rb +25 -25
  98. data/test/cases/migration_test_sqlserver.rb +25 -29
  99. data/test/cases/order_test_sqlserver.rb +53 -54
  100. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  101. data/test/cases/rake_test_sqlserver.rb +33 -45
  102. data/test/cases/schema_dumper_test_sqlserver.rb +107 -109
  103. data/test/cases/schema_test_sqlserver.rb +20 -26
  104. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  105. data/test/cases/showplan_test_sqlserver.rb +28 -35
  106. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  107. data/test/cases/transaction_test_sqlserver.rb +18 -20
  108. data/test/cases/trigger_test_sqlserver.rb +14 -13
  109. data/test/cases/utils_test_sqlserver.rb +70 -70
  110. data/test/cases/uuid_test_sqlserver.rb +13 -14
  111. data/test/debug.rb +8 -6
  112. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  113. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  114. data/test/models/sqlserver/booking.rb +3 -1
  115. data/test/models/sqlserver/customers_view.rb +3 -1
  116. data/test/models/sqlserver/datatype.rb +2 -0
  117. data/test/models/sqlserver/datatype_migration.rb +2 -0
  118. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  119. data/test/models/sqlserver/edge_schema.rb +3 -3
  120. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  121. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  122. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  123. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  124. data/test/models/sqlserver/no_pk_data.rb +3 -1
  125. data/test/models/sqlserver/object_default.rb +3 -1
  126. data/test/models/sqlserver/quoted_table.rb +4 -2
  127. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  128. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  129. data/test/models/sqlserver/sst_memory.rb +3 -1
  130. data/test/models/sqlserver/string_default.rb +3 -1
  131. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  132. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  133. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  134. data/test/models/sqlserver/trigger.rb +4 -2
  135. data/test/models/sqlserver/trigger_history.rb +3 -1
  136. data/test/models/sqlserver/upper.rb +3 -1
  137. data/test/models/sqlserver/uppered.rb +3 -1
  138. data/test/models/sqlserver/uuid.rb +3 -1
  139. data/test/schema/sqlserver_specific_schema.rb +22 -22
  140. data/test/support/coerceable_test_sqlserver.rb +15 -9
  141. data/test/support/connection_reflection.rb +3 -2
  142. data/test/support/core_ext/query_cache.rb +4 -1
  143. data/test/support/load_schema_sqlserver.rb +5 -5
  144. data/test/support/minitest_sqlserver.rb +3 -1
  145. data/test/support/paths_sqlserver.rb +11 -11
  146. data/test/support/rake_helpers.rb +13 -10
  147. data/test/support/sql_counter_sqlserver.rb +3 -4
  148. data/test/support/test_in_memory_oltp.rb +9 -7
  149. metadata +17 -7
@@ -1,20 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Arel
2
4
  module Visitors
3
5
  class SQLServer < Arel::Visitors::ToSql
4
-
5
6
  OFFSET = " OFFSET "
6
7
  ROWS = " ROWS"
7
8
  FETCH = " FETCH NEXT "
8
9
  FETCH0 = " FETCH FIRST (SELECT 0) "
9
10
  ROWS_ONLY = " ROWS ONLY"
10
11
 
11
-
12
12
  private
13
13
 
14
14
  # SQLServer ToSql/Visitor (Overides)
15
15
 
16
16
  def visit_Arel_Nodes_BindParam o, collector
17
- collector.add_bind(o.value) { |i| "@#{i-1}" }
17
+ collector.add_bind(o.value) { |i| "@#{i - 1}" }
18
18
  end
19
19
 
20
20
  def visit_Arel_Nodes_Bin o, collector
@@ -22,6 +22,12 @@ module Arel
22
22
  collector << " #{ActiveRecord::ConnectionAdapters::SQLServerAdapter.cs_equality_operator} "
23
23
  end
24
24
 
25
+ def visit_Arel_Nodes_Concat(o, collector)
26
+ visit o.left, collector
27
+ collector << " + "
28
+ visit o.right, collector
29
+ end
30
+
25
31
  def visit_Arel_Nodes_UpdateStatement(o, a)
26
32
  if o.orders.any? && o.limit.nil?
27
33
  o.limit = Nodes::Limit.new(9_223_372_036_854_775_807)
@@ -30,8 +36,8 @@ module Arel
30
36
  end
31
37
 
32
38
  def visit_Arel_Nodes_Lock o, collector
33
- o.expr = Arel.sql('WITH(UPDLOCK)') if o.expr.to_s =~ /FOR UPDATE/
34
- collector << SPACE
39
+ o.expr = Arel.sql("WITH(UPDLOCK)") if o.expr.to_s =~ /FOR UPDATE/
40
+ collector << " "
35
41
  visit o.expr, collector
36
42
  end
37
43
 
@@ -52,14 +58,19 @@ module Arel
52
58
  end
53
59
  end
54
60
 
61
+ def visit_Arel_Nodes_Grouping(o, collector)
62
+ remove_invalid_ordering_from_select_statement(o.expr)
63
+ super
64
+ end
65
+
55
66
  def visit_Arel_Nodes_SelectStatement o, collector
56
67
  @select_statement = o
57
68
  distinct_One_As_One_Is_So_Not_Fetch o
58
69
  if o.with
59
70
  collector = visit o.with, collector
60
- collector << SPACE
71
+ collector << " "
61
72
  end
62
- collector = o.cores.inject(collector) { |c,x|
73
+ collector = o.cores.inject(collector) { |c, x|
63
74
  visit_Arel_Nodes_SelectCore(x, c)
64
75
  }
65
76
  collector = visit_Orders_And_Let_Fetch_Happen o, collector
@@ -73,15 +84,17 @@ module Arel
73
84
  # Apparently, o.engine.connection can actually be a different adapter
74
85
  # than sqlserver. Can be removed if fixed in ActiveRecord. See:
75
86
  # github.com/rails-sqlserver/activerecord-sqlserver-adapter/issues/450
76
- table_name = begin
77
- if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server?
78
- remote_server_table_name(o)
79
- else
87
+ table_name =
88
+ begin
89
+ if o.class.engine.connection.respond_to?(:sqlserver?) && o.class.engine.connection.database_prefix_remote_server?
90
+ remote_server_table_name(o)
91
+ else
92
+ quote_table_name(o.name)
93
+ end
94
+ rescue Exception
80
95
  quote_table_name(o.name)
81
96
  end
82
- rescue Exception => e
83
- quote_table_name(o.name)
84
- end
97
+
85
98
  if o.table_alias
86
99
  collector << "#{table_name} #{quote_table_name o.table_alias}"
87
100
  else
@@ -95,8 +108,8 @@ 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
99
- collector = inject_join o.right, collector, ' '
111
+ collector << " " if o.left
112
+ collector = inject_join o.right, collector, " "
100
113
  end
101
114
  collector
102
115
  end
@@ -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
@@ -156,6 +178,7 @@ module Arel
156
178
 
157
179
  def node_value(node)
158
180
  return nil unless node
181
+
159
182
  case node.expr
160
183
  when NilClass then nil
161
184
  when Numeric then node.expr
@@ -169,9 +192,11 @@ module Arel
169
192
 
170
193
  def make_Fetch_Possible_And_Deterministic o
171
194
  return if o.limit.nil? && o.offset.nil?
195
+
172
196
  t = table_From_Statement o
173
197
  pk = primary_Key_From_Table t
174
198
  return unless pk
199
+
175
200
  if o.orders.empty?
176
201
  # Prefer deterministic vs a simple `(SELECT NULL)` expr.
177
202
  o.orders = [pk.asc]
@@ -196,14 +221,15 @@ module Arel
196
221
  elsif Arel::Nodes::SqlLiteral === core.from
197
222
  Arel::Table.new(core.from)
198
223
  elsif Arel::Nodes::JoinSource === core.source
199
- Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left
224
+ Arel::Nodes::SqlLiteral === core.source.left ? Arel::Table.new(core.source.left, @engine) : core.source.left.left
200
225
  end
201
226
  end
202
227
 
203
228
  def primary_Key_From_Table t
204
229
  return unless t
230
+
205
231
  column_name = @connection.schema_cache.primary_keys(t.name) ||
206
- @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
232
+ @connection.schema_cache.columns_hash(t.name).first.try(:second).try(:name)
207
233
  column_name ? t[column_name] : nil
208
234
  end
209
235
 
@@ -213,6 +239,14 @@ module Arel
213
239
  ).quoted
214
240
  end
215
241
 
242
+ # Need to remove ordering from subqueries unless TOP/OFFSET also used. Otherwise, SQLServer
243
+ # returns error "The ORDER BY clause is invalid in views, inline functions, derived tables,
244
+ # subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified."
245
+ def remove_invalid_ordering_from_select_statement(node)
246
+ return unless Arel::Nodes::SelectStatement === node
247
+
248
+ node.orders = [] unless node.offset || node.limit
249
+ end
216
250
  end
217
251
  end
218
252
  end
@@ -1,2 +1,4 @@
1
- require 'arel'
2
- require 'arel/visitors/sqlserver'
1
+ # frozen_string_literal: true
2
+
3
+ require "arel"
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) {