1 /* Testing SQLCancel() */
2 
3 #include "common.h"
4 
5 #include <signal.h>
6 
7 #if HAVE_UNISTD_H
8 #include <unistd.h>
9 #endif /* HAVE_UNISTD_H */
10 
11 #include <freetds/thread.h>
12 #include <freetds/utils.h>
13 #include <freetds/replacements.h>
14 
15 #if TDS_HAVE_MUTEX
16 
17 #ifdef _WIN32
18 #undef HAVE_ALARM
19 #endif
20 
21 static SQLTCHAR sqlstate[SQL_SQLSTATE_SIZE + 1];
22 static tds_mutex mtx;
23 
24 static void
getErrorInfo(SQLSMALLINT sqlhdltype,SQLHANDLE sqlhandle)25 getErrorInfo(SQLSMALLINT sqlhdltype, SQLHANDLE sqlhandle)
26 {
27 	SQLINTEGER naterror = 0;
28 	SQLTCHAR msgtext[SQL_MAX_MESSAGE_LENGTH + 1];
29 	SQLSMALLINT msgtextl = 0;
30 
31 	msgtext[0] = 0;
32 	SQLGetDiagRec(sqlhdltype,
33 			      (SQLHANDLE) sqlhandle,
34 			      1,
35 			      sqlstate,
36 			      &naterror,
37 			      msgtext, (SQLSMALLINT) TDS_VECTOR_SIZE(msgtext), &msgtextl);
38 	sqlstate[TDS_VECTOR_SIZE(sqlstate)-1] = 0;
39 	fprintf(stderr, "Diagnostic info:\n");
40 	fprintf(stderr, "  SQL State: %s\n", C(sqlstate));
41 	fprintf(stderr, "  SQL code : %d\n", (int) naterror);
42 	fprintf(stderr, "  Message  : %s\n", C(msgtext));
43 }
44 
45 static void
exit_forced(int s)46 exit_forced(int s)
47 {
48 	exit(1);
49 }
50 
51 #if HAVE_ALARM
52 static void
sigalrm_handler(int s)53 sigalrm_handler(int s)
54 {
55 	printf(">>>> SQLCancel() ...\n");
56 	CHKCancel("S");
57 	printf(">>>> ... SQLCancel done\n");
58 
59 	alarm(4);
60 	signal(SIGALRM, exit_forced);
61 }
62 #else
63 #define alarm(x) return;
64 #define signal(sig,h)
65 #endif
66 
67 volatile int exit_thread;
68 
TDS_THREAD_PROC_DECLARE(wait_thread_proc,arg)69 static TDS_THREAD_PROC_DECLARE(wait_thread_proc, arg)
70 {
71 	int n;
72 
73 	tds_sleep_s(4);
74 
75 	printf(">>>> SQLCancel() ...\n");
76 	CHKCancel("S");
77 	printf(">>>> ... SQLCancel done\n");
78 
79 	for (n = 0; n < 4; ++n) {
80 		tds_sleep_s(1);
81 		tds_mutex_lock(&mtx);
82 		if (exit_thread) {
83 			tds_mutex_unlock(&mtx);
84 			return TDS_THREAD_RESULT(0);
85 		}
86 		tds_mutex_unlock(&mtx);
87 	}
88 
89 	exit_forced(0);
90 	return TDS_THREAD_RESULT(0);
91 }
92 
93 static void
Test(int use_threads,int return_data)94 Test(int use_threads, int return_data)
95 {
96 	tds_thread wait_thread;
97 
98 #if !HAVE_ALARM
99 	if (!use_threads) return;
100 #endif
101 
102 	printf("testing with %s\n", use_threads ? "threads" : "signals");
103 	printf(">> Wait 5 minutes...\n");
104 	if (!use_threads) {
105 		alarm(4);
106 		signal(SIGALRM, sigalrm_handler);
107 	} else {
108 		int err;
109 
110 		exit_thread = 0;
111 		err = tds_thread_create(&wait_thread, wait_thread_proc, NULL);
112 		if (err != 0) {
113 			perror("tds_thread_create");
114 			exit(1);
115 		}
116 	}
117 	if (!return_data)
118 		CHKExecDirect(T("WAITFOR DELAY '000:05:00'"), SQL_NTS, "E");
119 	else
120 		odbc_command2("SELECT MAX(p1.k + p2.k * p3.k ^ p4.k) FROM tab1 p1, tab1 p2, tab1 p3, tab1 p4", "E");
121 
122 	tds_mutex_lock(&mtx);
123 	exit_thread = 1;
124 	tds_mutex_unlock(&mtx);
125 	if (!use_threads) {
126 		alarm(0);
127 	} else {
128 		tds_thread_join(wait_thread, NULL);
129 	}
130 	getErrorInfo(SQL_HANDLE_STMT, odbc_stmt);
131 	if (strcmp(C(sqlstate), "HY008") != 0) {
132 		fprintf(stderr, "Unexpected sql state returned\n");
133 		odbc_disconnect();
134 		exit(1);
135 	}
136 
137 	odbc_reset_statement();
138 
139 	odbc_command("SELECT name FROM sysobjects WHERE 0=1");
140 }
141 
142 int
main(int argc,char ** argv)143 main(int argc, char **argv)
144 {
145 	if (tds_mutex_init(&mtx))
146 		return 1;
147 
148 	if (odbc_read_login_info())
149 		exit(1);
150 
151 	/*
152 	 * prepare our odbcinst.ini
153 	 * is better to do it before connect cause uniODBC cache INIs
154 	 * the name must be odbcinst.ini cause unixODBC accept only this name
155 	 */
156 	if (odbc_driver[0]) {
157 		FILE *f = fopen("odbcinst.ini", "w");
158 
159 		if (f) {
160 			fprintf(f, "[FreeTDS]\nDriver = %s\nThreading = 0\n", odbc_driver);
161 			fclose(f);
162 			/* force iODBC */
163 			setenv("ODBCINSTINI", "./odbcinst.ini", 1);
164 			setenv("SYSODBCINSTINI", "./odbcinst.ini", 1);
165 			/* force unixODBC (only directory) */
166 			setenv("ODBCSYSINI", ".", 1);
167 		}
168 	}
169 
170 	odbc_use_version3 = 1;
171 	odbc_connect();
172 
173 	odbc_command("IF OBJECT_ID('tab1') IS NOT NULL DROP TABLE tab1");
174 	odbc_command("CREATE TABLE tab1 ( k INT, vc VARCHAR(200) )");
175 
176 	printf(">> Creating tab1...\n");
177 	odbc_command("DECLARE @i INT\n"
178 		"SET @i = 1\n"
179 		"WHILE @i <= 2000 BEGIN\n"
180 		"INSERT INTO tab1 VALUES ( @i, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' )\n"
181 		"SET @i = @i + 1\n"
182 		"END");
183 	while (CHKMoreResults("SNo") == SQL_SUCCESS)
184 		continue;
185 	printf(">> ...done.\n");
186 
187 	odbc_reset_statement();
188 
189 	Test(0, 0);
190 	Test(1, 0);
191 	Test(0, 1);
192 	Test(1, 1);
193 
194 	odbc_command("DROP TABLE tab1");
195 
196 	odbc_disconnect();
197 	return 0;
198 }
199 
200 #else
201 int
main(void)202 main(void)
203 {
204 	printf("Not possible for this platform.\n");
205 	return 0;
206 }
207 #endif
208 
209