LCOV - code coverage report
Current view: top level - src/dblib/unittests - timeout.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 67 141 47.5 %
Date: 2024-04-18 20:40:06 Functions: 4 5 80.0 %

          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             : }

Generated by: LCOV version 1.13