activerecord-sqlserver-adapter-odbc-extended 8.0.10
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 +7 -0
- data/.github/issue_template.md +22 -0
- data/.github/workflows/ci.yml +32 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +69 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Dockerfile.ci +14 -0
- data/Gemfile +26 -0
- data/LICENSE.txt +21 -0
- data/README.md +104 -0
- data/RUNNING_UNIT_TESTS.md +38 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/activerecord-sqlserver-adapter-odbc-extended.gemspec +34 -0
- data/compose.ci.yaml +15 -0
- data/lib/active_record/connection_adapters/extended_sqlserver_adapter.rb +204 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +41 -0
- data/lib/active_record/connection_adapters/sqlserver/odbc_database_statements.rb +234 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary_ext.rb +25 -0
- data/lib/activerecord-sqlserver-adapter-odbc-extended.rb +12 -0
- data/test/appveyor/dbsetup.ps1 +27 -0
- data/test/appveyor/dbsetup.sql +11 -0
- data/test/cases/active_schema_test_sqlserver.rb +127 -0
- data/test/cases/adapter_test_sqlserver.rb +648 -0
- data/test/cases/change_column_collation_test_sqlserver.rb +33 -0
- data/test/cases/change_column_null_test_sqlserver.rb +44 -0
- data/test/cases/coerced_tests.rb +2796 -0
- data/test/cases/column_test_sqlserver.rb +848 -0
- data/test/cases/connection_test_sqlserver.rb +138 -0
- data/test/cases/dbconsole.rb +19 -0
- data/test/cases/disconnected_test_sqlserver.rb +42 -0
- data/test/cases/eager_load_too_many_ids_test_sqlserver.rb +18 -0
- data/test/cases/enum_test_sqlserver.rb +49 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +57 -0
- data/test/cases/fetch_test_sqlserver.rb +88 -0
- data/test/cases/fully_qualified_identifier_test_sqlserver.rb +72 -0
- data/test/cases/helper_sqlserver.rb +61 -0
- data/test/cases/migration_test_sqlserver.rb +144 -0
- data/test/cases/order_test_sqlserver.rb +153 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +102 -0
- data/test/cases/primary_keys_test_sqlserver.rb +103 -0
- data/test/cases/rake_test_sqlserver.rb +198 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +296 -0
- data/test/cases/schema_test_sqlserver.rb +111 -0
- data/test/cases/trigger_test_sqlserver.rb +51 -0
- data/test/cases/utils_test_sqlserver.rb +129 -0
- data/test/cases/uuid_test_sqlserver.rb +54 -0
- data/test/cases/view_test_sqlserver.rb +58 -0
- data/test/config.yml +38 -0
- data/test/debug.rb +16 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/create_clients_and_change_column_collation.rb +19 -0
- data/test/migrations/create_clients_and_change_column_null.rb +25 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/alien.rb +5 -0
- data/test/models/sqlserver/booking.rb +5 -0
- data/test/models/sqlserver/composite_pk.rb +9 -0
- data/test/models/sqlserver/customers_view.rb +5 -0
- data/test/models/sqlserver/datatype.rb +5 -0
- data/test/models/sqlserver/datatype_migration.rb +10 -0
- data/test/models/sqlserver/dollar_table_name.rb +5 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +5 -0
- data/test/models/sqlserver/fk_has_pk.rb +5 -0
- data/test/models/sqlserver/natural_pk_data.rb +6 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +5 -0
- data/test/models/sqlserver/no_pk_data.rb +5 -0
- data/test/models/sqlserver/object_default.rb +5 -0
- data/test/models/sqlserver/quoted_table.rb +9 -0
- data/test/models/sqlserver/quoted_view_1.rb +5 -0
- data/test/models/sqlserver/quoted_view_2.rb +5 -0
- data/test/models/sqlserver/sst_memory.rb +5 -0
- data/test/models/sqlserver/sst_string_collation.rb +3 -0
- data/test/models/sqlserver/string_default.rb +5 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +5 -0
- data/test/models/sqlserver/string_defaults_view.rb +5 -0
- data/test/models/sqlserver/table_with_spaces.rb +5 -0
- data/test/models/sqlserver/tinyint_pk.rb +5 -0
- data/test/models/sqlserver/trigger.rb +17 -0
- data/test/models/sqlserver/trigger_history.rb +5 -0
- data/test/models/sqlserver/upper.rb +5 -0
- data/test/models/sqlserver/uppered.rb +5 -0
- data/test/models/sqlserver/uuid.rb +5 -0
- data/test/schema/datatypes/2012.sql +56 -0
- data/test/schema/enable-in-memory-oltp.sql +81 -0
- data/test/schema/sqlserver_specific_schema.rb +363 -0
- data/test/support/coerceable_test_sqlserver.rb +55 -0
- data/test/support/connection_reflection.rb +32 -0
- data/test/support/core_ext/query_cache.rb +38 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_6_1_topic_associations.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic.dump +0 -0
- data/test/support/marshal_compatibility_fixtures/SQLServer/rails_7_1_topic_associations.dump +0 -0
- data/test/support/minitest_sqlserver.rb +3 -0
- data/test/support/paths_sqlserver.rb +50 -0
- data/test/support/query_assertions.rb +49 -0
- data/test/support/rake_helpers.rb +46 -0
- data/test/support/table_definition_sqlserver.rb +24 -0
- data/test/support/test_in_memory_oltp.rb +17 -0
- metadata +240 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
ActiveRecord::Schema.define do
|
|
4
|
+
# Exhaustive Data Types
|
|
5
|
+
|
|
6
|
+
execute File.read(ARTest::SQLServer.schema_datatypes_2012_file)
|
|
7
|
+
|
|
8
|
+
create_table :sst_datatypes_migration, force: true do |t|
|
|
9
|
+
# Simple Rails conventions.
|
|
10
|
+
t.integer :integer_col
|
|
11
|
+
t.bigint :bigint_col
|
|
12
|
+
t.boolean :boolean_col
|
|
13
|
+
t.decimal :decimal_col
|
|
14
|
+
t.float :float_col
|
|
15
|
+
t.string :string_col
|
|
16
|
+
t.text :text_col
|
|
17
|
+
t.datetime :datetime_nil_precision_col, precision: nil
|
|
18
|
+
t.datetime :datetime_col # Precision defaults to 6
|
|
19
|
+
t.timestamp :timestamp_col # Precision defaults to 6
|
|
20
|
+
t.time :time_col
|
|
21
|
+
t.date :date_col
|
|
22
|
+
t.binary :binary_col
|
|
23
|
+
# Our type methods.
|
|
24
|
+
t.real :real_col
|
|
25
|
+
t.money :money_col
|
|
26
|
+
t.smalldatetime :smalldatetime_col
|
|
27
|
+
t.datetime2 :datetime2_col
|
|
28
|
+
t.datetimeoffset :datetimeoffset
|
|
29
|
+
t.smallmoney :smallmoney_col
|
|
30
|
+
t.char :char_col
|
|
31
|
+
t.varchar :varchar_col
|
|
32
|
+
t.text_basic :text_basic_col
|
|
33
|
+
t.nchar :nchar_col
|
|
34
|
+
t.ntext :ntext_col
|
|
35
|
+
t.binary_basic :binary_basic_col
|
|
36
|
+
t.binary_basic :binary_basic_16_col, limit: 16
|
|
37
|
+
t.varbinary :varbinary_col
|
|
38
|
+
t.uuid :uuid_col
|
|
39
|
+
t.ss_timestamp :sstimestamp_col
|
|
40
|
+
if supports_json?
|
|
41
|
+
t.json :json_col
|
|
42
|
+
else
|
|
43
|
+
t.text :json_col
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Edge Cases
|
|
48
|
+
|
|
49
|
+
if ENV["IN_MEMORY_OLTP"] && supports_in_memory_oltp?
|
|
50
|
+
create_table "sst_memory", force: true, id: false,
|
|
51
|
+
options: "WITH (MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_AND_DATA)" do |t|
|
|
52
|
+
t.primary_key_nonclustered :id
|
|
53
|
+
t.string :name
|
|
54
|
+
t.timestamps
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
create_table "sst_bookings", force: true do |t|
|
|
59
|
+
t.string :name
|
|
60
|
+
t.datetime2 :created_at, null: false
|
|
61
|
+
t.datetime2 :updated_at, null: false
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
create_table "sst_uuids", force: true, id: :uuid do |t|
|
|
65
|
+
t.string :name
|
|
66
|
+
t.uuid :other_uuid, default: "NEWID()"
|
|
67
|
+
t.uuid :uuid_nil_default, default: nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
create_table "sst_my$strange_table", force: true do |t|
|
|
71
|
+
t.string :name
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
create_table :SST_UPPER_TESTS, force: true do |t|
|
|
75
|
+
t.column :COLUMN1, :string
|
|
76
|
+
t.column :COLUMN2, :integer
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
create_table :sst_no_pk_data, force: true, id: false do |t|
|
|
80
|
+
t.string :name
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
create_table "sst_quoted-table", force: true do |t|
|
|
84
|
+
end
|
|
85
|
+
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_quoted-view1') DROP VIEW [sst_quoted-view1]"
|
|
86
|
+
execute "CREATE VIEW [sst_quoted-view1] AS SELECT * FROM [sst_quoted-table]"
|
|
87
|
+
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_quoted-view2') DROP VIEW [sst_quoted-view2]"
|
|
88
|
+
execute "CREATE VIEW [sst_quoted-view2] AS \n /*#{'x' * 4000}}*/ \n SELECT * FROM [sst_quoted-table]"
|
|
89
|
+
|
|
90
|
+
create_table :sst_string_defaults, force: true do |t|
|
|
91
|
+
t.column :string_with_null_default, :string, default: nil
|
|
92
|
+
t.column :string_with_pretend_null_one, :string, default: "null"
|
|
93
|
+
t.column :string_with_pretend_null_two, :string, default: "(null)"
|
|
94
|
+
t.column :string_with_pretend_null_three, :string, default: "NULL"
|
|
95
|
+
t.column :string_with_pretend_null_four, :string, default: "(NULL)"
|
|
96
|
+
t.column :string_with_pretend_paren_three, :string, default: "(3)"
|
|
97
|
+
t.column :string_with_multiline_default, :string, default: "Some long default with a\nnew line."
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
create_table :sst_string_collation, collation: :SQL_Latin1_General_CP1_CI_AS, force: true do |t|
|
|
101
|
+
t.string :string_without_collation
|
|
102
|
+
t.varchar :string_default_collation, collation: :SQL_Latin1_General_CP1_CI_AS
|
|
103
|
+
t.varchar :string_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS
|
|
104
|
+
t.varchar :varchar_with_collation, collation: :SQL_Latin1_General_CP1_CS_AS
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
create_table :sst_edge_schemas, force: true do |t|
|
|
108
|
+
t.string :description
|
|
109
|
+
t.column "crazy]]quote", :string
|
|
110
|
+
t.column "with spaces", :string
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_data') DROP TABLE sst_natural_pk_data"
|
|
114
|
+
execute <<-NATURALPKTABLESQL
|
|
115
|
+
CREATE TABLE sst_natural_pk_data(
|
|
116
|
+
parent_id int,
|
|
117
|
+
name nvarchar(255),
|
|
118
|
+
description nvarchar(1000),
|
|
119
|
+
legacy_id nvarchar(10) NOT NULL PRIMARY KEY
|
|
120
|
+
)
|
|
121
|
+
NATURALPKTABLESQL
|
|
122
|
+
|
|
123
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_natural_pk_int_data') DROP TABLE sst_natural_pk_int_data"
|
|
124
|
+
execute <<-NATURALPKINTTABLESQL
|
|
125
|
+
CREATE TABLE sst_natural_pk_int_data(
|
|
126
|
+
legacy_id int NOT NULL PRIMARY KEY,
|
|
127
|
+
parent_id int,
|
|
128
|
+
name nvarchar(255),
|
|
129
|
+
description nvarchar(1000)
|
|
130
|
+
)
|
|
131
|
+
NATURALPKINTTABLESQL
|
|
132
|
+
|
|
133
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_tinyint_pk') DROP TABLE sst_tinyint_pk"
|
|
134
|
+
execute <<-TINYITPKTABLE
|
|
135
|
+
CREATE TABLE sst_tinyint_pk(
|
|
136
|
+
id tinyint IDENTITY NOT NULL PRIMARY KEY,
|
|
137
|
+
name nvarchar(255)
|
|
138
|
+
)
|
|
139
|
+
TINYITPKTABLE
|
|
140
|
+
|
|
141
|
+
execute "DROP DEFAULT [sst_getdateobject];" rescue nil
|
|
142
|
+
execute "CREATE DEFAULT [sst_getdateobject] AS getdate();" rescue nil
|
|
143
|
+
create_table "sst_defaultobjects", force: true do |t|
|
|
144
|
+
t.string :name
|
|
145
|
+
t.date :date
|
|
146
|
+
end
|
|
147
|
+
execute "sp_bindefault 'sst_getdateobject', 'sst_defaultobjects.date'"
|
|
148
|
+
|
|
149
|
+
execute "DROP PROCEDURE my_getutcdate" rescue nil
|
|
150
|
+
execute <<-SQL
|
|
151
|
+
CREATE PROCEDURE my_getutcdate AS
|
|
152
|
+
SELECT GETUTCDATE() utcdate
|
|
153
|
+
SQL
|
|
154
|
+
|
|
155
|
+
create_table 'A Table With Spaces', force: true do |t|
|
|
156
|
+
t.string :name
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Constraints
|
|
160
|
+
|
|
161
|
+
create_table(:sst_has_fks, force: true) do |t|
|
|
162
|
+
t.column(:fk_id, :bigint, null: false)
|
|
163
|
+
t.column(:fk_id2, :bigint)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
create_table(:sst_has_pks, force: true) {}
|
|
167
|
+
execute <<-ADDFKSQL
|
|
168
|
+
ALTER TABLE sst_has_fks
|
|
169
|
+
ADD CONSTRAINT FK__sst_has_fks_id
|
|
170
|
+
FOREIGN KEY ([fk_id])
|
|
171
|
+
REFERENCES [sst_has_pks] ([id]),
|
|
172
|
+
|
|
173
|
+
CONSTRAINT FK__sst_has_fks_id2
|
|
174
|
+
FOREIGN KEY ([fk_id2])
|
|
175
|
+
REFERENCES [sst_has_pks] ([id])
|
|
176
|
+
ADDFKSQL
|
|
177
|
+
|
|
178
|
+
# Views
|
|
179
|
+
|
|
180
|
+
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_customers_view') DROP VIEW sst_customers_view"
|
|
181
|
+
execute <<-CUSTOMERSVIEW
|
|
182
|
+
CREATE VIEW sst_customers_view AS
|
|
183
|
+
SELECT id, name, balance
|
|
184
|
+
FROM customers
|
|
185
|
+
CUSTOMERSVIEW
|
|
186
|
+
|
|
187
|
+
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_string_defaults_view') DROP VIEW sst_string_defaults_view"
|
|
188
|
+
execute <<-STRINGDEFAULTSVIEW
|
|
189
|
+
CREATE VIEW sst_string_defaults_view AS
|
|
190
|
+
SELECT id, string_with_pretend_null_one as pretend_null
|
|
191
|
+
FROM sst_string_defaults
|
|
192
|
+
STRINGDEFAULTSVIEW
|
|
193
|
+
|
|
194
|
+
execute "IF EXISTS (SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = 'sst_string_defaults_big_view') DROP VIEW sst_string_defaults_big_view"
|
|
195
|
+
execute <<-STRINGDEFAULTSBIGVIEW
|
|
196
|
+
CREATE VIEW sst_string_defaults_big_view AS
|
|
197
|
+
SELECT id, string_with_pretend_null_one as pretend_null
|
|
198
|
+
/*#{'x' * 4000}}*/
|
|
199
|
+
FROM sst_string_defaults
|
|
200
|
+
STRINGDEFAULTSBIGVIEW
|
|
201
|
+
|
|
202
|
+
# Trigger
|
|
203
|
+
|
|
204
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_trigger') DROP TABLE sst_table_with_trigger"
|
|
205
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_trigger_history') DROP TABLE sst_table_with_trigger_history"
|
|
206
|
+
execute <<-SQL
|
|
207
|
+
CREATE TABLE sst_table_with_trigger(
|
|
208
|
+
id bigint IDENTITY NOT NULL PRIMARY KEY,
|
|
209
|
+
event_name nvarchar(255)
|
|
210
|
+
)
|
|
211
|
+
CREATE TABLE sst_table_with_trigger_history(
|
|
212
|
+
id bigint IDENTITY NOT NULL PRIMARY KEY,
|
|
213
|
+
id_source nvarchar(36),
|
|
214
|
+
event_name nvarchar(255)
|
|
215
|
+
)
|
|
216
|
+
SQL
|
|
217
|
+
execute <<-SQL
|
|
218
|
+
CREATE TRIGGER sst_table_with_trigger_t ON sst_table_with_trigger
|
|
219
|
+
FOR INSERT
|
|
220
|
+
AS
|
|
221
|
+
INSERT INTO sst_table_with_trigger_history (id_source, event_name)
|
|
222
|
+
SELECT id AS id_source, event_name FROM INSERTED
|
|
223
|
+
SQL
|
|
224
|
+
|
|
225
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_uuid_trigger') DROP TABLE sst_table_with_uuid_trigger"
|
|
226
|
+
execute <<-SQL
|
|
227
|
+
CREATE TABLE sst_table_with_uuid_trigger(
|
|
228
|
+
id uniqueidentifier DEFAULT NEWID() PRIMARY KEY,
|
|
229
|
+
event_name nvarchar(255)
|
|
230
|
+
)
|
|
231
|
+
SQL
|
|
232
|
+
execute <<-SQL
|
|
233
|
+
CREATE TRIGGER sst_table_with_uuid_trigger_t ON sst_table_with_uuid_trigger
|
|
234
|
+
FOR INSERT
|
|
235
|
+
AS
|
|
236
|
+
INSERT INTO sst_table_with_trigger_history (id_source, event_name)
|
|
237
|
+
SELECT id AS id_source, event_name FROM INSERTED
|
|
238
|
+
SQL
|
|
239
|
+
|
|
240
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_composite_pk_trigger') DROP TABLE sst_table_with_composite_pk_trigger"
|
|
241
|
+
execute <<-SQL
|
|
242
|
+
CREATE TABLE sst_table_with_composite_pk_trigger(
|
|
243
|
+
pk_col_one int NOT NULL,
|
|
244
|
+
pk_col_two int NOT NULL,
|
|
245
|
+
event_name nvarchar(255),
|
|
246
|
+
CONSTRAINT PK_sst_table_with_composite_pk_trigger PRIMARY KEY (pk_col_one, pk_col_two)
|
|
247
|
+
)
|
|
248
|
+
SQL
|
|
249
|
+
execute <<-SQL
|
|
250
|
+
CREATE TRIGGER sst_table_with_composite_pk_trigger_t ON sst_table_with_composite_pk_trigger
|
|
251
|
+
FOR INSERT
|
|
252
|
+
AS
|
|
253
|
+
INSERT INTO sst_table_with_trigger_history (id_source, event_name)
|
|
254
|
+
SELECT pk_col_one AS id_source, event_name FROM INSERTED
|
|
255
|
+
SQL
|
|
256
|
+
|
|
257
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_table_with_composite_pk_trigger_with_different_data_type') DROP TABLE sst_table_with_composite_pk_trigger_with_different_data_type"
|
|
258
|
+
execute <<-SQL
|
|
259
|
+
CREATE TABLE sst_table_with_composite_pk_trigger_with_different_data_type(
|
|
260
|
+
pk_col_one uniqueidentifier DEFAULT NEWID(),
|
|
261
|
+
pk_col_two int NOT NULL,
|
|
262
|
+
event_name nvarchar(255),
|
|
263
|
+
CONSTRAINT PK_sst_table_with_composite_pk_trigger_with_different_data_type PRIMARY KEY (pk_col_one, pk_col_two)
|
|
264
|
+
)
|
|
265
|
+
SQL
|
|
266
|
+
execute <<-SQL
|
|
267
|
+
CREATE TRIGGER sst_table_with_composite_pk_trigger_with_different_data_type_t ON sst_table_with_composite_pk_trigger_with_different_data_type
|
|
268
|
+
FOR INSERT
|
|
269
|
+
AS
|
|
270
|
+
INSERT INTO sst_table_with_trigger_history (id_source, event_name)
|
|
271
|
+
SELECT pk_col_one AS id_source, event_name FROM INSERTED
|
|
272
|
+
SQL
|
|
273
|
+
|
|
274
|
+
# Another schema.
|
|
275
|
+
|
|
276
|
+
create_table :sst_schema_columns, force: true do |t|
|
|
277
|
+
t.column :field1, :integer
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test') EXEC sp_executesql N'CREATE SCHEMA test'"
|
|
281
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_columns' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_columns"
|
|
282
|
+
execute <<-SIMILIARTABLEINOTHERSCHEMA
|
|
283
|
+
CREATE TABLE test.sst_schema_columns(
|
|
284
|
+
id int IDENTITY NOT NULL primary key,
|
|
285
|
+
filed_1 int,
|
|
286
|
+
field_2 int,
|
|
287
|
+
name varchar(255),
|
|
288
|
+
description varchar(1000),
|
|
289
|
+
n_name nvarchar(255),
|
|
290
|
+
n_description nvarchar(1000)
|
|
291
|
+
)
|
|
292
|
+
SIMILIARTABLEINOTHERSCHEMA
|
|
293
|
+
|
|
294
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_identity' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_identity"
|
|
295
|
+
execute <<-SIMILIARTABLEINOTHERSCHEMA
|
|
296
|
+
CREATE TABLE test.sst_schema_identity(
|
|
297
|
+
id int IDENTITY NOT NULL primary key,
|
|
298
|
+
filed_1 int
|
|
299
|
+
)
|
|
300
|
+
SIMILIARTABLEINOTHERSCHEMA
|
|
301
|
+
|
|
302
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_natural_id' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_natural_id"
|
|
303
|
+
execute <<-NATURALPKTABLESQLINOTHERSCHEMA
|
|
304
|
+
CREATE TABLE test.sst_schema_natural_id(
|
|
305
|
+
parent_id int,
|
|
306
|
+
name nvarchar(255),
|
|
307
|
+
description nvarchar(1000),
|
|
308
|
+
legacy_id nvarchar(10) NOT NULL PRIMARY KEY,
|
|
309
|
+
)
|
|
310
|
+
NATURALPKTABLESQLINOTHERSCHEMA
|
|
311
|
+
|
|
312
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_multiple_schema' and TABLE_SCHEMA = 'test') DROP TABLE test.sst_schema_test_multiple_schema"
|
|
313
|
+
execute <<-SCHEMATESTMULTIPLESCHEMA
|
|
314
|
+
CREATE TABLE test.sst_schema_test_multiple_schema(
|
|
315
|
+
field_1 int NOT NULL PRIMARY KEY,
|
|
316
|
+
field_2 int,
|
|
317
|
+
)
|
|
318
|
+
SCHEMATESTMULTIPLESCHEMA
|
|
319
|
+
execute "IF NOT EXISTS(SELECT * FROM sys.schemas WHERE name = 'test2') EXEC sp_executesql N'CREATE SCHEMA test2'"
|
|
320
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_schema_test_multiple_schema' and TABLE_SCHEMA = 'test2') DROP TABLE test2.sst_schema_test_multiple_schema"
|
|
321
|
+
execute <<-SCHEMATESTMULTIPLESCHEMA
|
|
322
|
+
CREATE TABLE test2.sst_schema_test_multiple_schema(
|
|
323
|
+
field_1 int,
|
|
324
|
+
field_2 int NOT NULL PRIMARY KEY,
|
|
325
|
+
)
|
|
326
|
+
SCHEMATESTMULTIPLESCHEMA
|
|
327
|
+
|
|
328
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'unique_key_dumped_table') DROP TABLE unique_key_dumped_table"
|
|
329
|
+
execute <<-SQLSERVERUNIQUEKEYS
|
|
330
|
+
CREATE TABLE unique_key_dumped_table (
|
|
331
|
+
id int IDENTITY(1,1) NOT NULL,
|
|
332
|
+
unique_field int DEFAULT 0 NOT NULL,
|
|
333
|
+
CONSTRAINT IX_UNIQUE_KEY UNIQUE (unique_field),
|
|
334
|
+
CONSTRAINT PK_UNIQUE_KEY PRIMARY KEY (id)
|
|
335
|
+
);
|
|
336
|
+
SQLSERVERUNIQUEKEYS
|
|
337
|
+
|
|
338
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_without_identity') DROP TABLE sst_composite_without_identity"
|
|
339
|
+
execute <<-COMPOSITE_WITHOUT_IDENTITY
|
|
340
|
+
CREATE TABLE sst_composite_without_identity (
|
|
341
|
+
pk_col_one int NOT NULL,
|
|
342
|
+
pk_col_two int NOT NULL,
|
|
343
|
+
CONSTRAINT PK_sst_composite_without_identity PRIMARY KEY (pk_col_one, pk_col_two)
|
|
344
|
+
);
|
|
345
|
+
COMPOSITE_WITHOUT_IDENTITY
|
|
346
|
+
|
|
347
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'sst_composite_with_identity') DROP TABLE sst_composite_with_identity"
|
|
348
|
+
execute <<-COMPOSITE_WITH_IDENTITY
|
|
349
|
+
CREATE TABLE sst_composite_with_identity (
|
|
350
|
+
pk_col_one int IDENTITY NOT NULL,
|
|
351
|
+
pk_col_two int NOT NULL,
|
|
352
|
+
CONSTRAINT PK_sst_composite_with_identity PRIMARY KEY (pk_col_one, pk_col_two)
|
|
353
|
+
);
|
|
354
|
+
COMPOSITE_WITH_IDENTITY
|
|
355
|
+
|
|
356
|
+
execute "IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'aliens' and TABLE_SCHEMA = 'test') DROP TABLE test.aliens"
|
|
357
|
+
execute <<-TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL
|
|
358
|
+
CREATE TABLE test.aliens(
|
|
359
|
+
id int IDENTITY NOT NULL primary key,
|
|
360
|
+
name varchar(255)
|
|
361
|
+
)
|
|
362
|
+
TABLE_IN_OTHER_SCHEMA_USED_BY_MODEL
|
|
363
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ARTest
|
|
4
|
+
module SQLServer
|
|
5
|
+
module CoerceableTest
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
cattr_accessor :coerced_tests, instance_accessor: false
|
|
10
|
+
self.coerced_tests = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def coerce_tests!(*methods)
|
|
15
|
+
methods.each do |method|
|
|
16
|
+
coerced_tests.push(method)
|
|
17
|
+
coerced_test_warning(method)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def coerce_all_tests!
|
|
22
|
+
instance_methods(false).each do |method|
|
|
23
|
+
next unless method.to_s =~ /\Atest/
|
|
24
|
+
|
|
25
|
+
undef_method(method)
|
|
26
|
+
end
|
|
27
|
+
STDOUT.puts "🙉 🙈 🙊 Undefined all tests: #{name}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def coerced_test_warning(test_to_coerce)
|
|
33
|
+
if test_to_coerce.is_a?(Regexp)
|
|
34
|
+
method = instance_methods(false).select { |m| m =~ test_to_coerce }
|
|
35
|
+
else
|
|
36
|
+
method = test_to_coerce
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Array(method).each do |m|
|
|
40
|
+
result = if m && method_defined?(m)
|
|
41
|
+
alias_method("original_#{test_to_coerce.inspect.tr('/\:"', '')}", m)
|
|
42
|
+
undef_method(m)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
if result.blank?
|
|
46
|
+
STDOUT.puts "🐳 Unfound coerced test: #{name}##{m}"
|
|
47
|
+
else
|
|
48
|
+
STDOUT.puts "🐵 Undefined coerced test: #{name}##{m}"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ARTest
|
|
4
|
+
module SQLServer
|
|
5
|
+
module ConnectionReflection
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included { extend ConnectionReflection }
|
|
9
|
+
|
|
10
|
+
def connection
|
|
11
|
+
ActiveRecord::Base.lease_connection
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def connection_options
|
|
15
|
+
connection.instance_variable_get :@connection_parameters
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def connection_tds_73
|
|
19
|
+
rc = connection.raw_connection
|
|
20
|
+
rc.respond_to?(:tds_73?) && rc.tds_73?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def connection_odbc?
|
|
24
|
+
connection_options[:mode] == :odbc
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def connection_sqlserver_azure?
|
|
28
|
+
connection.sqlserver_azure?
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record/connection_adapters/sqlserver_adapter"
|
|
4
|
+
|
|
5
|
+
module SqlIgnoredCache
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
IGNORED_SQL = [
|
|
9
|
+
/INFORMATION_SCHEMA\.(TABLES|VIEWS|COLUMNS|KEY_COLUMN_USAGE)/im,
|
|
10
|
+
/sys.columns/i,
|
|
11
|
+
/SELECT @@version/,
|
|
12
|
+
/SELECT @@TRANCOUNT/,
|
|
13
|
+
/(BEGIN|COMMIT|ROLLBACK|SAVE) TRANSACTION/,
|
|
14
|
+
/SELECT CAST\(.* AS .*\) AS value/,
|
|
15
|
+
/SELECT DATABASEPROPERTYEX/im
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
# We don't want to coerce every ActiveRecord test that relies on `query_cache`
|
|
19
|
+
# just because we do more queries than the other adapters.
|
|
20
|
+
#
|
|
21
|
+
# Removing internal queries from the cache will make AR tests pass without
|
|
22
|
+
# compromising cache outside tests.
|
|
23
|
+
def cache_sql(sql, name, binds)
|
|
24
|
+
result = super
|
|
25
|
+
|
|
26
|
+
@query_cache.instance_variable_get(:@map).delete_if do |cache_key, _v|
|
|
27
|
+
# Query cache key generated by `sql` or `[sql, binds]`, so need to retrieve `sql` for both cases.
|
|
28
|
+
cache_key_sql = Array(cache_key).first
|
|
29
|
+
Regexp.union(IGNORED_SQL).match?(cache_key_sql)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
result
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
ActiveSupport.on_load(:active_record) do
|
|
37
|
+
ActiveRecord::ConnectionAdapters::SQLServerAdapter.prepend(SqlIgnoredCache)
|
|
38
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ARTest
|
|
4
|
+
module SQLServer
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def schema_root
|
|
8
|
+
File.join ARTest::SQLServer.test_root_sqlserver, "schema"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def schema_file
|
|
12
|
+
File.join schema_root, "sqlserver_specific_schema.rb"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def schema_datatypes_2012_file
|
|
16
|
+
File.join schema_root, "datatypes", "2012.sql"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def load_schema
|
|
20
|
+
original_stdout = $stdout
|
|
21
|
+
$stdout = StringIO.new
|
|
22
|
+
load schema_file
|
|
23
|
+
ensure
|
|
24
|
+
$stdout = original_stdout
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
ARTest::SQLServer.load_schema
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ARTest
|
|
4
|
+
module SQLServer
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def root_sqlserver
|
|
8
|
+
File.expand_path File.join(File.dirname(__FILE__), "..", "..")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def test_root_sqlserver
|
|
12
|
+
File.join root_sqlserver, "test"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def root_activerecord
|
|
16
|
+
File.join Gem.loaded_specs["rails"].full_gem_path, "activerecord"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def root_activerecord_lib
|
|
20
|
+
File.join root_activerecord, "lib"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def root_activerecord_test
|
|
24
|
+
File.join root_activerecord, "test"
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_load_paths
|
|
28
|
+
["lib", "test", root_activerecord_lib, root_activerecord_test]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def add_to_load_paths!
|
|
32
|
+
test_load_paths.each { |p| $LOAD_PATH.unshift(p) unless $LOAD_PATH.include?(p) }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def migrations_root
|
|
36
|
+
File.join test_root_sqlserver, "migrations"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def arconfig_file
|
|
40
|
+
File.join test_root_sqlserver, "config.yml"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def arconfig_file_env!
|
|
44
|
+
ENV["ARCONFIG"] = arconfig_file
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
ARTest::SQLServer.add_to_load_paths!
|
|
50
|
+
ARTest::SQLServer.arconfig_file_env!
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module ARTest
|
|
2
|
+
module SQLServer
|
|
3
|
+
module QueryAssertions
|
|
4
|
+
def assert_queries_count(count = nil, include_schema: false, &block)
|
|
5
|
+
ActiveRecord::Base.lease_connection.materialize_transactions
|
|
6
|
+
|
|
7
|
+
counter = ActiveRecord::Assertions::QueryAssertions::SQLCounter.new
|
|
8
|
+
ActiveSupport::Notifications.subscribed(counter, "sql.active_record") do
|
|
9
|
+
result = _assert_nothing_raised_or_warn("assert_queries_count", &block)
|
|
10
|
+
queries = include_schema ? counter.log_all : counter.log
|
|
11
|
+
|
|
12
|
+
# Start of monkey-patch
|
|
13
|
+
queries = include_release_savepoint_placeholder_queries(queries)
|
|
14
|
+
# End of monkey-patch
|
|
15
|
+
|
|
16
|
+
if count
|
|
17
|
+
assert_equal count, queries.size, "#{queries.size} instead of #{count} queries were executed. Queries: #{queries.join("\n\n")}"
|
|
18
|
+
else
|
|
19
|
+
assert_operator queries.size, :>=, 1, "1 or more queries expected, but none were executed.#{queries.empty? ? '' : "\nQueries:\n#{queries.join("\n")}"}"
|
|
20
|
+
end
|
|
21
|
+
result
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
# Rails tests expect a save-point to be created and released. SQL Server does not release
|
|
28
|
+
# save-points and so the number of queries will be off. This monkey patch adds a placeholder queries
|
|
29
|
+
# to replace the missing save-point releases.
|
|
30
|
+
def include_release_savepoint_placeholder_queries(queries)
|
|
31
|
+
grouped_queries = [[]]
|
|
32
|
+
|
|
33
|
+
queries.each do |query|
|
|
34
|
+
if query =~ /SAVE TRANSACTION \S+/
|
|
35
|
+
grouped_queries << [query]
|
|
36
|
+
else
|
|
37
|
+
grouped_queries.last << query
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
grouped_queries.each do |group|
|
|
42
|
+
group.append "/* release savepoint placeholder for testing */" if group.first =~ /SAVE TRANSACTION \S+/
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
grouped_queries.flatten
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
SQLSERVER_HELPER = "test/cases/helper_sqlserver.rb"
|
|
4
|
+
SQLSERVER_COERCED = "test/cases/coerced_tests.rb"
|
|
5
|
+
|
|
6
|
+
def env_ar_test_files
|
|
7
|
+
return unless ENV["TEST_FILES_AR"] && !ENV["TEST_FILES_AR"].empty?
|
|
8
|
+
|
|
9
|
+
@env_ar_test_files ||= begin
|
|
10
|
+
ENV["TEST_FILES_AR"].split(",").map { |file|
|
|
11
|
+
File.join ARTest::SQLServer.root_activerecord, file.strip
|
|
12
|
+
}.sort
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def env_test_files
|
|
17
|
+
return unless ENV["TEST_FILES"] && !ENV["TEST_FILES"].empty?
|
|
18
|
+
|
|
19
|
+
@env_test_files ||= ENV["TEST_FILES"].split(",").map(&:strip)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def sqlserver_cases
|
|
23
|
+
@sqlserver_cases ||= Dir.glob("test/cases/*_test_sqlserver.rb")
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def ar_cases
|
|
27
|
+
@ar_cases ||= begin
|
|
28
|
+
Dir.glob("#{ARTest::SQLServer.root_activerecord}/test/cases/**/*_test.rb").reject {
|
|
29
|
+
|x| x.include?("/adapters/") || x.include?("/encryption/performance")
|
|
30
|
+
}.sort
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_files
|
|
35
|
+
if env_ar_test_files
|
|
36
|
+
[SQLSERVER_HELPER] + env_ar_test_files
|
|
37
|
+
elsif env_test_files
|
|
38
|
+
env_test_files
|
|
39
|
+
elsif ENV["ONLY_SQLSERVER"]
|
|
40
|
+
sqlserver_cases
|
|
41
|
+
elsif ENV["ONLY_ACTIVERECORD"]
|
|
42
|
+
[SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED])
|
|
43
|
+
else
|
|
44
|
+
[SQLSERVER_HELPER] + (ar_cases + [SQLSERVER_COERCED] + sqlserver_cases)
|
|
45
|
+
end
|
|
46
|
+
end
|