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