LCOV - code coverage report
Current view: top level - src/odbc/unittests - mars1.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 74 77 96.1 %
Date: 2026-07-19 14:02:14 Functions: 4 4 100.0 %

          Line data    Source code
       1             : #include "common.h"
       2             : 
       3             : /* first MARS test, test 2 concurrent recordset */
       4             : 
       5             : #define SET_STMT(n) do { \
       6             :         if (pcur_stmt != &n) { \
       7             :                 if (pcur_stmt) *pcur_stmt = odbc_stmt; \
       8             :                 pcur_stmt = &n; \
       9             :                 odbc_stmt = *pcur_stmt; \
      10             :         } \
      11             : } while(0)
      12             : 
      13             : static void
      14           2 : AutoCommit(int onoff)
      15             : {
      16           2 :         CHKSetConnectAttr(SQL_ATTR_AUTOCOMMIT, TDS_INT2PTR(onoff), 0, "S");
      17           2 : }
      18             : 
      19             : static void
      20           2 : EndTransaction(SQLSMALLINT type)
      21             : {
      22           2 :         CHKEndTran(SQL_HANDLE_DBC, odbc_conn, type, "S");
      23           2 : }
      24             : 
      25             : 
      26             : static void
      27          10 : my_attrs(void)
      28             : {
      29          10 :         SQLSetConnectAttr(odbc_conn, 1224 /*SQL_COPT_SS_MARS_ENABLED*/, (SQLPOINTER) 1 /*SQL_MARS_ENABLED_YES*/, SQL_IS_UINTEGER);
      30          10 : }
      31             : 
      32          10 : TEST_MAIN()
      33             : {
      34             :         /* adjust these parameters for memory leak testing */
      35             :         /* TODO a way for this test to detect memory leak here. */
      36          10 :         const int n_iterations = 20;    /* E.g. 200000 */
      37          10 :         const int freq_parameterized = 2;       /* set 1 to parameterize all, INT_MAX for none */
      38             : 
      39             :         SQLINTEGER len, out;
      40             :         int i, j;
      41             :         SQLHSTMT stmt1, stmt2;
      42          10 :         SQLHSTMT *pcur_stmt = NULL;
      43             :         SQLINTEGER bind1;
      44          10 :         char bind2[20] = "parameters";
      45             : 
      46          10 :         odbc_set_conn_attr = my_attrs;
      47          10 :         odbc_connect();
      48             : 
      49          10 :         stmt1 = odbc_stmt;
      50             : 
      51          10 :         out = 0;
      52          10 :         len = sizeof(out);
      53          10 :         CHKGetConnectAttr(1224, (SQLPOINTER) &out, sizeof(out), &len, "SE");
      54             : 
      55             :         /* test we really support MARS on this connection */
      56             :         /* TODO should out be correct ?? */
      57          10 :         printf("Following row can contain an error due to MARS detection (is expected)\n");
      58          10 :         if (!out || odbc_command2("BEGIN TRANSACTION", "SNoE") != SQL_ERROR) {
      59           8 :                 printf("MARS not supported for this connection\n");
      60           8 :                 odbc_disconnect();
      61           8 :                 odbc_test_skipped();
      62           0 :                 return 0;
      63             :         }
      64           2 :         odbc_read_error();
      65           2 :         if (!strstr(odbc_err, "MARS")) {
      66           0 :                 fprintf(stderr, "Error message invalid \"%s\"\n", odbc_err);
      67           0 :                 return 1;
      68             :         }
      69             : 
      70             :         /* create a test table with some data */
      71           2 :         odbc_command("create table #mars1 (n int, v varchar(100))");
      72         122 :         for (i = 0; i < 60; ++i) {
      73             :                 char cmd[120], buf[80];
      74         120 :                 memset(buf, 'a' + (i % 26), sizeof(buf));
      75         120 :                 buf[i * 7 % 73] = 0;
      76         120 :                 sprintf(cmd, "insert into #mars1 values(%d, '%s')", i, buf);
      77         120 :                 odbc_command(cmd);
      78             :         }
      79             : 
      80             :         /* and another to avid locking problems */
      81           2 :         odbc_command("create table #mars2 (n int, v varchar(100))");
      82             : 
      83           2 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
      84             : 
      85             :         /* try to do a select which return a lot of data (to test server didn't cache everything) */
      86           2 :         odbc_command("select a.n, b.n, c.n, a.v from #mars1 a, #mars1 b, #mars1 c order by a.n, b.n, c.n");
      87           2 :         CHKFetch("S");
      88             : 
      89           2 :         CHKAllocStmt(&stmt2, "S");
      90             : 
      91             :         /* Use a parameterized insert. This causes DONEINPROC to be returned by SQL Server,
      92             :          * leading to result_type==TDS_CMD_DONE when it's complete. Without the parameter,
      93             :          * result_type == TDS_DONE_RESULT.
      94             :          * And in odbc_SQLExecute(), it calls odbc_unlock_statement() for TDS_CMD_DONE, but
      95             :          * not for TDS_DONE_RESULT. (We don't know why...)
      96             :          * This means that the stmt->tds TDSSOCKET struct is completely freed after every
      97             :          * iteration of the insert if and only if it was a parameterized insert. So we need
      98             :          * to test both parameterized and non-parameterized inserts.
      99             :          */
     100           2 :         SQLBindParameter(stmt2, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &bind1, 0, NULL);
     101           2 :         SQLBindParameter(stmt2, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, &bind2, 20, NULL);
     102             : 
     103          42 :         for (i = 1; i <= n_iterations; ++i) {
     104          40 :                 SET_STMT(stmt2);
     105             : 
     106             :                 /* Test option - force reallocation of socket
     107             :                  * odbc_reset_statement();
     108             :                  */
     109          40 :                 if (i % freq_parameterized == 0) {
     110          20 :                         bind1 = i;
     111          20 :                         odbc_command("@insert into #mars2 values(?, ?)");
     112             :                 } else {
     113          20 :                         odbc_command("@insert into #mars2 values(1, 'foo')");
     114             :                 }
     115             : 
     116             :                 /* Perform several fetches for each insert, so we also test continuing to draw
     117             :                  * further packets of the fetch
     118             :                  */
     119          40 :                 SET_STMT(stmt1);
     120         440 :                 for (j = 0; j < 10; ++j)
     121         400 :                         CHKFetch("S");
     122             :         }
     123           2 :         printf("Performed %d inserts while fetching.\n", i - 1);
     124             : 
     125             :         /* reset statements */
     126           2 :         SET_STMT(stmt1);
     127           2 :         odbc_reset_statement();
     128           2 :         SET_STMT(stmt2);
     129           2 :         odbc_reset_statement();
     130             : 
     131             :         /* now to 2 select with prepare/execute */
     132           2 :         CHKPrepare(T("select a.n, b.n, a.v from #mars1 a, #mars1 b order by a.n, b.n"), SQL_NTS, "S");
     133           2 :         SET_STMT(stmt1);
     134           2 :         CHKPrepare(T("select a.n, b.n, a.v from #mars1 a, #mars1 b order by a.n desc, b.n"), SQL_NTS, "S");
     135           2 :         SET_STMT(stmt2);
     136           2 :         CHKExecute("S");
     137           2 :         SET_STMT(stmt1);
     138           2 :         CHKExecute("S");
     139           2 :         SET_STMT(stmt2);
     140           2 :         CHKFetch("S");
     141           2 :         SET_STMT(stmt1);
     142           2 :         CHKFetch("S");
     143           2 :         SET_STMT(stmt2);
     144           2 :         CHKFetch("S");
     145           2 :         odbc_reset_statement();
     146           2 :         SET_STMT(stmt1);
     147           2 :         CHKFetch("S");
     148           2 :         odbc_reset_statement();
     149             : 
     150           2 :         EndTransaction(SQL_COMMIT);
     151             : 
     152           2 :         odbc_disconnect();
     153           2 :         return 0;
     154             : }
     155             : 

Generated by: LCOV version 1.13