activerecord-sqlserver-adapter 8.0.10 → 8.1.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/.devcontainer/Dockerfile +1 -1
- data/.github/workflows/ci.yml +34 -3
- data/CHANGELOG.md +14 -68
- data/Dockerfile.ci +1 -1
- data/Gemfile +7 -9
- data/Guardfile +2 -2
- data/README.md +33 -13
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/activerecord-sqlserver-adapter.gemspec +15 -16
- data/compose.ci.yaml +8 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +1 -2
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain_subscriber.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/finder_methods.rb +4 -4
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +118 -83
- data/lib/active_record/connection_adapters/sqlserver/database_tasks.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +7 -7
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +24 -12
- data/lib/active_record/connection_adapters/sqlserver/schema_dumper.rb +17 -8
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +162 -156
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +2 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +5 -5
- data/lib/active_record/connection_adapters/sqlserver/sql_type_metadata.rb +2 -7
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/type/data.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +3 -3
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +3 -4
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +4 -6
- data/lib/active_record/connection_adapters/sqlserver/type/time_value_fractional.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +0 -2
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +10 -12
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +118 -66
- data/lib/active_record/connection_adapters/sqlserver_column.rb +17 -9
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +5 -5
- data/lib/arel/visitors/sqlserver.rb +55 -26
- data/test/cases/active_schema_test_sqlserver.rb +45 -23
- data/test/cases/adapter_test_sqlserver.rb +72 -59
- data/test/cases/coerced_tests.rb +365 -161
- data/test/cases/column_test_sqlserver.rb +328 -316
- data/test/cases/connection_test_sqlserver.rb +15 -11
- data/test/cases/enum_test_sqlserver.rb +8 -9
- data/test/cases/execute_procedure_test_sqlserver.rb +1 -1
- data/test/cases/fetch_test_sqlserver.rb +1 -1
- data/test/cases/helper_sqlserver.rb +7 -3
- data/test/cases/index_test_sqlserver.rb +8 -6
- data/test/cases/insert_all_test_sqlserver.rb +3 -28
- data/test/cases/json_test_sqlserver.rb +8 -8
- data/test/cases/lateral_test_sqlserver.rb +2 -2
- data/test/cases/migration_test_sqlserver.rb +12 -12
- data/test/cases/pessimistic_locking_test_sqlserver.rb +6 -6
- data/test/cases/primary_keys_test_sqlserver.rb +4 -4
- data/test/cases/rake_test_sqlserver.rb +15 -7
- data/test/cases/schema_dumper_test_sqlserver.rb +109 -113
- data/test/cases/schema_test_sqlserver.rb +7 -7
- data/test/cases/transaction_test_sqlserver.rb +6 -8
- data/test/cases/trigger_test_sqlserver.rb +1 -1
- data/test/cases/utils_test_sqlserver.rb +3 -3
- data/test/cases/view_test_sqlserver.rb +12 -8
- data/test/cases/virtual_column_test_sqlserver.rb +113 -0
- data/test/migrations/create_clients_and_change_column_collation.rb +2 -2
- data/test/models/sqlserver/edge_schema.rb +2 -2
- data/test/schema/sqlserver_specific_schema.rb +49 -37
- data/test/support/coerceable_test_sqlserver.rb +10 -10
- data/test/support/connection_reflection.rb +0 -5
- data/test/support/core_ext/backtrace_cleaner.rb +36 -0
- data/test/support/query_assertions.rb +6 -6
- data/test/support/rake_helpers.rb +6 -10
- metadata +12 -107
|
@@ -6,56 +6,52 @@ require "stringio"
|
|
|
6
6
|
class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
7
7
|
before { all_tables }
|
|
8
8
|
|
|
9
|
-
let(:all_tables)
|
|
10
|
-
let(:schema)
|
|
9
|
+
let(:all_tables) { ActiveRecord::Base.lease_connection.tables }
|
|
10
|
+
let(:schema) { @generated_schema }
|
|
11
11
|
|
|
12
12
|
it "sst_datatypes" do
|
|
13
13
|
generate_schema_for_table "sst_datatypes"
|
|
14
14
|
|
|
15
|
-
assert_line :bigint,
|
|
16
|
-
assert_line :int,
|
|
17
|
-
assert_line :smallint,
|
|
18
|
-
assert_line :tinyint,
|
|
19
|
-
assert_line :bit,
|
|
20
|
-
assert_line :decimal_9_2,
|
|
21
|
-
assert_line :numeric_18_0,
|
|
22
|
-
assert_line :numeric_36_2,
|
|
23
|
-
assert_line :money,
|
|
24
|
-
assert_line :smallmoney,
|
|
15
|
+
assert_line :bigint, type: "bigint", default: 42
|
|
16
|
+
assert_line :int, type: "integer", default: 42
|
|
17
|
+
assert_line :smallint, type: "integer", limit: 2, default: 42
|
|
18
|
+
assert_line :tinyint, type: "integer", limit: 1, default: 42
|
|
19
|
+
assert_line :bit, type: "boolean", default: true
|
|
20
|
+
assert_line :decimal_9_2, type: "decimal", precision: 9, scale: 2, default: 12345.01
|
|
21
|
+
assert_line :numeric_18_0, type: "decimal", precision: 18, default: 191
|
|
22
|
+
assert_line :numeric_36_2, type: "decimal", precision: 36, scale: 2, default: 12345678901234567890.01
|
|
23
|
+
assert_line :money, type: "money", precision: 19, scale: 4, default: 4.2
|
|
24
|
+
assert_line :smallmoney, type: "smallmoney", precision: 10, scale: 4, default: 4.2
|
|
25
25
|
# Approximate Numerics
|
|
26
|
-
assert_line :float,
|
|
27
|
-
assert_line :real,
|
|
26
|
+
assert_line :float, type: "float", default: 123.00000001
|
|
27
|
+
assert_line :real, type: "real", default: 123.45
|
|
28
28
|
# Date and Time
|
|
29
|
-
assert_line :date,
|
|
30
|
-
assert_line :datetime,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
assert_line :
|
|
37
|
-
|
|
38
|
-
assert_line :time_7, type: "time", precision: 7, default: "04:20:00.2883215"
|
|
39
|
-
assert_line :time_2, type: "time", precision: 2
|
|
40
|
-
assert_line :time_default, type: "time", precision: 7, default: "15:03:42.0621978"
|
|
41
|
-
end
|
|
29
|
+
assert_line :date, type: "date", default: "01-01-0001"
|
|
30
|
+
assert_line :datetime, type: "datetime", precision: nil, default: "01-01-1753 00:00:00.123"
|
|
31
|
+
assert_line :datetime2_7, type: "datetime", precision: 7, default: "12-31-9999 23:59:59.9999999"
|
|
32
|
+
assert_line :datetime2_3, type: "datetime", precision: 3
|
|
33
|
+
assert_line :datetime2_1, type: "datetime", precision: 1
|
|
34
|
+
assert_line :smalldatetime, type: "smalldatetime", default: "01-01-1901 15:45:00.0"
|
|
35
|
+
assert_line :time_7, type: "time", precision: 7, default: "04:20:00.2883215"
|
|
36
|
+
assert_line :time_2, type: "time", precision: 2
|
|
37
|
+
assert_line :time_default, type: "time", precision: 7, default: "15:03:42.0621978"
|
|
42
38
|
# Character Strings
|
|
43
|
-
assert_line :char_10,
|
|
44
|
-
assert_line :varchar_50,
|
|
45
|
-
assert_line :varchar_max,
|
|
46
|
-
assert_line :text,
|
|
39
|
+
assert_line :char_10, type: "char", limit: 10, default: "1234567890"
|
|
40
|
+
assert_line :varchar_50, type: "varchar", limit: 50, default: "test varchar_50"
|
|
41
|
+
assert_line :varchar_max, type: "varchar_max", default: "test varchar_max"
|
|
42
|
+
assert_line :text, type: "text_basic", default: "test text"
|
|
47
43
|
# Unicode Character Strings
|
|
48
|
-
assert_line :nchar_10,
|
|
49
|
-
assert_line :nvarchar_50,
|
|
50
|
-
assert_line :nvarchar_max,
|
|
51
|
-
assert_line :ntext,
|
|
44
|
+
assert_line :nchar_10, type: "nchar", limit: 10, default: "12345678åå"
|
|
45
|
+
assert_line :nvarchar_50, type: "string", limit: 50, default: "test nvarchar_50 åå"
|
|
46
|
+
assert_line :nvarchar_max, type: "text", default: "test nvarchar_max åå"
|
|
47
|
+
assert_line :ntext, type: "ntext", default: "test ntext åå"
|
|
52
48
|
# Binary Strings
|
|
53
|
-
assert_line :binary_49,
|
|
54
|
-
assert_line :varbinary_49,
|
|
55
|
-
assert_line :varbinary_max,
|
|
49
|
+
assert_line :binary_49, type: "binary_basic", limit: 49
|
|
50
|
+
assert_line :varbinary_49, type: "varbinary", limit: 49
|
|
51
|
+
assert_line :varbinary_max, type: "binary"
|
|
56
52
|
# Other Data Types
|
|
57
|
-
assert_line :uniqueidentifier,
|
|
58
|
-
assert_line :timestamp,
|
|
53
|
+
assert_line :uniqueidentifier, type: "uuid", default: -> { "newid()" }
|
|
54
|
+
assert_line :timestamp, type: "ss_timestamp"
|
|
59
55
|
end
|
|
60
56
|
|
|
61
57
|
it "sst_datatypes_migration" do
|
|
@@ -63,80 +59,80 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
63
59
|
generate_schema_for_table "sst_datatypes_migration"
|
|
64
60
|
|
|
65
61
|
# Simple Rails conventions
|
|
66
|
-
_(columns["integer_col"].sql_type).must_equal
|
|
67
|
-
_(columns["bigint_col"].sql_type).must_equal
|
|
68
|
-
_(columns["boolean_col"].sql_type).must_equal
|
|
69
|
-
_(columns["decimal_col"].sql_type).must_equal
|
|
70
|
-
_(columns["float_col"].sql_type).must_equal
|
|
71
|
-
_(columns["string_col"].sql_type).must_equal
|
|
72
|
-
_(columns["text_col"].sql_type).must_equal
|
|
62
|
+
_(columns["integer_col"].sql_type).must_equal "int(4)"
|
|
63
|
+
_(columns["bigint_col"].sql_type).must_equal "bigint(8)"
|
|
64
|
+
_(columns["boolean_col"].sql_type).must_equal "bit"
|
|
65
|
+
_(columns["decimal_col"].sql_type).must_equal "decimal(18,0)"
|
|
66
|
+
_(columns["float_col"].sql_type).must_equal "float"
|
|
67
|
+
_(columns["string_col"].sql_type).must_equal "nvarchar(4000)"
|
|
68
|
+
_(columns["text_col"].sql_type).must_equal "nvarchar(max)"
|
|
73
69
|
_(columns["datetime_nil_precision_col"].sql_type).must_equal "datetime"
|
|
74
|
-
_(columns["datetime_col"].sql_type).must_equal
|
|
75
|
-
_(columns["timestamp_col"].sql_type).must_equal
|
|
76
|
-
_(columns["time_col"].sql_type).must_equal
|
|
77
|
-
_(columns["date_col"].sql_type).must_equal
|
|
78
|
-
_(columns["binary_col"].sql_type).must_equal
|
|
79
|
-
|
|
80
|
-
assert_line :integer_col,
|
|
81
|
-
assert_line :bigint_col,
|
|
82
|
-
assert_line :boolean_col,
|
|
83
|
-
assert_line :decimal_col,
|
|
84
|
-
assert_line :float_col,
|
|
85
|
-
assert_line :string_col,
|
|
86
|
-
assert_line :text_col,
|
|
87
|
-
assert_line :datetime_nil_precision_col, type: "datetime",
|
|
88
|
-
assert_line :datetime_col,
|
|
89
|
-
assert_line :datetime_col,
|
|
90
|
-
assert_line :timestamp_col,
|
|
91
|
-
assert_line :time_col,
|
|
92
|
-
assert_line :date_col,
|
|
93
|
-
assert_line :binary_col,
|
|
70
|
+
_(columns["datetime_col"].sql_type).must_equal "datetime2(6)"
|
|
71
|
+
_(columns["timestamp_col"].sql_type).must_equal "datetime2(6)"
|
|
72
|
+
_(columns["time_col"].sql_type).must_equal "time(7)"
|
|
73
|
+
_(columns["date_col"].sql_type).must_equal "date"
|
|
74
|
+
_(columns["binary_col"].sql_type).must_equal "varbinary(max)"
|
|
75
|
+
|
|
76
|
+
assert_line :integer_col, type: "integer"
|
|
77
|
+
assert_line :bigint_col, type: "bigint"
|
|
78
|
+
assert_line :boolean_col, type: "boolean"
|
|
79
|
+
assert_line :decimal_col, type: "decimal", precision: 18
|
|
80
|
+
assert_line :float_col, type: "float"
|
|
81
|
+
assert_line :string_col, type: "string"
|
|
82
|
+
assert_line :text_col, type: "text"
|
|
83
|
+
assert_line :datetime_nil_precision_col, type: "datetime", precision: nil
|
|
84
|
+
assert_line :datetime_col, type: "datetime"
|
|
85
|
+
assert_line :datetime_col, type: "datetime"
|
|
86
|
+
assert_line :timestamp_col, type: "datetime"
|
|
87
|
+
assert_line :time_col, type: "time", precision: 7
|
|
88
|
+
assert_line :date_col, type: "date"
|
|
89
|
+
assert_line :binary_col, type: "binary"
|
|
94
90
|
|
|
95
91
|
# Our type methods.
|
|
96
|
-
_(columns["real_col"].sql_type).must_equal
|
|
97
|
-
_(columns["money_col"].sql_type).must_equal
|
|
98
|
-
_(columns["smalldatetime_col"].sql_type).must_equal
|
|
99
|
-
_(columns["datetime2_col"].sql_type).must_equal
|
|
100
|
-
_(columns["datetimeoffset"].sql_type).must_equal
|
|
101
|
-
_(columns["smallmoney_col"].sql_type).must_equal
|
|
102
|
-
_(columns["char_col"].sql_type).must_equal
|
|
103
|
-
_(columns["varchar_col"].sql_type).must_equal
|
|
104
|
-
_(columns["text_basic_col"].sql_type).must_equal
|
|
105
|
-
_(columns["nchar_col"].sql_type).must_equal
|
|
106
|
-
_(columns["ntext_col"].sql_type).must_equal
|
|
107
|
-
_(columns["binary_basic_col"].sql_type).must_equal
|
|
92
|
+
_(columns["real_col"].sql_type).must_equal "real"
|
|
93
|
+
_(columns["money_col"].sql_type).must_equal "money"
|
|
94
|
+
_(columns["smalldatetime_col"].sql_type).must_equal "smalldatetime"
|
|
95
|
+
_(columns["datetime2_col"].sql_type).must_equal "datetime2(7)"
|
|
96
|
+
_(columns["datetimeoffset"].sql_type).must_equal "datetimeoffset(7)"
|
|
97
|
+
_(columns["smallmoney_col"].sql_type).must_equal "smallmoney"
|
|
98
|
+
_(columns["char_col"].sql_type).must_equal "char(1)"
|
|
99
|
+
_(columns["varchar_col"].sql_type).must_equal "varchar(8000)"
|
|
100
|
+
_(columns["text_basic_col"].sql_type).must_equal "text"
|
|
101
|
+
_(columns["nchar_col"].sql_type).must_equal "nchar(1)"
|
|
102
|
+
_(columns["ntext_col"].sql_type).must_equal "ntext"
|
|
103
|
+
_(columns["binary_basic_col"].sql_type).must_equal "binary(1)"
|
|
108
104
|
_(columns["binary_basic_16_col"].sql_type).must_equal "binary(16)"
|
|
109
|
-
_(columns["varbinary_col"].sql_type).must_equal
|
|
110
|
-
_(columns["uuid_col"].sql_type).must_equal
|
|
111
|
-
_(columns["sstimestamp_col"].sql_type).must_equal
|
|
112
|
-
_(columns["json_col"].sql_type).must_equal
|
|
113
|
-
|
|
114
|
-
assert_line :real_col,
|
|
115
|
-
assert_line :money_col,
|
|
116
|
-
assert_line :smalldatetime_col,
|
|
117
|
-
assert_line :datetime2_col,
|
|
118
|
-
assert_line :datetimeoffset,
|
|
119
|
-
assert_line :smallmoney_col,
|
|
120
|
-
assert_line :char_col,
|
|
121
|
-
assert_line :varchar_col,
|
|
122
|
-
assert_line :text_basic_col,
|
|
123
|
-
assert_line :nchar_col,
|
|
124
|
-
assert_line :ntext_col,
|
|
125
|
-
assert_line :binary_basic_col,
|
|
126
|
-
assert_line :binary_basic_16_col, type: "binary_basic",
|
|
127
|
-
assert_line :varbinary_col,
|
|
128
|
-
assert_line :uuid_col,
|
|
129
|
-
assert_line :sstimestamp_col,
|
|
130
|
-
assert_line :json_col,
|
|
105
|
+
_(columns["varbinary_col"].sql_type).must_equal "varbinary(8000)"
|
|
106
|
+
_(columns["uuid_col"].sql_type).must_equal "uniqueidentifier"
|
|
107
|
+
_(columns["sstimestamp_col"].sql_type).must_equal "timestamp"
|
|
108
|
+
_(columns["json_col"].sql_type).must_equal "nvarchar(max)"
|
|
109
|
+
|
|
110
|
+
assert_line :real_col, type: "real"
|
|
111
|
+
assert_line :money_col, type: "money", precision: 19, scale: 4
|
|
112
|
+
assert_line :smalldatetime_col, type: "smalldatetime"
|
|
113
|
+
assert_line :datetime2_col, type: "datetime", precision: 7
|
|
114
|
+
assert_line :datetimeoffset, type: "datetimeoffset", precision: 7
|
|
115
|
+
assert_line :smallmoney_col, type: "smallmoney", precision: 10, scale: 4
|
|
116
|
+
assert_line :char_col, type: "char", limit: 1
|
|
117
|
+
assert_line :varchar_col, type: "varchar"
|
|
118
|
+
assert_line :text_basic_col, type: "text_basic"
|
|
119
|
+
assert_line :nchar_col, type: "nchar", limit: 1
|
|
120
|
+
assert_line :ntext_col, type: "ntext"
|
|
121
|
+
assert_line :binary_basic_col, type: "binary_basic", limit: 1
|
|
122
|
+
assert_line :binary_basic_16_col, type: "binary_basic", limit: 16
|
|
123
|
+
assert_line :varbinary_col, type: "varbinary"
|
|
124
|
+
assert_line :uuid_col, type: "uuid"
|
|
125
|
+
assert_line :sstimestamp_col, type: "ss_timestamp", null: false
|
|
126
|
+
assert_line :json_col, type: "text"
|
|
131
127
|
end
|
|
132
128
|
|
|
133
129
|
it "dump column collation" do
|
|
134
|
-
generate_schema_for_table(
|
|
130
|
+
generate_schema_for_table("sst_string_collation")
|
|
135
131
|
|
|
136
132
|
assert_line :string_without_collation, type: "string"
|
|
137
133
|
assert_line :string_default_collation, type: "varchar"
|
|
138
|
-
assert_line :string_with_collation,
|
|
139
|
-
assert_line :varchar_with_collation,
|
|
134
|
+
assert_line :string_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS"
|
|
135
|
+
assert_line :varchar_with_collation, type: "varchar", collation: "SQL_Latin1_General_CP1_CS_AS"
|
|
140
136
|
end
|
|
141
137
|
|
|
142
138
|
# Special Cases
|
|
@@ -145,7 +141,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
145
141
|
generate_schema_for_table("movies") do |output|
|
|
146
142
|
match = output.match(%r{create_table "movies"(.*)do})
|
|
147
143
|
assert_not_nil(match, "non-standard primary key table not found")
|
|
148
|
-
assert_match %r
|
|
144
|
+
assert_match %r{primary_key: "movieid"}, match[1], "non-standard primary key not preserved"
|
|
149
145
|
end
|
|
150
146
|
end
|
|
151
147
|
|
|
@@ -193,7 +189,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
193
189
|
|
|
194
190
|
@generated_schema = stream.string
|
|
195
191
|
yield @generated_schema if block_given?
|
|
196
|
-
@schema_lines =
|
|
192
|
+
@schema_lines = {}
|
|
197
193
|
type_matcher = /\A\s+t\.\w+\s+"(.*?)"[,\n]/
|
|
198
194
|
@generated_schema.each_line do |line|
|
|
199
195
|
next unless line =~ type_matcher
|
|
@@ -216,13 +212,13 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
216
212
|
# Check that the expected and actual option keys.
|
|
217
213
|
expected_options_keys = expected_options.keys
|
|
218
214
|
expected_options_keys.delete(:type)
|
|
219
|
-
_(expected_options_keys.sort).must_equal
|
|
215
|
+
_(expected_options_keys.sort).must_equal line.options.keys.sort, "For column '#{column_name}' expected schema options and actual schema options do not match."
|
|
220
216
|
|
|
221
217
|
# Check the expected and actual option values.
|
|
222
218
|
expected_options.each do |key, expected|
|
|
223
|
-
actual
|
|
219
|
+
actual = (key == :type) ? line.send(:type_method) : line.send(key)
|
|
224
220
|
|
|
225
|
-
message
|
|
221
|
+
message = "#{key.to_s.titleize} of #{expected.inspect} not found in:\n#{line}"
|
|
226
222
|
|
|
227
223
|
if expected.nil?
|
|
228
224
|
_(actual).must_be_nil message
|
|
@@ -242,9 +238,9 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
242
238
|
LINE_PARSER = %r{t\.(\w+)\s+"(.*?)"[,\s+](.*)}
|
|
243
239
|
|
|
244
240
|
attr_reader :line,
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
241
|
+
:type_method,
|
|
242
|
+
:col_name,
|
|
243
|
+
:options
|
|
248
244
|
|
|
249
245
|
def self.option(method_name)
|
|
250
246
|
define_method(method_name) do
|
|
@@ -287,7 +283,7 @@ class SchemaDumperTestSQLServer < ActiveRecord::TestCase
|
|
|
287
283
|
|
|
288
284
|
def parse_options(opts)
|
|
289
285
|
if opts.present?
|
|
290
|
-
eval
|
|
286
|
+
eval("{#{opts}}", binding, __FILE__, __LINE__) # standard:disable Security/Eval
|
|
291
287
|
else
|
|
292
288
|
{}
|
|
293
289
|
end
|
|
@@ -23,7 +23,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
23
23
|
columns = connection.columns("test.sst_schema_identity")
|
|
24
24
|
|
|
25
25
|
assert_equal 2, columns.size
|
|
26
|
-
assert_equal 1, columns.
|
|
26
|
+
assert_equal 1, columns.count { |c| c.is_identity? }
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
it "read only column properties for table in specific schema" do
|
|
@@ -34,9 +34,9 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
34
34
|
assert_equal 7, test_columns.size
|
|
35
35
|
assert_equal 2, dbo_columns.size
|
|
36
36
|
assert_equal 2, columns.size
|
|
37
|
-
assert_equal 1, test_columns.
|
|
38
|
-
assert_equal 1, dbo_columns.
|
|
39
|
-
assert_equal 1, columns.
|
|
37
|
+
assert_equal 1, test_columns.count { |c| c.is_identity? }
|
|
38
|
+
assert_equal 1, dbo_columns.count { |c| c.is_identity? }
|
|
39
|
+
assert_equal 1, columns.count { |c| c.is_identity? }
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
it "return correct varchar and nvarchar column limit length when table is in non-dbo schema" do
|
|
@@ -50,7 +50,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
describe "parsing table name from raw SQL" do
|
|
53
|
-
describe
|
|
53
|
+
describe "SELECT statements" do
|
|
54
54
|
it do
|
|
55
55
|
assert_equal "[sst_schema_columns]", connection.send(:get_raw_table_name, "SELECT [sst_schema_columns].[id] FROM [sst_schema_columns]")
|
|
56
56
|
end
|
|
@@ -72,7 +72,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
describe
|
|
75
|
+
describe "INSERT statements" do
|
|
76
76
|
it do
|
|
77
77
|
assert_equal "[dashboards]", connection.send(:get_raw_table_name, "INSERT INTO [dashboards] DEFAULT VALUES; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
|
|
78
78
|
end
|
|
@@ -124,7 +124,7 @@ class SchemaTestSQLServer < ActiveRecord::TestCase
|
|
|
124
124
|
end
|
|
125
125
|
end
|
|
126
126
|
|
|
127
|
-
describe
|
|
127
|
+
describe "CREATE VIEW statements" do
|
|
128
128
|
it do
|
|
129
129
|
assert_equal "test_table_as", connection.send(:get_raw_table_name, "CREATE VIEW test_views ( test_table_a_id, test_table_b_id ) AS SELECT test_table_as.id as test_table_a_id, test_table_bs.id as test_table_b_id FROM (test_table_as with(nolock) LEFT JOIN test_table_bs with(nolock) ON (test_table_as.id = test_table_bs.test_table_a_id))")
|
|
130
130
|
end
|
|
@@ -18,17 +18,15 @@ class TransactionTestSQLServer < ActiveRecord::TestCase
|
|
|
18
18
|
end
|
|
19
19
|
|
|
20
20
|
it "allow nested transactions to totally rollback" do
|
|
21
|
-
|
|
21
|
+
Ship.transaction do
|
|
22
|
+
Ship.create! name: "Black Pearl"
|
|
22
23
|
Ship.transaction do
|
|
23
|
-
Ship.create! name: "
|
|
24
|
-
|
|
25
|
-
Ship.create! name: "Flying Dutchman"
|
|
26
|
-
raise "HELL"
|
|
27
|
-
end
|
|
24
|
+
Ship.create! name: "Flying Dutchman"
|
|
25
|
+
raise "HELL"
|
|
28
26
|
end
|
|
29
|
-
rescue Exception
|
|
30
|
-
assert_no_ships
|
|
31
27
|
end
|
|
28
|
+
rescue
|
|
29
|
+
assert_no_ships
|
|
32
30
|
end
|
|
33
31
|
|
|
34
32
|
it "can use an isolation level and reverts back to starting isolation level" do
|
|
@@ -40,7 +40,7 @@ class SQLServerTriggerTest < ActiveRecord::TestCase
|
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
it "can insert into a table with composite pk with different data type with output inserted - with a hash setting for table name" do
|
|
43
|
-
exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger_with_different_data_type"] = {
|
|
43
|
+
exclude_output_inserted_table_names["sst_table_with_composite_pk_trigger_with_different_data_type"] = {pk_col_one: "uniqueidentifier", pk_col_two: "int"}
|
|
44
44
|
assert SSTestTriggerHistory.all.empty?
|
|
45
45
|
obj = SSTestTriggerCompositePkWithDefferentDataType.create! pk_col_two: 123, event_name: "test trigger"
|
|
46
46
|
_(obj.event_name).must_equal "test trigger"
|
|
@@ -44,9 +44,9 @@ class UtilsTestSQLServer < ActiveRecord::TestCase
|
|
|
44
44
|
]
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
let(:server_names)
|
|
47
|
+
let(:server_names) { valid_names.partition { |name| name =~ /server/ } }
|
|
48
48
|
let(:database_names) { valid_names.partition { |name| name =~ /database/ } }
|
|
49
|
-
let(:schema_names)
|
|
49
|
+
let(:schema_names) { valid_names.partition { |name| name =~ /schema/ } }
|
|
50
50
|
|
|
51
51
|
it "extracts and returns #object identifier unquoted by default or quoted as needed" do
|
|
52
52
|
valid_names.each do |n|
|
|
@@ -61,7 +61,7 @@ class UtilsTestSQLServer < ActiveRecord::TestCase
|
|
|
61
61
|
present, blank = send(:"#{part}_names")
|
|
62
62
|
present.each do |n|
|
|
63
63
|
name = extract_identifiers(n)
|
|
64
|
-
_(name.send(:"#{part}")).must_equal
|
|
64
|
+
_(name.send(:"#{part}")).must_equal part.to_s, "With #{n.inspect} for ##{part} method"
|
|
65
65
|
_(name.send(:"#{part}_quoted")).must_equal "[#{part}]", "With #{n.inspect} for ##{part}_quoted method"
|
|
66
66
|
end
|
|
67
67
|
blank.each do |n|
|
|
@@ -5,14 +5,18 @@ require "cases/helper_sqlserver"
|
|
|
5
5
|
class ViewTestSQLServer < ActiveRecord::TestCase
|
|
6
6
|
let(:connection) { ActiveRecord::Base.lease_connection }
|
|
7
7
|
|
|
8
|
-
describe
|
|
8
|
+
describe "view with default values" do
|
|
9
9
|
before do
|
|
10
|
-
|
|
10
|
+
begin
|
|
11
|
+
connection.drop_table :view_casing_table
|
|
12
|
+
rescue
|
|
13
|
+
nil
|
|
14
|
+
end
|
|
11
15
|
connection.create_table :view_casing_table, force: true do |t|
|
|
12
|
-
t.boolean :Default_Falsey,
|
|
13
|
-
t.boolean :Default_Truthy,
|
|
14
|
-
t.string
|
|
15
|
-
t.string
|
|
16
|
+
t.boolean :Default_Falsey, null: false, default: false
|
|
17
|
+
t.boolean :Default_Truthy, null: false, default: true
|
|
18
|
+
t.string :default_string_null, null: true, default: nil
|
|
19
|
+
t.string :default_string, null: false, default: "abc"
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
connection.execute("DROP VIEW IF EXISTS view_casing_table_view;")
|
|
@@ -36,14 +40,14 @@ class ViewTestSQLServer < ActiveRecord::TestCase
|
|
|
36
40
|
assert_equal false, obj.falsey
|
|
37
41
|
assert_equal true, obj.truthy
|
|
38
42
|
assert_equal "abc", obj.s
|
|
39
|
-
assert_nil
|
|
43
|
+
assert_nil obj.s_null
|
|
40
44
|
assert_equal 0, klass.count
|
|
41
45
|
|
|
42
46
|
obj.save!
|
|
43
47
|
assert_equal false, obj.falsey
|
|
44
48
|
assert_equal true, obj.truthy
|
|
45
49
|
assert_equal "abc", obj.s
|
|
46
|
-
assert_nil
|
|
50
|
+
assert_nil obj.s_null
|
|
47
51
|
assert_equal 1, klass.count
|
|
48
52
|
end
|
|
49
53
|
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cases/helper_sqlserver"
|
|
4
|
+
require "support/schema_dumping_helper"
|
|
5
|
+
|
|
6
|
+
class VirtualColumnTestSQLServer < ActiveRecord::TestCase
|
|
7
|
+
include SchemaDumpingHelper
|
|
8
|
+
|
|
9
|
+
class VirtualColumn < ActiveRecord::Base
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
@connection = ActiveRecord::Base.lease_connection
|
|
14
|
+
@connection.create_table :virtual_columns, force: true do |t|
|
|
15
|
+
t.string :name
|
|
16
|
+
t.virtual :upper_name, as: "UPPER(name)", stored: true
|
|
17
|
+
t.virtual :lower_name, as: "LOWER(name)", stored: false
|
|
18
|
+
t.virtual :octet_name, as: "LEN(name)"
|
|
19
|
+
t.virtual :mutated_name, as: "REPLACE(name, 'l', 'L')"
|
|
20
|
+
t.integer :column1
|
|
21
|
+
end
|
|
22
|
+
VirtualColumn.create(name: "Rails", column1: 10)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def teardown
|
|
26
|
+
@connection.drop_table :virtual_columns, if_exists: true
|
|
27
|
+
VirtualColumn.reset_column_information
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_virtual_column_with_full_inserts
|
|
31
|
+
partial_inserts_was = VirtualColumn.partial_inserts
|
|
32
|
+
VirtualColumn.partial_inserts = false
|
|
33
|
+
assert_nothing_raised do
|
|
34
|
+
VirtualColumn.create!(name: "Rails")
|
|
35
|
+
end
|
|
36
|
+
ensure
|
|
37
|
+
VirtualColumn.partial_inserts = partial_inserts_was
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_stored_column
|
|
41
|
+
column = VirtualColumn.columns_hash["upper_name"]
|
|
42
|
+
assert_predicate column, :virtual?
|
|
43
|
+
assert_predicate column, :virtual_stored?
|
|
44
|
+
assert_equal "RAILS", VirtualColumn.take.upper_name
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_explicit_virtual_column
|
|
48
|
+
column = VirtualColumn.columns_hash["lower_name"]
|
|
49
|
+
assert_predicate column, :virtual?
|
|
50
|
+
assert_not_predicate column, :virtual_stored?
|
|
51
|
+
assert_equal "rails", VirtualColumn.take.lower_name
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_implicit_virtual_column
|
|
55
|
+
column = VirtualColumn.columns_hash["octet_name"]
|
|
56
|
+
assert_predicate column, :virtual?
|
|
57
|
+
assert_not_predicate column, :virtual_stored?
|
|
58
|
+
assert_equal 5, VirtualColumn.take.octet_name
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def test_virtual_column_with_comma_in_definition
|
|
62
|
+
column = VirtualColumn.columns_hash["mutated_name"]
|
|
63
|
+
assert_predicate column, :virtual?
|
|
64
|
+
assert_not_predicate column, :virtual_stored?
|
|
65
|
+
assert_not_nil column.default_function
|
|
66
|
+
assert_equal "RaiLs", VirtualColumn.take.mutated_name
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def test_change_table_with_stored_generated_column
|
|
70
|
+
@connection.change_table :virtual_columns do |t|
|
|
71
|
+
t.virtual :decr_column1, as: "column1 - 1", stored: true
|
|
72
|
+
end
|
|
73
|
+
VirtualColumn.reset_column_information
|
|
74
|
+
column = VirtualColumn.columns_hash["decr_column1"]
|
|
75
|
+
assert_predicate column, :virtual?
|
|
76
|
+
assert_predicate column, :virtual_stored?
|
|
77
|
+
assert_equal 9, VirtualColumn.take.decr_column1
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_change_table_with_explicit_virtual_generated_column
|
|
81
|
+
@connection.change_table :virtual_columns do |t|
|
|
82
|
+
t.virtual :incr_column1, as: "column1 + 1", stored: false
|
|
83
|
+
end
|
|
84
|
+
VirtualColumn.reset_column_information
|
|
85
|
+
column = VirtualColumn.columns_hash["incr_column1"]
|
|
86
|
+
assert_predicate column, :virtual?
|
|
87
|
+
assert_not_predicate column, :virtual_stored?
|
|
88
|
+
assert_equal 11, VirtualColumn.take.incr_column1
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def test_change_table_with_implicit_virtual_generated_column
|
|
92
|
+
@connection.change_table :virtual_columns do |t|
|
|
93
|
+
t.virtual :sqr_column1, as: "power(column1, 2)"
|
|
94
|
+
end
|
|
95
|
+
VirtualColumn.reset_column_information
|
|
96
|
+
column = VirtualColumn.columns_hash["sqr_column1"]
|
|
97
|
+
assert_predicate column, :virtual?
|
|
98
|
+
assert_not_predicate column, :virtual_stored?
|
|
99
|
+
assert_equal 100, VirtualColumn.take.sqr_column1
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def test_schema_dumping
|
|
103
|
+
output = dump_table_schema("virtual_columns")
|
|
104
|
+
assert_match(/t\.virtual\s+"lower_name",\s+as: "\(lower\(\[name\]\)\)", stored: false$/i, output)
|
|
105
|
+
assert_match(/t\.virtual\s+"upper_name",\s+as: "\(upper\(\[name\]\)\)", stored: true$/i, output)
|
|
106
|
+
assert_match(/t\.virtual\s+"octet_name",\s+as: "\(len\(\[name\]\)\)", stored: false$/i, output)
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def test_build_fixture_sql
|
|
110
|
+
fixtures = ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT, :virtual_columns).first
|
|
111
|
+
assert_equal 2, fixtures.size
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -9,8 +9,8 @@ class CreateClientsAndChangeColumnCollation < ActiveRecord::Migration[5.2]
|
|
|
9
9
|
t.timestamps
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
change_column :clients, :name, :string, collation:
|
|
13
|
-
change_column :clients, :code, :string, collation:
|
|
12
|
+
change_column :clients, :name, :string, collation: "SQL_Latin1_General_CP1_CS_AS"
|
|
13
|
+
change_column :clients, :code, :string, collation: "SQL_Latin1_General_CP1_CI_AS"
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def down
|
|
@@ -4,10 +4,10 @@ class SSTestEdgeSchema < ActiveRecord::Base
|
|
|
4
4
|
self.table_name = "sst_edge_schemas"
|
|
5
5
|
|
|
6
6
|
def with_spaces
|
|
7
|
-
read_attribute :
|
|
7
|
+
read_attribute :"with spaces"
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def with_spaces=(value)
|
|
11
|
-
write_attribute :
|
|
11
|
+
write_attribute :"with spaces", value
|
|
12
12
|
end
|
|
13
13
|
end
|