Line data Source code
1 : /*
2 : * Purpose: Test handling of timeouts with an error handler
3 : * Functions: dberrhandle, dbsetlogintime, dbsettime, dbsetopt, dbclropt, dbisopt
4 : * \todo We test returning INT_CANCEL for a login timeout. We don't test it for a query_timeout.
5 : */
6 :
7 : /* this test requires Sybase behaviour for some timeout handling */
8 : #undef MSDBLIB
9 : #define SYBDBLIB 1
10 :
11 : #include "common.h"
12 : #include <time.h>
13 :
14 : static int ntimeouts = 0, ncancels = 0;
15 : static const int max_timeouts = 2, timeout_seconds = 2;
16 : static time_t start_time;
17 :
18 : static int timeout_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
19 : static int chkintr(DBPROCESS * dbproc);
20 : static int hndlintr(DBPROCESS * dbproc);
21 :
22 : #if !defined(SYBETIME)
23 : #define SYBETIME SQLETIME
24 : #define INT_TIMEOUT INT_CANCEL
25 : dbsetinterrupt(DBPROCESS *dbproc, void* hand, void* p) {}
26 : #endif
27 :
28 : static int
29 48 : timeout_err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr)
30 : {
31 : /*
32 : * For server messages, cancel the query and rely on the
33 : * message handler to spew the appropriate error messages out.
34 : */
35 48 : if (dberr == SYBESMSG)
36 : return INT_CANCEL;
37 :
38 48 : if (dberr == SYBETIME) {
39 48 : fprintf(stderr, "%d timeouts received in %ld seconds, ", ++ntimeouts, (long int) (time(NULL) - start_time));
40 48 : if (ntimeouts > max_timeouts) {
41 16 : if (++ncancels > 1) {
42 0 : fprintf(stderr, "could not timeout cleanly, breaking connection\n");
43 0 : ncancels = 0;
44 0 : return INT_CANCEL;
45 : }
46 16 : fprintf(stderr, "lost patience, cancelling (allowing 10 seconds)\n");
47 16 : if (dbsettime(10) == FAIL)
48 0 : fprintf(stderr, "... but dbsettime() failed in error handler\n");
49 : return INT_TIMEOUT;
50 : }
51 32 : fprintf(stderr, "continuing to wait\n");
52 32 : return INT_CONTINUE;
53 : }
54 :
55 0 : ntimeouts = 0; /* reset */
56 :
57 0 : fprintf(stderr,
58 : "DB-LIBRARY error (severity %d, dberr %d, oserr %d, dberrstr %s, oserrstr %s):\n",
59 : severity, dberr, oserr, dberrstr ? dberrstr : "(null)", oserrstr ? oserrstr : "(null)");
60 0 : fflush(stderr);
61 :
62 : /*
63 : * If the dbprocess is dead or the dbproc is a NULL pointer and
64 : * we are not in the middle of logging in, then we need to exit.
65 : * We can't do anything from here on out anyway.
66 : * It's OK to end up here in response to a dbconvert() that
67 : * resulted in overflow, so don't exit in that case.
68 : */
69 0 : if ((dbproc == NULL) || DBDEAD(dbproc)) {
70 0 : if (dberr != SYBECOFL) {
71 0 : fprintf(stderr, "error: dbproc (%p) is %s, goodbye\n",
72 0 : dbproc, dbproc? (DBDEAD(dbproc)? "DEAD" : "OK") : "NULL");
73 0 : exit(255);
74 : }
75 : }
76 :
77 : return INT_CANCEL;
78 : }
79 :
80 : static int
81 96 : chkintr(DBPROCESS * dbproc TDS_UNUSED)
82 : {
83 96 : printf("in chkintr, %ld seconds elapsed\n", (long int) (time(NULL) - start_time));
84 96 : return FALSE;
85 : }
86 :
87 : static int
88 0 : hndlintr(DBPROCESS * dbproc TDS_UNUSED)
89 : {
90 0 : printf("in hndlintr, %ld seconds elapsed\n", (long int) (time(NULL) - start_time));
91 0 : return INT_CONTINUE;
92 : }
93 :
94 : static int failed = 0;
95 :
96 : static void
97 16 : test(int per_process)
98 : {
99 : LOGINREC *login;
100 : DBPROCESS *dbproc;
101 : int i, r;
102 : RETCODE erc, row_code;
103 16 : int num_resultset = 0;
104 : char teststr[1024];
105 : char timeout[32];
106 :
107 16 : sprintf(timeout, "%d", timeout_seconds);
108 :
109 16 : ntimeouts = 0;
110 16 : ncancels = 0;
111 :
112 : /*
113 : * Connect to server
114 : */
115 16 : dberrhandle(timeout_err_handler);
116 16 : dbmsghandle(syb_msg_handler);
117 :
118 16 : printf("About to logon\n");
119 :
120 16 : login = dblogin();
121 16 : DBSETLPWD(login, PASSWORD);
122 16 : DBSETLUSER(login, USER);
123 16 : DBSETLAPP(login, "timeout");
124 :
125 16 : printf("About to open %s.%s\n", SERVER, DATABASE);
126 :
127 : /*
128 : * One way to test the login timeout is to connect to a discard server (grep discard /etc/services).
129 : * It's normally supported by inetd.
130 : */
131 16 : printf ("using %d 1-second login timeouts\n", max_timeouts);
132 16 : dbsetlogintime(1);
133 :
134 16 : start_time = time(NULL); /* keep track of when we started for reporting purposes */
135 :
136 16 : if (NULL == (dbproc = dbopen(login, SERVER))){
137 0 : fprintf(stderr, "Failed: dbopen\n");
138 0 : exit(1);
139 : }
140 :
141 16 : printf ("connected.\n");
142 :
143 16 : if (strlen(DATABASE))
144 16 : dbuse(dbproc, DATABASE);
145 :
146 16 : dbloginfree(login);
147 :
148 : /* Set a very long global timeout. */
149 16 : if (per_process)
150 8 : dbsettime(5 * 60);
151 :
152 : /* Verify no query timeout is set for this DBPROCESS */
153 16 : if (dbisopt(dbproc, DBSETTIME, 0)) {
154 0 : printf("unexpected return code from dbisopt() before calling dbsetopt(..., DBSETTIME, ...)\n");
155 0 : exit(1);
156 : }
157 :
158 16 : if (FAIL == dbsetopt(dbproc, DBSETTIME, timeout, 0)) {
159 0 : fprintf(stderr, "Failed: dbsetopt(..., DBSETTIME, \"%d\")\n", timeout_seconds);
160 0 : exit(1);
161 : }
162 :
163 : /* Verify a query timeout is actually set for this DBPROCESS now */
164 16 : if (!dbisopt(dbproc, DBSETTIME, 0)) {
165 0 : printf("unexpected return code from dbisopt() after calling dbsetopt(..., DBSETTIME, ...)\n");
166 0 : exit(1);
167 : }
168 :
169 16 : if (FAIL == dbclropt(dbproc, DBSETTIME, 0)) {
170 0 : fprintf(stderr, "Failed: dbclropt(..., DBSETTIME, ...)\n");
171 0 : exit(1);
172 : }
173 :
174 : /* Verify no query timeout remains set for this DBPROCESS */
175 16 : if (dbisopt(dbproc, DBSETTIME, 0)) {
176 0 : printf("unexpected return code from dbisopt() after calling dbclropt(..., DBSETTIME, ...)\n");
177 0 : exit(1);
178 : }
179 :
180 16 : printf ("using %d %d-second query timeouts\n", max_timeouts, timeout_seconds);
181 16 : if (per_process) {
182 8 : if (FAIL == dbsetopt(dbproc, DBSETTIME, timeout, 0)) {
183 0 : fprintf(stderr, "Failed: dbsetopt(..., DBSETTIME, \"%d\")\n", timeout_seconds);
184 0 : exit(1);
185 : }
186 :
187 : /* Verify setting the global timeout won't override the per-process timeout value */
188 8 : if (FAIL == dbsettime(35)) {
189 0 : fprintf(stderr, "Failed: dbsettime\n");
190 0 : exit(1);
191 : }
192 : } else {
193 8 : if (FAIL == dbsettime(timeout_seconds)) {
194 0 : fprintf(stderr, "Failed: dbsettime\n");
195 0 : exit(1);
196 : }
197 : }
198 :
199 : /* send something that will take awhile to execute */
200 16 : printf ("issuing a query that will take 30 seconds\n");
201 :
202 16 : if (FAIL == sql_cmd(dbproc)) {
203 0 : fprintf(stderr, "Failed: dbcmd\n");
204 0 : exit(1);
205 : }
206 :
207 16 : start_time = time(NULL); /* keep track of when we started for reporting purposes */
208 16 : ntimeouts = 0;
209 16 : dbsetinterrupt(dbproc, (void*)chkintr, (void*)hndlintr);
210 :
211 16 : if (FAIL == dbsqlsend(dbproc)) {
212 0 : fprintf(stderr, "Failed: dbsend\n");
213 0 : exit(1);
214 : }
215 :
216 : /* wait for it to execute */
217 16 : printf("executing dbsqlok\n");
218 16 : erc = dbsqlok(dbproc);
219 16 : if (erc != FAIL) {
220 0 : fprintf(stderr, "dbsqlok should fail for timeout\n");
221 0 : exit(1);
222 : }
223 :
224 : /* retrieve outputs per usual */
225 : r = 0;
226 16 : for (i=0; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; i++) {
227 : int nrows, ncols;
228 0 : switch (erc) {
229 0 : case SUCCEED:
230 0 : if (DBROWS(dbproc) == FAIL){
231 0 : r++;
232 0 : continue;
233 : }
234 0 : assert(DBROWS(dbproc) == SUCCEED);
235 0 : printf("dbrows() returned SUCCEED, processing rows\n");
236 :
237 0 : ncols = dbnumcols(dbproc);
238 0 : ++num_resultset;
239 0 : printf("bound 1 of %d columns ('%s') in result %d.\n", ncols, dbcolname(dbproc, 1), ++r);
240 0 : dbbind(dbproc, 1, STRINGBIND, 0, (BYTE *) teststr);
241 :
242 0 : printf("\t%s\n\t-----------\n", dbcolname(dbproc, 1));
243 0 : while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
244 0 : if (row_code == REG_ROW) {
245 0 : printf("\t%s\n", teststr);
246 : } else {
247 : /* not supporting computed rows in this unit test */
248 0 : failed = 1;
249 0 : fprintf(stderr, "Failed. Expected a row\n");
250 0 : exit(1);
251 : }
252 : }
253 0 : nrows = (int) dbcount(dbproc);
254 0 : printf("row count %d\n", nrows);
255 0 : if (nrows < 0){
256 0 : failed++;
257 0 : printf("error: dbrows() returned SUCCEED, but dbcount() returned -1\n");
258 : }
259 :
260 0 : if (FAIL == dbclropt(dbproc, DBSETTIME, 0)) {
261 0 : failed++;
262 0 : printf("error: dbclropt(dbproc, DBSETTIME, ...) failed\n");
263 : }
264 : break;
265 0 : case FAIL:
266 0 : printf("dbresults returned FAIL\n");
267 0 : exit(1);
268 0 : default:
269 0 : printf("unexpected return code %d from dbresults\n", erc);
270 0 : exit(1);
271 : }
272 0 : if (i > 1) {
273 0 : failed++;
274 0 : break;
275 : }
276 : } /* while dbresults */
277 16 : }
278 :
279 : int
280 8 : main(int argc, char **argv)
281 : {
282 8 : set_malloc_options();
283 :
284 8 : read_login_info(argc, argv);
285 :
286 8 : printf("Starting %s\n", argv[0]);
287 :
288 8 : dbinit();
289 :
290 8 : test(0);
291 8 : test(1);
292 :
293 8 : dbexit();
294 :
295 8 : printf("%s %s\n", __FILE__, (failed ? "failed!" : "OK"));
296 8 : return failed ? 1 : 0;
297 : }
|