activerecord-sqlserver-adapter 5.2.1 → 7.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +9 -0
  3. data/.github/issue_template.md +23 -0
  4. data/.github/workflows/ci.yml +29 -0
  5. data/.gitignore +1 -0
  6. data/.rubocop.yml +29 -0
  7. data/CHANGELOG.md +17 -27
  8. data/{Dockerfile → Dockerfile.ci} +1 -1
  9. data/Gemfile +49 -41
  10. data/Guardfile +9 -8
  11. data/MIT-LICENSE +1 -1
  12. data/README.md +65 -42
  13. data/RUNNING_UNIT_TESTS.md +3 -0
  14. data/Rakefile +14 -16
  15. data/VERSION +1 -1
  16. data/activerecord-sqlserver-adapter.gemspec +25 -14
  17. data/appveyor.yml +22 -17
  18. data/docker-compose.ci.yml +7 -5
  19. data/guides/RELEASING.md +11 -0
  20. data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
  21. data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
  22. data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
  23. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
  24. data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
  25. data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
  26. data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
  27. data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
  28. data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
  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 +51 -14
  32. data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
  33. data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
  34. data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
  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 +36 -7
  39. data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
  40. data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
  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 +5 -3
  46. data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
  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/decimal_without_scale.rb +22 -0
  52. data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
  53. data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
  54. data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
  55. data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
  56. data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
  57. data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
  58. data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
  59. data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
  60. data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
  61. data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
  62. data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
  63. data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
  64. data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
  65. data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
  66. data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
  67. data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
  68. data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
  69. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
  70. data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
  71. data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
  72. data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
  73. data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
  74. data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
  75. data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
  76. data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
  77. data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
  78. data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
  79. data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
  80. data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
  81. data/lib/active_record/sqlserver_base.rb +11 -9
  82. data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
  83. data/lib/activerecord-sqlserver-adapter.rb +3 -1
  84. data/lib/arel/visitors/sqlserver.rb +177 -56
  85. data/lib/arel_sqlserver.rb +4 -2
  86. data/test/appveyor/dbsetup.ps1 +4 -4
  87. data/test/cases/active_schema_test_sqlserver.rb +55 -0
  88. data/test/cases/adapter_test_sqlserver.rb +258 -173
  89. data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
  90. data/test/cases/change_column_null_test_sqlserver.rb +14 -12
  91. data/test/cases/coerced_tests.rb +1421 -397
  92. data/test/cases/column_test_sqlserver.rb +321 -315
  93. data/test/cases/connection_test_sqlserver.rb +17 -20
  94. data/test/cases/disconnected_test_sqlserver.rb +39 -0
  95. data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
  96. data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
  97. data/test/cases/fetch_test_sqlserver.rb +33 -21
  98. data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
  99. data/test/cases/helper_sqlserver.rb +15 -15
  100. data/test/cases/in_clause_test_sqlserver.rb +63 -0
  101. data/test/cases/index_test_sqlserver.rb +15 -15
  102. data/test/cases/json_test_sqlserver.rb +25 -25
  103. data/test/cases/lateral_test_sqlserver.rb +35 -0
  104. data/test/cases/migration_test_sqlserver.rb +74 -27
  105. data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
  106. data/test/cases/order_test_sqlserver.rb +59 -53
  107. data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
  108. data/test/cases/primary_keys_test_sqlserver.rb +103 -0
  109. data/test/cases/rake_test_sqlserver.rb +70 -45
  110. data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
  111. data/test/cases/schema_test_sqlserver.rb +20 -26
  112. data/test/cases/scratchpad_test_sqlserver.rb +4 -4
  113. data/test/cases/showplan_test_sqlserver.rb +28 -35
  114. data/test/cases/specific_schema_test_sqlserver.rb +68 -65
  115. data/test/cases/transaction_test_sqlserver.rb +18 -20
  116. data/test/cases/trigger_test_sqlserver.rb +14 -13
  117. data/test/cases/utils_test_sqlserver.rb +70 -70
  118. data/test/cases/uuid_test_sqlserver.rb +13 -14
  119. data/test/debug.rb +8 -6
  120. data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
  121. data/test/migrations/create_clients_and_change_column_null.rb +3 -1
  122. data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
  123. data/test/models/sqlserver/booking.rb +3 -1
  124. data/test/models/sqlserver/composite_pk.rb +9 -0
  125. data/test/models/sqlserver/customers_view.rb +3 -1
  126. data/test/models/sqlserver/datatype.rb +2 -0
  127. data/test/models/sqlserver/datatype_migration.rb +2 -0
  128. data/test/models/sqlserver/dollar_table_name.rb +3 -1
  129. data/test/models/sqlserver/edge_schema.rb +3 -3
  130. data/test/models/sqlserver/fk_has_fk.rb +3 -1
  131. data/test/models/sqlserver/fk_has_pk.rb +3 -1
  132. data/test/models/sqlserver/natural_pk_data.rb +4 -2
  133. data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
  134. data/test/models/sqlserver/no_pk_data.rb +3 -1
  135. data/test/models/sqlserver/object_default.rb +3 -1
  136. data/test/models/sqlserver/quoted_table.rb +4 -2
  137. data/test/models/sqlserver/quoted_view_1.rb +3 -1
  138. data/test/models/sqlserver/quoted_view_2.rb +3 -1
  139. data/test/models/sqlserver/sst_memory.rb +3 -1
  140. data/test/models/sqlserver/sst_string_collation.rb +3 -0
  141. data/test/models/sqlserver/string_default.rb +3 -1
  142. data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
  143. data/test/models/sqlserver/string_defaults_view.rb +3 -1
  144. data/test/models/sqlserver/tinyint_pk.rb +3 -1
  145. data/test/models/sqlserver/trigger.rb +4 -2
  146. data/test/models/sqlserver/trigger_history.rb +3 -1
  147. data/test/models/sqlserver/upper.rb +3 -1
  148. data/test/models/sqlserver/uppered.rb +3 -1
  149. data/test/models/sqlserver/uuid.rb +3 -1
  150. data/test/schema/sqlserver_specific_schema.rb +56 -21
  151. data/test/support/coerceable_test_sqlserver.rb +19 -13
  152. data/test/support/connection_reflection.rb +3 -2
  153. data/test/support/core_ext/query_cache.rb +4 -1
  154. data/test/support/load_schema_sqlserver.rb +5 -5
  155. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
  156. data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
  157. data/test/support/minitest_sqlserver.rb +3 -1
  158. data/test/support/paths_sqlserver.rb +11 -11
  159. data/test/support/rake_helpers.rb +15 -10
  160. data/test/support/sql_counter_sqlserver.rb +16 -15
  161. data/test/support/test_in_memory_oltp.rb +9 -7
  162. metadata +47 -13
  163. data/.travis.yml +0 -25
  164. data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +0 -26
@@ -1,32 +1,33 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module ConnectionAdapters
3
5
  module SQLServer
4
6
  module SchemaStatements
5
-
6
7
  def native_database_types
7
8
  @native_database_types ||= initialize_native_database_types.freeze
8
9
  end
9
10
 
10
- def create_table(table_name, comment: nil, **options)
11
+ def create_table(table_name, **options)
11
12
  res = super
12
13
  clear_cache!
13
14
  res
14
15
  end
15
16
 
16
- def drop_table(table_name, options = {})
17
+ def drop_table(table_name, **options)
17
18
  # Mimic CASCADE option as best we can.
18
19
  if options[:force] == :cascade
19
20
  execute_procedure(:sp_fkeys, pktable_name: table_name).each do |fkdata|
20
- fktable = fkdata['FKTABLE_NAME']
21
- fkcolmn = fkdata['FKCOLUMN_NAME']
22
- pktable = fkdata['PKTABLE_NAME']
23
- pkcolmn = fkdata['PKCOLUMN_NAME']
24
- 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"]
25
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)} )"
26
27
  end
27
28
  end
28
29
  if options[:if_exists] && @version_year < 2016
29
- execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}"
30
+ execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = #{quote(table_name)}) DROP TABLE #{quote_table_name(table_name)}", "SCHEMA"
30
31
  else
31
32
  super
32
33
  end
@@ -47,11 +48,11 @@ module ActiveRecord
47
48
  orders = {}
48
49
  columns = []
49
50
 
50
- index[:index_keys].split(',').each do |column|
51
+ index[:index_keys].split(",").each do |column|
51
52
  column.strip!
52
53
 
53
- if column.ends_with?('(-)')
54
- column.gsub! '(-)', ''
54
+ if column.end_with?("(-)")
55
+ column.gsub! "(-)", ""
55
56
  orders[column] = :desc
56
57
  end
57
58
 
@@ -65,15 +66,15 @@ module ActiveRecord
65
66
 
66
67
  def columns(table_name)
67
68
  return [] if table_name.blank?
69
+
68
70
  column_definitions(table_name).map do |ci|
69
- sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity
71
+ sqlserver_options = ci.slice :ordinal_position, :is_primary, :is_identity, :table_name
70
72
  sql_type_metadata = fetch_type_metadata ci[:type], sqlserver_options
71
73
  new_column(
72
74
  ci[:name],
73
75
  ci[:default_value],
74
76
  sql_type_metadata,
75
77
  ci[:null],
76
- ci[:table_name],
77
78
  ci[:default_function],
78
79
  ci[:collation],
79
80
  nil,
@@ -82,16 +83,16 @@ module ActiveRecord
82
83
  end
83
84
  end
84
85
 
85
- def new_column(name, default, sql_type_metadata, null, table_name, default_function = nil, collation = nil, comment = nil, sqlserver_options = {})
86
- SQLServerColumn.new(
86
+ def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {})
87
+ SQLServer::Column.new(
87
88
  name,
88
89
  default,
89
90
  sql_type_metadata,
90
- null, table_name,
91
+ null,
91
92
  default_function,
92
- collation,
93
- comment,
94
- sqlserver_options
93
+ collation: collation,
94
+ comment: comment,
95
+ **sqlserver_options
95
96
  )
96
97
  end
97
98
 
@@ -104,7 +105,7 @@ module ActiveRecord
104
105
  identifier = database_prefix_identifier(table_name)
105
106
  database = identifier.fully_qualified_database_quoted
106
107
  sql = %{
107
- SELECT KCU.COLUMN_NAME AS [name]
108
+ SELECT #{lowercase_schema_reflection_sql('KCU.COLUMN_NAME')} AS [name]
108
109
  FROM #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
109
110
  LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
110
111
  ON KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
@@ -116,12 +117,12 @@ module ActiveRecord
116
117
  AND KCU.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))}
117
118
  AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
118
119
  ORDER BY KCU.ORDINAL_POSITION ASC
119
- }.gsub(/[[:space:]]/, ' ')
120
+ }.gsub(/[[:space:]]/, " ")
120
121
  binds = []
121
122
  nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
122
- binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
123
- binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
124
- 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"] }
125
126
  end
126
127
 
127
128
  def rename_table(table_name, new_name)
@@ -129,8 +130,10 @@ module ActiveRecord
129
130
  rename_table_indexes(table_name, new_name)
130
131
  end
131
132
 
132
- def remove_column(table_name, column_name, type = nil, options = {})
133
- raise ArgumentError.new('You must specify at least one column name. Example: remove_column(:people, :first_name)') if column_name.is_a? Array
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
135
+ return if options[:if_exists] == true && !column_exists?(table_name, column_name)
136
+
134
137
  remove_check_constraints(table_name, column_name)
135
138
  remove_default_constraint(table_name, column_name)
136
139
  remove_indexes(table_name, column_name)
@@ -143,18 +146,20 @@ module ActiveRecord
143
146
  column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
144
147
  without_constraints = options.key?(:default) || options.key?(:limit)
145
148
  default = if !options.key?(:default) && column_object
146
- column_object.default
147
- else
148
- options[:default]
149
- end
149
+ column_object.default
150
+ else
151
+ options[:default]
152
+ end
150
153
  if without_constraints || (column_object && column_object.type != type.to_sym)
151
154
  remove_default_constraint(table_name, column_name)
152
155
  indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
153
156
  remove_indexes(table_name, column_name)
154
157
  end
155
158
  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?
156
- sql_commands << "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])}"
157
- sql_commands.last << ' NOT NULL' if !options[:null].nil? && options[:null] == false
159
+ 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])}"
160
+ alter_command += " COLLATE #{options[:collation]}" if options[:collation].present?
161
+ alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
162
+ sql_commands << alter_command
158
163
  if without_constraints
159
164
  default = quote_default_expression(default, column_object || column_for(table_name, column_name))
160
165
  sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{default} FOR #{quote_column_name(column_name)}"
@@ -171,6 +176,7 @@ module ActiveRecord
171
176
  clear_cache!
172
177
  column = column_for(table_name, column_name)
173
178
  return unless column
179
+
174
180
  remove_default_constraint(table_name, column_name)
175
181
  default = extract_new_default_value(default_or_changes)
176
182
  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)}"
@@ -180,15 +186,16 @@ module ActiveRecord
180
186
  def rename_column(table_name, column_name, new_column_name)
181
187
  clear_cache!
182
188
  identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}")
183
- execute_procedure :sp_rename, identifier.quoted, new_column_name, 'COLUMN'
189
+ execute_procedure :sp_rename, identifier.quoted, new_column_name, "COLUMN"
184
190
  rename_column_indexes(table_name, column_name, new_column_name)
185
191
  clear_cache!
186
192
  end
187
193
 
188
194
  def rename_index(table_name, old_name, new_name)
189
- 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
195
+ raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{index_name_length} characters" if new_name.length > index_name_length
196
+
190
197
  identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}")
191
- execute_procedure :sp_rename, identifier.quoted, new_name, 'INDEX'
198
+ execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX"
192
199
  end
193
200
 
194
201
  def remove_index!(table_name, index_name)
@@ -200,13 +207,13 @@ module ActiveRecord
200
207
  fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema
201
208
  fk_info.map do |row|
202
209
  from_table = identifier.object
203
- to_table = row['PKTABLE_NAME']
210
+ to_table = row["PKTABLE_NAME"]
204
211
  options = {
205
- name: row['FK_NAME'],
206
- column: row['FKCOLUMN_NAME'],
207
- primary_key: row['PKCOLUMN_NAME'],
208
- on_update: extract_foreign_key_action('update', row['FK_NAME']),
209
- on_delete: extract_foreign_key_action('delete', row['FK_NAME'])
212
+ name: row["FK_NAME"],
213
+ column: row["FKCOLUMN_NAME"],
214
+ primary_key: row["PKCOLUMN_NAME"],
215
+ on_update: extract_foreign_key_action("update", row["FK_NAME"]),
216
+ on_delete: extract_foreign_key_action("delete", row["FK_NAME"])
210
217
  }
211
218
  ForeignKeyDefinition.new from_table, to_table, options
212
219
  end
@@ -214,8 +221,8 @@ module ActiveRecord
214
221
 
215
222
  def extract_foreign_key_action(action, fk_name)
216
223
  case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'")
217
- when 'CASCADE' then :cascade
218
- when 'SET_NULL' then :nullify
224
+ when "CASCADE" then :cascade
225
+ when "SET_NULL" then :nullify
219
226
  end
220
227
  end
221
228
 
@@ -223,21 +230,21 @@ module ActiveRecord
223
230
  type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
224
231
  limit = nil unless type_limitable
225
232
  case type.to_s
226
- when 'integer'
233
+ when "integer"
227
234
  case limit
228
- when 1 then 'tinyint'
229
- when 2 then 'smallint'
230
- when 3..4, nil then 'integer'
231
- when 5..8 then 'bigint'
235
+ when 1 then "tinyint"
236
+ when 2 then "smallint"
237
+ when 3..4, nil then "integer"
238
+ when 5..8 then "bigint"
232
239
  else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
233
240
  end
234
- when 'datetime2'
241
+ when "datetime2"
235
242
  column_type_sql = super
236
243
  if precision
237
244
  if (0..7) === precision
238
245
  column_type_sql << "(#{precision})"
239
246
  else
240
- raise(ActiveRecordError, "The dattime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7")
247
+ raise(ActiveRecordError, "The datetime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7")
241
248
  end
242
249
  end
243
250
  column_type_sql
@@ -247,11 +254,11 @@ module ActiveRecord
247
254
  end
248
255
 
249
256
  def columns_for_distinct(columns, orders)
250
- order_columns = orders.reject(&:blank?).map{ |s|
251
- s = s.to_sql unless s.is_a?(String)
252
- s.gsub(/\s+(?:ASC|DESC)\b/i, '')
253
- .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, '')
254
- }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
257
+ order_columns = orders.reject(&:blank?).map { |s|
258
+ s = s.to_sql unless s.is_a?(String)
259
+ s.gsub(/\s+(?:ASC|DESC)\b/i, "")
260
+ .gsub(/\s+NULLS\s+(?:FIRST|LAST)\b/i, "")
261
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
255
262
 
256
263
  (order_columns << super).join(", ")
257
264
  end
@@ -268,7 +275,7 @@ module ActiveRecord
268
275
  do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
269
276
  end
270
277
  sql = "ALTER TABLE #{table_id} ALTER COLUMN #{column_id} #{type_to_sql column.type, limit: column.limit, precision: column.precision, scale: column.scale}"
271
- sql << ' NOT NULL' if !allow_null.nil? && allow_null == false
278
+ sql += " NOT NULL" if !allow_null.nil? && allow_null == false
272
279
  do_execute sql
273
280
  end
274
281
 
@@ -276,25 +283,45 @@ module ActiveRecord
276
283
  SQLServer::SchemaDumper.create(self, options)
277
284
  end
278
285
 
286
+ def create_schema(schema_name, authorization = nil)
287
+ sql = "CREATE SCHEMA [#{schema_name}]"
288
+ sql += " AUTHORIZATION [#{authorization}]" if authorization
289
+
290
+ execute sql
291
+ end
292
+
293
+ def change_table_schema(schema_name, table_name)
294
+ execute "ALTER SCHEMA [#{schema_name}] TRANSFER [#{table_name}]"
295
+ end
296
+
297
+ def drop_schema(schema_name)
298
+ execute "DROP SCHEMA [#{schema_name}]"
299
+ end
300
+
279
301
  private
280
302
 
281
303
  def data_source_sql(name = nil, type: nil)
282
304
  scope = quoted_scope name, type: type
305
+
283
306
  table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
307
+ database = scope[:database].present? ? "#{scope[:database]}." : ""
308
+ table_catalog = scope[:database].present? ? quote(scope[:database]) : "DB_NAME()"
309
+
284
310
  sql = "SELECT #{table_name}"
285
- sql << ' FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)'
286
- sql << ' WHERE TABLE_CATALOG = DB_NAME()'
287
- sql << " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
288
- sql << " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
289
- sql << " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
290
- sql << " ORDER BY #{table_name}"
311
+ sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
312
+ sql += " WHERE TABLE_CATALOG = #{table_catalog}"
313
+ sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
314
+ sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
315
+ sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
316
+ sql += " ORDER BY #{table_name}"
291
317
  sql
292
318
  end
293
319
 
294
320
  def quoted_scope(name = nil, type: nil)
295
321
  identifier = SQLServer::Utils.extract_identifiers(name)
296
322
  {}.tap do |scope|
297
- scope[:schema] = identifier.schema || 'dbo'
323
+ scope[:database] = identifier.database if identifier.database
324
+ scope[:schema] = identifier.schema || "dbo"
298
325
  scope[:name] = identifier.object if identifier.object
299
326
  scope[:type] = type if type
300
327
  end
@@ -304,37 +331,37 @@ module ActiveRecord
304
331
 
305
332
  def initialize_native_database_types
306
333
  {
307
- primary_key: 'bigint NOT NULL IDENTITY(1,1) PRIMARY KEY',
308
- primary_key_nonclustered: 'int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED',
309
- integer: { name: 'int', limit: 4 },
310
- bigint: { name: 'bigint' },
311
- boolean: { name: 'bit' },
312
- decimal: { name: 'decimal' },
313
- money: { name: 'money' },
314
- smallmoney: { name: 'smallmoney' },
315
- float: { name: 'float' },
316
- real: { name: 'real' },
317
- date: { name: 'date' },
318
- datetime: { name: 'datetime' },
319
- datetime2: { name: 'datetime2' },
320
- datetimeoffset: { name: 'datetimeoffset' },
321
- smalldatetime: { name: 'smalldatetime' },
322
- timestamp: { name: 'datetime' },
323
- time: { name: 'time' },
324
- char: { name: 'char' },
325
- varchar: { name: 'varchar', limit: 8000 },
326
- varchar_max: { name: 'varchar(max)' },
327
- text_basic: { name: 'text' },
328
- nchar: { name: 'nchar' },
329
- string: { name: 'nvarchar', limit: 4000 },
330
- text: { name: 'nvarchar(max)' },
331
- ntext: { name: 'ntext' },
332
- binary_basic: { name: 'binary' },
333
- varbinary: { name: 'varbinary', limit: 8000 },
334
- binary: { name: 'varbinary(max)' },
335
- uuid: { name: 'uniqueidentifier' },
336
- ss_timestamp: { name: 'timestamp' },
337
- json: { name: 'nvarchar(max)' }
334
+ primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY",
335
+ primary_key_nonclustered: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED",
336
+ integer: { name: "int", limit: 4 },
337
+ bigint: { name: "bigint" },
338
+ boolean: { name: "bit" },
339
+ decimal: { name: "decimal" },
340
+ money: { name: "money" },
341
+ smallmoney: { name: "smallmoney" },
342
+ float: { name: "float" },
343
+ real: { name: "real" },
344
+ date: { name: "date" },
345
+ datetime: { name: "datetime" },
346
+ datetime2: { name: "datetime2" },
347
+ datetimeoffset: { name: "datetimeoffset" },
348
+ smalldatetime: { name: "smalldatetime" },
349
+ timestamp: { name: "datetime" },
350
+ time: { name: "time" },
351
+ char: { name: "char" },
352
+ varchar: { name: "varchar", limit: 8000 },
353
+ varchar_max: { name: "varchar(max)" },
354
+ text_basic: { name: "text" },
355
+ nchar: { name: "nchar" },
356
+ string: { name: "nvarchar", limit: 4000 },
357
+ text: { name: "nvarchar(max)" },
358
+ ntext: { name: "ntext" },
359
+ binary_basic: { name: "binary" },
360
+ varbinary: { name: "varbinary", limit: 8000 },
361
+ binary: { name: "varbinary(max)" },
362
+ uuid: { name: "uniqueidentifier" },
363
+ ss_timestamp: { name: "timestamp" },
364
+ json: { name: "nvarchar(max)" }
338
365
  }
339
366
  end
340
367
 
@@ -343,61 +370,15 @@ module ActiveRecord
343
370
  database = identifier.fully_qualified_database_quoted
344
371
  view_exists = view_exists?(table_name)
345
372
  view_tblnm = view_table_name(table_name) if view_exists
346
- sql = %{
347
- SELECT DISTINCT
348
- #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
349
- #{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name,
350
- columns.DATA_TYPE AS type,
351
- columns.COLUMN_DEFAULT AS default_value,
352
- columns.NUMERIC_SCALE AS numeric_scale,
353
- columns.NUMERIC_PRECISION AS numeric_precision,
354
- columns.DATETIME_PRECISION AS datetime_precision,
355
- columns.COLLATION_NAME AS [collation],
356
- columns.ordinal_position,
357
- CASE
358
- WHEN columns.DATA_TYPE IN ('nchar','nvarchar','char','varchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
359
- ELSE COL_LENGTH('#{database}.'+columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME)
360
- END AS [length],
361
- CASE
362
- WHEN columns.IS_NULLABLE = 'YES' THEN 1
363
- ELSE NULL
364
- END AS [is_nullable],
365
- CASE
366
- WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1
367
- ELSE NULL
368
- END AS [is_primary],
369
- c.is_identity AS [is_identity]
370
- FROM #{database}.INFORMATION_SCHEMA.COLUMNS columns
371
- LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC
372
- ON TC.TABLE_NAME = columns.TABLE_NAME
373
- AND TC.TABLE_SCHEMA = columns.TABLE_SCHEMA
374
- AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY'
375
- LEFT OUTER JOIN #{database}.INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
376
- ON KCU.COLUMN_NAME = columns.COLUMN_NAME
377
- AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
378
- AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
379
- AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
380
- INNER JOIN #{database}.sys.schemas AS s
381
- ON s.name = columns.TABLE_SCHEMA
382
- AND s.schema_id = s.schema_id
383
- INNER JOIN #{database}.sys.objects AS o
384
- ON s.schema_id = o.schema_id
385
- AND o.is_ms_shipped = 0
386
- AND o.type IN ('U', 'V')
387
- AND o.name = columns.TABLE_NAME
388
- INNER JOIN #{database}.sys.columns AS c
389
- ON o.object_id = c.object_id
390
- AND c.name = columns.COLUMN_NAME
391
- WHERE columns.TABLE_NAME = #{prepared_statements ? '@0' : quote(identifier.object)}
392
- AND columns.TABLE_SCHEMA = #{identifier.schema.blank? ? 'schema_name()' : (prepared_statements ? '@1' : quote(identifier.schema))}
393
- ORDER BY columns.ordinal_position
394
- }.gsub(/[ \t\r\n]+/, ' ').strip
373
+
374
+ sql = column_definitions_sql(database, identifier)
375
+
395
376
  binds = []
396
377
  nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
397
- binds << Relation::QueryAttribute.new('TABLE_NAME', identifier.object, nv128)
398
- binds << Relation::QueryAttribute.new('TABLE_SCHEMA', identifier.schema, nv128) unless identifier.schema.blank?
399
- results = sp_executesql(sql, 'SCHEMA', binds)
400
- results.map do |ci|
378
+ binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
379
+ binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
380
+ results = sp_executesql(sql, "SCHEMA", binds)
381
+ columns = results.map do |ci|
401
382
  ci = ci.symbolize_keys
402
383
  ci[:_type] = ci[:type]
403
384
  ci[:table_name] = view_tblnm || table_name
@@ -427,7 +408,7 @@ module ActiveRecord
427
408
  WHERE
428
409
  c.TABLE_NAME = '#{view_tblnm}'
429
410
  AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
430
- }.squish, 'SCHEMA'
411
+ }.squish, "SCHEMA"
431
412
  end
432
413
  case default
433
414
  when nil
@@ -446,7 +427,7 @@ module ActiveRecord
446
427
  else ci[:type]
447
428
  end
448
429
  value = default.match(/\A\((.*)\)\Z/m)[1]
449
- value = select_value("SELECT CAST(#{value} AS #{type}) AS value", 'SCHEMA')
430
+ value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
450
431
  [value, nil]
451
432
  end
452
433
  end
@@ -456,10 +437,92 @@ module ActiveRecord
456
437
  ci[:is_identity] = ci[:is_identity].to_i == 1 unless [TrueClass, FalseClass].include?(ci[:is_identity].class)
457
438
  ci
458
439
  end
440
+
441
+ # Since Rails 7, it's expected that all adapter raise error when table doesn't exists.
442
+ # I'm not aware of the possibility of tables without columns on SQL Server (postgres have those).
443
+ # Raise error if the method return an empty array
444
+ columns.tap do |result|
445
+ raise ActiveRecord::StatementInvalid, "Table '#{table_name}' doesn't exist" if result.empty?
446
+ end
447
+ end
448
+
449
+ def column_definitions_sql(database, identifier)
450
+ object_name = prepared_statements ? "@0" : quote(identifier.object)
451
+ schema_name = if identifier.schema.blank?
452
+ "schema_name()"
453
+ else
454
+ prepared_statements ? "@1" : quote(identifier.schema)
455
+ end
456
+
457
+ %{
458
+ SELECT
459
+ #{lowercase_schema_reflection_sql('o.name')} AS [table_name],
460
+ #{lowercase_schema_reflection_sql('c.name')} AS [name],
461
+ t.name AS [type],
462
+ d.definition AS [default_value],
463
+ CASE
464
+ WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint')
465
+ THEN c.scale
466
+ END AS [numeric_scale],
467
+ CASE
468
+ WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint', 'real', 'float')
469
+ THEN c.precision
470
+ END AS [numeric_precision],
471
+ CASE
472
+ WHEN t.name IN ('date', 'datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'time')
473
+ THEN c.scale
474
+ END AS [datetime_precision],
475
+ c.collation_name AS [collation],
476
+ ROW_NUMBER() OVER (ORDER BY c.column_id) AS [ordinal_position],
477
+ CASE
478
+ WHEN t.name IN ('nchar', 'nvarchar') AND c.max_length > 0
479
+ THEN c.max_length / 2
480
+ ELSE c.max_length
481
+ END AS [length],
482
+ CASE c.is_nullable
483
+ WHEN 1
484
+ THEN 1
485
+ END AS [is_nullable],
486
+ CASE
487
+ WHEN ic.object_id IS NOT NULL
488
+ THEN 1
489
+ END AS [is_primary],
490
+ c.is_identity AS [is_identity]
491
+ FROM #{database}.sys.columns c
492
+ INNER JOIN #{database}.sys.objects o
493
+ ON c.object_id = o.object_id
494
+ INNER JOIN #{database}.sys.schemas s
495
+ ON o.schema_id = s.schema_id
496
+ INNER JOIN #{database}.sys.types t
497
+ ON c.system_type_id = t.system_type_id
498
+ AND c.user_type_id = t.user_type_id
499
+ LEFT OUTER JOIN #{database}.sys.default_constraints d
500
+ ON c.object_id = d.parent_object_id
501
+ AND c.default_object_id = d.object_id
502
+ LEFT OUTER JOIN #{database}.sys.key_constraints k
503
+ ON c.object_id = k.parent_object_id
504
+ AND k.type = 'PK'
505
+ LEFT OUTER JOIN #{database}.sys.index_columns ic
506
+ ON k.parent_object_id = ic.object_id
507
+ AND k.unique_index_id = ic.index_id
508
+ AND c.column_id = ic.column_id
509
+ WHERE
510
+ o.name = #{object_name}
511
+ AND s.name = #{schema_name}
512
+ ORDER BY
513
+ c.column_id
514
+ }.gsub(/[ \t\r\n]+/, " ").strip
515
+ end
516
+
517
+ def remove_columns_for_alter(table_name, *column_names, **options)
518
+ first, *rest = column_names
519
+
520
+ # return an array like this [DROP COLUMN col_1, col_2, col_3]. Abstract adapter joins fragments with ", "
521
+ [remove_column_for_alter(table_name, first)] + rest.map { |column_name| quote_column_name(column_name) }
459
522
  end
460
523
 
461
524
  def remove_check_constraints(table_name, column_name)
462
- 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'
525
+ 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"
463
526
  constraints.each do |constraint|
464
527
  do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
465
528
  end
@@ -467,8 +530,8 @@ module ActiveRecord
467
530
 
468
531
  def remove_default_constraint(table_name, column_name)
469
532
  # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
470
- execute_procedure(:sp_helpconstraint, table_name, 'nomsg').flatten.select do |row|
471
- row['constraint_type'] == "DEFAULT on column #{column_name}"
533
+ execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row|
534
+ row["constraint_type"] == "DEFAULT on column #{column_name}"
472
535
  end.each do |row|
473
536
  do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
474
537
  end
@@ -482,15 +545,20 @@ module ActiveRecord
482
545
 
483
546
  # === SQLServer Specific (Misc Helpers) ========================= #
484
547
 
548
+ # Parses just the table name from the SQL. Table name does not include database/schema/etc.
485
549
  def get_table_name(sql)
486
- tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
550
+ tn = get_raw_table_name(sql)
551
+ SQLServer::Utils.extract_identifiers(tn).object
552
+ end
553
+
554
+ # Parses the raw table name that is used in the SQL. Table name could include database/schema/etc.
555
+ def get_raw_table_name(sql)
556
+ case sql
557
+ when /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
487
558
  Regexp.last_match[3] || Regexp.last_match[4]
488
- elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
559
+ when /FROM\s+([^\(\s]+)\s*/i
489
560
  Regexp.last_match[1]
490
- else
491
- nil
492
561
  end
493
- SQLServer::Utils.extract_identifiers(tn).object
494
562
  end
495
563
 
496
564
  def default_constraint_name(table_name, column_name)
@@ -505,22 +573,22 @@ module ActiveRecord
505
573
 
506
574
  def view_table_name(table_name)
507
575
  view_info = view_information(table_name)
508
- view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
576
+ view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
509
577
  end
510
578
 
511
579
  def view_information(table_name)
512
580
  @view_information ||= {}
513
581
  @view_information[table_name] ||= begin
514
582
  identifier = SQLServer::Utils.extract_identifiers(table_name)
515
- view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", 'SCHEMA'
583
+ view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA"
516
584
  if view_info
517
585
  view_info = view_info.with_indifferent_access
518
586
  if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
519
587
  view_info[:VIEW_DEFINITION] = begin
520
- select_values("EXEC sp_helptext #{identifier.object_quoted}", 'SCHEMA').join
521
- rescue
522
- warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
523
- nil
588
+ select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
589
+ rescue
590
+ warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
591
+ nil
524
592
  end
525
593
  end
526
594
  end
@@ -531,14 +599,14 @@ module ActiveRecord
531
599
  def views_real_column_name(table_name, column_name)
532
600
  view_definition = view_information(table_name)[:VIEW_DEFINITION]
533
601
  return column_name unless view_definition
602
+
534
603
  match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
535
604
  match_data ? match_data[1] : column_name
536
605
  end
537
606
 
538
- def create_table_definition(*args)
539
- SQLServer::TableDefinition.new(*args)
607
+ def create_table_definition(*args, **options)
608
+ SQLServer::TableDefinition.new(self, *args, **options)
540
609
  end
541
-
542
610
  end
543
611
  end
544
612
  end