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 60 : 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 60 : if (dberr == SYBESMSG)
36 : return INT_CANCEL;
37 :
38 60 : if (dberr == SYBETIME) {
39 60 : fprintf(stderr, "%d timeouts received in %ld seconds, ", ++ntimeouts, (long int) (time(NULL) - start_time));
40 60 : if (ntimeouts > max_timeouts) {
41 20 : 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 20 : fprintf(stderr, "lost patience, cancelling (allowing 10 seconds)\n");
47 20 : if (dbsettime(10) == FAIL)
48 0 : fprintf(stderr, "... but dbsettime() failed in error handler\n");
49 : return INT_TIMEOUT;
50 : }
51 40 : fprintf(stderr, "continuing to wait\n");
52 40 : 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 120 : chkintr(DBPROCESS * dbproc TDS_UNUSED)
82 : {
83 120 : printf("in chkintr, %ld seconds elapsed\n", (long int) (time(NULL) - start_time));
84 120 : 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 20 : test(int per_process)
98 : {
99 : LOGINREC *login;
100 : DBPROCESS *dbproc;
101 : int i, r;
102 : RETCODE erc, row_code;
103 : char teststr[1024];
104 : char timeout[32];
105 :
106 20 : sprintf(timeout, "%d", timeout_seconds);
107 :
108 20 : ntimeouts = 0;
109 20 : ncancels = 0;
110 :
111 : /*
112 : * Connect to server
113 : */
114 20 : dberrhandle(timeout_err_handler);
115 20 : dbmsghandle(syb_msg_handler);
116 :
117 20 : printf("About to logon\n");
118 :
119 20 : login = dblogin();
120 20 : DBSETLPWD(login, PASSWORD);
121 20 : DBSETLUSER(login, USER);
122 20 : DBSETLAPP(login, "timeout");
123 :
124 20 : printf("About to open %s.%s\n", SERVER, DATABASE);
125 :
126 : /*
127 : * One way to test the login timeout is to connect to a discard server (grep discard /etc/services).
128 : * It's normally supported by inetd.
129 : */
130 20 : printf ("using %d 1-second login timeouts\n", max_timeouts);
131 20 : dbsetlogintime(1);
132 :
133 20 : start_time = time(NULL); /* keep track of when we started for reporting purposes */
134 :
135 20 : if (NULL == (dbproc = dbopen(login, SERVER))){
136 0 : fprintf(stderr, "Failed: dbopen\n");
137 0 : exit(1);
138 : }
139 :
140 20 : printf ("connected.\n");
141 :
142 20 : if (strlen(DATABASE))
143 20 : dbuse(dbproc, DATABASE);
144 :
145 20 : dbloginfree(login);
146 :
147 : /* Set a very long global timeout. */
148 20 : if (per_process)
149 10 : dbsettime(5 * 60);
150 :
151 : /* Verify no query timeout is set for this DBPROCESS */
152 20 : if (dbisopt(dbproc, DBSETTIME, 0)) {
153 0 : printf("unexpected return code from dbisopt() before calling dbsetopt(..., DBSETTIME, ...)\n");
154 0 : exit(1);
155 : }
156 :
157 20 : if (FAIL == dbsetopt(dbproc, DBSETTIME, timeout, 0)) {
158 0 : fprintf(stderr, "Failed: dbsetopt(..., DBSETTIME, \"%d\")\n", timeout_seconds);
159 0 : exit(1);
160 : }
161 :
162 : /* Verify a query timeout is actually set for this DBPROCESS now */
163 20 : if (!dbisopt(dbproc, DBSETTIME, 0)) {
164 0 : printf("unexpected return code from dbisopt() after calling dbsetopt(..., DBSETTIME, ...)\n");
165 0 : exit(1);
166 : }
167 :
168 20 : if (FAIL == dbclropt(dbproc, DBSETTIME, 0)) {
169 0 : fprintf(stderr, "Failed: dbclropt(..., DBSETTIME, ...)\n");
170 0 : exit(1);
171 : }
172 :
173 : /* Verify no query timeout remains set for this DBPROCESS */
174 20 : if (dbisopt(dbproc, DBSETTIME, 0)) {
175 0 : printf("unexpected return code from dbisopt() after calling dbclropt(..., DBSETTIME, ...)\n");
176 0 : exit(1);
177 : }
178 :
179 20 : printf ("using %d %d-second query timeouts\n", max_timeouts, timeout_seconds);
180 20 : if (per_process) {
181 10 : if (FAIL == dbsetopt(dbproc, DBSETTIME, timeout, 0)) {
182 0 : fprintf(stderr, "Failed: dbsetopt(..., DBSETTIME, \"%d\")\n", timeout_seconds);
183 0 : exit(1);
184 : }
185 :
186 : /* Verify setting the global timeout won't override the per-process timeout value */
187 10 : if (FAIL == dbsettime(35)) {
188 0 : fprintf(stderr, "Failed: dbsettime\n");
189 0 : exit(1);
190 : }
191 : } else {
192 10 : if (FAIL == dbsettime(timeout_seconds)) {
193 0 : fprintf(stderr, "Failed: dbsettime\n");
194 0 : exit(1);
195 : }
196 : }
197 :
198 : /* send something that will take awhile to execute */
199 20 : printf ("issuing a query that will take 30 seconds\n");
200 :
201 20 : if (FAIL == sql_cmd(dbproc)) {
202 0 : fprintf(stderr, "Failed: dbcmd\n");
203 0 : exit(1);
204 : }
205 :
206 20 : start_time = time(NULL); /* keep track of when we started for reporting purposes */
207 20 : ntimeouts = 0;
208 20 : dbsetinterrupt(dbproc, (void*)chkintr, (void*)hndlintr);
209 :
210 20 : if (FAIL == dbsqlsend(dbproc)) {
211 0 : fprintf(stderr, "Failed: dbsend\n");
212 0 : exit(1);
213 : }
214 :
215 : /* wait for it to execute */
216 20 : printf("executing dbsqlok\n");
217 20 : erc = dbsqlok(dbproc);
218 20 : if (erc != FAIL) {
219 0 : fprintf(stderr, "dbsqlok should fail for timeout\n");
220 0 : exit(1);
221 : }
222 :
223 : /* retrieve outputs per usual */
224 : r = 0;
225 20 : for (i=0; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; i++) {
226 : int nrows, ncols;
227 0 : switch (erc) {
228 0 : case SUCCEED:
229 0 : if (DBROWS(dbproc) == FAIL){
230 0 : r++;
231 0 : continue;
232 : }
233 0 : assert(DBROWS(dbproc) == SUCCEED);
234 0 : printf("dbrows() returned SUCCEED, processing rows\n");
235 :
236 0 : ncols = dbnumcols(dbproc);
237 0 : printf("bound 1 of %d columns ('%s') in result %d.\n", ncols, dbcolname(dbproc, 1), ++r);
238 0 : dbbind(dbproc, 1, STRINGBIND, 0, (BYTE *) teststr);
239 :
240 0 : printf("\t%s\n\t-----------\n", dbcolname(dbproc, 1));
241 0 : while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
242 0 : if (row_code == REG_ROW) {
243 0 : printf("\t%s\n", teststr);
244 : } else {
245 : /* not supporting computed rows in this unit test */
246 0 : failed = 1;
247 0 : fprintf(stderr, "Failed. Expected a row\n");
248 0 : exit(1);
249 : }
250 : }
251 0 : nrows = (int) dbcount(dbproc);
252 0 : printf("row count %d\n", nrows);
253 0 : if (nrows < 0){
254 0 : failed++;
255 0 : printf("error: dbrows() returned SUCCEED, but dbcount() returned -1\n");
256 : }
257 :
258 0 : if (FAIL == dbclropt(dbproc, DBSETTIME, 0)) {
259 0 : failed++;
260 0 : printf("error: dbclropt(dbproc, DBSETTIME, ...) failed\n");
261 : }
262 : break;
263 0 : case FAIL:
264 0 : printf("dbresults returned FAIL\n");
265 0 : exit(1);
266 0 : default:
267 0 : printf("unexpected return code %d from dbresults\n", erc);
268 0 : exit(1);
269 : }
270 0 : if (i > 1) {
271 0 : failed++;
272 0 : break;
273 : }
274 : } /* while dbresults */
275 20 : }
276 :
277 : int
278 10 : main(int argc, char **argv)
279 : {
280 10 : set_malloc_options();
281 :
282 10 : read_login_info(argc, argv);
283 :
284 10 : printf("Starting %s\n", argv[0]);
285 :
286 10 : dbinit();
287 :
288 10 : test(0);
289 10 : test(1);
290 :
291 10 : dbexit();
292 :
293 10 : printf("%s %s\n", __FILE__, (failed ? "failed!" : "OK"));
294 10 : return failed ? 1 : 0;
295 : }
|