activerecord-sqlserver-adapter 5.2.1 → 6.0.2
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 +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/CHANGELOG.md +58 -20
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +48 -41
- data/Guardfile +9 -8
- data/README.md +28 -31
- 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 +24 -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 +3 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/calculations.rb +5 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +3 -3
- 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 +8 -7
- data/lib/active_record/connection_adapters/sqlserver/core_ext/preloader.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/query_methods.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +88 -44
- 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 +46 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +16 -5
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +9 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +210 -163
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +8 -8
- 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/sql_type_metadata.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +43 -44
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +7 -9
- data/lib/active_record/connection_adapters/sqlserver/type.rb +38 -35
- 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 +2 -2
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +4 -3
- 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/utils.rb +10 -11
- data/lib/active_record/connection_adapters/sqlserver/version.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +145 -94
- data/lib/active_record/connection_adapters/sqlserver_column.rb +9 -5
- data/lib/active_record/sqlserver_base.rb +9 -1
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +28 -32
- data/lib/activerecord-sqlserver-adapter.rb +3 -1
- data/lib/arel/visitors/sqlserver.rb +108 -34
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/adapter_test_sqlserver.rb +246 -171
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +722 -381
- data/test/cases/column_test_sqlserver.rb +287 -285
- data/test/cases/connection_test_sqlserver.rb +17 -20
- data/test/cases/execute_procedure_test_sqlserver.rb +20 -20
- data/test/cases/fetch_test_sqlserver.rb +16 -22
- 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 +36 -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 +67 -27
- data/test/cases/optimizer_hints_test_sqlserver.rb +72 -0
- data/test/cases/order_test_sqlserver.rb +53 -54
- data/test/cases/pessimistic_locking_test_sqlserver.rb +27 -33
- data/test/cases/rake_test_sqlserver.rb +33 -45
- data/test/cases/schema_dumper_test_sqlserver.rb +115 -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_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/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/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 +31 -21
- data/test/support/coerceable_test_sqlserver.rb +15 -9
- 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/minitest_sqlserver.rb +3 -1
- data/test/support/paths_sqlserver.rb +11 -11
- data/test/support/rake_helpers.rb +13 -10
- data/test/support/sql_counter_sqlserver.rb +3 -4
- data/test/support/test_in_memory_oltp.rb +9 -7
- metadata +27 -12
- data/.travis.yml +0 -25
|
@@ -1,20 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module SQLServer
|
|
4
6
|
class SchemaCreation < AbstractAdapter::SchemaCreation
|
|
5
|
-
|
|
6
7
|
private
|
|
7
8
|
|
|
8
9
|
def visit_TableDefinition(o)
|
|
10
|
+
if_not_exists = o.if_not_exists
|
|
11
|
+
|
|
9
12
|
if o.as
|
|
10
13
|
table_name = quote_table_name(o.temporary ? "##{o.name}" : o.name)
|
|
11
14
|
query = o.as.respond_to?(:to_sql) ? o.as.to_sql : o.as
|
|
12
15
|
projections, source = query.match(%r{SELECT\s+(.*)?\s+FROM\s+(.*)?}).captures
|
|
13
|
-
|
|
16
|
+
sql = "SELECT #{projections} INTO #{table_name} FROM #{source}"
|
|
14
17
|
else
|
|
15
18
|
o.instance_variable_set :@as, nil
|
|
16
|
-
|
|
19
|
+
o.instance_variable_set :@if_not_exists, false
|
|
20
|
+
sql = super
|
|
17
21
|
end
|
|
22
|
+
|
|
23
|
+
if if_not_exists
|
|
24
|
+
o.instance_variable_set :@if_not_exists, true
|
|
25
|
+
table_name = o.temporary ? "##{o.name}" : o.name
|
|
26
|
+
sql = "IF NOT EXISTS (SELECT * FROM sysobjects WHERE name='#{table_name}' and xtype='U') #{sql}"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
sql
|
|
18
30
|
end
|
|
19
31
|
|
|
20
32
|
def add_column_options!(sql, options)
|
|
@@ -34,7 +46,7 @@ module ActiveRecord
|
|
|
34
46
|
def action_sql(action, dependency)
|
|
35
47
|
case dependency
|
|
36
48
|
when :restrict
|
|
37
|
-
raise ArgumentError,
|
|
49
|
+
raise ArgumentError, <<~MSG.squish
|
|
38
50
|
'#{dependency}' is not supported for :on_update or :on_delete.
|
|
39
51
|
Supported values are: :nullify, :cascade
|
|
40
52
|
MSG
|
|
@@ -50,7 +62,6 @@ module ActiveRecord
|
|
|
50
62
|
def options_primary_key_with_nil_default?(options)
|
|
51
63
|
options[:primary_key] && options.include?(:default) && options[:default].nil?
|
|
52
64
|
end
|
|
53
|
-
|
|
54
65
|
end
|
|
55
66
|
end
|
|
56
67
|
end
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module SQLServer
|
|
4
6
|
class SchemaDumper < ConnectionAdapters::SchemaDumper
|
|
5
|
-
|
|
6
7
|
SQLSEVER_NO_LIMIT_TYPES = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
"text",
|
|
9
|
+
"ntext",
|
|
10
|
+
"varchar(max)",
|
|
11
|
+
"nvarchar(max)",
|
|
12
|
+
"varbinary(max)"
|
|
12
13
|
].freeze
|
|
13
14
|
|
|
14
15
|
private
|
|
@@ -19,18 +20,19 @@ module ActiveRecord
|
|
|
19
20
|
|
|
20
21
|
def schema_limit(column)
|
|
21
22
|
return if SQLSEVER_NO_LIMIT_TYPES.include?(column.sql_type)
|
|
23
|
+
|
|
22
24
|
super
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
def schema_collation(column)
|
|
26
28
|
return unless column.collation
|
|
29
|
+
|
|
27
30
|
column.collation if column.collation != @connection.collation
|
|
28
31
|
end
|
|
29
32
|
|
|
30
33
|
def default_primary_key?(column)
|
|
31
34
|
super && column.is_primary? && column.is_identity?
|
|
32
35
|
end
|
|
33
|
-
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -1,27 +1,28 @@
|
|
|
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
|
|
@@ -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.ends_with?(
|
|
54
|
-
column.gsub!
|
|
54
|
+
if column.ends_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
|
+
def new_column(name, default, sql_type_metadata, null, default_function = nil, collation = nil, comment = nil, sqlserver_options = {})
|
|
86
87
|
SQLServerColumn.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)
|
|
@@ -130,7 +131,8 @@ module ActiveRecord
|
|
|
130
131
|
end
|
|
131
132
|
|
|
132
133
|
def remove_column(table_name, column_name, type = nil, options = {})
|
|
133
|
-
raise ArgumentError.new(
|
|
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
|
+
|
|
134
136
|
remove_check_constraints(table_name, column_name)
|
|
135
137
|
remove_default_constraint(table_name, column_name)
|
|
136
138
|
remove_indexes(table_name, column_name)
|
|
@@ -143,18 +145,19 @@ module ActiveRecord
|
|
|
143
145
|
column_object = schema_cache.columns(table_name).find { |c| c.name.to_s == column_name.to_s }
|
|
144
146
|
without_constraints = options.key?(:default) || options.key?(:limit)
|
|
145
147
|
default = if !options.key?(:default) && column_object
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
148
|
+
column_object.default
|
|
149
|
+
else
|
|
150
|
+
options[:default]
|
|
151
|
+
end
|
|
150
152
|
if without_constraints || (column_object && column_object.type != type.to_sym)
|
|
151
153
|
remove_default_constraint(table_name, column_name)
|
|
152
154
|
indexes = indexes(table_name).select { |index| index.columns.include?(column_name.to_s) }
|
|
153
155
|
remove_indexes(table_name, column_name)
|
|
154
156
|
end
|
|
155
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?
|
|
156
|
-
|
|
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])}"
|
|
159
|
+
alter_command += " NOT NULL" if !options[:null].nil? && options[:null] == false
|
|
160
|
+
sql_commands << alter_command
|
|
158
161
|
if without_constraints
|
|
159
162
|
default = quote_default_expression(default, column_object || column_for(table_name, column_name))
|
|
160
163
|
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 +174,7 @@ module ActiveRecord
|
|
|
171
174
|
clear_cache!
|
|
172
175
|
column = column_for(table_name, column_name)
|
|
173
176
|
return unless column
|
|
177
|
+
|
|
174
178
|
remove_default_constraint(table_name, column_name)
|
|
175
179
|
default = extract_new_default_value(default_or_changes)
|
|
176
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)}"
|
|
@@ -180,15 +184,16 @@ module ActiveRecord
|
|
|
180
184
|
def rename_column(table_name, column_name, new_column_name)
|
|
181
185
|
clear_cache!
|
|
182
186
|
identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{column_name}")
|
|
183
|
-
execute_procedure :sp_rename, identifier.quoted, new_column_name,
|
|
187
|
+
execute_procedure :sp_rename, identifier.quoted, new_column_name, "COLUMN"
|
|
184
188
|
rename_column_indexes(table_name, column_name, new_column_name)
|
|
185
189
|
clear_cache!
|
|
186
190
|
end
|
|
187
191
|
|
|
188
192
|
def rename_index(table_name, old_name, new_name)
|
|
189
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
|
+
|
|
190
195
|
identifier = SQLServer::Utils.extract_identifiers("#{table_name}.#{old_name}")
|
|
191
|
-
execute_procedure :sp_rename, identifier.quoted, new_name,
|
|
196
|
+
execute_procedure :sp_rename, identifier.quoted, new_name, "INDEX"
|
|
192
197
|
end
|
|
193
198
|
|
|
194
199
|
def remove_index!(table_name, index_name)
|
|
@@ -200,13 +205,13 @@ module ActiveRecord
|
|
|
200
205
|
fk_info = execute_procedure :sp_fkeys, nil, identifier.schema, nil, identifier.object, identifier.schema
|
|
201
206
|
fk_info.map do |row|
|
|
202
207
|
from_table = identifier.object
|
|
203
|
-
to_table = row[
|
|
208
|
+
to_table = row["PKTABLE_NAME"]
|
|
204
209
|
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(
|
|
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"])
|
|
210
215
|
}
|
|
211
216
|
ForeignKeyDefinition.new from_table, to_table, options
|
|
212
217
|
end
|
|
@@ -214,8 +219,8 @@ module ActiveRecord
|
|
|
214
219
|
|
|
215
220
|
def extract_foreign_key_action(action, fk_name)
|
|
216
221
|
case select_value("SELECT #{action}_referential_action_desc FROM sys.foreign_keys WHERE name = '#{fk_name}'")
|
|
217
|
-
when
|
|
218
|
-
when
|
|
222
|
+
when "CASCADE" then :cascade
|
|
223
|
+
when "SET_NULL" then :nullify
|
|
219
224
|
end
|
|
220
225
|
end
|
|
221
226
|
|
|
@@ -223,21 +228,21 @@ module ActiveRecord
|
|
|
223
228
|
type_limitable = %w(string integer float char nchar varchar nvarchar).include?(type.to_s)
|
|
224
229
|
limit = nil unless type_limitable
|
|
225
230
|
case type.to_s
|
|
226
|
-
when
|
|
231
|
+
when "integer"
|
|
227
232
|
case limit
|
|
228
|
-
when 1 then
|
|
229
|
-
when 2 then
|
|
230
|
-
when 3..4, nil then
|
|
231
|
-
when 5..8 then
|
|
233
|
+
when 1 then "tinyint"
|
|
234
|
+
when 2 then "smallint"
|
|
235
|
+
when 3..4, nil then "integer"
|
|
236
|
+
when 5..8 then "bigint"
|
|
232
237
|
else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
|
|
233
238
|
end
|
|
234
|
-
when
|
|
239
|
+
when "datetime2"
|
|
235
240
|
column_type_sql = super
|
|
236
241
|
if precision
|
|
237
242
|
if (0..7) === precision
|
|
238
243
|
column_type_sql << "(#{precision})"
|
|
239
244
|
else
|
|
240
|
-
raise(ActiveRecordError, "The
|
|
245
|
+
raise(ActiveRecordError, "The datetime2 type has precision of #{precision}. The allowed range of precision is from 0 to 7")
|
|
241
246
|
end
|
|
242
247
|
end
|
|
243
248
|
column_type_sql
|
|
@@ -247,11 +252,11 @@ module ActiveRecord
|
|
|
247
252
|
end
|
|
248
253
|
|
|
249
254
|
def columns_for_distinct(columns, orders)
|
|
250
|
-
order_columns = orders.reject(&:blank?).map{ |s|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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}" }
|
|
255
260
|
|
|
256
261
|
(order_columns << super).join(", ")
|
|
257
262
|
end
|
|
@@ -268,7 +273,7 @@ module ActiveRecord
|
|
|
268
273
|
do_execute("UPDATE #{table_id} SET #{column_id}=#{quote(default)} WHERE #{column_id} IS NULL")
|
|
269
274
|
end
|
|
270
275
|
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
|
|
276
|
+
sql += " NOT NULL" if !allow_null.nil? && allow_null == false
|
|
272
277
|
do_execute sql
|
|
273
278
|
end
|
|
274
279
|
|
|
@@ -276,25 +281,45 @@ module ActiveRecord
|
|
|
276
281
|
SQLServer::SchemaDumper.create(self, options)
|
|
277
282
|
end
|
|
278
283
|
|
|
284
|
+
def create_schema(schema_name, authorization = nil)
|
|
285
|
+
sql = "CREATE SCHEMA [#{schema_name}]"
|
|
286
|
+
sql += " AUTHORIZATION [#{authorization}]" if authorization
|
|
287
|
+
|
|
288
|
+
execute sql
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
def change_table_schema(schema_name, table_name)
|
|
292
|
+
execute "ALTER SCHEMA [#{schema_name}] TRANSFER [#{table_name}]"
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def drop_schema(schema_name)
|
|
296
|
+
execute "DROP SCHEMA [#{schema_name}]"
|
|
297
|
+
end
|
|
298
|
+
|
|
279
299
|
private
|
|
280
300
|
|
|
281
301
|
def data_source_sql(name = nil, type: nil)
|
|
282
302
|
scope = quoted_scope name, type: type
|
|
303
|
+
|
|
283
304
|
table_name = lowercase_schema_reflection_sql 'TABLE_NAME'
|
|
305
|
+
database = scope[:database].present? ? "#{scope[:database]}." : ""
|
|
306
|
+
table_catalog = scope[:database].present? ? quote(scope[:database]) : "DB_NAME()"
|
|
307
|
+
|
|
284
308
|
sql = "SELECT #{table_name}"
|
|
285
|
-
sql
|
|
286
|
-
sql
|
|
287
|
-
sql
|
|
288
|
-
sql
|
|
289
|
-
sql
|
|
290
|
-
sql
|
|
309
|
+
sql += " FROM #{database}INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
|
|
310
|
+
sql += " WHERE TABLE_CATALOG = #{table_catalog}"
|
|
311
|
+
sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
|
312
|
+
sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
|
|
313
|
+
sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
|
|
314
|
+
sql += " ORDER BY #{table_name}"
|
|
291
315
|
sql
|
|
292
316
|
end
|
|
293
317
|
|
|
294
318
|
def quoted_scope(name = nil, type: nil)
|
|
295
319
|
identifier = SQLServer::Utils.extract_identifiers(name)
|
|
296
320
|
{}.tap do |scope|
|
|
297
|
-
scope[:
|
|
321
|
+
scope[:database] = identifier.database if identifier.database
|
|
322
|
+
scope[:schema] = identifier.schema || "dbo"
|
|
298
323
|
scope[:name] = identifier.object if identifier.object
|
|
299
324
|
scope[:type] = type if type
|
|
300
325
|
end
|
|
@@ -304,37 +329,37 @@ module ActiveRecord
|
|
|
304
329
|
|
|
305
330
|
def initialize_native_database_types
|
|
306
331
|
{
|
|
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:
|
|
332
|
+
primary_key: "bigint NOT NULL IDENTITY(1,1) PRIMARY KEY",
|
|
333
|
+
primary_key_nonclustered: "int NOT NULL IDENTITY(1,1) PRIMARY KEY NONCLUSTERED",
|
|
334
|
+
integer: { name: "int", limit: 4 },
|
|
335
|
+
bigint: { name: "bigint" },
|
|
336
|
+
boolean: { name: "bit" },
|
|
337
|
+
decimal: { name: "decimal" },
|
|
338
|
+
money: { name: "money" },
|
|
339
|
+
smallmoney: { name: "smallmoney" },
|
|
340
|
+
float: { name: "float" },
|
|
341
|
+
real: { name: "real" },
|
|
342
|
+
date: { name: "date" },
|
|
343
|
+
datetime: { name: "datetime" },
|
|
344
|
+
datetime2: { name: "datetime2" },
|
|
345
|
+
datetimeoffset: { name: "datetimeoffset" },
|
|
346
|
+
smalldatetime: { name: "smalldatetime" },
|
|
347
|
+
timestamp: { name: "datetime" },
|
|
348
|
+
time: { name: "time" },
|
|
349
|
+
char: { name: "char" },
|
|
350
|
+
varchar: { name: "varchar", limit: 8000 },
|
|
351
|
+
varchar_max: { name: "varchar(max)" },
|
|
352
|
+
text_basic: { name: "text" },
|
|
353
|
+
nchar: { name: "nchar" },
|
|
354
|
+
string: { name: "nvarchar", limit: 4000 },
|
|
355
|
+
text: { name: "nvarchar(max)" },
|
|
356
|
+
ntext: { name: "ntext" },
|
|
357
|
+
binary_basic: { name: "binary" },
|
|
358
|
+
varbinary: { name: "varbinary", limit: 8000 },
|
|
359
|
+
binary: { name: "varbinary(max)" },
|
|
360
|
+
uuid: { name: "uniqueidentifier" },
|
|
361
|
+
ss_timestamp: { name: "timestamp" },
|
|
362
|
+
json: { name: "nvarchar(max)" }
|
|
338
363
|
}
|
|
339
364
|
end
|
|
340
365
|
|
|
@@ -343,60 +368,14 @@ module ActiveRecord
|
|
|
343
368
|
database = identifier.fully_qualified_database_quoted
|
|
344
369
|
view_exists = view_exists?(table_name)
|
|
345
370
|
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
|
|
371
|
+
|
|
372
|
+
sql = column_definitions_sql(database, identifier)
|
|
373
|
+
|
|
395
374
|
binds = []
|
|
396
375
|
nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
|
|
397
|
-
binds << Relation::QueryAttribute.new(
|
|
398
|
-
binds << Relation::QueryAttribute.new(
|
|
399
|
-
results = sp_executesql(sql,
|
|
376
|
+
binds << Relation::QueryAttribute.new("TABLE_NAME", identifier.object, nv128)
|
|
377
|
+
binds << Relation::QueryAttribute.new("TABLE_SCHEMA", identifier.schema, nv128) unless identifier.schema.blank?
|
|
378
|
+
results = sp_executesql(sql, "SCHEMA", binds)
|
|
400
379
|
results.map do |ci|
|
|
401
380
|
ci = ci.symbolize_keys
|
|
402
381
|
ci[:_type] = ci[:type]
|
|
@@ -427,7 +406,7 @@ module ActiveRecord
|
|
|
427
406
|
WHERE
|
|
428
407
|
c.TABLE_NAME = '#{view_tblnm}'
|
|
429
408
|
AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
|
|
430
|
-
}.squish,
|
|
409
|
+
}.squish, "SCHEMA"
|
|
431
410
|
end
|
|
432
411
|
case default
|
|
433
412
|
when nil
|
|
@@ -446,7 +425,7 @@ module ActiveRecord
|
|
|
446
425
|
else ci[:type]
|
|
447
426
|
end
|
|
448
427
|
value = default.match(/\A\((.*)\)\Z/m)[1]
|
|
449
|
-
value = select_value("SELECT CAST(#{value} AS #{type}) AS value",
|
|
428
|
+
value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
|
|
450
429
|
[value, nil]
|
|
451
430
|
end
|
|
452
431
|
end
|
|
@@ -458,8 +437,76 @@ module ActiveRecord
|
|
|
458
437
|
end
|
|
459
438
|
end
|
|
460
439
|
|
|
440
|
+
def column_definitions_sql(database, identifier)
|
|
441
|
+
object_name = prepared_statements ? "@0" : quote(identifier.object)
|
|
442
|
+
schema_name = if identifier.schema.blank?
|
|
443
|
+
"schema_name()"
|
|
444
|
+
else
|
|
445
|
+
prepared_statements ? "@1" : quote(identifier.schema)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
%{
|
|
449
|
+
SELECT
|
|
450
|
+
#{lowercase_schema_reflection_sql('o.name')} AS [table_name],
|
|
451
|
+
#{lowercase_schema_reflection_sql('c.name')} AS [name],
|
|
452
|
+
t.name AS [type],
|
|
453
|
+
d.definition AS [default_value],
|
|
454
|
+
CASE
|
|
455
|
+
WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint')
|
|
456
|
+
THEN c.scale
|
|
457
|
+
END AS [numeric_scale],
|
|
458
|
+
CASE
|
|
459
|
+
WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint', 'real', 'float')
|
|
460
|
+
THEN c.precision
|
|
461
|
+
END AS [numeric_precision],
|
|
462
|
+
CASE
|
|
463
|
+
WHEN t.name IN ('date', 'datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'time')
|
|
464
|
+
THEN c.scale
|
|
465
|
+
END AS [datetime_precision],
|
|
466
|
+
c.collation_name AS [collation],
|
|
467
|
+
ROW_NUMBER() OVER (ORDER BY c.column_id) AS [ordinal_position],
|
|
468
|
+
CASE
|
|
469
|
+
WHEN t.name IN ('nchar', 'nvarchar') AND c.max_length > 0
|
|
470
|
+
THEN c.max_length / 2
|
|
471
|
+
ELSE c.max_length
|
|
472
|
+
END AS [length],
|
|
473
|
+
CASE c.is_nullable
|
|
474
|
+
WHEN 1
|
|
475
|
+
THEN 1
|
|
476
|
+
END AS [is_nullable],
|
|
477
|
+
CASE
|
|
478
|
+
WHEN ic.object_id IS NOT NULL
|
|
479
|
+
THEN 1
|
|
480
|
+
END AS [is_primary],
|
|
481
|
+
c.is_identity AS [is_identity]
|
|
482
|
+
FROM #{database}.sys.columns c
|
|
483
|
+
INNER JOIN #{database}.sys.objects o
|
|
484
|
+
ON c.object_id = o.object_id
|
|
485
|
+
INNER JOIN #{database}.sys.schemas s
|
|
486
|
+
ON o.schema_id = s.schema_id
|
|
487
|
+
INNER JOIN #{database}.sys.types t
|
|
488
|
+
ON c.system_type_id = t.system_type_id
|
|
489
|
+
AND c.user_type_id = t.user_type_id
|
|
490
|
+
LEFT OUTER JOIN #{database}.sys.default_constraints d
|
|
491
|
+
ON c.object_id = d.parent_object_id
|
|
492
|
+
AND c.default_object_id = d.object_id
|
|
493
|
+
LEFT OUTER JOIN #{database}.sys.key_constraints k
|
|
494
|
+
ON c.object_id = k.parent_object_id
|
|
495
|
+
AND k.type = 'PK'
|
|
496
|
+
LEFT OUTER JOIN #{database}.sys.index_columns ic
|
|
497
|
+
ON k.parent_object_id = ic.object_id
|
|
498
|
+
AND k.unique_index_id = ic.index_id
|
|
499
|
+
AND c.column_id = ic.column_id
|
|
500
|
+
WHERE
|
|
501
|
+
o.name = #{object_name}
|
|
502
|
+
AND s.name = #{schema_name}
|
|
503
|
+
ORDER BY
|
|
504
|
+
c.column_id
|
|
505
|
+
}.gsub(/[ \t\r\n]+/, " ").strip
|
|
506
|
+
end
|
|
507
|
+
|
|
461
508
|
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)}'",
|
|
509
|
+
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
510
|
constraints.each do |constraint|
|
|
464
511
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
|
|
465
512
|
end
|
|
@@ -467,8 +514,8 @@ module ActiveRecord
|
|
|
467
514
|
|
|
468
515
|
def remove_default_constraint(table_name, column_name)
|
|
469
516
|
# 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[
|
|
517
|
+
execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row|
|
|
518
|
+
row["constraint_type"] == "DEFAULT on column #{column_name}"
|
|
472
519
|
end.each do |row|
|
|
473
520
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
|
|
474
521
|
end
|
|
@@ -484,12 +531,12 @@ module ActiveRecord
|
|
|
484
531
|
|
|
485
532
|
def get_table_name(sql)
|
|
486
533
|
tn = if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)(\s+INTO)?\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
534
|
+
Regexp.last_match[3] || Regexp.last_match[4]
|
|
535
|
+
elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
|
|
536
|
+
Regexp.last_match[1]
|
|
537
|
+
else
|
|
538
|
+
nil
|
|
539
|
+
end
|
|
493
540
|
SQLServer::Utils.extract_identifiers(tn).object
|
|
494
541
|
end
|
|
495
542
|
|
|
@@ -505,22 +552,22 @@ module ActiveRecord
|
|
|
505
552
|
|
|
506
553
|
def view_table_name(table_name)
|
|
507
554
|
view_info = view_information(table_name)
|
|
508
|
-
view_info ? get_table_name(view_info[
|
|
555
|
+
view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
|
509
556
|
end
|
|
510
557
|
|
|
511
558
|
def view_information(table_name)
|
|
512
559
|
@view_information ||= {}
|
|
513
560
|
@view_information[table_name] ||= begin
|
|
514
561
|
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)}",
|
|
562
|
+
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA"
|
|
516
563
|
if view_info
|
|
517
564
|
view_info = view_info.with_indifferent_access
|
|
518
565
|
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
|
|
519
566
|
view_info[:VIEW_DEFINITION] = begin
|
|
520
|
-
select_values("EXEC sp_helptext #{identifier.object_quoted}",
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
567
|
+
select_values("EXEC sp_helptext #{identifier.object_quoted}", "SCHEMA").join
|
|
568
|
+
rescue
|
|
569
|
+
warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
|
|
570
|
+
nil
|
|
524
571
|
end
|
|
525
572
|
end
|
|
526
573
|
end
|
|
@@ -531,14 +578,14 @@ module ActiveRecord
|
|
|
531
578
|
def views_real_column_name(table_name, column_name)
|
|
532
579
|
view_definition = view_information(table_name)[:VIEW_DEFINITION]
|
|
533
580
|
return column_name unless view_definition
|
|
581
|
+
|
|
534
582
|
match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
|
|
535
583
|
match_data ? match_data[1] : column_name
|
|
536
584
|
end
|
|
537
585
|
|
|
538
|
-
def create_table_definition(*args)
|
|
539
|
-
SQLServer::TableDefinition.new(*args)
|
|
586
|
+
def create_table_definition(*args, **options)
|
|
587
|
+
SQLServer::TableDefinition.new(self, *args, **options)
|
|
540
588
|
end
|
|
541
|
-
|
|
542
589
|
end
|
|
543
590
|
end
|
|
544
591
|
end
|