LCOV - code coverage report
Current view: top level - src/odbc/unittests - tokens.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 236 289 81.7 %
Date: 2025-01-18 11:50:39 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /* Check some internal token behaviour */
       2             : 
       3             : #include "common.h"
       4             : 
       5             : #include <assert.h>
       6             : 
       7             : #if HAVE_UNISTD_H
       8             : #include <unistd.h>
       9             : #endif /* HAVE_UNISTD_H */
      10             : 
      11             : #include <freetds/time.h>
      12             : 
      13             : #if HAVE_ERRNO_H
      14             : #include <errno.h>
      15             : #endif /* HAVE_ERRNO_H */
      16             : 
      17             : #if HAVE_SYS_SOCKET_H
      18             : #include <sys/socket.h>
      19             : #endif /* HAVE_SYS_SOCKET_H */
      20             : 
      21             : #if HAVE_NETINET_IN_H
      22             : #include <netinet/in.h>
      23             : #endif /* HAVE_NETINET_IN_H */
      24             : 
      25             : #include <freetds/tds.h>
      26             : #include <freetds/replacements.h>
      27             : #include <freetds/server.h>
      28             : #include <freetds/utils.h>
      29             : 
      30             : #include "fake_thread.h"
      31             : #include "parser.h"
      32             : 
      33             : #if TDS_HAVE_MUTEX
      34             : 
      35             : #ifdef _WIN32
      36             : #define SHUT_RDWR SD_BOTH
      37             : #endif
      38             : 
      39             : static void parse_sql(TDSSOCKET * tds, const char *sql);
      40             : 
      41             : static void
      42           8 : setup_override(void)
      43             : {
      44             :         char buf[128];
      45             :         FILE *f;
      46             : 
      47           8 :         sprintf(buf, "tokens_pwd.%d", (int) getpid());
      48           8 :         f = fopen(buf, "w");
      49           8 :         assert(f);
      50           8 :         fprintf(f, "UID=guest\nPWD=sybase\nSRV=%s\nDB=tempdb\n", odbc_server);
      51           8 :         fclose(f);
      52           8 :         rename(buf, "tokens_pwd");
      53           8 :         unlink(buf);
      54           8 :         setenv("TDSPWDFILE", "tokens_pwd", 1);
      55           8 :         unsetenv("TDSINIOVERRIDE");
      56             : 
      57           8 :         unsetenv("TDSHOST");
      58           8 :         unsetenv("TDSPORT");
      59           8 :         unsetenv("TDSVER");
      60           8 : }
      61             : 
      62             : static TDSSOCKET *
      63          16 : tds_from_sock(TDSCONTEXT *ctx, TDS_SYS_SOCKET fd)
      64             : {
      65             :         TDSSOCKET *tds;
      66             : 
      67          16 :         tds = tds_alloc_socket(ctx, 4096);
      68          16 :         if (!tds) {
      69           0 :                 CLOSESOCKET(fd);
      70           0 :                 fprintf(stderr, "out of memory");
      71           0 :                 return NULL;
      72             :         }
      73          16 :         tds_set_s(tds, fd);
      74          16 :         tds->out_flag = TDS_LOGIN;
      75             :         /* TODO proper charset */
      76          16 :         tds_iconv_open(tds->conn, "ISO8859-1", 0);
      77             :         /* get_incoming(tds->s); */
      78          16 :         tds->state = TDS_IDLE;
      79             : 
      80          16 :         tds->conn->client_spid = 0x33;
      81          16 :         tds->conn->product_version = TDS_MS_VER(10, 0, 6000);
      82             : 
      83          16 :         return tds;
      84             : }
      85             : 
      86             : static void handle_one(TDS_SYS_SOCKET sock);
      87             : static TDS_SYS_SOCKET stop_socket = INVALID_SOCKET;
      88             : 
      89             : /* accept a socket and emulate a server */
      90           8 : TDS_THREAD_PROC_DECLARE(fake_thread_proc, arg)
      91             : {
      92           8 :         TDS_SYS_SOCKET s = TDS_PTR2INT(arg), sock;
      93             :         socklen_t len;
      94             :         struct sockaddr_in sin;
      95             :         struct pollfd fds[2];
      96             :         TDS_SYS_SOCKET sockets[2];
      97             : 
      98           8 :         assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) >= 0);
      99           8 :         stop_socket = sockets[1];
     100             : 
     101             :         for (;;) {
     102          40 :                 fds[0].fd = s;
     103          24 :                 fds[0].events = POLLIN;
     104          24 :                 fds[0].revents = 0;
     105          24 :                 fds[1].fd = sockets[0];
     106          24 :                 fds[1].events = POLLIN;
     107          24 :                 fds[1].revents = 0;
     108          24 :                 if (poll(fds, 2, 30000) <= 0) {
     109           0 :                         fprintf(stderr, "poll: %d\n", sock_errno);
     110           0 :                         exit(1);
     111             :                 }
     112          24 :                 if (fds[1].revents)
     113             :                         break;
     114             : 
     115          16 :                 memset(&sin, 0, sizeof(sin));
     116          16 :                 len = sizeof(sin);
     117          16 :                 if (TDS_IS_SOCKET_INVALID(sock = tds_accept(s, (struct sockaddr *) &sin, &len))) {
     118           0 :                         perror("accept");
     119           0 :                         exit(1);
     120             :                 }
     121          16 :                 tds_socket_set_nodelay(sock);
     122          16 :                 handle_one(sock);
     123             :         }
     124           8 :         CLOSESOCKET(s);
     125           8 :         CLOSESOCKET(sockets[0]);
     126           8 :         CLOSESOCKET(sockets[1]);
     127             : 
     128           8 :         return TDS_THREAD_RESULT(0);
     129             : }
     130             : 
     131             : static void
     132          16 : handle_one(TDS_SYS_SOCKET sock)
     133             : {
     134             :         TDSSOCKET *tds;
     135             :         TDSLOGIN *login;
     136             :         TDSCONTEXT *ctx;
     137             : 
     138          16 :         ctx = tds_alloc_context(NULL);
     139          16 :         if (!ctx)
     140           0 :                 exit(1);
     141             : 
     142          16 :         tds = tds_from_sock(ctx, sock);
     143          16 :         if (!tds)
     144           0 :                 exit(1);
     145             : 
     146          16 :         login = tds_alloc_read_login(tds);
     147          16 :         if (!login) {
     148           0 :                 fprintf(stderr, "Error reading login\n");
     149           0 :                 exit(1);
     150             :         }
     151             : 
     152          48 :         if (!strcmp(tds_dstr_cstr(&login->user_name), "guest") && !strcmp(tds_dstr_cstr(&login->password), "sybase")) {
     153          16 :                 tds->out_flag = TDS_REPLY;
     154          16 :                 tds_env_change(tds, TDS_ENV_DATABASE, "master", "pubs2");
     155          16 :                 if (IS_TDS71_PLUS(tds->conn))
     156          16 :                         tds_put_n(tds, "\xe3\x08\x00\x07\x05\x09\x04\xd0\x00\x34\x00", 11);
     157          16 :                 tds_send_msg(tds, 5701, 2, 10, "Changed database context to 'pubs2'.", "JDBC", NULL, 1);
     158          16 :                 if (!login->suppress_language) {
     159          16 :                         tds_env_change(tds, TDS_ENV_LANG, NULL, "us_english");
     160          16 :                         tds_send_msg(tds, 5703, 1, 10, "Changed language setting to 'us_english'.", "JDBC", NULL, 1);
     161             :                 }
     162          16 :                 if (IS_TDS50(tds->conn))
     163           0 :                         tds_env_change(tds, TDS_ENV_PACKSIZE, NULL, "512");
     164          16 :                 tds_send_login_ack(tds, "Microsoft SQL Server");
     165          16 :                 if (!IS_TDS50(tds->conn))
     166          16 :                         tds_env_change(tds, TDS_ENV_PACKSIZE, "4096", "4096");
     167          16 :                 if (IS_TDS50(tds->conn))
     168           0 :                         tds_send_capabilities_token(tds);
     169          16 :                 tds_send_done_token(tds, TDS_DONE_FINAL, 0);
     170             :         } else {
     171           0 :                 exit(1);
     172             :         }
     173          16 :         tds_flush_packet(tds);
     174          16 :         tds_free_login(login);
     175          16 :         login = NULL;
     176             : 
     177             :         for (;;) {
     178             :                 const char *query;
     179             : 
     180         224 :                 query = tds_get_generic_query(tds);
     181         224 :                 if (!query)
     182             :                         break;
     183         208 :                 printf("query : %s\n", query);
     184             : 
     185         208 :                 tds->out_flag = TDS_REPLY;
     186             : 
     187         208 :                 if (strncmp(query, "use tempdb", 10) == 0) {
     188          16 :                         tds_env_change(tds, TDS_ENV_DATABASE, "pubs2", "tempdb");
     189          16 :                         if (IS_TDS71_PLUS(tds->conn))
     190          16 :                                 tds_put_n(tds, "\xe3\x08\x00\x07\x05\x09\x04\xd0\x00\x34\x00", 11);
     191          16 :                         tds_send_msg(tds, 5701, 2, 10, "Changed database context to 'tempdb'.", "JDBC", NULL, 1);
     192          16 :                         tds_send_done_token(tds, TDS_DONE_FINAL, 0);
     193          16 :                         tds_flush_packet(tds);
     194          16 :                         continue;
     195             :                 }
     196             : 
     197         192 :                 if (strncmp(query, "SET TEXTSIZE ", 13) == 0) {
     198          16 :                         tds_send_done_token(tds, TDS_DONE_FINAL, 0);
     199          16 :                         tds_flush_packet(tds);
     200          16 :                         continue;
     201             :                 }
     202             : 
     203         176 :                 parse_sql(tds, query);
     204         176 :                 continue;
     205             :         }
     206          16 :         shutdown(sock, SHUT_RDWR);
     207          16 :         tds_close_socket(tds);
     208          16 :         tds_free_socket(tds);
     209          16 :         tds_free_context(ctx);
     210          16 :         tds_free_query();
     211          16 : }
     212             : 
     213             : static char *
     214        1192 : read_line(void *param, char *s, size_t size)
     215             : {
     216        1192 :         const char **const p = (const char **) param;
     217        1192 :         const char *start = *p, *end;
     218             :         size_t len;
     219             : 
     220        1192 :         if (!*start)
     221             :                 return NULL;
     222        1016 :         end = strchr(start, '\n');
     223        1016 :         end = end ? end + 1 : strchr(start, 0);
     224        1016 :         len = end - start;
     225        1016 :         if (len >= size) {
     226           0 :                 fprintf(stderr, "Line too long\n");
     227           0 :                 exit(1);
     228             :         }
     229        1016 :         memcpy(s, start, len);
     230        1016 :         s[len] = 0;
     231        1016 :         *p = start + len;
     232        1016 :         return s;
     233             : }
     234             : 
     235             : static void
     236         176 : parse_sql(TDSSOCKET *tds, const char *sql)
     237             : {
     238         176 :         bool cond = true;
     239         176 :         odbc_parser *parser = odbc_init_parser_func(read_line, &sql);
     240             :         int done_token;
     241             :         TDSRESULTINFO *resinfo;
     242             :         TDSCOLUMN *col;
     243             : 
     244         176 :         resinfo = tds_alloc_results(1);
     245         176 :         if (!resinfo) {
     246           0 :                 fprintf(stderr, "out of memory");
     247           0 :                 exit(1);
     248             :         }
     249         176 :         col = resinfo->columns[0];
     250         176 :         tds_set_column_type(tds->conn, col, SYBVARCHAR);
     251         176 :         col->column_size = 30;
     252         176 :         col->on_server.column_size = 30;
     253         176 :         if (!tds_dstr_copy(&col->column_name, "name"))
     254           0 :                 exit(1);
     255         176 :         col->column_cur_size = 8;
     256         176 :         col->column_data = (void *) "row data";
     257             : 
     258        1016 :         for (;;) {
     259             :                 char *p;
     260        1192 :                 const char *cmd = odbc_get_cmd_line(parser, &p, &cond);
     261             : 
     262        1192 :                 if (!cmd)
     263             :                         break;
     264             : 
     265        1016 :                 if (!strcmp(cmd, "info")) {
     266          64 :                         if (!cond)
     267        1016 :                                 continue;
     268             : 
     269             :                         /* RAISERROR */
     270          64 :                         tds_send_msg(tds, 50000, 1, 5, "information message", "JDBC", NULL, 1);
     271          64 :                         continue;
     272             :                 }
     273             : 
     274         952 :                 if (!strcmp(cmd, "error")) {
     275         112 :                         if (!cond)
     276           0 :                                 continue;
     277             : 
     278             :                         /* division by zero */
     279         112 :                         tds_send_err(tds, 8134, 1, 16, "division by zero", "JDBC", NULL, 1);
     280         112 :                         continue;
     281             :                 }
     282             : 
     283         840 :                 if (!strcmp(cmd, "rowfmt")) {
     284         176 :                         if (!cond)
     285           0 :                                 continue;
     286             : 
     287         176 :                         tds_send_table_header(tds, resinfo);
     288         176 :                         continue;
     289             :                 }
     290             : 
     291         664 :                 if (!strcmp(cmd, "row")) {
     292         264 :                         if (!cond)
     293           0 :                                 continue;
     294             : 
     295         264 :                         tds_send_row(tds, resinfo);
     296         264 :                         continue;
     297             :                 }
     298             : 
     299         400 :                 if (!strcmp(cmd, "quit")) {
     300           0 :                         if (!cond)
     301           0 :                                 continue;
     302           0 :                         shutdown(tds->conn->s, SHUT_RDWR);
     303           0 :                         break;
     304             :                 }
     305             : 
     306         400 :                 if (!strcmp(cmd, "done"))
     307             :                         done_token = TDS_DONE_TOKEN;
     308         128 :                 else if (!strcmp(cmd, "doneinproc"))
     309             :                         done_token = TDS_DONEINPROC_TOKEN;
     310             :                 else
     311           0 :                         done_token = 0;
     312             :                 if (done_token) {
     313             :                         const char *tok;
     314             :                         TDS_SMALLINT flags = TDS_DONE_MORE_RESULTS;
     315             :                         int rows = -1;
     316             :                         bool nocount = false;
     317             : 
     318         992 :                         while ((tok = odbc_get_tok(&p)) != NULL) {
     319         592 :                                 if (!strcmp(tok, "final"))
     320         176 :                                         flags &= ~TDS_DONE_MORE_RESULTS;
     321         416 :                                 else if (!strcmp(tok, "error"))
     322          64 :                                         flags |= TDS_DONE_ERROR;
     323         352 :                                 else if (!strcmp(tok, "nocount"))
     324             :                                         nocount = true;
     325             :                                 else
     326         352 :                                         rows = atoi(tok);
     327             :                         }
     328             : 
     329         400 :                         if (!cond)
     330           0 :                                 continue;
     331             : 
     332         400 :                         if (rows >= 0)
     333         352 :                                 flags |= nocount ? 0 : TDS_DONE_COUNT;
     334             :                         else
     335             :                                 rows = 0;
     336         400 :                         tds_send_done(tds, done_token, flags, rows);
     337         400 :                         continue;
     338             :                 }
     339           0 :                 odbc_fatal(parser, ": unknown command '%s'\n", cmd);
     340             :         }
     341         176 :         tds_free_results(resinfo);
     342         176 :         odbc_free_parser(parser);
     343         176 :         tds_flush_packet(tds);
     344         176 : }
     345             : 
     346             : /*
     347             :  * Fetch tests:
     348             :  * All reply starts with ROWFMT + ROW, we test behaviour or fetch:
     349             :  * - is there a row?
     350             :  * - is there an error or info?
     351             :  * - is there row count and how much
     352             :  */
     353             : static void
     354          88 : test_fetch(const char *replies, const char *expected_no_row, const char *expected_row, int line)
     355             : {
     356             :         char *sql;
     357          88 :         ODBC_BUF *odbc_buf = NULL;
     358          88 :         int expected_rows = -1;
     359          88 :         char state[12] = "No";
     360             :         SQLLEN char_data_ind;
     361             :         char char_data[64];
     362             : 
     363          88 :         printf("Testing SQLFetch, line %d\n", line);
     364             : 
     365          88 :         CHKBindCol(1, SQL_C_CHAR, char_data, sizeof(char_data), &char_data_ind, "S");
     366             : 
     367          88 :         sql = odbc_buf_asprintf(&odbc_buf, "rowfmt\nrow\n%s", replies);
     368             : 
     369          88 :         CHKExecDirect(T(sql), SQL_NTS, "S");
     370          88 :         CHKFetch("S");
     371          88 :         sscanf(expected_no_row, " %10s %d", state, &expected_rows);
     372          88 :         CHKFetch(state);
     373          88 :         ODBC_CHECK_ROWS(expected_rows);
     374         208 :         while (CHKMoreResults("SENo") != SQL_NO_DATA)
     375          32 :                 continue;
     376             : 
     377          88 :         sql = odbc_buf_asprintf(&odbc_buf, "rowfmt\nrow\nrow\n%s", replies);
     378             : 
     379          88 :         CHKExecDirect(T(sql), SQL_NTS, "S");
     380          88 :         ODBC_CHECK_ROWS(-1);
     381          88 :         CHKFetch("S");
     382          88 :         strcpy(state, "S");
     383          88 :         expected_rows = -1;
     384          88 :         sscanf(expected_row, " %10s %d", state, &expected_rows);
     385          88 :         CHKFetch(state);
     386          88 :         ODBC_CHECK_ROWS(expected_rows);
     387         296 :         while (CHKMoreResults("SIENo") != SQL_NO_DATA)
     388         120 :                 continue;
     389             : 
     390          88 :         odbc_reset_statement();
     391          88 :         ODBC_FREE();
     392          88 : }
     393             : 
     394             : #define test_fetch(r, e1, e2) test_fetch(r, e1, e2, __LINE__)
     395             : 
     396             : int
     397           8 : main(void)
     398             : {
     399             :         int port;
     400             :         char connect[100];
     401             : 
     402             :         tds_socket_init();
     403             : 
     404           8 :         for (port = 12340; port < 12350; ++port)
     405           8 :                 if (init_fake_server(port))
     406             :                         break;
     407           8 :         if (port == 12350) {
     408           0 :                 fprintf(stderr, "Cannot bind to a port\n");
     409           0 :                 return 1;
     410             :         }
     411           8 :         printf("Fake server bound at port %d\n", port);
     412             : 
     413           8 :         odbc_read_login_info();
     414           8 :         setup_override();
     415             : 
     416           8 :         odbc_use_version3 = 1;
     417           8 :         sprintf(connect, "SERVER=127.0.0.1,%d;TDS_Version=7.3;UID=guest;PWD=sybase;DATABASE=tempdb;Encrypt=No;", port);
     418           8 :         odbc_conn_additional_params = connect;
     419           8 :         odbc_connect();
     420             : 
     421             :         /*
     422             :          * Test cases for SQLFetch (with and without row)
     423             :          */
     424             : 
     425             :         /* info + done with row */
     426           8 :         test_fetch("info\ndone 1\ndone final 2", "No 1", "");
     427             : 
     428             :         /* info + done with row */
     429           8 :         if (!odbc_driver_is_freetds())
     430           0 :                 test_fetch("info\ndone error 1\ndone final 2", "No 1", "");
     431             : 
     432             :         /* info + done with row */
     433           8 :         if (!odbc_driver_is_freetds())
     434           0 :                 test_fetch("info\ndone error\ndone final 2", "No 0", "");
     435             : 
     436             :         /* error + done with row */
     437           8 :         test_fetch("error\ndone 1\ndone final", "E 1", "");
     438             : 
     439             :         /* error + done with row */
     440           8 :         test_fetch("error\ndone error 1\ndone final", "E 1", "");
     441             : 
     442             :         /* error + done with row */
     443           8 :         if (!odbc_driver_is_freetds())
     444           0 :                 test_fetch("error\ndone error\ndone final 3", "E 0", "");
     445             : 
     446             :         /* info + doneinproc with row */
     447           8 :         test_fetch("info\ndoneinproc 1\ndone final", "No 1", "");
     448             : 
     449             :         /* info + doneinproc with row */
     450           8 :         if (!odbc_driver_is_freetds())
     451           0 :                 test_fetch("info\ndoneinproc error 1\ndone final", "No 1", "");
     452             : 
     453             :         /* info + doneinproc with row */
     454           8 :         if (!odbc_driver_is_freetds())
     455           0 :                 test_fetch("info\ndoneinproc error\ndone final 3", "No 0", "");
     456             : 
     457             :         /* error + doneinproc with row */
     458           8 :         if (!odbc_driver_is_freetds())
     459           0 :                 test_fetch("error\ndoneinproc 1\ndoneinproc 2\ndone final", "E 1", "");
     460             : 
     461             :         /* error + doneinproc with row */
     462           8 :         if (!odbc_driver_is_freetds())
     463           0 :                 test_fetch("error\ndoneinproc error 1\ndoneinproc 2\ndone final", "E 1", "");
     464             : 
     465             :         /* error + doneinproc with row */
     466           8 :         if (!odbc_driver_is_freetds())
     467           0 :                 test_fetch("error\ndoneinproc error\ndoneinproc 2\ndone final", "E 0", "");
     468             : 
     469             :         /* doneinproc with row + doneinproc with different row */
     470           8 :         if (!odbc_driver_is_freetds())
     471           0 :                 test_fetch("doneinproc 1\ndoneinproc 2\ndone final", "No 1", "S 1");
     472             : 
     473             :         /* doneinproc with row + done with different row */
     474           8 :         if (!odbc_driver_is_freetds())
     475           0 :                 test_fetch("doneinproc 1\ndone 2\ndone final", "No 1", "S 1");
     476             : 
     477             :         /* done with row + done with different row */
     478           8 :         if (!odbc_driver_is_freetds())
     479           0 :                 test_fetch("done 1\ndone 2\ndone final", "No 1", "S 1");
     480             : 
     481             :         /* doneinproc without row + doneinproc with rows */
     482           8 :         if (!odbc_driver_is_freetds())
     483           0 :                 test_fetch("doneinproc\ndoneinproc 2\ndone final 3", "No 0", "S 0");
     484             : 
     485             :         /* doneinproc with row but not count flag + doneinproc with rows */
     486           8 :         if (!odbc_driver_is_freetds())
     487           0 :                 test_fetch("doneinproc nocount 5\ndoneinproc 2\ndone final 3", "No 5", "S 5");
     488             : 
     489           8 :         odbc_disconnect();
     490             : 
     491           8 :         odbc_use_version3 = 0;
     492           8 :         odbc_connect();
     493             : 
     494             :         /*
     495             :          * Test cases for SQLFetch (with and without row)
     496             :          */
     497             : 
     498             :         /* info + done with row */
     499           8 :         test_fetch("info\ndone 1\ndone final 2", "No 1", "");
     500             : 
     501             :         /* info + done with row */
     502           8 :         if (!odbc_driver_is_freetds())
     503           0 :                 test_fetch("info\ndone error 1\ndone final 2", "No 1", "");
     504             : 
     505             :         /* info + done with row */
     506           8 :         if (!odbc_driver_is_freetds())
     507           0 :                 test_fetch("info\ndone error\ndone final 2", "No 0", "");
     508             : 
     509             :         /* error + done with row */
     510           8 :         test_fetch("error\ndone 1\ndone final", "E 1", "");
     511             : 
     512             :         /* error + done with row */
     513           8 :         test_fetch("error\ndone error 1\ndone final", "E 1", "");
     514             : 
     515             :         /* error + done with row */
     516           8 :         if (!odbc_driver_is_freetds())
     517           0 :                 test_fetch("error\ndone error\ndone final 3", "E 0", "");
     518             : 
     519             :         /* info + doneinproc with row */
     520           8 :         test_fetch("info\ndoneinproc 1\ndone final", "No 1", "");
     521             : 
     522             :         /* info + doneinproc with row */
     523           8 :         if (!odbc_driver_is_freetds())
     524           0 :                 test_fetch("info\ndoneinproc error 1\ndone final", "No 1", "");
     525             : 
     526             :         /* info + doneinproc with row */
     527           8 :         if (!odbc_driver_is_freetds())
     528           0 :                 test_fetch("info\ndoneinproc error\ndone final 3", "No 0", "");
     529             : 
     530             :         /* error + doneinproc with row */
     531           8 :         test_fetch("error\ndoneinproc 1\ndoneinproc 2\ndone final", "E 2", "");
     532             : 
     533             :         /* error + doneinproc with row */
     534           8 :         test_fetch("error\ndoneinproc error 1\ndoneinproc 2\ndone final", "E 2", "");
     535             : 
     536             :         /* error + doneinproc with row */
     537           8 :         test_fetch("error\ndoneinproc error\ndoneinproc 2\ndone final", "E 2", "");
     538             : 
     539             :         /* doneinproc with row + doneinproc with different row */
     540           8 :         if (!odbc_driver_is_freetds())
     541           0 :                 test_fetch("doneinproc 1\ndoneinproc 2\ndone final", "No 2", "S 2");
     542             : 
     543             :         /* doneinproc with row + done with different row */
     544           8 :         if (!odbc_driver_is_freetds())
     545           0 :                 test_fetch("doneinproc 1\ndone 2\ndone final", "No 1", "S 1");
     546             : 
     547             :         /* done with row + done with different row */
     548           8 :         if (!odbc_driver_is_freetds())
     549           0 :                 test_fetch("done 1\ndone 2\ndone final", "No 1", "S 1");
     550             : 
     551             :         /* doneinproc without row + doneinproc with rows */
     552           8 :         if (!odbc_driver_is_freetds())
     553           0 :                 test_fetch("doneinproc\ndoneinproc 2\ndone final 3", "No 2", "S 2");
     554             : 
     555           8 :         odbc_disconnect();
     556             : 
     557           8 :         shutdown(stop_socket, SHUT_RDWR);
     558          16 :         tds_thread_join(fake_thread, NULL);
     559             : 
     560           8 :         return 0;
     561             : }
     562             : 
     563             : #else /* !TDS_HAVE_MUTEX */
     564             : int
     565             : main(void)
     566             : {
     567             :         printf("Not possible for this platform.\n");
     568             :         odbc_test_skipped();
     569             :         return 0;
     570             : }
     571             : #endif

Generated by: LCOV version 1.13