LCOV - code coverage report
Current view: top level - src/odbc/unittests - transaction2.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 146 154 94.8 %
Date: 2025-01-18 12:13:41 Functions: 9 9 100.0 %

          Line data    Source code
       1             : #include "common.h"
       2             : #include <assert.h>
       3             : 
       4             : /* Test transaction types */
       5             : 
       6             : static void
       7          16 : ReadErrorConn(void)
       8             : {
       9          16 :         ODBC_BUF *odbc_buf = NULL;
      10          16 :         SQLTCHAR *err = (SQLTCHAR *) ODBC_GET(sizeof(odbc_err)*sizeof(SQLTCHAR));
      11          16 :         SQLTCHAR *state = (SQLTCHAR *) ODBC_GET(sizeof(odbc_sqlstate)*sizeof(SQLTCHAR));
      12             : 
      13          16 :         memset(odbc_err, 0, sizeof(odbc_err));
      14          16 :         memset(odbc_sqlstate, 0, sizeof(odbc_sqlstate));
      15          16 :         CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, state, NULL, err, sizeof(odbc_err), NULL, "SI");
      16          16 :         strcpy(odbc_err, C(err));
      17          16 :         strcpy(odbc_sqlstate, C(state));
      18          16 :         ODBC_FREE();
      19          16 :         printf("Message: '%s' %s\n", odbc_sqlstate, odbc_err);
      20          16 : }
      21             : 
      22             : static void
      23          58 : AutoCommit(int onoff)
      24             : {
      25          58 :         CHKSetConnectAttr(SQL_ATTR_AUTOCOMMIT, TDS_INT2PTR(onoff), 0, "S");
      26          58 : }
      27             : 
      28             : static void
      29         508 : EndTransaction(SQLSMALLINT type)
      30             : {
      31         508 :         CHKEndTran(SQL_HANDLE_DBC, odbc_conn, type, "S");
      32         508 : }
      33             : 
      34             : #define SWAP(t,a,b) do { t xyz = a; a = b; b = xyz; } while(0)
      35             : #define SWAP_CONN() do { SWAP(HENV,env,odbc_env); SWAP(HDBC,dbc,odbc_conn); SWAP(HSTMT,stmt,odbc_stmt);} while(0)
      36             : 
      37             : static HENV env = SQL_NULL_HENV;
      38             : static HDBC dbc = SQL_NULL_HDBC;
      39             : static HSTMT stmt = SQL_NULL_HSTMT;
      40             : 
      41             : static int
      42          68 : CheckDirtyRead(void)
      43             : {
      44             :         SQLRETURN RetCode;
      45             : 
      46             :         /* transaction 1 try to change a row but not commit */
      47          68 :         odbc_command("UPDATE test_transaction SET t = 'second' WHERE n = 1");
      48             : 
      49          68 :         SWAP_CONN();
      50             : 
      51             :         /* second transaction try to fetch uncommited row */
      52          68 :         RetCode = odbc_command2("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1", "SE");
      53          68 :         if (RetCode == SQL_ERROR) {
      54          52 :                 EndTransaction(SQL_ROLLBACK);
      55          52 :                 SWAP_CONN();
      56          52 :                 EndTransaction(SQL_ROLLBACK);
      57          52 :                 return 0;       /* no dirty read */
      58             :         }
      59             : 
      60          16 :         CHKFetch("S");
      61          16 :         CHKFetch("No");
      62          16 :         SQLMoreResults(odbc_stmt);
      63          16 :         EndTransaction(SQL_ROLLBACK);
      64          16 :         SWAP_CONN();
      65          16 :         EndTransaction(SQL_ROLLBACK);
      66          16 :         return 1;
      67             : }
      68             : 
      69             : static int
      70          68 : CheckNonrepeatableRead(void)
      71             : {
      72             :         SQLRETURN RetCode;
      73             : 
      74             :         /* transaction 2 read a row */
      75          68 :         SWAP_CONN();
      76          68 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial' AND n = 1");
      77          68 :         SQLMoreResults(odbc_stmt);
      78             : 
      79             :         /* transaction 1 change a row and commit */
      80          68 :         SWAP_CONN();
      81          68 :         RetCode = odbc_command2("UPDATE test_transaction SET t = 'second' WHERE n = 1", "SE");
      82          68 :         if (RetCode == SQL_ERROR) {
      83          36 :                 EndTransaction(SQL_ROLLBACK);
      84          36 :                 SWAP_CONN();
      85          36 :                 EndTransaction(SQL_ROLLBACK);
      86          36 :                 SWAP_CONN();
      87          36 :                 return 0;       /* no dirty read */
      88             :         }
      89          32 :         EndTransaction(SQL_COMMIT);
      90             : 
      91          32 :         SWAP_CONN();
      92             : 
      93             :         /* second transaction try to fetch commited row */
      94          32 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1");
      95             : 
      96          32 :         CHKFetch("S");
      97          32 :         CHKFetch("No");
      98          32 :         SQLMoreResults(odbc_stmt);
      99          32 :         EndTransaction(SQL_ROLLBACK);
     100          32 :         SWAP_CONN();
     101          32 :         odbc_command("UPDATE test_transaction SET t = 'initial' WHERE n = 1");
     102          32 :         EndTransaction(SQL_COMMIT);
     103          32 :         return 1;
     104             : }
     105             : 
     106             : static int
     107          68 : CheckPhantom(void)
     108             : {
     109             :         SQLRETURN RetCode;
     110             : 
     111             :         /* transaction 2 read a row */
     112          68 :         SWAP_CONN();
     113          68 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
     114          68 :         SQLMoreResults(odbc_stmt);
     115             : 
     116             :         /* transaction 1 insert a row that match critera */
     117          68 :         SWAP_CONN();
     118          68 :         RetCode = odbc_command2("INSERT INTO test_transaction(n, t) VALUES(2, 'initial')", "SE");
     119          68 :         if (RetCode == SQL_ERROR) {
     120          24 :                 EndTransaction(SQL_ROLLBACK);
     121          24 :                 SWAP_CONN();
     122          24 :                 EndTransaction(SQL_ROLLBACK);
     123          24 :                 SWAP_CONN();
     124          24 :                 return 0;       /* no dirty read */
     125             :         }
     126          44 :         EndTransaction(SQL_COMMIT);
     127             : 
     128          44 :         SWAP_CONN();
     129             : 
     130             :         /* second transaction try to fetch commited row */
     131          44 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
     132             : 
     133          44 :         CHKFetch("S");
     134          44 :         CHKFetch("S");
     135          44 :         CHKFetch("No");
     136          44 :         SQLMoreResults(odbc_stmt);
     137          44 :         EndTransaction(SQL_ROLLBACK);
     138          44 :         SWAP_CONN();
     139          44 :         odbc_command("DELETE test_transaction WHERE n = 2");
     140          44 :         EndTransaction(SQL_COMMIT);
     141          44 :         return 1;
     142             : }
     143             : 
     144             : static int test_with_connect = 0;
     145             : 
     146             : static int global_txn;
     147             : 
     148             : static int hide_error;
     149             : 
     150             : static void
     151          34 : my_attrs(void)
     152             : {
     153          34 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(global_txn), 0, "S");
     154          34 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     155          34 : }
     156             : 
     157             : static void
     158             : ConnectWithTxn(int txn)
     159             : {
     160          34 :         global_txn = txn;
     161          34 :         odbc_set_conn_attr = my_attrs;
     162          34 :         odbc_connect();
     163          34 :         odbc_set_conn_attr = NULL;
     164             : }
     165             : 
     166             : static int
     167          68 : Test(int txn, const char *expected)
     168             : {
     169             :         int dirty, repeatable, phantom;
     170             :         char buf[128];
     171             : 
     172          68 :         SWAP_CONN();
     173          68 :         if (test_with_connect) {
     174          34 :                 odbc_disconnect();
     175          34 :                 ConnectWithTxn(txn);
     176          34 :                 CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     177             :         } else {
     178          34 :                 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(txn), 0, "S");
     179             :         }
     180          68 :         SWAP_CONN();
     181             : 
     182          68 :         dirty = CheckDirtyRead();
     183          68 :         repeatable = CheckNonrepeatableRead();
     184          68 :         phantom = CheckPhantom();
     185             : 
     186          68 :         sprintf(buf, "dirty %d non repeatable %d phantom %d", dirty, repeatable, phantom);
     187          68 :         if (strcmp(buf, expected) != 0) {
     188           4 :                 if (hide_error) {
     189           4 :                         hide_error = 0;
     190           4 :                         return 0;
     191             :                 }
     192           0 :                 fprintf(stderr, "detected wrong TXN\nexpected '%s' got '%s'\n", expected, buf);
     193           0 :                 exit(1);
     194             :         }
     195          64 :         hide_error = 0;
     196          64 :         return 1;
     197             : }
     198             : 
     199             : int
     200           8 : main(int argc, char *argv[])
     201             : {
     202           8 :         odbc_use_version3 = 1;
     203           8 :         odbc_connect();
     204             : 
     205             :         /* Invalid argument value */
     206           8 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(SQL_TXN_REPEATABLE_READ | SQL_TXN_READ_COMMITTED), 0, "E");
     207           8 :         ReadErrorConn();
     208           8 :         if (strcmp(odbc_sqlstate, "HY024") != 0) {
     209           0 :                 odbc_disconnect();
     210           0 :                 fprintf(stderr, "Unexpected success\n");
     211           0 :                 return 1;
     212             :         }
     213             : 
     214             :         /* here we can't use temporary table cause we use two connection */
     215           8 :         odbc_command("IF OBJECT_ID('test_transaction') IS NOT NULL DROP TABLE test_transaction");
     216           8 :         odbc_command("CREATE TABLE test_transaction(n NUMERIC(18,0) PRIMARY KEY, t VARCHAR(30))");
     217             : 
     218           8 :         CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     219             : 
     220           8 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     221           8 :         odbc_command("INSERT INTO test_transaction(n, t) VALUES(1, 'initial')");
     222             : 
     223             : #ifdef ENABLE_DEVELOPING
     224             :         /* test setting with active transaction "Operation invalid at this time" */
     225             :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(SQL_TXN_REPEATABLE_READ), 0, "E");
     226             :         ReadErrorConn();
     227             :         if (strcmp(odbc_sqlstate, "HY011") != 0) {
     228             :                 odbc_disconnect();
     229             :                 fprintf(stderr, "Unexpected success\n");
     230             :                 return 1;
     231             :         }
     232             : #endif
     233             : 
     234           8 :         EndTransaction(SQL_COMMIT);
     235             : 
     236           8 :         odbc_command("SELECT * FROM test_transaction");
     237             : 
     238             :         /* test setting with pending data */
     239           8 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(SQL_TXN_REPEATABLE_READ), 0, "E");
     240           8 :         ReadErrorConn();
     241           8 :         if (strcmp(odbc_sqlstate, "HY011") != 0) {
     242           0 :                 odbc_disconnect();
     243           0 :                 fprintf(stderr, "Unexpected success\n");
     244           0 :                 return 1;
     245             :         }
     246             : 
     247           8 :         SQLMoreResults(odbc_stmt);
     248             : 
     249           8 :         EndTransaction(SQL_COMMIT);
     250             : 
     251             : 
     252             :         /* save this connection and do another */
     253           8 :         SWAP_CONN();
     254             : 
     255           8 :         odbc_connect();
     256             : 
     257           8 :         CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     258           8 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     259             : 
     260           8 :         SWAP_CONN();
     261             : 
     262          24 :         for (test_with_connect = 0; test_with_connect <= 1; ++test_with_connect) {
     263          16 :                 Test(SQL_TXN_READ_UNCOMMITTED, "dirty 1 non repeatable 1 phantom 1");
     264          16 :                 Test(SQL_TXN_READ_COMMITTED, "dirty 0 non repeatable 1 phantom 1");
     265          16 :                 if (odbc_db_is_microsoft()) {
     266          12 :                         Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 1");
     267             :                 } else {
     268           4 :                         hide_error = 1;
     269           4 :                         if (!Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 1"))
     270           4 :                                 Test(SQL_TXN_REPEATABLE_READ, "dirty 0 non repeatable 0 phantom 0");
     271             :                 }
     272          16 :                 Test(SQL_TXN_SERIALIZABLE, "dirty 0 non repeatable 0 phantom 0");
     273             :         }
     274             : 
     275           8 :         odbc_disconnect();
     276             : 
     277           8 :         SWAP_CONN();
     278             : 
     279           8 :         EndTransaction(SQL_COMMIT);
     280             : 
     281             :         /* Sybase do not accept DROP TABLE during a transaction */
     282           8 :         AutoCommit(SQL_AUTOCOMMIT_ON);
     283           8 :         odbc_command("DROP TABLE test_transaction");
     284             : 
     285           8 :         odbc_disconnect();
     286           8 :         return 0;
     287             : }

Generated by: LCOV version 1.13