LCOV - code coverage report
Current view: top level - src/dblib/unittests - timeout.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 66 139 47.5 %
Date: 2025-02-21 09:36: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          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             : }

Generated by: LCOV version 1.13