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

Generated by: LCOV version 1.13