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 32 : getErrorInfo(SQLSMALLINT sqlhdltype, SQLHANDLE sqlhandle)
28 : {
29 32 : SQLINTEGER naterror = 0;
30 : SQLTCHAR msgtext[SQL_MAX_MESSAGE_LENGTH + 1];
31 32 : SQLSMALLINT msgtextl = 0;
32 :
33 32 : msgtext[0] = 0;
34 32 : SQLGetDiagRec(sqlhdltype,
35 : (SQLHANDLE) sqlhandle,
36 : 1,
37 : sqlstate,
38 : &naterror,
39 : msgtext, (SQLSMALLINT) TDS_VECTOR_SIZE(msgtext), &msgtextl);
40 32 : sqlstate[TDS_VECTOR_SIZE(sqlstate)-1] = 0;
41 32 : fprintf(stderr, "Diagnostic info:\n");
42 32 : fprintf(stderr, " SQL State: %s\n", C(sqlstate));
43 32 : fprintf(stderr, " SQL code : %d\n", (int) naterror);
44 32 : fprintf(stderr, " Message : %s\n", C(msgtext));
45 32 : }
46 :
47 : static void
48 0 : exit_forced(int s)
49 : {
50 0 : exit(1);
51 : }
52 :
53 : #if HAVE_ALARM
54 : static void
55 16 : sigalrm_handler(int s)
56 : {
57 16 : printf(">>>> SQLCancel() ...\n");
58 16 : CHKCancel("S");
59 16 : printf(">>>> ... SQLCancel done\n");
60 :
61 16 : alarm(4);
62 16 : signal(SIGALRM, exit_forced);
63 16 : }
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 alarm_thread_proc(LPVOID arg)
74 : {
75 : unsigned int timeout = (uintptr_t) arg;
76 : switch (WaitForSingleObject(alarm_cond, timeout * 1000)) {
77 : case WAIT_OBJECT_0:
78 : return 0;
79 : }
80 : abort();
81 : return 0;
82 : }
83 :
84 : #undef alarm
85 : #define alarm tds_alarm
86 : static void alarm(unsigned int timeout)
87 : {
88 : static HANDLE thread = NULL;
89 :
90 : /* create an event to stop the alarm thread */
91 : if (alarm_cond == NULL) {
92 : alarm_cond = CreateEvent(NULL, TRUE, FALSE, NULL);
93 : assert(alarm_cond != NULL);
94 : }
95 :
96 : /* stop old alarm */
97 : if (thread) {
98 : SetEvent(alarm_cond);
99 : assert(WaitForSingleObject(thread, INFINITE) == WAIT_OBJECT_0);
100 : CloseHandle(thread);
101 : thread = NULL;
102 : }
103 :
104 : if (timeout) {
105 : ResetEvent(alarm_cond);
106 :
107 : /* start alarm thread */
108 : thread = CreateThread(NULL, 0, alarm_thread_proc, (LPVOID) (uintptr_t) timeout, 0, NULL);
109 : assert(thread);
110 : }
111 : }
112 : #endif
113 :
114 : volatile bool exit_thread;
115 :
116 16 : static TDS_THREAD_PROC_DECLARE(wait_thread_proc, arg)
117 : {
118 : int n;
119 :
120 16 : tds_sleep_s(4);
121 :
122 16 : printf(">>>> SQLCancel() ...\n");
123 16 : CHKCancel("S");
124 16 : printf(">>>> ... SQLCancel done\n");
125 :
126 16 : for (n = 0; n < 4; ++n) {
127 16 : tds_sleep_s(1);
128 16 : tds_mutex_lock(&mtx);
129 16 : if (exit_thread) {
130 16 : tds_mutex_unlock(&mtx);
131 : return TDS_THREAD_RESULT(0);
132 : }
133 0 : tds_mutex_unlock(&mtx);
134 : }
135 :
136 : exit_forced(0);
137 : return TDS_THREAD_RESULT(0);
138 : }
139 :
140 : static void
141 32 : Test(bool use_threads, bool return_data)
142 : {
143 : tds_thread wait_thread;
144 :
145 : #if !HAVE_ALARM
146 : if (!use_threads) return;
147 : #endif
148 :
149 32 : printf("testing with %s\n", use_threads ? "threads" : "signals");
150 32 : printf(">> Wait 5 minutes...\n");
151 32 : if (!use_threads) {
152 16 : alarm(4);
153 16 : signal(SIGALRM, sigalrm_handler);
154 : } else {
155 : int err;
156 :
157 16 : exit_thread = false;
158 16 : alarm(120);
159 16 : err = tds_thread_create(&wait_thread, wait_thread_proc, NULL);
160 16 : if (err != 0) {
161 0 : perror("tds_thread_create");
162 0 : exit(1);
163 : }
164 : }
165 32 : if (!return_data)
166 16 : CHKExecDirect(T("WAITFOR DELAY '000:05:00'"), SQL_NTS, "E");
167 : else
168 16 : odbc_command2("SELECT MAX(p1.k + p2.k * p3.k ^ p4.k) FROM tab1 p1, tab1 p2, tab1 p3, tab1 p4", "E");
169 :
170 32 : tds_mutex_lock(&mtx);
171 32 : exit_thread = true;
172 32 : tds_mutex_unlock(&mtx);
173 32 : alarm(0);
174 32 : if (use_threads)
175 16 : tds_thread_join(wait_thread, NULL);
176 :
177 32 : getErrorInfo(SQL_HANDLE_STMT, odbc_stmt);
178 32 : if (strcmp(C(sqlstate), "HY008") != 0) {
179 0 : fprintf(stderr, "Unexpected sql state returned\n");
180 0 : odbc_disconnect();
181 0 : exit(1);
182 : }
183 :
184 32 : odbc_reset_statement();
185 :
186 32 : odbc_command("SELECT name FROM sysobjects WHERE 0=1");
187 32 : }
188 :
189 : int
190 8 : main(int argc, char **argv)
191 : {
192 8 : if (tds_mutex_init(&mtx))
193 : return 1;
194 :
195 8 : if (odbc_read_login_info())
196 0 : exit(1);
197 :
198 : /*
199 : * prepare our odbcinst.ini
200 : * is better to do it before connect cause unixODBC cache INIs
201 : * the name must be odbcinst.ini cause unixODBC accept only this name
202 : */
203 8 : if (odbc_driver[0]) {
204 8 : FILE *f = fopen("odbcinst.ini", "w");
205 :
206 8 : if (f) {
207 8 : fprintf(f, "[FreeTDS]\nDriver = %s\nThreading = 0\n", odbc_driver);
208 8 : fclose(f);
209 : /* force iODBC */
210 8 : setenv("ODBCINSTINI", "./odbcinst.ini", 1);
211 8 : setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
212 : /* force unixODBC (only directory) */
213 8 : setenv("ODBCSYSINI", ".", 1);
214 : }
215 : }
216 :
217 8 : odbc_use_version3 = 1;
218 8 : odbc_connect();
219 :
220 8 : odbc_command("IF OBJECT_ID('tab1') IS NOT NULL DROP TABLE tab1");
221 8 : odbc_command("CREATE TABLE tab1 ( k INT, vc VARCHAR(200) )");
222 :
223 8 : printf(">> Creating tab1...\n");
224 8 : odbc_command("DECLARE @i INT\n"
225 : "SET @i = 1\n"
226 : "WHILE @i <= 2000 BEGIN\n"
227 : "INSERT INTO tab1 VALUES ( @i, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' )\n"
228 : "SET @i = @i + 1\n"
229 : "END");
230 32016 : while (CHKMoreResults("SNo") == SQL_SUCCESS)
231 32000 : continue;
232 8 : printf(">> ...done.\n");
233 :
234 8 : odbc_reset_statement();
235 :
236 8 : Test(false, false);
237 8 : Test(true, false);
238 8 : Test(false, true);
239 8 : Test(true, true);
240 :
241 8 : odbc_command("DROP TABLE tab1");
242 :
243 8 : odbc_disconnect();
244 8 : return 0;
245 : }
246 :
247 : #else
248 : int
249 : main(void)
250 : {
251 : printf("Not possible for this platform.\n");
252 : return 0;
253 : }
254 : #endif
255 :
|