activerecord-sqlserver-adapter 6.0.0.rc1 → 6.0.0.rc2

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 (134) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +29 -0
  4. data/CHANGELOG.md +20 -0
  5. data/Gemfile +11 -5
  6. data/Guardfile +9 -8
  7. data/Rakefile +12 -16
  8. data/VERSION +1 -1
  9. data/activerecord-sqlserver-adapter.gemspec +3 -3
  10. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +0 -4
  11. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -4
  12. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +3 -4
  13. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +1 -3
  14. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +2 -3
  15. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +2 -3
  16. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +35 -32
  17. data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +7 -12
  18. data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -3
  19. data/lib/active_record/connection_adapters/sqlserver/quoting.rb +8 -8
  20. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +0 -2
  21. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +7 -7
  22. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +106 -103
  23. data/lib/active_record/connection_adapters/sqlserver/showplan.rb +6 -8
  24. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
  25. data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
  26. data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +0 -2
  27. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +1 -4
  28. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +4 -8
  29. data/lib/active_record/connection_adapters/sqlserver/type.rb +35 -35
  30. data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +0 -2
  31. data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +0 -2
  32. data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +0 -2
  33. data/lib/active_record/connection_adapters/sqlserver/type/char.rb +2 -2
  34. data/lib/active_record/connection_adapters/sqlserver/type/data.rb +0 -2
  35. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +2 -3
  36. data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +2 -3
  37. data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +0 -2
  38. data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +0 -2
  39. data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +0 -2
  40. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +0 -2
  41. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +0 -2
  42. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +0 -1
  43. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +0 -2
  44. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +0 -2
  45. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +0 -2
  46. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +0 -2
  47. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +0 -2
  48. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +0 -2
  49. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +0 -2
  50. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +2 -3
  51. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +6 -9
  52. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +0 -2
  53. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +0 -2
  54. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +1 -3
  55. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +0 -2
  56. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +0 -2
  57. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +0 -2
  58. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +0 -2
  59. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +1 -2
  60. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +1 -3
  61. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +0 -2
  62. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +1 -3
  63. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +0 -2
  64. data/lib/active_record/connection_adapters/sqlserver/utils.rb +8 -11
  65. data/lib/active_record/connection_adapters/sqlserver/version.rb +0 -2
  66. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +85 -83
  67. data/lib/active_record/connection_adapters/sqlserver_column.rb +0 -2
  68. data/lib/active_record/sqlserver_base.rb +1 -1
  69. data/lib/active_record/tasks/sqlserver_database_tasks.rb +26 -32
  70. data/lib/activerecord-sqlserver-adapter.rb +1 -1
  71. data/lib/arel/visitors/sqlserver.rb +18 -14
  72. data/lib/arel_sqlserver.rb +2 -2
  73. data/test/cases/adapter_test_sqlserver.rb +161 -182
  74. data/test/cases/change_column_null_test_sqlserver.rb +12 -12
  75. data/test/cases/coerced_tests.rb +88 -270
  76. data/test/cases/column_test_sqlserver.rb +281 -283
  77. data/test/cases/connection_test_sqlserver.rb +15 -20
  78. data/test/cases/execute_procedure_test_sqlserver.rb +18 -20
  79. data/test/cases/fetch_test_sqlserver.rb +14 -22
  80. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +12 -18
  81. data/test/cases/helper_sqlserver.rb +13 -15
  82. data/test/cases/in_clause_test_sqlserver.rb +9 -9
  83. data/test/cases/index_test_sqlserver.rb +13 -15
  84. data/test/cases/json_test_sqlserver.rb +23 -25
  85. data/test/cases/migration_test_sqlserver.rb +22 -28
  86. data/test/cases/order_test_sqlserver.rb +51 -54
  87. data/test/cases/pessimistic_locking_test_sqlserver.rb +25 -33
  88. data/test/cases/rake_test_sqlserver.rb +31 -45
  89. data/test/cases/schema_dumper_test_sqlserver.rb +104 -108
  90. data/test/cases/schema_test_sqlserver.rb +18 -26
  91. data/test/cases/scratchpad_test_sqlserver.rb +2 -4
  92. data/test/cases/showplan_test_sqlserver.rb +24 -33
  93. data/test/cases/specific_schema_test_sqlserver.rb +66 -65
  94. data/test/cases/transaction_test_sqlserver.rb +16 -19
  95. data/test/cases/trigger_test_sqlserver.rb +12 -12
  96. data/test/cases/utils_test_sqlserver.rb +68 -70
  97. data/test/cases/uuid_test_sqlserver.rb +11 -13
  98. data/test/debug.rb +6 -6
  99. data/test/migrations/create_clients_and_change_column_null.rb +1 -1
  100. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +2 -4
  101. data/test/models/sqlserver/booking.rb +1 -1
  102. data/test/models/sqlserver/customers_view.rb +1 -1
  103. data/test/models/sqlserver/dollar_table_name.rb +1 -1
  104. data/test/models/sqlserver/edge_schema.rb +1 -3
  105. data/test/models/sqlserver/fk_has_fk.rb +1 -1
  106. data/test/models/sqlserver/fk_has_pk.rb +1 -1
  107. data/test/models/sqlserver/natural_pk_data.rb +2 -2
  108. data/test/models/sqlserver/natural_pk_int_data.rb +1 -1
  109. data/test/models/sqlserver/no_pk_data.rb +1 -1
  110. data/test/models/sqlserver/object_default.rb +1 -1
  111. data/test/models/sqlserver/quoted_table.rb +2 -2
  112. data/test/models/sqlserver/quoted_view_1.rb +1 -1
  113. data/test/models/sqlserver/quoted_view_2.rb +1 -1
  114. data/test/models/sqlserver/sst_memory.rb +1 -1
  115. data/test/models/sqlserver/string_default.rb +1 -1
  116. data/test/models/sqlserver/string_defaults_big_view.rb +1 -1
  117. data/test/models/sqlserver/string_defaults_view.rb +1 -1
  118. data/test/models/sqlserver/tinyint_pk.rb +1 -1
  119. data/test/models/sqlserver/trigger.rb +2 -2
  120. data/test/models/sqlserver/trigger_history.rb +1 -1
  121. data/test/models/sqlserver/upper.rb +1 -1
  122. data/test/models/sqlserver/uppered.rb +1 -1
  123. data/test/models/sqlserver/uuid.rb +1 -1
  124. data/test/schema/sqlserver_specific_schema.rb +20 -22
  125. data/test/support/coerceable_test_sqlserver.rb +1 -4
  126. data/test/support/connection_reflection.rb +1 -2
  127. data/test/support/core_ext/query_cache.rb +1 -1
  128. data/test/support/load_schema_sqlserver.rb +3 -5
  129. data/test/support/minitest_sqlserver.rb +1 -1
  130. data/test/support/paths_sqlserver.rb +9 -11
  131. data/test/support/rake_helpers.rb +12 -10
  132. data/test/support/sql_counter_sqlserver.rb +0 -4
  133. data/test/support/test_in_memory_oltp.rb +7 -7
  134. metadata +5 -4
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module DatabaseTasks
7
-
8
7
  def create_database(database, options = {})
9
8
  name = SQLServer::Utils.extract_identifiers(database)
10
9
  db_options = create_database_options(options)
@@ -19,7 +18,7 @@ module ActiveRecord
19
18
  end
20
19
 
21
20
  def current_database
22
- select_value 'SELECT DB_NAME()'
21
+ select_value "SELECT DB_NAME()"
23
22
  end
24
23
 
25
24
  def charset
@@ -32,20 +31,20 @@ module ActiveRecord
32
31
 
33
32
  private
34
33
 
35
- def create_database_options(options={})
34
+ def create_database_options(options = {})
36
35
  keys = [:collate]
37
36
  copts = @connection_options
38
37
  options = {
39
38
  collate: copts[:collation]
40
39
  }.merge(options.symbolize_keys).select { |_, v|
41
40
  v.present?
42
- }.slice(*keys).map { |k,v|
41
+ }.slice(*keys).map { |k, v|
43
42
  "#{k.to_s.upcase} #{v}"
44
- }.join(' ')
43
+ }.join(" ")
45
44
  options
46
45
  end
47
46
 
48
- def create_database_edition_options(options={})
47
+ def create_database_edition_options(options = {})
49
48
  keys = [:maxsize, :edition, :service_objective]
50
49
  copts = @connection_options
51
50
  edition_options = {
@@ -54,17 +53,13 @@ module ActiveRecord
54
53
  service_objective: copts[:azure_service_objective]
55
54
  }.merge(options.symbolize_keys).select { |_, v|
56
55
  v.present?
57
- }.slice(*keys).map { |k,v|
56
+ }.slice(*keys).map { |k, v|
58
57
  "#{k.to_s.upcase} = #{v}"
59
- }.join(', ')
58
+ }.join(", ")
60
59
  edition_options = "( #{edition_options} )" if edition_options.present?
61
60
  edition_options
62
61
  end
63
-
64
62
  end
65
63
  end
66
64
  end
67
65
  end
68
-
69
-
70
-
@@ -1,9 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
-
5
4
  class DeadlockVictim < WrappedDatabaseException
6
5
  end
7
-
8
-
9
6
  end
@@ -4,10 +4,9 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module Quoting
7
-
8
- QUOTED_TRUE = '1'.freeze
9
- QUOTED_FALSE = '0'.freeze
10
- QUOTED_STRING_PREFIX = 'N'.freeze
7
+ QUOTED_TRUE = "1".freeze
8
+ QUOTED_FALSE = "0".freeze
9
+ QUOTED_STRING_PREFIX = "N".freeze
11
10
 
12
11
  def fetch_type_metadata(sql_type, sqlserver_options = {})
13
12
  cast_type = lookup_cast_type(sql_type)
@@ -63,10 +62,12 @@ module ActiveRecord
63
62
  end
64
63
 
65
64
  def quoted_date(value)
66
- if value.acts_like?(:date)
67
- Type::Date.new.serialize(value)
68
- else value.acts_like?(:time)
65
+ if value.acts_like?(:time)
69
66
  Type::DateTime.new.serialize(value)
67
+ elsif value.acts_like?(:date)
68
+ Type::Date.new.serialize(value)
69
+ else
70
+ value
70
71
  end
71
72
  end
72
73
 
@@ -130,7 +131,6 @@ module ActiveRecord
130
131
  super
131
132
  end
132
133
  end
133
-
134
134
  end
135
135
  end
136
136
  end
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  class SchemaCreation < AbstractAdapter::SchemaCreation
7
-
8
7
  private
9
8
 
10
9
  def visit_TableDefinition(o)
@@ -63,7 +62,6 @@ module ActiveRecord
63
62
  def options_primary_key_with_nil_default?(options)
64
63
  options[:primary_key] && options.include?(:default) && options[:default].nil?
65
64
  end
66
-
67
65
  end
68
66
  end
69
67
  end
@@ -4,13 +4,12 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  class SchemaDumper < ConnectionAdapters::SchemaDumper
7
-
8
7
  SQLSEVER_NO_LIMIT_TYPES = [
9
- 'text',
10
- 'ntext',
11
- 'varchar(max)',
12
- 'nvarchar(max)',
13
- 'varbinary(max)'
8
+ "text",
9
+ "ntext",
10
+ "varchar(max)",
11
+ "nvarchar(max)",
12
+ "varbinary(max)"
14
13
  ].freeze
15
14
 
16
15
  private
@@ -21,18 +20,19 @@ module ActiveRecord
21
20
 
22
21
  def schema_limit(column)
23
22
  return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type)
23
+
24
24
  super
25
25
  end
26
26
 
27
27
  def schema_collation(column)
28
28
  return unless column.collation
29
+
29
30
  column.collation if column.collation != @connection.collation
30
31
  end
31
32
 
32
33
  def default_primary_key?(column)
33
34
  super && column.is_primary? && column.is_identity?
34
35
  end
35
-
36
36
  end
37
37
  end
38
38
  end
@@ -4,7 +4,6 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
  module SQLServer
6
6
  module SchemaStatements
7
-
8
7
  def native_database_types
9
8
  @native_database_types ||= initialize_native_database_types.freeze
10
9
  end
@@ -19,11 +18,11 @@ module ActiveRecord
19
18
  # Mimic CASCADE option as best we can.
20
19
  if options[:force] == :cascade
21
20
  execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
22
- fktable = fkdata['FKTABLE_NAME']
23
- fkcolmn = fkdata['FKCOLUMN_NAME']
24
- pktable = fkdata['PKTABLE_NAME']
25
- pkcolmn = fkdata['PKCOLUMN_NAME']
26
- remove_foreign_key fktable, name: fkdata['FK_NAME']
21
+ fktable = fkdata["FKTABLE_NAME"]
22
+ fkcolmn = fkdata["FKCOLUMN_NAME"]
23
+ pktable = fkdata["PKTABLE_NAME"]
24
+ pkcolmn = fkdata["PKCOLUMN_NAME"]
25
+ remove_foreign_key fktable, name: fkdata["FK_NAME"]
27
26
  do_execute "DELETE FROM #{quote_table_name(fktable)} WHERE #{quote_column_name(fkcolmn)} IN ( SELECT #{quote_column_name(pkcolmn)} FROM #{quote_table_name(pktable)} )"
28
27
  end
29
28
  end
@@ -49,11 +48,11 @@ module ActiveRecord
49
48
  orders = {}
50
49
  columns = []
51
50
 
52
- index[:index_keys].split(',').each do |column|
51
+ index[:index_keys].split(",").each do |column|
53
52
  column.strip!
54
53
 
55
- if column.ends_with?('(-)')
56
- column.gsub! '(-)', ''
54
+ if column.ends_with?("(-)")
55
+ column.gsub! "(-)", ""
57
56
  orders[column] = :desc
58
57
  end
59
58
 
@@ -67,6 +66,7 @@ module ActiveRecord
67
66
 
68
67
  def columns(table_name)
69
68
  return [] if table_name.blank?
69
+
70
70
  column_definitions(table_name).map do |ci|
71
71
  sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name
72
72
  sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options
@@ -105,7 +105,7 @@ module ActiveRecord
105
105
  identifier = database_prefix_identifier(table_name)
106
106
  database = identifier.fully_qualified_database_quoted
107
107
  sql = %{
108
- SELECT KCU.COLUMN_NAME AS [name]
108
+ SELECT #{lowercase_schema_reflection_sql('KCU.COLUMN_NAME')} AS [name]
109
109
  FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
110
110
  LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
111
111
  ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
@@ -117,12 +117,12 @@ module ActiveRecord
117
117
  AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))}
118
118
  AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
119
119
  ORDER BY KCU.ORDINAL_POSITION ASC
120
- }.gsub(/[[:space:]]/, ' ')
120
+ }.gsub(/[[:space:]]/, " ")
121
121
  binds = []
122
122
  nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
123
- binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
124
- binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
125
- sp_executesql(sql, 'SCHEMA', binds).map { |r| r['name'] }
123
+ binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
124
+ binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
125
+ sp_executesql(sql, "SCHEMA", binds).map { |r| r["name"] }
126
126
  end
127
127
 
128
128
  def rename_table(table_name, new_name)
@@ -131,7 +131,8 @@ module ActiveRecord
131
131
  end
132
132
 
133
133
  def remove_column(table_name, column_name, type = nil, options = {})
134
- raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
134
+ raise ArgumentError.new("You must specify at least one column name. Example: remove_column(:people, :first_name)") if column_name.is_a? Array
135
+
135
136
  remove_check_constraints(table_name, column_name)
136
137
  remove_default_constraint(table_name, column_name)
137
138
  remove_indexes(table_name, column_name)
@@ -144,10 +145,10 @@ module ActiveRecord
144
145
  column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
145
146
  without_constraints = options.key?(:default) || options.key?(:limit)
146
147
  default = if !options.key?(:default) && column_object
147
- column_object.default
148
- else
149
- options[:default]
150
- end
148
+ column_object.default
149
+ else
150
+ options[:default]
151
+ end
151
152
  if without_constraints || (column_object && column_object.type != type.to_sym)
152
153
  remove_default_constraint(table_name, column_name)
153
154
  indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
@@ -155,7 +156,7 @@ module ActiveRecord
155
156
  end
156
157
  sql_commands << "UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_expression(options[:default], column_object)} WHERE #{quote_column_name(column_name)} IS NULL" if !options[:null].nil? && options[:null] == false && !options[:default].nil?
157
158
  alter_command = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, limit: options[:limit], precision: options[:precision], scale: options[:scale])}"
158
- alter_command += ' NOT NULL' if !options[:null].nil? && options[:null] == false
159
+ alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
159
160
  sql_commands << alter_command
160
161
  if without_constraints
161
162
  default = quote_default_expression(default, column_object || column_for(table_name, column_name))
@@ -173,6 +174,7 @@ module ActiveRecord
173
174
  clear_cache!
174
175
  column = column_for(table_name, column_name)
175
176
  return unless column
177
+
176
178
  remove_default_constraint(table_name, column_name)
177
179
  default = extract_new_default_value(default_or_changes)
178
180
  do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote_default_expression(default, column)} FOR #{quote_column_name(column_name)}"
@@ -182,15 +184,16 @@ module ActiveRecord
182
184
  def rename_column(table_name, column_name, new_column_name)
183
185
  clear_cache!
184
186
  identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}")
185
- execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN'
187
+ execute_procedure :sp_rename, identifier.quoted, new_column_name, "COLUMN"
186
188
  rename_column_indexes(table_name, column_name, new_column_name)
187
189
  clear_cache!
188
190
  end
189
191
 
190
192
  def rename_index(table_name, old_name, new_name)
191
193
  raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" if new_name.length > allowed_index_name_length
194
+
192
195
  identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}")
193
- execute_procedure :sp_rename, identifier.quoted, new_name, 'INDEX'
196
+ execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX"
194
197
  end
195
198
 
196
199
  def remove_index!(table_name, index_name)
@@ -202,13 +205,13 @@ module ActiveRecord
202
205
  fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema
203
206
  fk_info.map do |row|
204
207
  from_table = identifier.object
205
- to_table = row['PKTABLE_NAME']
208
+ to_table = row["PKTABLE_NAME"]
206
209
  options = {
207
- name: row['FK_NAME'],
208
- column: row['FKCOLUMN_NAME'],
209
- primary_key: row['PKCOLUMN_NAME'],
210
- on_update: extract_foreign_key_action('update', row['FK_NAME']),
211
- on_delete: extract_foreign_key_action('delete', row['FK_NAME'])
210
+ name: row["FK_NAME"],
211
+ column: row["FKCOLUMN_NAME"],
212
+ primary_key: row["PKCOLUMN_NAME"],
213
+ on_update: extract_foreign_key_action("update", row["FK_NAME"]),
214
+ on_delete: extract_foreign_key_action("delete", row["FK_NAME"])
212
215
  }
213
216
  ForeignKeyDefinition.new from_table, to_table, options
214
217
  end
@@ -216,8 +219,8 @@ module ActiveRecord
216
219
 
217
220
  def extract_foreign_key_action(action, fk_name)
218
221
  case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'")
219
- when 'CASCADE' then :cascade
220
- when 'SET_NULL' then :nullify
222
+ when "CASCADE" then :cascade
223
+ when "SET_NULL" then :nullify
221
224
  end
222
225
  end
223
226
 
@@ -225,15 +228,15 @@ module ActiveRecord
225
228
  type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
226
229
  limit = nil unless type_limitable
227
230
  case type.to_s
228
- when 'integer'
231
+ when "integer"
229
232
  case limit
230
- when 1 then 'tinyint'
231
- when 2 then 'smallint'
232
- when 3..4, nil then 'integer'
233
- when 5..8 then 'bigint'
233
+ when 1 then "tinyint"
234
+ when 2 then "smallint"
235
+ when 3..4, nil then "integer"
236
+ when 5..8 then "bigint"
234
237
  else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
235
238
  end
236
- when 'datetime2'
239
+ when "datetime2"
237
240
  column_type_sql = super
238
241
  if precision
239
242
  if (0..7) === precision
@@ -249,11 +252,11 @@ module ActiveRecord
249
252
  end
250
253
 
251
254
  def columns_for_distinct(columns, orders)
252
- order_columns = orders.reject(&:blank?).map{ |s|
253
- s = s.to_sql unless s.is_a?(String)
254
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
255
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
256
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
255
+ order_columns = orders.reject(&:blank?).map { |s|
256
+ s = s.to_sql unless s.is_a?(String)
257
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
258
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
259
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
257
260
 
258
261
  (order_columns << super).join(", ")
259
262
  end
@@ -270,7 +273,7 @@ module ActiveRecord
270
273
  do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
271
274
  end
272
275
  sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
273
- sql += ' NOT NULL' if !allow_null.nil? && allow_null == false
276
+ sql += " NOT NULL" if !allow_null.nil? && allow_null == false
274
277
  do_execute sql
275
278
  end
276
279
 
@@ -282,10 +285,10 @@ module ActiveRecord
282
285
 
283
286
  def data_source_sql(name = nil, type: nil)
284
287
  scope = quoted_scope name, type: type
285
- table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
288
+ table_name = lowercase_schema_reflection_sql "TABLE_NAME"
286
289
  sql = "SELECT #{table_name}"
287
- sql += ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)'
288
- sql += ' WHERE TABLE_CATALOG = DB_NAME()'
290
+ sql += " FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
291
+ sql += " WHERE TABLE_CATALOG = DB_NAME()"
289
292
  sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
290
293
  sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
291
294
  sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
@@ -296,7 +299,7 @@ module ActiveRecord
296
299
  def quoted_scope(name = nil, type: nil)
297
300
  identifier = SQLServer::Utils.extract_identifiers(name)
298
301
  {}.tap do |scope|
299
- scope[:schema] = identifier.schema || 'dbo'
302
+ scope[:schema] = identifier.schema || "dbo"
300
303
  scope[:name] = identifier.object if identifier.object
301
304
  scope[:type] = type if type
302
305
  end
@@ -306,37 +309,37 @@ module ActiveRecord
306
309
 
307
310
  def initialize_native_database_types
308
311
  {
309
- primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
310
- primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
311
- integer: { name: 'int', limit: 4 },
312
- bigint: { name: 'bigint' },
313
- boolean: { name: 'bit' },
314
- decimal: { name: 'decimal' },
315
- money: { name: 'money' },
316
- smallmoney: { name: 'smallmoney' },
317
- float: { name: 'float' },
318
- real: { name: 'real' },
319
- date: { name: 'date' },
320
- datetime: { name: 'datetime' },
321
- datetime2: { name: 'datetime2' },
322
- datetimeoffset: { name: 'datetimeoffset' },
323
- smalldatetime: { name: 'smalldatetime' },
324
- timestamp: { name: 'datetime' },
325
- time: { name: 'time' },
326
- char: { name: 'char' },
327
- varchar: { name: 'varchar', limit: 8000 },
328
- varchar_max: { name: 'varchar(max)' },
329
- text_basic: { name: 'text' },
330
- nchar: { name: 'nchar' },
331
- string: { name: 'nvarchar', limit: 4000 },
332
- text: { name: 'nvarchar(max)' },
333
- ntext: { name: 'ntext' },
334
- binary_basic: { name: 'binary' },
335
- varbinary: { name: 'varbinary', limit: 8000 },
336
- binary: { name: 'varbinary(max)' },
337
- uuid: { name: 'uniqueidentifier' },
338
- ss_timestamp: { name: 'timestamp' },
339
- json: { name: 'nvarchar(max)' }
312
+ primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY",
313
+ primary_key_nonclustered: "int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED",
314
+ integer: { name: "int", limit: 4 },
315
+ bigint: { name: "bigint" },
316
+ boolean: { name: "bit" },
317
+ decimal: { name: "decimal" },
318
+ money: { name: "money" },
319
+ smallmoney: { name: "smallmoney" },
320
+ float: { name: "float" },
321
+ real: { name: "real" },
322
+ date: { name: "date" },
323
+ datetime: { name: "datetime" },
324
+ datetime2: { name: "datetime2" },
325
+ datetimeoffset: { name: "datetimeoffset" },
326
+ smalldatetime: { name: "smalldatetime" },
327
+ timestamp: { name: "datetime" },
328
+ time: { name: "time" },
329
+ char: { name: "char" },
330
+ varchar: { name: "varchar", limit: 8000 },
331
+ varchar_max: { name: "varchar(max)" },
332
+ text_basic: { name: "text" },
333
+ nchar: { name: "nchar" },
334
+ string: { name: "nvarchar", limit: 4000 },
335
+ text: { name: "nvarchar(max)" },
336
+ ntext: { name: "ntext" },
337
+ binary_basic: { name: "binary" },
338
+ varbinary: { name: "varbinary", limit: 8000 },
339
+ binary: { name: "varbinary(max)" },
340
+ uuid: { name: "uniqueidentifier" },
341
+ ss_timestamp: { name: "timestamp" },
342
+ json: { name: "nvarchar(max)" }
340
343
  }
341
344
  end
342
345
 
@@ -350,9 +353,9 @@ module ActiveRecord
350
353
 
351
354
  binds = []
352
355
  nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
353
- binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
354
- binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
355
- results = sp_executesql(sql, 'SCHEMA', binds)
356
+ binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
357
+ binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
358
+ results = sp_executesql(sql, "SCHEMA", binds)
356
359
  results.map do |ci|
357
360
  ci = ci.symbolize_keys
358
361
  ci[:_type] = ci[:type]
@@ -383,7 +386,7 @@ module ActiveRecord
383
386
  WHERE
384
387
  c.TABLE_NAME = '#{view_tblnm}'
385
388
  AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
386
- }.squish, 'SCHEMA'
389
+ }.squish, "SCHEMA"
387
390
  end
388
391
  case default
389
392
  when nil
@@ -402,7 +405,7 @@ module ActiveRecord
402
405
  else ci[:type]
403
406
  end
404
407
  value = default.match(/\A\((.*)\)\Z/m)[1]
405
- value = select_value("SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA')
408
+ value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
406
409
  [value, nil]
407
410
  end
408
411
  end
@@ -415,11 +418,11 @@ module ActiveRecord
415
418
  end
416
419
 
417
420
  def column_definitions_sql(database, identifier)
418
- object_name = prepared_statements ? '@0' : quote(identifier.object)
419
- schema_name = if identifier.schema.blank?
420
- 'schema_name()'
421
+ object_name = prepared_statements ? "@0" : quote(identifier.object)
422
+ schema_name = if identifier.schema.blank?
423
+ "schema_name()"
421
424
  else
422
- prepared_statements ? '@1' : quote(identifier.schema)
425
+ prepared_statements ? "@1" : quote(identifier.schema)
423
426
  end
424
427
 
425
428
  %{
@@ -478,11 +481,11 @@ module ActiveRecord
478
481
  AND s.name = #{schema_name}
479
482
  ORDER BY
480
483
  c.column_id
481
- }.gsub(/[ \t\r\n]+/, ' ').strip
484
+ }.gsub(/[ \t\r\n]+/, " ").strip
482
485
  end
483
486
 
484
487
  def remove_check_constraints(table_name, column_name)
485
- constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", 'SCHEMA'
488
+ constraints = select_values "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'", "SCHEMA"
486
489
  constraints.each do |constraint|
487
490
  do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
488
491
  end
@@ -490,8 +493,8 @@ module ActiveRecord
490
493
 
491
494
  def remove_default_constraint(table_name, column_name)
492
495
  # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
493
- execute_procedure(:sp_helpconstraint, table_name, 'nomsg').flatten.select do |row|
494
- row['constraint_type'] == "DEFAULT on column #{column_name}"
496
+ execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row|
497
+ row["constraint_type"] == "DEFAULT on column #{column_name}"
495
498
  end.each do |row|
496
499
  do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
497
500
  end
@@ -507,12 +510,12 @@ module ActiveRecord
507
510
 
508
511
  def get_table_name(sql)
509
512
  tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
510
- Regexp.last_match[3] || Regexp.last_match[4]
511
- elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
512
- Regexp.last_match[1]
513
- else
514
- nil
515
- end
513
+ Regexp.last_match[3] || Regexp.last_match[4]
514
+ elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
515
+ Regexp.last_match[1]
516
+ else
517
+ nil
518
+ end
516
519
  SQLServer::Utils.extract_identifiers(tn).object
517
520
  end
518
521
 
@@ -528,22 +531,22 @@ module ActiveRecord
528
531
 
529
532
  def view_table_name(table_name)
530
533
  view_info = view_information(table_name)
531
- view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
534
+ view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
532
535
  end
533
536
 
534
537
  def view_information(table_name)
535
538
  @view_information ||= {}
536
539
  @view_information[table_name] ||= begin
537
540
  identifier = SQLServer::Utils.extract_identifiers(table_name)
538
- view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
541
+ view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA"
539
542
  if view_info
540
543
  view_info = view_info.with_indifferent_access
541
544
  if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
542
545
  view_info[:VIEW_DEFINITION] = begin
543
- select_values("EXEC sp_helptext #{identifier.object_quoted}", 'SCHEMA').join
544
- rescue
545
- warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
546
- nil
546
+ select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
547
+ rescue
548
+ warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
549
+ nil
547
550
  end
548
551
  end
549
552
  end
@@ -554,6 +557,7 @@ module ActiveRecord
554
557
  def views_real_column_name(table_name, column_name)
555
558
  view_definition = view_information(table_name)[:VIEW_DEFINITION]
556
559
  return column_name unless view_definition
560
+
557
561
  match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
558
562
  match_data ? match_data[1] : column_name
559
563
  end
@@ -561,7 +565,6 @@ module ActiveRecord
561
565
  def create_table_definition(*args, **options)
562
566
  SQLServer::TableDefinition.new(self, *args, **options)
563
567
  end
564
-
565
568
  end
566
569
  end
567
570
  end