LCOV - code coverage report
Current view: top level - src/odbc/unittests - mars1.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 75 78 96.2 %
Date: 2026-02-12 22:26:45 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_use_version3 = true;
      47          10 :         odbc_set_conn_attr = my_attrs;
      48          10 :         odbc_connect();
      49             : 
      50          10 :         stmt1 = odbc_stmt;
      51             : 
      52          10 :         out = 0;
      53          10 :         len = sizeof(out);
      54          10 :         CHKGetConnectAttr(1224, (SQLPOINTER) &out, sizeof(out), &len, "SE");
      55             : 
      56             :         /* test we really support MARS on this connection */
      57             :         /* TODO should out be correct ?? */
      58          10 :         printf("Following row can contain an error due to MARS detection (is expected)\n");
      59          10 :         if (!out || odbc_command2("BEGIN TRANSACTION", "SNoE") != SQL_ERROR) {
      60           8 :                 printf("MARS not supported for this connection\n");
      61           8 :                 odbc_disconnect();
      62           8 :                 odbc_test_skipped();
      63           0 :                 return 0;
      64             :         }
      65           2 :         odbc_read_error();
      66           2 :         if (!strstr(odbc_err, "MARS")) {
      67           0 :                 fprintf(stderr, "Error message invalid \"%s\"\n", odbc_err);
      68           0 :                 return 1;
      69             :         }
      70             : 
      71             :         /* create a test table with some data */
      72           2 :         odbc_command("create table #mars1 (n int, v varchar(100))");
      73         122 :         for (i = 0; i < 60; ++i) {
      74             :                 char cmd[120], buf[80];
      75         120 :                 memset(buf, 'a' + (i % 26), sizeof(buf));
      76         120 :                 buf[i * 7 % 73] = 0;
      77         120 :                 sprintf(cmd, "insert into #mars1 values(%d, '%s')", i, buf);
      78         120 :                 odbc_command(cmd);
      79             :         }
      80             : 
      81             :         /* and another to avid locking problems */
      82           2 :         odbc_command("create table #mars2 (n int, v varchar(100))");
      83             : 
      84           2 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
      85             : 
      86             :         /* try to do a select which return a lot of data (to test server didn't cache everything) */
      87           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");
      88           2 :         CHKFetch("S");
      89             : 
      90           2 :         CHKAllocStmt(&stmt2, "S");
      91             : 
      92             :         /* Use a parameterized insert. This causes DONEINPROC to be returned by SQL Server,
      93             :          * leading to result_type==TDS_CMD_DONE when it's complete. Without the parameter,
      94             :          * result_type == TDS_DONE_RESULT.
      95             :          * And in odbc_SQLExecute(), it calls odbc_unlock_statement() for TDS_CMD_DONE, but
      96             :          * not for TDS_DONE_RESULT. (We don't know why...)
      97             :          * This means that the stmt->tds TDSSOCKET struct is completely freed after every
      98             :          * iteration of the insert if and only if it was a parameterized insert. So we need
      99             :          * to test both parameterized and non-parameterized inserts.
     100             :          */
     101           2 :         SQLBindParameter(stmt2, 1, SQL_PARAM_INPUT, SQL_C_SLONG, SQL_INTEGER, 0, 0, &bind1, 0, NULL);
     102           2 :         SQLBindParameter(stmt2, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, &bind2, 20, NULL);
     103             : 
     104          42 :         for (i = 1; i <= n_iterations; ++i) {
     105          40 :                 SET_STMT(stmt2);
     106             : 
     107             :                 /* Test option - force reallocation of socket
     108             :                  * odbc_reset_statement();
     109             :                  */
     110          40 :                 if (i % freq_parameterized == 0) {
     111          20 :                         bind1 = i;
     112          20 :                         odbc_command("@insert into #mars2 values(?, ?)");
     113             :                 } else {
     114          20 :                         odbc_command("@insert into #mars2 values(1, 'foo')");
     115             :                 }
     116             : 
     117             :                 /* Perform several fetches for each insert, so we also test continuing to draw
     118             :                  * further packets of the fetch
     119             :                  */
     120          40 :                 SET_STMT(stmt1);
     121         440 :                 for (j = 0; j < 10; ++j)
     122         400 :                         CHKFetch("S");
     123             :         }
     124           2 :         printf("Performed %d inserts while fetching.\n", i - 1);
     125             : 
     126             :         /* reset statements */
     127           2 :         SET_STMT(stmt1);
     128           2 :         odbc_reset_statement();
     129           2 :         SET_STMT(stmt2);
     130           2 :         odbc_reset_statement();
     131             : 
     132             :         /* now to 2 select with prepare/execute */
     133           2 :         CHKPrepare(T("select a.n, b.n, a.v from #mars1 a, #mars1 b order by a.n, b.n"), SQL_NTS, "S");
     134           2 :         SET_STMT(stmt1);
     135           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");
     136           2 :         SET_STMT(stmt2);
     137           2 :         CHKExecute("S");
     138           2 :         SET_STMT(stmt1);
     139           2 :         CHKExecute("S");
     140           2 :         SET_STMT(stmt2);
     141           2 :         CHKFetch("S");
     142           2 :         SET_STMT(stmt1);
     143           2 :         CHKFetch("S");
     144           2 :         SET_STMT(stmt2);
     145           2 :         CHKFetch("S");
     146           2 :         odbc_reset_statement();
     147           2 :         SET_STMT(stmt1);
     148           2 :         CHKFetch("S");
     149           2 :         odbc_reset_statement();
     150             : 
     151           2 :         EndTransaction(SQL_COMMIT);
     152             : 
     153           2 :         odbc_disconnect();
     154           2 :         return 0;
     155             : }
     156             : 

Generated by: LCOV version 1.13