tiny_tds 1.0.4 → 3.2.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 +5 -5
- data/.codeclimate.yml +20 -0
- data/.gitattributes +1 -0
- data/.github/workflows/ci.yml +590 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +31 -0
- data/{CHANGELOG → CHANGELOG.md} +133 -26
- data/Gemfile +1 -5
- data/ISSUE_TEMPLATE.md +36 -3
- data/README.md +147 -85
- data/Rakefile +51 -94
- data/VERSION +1 -1
- data/docker-compose.yml +34 -0
- data/ext/tiny_tds/client.c +149 -67
- data/ext/tiny_tds/client.h +11 -5
- data/ext/tiny_tds/extconf.rb +144 -283
- data/ext/tiny_tds/extconsts.rb +4 -11
- data/ext/tiny_tds/result.c +68 -50
- data/ext/tiny_tds/tiny_tds_ext.c +4 -1
- data/lib/tiny_tds/bin.rb +44 -40
- data/lib/tiny_tds/client.rb +63 -55
- data/lib/tiny_tds/error.rb +0 -3
- data/lib/tiny_tds/gem.rb +23 -0
- data/lib/tiny_tds/result.rb +0 -3
- data/lib/tiny_tds.rb +37 -32
- data/{ports/patches/freetds/1.00 → patches/freetds/1.00.27}/0001-mingw_missing_inet_pton.diff +4 -4
- data/patches/freetds/1.00.27/0002-Don-t-use-MSYS2-file-libws2_32.diff +28 -0
- data/patches/libiconv/1.14/1-avoid-gets-error.patch +17 -0
- data/setup_cimgruby_dev.sh +25 -0
- data/start_dev.sh +21 -0
- data/tasks/native_gem.rake +16 -0
- data/tasks/package.rake +6 -0
- data/tasks/ports.rake +24 -0
- data/tasks/test.rake +7 -0
- data/test/bin/install-freetds.sh +18 -0
- data/test/bin/install-mssql.ps1 +42 -0
- data/test/bin/install-mssqltools.sh +9 -0
- data/test/bin/install-openssl.sh +18 -0
- data/test/bin/restore-from-native-gem.ps1 +10 -0
- data/test/bin/setup_tinytds_db.sh +7 -0
- data/test/bin/setup_volume_permissions.sh +10 -0
- data/test/client_test.rb +161 -112
- data/test/gem_test.rb +100 -0
- data/test/result_test.rb +293 -313
- data/test/schema_test.rb +369 -395
- data/test/sql/db-create.sql +18 -0
- data/test/sql/db-login.sql +38 -0
- data/test/test_helper.rb +116 -85
- data/test/thread_test.rb +22 -31
- data/tiny_tds.gemspec +27 -24
- metadata +109 -56
- data/appveyor.yml +0 -51
- data/test/appveyor/dbsetup.ps1 +0 -27
- data/test/appveyor/dbsetup.sql +0 -9
- data/test/benchmark/query.rb +0 -77
- data/test/benchmark/query_odbc.rb +0 -106
- data/test/benchmark/query_tinytds.rb +0 -126
- data/test/schema/sqlserver_2000.sql +0 -140
- data/test/schema/sqlserver_2005.sql +0 -140
- data/test/schema/sqlserver_2014.sql +0 -140
- data/test/schema/sybase_ase.sql +0 -138
- /data/bin/{defncopy → defncopy-ttds} +0 -0
- /data/bin/{tsql → tsql-ttds} +0 -0
- /data/test/schema/{sqlserver_2008.sql → sqlserver_2017.sql} +0 -0
data/test/result_test.rb
CHANGED
@@ -1,43 +1,40 @@
|
|
1
|
-
|
2
|
-
require 'test_helper'
|
1
|
+
require "test_helper"
|
3
2
|
|
4
3
|
class ResultTest < TinyTds::TestCase
|
5
|
-
|
6
|
-
describe 'Basic query and result' do
|
7
|
-
|
4
|
+
describe "Basic query and result" do
|
8
5
|
before do
|
9
6
|
@@current_schema_loaded ||= load_current_schema
|
10
7
|
@client = new_connection
|
11
|
-
@query1 =
|
8
|
+
@query1 = "SELECT 1 AS [one]"
|
12
9
|
end
|
13
10
|
|
14
|
-
it
|
11
|
+
it "has included Enumerable" do
|
15
12
|
assert TinyTds::Result.ancestors.include?(Enumerable)
|
16
13
|
end
|
17
14
|
|
18
|
-
it
|
15
|
+
it "responds to #each" do
|
19
16
|
result = @client.execute(@query1)
|
20
17
|
assert result.respond_to?(:each)
|
21
18
|
end
|
22
19
|
|
23
|
-
it
|
20
|
+
it "returns all results for #each with no block" do
|
24
21
|
result = @client.execute(@query1)
|
25
22
|
data = result.each
|
26
23
|
row = data.first
|
27
24
|
assert_instance_of Array, data
|
28
25
|
assert_equal 1, data.size
|
29
|
-
assert_instance_of Hash, row,
|
26
|
+
assert_instance_of Hash, row, "hash is the default query option"
|
30
27
|
end
|
31
28
|
|
32
|
-
it
|
29
|
+
it "returns all results for #each with a block yielding a row at a time" do
|
33
30
|
result = @client.execute(@query1)
|
34
31
|
data = result.each do |row|
|
35
|
-
assert_instance_of Hash, row,
|
32
|
+
assert_instance_of Hash, row, "hash is the default query option"
|
36
33
|
end
|
37
34
|
assert_instance_of Array, data
|
38
35
|
end
|
39
36
|
|
40
|
-
it
|
37
|
+
it "allows successive calls to each returning the same data" do
|
41
38
|
result = @client.execute(@query1)
|
42
39
|
data = result.each
|
43
40
|
result.each
|
@@ -45,151 +42,150 @@ class ResultTest < TinyTds::TestCase
|
|
45
42
|
assert_equal data.first.object_id, result.each.first.object_id
|
46
43
|
end
|
47
44
|
|
48
|
-
it
|
45
|
+
it "returns hashes with string keys" do
|
49
46
|
result = @client.execute(@query1)
|
50
|
-
row = result.each(:
|
47
|
+
row = result.each(as: :hash, symbolize_keys: false).first
|
51
48
|
assert_instance_of Hash, row
|
52
|
-
assert_equal [
|
53
|
-
assert_equal [
|
49
|
+
assert_equal ["one"], row.keys
|
50
|
+
assert_equal ["one"], result.fields
|
54
51
|
end
|
55
52
|
|
56
|
-
it
|
53
|
+
it "returns hashes with symbol keys" do
|
57
54
|
result = @client.execute(@query1)
|
58
|
-
row = result.each(:
|
55
|
+
row = result.each(as: :hash, symbolize_keys: true).first
|
59
56
|
assert_instance_of Hash, row
|
60
57
|
assert_equal [:one], row.keys
|
61
58
|
assert_equal [:one], result.fields
|
62
59
|
end
|
63
60
|
|
64
|
-
it
|
61
|
+
it "returns arrays with string fields" do
|
65
62
|
result = @client.execute(@query1)
|
66
|
-
row = result.each(:
|
63
|
+
row = result.each(as: :array, symbolize_keys: false).first
|
67
64
|
assert_instance_of Array, row
|
68
|
-
assert_equal [
|
65
|
+
assert_equal ["one"], result.fields
|
69
66
|
end
|
70
67
|
|
71
|
-
it
|
68
|
+
it "returns arrays with symbol fields" do
|
72
69
|
result = @client.execute(@query1)
|
73
|
-
row = result.each(:
|
70
|
+
row = result.each(as: :array, symbolize_keys: true).first
|
74
71
|
assert_instance_of Array, row
|
75
72
|
assert_equal [:one], result.fields
|
76
73
|
end
|
77
74
|
|
78
|
-
it
|
75
|
+
it "allows sql concat + to work" do
|
79
76
|
rollback_transaction(@client) do
|
80
77
|
@client.execute("DELETE FROM [datatypes]").do
|
81
78
|
@client.execute("INSERT INTO [datatypes] ([char_10], [varchar_50]) VALUES ('1', '2')").do
|
82
|
-
result = @client.execute("SELECT TOP (1) [char_10] + 'test' + [varchar_50] AS [test] FROM [datatypes]").each.first[
|
83
|
-
result.must_equal "1 test2"
|
79
|
+
result = @client.execute("SELECT TOP (1) [char_10] + 'test' + [varchar_50] AS [test] FROM [datatypes]").each.first["test"]
|
80
|
+
_(result).must_equal "1 test2"
|
84
81
|
end
|
85
82
|
end
|
86
83
|
|
87
|
-
it
|
84
|
+
it "must be able to turn :cache_rows option off" do
|
88
85
|
result = @client.execute(@query1)
|
89
86
|
local = []
|
90
|
-
result.each(:
|
87
|
+
result.each(cache_rows: false) do |row|
|
91
88
|
local << row
|
92
89
|
end
|
93
|
-
assert local.first,
|
94
|
-
assert_equal [], result.each,
|
95
|
-
assert_equal [
|
90
|
+
assert local.first, "should have iterated over each row"
|
91
|
+
assert_equal [], result.each, "should not have been cached"
|
92
|
+
assert_equal ["one"], result.fields, "should still cache field names"
|
96
93
|
end
|
97
94
|
|
98
|
-
it
|
95
|
+
it "must be able to get the first result row only" do
|
99
96
|
load_current_schema
|
100
97
|
big_query = "SELECT [id] FROM [datatypes]"
|
101
|
-
one = @client.execute(big_query).each(:
|
98
|
+
one = @client.execute(big_query).each(first: true)
|
102
99
|
many = @client.execute(big_query).each
|
103
100
|
assert many.size > 1
|
104
101
|
assert one.size == 1
|
105
102
|
end
|
106
103
|
|
107
|
-
it
|
108
|
-
data = @client.execute("SELECT [id] FROM [datatypes] WHERE [id] = -1").each(:
|
104
|
+
it "copes with no results when using first option" do
|
105
|
+
data = @client.execute("SELECT [id] FROM [datatypes] WHERE [id] = -1").each(first: true)
|
109
106
|
assert_equal [], data
|
110
107
|
end
|
111
108
|
|
112
|
-
it
|
109
|
+
it "must delete, insert and find data" do
|
113
110
|
rollback_transaction(@client) do
|
114
|
-
text =
|
111
|
+
text = "test insert and delete"
|
115
112
|
@client.execute("DELETE FROM [datatypes] WHERE [varchar_50] IS NOT NULL").do
|
116
113
|
@client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
|
117
114
|
row = @client.execute("SELECT [varchar_50] FROM [datatypes] WHERE [varchar_50] IS NOT NULL").each.first
|
118
115
|
assert row
|
119
|
-
assert_equal text, row[
|
116
|
+
assert_equal text, row["varchar_50"]
|
120
117
|
end
|
121
118
|
end
|
122
119
|
|
123
|
-
it
|
120
|
+
it "must insert and find unicode data" do
|
124
121
|
rollback_transaction(@client) do
|
125
|
-
text =
|
122
|
+
text = "😍"
|
126
123
|
@client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
|
127
124
|
@client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES (N'#{text}')").do
|
128
125
|
row = @client.execute("SELECT [nvarchar_50] FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").each.first
|
129
|
-
assert_equal text, row[
|
126
|
+
assert_equal text, row["nvarchar_50"]
|
130
127
|
end
|
131
128
|
end
|
132
129
|
|
133
|
-
it
|
130
|
+
it "must delete and update with affected rows support and insert with identity support in native sql" do
|
134
131
|
rollback_transaction(@client) do
|
135
|
-
text =
|
132
|
+
text = "test affected rows sql"
|
136
133
|
@client.execute("DELETE FROM [datatypes]").do
|
137
|
-
afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first[
|
138
|
-
|
134
|
+
afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first["AffectedRows"]
|
135
|
+
_(["Fixnum", "Integer"]).must_include afrows.class.name
|
139
136
|
@client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
|
140
|
-
pk1 = @client.execute(@client.identity_sql).each.first[
|
141
|
-
|
137
|
+
pk1 = @client.execute(@client.identity_sql).each.first["Ident"]
|
138
|
+
_(["Fixnum", "Integer"]).must_include pk1.class.name, "we it be able to CAST to bigint"
|
142
139
|
@client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
|
143
|
-
afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first[
|
140
|
+
afrows = @client.execute("SELECT @@ROWCOUNT AS AffectedRows").each.first["AffectedRows"]
|
144
141
|
assert_equal 1, afrows
|
145
142
|
end
|
146
143
|
end
|
147
144
|
|
148
|
-
it
|
145
|
+
it "has a #do method that cancels result rows and returns affected rows natively" do
|
149
146
|
rollback_transaction(@client) do
|
150
|
-
text =
|
151
|
-
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first[
|
147
|
+
text = "test affected rows native"
|
148
|
+
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first["count"]
|
152
149
|
deleted_rows = @client.execute("DELETE FROM [datatypes]").do
|
153
|
-
assert_equal count, deleted_rows,
|
150
|
+
assert_equal count, deleted_rows, "should have deleted rows equal to count"
|
154
151
|
inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
|
155
|
-
assert_equal 1, inserted_rows,
|
152
|
+
assert_equal 1, inserted_rows, "should have inserted row for one above"
|
156
153
|
updated_rows = @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
|
157
|
-
assert_equal 1, updated_rows,
|
154
|
+
assert_equal 1, updated_rows, "should have updated row for one above"
|
158
155
|
end
|
159
156
|
end
|
160
157
|
|
161
|
-
it
|
158
|
+
it "allows native affected rows using #do to work under transaction" do
|
162
159
|
rollback_transaction(@client) do
|
163
|
-
text =
|
160
|
+
text = "test affected rows native in transaction"
|
164
161
|
@client.execute("BEGIN TRANSACTION").do
|
165
162
|
@client.execute("DELETE FROM [datatypes]").do
|
166
163
|
inserted_rows = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
|
167
|
-
assert_equal 1, inserted_rows,
|
164
|
+
assert_equal 1, inserted_rows, "should have inserted row for one above"
|
168
165
|
updated_rows = @client.execute("UPDATE [datatypes] SET [varchar_50] = NULL WHERE [varchar_50] = '#{text}'").do
|
169
|
-
assert_equal 1, updated_rows,
|
166
|
+
assert_equal 1, updated_rows, "should have updated row for one above"
|
170
167
|
end
|
171
168
|
end
|
172
169
|
|
173
|
-
it
|
170
|
+
it "has an #insert method that cancels result rows and returns IDENTITY natively" do
|
174
171
|
rollback_transaction(@client) do
|
175
|
-
text =
|
172
|
+
text = "test scope identity rows native"
|
176
173
|
@client.execute("DELETE FROM [datatypes] WHERE [varchar_50] = '#{text}'").do
|
177
174
|
@client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").do
|
178
|
-
sql_identity = @client.execute(@client.identity_sql).each.first[
|
175
|
+
sql_identity = @client.execute(@client.identity_sql).each.first["Ident"]
|
179
176
|
native_identity = @client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('#{text}')").insert
|
180
|
-
assert_equal sql_identity+1, native_identity
|
177
|
+
assert_equal sql_identity + 1, native_identity
|
181
178
|
end
|
182
179
|
end
|
183
180
|
|
184
|
-
it
|
181
|
+
it "returns bigint for #insert when needed" do
|
185
182
|
return if sqlserver_azure? # We can not alter clustered index like this test does.
|
186
|
-
|
187
|
-
|
188
|
-
# and and sp_helpindex creates a temporary table #spindtab.
|
183
|
+
# 'CREATE TABLE' command is not allowed within a multi-statement transaction
|
184
|
+
# and and sp_helpindex creates a temporary table #spindtab.
|
189
185
|
rollback_transaction(@client) do
|
190
186
|
seed = 9223372036854775805
|
191
187
|
@client.execute("DELETE FROM [datatypes]").do
|
192
|
-
id_constraint_name = @client.execute("EXEC sp_helpindex [datatypes]").detect{ |row| row[
|
188
|
+
id_constraint_name = @client.execute("EXEC sp_helpindex [datatypes]").detect { |row| row["index_keys"] == "id" }["index_name"]
|
193
189
|
@client.execute("ALTER TABLE [datatypes] DROP CONSTRAINT [#{id_constraint_name}]").do
|
194
190
|
@client.execute("ALTER TABLE [datatypes] DROP COLUMN [id]").do
|
195
191
|
@client.execute("ALTER TABLE [datatypes] ADD [id] [bigint] NOT NULL IDENTITY(1,1) PRIMARY KEY").do
|
@@ -199,146 +195,154 @@ class ResultTest < TinyTds::TestCase
|
|
199
195
|
end
|
200
196
|
end
|
201
197
|
|
202
|
-
it
|
198
|
+
it "must be able to begin/commit transactions with raw sql" do
|
203
199
|
rollback_transaction(@client) do
|
204
200
|
@client.execute("BEGIN TRANSACTION").do
|
205
201
|
@client.execute("DELETE FROM [datatypes]").do
|
206
202
|
@client.execute("COMMIT TRANSACTION").do
|
207
|
-
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first[
|
203
|
+
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first["count"]
|
208
204
|
assert_equal 0, count
|
209
205
|
end
|
210
206
|
end
|
211
207
|
|
212
|
-
it
|
208
|
+
it "must be able to begin/rollback transactions with raw sql" do
|
213
209
|
load_current_schema
|
214
210
|
@client.execute("BEGIN TRANSACTION").do
|
215
211
|
@client.execute("DELETE FROM [datatypes]").do
|
216
212
|
@client.execute("ROLLBACK TRANSACTION").do
|
217
|
-
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first[
|
218
|
-
|
213
|
+
count = @client.execute("SELECT COUNT(*) AS [count] FROM [datatypes]").each.first["count"]
|
214
|
+
_(count).wont_equal 0
|
219
215
|
end
|
220
216
|
|
221
|
-
it
|
217
|
+
it "has a #fields accessor with logic default and valid outcome" do
|
222
218
|
result = @client.execute(@query1)
|
223
|
-
result.fields.must_equal [
|
219
|
+
_(result.fields).must_equal ["one"]
|
224
220
|
result.each
|
225
|
-
result.fields.must_equal [
|
221
|
+
_(result.fields).must_equal ["one"]
|
226
222
|
end
|
227
223
|
|
228
|
-
it
|
224
|
+
it "always returns an array for fields for all sql" do
|
229
225
|
result = @client.execute("USE [tinytdstest]")
|
230
|
-
result.fields.must_equal []
|
226
|
+
_(result.fields).must_equal []
|
231
227
|
result.do
|
232
|
-
result.fields.must_equal []
|
228
|
+
_(result.fields).must_equal []
|
233
229
|
end
|
234
230
|
|
235
|
-
it
|
231
|
+
it "returns fields even when no results are found" do
|
236
232
|
no_results_query = "SELECT [id], [varchar_50] FROM [datatypes] WHERE [varchar_50] = 'NOTFOUND'"
|
237
233
|
# Fields before each.
|
238
234
|
result = @client.execute(no_results_query)
|
239
|
-
result.fields.must_equal [
|
235
|
+
_(result.fields).must_equal ["id", "varchar_50"]
|
240
236
|
result.each
|
241
|
-
result.fields.must_equal [
|
237
|
+
_(result.fields).must_equal ["id", "varchar_50"]
|
242
238
|
# Each then fields
|
243
239
|
result = @client.execute(no_results_query)
|
244
240
|
result.each
|
245
|
-
result.fields.must_equal [
|
241
|
+
_(result.fields).must_equal ["id", "varchar_50"]
|
246
242
|
end
|
247
243
|
|
248
|
-
it
|
244
|
+
it "allows the result to be canceled before reading" do
|
249
245
|
result = @client.execute(@query1)
|
250
246
|
result.cancel
|
251
247
|
@client.execute(@query1).each
|
252
248
|
end
|
253
249
|
|
254
|
-
it
|
250
|
+
it "works in tandem with the client when needing to find out if client has sql sent and result is canceled or not" do
|
255
251
|
# Default state.
|
256
252
|
@client = TinyTds::Client.new(connection_options)
|
257
|
-
@client.sqlsent
|
258
|
-
@client.canceled
|
253
|
+
_(@client.sqlsent?).must_equal false
|
254
|
+
_(@client.canceled?).must_equal false
|
259
255
|
# With active result before and after cancel.
|
260
256
|
result = @client.execute(@query1)
|
261
|
-
@client.sqlsent
|
262
|
-
@client.canceled
|
257
|
+
_(@client.sqlsent?).must_equal true
|
258
|
+
_(@client.canceled?).must_equal false
|
263
259
|
result.cancel
|
264
|
-
@client.sqlsent
|
265
|
-
@client.canceled
|
266
|
-
assert result.cancel,
|
260
|
+
_(@client.sqlsent?).must_equal false
|
261
|
+
_(@client.canceled?).must_equal true
|
262
|
+
assert result.cancel, "must be safe to call again"
|
267
263
|
# With each and no block.
|
268
264
|
@client.execute(@query1).each
|
269
|
-
@client.sqlsent
|
270
|
-
@client.canceled
|
265
|
+
_(@client.sqlsent?).must_equal false
|
266
|
+
_(@client.canceled?).must_equal false
|
271
267
|
# With each and block.
|
272
268
|
@client.execute(@query1).each do |row|
|
273
|
-
@client.sqlsent
|
274
|
-
@client.canceled
|
269
|
+
_(@client.sqlsent?).must_equal true, "when iterating over each row in a block"
|
270
|
+
_(@client.canceled?).must_equal false
|
275
271
|
end
|
276
|
-
@client.sqlsent
|
277
|
-
@client.canceled
|
272
|
+
_(@client.sqlsent?).must_equal false
|
273
|
+
_(@client.canceled?).must_equal false
|
278
274
|
# With each and block canceled half way thru.
|
279
|
-
count = @client.execute("SELECT COUNT([id]) AS [count] FROM [datatypes]").each[0][
|
280
|
-
assert count > 10,
|
275
|
+
count = @client.execute("SELECT COUNT([id]) AS [count] FROM [datatypes]").each[0]["count"]
|
276
|
+
assert count > 10, "since we want to cancel early for test"
|
281
277
|
result = @client.execute("SELECT [id] FROM [datatypes]")
|
282
278
|
index = 0
|
283
279
|
result.each do |row|
|
284
280
|
break if index > 10
|
285
281
|
index += 1
|
286
282
|
end
|
287
|
-
@client.sqlsent
|
288
|
-
@client.canceled
|
283
|
+
_(@client.sqlsent?).must_equal true
|
284
|
+
_(@client.canceled?).must_equal false
|
289
285
|
result.cancel
|
290
|
-
@client.sqlsent
|
291
|
-
@client.canceled
|
286
|
+
_(@client.sqlsent?).must_equal false
|
287
|
+
_(@client.canceled?).must_equal true
|
292
288
|
# With do method.
|
293
289
|
@client.execute(@query1).do
|
294
|
-
@client.sqlsent
|
295
|
-
@client.canceled
|
290
|
+
_(@client.sqlsent?).must_equal false
|
291
|
+
_(@client.canceled?).must_equal true
|
296
292
|
# With insert method.
|
297
293
|
rollback_transaction(@client) do
|
298
294
|
@client.execute("INSERT INTO [datatypes] ([varchar_50]) VALUES ('test')").insert
|
299
|
-
@client.sqlsent
|
300
|
-
@client.canceled
|
295
|
+
_(@client.sqlsent?).must_equal false
|
296
|
+
_(@client.canceled?).must_equal true
|
301
297
|
end
|
302
298
|
# With first
|
303
|
-
@client.execute("SELECT [id] FROM [datatypes]").each(:
|
304
|
-
@client.sqlsent
|
305
|
-
@client.canceled
|
299
|
+
@client.execute("SELECT [id] FROM [datatypes]").each(first: true)
|
300
|
+
_(@client.sqlsent?).must_equal false
|
301
|
+
_(@client.canceled?).must_equal true
|
306
302
|
end
|
307
303
|
|
308
|
-
it
|
304
|
+
it "use same string object for hash keys" do
|
309
305
|
data = @client.execute("SELECT [id], [bigint] FROM [datatypes]").each
|
310
|
-
assert_equal data.first.keys.map{ |r| r.object_id }, data.last.keys.map{ |r| r.object_id }
|
306
|
+
assert_equal data.first.keys.map { |r| r.object_id }, data.last.keys.map { |r| r.object_id }
|
311
307
|
end
|
312
308
|
|
313
|
-
it
|
309
|
+
it "has properly encoded column names with symbol keys" do
|
314
310
|
col_name = "öäüß"
|
315
|
-
|
311
|
+
begin
|
312
|
+
@client.execute("DROP TABLE [test_encoding]").do
|
313
|
+
rescue
|
314
|
+
nil
|
315
|
+
end
|
316
316
|
@client.execute("CREATE TABLE [dbo].[test_encoding] ( [id] int NOT NULL IDENTITY(1,1) PRIMARY KEY, [#{col_name}] [nvarchar](10) NOT NULL )").do
|
317
317
|
@client.execute("INSERT INTO [test_encoding] ([#{col_name}]) VALUES (N'#{col_name}')").do
|
318
318
|
result = @client.execute("SELECT [#{col_name}] FROM [test_encoding]")
|
319
|
-
row = result.each(:
|
319
|
+
row = result.each(as: :hash, symbolize_keys: true).first
|
320
320
|
assert_instance_of Symbol, result.fields.first
|
321
321
|
assert_equal col_name.to_sym, result.fields.first
|
322
322
|
assert_instance_of Symbol, row.keys.first
|
323
323
|
assert_equal col_name.to_sym, row.keys.first
|
324
324
|
end
|
325
325
|
|
326
|
-
it
|
326
|
+
it "allows #return_code to work with stored procedures and reset per sql batch" do
|
327
327
|
assert_nil @client.return_code
|
328
328
|
result = @client.execute("EXEC tinytds_TestReturnCodes")
|
329
|
-
assert_equal [{"one"=>1}], result.each
|
329
|
+
assert_equal [{"one" => 1}], result.each
|
330
330
|
assert_equal 420, @client.return_code
|
331
331
|
assert_equal 420, result.return_code
|
332
|
-
result = @client.execute(
|
332
|
+
result = @client.execute("SELECT 1 as [one]")
|
333
333
|
result.each
|
334
334
|
assert_nil @client.return_code
|
335
335
|
assert_nil result.return_code
|
336
336
|
end
|
337
337
|
|
338
|
-
|
338
|
+
it "with LOGINPROPERTY function" do
|
339
|
+
v = @client.execute("SELECT LOGINPROPERTY('sa', 'IsLocked') as v").first["v"]
|
340
|
+
_(v).must_equal 0
|
341
|
+
end
|
339
342
|
|
343
|
+
describe "with multiple result sets" do
|
340
344
|
before do
|
341
|
-
@empty_select
|
345
|
+
@empty_select = "SELECT 1 AS [rs1] WHERE 1 = 0"
|
342
346
|
@double_select = "SELECT 1 AS [rs1]
|
343
347
|
SELECT 2 AS [rs2]"
|
344
348
|
@triple_select_1st_empty = "SELECT 1 AS [rs1] WHERE 1 = 0
|
@@ -352,50 +356,41 @@ class ResultTest < TinyTds::TestCase
|
|
352
356
|
SELECT 3 AS [rs3] WHERE 1 = 0"
|
353
357
|
end
|
354
358
|
|
355
|
-
it
|
359
|
+
it "handles a command buffer with double selects" do
|
356
360
|
result = @client.execute(@double_select)
|
357
361
|
result_sets = result.each
|
358
362
|
assert_equal 2, result_sets.size
|
359
|
-
assert_equal [{
|
360
|
-
assert_equal [{
|
361
|
-
assert_equal [[
|
362
|
-
assert_equal result.each.object_id, result.each.object_id,
|
363
|
+
assert_equal [{"rs1" => 1}], result_sets.first
|
364
|
+
assert_equal [{"rs2" => 2}], result_sets.last
|
365
|
+
assert_equal [["rs1"], ["rs2"]], result.fields
|
366
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
363
367
|
# As array
|
364
368
|
result = @client.execute(@double_select)
|
365
|
-
result_sets = result.each(:
|
369
|
+
result_sets = result.each(as: :array)
|
366
370
|
assert_equal 2, result_sets.size
|
367
371
|
assert_equal [[1]], result_sets.first
|
368
372
|
assert_equal [[2]], result_sets.last
|
369
|
-
assert_equal [[
|
370
|
-
assert_equal result.each.object_id, result.each.object_id,
|
373
|
+
assert_equal [["rs1"], ["rs2"]], result.fields
|
374
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
371
375
|
end
|
372
376
|
|
373
|
-
it
|
377
|
+
it "yields each row for each result set" do
|
374
378
|
data = []
|
375
379
|
result_sets = @client.execute(@double_select).each { |row| data << row }
|
376
380
|
assert_equal data.first, result_sets.first[0]
|
377
381
|
assert_equal data.last, result_sets.last[0]
|
378
382
|
end
|
379
383
|
|
380
|
-
it
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
assert constraint_info.key?("constraint_name")
|
388
|
-
elsif sybase_ase?
|
389
|
-
results1, results2 = @client.execute("EXEC sp_helpconstraint 'datatypes'").each
|
390
|
-
assert results1['name'] =~ /^datatypes_bit/
|
391
|
-
assert results1['defintion'] == 'DEFAULT 0'
|
392
|
-
assert results2['name'] =~ /^datatypes_id/
|
393
|
-
assert results2['defintion'] =~ /^PRIMARY KEY/
|
394
|
-
end
|
384
|
+
it "works from a stored procedure" do
|
385
|
+
results1, results2 = @client.execute("EXEC sp_helpconstraint '[datatypes]'").each
|
386
|
+
assert_equal [{"Object Name" => "[datatypes]"}], results1
|
387
|
+
constraint_info = results2.first
|
388
|
+
assert constraint_info.key?("constraint_keys")
|
389
|
+
assert constraint_info.key?("constraint_type")
|
390
|
+
assert constraint_info.key?("constraint_name")
|
395
391
|
end
|
396
392
|
|
397
|
-
describe
|
398
|
-
|
393
|
+
describe "using :empty_sets TRUE" do
|
399
394
|
before do
|
400
395
|
close_client
|
401
396
|
@old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
|
@@ -407,76 +402,74 @@ class ResultTest < TinyTds::TestCase
|
|
407
402
|
TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
|
408
403
|
end
|
409
404
|
|
410
|
-
it
|
405
|
+
it "handles a basic empty result set" do
|
411
406
|
result = @client.execute(@empty_select)
|
412
407
|
assert_equal [], result.each
|
413
|
-
assert_equal [
|
408
|
+
assert_equal ["rs1"], result.fields
|
414
409
|
end
|
415
410
|
|
416
|
-
it
|
411
|
+
it "includes empty result sets by default - using 1st empty buffer" do
|
417
412
|
result = @client.execute(@triple_select_1st_empty)
|
418
413
|
result_sets = result.each
|
419
414
|
assert_equal 3, result_sets.size
|
420
415
|
assert_equal [], result_sets[0]
|
421
|
-
assert_equal [{
|
422
|
-
assert_equal [{
|
423
|
-
assert_equal [[
|
424
|
-
assert_equal result.each.object_id, result.each.object_id,
|
416
|
+
assert_equal [{"rs2" => 2}], result_sets[1]
|
417
|
+
assert_equal [{"rs3" => 3}], result_sets[2]
|
418
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
419
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
425
420
|
# As array
|
426
421
|
result = @client.execute(@triple_select_1st_empty)
|
427
|
-
result_sets = result.each(:
|
422
|
+
result_sets = result.each(as: :array)
|
428
423
|
assert_equal 3, result_sets.size
|
429
424
|
assert_equal [], result_sets[0]
|
430
425
|
assert_equal [[2]], result_sets[1]
|
431
426
|
assert_equal [[3]], result_sets[2]
|
432
|
-
assert_equal [[
|
433
|
-
assert_equal result.each.object_id, result.each.object_id,
|
427
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
428
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
434
429
|
end
|
435
430
|
|
436
|
-
it
|
431
|
+
it "includes empty result sets by default - using 2nd empty buffer" do
|
437
432
|
result = @client.execute(@triple_select_2nd_empty)
|
438
433
|
result_sets = result.each
|
439
434
|
assert_equal 3, result_sets.size
|
440
|
-
assert_equal [{
|
435
|
+
assert_equal [{"rs1" => 1}], result_sets[0]
|
441
436
|
assert_equal [], result_sets[1]
|
442
|
-
assert_equal [{
|
443
|
-
assert_equal [[
|
444
|
-
assert_equal result.each.object_id, result.each.object_id,
|
437
|
+
assert_equal [{"rs3" => 3}], result_sets[2]
|
438
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
439
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
445
440
|
# As array
|
446
441
|
result = @client.execute(@triple_select_2nd_empty)
|
447
|
-
result_sets = result.each(:
|
442
|
+
result_sets = result.each(as: :array)
|
448
443
|
assert_equal 3, result_sets.size
|
449
444
|
assert_equal [[1]], result_sets[0]
|
450
445
|
assert_equal [], result_sets[1]
|
451
446
|
assert_equal [[3]], result_sets[2]
|
452
|
-
assert_equal [[
|
453
|
-
assert_equal result.each.object_id, result.each.object_id,
|
447
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
448
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
454
449
|
end
|
455
450
|
|
456
|
-
it
|
451
|
+
it "includes empty result sets by default - using 3rd empty buffer" do
|
457
452
|
result = @client.execute(@triple_select_3rd_empty)
|
458
453
|
result_sets = result.each
|
459
454
|
assert_equal 3, result_sets.size
|
460
|
-
assert_equal [{
|
461
|
-
assert_equal [{
|
455
|
+
assert_equal [{"rs1" => 1}], result_sets[0]
|
456
|
+
assert_equal [{"rs2" => 2}], result_sets[1]
|
462
457
|
assert_equal [], result_sets[2]
|
463
|
-
assert_equal [[
|
464
|
-
assert_equal result.each.object_id, result.each.object_id,
|
458
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
459
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
465
460
|
# As array
|
466
461
|
result = @client.execute(@triple_select_3rd_empty)
|
467
|
-
result_sets = result.each(:
|
462
|
+
result_sets = result.each(as: :array)
|
468
463
|
assert_equal 3, result_sets.size
|
469
464
|
assert_equal [[1]], result_sets[0]
|
470
465
|
assert_equal [[2]], result_sets[1]
|
471
466
|
assert_equal [], result_sets[2]
|
472
|
-
assert_equal [[
|
473
|
-
assert_equal result.each.object_id, result.each.object_id,
|
467
|
+
assert_equal [["rs1"], ["rs2"], ["rs3"]], result.fields
|
468
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
474
469
|
end
|
475
|
-
|
476
470
|
end
|
477
471
|
|
478
|
-
describe
|
479
|
-
|
472
|
+
describe "using :empty_sets FALSE" do
|
480
473
|
before do
|
481
474
|
close_client
|
482
475
|
@old_query_option_value = TinyTds::Client.default_query_options[:empty_sets]
|
@@ -488,231 +481,220 @@ class ResultTest < TinyTds::TestCase
|
|
488
481
|
TinyTds::Client.default_query_options[:empty_sets] = @old_query_option_value
|
489
482
|
end
|
490
483
|
|
491
|
-
it
|
484
|
+
it "handles a basic empty result set" do
|
492
485
|
result = @client.execute(@empty_select)
|
493
486
|
assert_equal [], result.each
|
494
|
-
assert_equal [
|
487
|
+
assert_equal ["rs1"], result.fields
|
495
488
|
end
|
496
489
|
|
497
|
-
it
|
490
|
+
it "must not include empty result sets by default - using 1st empty buffer" do
|
498
491
|
result = @client.execute(@triple_select_1st_empty)
|
499
492
|
result_sets = result.each
|
500
493
|
assert_equal 2, result_sets.size
|
501
|
-
assert_equal [{
|
502
|
-
assert_equal [{
|
503
|
-
assert_equal [[
|
504
|
-
assert_equal result.each.object_id, result.each.object_id,
|
494
|
+
assert_equal [{"rs2" => 2}], result_sets[0]
|
495
|
+
assert_equal [{"rs3" => 3}], result_sets[1]
|
496
|
+
assert_equal [["rs2"], ["rs3"]], result.fields
|
497
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
505
498
|
# As array
|
506
499
|
result = @client.execute(@triple_select_1st_empty)
|
507
|
-
result_sets = result.each(:
|
500
|
+
result_sets = result.each(as: :array)
|
508
501
|
assert_equal 2, result_sets.size
|
509
502
|
assert_equal [[2]], result_sets[0]
|
510
503
|
assert_equal [[3]], result_sets[1]
|
511
|
-
assert_equal [[
|
512
|
-
assert_equal result.each.object_id, result.each.object_id,
|
504
|
+
assert_equal [["rs2"], ["rs3"]], result.fields
|
505
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
513
506
|
end
|
514
507
|
|
515
|
-
it
|
508
|
+
it "must not include empty result sets by default - using 2nd empty buffer" do
|
516
509
|
result = @client.execute(@triple_select_2nd_empty)
|
517
510
|
result_sets = result.each
|
518
511
|
assert_equal 2, result_sets.size
|
519
|
-
assert_equal [{
|
520
|
-
assert_equal [{
|
521
|
-
assert_equal [[
|
522
|
-
assert_equal result.each.object_id, result.each.object_id,
|
512
|
+
assert_equal [{"rs1" => 1}], result_sets[0]
|
513
|
+
assert_equal [{"rs3" => 3}], result_sets[1]
|
514
|
+
assert_equal [["rs1"], ["rs3"]], result.fields
|
515
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
523
516
|
# As array
|
524
517
|
result = @client.execute(@triple_select_2nd_empty)
|
525
|
-
result_sets = result.each(:
|
518
|
+
result_sets = result.each(as: :array)
|
526
519
|
assert_equal 2, result_sets.size
|
527
520
|
assert_equal [[1]], result_sets[0]
|
528
521
|
assert_equal [[3]], result_sets[1]
|
529
|
-
assert_equal [[
|
530
|
-
assert_equal result.each.object_id, result.each.object_id,
|
522
|
+
assert_equal [["rs1"], ["rs3"]], result.fields
|
523
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
531
524
|
end
|
532
525
|
|
533
|
-
it
|
526
|
+
it "must not include empty result sets by default - using 3rd empty buffer" do
|
534
527
|
result = @client.execute(@triple_select_3rd_empty)
|
535
528
|
result_sets = result.each
|
536
529
|
assert_equal 2, result_sets.size
|
537
|
-
assert_equal [{
|
538
|
-
assert_equal [{
|
539
|
-
assert_equal [[
|
540
|
-
assert_equal result.each.object_id, result.each.object_id,
|
530
|
+
assert_equal [{"rs1" => 1}], result_sets[0]
|
531
|
+
assert_equal [{"rs2" => 2}], result_sets[1]
|
532
|
+
assert_equal [["rs1"], ["rs2"]], result.fields
|
533
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
541
534
|
# As array
|
542
535
|
result = @client.execute(@triple_select_3rd_empty)
|
543
|
-
result_sets = result.each(:
|
536
|
+
result_sets = result.each(as: :array)
|
544
537
|
assert_equal 2, result_sets.size
|
545
538
|
assert_equal [[1]], result_sets[0]
|
546
539
|
assert_equal [[2]], result_sets[1]
|
547
|
-
assert_equal [[
|
548
|
-
assert_equal result.each.object_id, result.each.object_id,
|
540
|
+
assert_equal [["rs1"], ["rs2"]], result.fields
|
541
|
+
assert_equal result.each.object_id, result.each.object_id, "same cached rows"
|
549
542
|
end
|
550
|
-
|
551
543
|
end
|
552
|
-
|
553
544
|
end
|
554
545
|
|
555
|
-
|
546
|
+
unless sqlserver_azure?
|
547
|
+
describe "Complex query with multiple results sets but no actual results" do
|
548
|
+
let(:backup_file) { 'C:\\Users\\Public\\tinytdstest.bak' }
|
556
549
|
|
557
|
-
|
550
|
+
after { File.delete(backup_file) if File.exist?(backup_file) }
|
558
551
|
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
@client.execute("BACKUP DATABASE tinytdstest TO DISK = '#{backup_file}'").do
|
552
|
+
it "must not cancel the query until complete" do
|
553
|
+
@client.execute("BACKUP DATABASE tinytdstest TO DISK = '#{backup_file}'").do
|
554
|
+
end
|
563
555
|
end
|
556
|
+
end
|
564
557
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
it 'returns fixnum for 1' do
|
570
|
-
value = @client.execute('SELECT 1 AS [fixnum]').each.first['fixnum']
|
558
|
+
describe "when casting to native ruby values" do
|
559
|
+
it "returns fixnum for 1" do
|
560
|
+
value = @client.execute("SELECT 1 AS [fixnum]").each.first["fixnum"]
|
571
561
|
assert_equal 1, value
|
572
562
|
end
|
573
563
|
|
574
|
-
it
|
575
|
-
value = @client.execute(
|
576
|
-
|
564
|
+
it "returns nil for NULL" do
|
565
|
+
value = @client.execute("SELECT NULL AS [null]").each.first["null"]
|
566
|
+
assert_nil value
|
577
567
|
end
|
578
|
-
|
579
568
|
end
|
580
569
|
|
581
|
-
describe
|
582
|
-
|
583
|
-
describe 'char max' do
|
584
|
-
|
570
|
+
describe "with data type" do
|
571
|
+
describe "char max" do
|
585
572
|
before do
|
586
|
-
@big_text =
|
587
|
-
@old_textsize = @client.execute("SELECT @@TEXTSIZE AS [textsize]").each.first[
|
588
|
-
@client.execute("SET TEXTSIZE #{(@big_text.length*2)+1}").do
|
573
|
+
@big_text = "x" * 2_000_000
|
574
|
+
@old_textsize = @client.execute("SELECT @@TEXTSIZE AS [textsize]").each.first["textsize"].inspect
|
575
|
+
@client.execute("SET TEXTSIZE #{(@big_text.length * 2) + 1}").do
|
589
576
|
end
|
590
577
|
|
591
|
-
it
|
578
|
+
it "must insert and select large varchar_max" do
|
592
579
|
insert_and_select_datatype :varchar_max
|
593
580
|
end
|
594
581
|
|
595
|
-
it
|
582
|
+
it "must insert and select large nvarchar_max" do
|
596
583
|
insert_and_select_datatype :nvarchar_max
|
597
584
|
end
|
598
|
-
|
599
|
-
end unless sqlserver_2000? || sybase_ase?
|
600
|
-
|
585
|
+
end
|
601
586
|
end
|
602
587
|
|
603
|
-
describe
|
604
|
-
|
605
|
-
it 'copes with nil or empty buffer' do
|
588
|
+
describe "when shit happens" do
|
589
|
+
it "copes with nil or empty buffer" do
|
606
590
|
assert_raises(TypeError) { @client.execute(nil) }
|
607
|
-
assert_equal [], @client.execute(
|
591
|
+
assert_equal [], @client.execute("").each
|
608
592
|
end
|
609
593
|
|
610
|
-
|
594
|
+
describe "using :message_handler option" do
|
595
|
+
let(:messages) { [] }
|
611
596
|
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
end
|
597
|
+
before do
|
598
|
+
close_client
|
599
|
+
@client = new_connection message_handler: proc { |m| messages << m }
|
616
600
|
end
|
617
601
|
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
602
|
+
after do
|
603
|
+
messages.clear
|
604
|
+
end
|
605
|
+
|
606
|
+
it "has a message handler that responds to call" do
|
607
|
+
assert @client.message_handler.respond_to?(:call)
|
608
|
+
end
|
609
|
+
|
610
|
+
it "calls the provided message handler when severity is 10 or less" do
|
611
|
+
(1..10).to_a.each do |severity|
|
612
|
+
messages.clear
|
613
|
+
msg = "Test #{severity} severity"
|
614
|
+
state = rand(1..255)
|
615
|
+
@client.execute("RAISERROR(N'#{msg}', #{severity}, #{state})").do
|
616
|
+
m = messages.first
|
617
|
+
assert_equal 1, messages.length, "there should be one message after one raiserror"
|
618
|
+
assert_equal msg, m.message, "message text"
|
619
|
+
assert_equal severity, m.severity, "message severity" unless severity == 10 && m.severity.to_i == 0
|
620
|
+
assert_equal state, m.os_error_number, "message state"
|
624
621
|
end
|
625
622
|
end
|
626
623
|
|
627
|
-
|
624
|
+
it "calls the provided message handler for `print` messages" do
|
625
|
+
messages.clear
|
626
|
+
msg = "hello"
|
627
|
+
@client.execute("PRINT '#{msg}'").do
|
628
|
+
m = messages.first
|
629
|
+
assert_equal 1, messages.length, "there should be one message after one print statement"
|
630
|
+
assert_equal msg, m.message, "message text"
|
631
|
+
end
|
628
632
|
|
629
|
-
it
|
630
|
-
|
633
|
+
it "must raise an error preceded by a `print` message" do
|
634
|
+
messages.clear
|
635
|
+
action = lambda { @client.execute("EXEC tinytds_TestPrintWithError").do }
|
631
636
|
assert_raise_tinytds_error(action) do |e|
|
632
|
-
assert_equal "
|
633
|
-
|
637
|
+
assert_equal "hello", messages.first.message, "message text"
|
638
|
+
|
639
|
+
assert_equal "Error following print", e.message
|
640
|
+
assert_equal 16, e.severity
|
634
641
|
assert_equal 50000, e.db_error_number
|
635
642
|
end
|
636
|
-
assert_followup_query
|
637
643
|
end
|
638
644
|
|
639
|
-
|
640
|
-
|
641
|
-
|
642
|
-
|
643
|
-
action = lambda { @client.execute(@query1) }
|
644
|
-
assert_raise_tinytds_error(action) do |e|
|
645
|
-
assert_match %r|with results pending|i, e.message
|
646
|
-
assert_equal 7, e.severity
|
647
|
-
assert_equal 20019, e.db_error_number
|
645
|
+
it "calls the provided message handler for each of a series of `print` messages" do
|
646
|
+
messages.clear
|
647
|
+
@client.execute("EXEC tinytds_TestSeveralPrints").do
|
648
|
+
assert_equal ["hello 1", "hello 2", "hello 3"], messages.map { |e| e.message }, "message list"
|
648
649
|
end
|
649
|
-
end
|
650
650
|
|
651
|
-
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
651
|
+
it "should flush info messages before raising error in cases of timeout" do
|
652
|
+
@client = new_connection timeout: 1, message_handler: proc { |m| messages << m }
|
653
|
+
action = lambda { @client.execute("print 'hello'; waitfor delay '00:00:02'").do }
|
654
|
+
messages.clear
|
655
|
+
assert_raise_tinytds_error(action) do |e|
|
656
|
+
assert_match %r{timed out}i, e.message, "ignore if non-english test run"
|
657
|
+
assert_equal 6, e.severity
|
658
|
+
assert_equal 20003, e.db_error_number
|
659
|
+
assert_equal "hello", messages.first&.message, "message text"
|
660
|
+
end
|
658
661
|
end
|
659
|
-
assert_followup_query
|
660
|
-
end
|
661
662
|
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
663
|
+
it "should print info messages before raising error in cases of timeout" do
|
664
|
+
@client = new_connection timeout: 1, message_handler: proc { |m| messages << m }
|
665
|
+
action = lambda { @client.execute("raiserror('hello', 1, 1) with nowait; waitfor delay '00:00:02'").do }
|
666
|
+
messages.clear
|
667
|
+
assert_raise_tinytds_error(action) do |e|
|
668
|
+
assert_match %r{timed out}i, e.message, "ignore if non-english test run"
|
669
|
+
assert_equal 6, e.severity
|
670
|
+
assert_equal 20003, e.db_error_number
|
671
|
+
assert_equal "hello", messages.first&.message, "message text"
|
672
|
+
end
|
668
673
|
end
|
669
|
-
assert_followup_query
|
670
674
|
end
|
671
675
|
|
672
|
-
it
|
673
|
-
|
674
|
-
|
675
|
-
@client.charset.must_equal 'ASCII'
|
676
|
-
find_value(202, :nvarchar_50).must_equal 'test nvarchar_50 ??'
|
677
|
-
end
|
678
|
-
|
679
|
-
it 'must error gracefully from writing non-convertable characters' do
|
680
|
-
close_client
|
681
|
-
@client = new_connection :encoding => 'ASCII'
|
682
|
-
@client.charset.must_equal 'ASCII'
|
683
|
-
rollback_transaction(@client) do
|
684
|
-
text = 'Test ✓'
|
685
|
-
@client.execute("DELETE FROM [datatypes] WHERE [nvarchar_50] IS NOT NULL").do
|
686
|
-
action = lambda { @client.execute("INSERT INTO [datatypes] ([nvarchar_50]) VALUES ('#{text}')").do }
|
687
|
-
assert_raise_tinytds_error(action) do |e|
|
688
|
-
e.message.must_match %r{Unclosed quotation mark}i
|
689
|
-
e.severity.must_equal 15
|
690
|
-
e.db_error_number.must_equal 105
|
691
|
-
end
|
692
|
-
assert_followup_query
|
676
|
+
it "must not raise an error when severity is 10 or less" do
|
677
|
+
(1..10).to_a.each do |severity|
|
678
|
+
@client.execute("RAISERROR(N'Test #{severity} severity', #{severity}, 1)").do
|
693
679
|
end
|
694
680
|
end
|
695
681
|
|
696
|
-
it
|
697
|
-
action = lambda { @client.execute("
|
682
|
+
it "raises an error when severity is greater than 10" do
|
683
|
+
action = lambda { @client.execute("RAISERROR(N'Test 11 severity', 11, 1)").do }
|
698
684
|
assert_raise_tinytds_error(action) do |e|
|
699
|
-
|
700
|
-
assert_equal
|
701
|
-
assert_equal
|
685
|
+
assert_equal "Test 11 severity", e.message
|
686
|
+
assert_equal 11, e.severity
|
687
|
+
assert_equal 50000, e.db_error_number
|
702
688
|
end
|
703
|
-
|
704
|
-
end unless sybase_ase?
|
705
|
-
|
689
|
+
end
|
706
690
|
end
|
707
|
-
|
708
691
|
end
|
709
692
|
|
710
|
-
|
711
693
|
protected
|
712
694
|
|
713
695
|
def assert_followup_query
|
714
696
|
result = @client.execute(@query1)
|
715
|
-
assert_equal 1, result.each.first[
|
697
|
+
assert_equal 1, result.each.first["one"]
|
716
698
|
end
|
717
699
|
|
718
700
|
def insert_and_select_datatype(datatype)
|
@@ -723,6 +705,4 @@ class ResultTest < TinyTds::TestCase
|
|
723
705
|
flunk "Large #{datatype} data with a length of #{@big_text.length} did not match found text with length of #{found_text.length}" unless @big_text == found_text
|
724
706
|
end
|
725
707
|
end
|
726
|
-
|
727
708
|
end
|
728
|
-
|