activerecord-sqlserver-adapter 4.1.8 → 4.2.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +15 -0
- data/CHANGELOG.md +60 -0
- data/Gemfile +45 -0
- data/Guardfile +29 -0
- data/MIT-LICENSE +5 -5
- data/README.md +193 -0
- data/RUNNING_UNIT_TESTS.md +95 -0
- data/Rakefile +48 -0
- data/activerecord-sqlserver-adapter.gemspec +28 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +5 -15
- data/lib/active_record/connection_adapters/sqlserver/core_ext/attribute_methods.rb +25 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/explain.rb +6 -4
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +9 -3
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +3 -1
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +130 -151
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +0 -25
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +39 -78
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +71 -47
- data/lib/active_record/connection_adapters/sqlserver/schema_creation.rb +14 -30
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +112 -108
- data/lib/active_record/connection_adapters/sqlserver/showplan.rb +4 -2
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_table.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/showplan/printer_xml.rb +1 -1
- data/lib/active_record/connection_adapters/sqlserver/table_definition.rb +52 -7
- data/lib/active_record/connection_adapters/sqlserver/transaction.rb +52 -0
- data/lib/active_record/connection_adapters/sqlserver/type.rb +46 -0
- data/lib/active_record/connection_adapters/sqlserver/type/big_integer.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/binary.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/boolean.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/castable.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/core_ext/value.rb +39 -0
- data/lib/active_record/connection_adapters/sqlserver/type/date.rb +14 -0
- data/lib/active_record/connection_adapters/sqlserver/type/datetime.rb +37 -0
- data/lib/active_record/connection_adapters/sqlserver/type/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/float.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/quoter.rb +32 -0
- data/lib/active_record/connection_adapters/sqlserver/type/real.rb +17 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_integer.rb +13 -0
- data/lib/active_record/connection_adapters/sqlserver/type/small_money.rb +21 -0
- data/lib/active_record/connection_adapters/sqlserver/type/smalldatetime.rb +24 -0
- data/lib/active_record/connection_adapters/sqlserver/type/string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/time.rb +59 -0
- data/lib/active_record/connection_adapters/sqlserver/type/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/tiny_integer.rb +22 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_char.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_string.rb +12 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_text.rb +15 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/unicode_varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/uuid.rb +23 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varbinary_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/type/varchar_max.rb +20 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +118 -12
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +133 -198
- data/lib/active_record/connection_adapters/sqlserver_column.rb +15 -86
- data/lib/active_record/sqlserver_base.rb +2 -0
- data/lib/arel/visitors/sqlserver.rb +120 -393
- data/lib/{arel/arel_sqlserver.rb → arel_sqlserver.rb} +1 -3
- data/test/cases/adapter_test_sqlserver.rb +420 -0
- data/test/cases/coerced_tests.rb +642 -0
- data/test/cases/column_test_sqlserver.rb +703 -0
- data/test/cases/connection_test_sqlserver.rb +216 -0
- data/test/cases/database_statements_test_sqlserver.rb +57 -0
- data/test/cases/execute_procedure_test_sqlserver.rb +38 -0
- data/test/cases/helper_sqlserver.rb +36 -0
- data/test/cases/migration_test_sqlserver.rb +66 -0
- data/test/cases/order_test_sqlserver.rb +147 -0
- data/test/cases/pessimistic_locking_test_sqlserver.rb +90 -0
- data/test/cases/schema_dumper_test_sqlserver.rb +175 -0
- data/test/cases/schema_test_sqlserver.rb +54 -0
- data/test/cases/scratchpad_test_sqlserver.rb +9 -0
- data/test/cases/showplan_test_sqlserver.rb +65 -0
- data/test/cases/specific_schema_test_sqlserver.rb +118 -0
- data/test/cases/transaction_test_sqlserver.rb +61 -0
- data/test/cases/utils_test_sqlserver.rb +91 -0
- data/test/cases/uuid_test_sqlserver.rb +41 -0
- data/test/config.yml +35 -0
- data/test/fixtures/1px.gif +0 -0
- data/test/migrations/transaction_table/1_table_will_never_be_created.rb +11 -0
- data/test/models/sqlserver/customers_view.rb +3 -0
- data/test/models/sqlserver/datatype.rb +3 -0
- data/test/models/sqlserver/datatype_migration.rb +3 -0
- data/test/models/sqlserver/dollar_table_name.rb +3 -0
- data/test/models/sqlserver/edge_schema.rb +13 -0
- data/test/models/sqlserver/fk_has_fk.rb +3 -0
- data/test/models/sqlserver/fk_has_pk.rb +3 -0
- data/test/models/sqlserver/natural_pk_data.rb +4 -0
- data/test/models/sqlserver/natural_pk_int_data.rb +3 -0
- data/test/models/sqlserver/no_pk_data.rb +3 -0
- data/test/models/sqlserver/quoted_table.rb +7 -0
- data/test/models/sqlserver/quoted_view_1.rb +3 -0
- data/test/models/sqlserver/quoted_view_2.rb +3 -0
- data/test/models/sqlserver/string_default.rb +3 -0
- data/test/models/sqlserver/string_defaults_big_view.rb +3 -0
- data/test/models/sqlserver/string_defaults_view.rb +3 -0
- data/test/models/sqlserver/tinyint_pk.rb +3 -0
- data/test/models/sqlserver/upper.rb +3 -0
- data/test/models/sqlserver/uppered.rb +3 -0
- data/test/models/sqlserver/uuid.rb +3 -0
- data/test/schema/datatypes/2012.sql +64 -0
- data/test/schema/sqlserver_specific_schema.rb +181 -0
- data/test/support/coerceable_test_sqlserver.rb +45 -0
- data/test/support/load_schema_sqlserver.rb +29 -0
- data/test/support/minitest_sqlserver.rb +1 -0
- data/test/support/paths_sqlserver.rb +48 -0
- data/test/support/rake_helpers.rb +41 -0
- data/test/support/sql_counter_sqlserver.rb +32 -0
- metadata +271 -21
- data/CHANGELOG +0 -39
- data/VERSION +0 -1
- data/lib/active_record/connection_adapters/sqlserver/core_ext/relation.rb +0 -17
- data/lib/active_record/sqlserver_test_case.rb +0 -17
- data/lib/arel/nodes_sqlserver.rb +0 -14
- data/lib/arel/select_manager_sqlserver.rb +0 -62
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
require 'models/reply'
|
|
3
|
+
require 'models/topic'
|
|
4
|
+
|
|
5
|
+
class ConnectionTestSQLServer < ActiveRecord::TestCase
|
|
6
|
+
|
|
7
|
+
self.use_transactional_fixtures = false
|
|
8
|
+
|
|
9
|
+
fixtures :topics, :accounts
|
|
10
|
+
|
|
11
|
+
before { assert connection.active? }
|
|
12
|
+
after { connection.reconnect! }
|
|
13
|
+
|
|
14
|
+
it 'affect rows' do
|
|
15
|
+
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
|
|
16
|
+
updated = Topic.update(topic_data.keys, topic_data.values)
|
|
17
|
+
assert_equal 2, updated.size
|
|
18
|
+
assert_equal "1 updated", Topic.find(1).content
|
|
19
|
+
assert_equal "2 updated", Topic.find(2).content
|
|
20
|
+
assert_equal 2, Topic.delete([1, 2])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it 'allow usage of :database connection option to remove setting from dsn' do
|
|
24
|
+
assert_equal 'activerecord_unittest', connection.current_database
|
|
25
|
+
begin
|
|
26
|
+
connection.use_database('activerecord_unittest2')
|
|
27
|
+
assert_equal 'activerecord_unittest2', connection.current_database
|
|
28
|
+
ensure
|
|
29
|
+
connection.use_database
|
|
30
|
+
assert_equal 'activerecord_unittest', connection.current_database, 'Would default back to connection options'
|
|
31
|
+
end
|
|
32
|
+
end unless sqlserver_azure?
|
|
33
|
+
|
|
34
|
+
describe 'ODBC connection management' do
|
|
35
|
+
|
|
36
|
+
it 'return finished ODBC statement handle from #execute without block' do
|
|
37
|
+
assert_all_odbc_statements_used_are_closed do
|
|
38
|
+
connection.execute('SELECT * FROM [topics]')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'finish ODBC statement handle from #execute with block' do
|
|
43
|
+
assert_all_odbc_statements_used_are_closed do
|
|
44
|
+
connection.execute('SELECT * FROM [topics]') { }
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'finish connection from #raw_select' do
|
|
49
|
+
assert_all_odbc_statements_used_are_closed do
|
|
50
|
+
connection.send(:raw_select,'SELECT * FROM [topics]')
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it 'execute without block closes statement' do
|
|
55
|
+
assert_all_odbc_statements_used_are_closed do
|
|
56
|
+
connection.execute("SELECT 1")
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it 'execute with block closes statement' do
|
|
61
|
+
assert_all_odbc_statements_used_are_closed do
|
|
62
|
+
connection.execute("SELECT 1") do |sth|
|
|
63
|
+
assert !sth.finished?, "Statement should still be alive within block"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it 'insert with identity closes statement' do
|
|
69
|
+
assert_all_odbc_statements_used_are_closed do
|
|
70
|
+
connection.exec_insert "INSERT INTO accounts ([id],[firm_id],[credit_limit]) VALUES (999, 1, 50)", "SQL", []
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it 'insert without identity closes statement' do
|
|
75
|
+
assert_all_odbc_statements_used_are_closed do
|
|
76
|
+
connection.exec_insert "INSERT INTO accounts ([firm_id],[credit_limit]) VALUES (1, 50)", "SQL", []
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'active closes statement' do
|
|
81
|
+
assert_all_odbc_statements_used_are_closed do
|
|
82
|
+
connection.active?
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
end if connection_mode_odbc?
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
describe 'Connection management' do
|
|
90
|
+
|
|
91
|
+
it 'set spid on connect' do
|
|
92
|
+
assert_instance_of Fixnum, connection.spid
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
it 'reset spid on disconnect!' do
|
|
96
|
+
connection.disconnect!
|
|
97
|
+
assert connection.spid.nil?
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'reset the connection' do
|
|
101
|
+
connection.disconnect!
|
|
102
|
+
connection.raw_connection.must_be_nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'be able to disconnect and reconnect at will' do
|
|
106
|
+
disconnect_raw_connection!
|
|
107
|
+
assert !connection.active?
|
|
108
|
+
connection.reconnect!
|
|
109
|
+
assert connection.active?
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
describe 'with a deadlock victim exception 1205' do
|
|
113
|
+
|
|
114
|
+
describe 'outside a transaction' do
|
|
115
|
+
|
|
116
|
+
before do
|
|
117
|
+
@query = "SELECT 1 as [one]"
|
|
118
|
+
@expected = connection.execute(@query)
|
|
119
|
+
# Execute the query to get a handle of the expected result, which
|
|
120
|
+
# will be returned after a simulated deadlock victim (1205).
|
|
121
|
+
raw_conn = connection.raw_connection
|
|
122
|
+
stubbed_handle = raw_conn.execute(@query)
|
|
123
|
+
connection.send(:finish_statement_handle, stubbed_handle)
|
|
124
|
+
raw_conn.stubs(:execute).raises(deadlock_victim_exception(@query)).then.returns(stubbed_handle)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it 'raise ActiveRecord::DeadlockVictim' do
|
|
128
|
+
assert_raise(ActiveRecord::DeadlockVictim) do
|
|
129
|
+
assert_equal @expected, connection.execute(@query)
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
describe 'within a transaction' do
|
|
136
|
+
|
|
137
|
+
before do
|
|
138
|
+
@query = "SELECT 1 as [one]"
|
|
139
|
+
@expected = connection.execute(@query)
|
|
140
|
+
# We "stub" the execute method to simulate raising a deadlock victim exception once.
|
|
141
|
+
connection.class.class_eval do
|
|
142
|
+
def execute_with_deadlock_exception(sql, *args)
|
|
143
|
+
if !@raised_deadlock_exception && sql == "SELECT 1 as [one]"
|
|
144
|
+
sql = "RAISERROR('Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}', 13, 1)"
|
|
145
|
+
@raised_deadlock_exception = true
|
|
146
|
+
elsif @raised_deadlock_exception == true && sql =~ /RAISERROR\('Transaction \(Process ID \d+\) was deadlocked on lock resources with another process and has been chosen as the deadlock victim\. Rerun the transaction\.: SELECT 1 as \[one\]', 13, 1\)/
|
|
147
|
+
sql = "SELECT 1 as [one]"
|
|
148
|
+
end
|
|
149
|
+
execute_without_deadlock_exception(sql, *args)
|
|
150
|
+
end
|
|
151
|
+
alias :execute_without_deadlock_exception :execute
|
|
152
|
+
alias :execute :execute_with_deadlock_exception
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
after do
|
|
157
|
+
# Cleanup the "stubbed" execute method.
|
|
158
|
+
connection.class.class_eval do
|
|
159
|
+
alias :execute :execute_without_deadlock_exception
|
|
160
|
+
remove_method :execute_with_deadlock_exception
|
|
161
|
+
remove_method :execute_without_deadlock_exception
|
|
162
|
+
end
|
|
163
|
+
connection.send(:remove_instance_variable, :@raised_deadlock_exception)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it 'raise ActiveRecord::DeadlockVictim if retry disabled' do
|
|
167
|
+
assert_raise(ActiveRecord::DeadlockVictim) do
|
|
168
|
+
ActiveRecord::Base.transaction do
|
|
169
|
+
assert_equal @expected, connection.execute(@query)
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
end if connection_mode_dblib? # Since it is easier to test, but feature should work in ODBC too.
|
|
177
|
+
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
private
|
|
182
|
+
|
|
183
|
+
def disconnect_raw_connection!
|
|
184
|
+
case connection.instance_variable_get(:@connection_options)[:mode]
|
|
185
|
+
when :dblib
|
|
186
|
+
connection.raw_connection.close rescue nil
|
|
187
|
+
when :odbc
|
|
188
|
+
connection.raw_connection.disconnect rescue nil
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def assert_all_odbc_statements_used_are_closed(&block)
|
|
193
|
+
odbc = connection.raw_connection.class.parent
|
|
194
|
+
existing_handles = []
|
|
195
|
+
ObjectSpace.each_object(odbc::Statement) { |h| existing_handles << h }
|
|
196
|
+
existing_handle_ids = existing_handles.map(&:object_id)
|
|
197
|
+
assert existing_handles.all?(&:finished?), "Somewhere before the block some statements were not closed"
|
|
198
|
+
GC.disable
|
|
199
|
+
yield
|
|
200
|
+
used_handles = []
|
|
201
|
+
ObjectSpace.each_object(odbc::Statement) { |h| used_handles << h unless existing_handle_ids.include?(h.object_id) }
|
|
202
|
+
assert used_handles.size > 0, "No statements were used within given block"
|
|
203
|
+
assert used_handles.all?(&:finished?), "Statement should have been closed within given block"
|
|
204
|
+
ensure
|
|
205
|
+
GC.enable
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def deadlock_victim_exception(sql)
|
|
209
|
+
require 'tiny_tds/error'
|
|
210
|
+
error = TinyTds::Error.new("Transaction (Process ID #{Process.pid}) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.: #{sql}")
|
|
211
|
+
error.severity = 13
|
|
212
|
+
error.db_error_number = 1205
|
|
213
|
+
error
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
|
|
3
|
+
class DatabaseStatementsTestSQLServer < ActiveRecord::TestCase
|
|
4
|
+
|
|
5
|
+
self.use_transactional_fixtures = false
|
|
6
|
+
|
|
7
|
+
after { connection.use_database }
|
|
8
|
+
|
|
9
|
+
it 'create database' do
|
|
10
|
+
connection.create_database 'activerecord_unittest3'
|
|
11
|
+
database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'"
|
|
12
|
+
assert_equal 'activerecord_unittest3', database_name
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it 'drop database' do
|
|
16
|
+
connection.drop_database 'activerecord_unittest3'
|
|
17
|
+
database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord_unittest3'"
|
|
18
|
+
assert_equal nil, database_name
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'create/use/drop database with name with dots' do
|
|
22
|
+
connection.drop_database '[activerecord.unittest]' rescue nil
|
|
23
|
+
connection.create_database '[activerecord.unittest]'
|
|
24
|
+
database_name = connection.select_value "SELECT name FROM master.dbo.sysdatabases WHERE name = 'activerecord.unittest'"
|
|
25
|
+
assert_equal 'activerecord.unittest', database_name
|
|
26
|
+
connection.use_database '[activerecord.unittest]'
|
|
27
|
+
connection.use_database
|
|
28
|
+
connection.drop_database '[activerecord.unittest]'
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe 'with collation' do
|
|
32
|
+
|
|
33
|
+
after { connection.drop_database 'activerecord_unittest3' }
|
|
34
|
+
|
|
35
|
+
it 'create database with default collation for the server' do
|
|
36
|
+
connection.create_database 'activerecord_unittest3'
|
|
37
|
+
default_collation = connection.select_value "SELECT SERVERPROPERTY('Collation')"
|
|
38
|
+
database_collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation"
|
|
39
|
+
assert_equal default_collation, database_collation
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'create database with collation set by the method' do
|
|
43
|
+
connection.create_database 'activerecord_unittest3', collation: 'SQL_Latin1_General_CP1_CI_AS'
|
|
44
|
+
collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation"
|
|
45
|
+
assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it 'create database with collation set by the config' do
|
|
49
|
+
connection.instance_variable_get(:@connection_options)[:collation] = 'SQL_Latin1_General_CP1_CI_AS'
|
|
50
|
+
connection.create_database 'activerecord_unittest3'
|
|
51
|
+
collation = connection.select_value "SELECT DATABASEPROPERTYEX('activerecord_unittest3', 'Collation') SQLCollation"
|
|
52
|
+
assert_equal 'SQL_Latin1_General_CP1_CI_AS', collation
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
|
|
3
|
+
class ExecuteProcedureTestSQLServer < ActiveRecord::TestCase
|
|
4
|
+
|
|
5
|
+
it 'execute a simple procedure' do
|
|
6
|
+
tables = ActiveRecord::Base.execute_procedure :sp_tables
|
|
7
|
+
assert_instance_of Array, tables
|
|
8
|
+
assert tables.first.respond_to?(:keys)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'take parameter arguments' do
|
|
12
|
+
tables = ActiveRecord::Base.execute_procedure :sp_tables, 'sst_datatypes'
|
|
13
|
+
table_info = tables.first
|
|
14
|
+
assert_equal 1, tables.size
|
|
15
|
+
assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}"
|
|
16
|
+
assert_equal 'TABLE', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'allow multiple result sets to be returned' do
|
|
20
|
+
results1, results2 = ActiveRecord::Base.execute_procedure('sp_helpconstraint','accounts')
|
|
21
|
+
assert_instance_of Array, results1
|
|
22
|
+
assert results1.first.respond_to?(:keys)
|
|
23
|
+
assert results1.first['Object Name']
|
|
24
|
+
assert_instance_of Array, results2
|
|
25
|
+
assert results2.first.respond_to?(:keys)
|
|
26
|
+
assert results2.first['constraint_name']
|
|
27
|
+
assert results2.first['constraint_type']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it 'take named parameter arguments' do
|
|
31
|
+
tables = ActiveRecord::Base.execute_procedure :sp_tables, table_name: 'tables', table_owner: 'sys'
|
|
32
|
+
table_info = tables.first
|
|
33
|
+
assert_equal 1, tables.size
|
|
34
|
+
assert_equal (ENV['ARUNIT_DB_NAME'] || 'activerecord_unittest'), table_info['TABLE_QUALIFIER'], "Table Info: #{table_info.inspect}"
|
|
35
|
+
assert_equal 'VIEW', table_info['TABLE_TYPE'], "Table Info: #{table_info.inspect}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require 'bundler' ; Bundler.require :default, :development, :test
|
|
2
|
+
require 'support/paths_sqlserver'
|
|
3
|
+
require 'support/minitest_sqlserver'
|
|
4
|
+
require 'cases/helper'
|
|
5
|
+
require 'support/load_schema_sqlserver'
|
|
6
|
+
require 'support/coerceable_test_sqlserver'
|
|
7
|
+
require 'support/sql_counter_sqlserver'
|
|
8
|
+
require 'mocha/mini_test'
|
|
9
|
+
|
|
10
|
+
module ActiveRecord
|
|
11
|
+
class TestCase < ActiveSupport::TestCase
|
|
12
|
+
|
|
13
|
+
SQLServer = ActiveRecord::ConnectionAdapters::SQLServer
|
|
14
|
+
|
|
15
|
+
include ARTest::SQLServer::CoerceableTest
|
|
16
|
+
|
|
17
|
+
let(:logger) { ActiveRecord::Base.logger }
|
|
18
|
+
let(:connection) { ActiveRecord::Base.connection }
|
|
19
|
+
|
|
20
|
+
class << self
|
|
21
|
+
def connection_mode_dblib? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :dblib ; end
|
|
22
|
+
def connection_mode_odbc? ; ActiveRecord::Base.connection.instance_variable_get(:@connection_options)[:mode] == :odbc ; end
|
|
23
|
+
def sqlserver_azure? ; ActiveRecord::Base.connection.sqlserver_azure? ; end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def connection_mode_dblib? ; self.class.connection_mode_dblib? ; end
|
|
30
|
+
def connection_mode_odbc? ; self.class.connection_mode_odbc? ; end
|
|
31
|
+
def sqlserver_azure? ; self.class.sqlserver_azure? ; end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Dir["#{ARTest::SQLServer.test_root_sqlserver}/models/**/*.rb"].each { |f| require f }
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
require 'models/person'
|
|
3
|
+
|
|
4
|
+
class MigrationTestSQLServer < ActiveRecord::TestCase
|
|
5
|
+
|
|
6
|
+
describe 'For transactions' do
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
@trans_test_table1 = 'sqlserver_trans_table1'
|
|
10
|
+
@trans_test_table2 = 'sqlserver_trans_table2'
|
|
11
|
+
@trans_tables = [@trans_test_table1,@trans_test_table2]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
after do
|
|
15
|
+
@trans_tables.each do |table_name|
|
|
16
|
+
ActiveRecord::Migration.drop_table(table_name) if connection.tables.include?(table_name)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'not create a tables if error in migrations' do
|
|
21
|
+
begin
|
|
22
|
+
migrations_dir = File.join ARTest::SQLServer.migrations_root, 'transaction_table'
|
|
23
|
+
quietly { ActiveRecord::Migrator.up(migrations_dir) }
|
|
24
|
+
rescue Exception => e
|
|
25
|
+
assert_match %r|this and all later migrations canceled|, e.message
|
|
26
|
+
end
|
|
27
|
+
connection.tables.wont_include @trans_test_table1
|
|
28
|
+
connection.tables.wont_include @trans_test_table2
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe 'For changing column' do
|
|
34
|
+
|
|
35
|
+
it 'not raise exception when column contains default constraint' do
|
|
36
|
+
lock_version_column = Person.columns_hash['lock_version']
|
|
37
|
+
assert_equal :integer, lock_version_column.type
|
|
38
|
+
assert lock_version_column.default.present?
|
|
39
|
+
assert_nothing_raised { connection.change_column 'people', 'lock_version', :string }
|
|
40
|
+
Person.reset_column_information
|
|
41
|
+
lock_version_column = Person.columns_hash['lock_version']
|
|
42
|
+
assert_equal :string, lock_version_column.type
|
|
43
|
+
assert lock_version_column.default.nil?
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it 'not drop the default contraint if just renaming' do
|
|
47
|
+
find_default = lambda do
|
|
48
|
+
connection.execute_procedure(:sp_helpconstraint, 'sst_string_defaults', 'nomsg').select do |row|
|
|
49
|
+
row['constraint_type'] == "DEFAULT on column string_with_pretend_paren_three"
|
|
50
|
+
end.last
|
|
51
|
+
end
|
|
52
|
+
default_before = find_default.call
|
|
53
|
+
connection.change_column :sst_string_defaults, :string_with_pretend_paren_three, :string, limit: 255
|
|
54
|
+
default_after = find_default.call
|
|
55
|
+
assert default_after
|
|
56
|
+
assert_equal default_before['constraint_keys'], default_after['constraint_keys']
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def quietly
|
|
63
|
+
silence_stream(STDOUT) { silence_stream(STDERR) { yield } }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require 'cases/helper_sqlserver'
|
|
2
|
+
require 'models/post'
|
|
3
|
+
|
|
4
|
+
class OrderTestSQLServer < ActiveRecord::TestCase
|
|
5
|
+
|
|
6
|
+
fixtures :posts
|
|
7
|
+
|
|
8
|
+
it 'not mangel complex order clauses' do
|
|
9
|
+
xyz_order = "CASE WHEN [title] LIKE N'XYZ%' THEN 0 ELSE 1 END"
|
|
10
|
+
xyz_post = Post.create title: 'XYZ Post', body: 'Test cased orders.'
|
|
11
|
+
assert_equal xyz_post, Post.order(Arel.sql(xyz_order)).first
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'support column' do
|
|
15
|
+
order = "title"
|
|
16
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
17
|
+
assert_equal post1, Post.order(order).first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it 'support column ASC' do
|
|
21
|
+
order = "title ASC"
|
|
22
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
23
|
+
assert_equal post1, Post.order(order).first
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it 'support column DESC' do
|
|
27
|
+
order = "title DESC"
|
|
28
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
29
|
+
assert_equal post1, Post.order(order).first
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it 'support column as symbol' do
|
|
33
|
+
order = :title
|
|
34
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
35
|
+
assert_equal post1, Post.order(order).first
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'support table and column' do
|
|
39
|
+
order = "posts.title"
|
|
40
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
41
|
+
assert_equal post1, Post.order(order).first
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'support quoted column' do
|
|
45
|
+
order = "[title]"
|
|
46
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
47
|
+
assert_equal post1, Post.order(order).first
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it 'support quoted table and column' do
|
|
51
|
+
order = "[posts].[title]"
|
|
52
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
53
|
+
assert_equal post1, Post.order(order).first
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'support primary: column, secondary: column' do
|
|
57
|
+
order = "title DESC, body"
|
|
58
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
59
|
+
post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.'
|
|
60
|
+
assert_equal post1, Post.order(order).first
|
|
61
|
+
assert_equal post2, Post.order(order).second
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'support primary: table and column, secondary: column' do
|
|
65
|
+
order = "posts.title DESC, body"
|
|
66
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
67
|
+
post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.'
|
|
68
|
+
assert_equal post1, Post.order(order).first
|
|
69
|
+
assert_equal post2, Post.order(order).second
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
it 'support primary: case expression, secondary: column' do
|
|
73
|
+
order = "(CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC, body"
|
|
74
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
75
|
+
post2 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.'
|
|
76
|
+
assert_equal post1, Post.order(order).first
|
|
77
|
+
assert_equal post2, Post.order(order).second
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'support primary: quoted table and column, secondary: case expresion' do
|
|
81
|
+
order = "[posts].[body] DESC, (CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END) DESC"
|
|
82
|
+
post1 = Post.create title: 'ZZZ Post', body: 'ZZZ Test cased orders.'
|
|
83
|
+
post2 = Post.create title: 'ZZY Post', body: 'ZZZ Test cased orders.'
|
|
84
|
+
assert_equal post1, Post.order(order).first
|
|
85
|
+
assert_equal post2, Post.order(order).second
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'support inline function' do
|
|
89
|
+
order = "LEN(title)"
|
|
90
|
+
post1 = Post.create title: 'A', body: 'AAA Test cased orders.'
|
|
91
|
+
assert_equal post1, Post.order(order).first
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'support inline function with parameters' do
|
|
95
|
+
order = "SUBSTRING(title, 1, 3)"
|
|
96
|
+
post1 = Post.create title: 'AAA Post', body: 'Test cased orders.'
|
|
97
|
+
assert_equal post1, Post.order(order).first
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it 'support inline function with parameters DESC' do
|
|
101
|
+
order = "SUBSTRING(title, 1, 3) DESC"
|
|
102
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
103
|
+
assert_equal post1, Post.order(order).first
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
it 'support primary: inline function, secondary: column' do
|
|
107
|
+
order = "LEN(title), body"
|
|
108
|
+
post1 = Post.create title: 'A', body: 'AAA Test cased orders.'
|
|
109
|
+
post2 = Post.create title: 'A', body: 'Test cased orders.'
|
|
110
|
+
assert_equal post1, Post.order(order).first
|
|
111
|
+
assert_equal post2, Post.order(order).second
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'support primary: inline function, secondary: column with direction' do
|
|
115
|
+
order = "LEN(title) ASC, body DESC"
|
|
116
|
+
post1 = Post.create title: 'A', body: 'ZZZ Test cased orders.'
|
|
117
|
+
post2 = Post.create title: 'A', body: 'Test cased orders.'
|
|
118
|
+
assert_equal post1, Post.order(order).first
|
|
119
|
+
assert_equal post2, Post.order(order).second
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'support primary: column, secondary: inline function' do
|
|
123
|
+
order = "body DESC, LEN(title)"
|
|
124
|
+
post1 = Post.create title: 'Post', body: 'ZZZ Test cased orders.'
|
|
125
|
+
post2 = Post.create title: 'Longer Post', body: 'ZZZ Test cased orders.'
|
|
126
|
+
assert_equal post1, Post.order(order).first
|
|
127
|
+
assert_equal post2, Post.order(order).second
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
it 'support primary: case expression, secondary: inline function' do
|
|
131
|
+
order = "CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC, LEN(body) ASC"
|
|
132
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Z'
|
|
133
|
+
post2 = Post.create title: 'ZZZ Post', body: 'Test cased orders.'
|
|
134
|
+
assert_equal post1, Post.order(order).first
|
|
135
|
+
assert_equal post2, Post.order(order).second
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
it 'support primary: inline function, secondary: case expression' do
|
|
139
|
+
order = "LEN(body), CASE WHEN [title] LIKE N'ZZZ%' THEN title ELSE '' END DESC"
|
|
140
|
+
post1 = Post.create title: 'ZZZ Post', body: 'Z'
|
|
141
|
+
post2 = Post.create title: 'Post', body: 'Z'
|
|
142
|
+
assert_equal post1, Post.order(order).first
|
|
143
|
+
assert_equal post2, Post.order(order).second
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
end
|