LCOV - code coverage report
Current view: top level - src/odbc/unittests - cancel.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 80 89 89.9 %
Date: 2024-04-18 21:21:48 Functions: 5 6 83.3 %

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

Generated by: LCOV version 1.13