activerecord-sqlserver-adapter 5.2.1 → 7.0.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.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.github/issue_template.md +23 -0
- data/.github/workflows/ci.yml +29 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +17 -27
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +49 -41
- data/Guardfile +9 -8
- data/MIT-LICENSE +1 -1
- data/README.md +65 -42
- data/RUNNING_UNIT_TESTS.md +3 -0
- data/Rakefile +14 -16
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +25 -14
- data/appveyor.yml +22 -17
- data/docker-compose.ci.yml +7 -5
- data/guides/RELEASING.md +11 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +2 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +10 -14
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +12 -5
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +2 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +10 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +30 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +117 -52
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +9 -12
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +2 -3
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +51 -14
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +40 -6
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +18 -10
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +235 -167
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +36 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -45
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +8 -10
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +7 -4
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +5 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +7 -5
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +8 -8
- data/lib/active_record/connection_adapters/sqlserver/type/datetime2.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/datetimeoffset.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/decimal_without_scale.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/json.rb +2 -1
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +6 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +8 -9
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +4 -3
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +6 -5
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +26 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +271 -180
- data/lib/active_record/connection_adapters/sqlserver_column.rb +76 -16
- data/lib/active_record/sqlserver_base.rb +11 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +38 -39
- data/lib/activerecord-sqlserver-adapter.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +177 -56
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/active_schema_test_sqlserver.rb +55 -0
- data/test/cases/adapter_test_sqlserver.rb +258 -173
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +1421 -397
- data/test/cases/column_test_sqlserver.rb +321 -315
- data/test/cases/connection_test_sqlserver.rb +17 -20
- data/test/cases/disconnected_test_sqlserver.rb +39 -0
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +28 -19
- data/test/cases/fetch_test_sqlserver.rb +33 -21
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +15 -19
- data/test/cases/helper_sqlserver.rb +15 -15
- data/test/cases/in_clause_test_sqlserver.rb +63 -0
- data/test/cases/index_test_sqlserver.rb +15 -15
- data/test/cases/json_test_sqlserver.rb +25 -25
- data/test/cases/lateral_test_sqlserver.rb +35 -0
- data/test/cases/migration_test_sqlserver.rb +74 -27
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +59 -53
- data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +70 -45
- data/test/cases/schema_dumper_test_sqlserver.rb +124 -109
- data/test/cases/schema_test_sqlserver.rb +20 -26
- data/test/cases/scratchpad_test_sqlserver.rb +4 -4
- data/test/cases/showplan_test_sqlserver.rb +28 -35
- data/test/cases/specific_schema_test_sqlserver.rb +68 -65
- data/test/cases/transaction_test_sqlserver.rb +18 -20
- data/test/cases/trigger_test_sqlserver.rb +14 -13
- data/test/cases/utils_test_sqlserver.rb +70 -70
- data/test/cases/uuid_test_sqlserver.rb +13 -14
- data/test/debug.rb +8 -6
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +3 -1
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +4 -4
- data/test/models/sqlserver/booking.rb +3 -1
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/customers_view.rb +3 -1
- data/test/models/sqlserver/datatype.rb +2 -0
- data/test/models/sqlserver/datatype_migration.rb +2 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -1
- data/test/models/sqlserver/edge_schema.rb +3 -3
- data/test/models/sqlserver/fk_has_fk.rb +3 -1
- data/test/models/sqlserver/fk_has_pk.rb +3 -1
- data/test/models/sqlserver/natural_pk_data.rb +4 -2
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -1
- data/test/models/sqlserver/no_pk_data.rb +3 -1
- data/test/models/sqlserver/object_default.rb +3 -1
- data/test/models/sqlserver/quoted_table.rb +4 -2
- data/test/models/sqlserver/quoted_view_1.rb +3 -1
- data/test/models/sqlserver/quoted_view_2.rb +3 -1
- data/test/models/sqlserver/sst_memory.rb +3 -1
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -1
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -1
- data/test/models/sqlserver/string_defaults_view.rb +3 -1
- data/test/models/sqlserver/tinyint_pk.rb +3 -1
- data/test/models/sqlserver/trigger.rb +4 -2
- data/test/models/sqlserver/trigger_history.rb +3 -1
- data/test/models/sqlserver/upper.rb +3 -1
- data/test/models/sqlserver/uppered.rb +3 -1
- data/test/models/sqlserver/uuid.rb +3 -1
- data/test/schema/sqlserver_specific_schema.rb +56 -21
- data/test/support/coerceable_test_sqlserver.rb +19 -13
- data/test/support/connection_reflection.rb +3 -2
- data/test/support/core_ext/query_cache.rb +4 -1
- data/test/support/load_schema_sqlserver.rb +5 -5
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/minitest_sqlserver.rb +3 -1
- data/test/support/paths_sqlserver.rb +11 -11
- data/test/support/rake_helpers.rb +15 -10
- data/test/support/sql_counter_sqlserver.rb +16 -15
- data/test/support/test_in_memory_oltp.rb +9 -7
- metadata +47 -13
- data/.travis.yml +0 -25
- 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,
|
|
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[
|
|
21
|
-
fkcolmn = fkdata[
|
|
22
|
-
pktable = fkdata[
|
|
23
|
-
pkcolmn = fkdata[
|
|
24
|
-
remove_foreign_key fktable, name: fkdata[
|
|
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(
|
|
51
|
+
index[:index_keys].split(",").each do |column|
|
|
51
52
|
column.strip!
|
|
52
53
|
|
|
53
|
-
if column.
|
|
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,
|
|
86
|
-
|
|
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,
|
|
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(
|
|
123
|
-
binds << Relation::QueryAttribute.new(
|
|
124
|
-
sp_executesql(sql,
|
|
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(
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
|
|
157
|
-
|
|
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,
|
|
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 #{
|
|
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,
|
|
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[
|
|
210
|
+
to_table = row["PKTABLE_NAME"]
|
|
204
211
|
options = {
|
|
205
|
-
name: row[
|
|
206
|
-
column: row[
|
|
207
|
-
primary_key: row[
|
|
208
|
-
on_update: extract_foreign_key_action(
|
|
209
|
-
on_delete: extract_foreign_key_action(
|
|
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
|
|
218
|
-
when
|
|
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
|
|
233
|
+
when "integer"
|
|
227
234
|
case limit
|
|
228
|
-
when 1 then
|
|
229
|
-
when 2 then
|
|
230
|
-
when 3..4, nil then
|
|
231
|
-
when 5..8 then
|
|
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
|
|
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
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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
|
|
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
|
|
286
|
-
sql
|
|
287
|
-
sql
|
|
288
|
-
sql
|
|
289
|
-
sql
|
|
290
|
-
sql
|
|
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[:
|
|
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:
|
|
308
|
-
primary_key_nonclustered:
|
|
309
|
-
integer: { name:
|
|
310
|
-
bigint: { name:
|
|
311
|
-
boolean: { name:
|
|
312
|
-
decimal: { name:
|
|
313
|
-
money: { name:
|
|
314
|
-
smallmoney: { name:
|
|
315
|
-
float: { name:
|
|
316
|
-
real: { name:
|
|
317
|
-
date: { name:
|
|
318
|
-
datetime: { name:
|
|
319
|
-
datetime2: { name:
|
|
320
|
-
datetimeoffset: { name:
|
|
321
|
-
smalldatetime: { name:
|
|
322
|
-
timestamp: { name:
|
|
323
|
-
time: { name:
|
|
324
|
-
char: { name:
|
|
325
|
-
varchar: { name:
|
|
326
|
-
varchar_max: { name:
|
|
327
|
-
text_basic: { name:
|
|
328
|
-
nchar: { name:
|
|
329
|
-
string: { name:
|
|
330
|
-
text: { name:
|
|
331
|
-
ntext: { name:
|
|
332
|
-
binary_basic: { name:
|
|
333
|
-
varbinary: { name:
|
|
334
|
-
binary: { name:
|
|
335
|
-
uuid: { name:
|
|
336
|
-
ss_timestamp: { name:
|
|
337
|
-
json: { name:
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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(
|
|
398
|
-
binds << Relation::QueryAttribute.new(
|
|
399
|
-
results = sp_executesql(sql,
|
|
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,
|
|
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",
|
|
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)}'",
|
|
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,
|
|
471
|
-
row[
|
|
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 =
|
|
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
|
-
|
|
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[
|
|
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)}",
|
|
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}",
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|