activerecord-sqlserver-adapter 5.2.1 → 6.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/.gitignore +1 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +6 -8
- data/CHANGELOG.md +38 -24
- data/{Dockerfile → Dockerfile.ci} +1 -1
- data/Gemfile +48 -41
- data/Guardfile +9 -8
- data/README.md +9 -30
- 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 +190 -164
- 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 +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/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/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 +37 -35
- 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 +128 -92
- 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 +58 -24
- data/lib/arel_sqlserver.rb +4 -2
- data/test/appveyor/dbsetup.ps1 +4 -4
- data/test/cases/adapter_test_sqlserver.rb +214 -171
- data/test/cases/change_column_null_test_sqlserver.rb +14 -12
- data/test/cases/coerced_tests.rb +631 -356
- data/test/cases/column_test_sqlserver.rb +283 -284
- 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/migration_test_sqlserver.rb +25 -29
- 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 +107 -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 +22 -22
- 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 +17 -7
|
@@ -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
|
|
|
@@ -280,21 +285,21 @@ module ActiveRecord
|
|
|
280
285
|
|
|
281
286
|
def data_source_sql(name = nil, type: nil)
|
|
282
287
|
scope = quoted_scope name, type: type
|
|
283
|
-
table_name = lowercase_schema_reflection_sql
|
|
288
|
+
table_name = lowercase_schema_reflection_sql "TABLE_NAME"
|
|
284
289
|
sql = "SELECT #{table_name}"
|
|
285
|
-
sql
|
|
286
|
-
sql
|
|
287
|
-
sql
|
|
288
|
-
sql
|
|
289
|
-
sql
|
|
290
|
-
sql
|
|
290
|
+
sql += " FROM INFORMATION_SCHEMA.TABLES WITH (NOLOCK)"
|
|
291
|
+
sql += " WHERE TABLE_CATALOG = DB_NAME()"
|
|
292
|
+
sql += " AND TABLE_SCHEMA = #{quote(scope[:schema])}"
|
|
293
|
+
sql += " AND TABLE_NAME = #{quote(scope[:name])}" if scope[:name]
|
|
294
|
+
sql += " AND TABLE_TYPE = #{quote(scope[:type])}" if scope[:type]
|
|
295
|
+
sql += " ORDER BY #{table_name}"
|
|
291
296
|
sql
|
|
292
297
|
end
|
|
293
298
|
|
|
294
299
|
def quoted_scope(name = nil, type: nil)
|
|
295
300
|
identifier = SQLServer::Utils.extract_identifiers(name)
|
|
296
301
|
{}.tap do |scope|
|
|
297
|
-
scope[:schema] = identifier.schema ||
|
|
302
|
+
scope[:schema] = identifier.schema || "dbo"
|
|
298
303
|
scope[:name] = identifier.object if identifier.object
|
|
299
304
|
scope[:type] = type if type
|
|
300
305
|
end
|
|
@@ -304,37 +309,37 @@ module ActiveRecord
|
|
|
304
309
|
|
|
305
310
|
def initialize_native_database_types
|
|
306
311
|
{
|
|
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:
|
|
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)" }
|
|
338
343
|
}
|
|
339
344
|
end
|
|
340
345
|
|
|
@@ -343,60 +348,14 @@ module ActiveRecord
|
|
|
343
348
|
database = identifier.fully_qualified_database_quoted
|
|
344
349
|
view_exists = view_exists?(table_name)
|
|
345
350
|
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
|
|
351
|
+
|
|
352
|
+
sql = column_definitions_sql(database, identifier)
|
|
353
|
+
|
|
395
354
|
binds = []
|
|
396
355
|
nv128 = SQLServer::Type::UnicodeVarchar.new limit: 128
|
|
397
|
-
binds << Relation::QueryAttribute.new(
|
|
398
|
-
binds << Relation::QueryAttribute.new(
|
|
399
|
-
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)
|
|
400
359
|
results.map do |ci|
|
|
401
360
|
ci = ci.symbolize_keys
|
|
402
361
|
ci[:_type] = ci[:type]
|
|
@@ -427,7 +386,7 @@ module ActiveRecord
|
|
|
427
386
|
WHERE
|
|
428
387
|
c.TABLE_NAME = '#{view_tblnm}'
|
|
429
388
|
AND c.COLUMN_NAME = '#{views_real_column_name(table_name, ci[:name])}'
|
|
430
|
-
}.squish,
|
|
389
|
+
}.squish, "SCHEMA"
|
|
431
390
|
end
|
|
432
391
|
case default
|
|
433
392
|
when nil
|
|
@@ -446,7 +405,7 @@ module ActiveRecord
|
|
|
446
405
|
else ci[:type]
|
|
447
406
|
end
|
|
448
407
|
value = default.match(/\A\((.*)\)\Z/m)[1]
|
|
449
|
-
value = select_value("SELECT CAST(#{value} AS #{type}) AS value",
|
|
408
|
+
value = select_value("SELECT CAST(#{value} AS #{type}) AS value", "SCHEMA")
|
|
450
409
|
[value, nil]
|
|
451
410
|
end
|
|
452
411
|
end
|
|
@@ -458,8 +417,75 @@ module ActiveRecord
|
|
|
458
417
|
end
|
|
459
418
|
end
|
|
460
419
|
|
|
420
|
+
def column_definitions_sql(database, identifier)
|
|
421
|
+
object_name = prepared_statements ? "@0" : quote(identifier.object)
|
|
422
|
+
schema_name = if identifier.schema.blank?
|
|
423
|
+
"schema_name()"
|
|
424
|
+
else
|
|
425
|
+
prepared_statements ? "@1" : quote(identifier.schema)
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
%{
|
|
429
|
+
SELECT
|
|
430
|
+
#{lowercase_schema_reflection_sql('o.name')} AS [table_name],
|
|
431
|
+
#{lowercase_schema_reflection_sql('c.name')} AS [name],
|
|
432
|
+
t.name AS [type],
|
|
433
|
+
d.definition AS [default_value],
|
|
434
|
+
CASE
|
|
435
|
+
WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint')
|
|
436
|
+
THEN c.scale
|
|
437
|
+
END AS [numeric_scale],
|
|
438
|
+
CASE
|
|
439
|
+
WHEN t.name IN ('decimal', 'bigint', 'int', 'money', 'numeric', 'smallint', 'smallmoney', 'tinyint', 'real', 'float')
|
|
440
|
+
THEN c.precision
|
|
441
|
+
END AS [numeric_precision],
|
|
442
|
+
CASE
|
|
443
|
+
WHEN t.name IN ('date', 'datetime', 'datetime2', 'datetimeoffset', 'smalldatetime', 'time')
|
|
444
|
+
THEN c.scale
|
|
445
|
+
END AS [datetime_precision],
|
|
446
|
+
c.collation_name AS [collation],
|
|
447
|
+
ROW_NUMBER() OVER (ORDER BY c.column_id) AS [ordinal_position],
|
|
448
|
+
CASE
|
|
449
|
+
WHEN t.name IN ('nchar', 'nvarchar') AND c.max_length > 0
|
|
450
|
+
THEN c.max_length / 2
|
|
451
|
+
ELSE c.max_length
|
|
452
|
+
END AS [length],
|
|
453
|
+
CASE c.is_nullable
|
|
454
|
+
WHEN 1
|
|
455
|
+
THEN 1
|
|
456
|
+
END AS [is_nullable],
|
|
457
|
+
CASE
|
|
458
|
+
WHEN ic.object_id IS NOT NULL
|
|
459
|
+
THEN 1
|
|
460
|
+
END AS [is_primary],
|
|
461
|
+
c.is_identity AS [is_identity]
|
|
462
|
+
FROM #{database}.sys.columns c
|
|
463
|
+
INNER JOIN #{database}.sys.objects o
|
|
464
|
+
ON c.object_id = o.object_id
|
|
465
|
+
INNER JOIN #{database}.sys.schemas s
|
|
466
|
+
ON o.schema_id = s.schema_id
|
|
467
|
+
INNER JOIN #{database}.sys.types t
|
|
468
|
+
ON c.system_type_id = t.system_type_id
|
|
469
|
+
AND c.user_type_id = t.user_type_id
|
|
470
|
+
LEFT OUTER JOIN #{database}.sys.default_constraints d
|
|
471
|
+
ON c.object_id = d.parent_object_id
|
|
472
|
+
AND c.default_object_id = d.object_id
|
|
473
|
+
LEFT OUTER JOIN #{database}.sys.key_constraints k
|
|
474
|
+
ON c.object_id = k.parent_object_id
|
|
475
|
+
LEFT OUTER JOIN #{database}.sys.index_columns ic
|
|
476
|
+
ON k.parent_object_id = ic.object_id
|
|
477
|
+
AND k.unique_index_id = ic.index_id
|
|
478
|
+
AND c.column_id = ic.column_id
|
|
479
|
+
WHERE
|
|
480
|
+
o.name = #{object_name}
|
|
481
|
+
AND s.name = #{schema_name}
|
|
482
|
+
ORDER BY
|
|
483
|
+
c.column_id
|
|
484
|
+
}.gsub(/[ \t\r\n]+/, " ").strip
|
|
485
|
+
end
|
|
486
|
+
|
|
461
487
|
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)}'",
|
|
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"
|
|
463
489
|
constraints.each do |constraint|
|
|
464
490
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
|
|
465
491
|
end
|
|
@@ -467,8 +493,8 @@ module ActiveRecord
|
|
|
467
493
|
|
|
468
494
|
def remove_default_constraint(table_name, column_name)
|
|
469
495
|
# 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[
|
|
496
|
+
execute_procedure(:sp_helpconstraint, table_name, "nomsg").flatten.select do |row|
|
|
497
|
+
row["constraint_type"] == "DEFAULT on column #{column_name}"
|
|
472
498
|
end.each do |row|
|
|
473
499
|
do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
|
|
474
500
|
end
|
|
@@ -484,12 +510,12 @@ module ActiveRecord
|
|
|
484
510
|
|
|
485
511
|
def get_table_name(sql)
|
|
486
512
|
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
|
-
|
|
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
|
|
493
519
|
SQLServer::Utils.extract_identifiers(tn).object
|
|
494
520
|
end
|
|
495
521
|
|
|
@@ -505,22 +531,22 @@ module ActiveRecord
|
|
|
505
531
|
|
|
506
532
|
def view_table_name(table_name)
|
|
507
533
|
view_info = view_information(table_name)
|
|
508
|
-
view_info ? get_table_name(view_info[
|
|
534
|
+
view_info ? get_table_name(view_info["VIEW_DEFINITION"]) : table_name
|
|
509
535
|
end
|
|
510
536
|
|
|
511
537
|
def view_information(table_name)
|
|
512
538
|
@view_information ||= {}
|
|
513
539
|
@view_information[table_name] ||= begin
|
|
514
540
|
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)}",
|
|
541
|
+
view_info = select_one "SELECT * FROM INFORMATION_SCHEMA.VIEWS WITH (NOLOCK) WHERE TABLE_NAME = #{quote(identifier.object)}", "SCHEMA"
|
|
516
542
|
if view_info
|
|
517
543
|
view_info = view_info.with_indifferent_access
|
|
518
544
|
if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
|
|
519
545
|
view_info[:VIEW_DEFINITION] = begin
|
|
520
|
-
select_values("EXEC sp_helptext #{identifier.object_quoted}",
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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
|
|
524
550
|
end
|
|
525
551
|
end
|
|
526
552
|
end
|
|
@@ -531,14 +557,14 @@ module ActiveRecord
|
|
|
531
557
|
def views_real_column_name(table_name, column_name)
|
|
532
558
|
view_definition = view_information(table_name)[:VIEW_DEFINITION]
|
|
533
559
|
return column_name unless view_definition
|
|
560
|
+
|
|
534
561
|
match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
|
|
535
562
|
match_data ? match_data[1] : column_name
|
|
536
563
|
end
|
|
537
564
|
|
|
538
|
-
def create_table_definition(*args)
|
|
539
|
-
SQLServer::TableDefinition.new(*args)
|
|
565
|
+
def create_table_definition(*args, **options)
|
|
566
|
+
SQLServer::TableDefinition.new(self, *args, **options)
|
|
540
567
|
end
|
|
541
|
-
|
|
542
568
|
end
|
|
543
569
|
end
|
|
544
570
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module SQLServer
|
|
@@ -41,7 +43,7 @@ module ActiveRecord
|
|
|
41
43
|
end
|
|
42
44
|
|
|
43
45
|
def build_separator
|
|
44
|
-
|
|
46
|
+
"+" + @widths.map { |w| "-" * (w + (cell_padding * 2)) }.join("+") + "+"
|
|
45
47
|
end
|
|
46
48
|
|
|
47
49
|
def build_cells(items)
|
|
@@ -54,7 +56,7 @@ module ActiveRecord
|
|
|
54
56
|
|
|
55
57
|
def cast_item(item)
|
|
56
58
|
case item
|
|
57
|
-
when NilClass then
|
|
59
|
+
when NilClass then "NULL"
|
|
58
60
|
when Float then item.to_s.to(9)
|
|
59
61
|
else item.to_s.truncate(max_column_width)
|
|
60
62
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ActiveRecord
|
|
2
4
|
module ConnectionAdapters
|
|
3
5
|
module SQLServer
|
|
@@ -10,7 +12,7 @@ module ActiveRecord
|
|
|
10
12
|
def pp
|
|
11
13
|
xml = @result.rows.first.first
|
|
12
14
|
if defined?(Nokogiri)
|
|
13
|
-
Nokogiri::XML(xml).to_xml indent: 2, encoding:
|
|
15
|
+
Nokogiri::XML(xml).to_xml indent: 2, encoding: "UTF-8"
|
|
14
16
|
else
|
|
15
17
|
xml
|
|
16
18
|
end
|