Line data Source code
1 : /* Testing SQLCancel() */
2 :
3 : #include "common.h"
4 :
5 : #include <assert.h>
6 : #include <signal.h>
7 :
8 : #if HAVE_UNISTD_H
9 : #include <unistd.h>
10 : #endif /* HAVE_UNISTD_H */
11 :
12 : #include <freetds/thread.h>
13 : #include <freetds/utils.h>
14 : #include <freetds/bool.h>
15 : #include <freetds/replacements.h>
16 :
17 : #if TDS_HAVE_MUTEX
18 :
19 : #ifdef _WIN32
20 : #undef HAVE_ALARM
21 : #endif
22 :
23 : static SQLTCHAR sqlstate[SQL_SQLSTATE_SIZE + 1];
24 : static tds_mutex mtx;
25 :
26 : static void
27 40 : getErrorInfo(SQLSMALLINT sqlhdltype, SQLHANDLE sqlhandle)
28 : {
29 40 : SQLINTEGER naterror = 0;
30 : SQLTCHAR msgtext[SQL_MAX_MESSAGE_LENGTH + 1];
31 40 : SQLSMALLINT msgtextl = 0;
32 :
33 40 : msgtext[0] = 0;
34 40 : SQLGetDiagRec(sqlhdltype,
35 : (SQLHANDLE) sqlhandle,
36 : 1,
37 : sqlstate,
38 : &naterror,
39 : msgtext, (SQLSMALLINT) TDS_VECTOR_SIZE(msgtext), &msgtextl);
40 40 : sqlstate[TDS_VECTOR_SIZE(sqlstate)-1] = 0;
41 40 : fprintf(stderr, "Diagnostic info:\n");
42 40 : fprintf(stderr, " SQL State: %s\n", C(sqlstate));
43 40 : fprintf(stderr, " SQL code : %d\n", (int) naterror);
44 40 : fprintf(stderr, " Message : %s\n", C(msgtext));
45 40 : }
46 :
47 : static void
48 0 : exit_forced(int s TDS_UNUSED)
49 : {
50 0 : exit(1);
51 : }
52 :
53 : #if HAVE_ALARM
54 : static void
55 20 : sigalrm_handler(int s TDS_UNUSED)
56 : {
57 20 : printf(">>>> SQLCancel() ...\n");
58 20 : CHKCancel("S");
59 20 : printf(">>>> ... SQLCancel done\n");
60 :
61 20 : alarm(4);
62 20 : signal(SIGALRM, exit_forced);
63 20 : }
64 : #else
65 : #define alarm(x) return;
66 : #define signal(sig,h)
67 : #endif
68 :
69 : #ifdef _WIN32
70 :
71 : static HANDLE alarm_cond = NULL;
72 :
73 : static DWORD WINAPI
74 : alarm_thread_proc(LPVOID arg)
75 : {
76 : unsigned int timeout = (uintptr_t) arg;
77 : switch (WaitForSingleObject(alarm_cond, timeout * 1000)) {
78 : case WAIT_OBJECT_0:
79 : return 0;
80 : }
81 : abort();
82 : return 0;
83 : }
84 :
85 : #undef alarm
86 : #define alarm tds_alarm
87 : static void
88 : alarm(unsigned int timeout)
89 : {
90 : static HANDLE thread = NULL;
91 :
92 : /* create an event to stop the alarm thread */
93 : if (alarm_cond == NULL) {
94 : alarm_cond = CreateEvent(NULL, TRUE, FALSE, NULL);
95 : assert(alarm_cond != NULL);
96 : }
97 :
98 : /* stop old alarm */
99 : if (thread) {
100 : SetEvent(alarm_cond);
101 : assert(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0);
102 : CloseHandle(thread);
103 : thread = NULL;
104 : }
105 :
106 : if (timeout) {
107 : ResetEvent(alarm_cond);
108 :
109 : /* start alarm thread */
110 : thread = CreateThread(NULL, 0, alarm_thread_proc, (LPVOID) (uintptr_t) timeout, 0, NULL);
111 : assert(thread);
112 : }
113 : }
114 : #endif
115 :
116 : volatile bool exit_thread;
117 :
118 20 : static TDS_THREAD_PROC_DECLARE(wait_thread_proc, arg TDS_UNUSED)
119 : {
120 : int n;
121 :
122 20 : tds_sleep_s(4);
123 :
124 20 : printf(">>>> SQLCancel() ...\n");
125 20 : CHKCancel("S");
126 20 : printf(">>>> ... SQLCancel done\n");
127 :
128 20 : for (n = 0; n < 4; ++n) {
129 20 : tds_sleep_s(1);
130 20 : tds_mutex_lock(&mtx);
131 20 : if (exit_thread) {
132 20 : tds_mutex_unlock(&mtx);
133 : return TDS_THREAD_RESULT(0);
134 : }
135 0 : tds_mutex_unlock(&mtx);
136 : }
137 :
138 : exit_forced(0);
139 : return TDS_THREAD_RESULT(0);
140 : }
141 :
142 : static void
143 40 : Test(bool use_threads, bool return_data)
144 : {
145 : tds_thread wait_thread;
146 :
147 : #if !HAVE_ALARM
148 : if (!use_threads) return;
149 : #endif
150 :
151 40 : printf("testing with %s\n", use_threads ? "threads" : "signals");
152 40 : printf(">> Wait 5 minutes...\n");
153 40 : if (!use_threads) {
154 20 : alarm(4);
155 20 : signal(SIGALRM, sigalrm_handler);
156 : } else {
157 : int err;
158 :
159 20 : exit_thread = false;
160 20 : alarm(120);
161 20 : err = tds_thread_create(&wait_thread, wait_thread_proc, NULL);
162 20 : if (err != 0) {
163 0 : perror("tds_thread_create");
164 0 : exit(1);
165 : }
166 : }
167 40 : if (!return_data)
168 20 : CHKExecDirect(T("WAITFOR DELAY '000:05:00'"), SQL_NTS, "E");
169 : else
170 20 : odbc_command2("SELECT MAX(p1.k + p2.k * p3.k ^ p4.k) FROM tab1 p1, tab1 p2, tab1 p3, tab1 p4", "E");
171 :
172 40 : tds_mutex_lock(&mtx);
173 40 : exit_thread = true;
174 40 : tds_mutex_unlock(&mtx);
175 40 : alarm(0);
176 40 : if (use_threads)
177 20 : tds_thread_join(wait_thread, NULL);
178 :
179 40 : getErrorInfo(SQL_HANDLE_STMT, odbc_stmt);
180 40 : if (strcmp(C(sqlstate), "HY008") != 0) {
181 0 : fprintf(stderr, "Unexpected sql state returned\n");
182 0 : odbc_disconnect();
183 0 : exit(1);
184 : }
185 :
186 40 : odbc_reset_statement();
187 :
188 40 : odbc_command("SELECT name FROM sysobjects WHERE 0=1");
189 80 : while (CHKMoreResults("SNo") == SQL_SUCCESS)
190 0 : continue;
191 40 : }
192 :
193 : /* Check that SQLCancel sends a cancellation even if no other activities are done */
194 : static void
195 10 : TestSendDuringCancel(void)
196 : {
197 10 : odbc_check_no_row("IF EXISTS(SELECT * FROM tab1 WHERE k=10000) SELECT 1");
198 10 : odbc_check_no_row("IF EXISTS(SELECT * FROM tab1 WHERE k=10001) SELECT 2");
199 10 : alarm(15);
200 : /* WAITFOR + INSERT */
201 10 : printf("Sending query...\n");
202 10 : CHKExecDirect(T(/* this row will be inserted */
203 : "INSERT INTO tab1 VALUES(10000, 'aaa') "
204 : /* force server to return a packet, otherwise SQLExecDirect will stop */
205 : "SELECT * FROM tab1 "
206 : /* wait 2 seconds, giving us time to cancel */
207 : "WAITFOR DELAY '000:00:02' "
208 : /* this won't be executed */
209 : "INSERT INTO tab1 VALUES(10001, 'bbb')"), SQL_NTS, "S");
210 : /* Cancel, should cancel the insert */
211 10 : printf("Cancel...\n");
212 10 : CHKCancel("S");
213 : /* sleep */
214 10 : printf("Sleep...\n");
215 10 : tds_sleep_s(3);
216 10 : alarm(6);
217 10 : printf("Checking...\n");
218 10 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM tab1 WHERE k=10000) SELECT 3");
219 10 : odbc_check_no_row("IF EXISTS(SELECT * FROM tab1 WHERE k=10001) SELECT 4");
220 10 : alarm(0);
221 10 : }
222 :
223 : /* Cancelling an IDLE statement should work */
224 : static void
225 10 : TestIdleStatement(void)
226 : {
227 : SQLHSTMT stmt;
228 10 : CHKAllocStmt(&stmt, "S");
229 :
230 10 : SWAP_STMT(stmt);
231 10 : CHKCancel("S");
232 10 : CHKFreeStmt(SQL_DROP, "S");
233 :
234 10 : SWAP_STMT(stmt);
235 10 : }
236 :
237 : /* Cancelling another statement should not conflict with an active one.
238 : * Very similar to TestSendDuringCancel but cancelling a different statement. */
239 : static void
240 20 : TestOtherStatement(bool some_activity)
241 : {
242 : SQLHSTMT stmt;
243 20 : CHKAllocStmt(&stmt, "S");
244 :
245 20 : if (some_activity)
246 10 : SWAP_STMT(stmt);
247 20 : odbc_command("DELETE tab1 WHERE k >= 10000");
248 40 : while (CHKMoreResults("SNo") == SQL_SUCCESS)
249 0 : continue;
250 20 : if (some_activity)
251 10 : SWAP_STMT(stmt);
252 :
253 20 : odbc_check_no_row("IF EXISTS(SELECT * FROM tab1 WHERE k=10000) SELECT 1");
254 20 : odbc_check_no_row("IF EXISTS(SELECT * FROM tab1 WHERE k=10001) SELECT 2");
255 20 : alarm(15);
256 : /* WAITFOR + INSERT */
257 20 : printf("Sending query...\n");
258 20 : CHKExecDirect(T(/* this row will be inserted */
259 : "INSERT INTO tab1 VALUES(10000, 'aaa') "
260 : /* force server to return a packet, otherwise SQLExecDirect will stop */
261 : "SELECT * FROM tab1 "
262 : /* wait 2 seconds, giving us time to cancel */
263 : "WAITFOR DELAY '000:00:02' "
264 : /* this won't be executed */
265 : "INSERT INTO tab1 VALUES(10001, 'bbb')"), SQL_NTS, "S");
266 : /* Cancel, should not cancel the insert */
267 20 : printf("Cancel...\n");
268 20 : SWAP_STMT(stmt);
269 20 : CHKCancel("S");
270 20 : SWAP_STMT(stmt);
271 : /* sleep */
272 20 : printf("Sleep...\n");
273 20 : tds_sleep_s(3);
274 40 : while (CHKMoreResults("SNo") == SQL_SUCCESS)
275 0 : continue;
276 20 : alarm(6);
277 20 : printf("Checking...\n");
278 20 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM tab1 WHERE k=10000) SELECT 3");
279 20 : odbc_check_no_row("IF NOT EXISTS(SELECT * FROM tab1 WHERE k=10001) SELECT 4");
280 20 : alarm(0);
281 :
282 20 : SWAP_STMT(stmt);
283 20 : CHKFreeStmt(SQL_DROP, "S");
284 20 : odbc_stmt = SQL_NULL_HSTMT;
285 20 : SWAP_STMT(stmt);
286 20 : }
287 :
288 : int
289 10 : main(void)
290 : {
291 10 : if (tds_mutex_init(&mtx))
292 : return 1;
293 :
294 10 : if (odbc_read_login_info())
295 0 : exit(1);
296 :
297 : /*
298 : * prepare our odbcinst.ini
299 : * is better to do it before connect cause unixODBC cache INIs
300 : * the name must be odbcinst.ini cause unixODBC accept only this name
301 : */
302 10 : if (odbc_driver[0]) {
303 10 : FILE *f = fopen("odbcinst.ini", "w");
304 :
305 10 : if (f) {
306 10 : fprintf(f, "[FreeTDS]\nDriver = %s\nThreading = 0\n", odbc_driver);
307 10 : fclose(f);
308 : /* force iODBC */
309 10 : setenv("ODBCINSTINI", "./odbcinst.ini", 1);
310 10 : setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
311 : /* force unixODBC (only directory) */
312 10 : setenv("ODBCSYSINI", ".", 1);
313 : }
314 : }
315 :
316 10 : odbc_use_version3 = 1;
317 10 : odbc_connect();
318 :
319 10 : odbc_command("IF OBJECT_ID('tab1') IS NOT NULL DROP TABLE tab1");
320 10 : odbc_command("CREATE TABLE tab1 ( k INT, vc VARCHAR(200) )");
321 :
322 10 : printf(">> Creating tab1...\n");
323 10 : odbc_command("DECLARE @i INT\n"
324 : "SET NOCOUNT ON\n"
325 : "SET @i = 1\n"
326 : "WHILE @i <= 2000 BEGIN\n"
327 : "INSERT INTO tab1 VALUES ( @i, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' )\n"
328 : "SET @i = @i + 1\n"
329 : "END");
330 20 : while (CHKMoreResults("SNo") == SQL_SUCCESS)
331 0 : continue;
332 10 : printf(">> ...done.\n");
333 :
334 10 : odbc_reset_statement();
335 :
336 10 : Test(false, false);
337 10 : Test(true, false);
338 10 : Test(false, true);
339 10 : Test(true, true);
340 :
341 10 : TestSendDuringCancel();
342 :
343 10 : TestIdleStatement();
344 :
345 10 : TestOtherStatement(false);
346 :
347 10 : TestOtherStatement(true);
348 :
349 10 : odbc_command("DROP TABLE tab1");
350 :
351 10 : odbc_disconnect();
352 10 : return 0;
353 : }
354 :
355 : #else
356 : int
357 : main(void)
358 : {
359 : printf("Not possible for this platform.\n");
360 : return 0;
361 : }
362 : #endif
363 :
|