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-02-21 09:36:06 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          20 : ReadErrorConn(void)
       8             : {
       9          20 :         ODBC_BUF *odbc_buf = NULL;
      10          20 :         SQLTCHAR *err = (SQLTCHAR *) ODBC_GET(sizeof(odbc_err)*sizeof(SQLTCHAR));
      11          20 :         SQLTCHAR *state = (SQLTCHAR *) ODBC_GET(sizeof(odbc_sqlstate)*sizeof(SQLTCHAR));
      12             : 
      13          20 :         memset(odbc_err, 0, sizeof(odbc_err));
      14          20 :         memset(odbc_sqlstate, 0, sizeof(odbc_sqlstate));
      15          20 :         CHKGetDiagRec(SQL_HANDLE_DBC, odbc_conn, 1, state, NULL, err, sizeof(odbc_err), NULL, "SI");
      16          20 :         strcpy(odbc_err, C(err));
      17          20 :         strcpy(odbc_sqlstate, C(state));
      18          20 :         ODBC_FREE();
      19          20 :         printf("Message: '%s' %s\n", odbc_sqlstate, odbc_err);
      20          20 : }
      21             : 
      22             : static void
      23          72 : AutoCommit(int onoff)
      24             : {
      25          72 :         CHKSetConnectAttr(SQL_ATTR_AUTOCOMMIT, TDS_INT2PTR(onoff), 0, "S");
      26          72 : }
      27             : 
      28             : static void
      29         630 : EndTransaction(SQLSMALLINT type)
      30             : {
      31         630 :         CHKEndTran(SQL_HANDLE_DBC, odbc_conn, type, "S");
      32         630 : }
      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          84 : CheckDirtyRead(void)
      43             : {
      44             :         SQLRETURN RetCode;
      45             : 
      46             :         /* transaction 1 try to change a row but not commit */
      47          84 :         odbc_command("UPDATE test_transaction SET t = 'second' WHERE n = 1");
      48             : 
      49          84 :         SWAP_CONN();
      50             : 
      51             :         /* second transaction try to fetch uncommited row */
      52          84 :         RetCode = odbc_command2("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1", "SE");
      53          84 :         if (RetCode == SQL_ERROR) {
      54          64 :                 EndTransaction(SQL_ROLLBACK);
      55          64 :                 SWAP_CONN();
      56          64 :                 EndTransaction(SQL_ROLLBACK);
      57          64 :                 return 0;       /* no dirty read */
      58             :         }
      59             : 
      60          20 :         CHKFetch("S");
      61          20 :         CHKFetch("No");
      62          20 :         SQLMoreResults(odbc_stmt);
      63          20 :         EndTransaction(SQL_ROLLBACK);
      64          20 :         SWAP_CONN();
      65          20 :         EndTransaction(SQL_ROLLBACK);
      66          20 :         return 1;
      67             : }
      68             : 
      69             : static int
      70          84 : CheckNonrepeatableRead(void)
      71             : {
      72             :         SQLRETURN RetCode;
      73             : 
      74             :         /* transaction 2 read a row */
      75          84 :         SWAP_CONN();
      76          84 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial' AND n = 1");
      77          84 :         SQLMoreResults(odbc_stmt);
      78             : 
      79             :         /* transaction 1 change a row and commit */
      80          84 :         SWAP_CONN();
      81          84 :         RetCode = odbc_command2("UPDATE test_transaction SET t = 'second' WHERE n = 1", "SE");
      82          84 :         if (RetCode == SQL_ERROR) {
      83          44 :                 EndTransaction(SQL_ROLLBACK);
      84          44 :                 SWAP_CONN();
      85          44 :                 EndTransaction(SQL_ROLLBACK);
      86          44 :                 SWAP_CONN();
      87          44 :                 return 0;       /* no dirty read */
      88             :         }
      89          40 :         EndTransaction(SQL_COMMIT);
      90             : 
      91          40 :         SWAP_CONN();
      92             : 
      93             :         /* second transaction try to fetch commited row */
      94          40 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'second' AND n = 1");
      95             : 
      96          40 :         CHKFetch("S");
      97          40 :         CHKFetch("No");
      98          40 :         SQLMoreResults(odbc_stmt);
      99          40 :         EndTransaction(SQL_ROLLBACK);
     100          40 :         SWAP_CONN();
     101          40 :         odbc_command("UPDATE test_transaction SET t = 'initial' WHERE n = 1");
     102          40 :         EndTransaction(SQL_COMMIT);
     103          40 :         return 1;
     104             : }
     105             : 
     106             : static int
     107          84 : CheckPhantom(void)
     108             : {
     109             :         SQLRETURN RetCode;
     110             : 
     111             :         /* transaction 2 read a row */
     112          84 :         SWAP_CONN();
     113          84 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
     114          84 :         SQLMoreResults(odbc_stmt);
     115             : 
     116             :         /* transaction 1 insert a row that match critera */
     117          84 :         SWAP_CONN();
     118          84 :         RetCode = odbc_command2("INSERT INTO test_transaction(n, t) VALUES(2, 'initial')", "SE");
     119          84 :         if (RetCode == SQL_ERROR) {
     120          28 :                 EndTransaction(SQL_ROLLBACK);
     121          28 :                 SWAP_CONN();
     122          28 :                 EndTransaction(SQL_ROLLBACK);
     123          28 :                 SWAP_CONN();
     124          28 :                 return 0;       /* no dirty read */
     125             :         }
     126          56 :         EndTransaction(SQL_COMMIT);
     127             : 
     128          56 :         SWAP_CONN();
     129             : 
     130             :         /* second transaction try to fetch commited row */
     131          56 :         odbc_command("SELECT * FROM test_transaction WHERE t = 'initial'");
     132             : 
     133          56 :         CHKFetch("S");
     134          56 :         CHKFetch("S");
     135          56 :         CHKFetch("No");
     136          56 :         SQLMoreResults(odbc_stmt);
     137          56 :         EndTransaction(SQL_ROLLBACK);
     138          56 :         SWAP_CONN();
     139          56 :         odbc_command("DELETE test_transaction WHERE n = 2");
     140          56 :         EndTransaction(SQL_COMMIT);
     141          56 :         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          42 : my_attrs(void)
     152             : {
     153          42 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(global_txn), 0, "S");
     154          42 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     155          42 : }
     156             : 
     157             : static void
     158             : ConnectWithTxn(int txn)
     159             : {
     160          42 :         global_txn = txn;
     161          42 :         odbc_set_conn_attr = my_attrs;
     162          42 :         odbc_connect();
     163          42 :         odbc_set_conn_attr = NULL;
     164             : }
     165             : 
     166             : static int
     167          84 : Test(int txn, const char *expected)
     168             : {
     169             :         int dirty, repeatable, phantom;
     170             :         char buf[128];
     171             : 
     172          84 :         SWAP_CONN();
     173          84 :         if (test_with_connect) {
     174          42 :                 odbc_disconnect();
     175          42 :                 ConnectWithTxn(txn);
     176          42 :                 CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     177             :         } else {
     178          42 :                 CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(txn), 0, "S");
     179             :         }
     180          84 :         SWAP_CONN();
     181             : 
     182          84 :         dirty = CheckDirtyRead();
     183          84 :         repeatable = CheckNonrepeatableRead();
     184          84 :         phantom = CheckPhantom();
     185             : 
     186          84 :         sprintf(buf, "dirty %d non repeatable %d phantom %d", dirty, repeatable, phantom);
     187          84 :         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          80 :         hide_error = 0;
     196          80 :         return 1;
     197             : }
     198             : 
     199             : int
     200          10 : main(void)
     201             : {
     202          10 :         odbc_use_version3 = 1;
     203          10 :         odbc_connect();
     204             : 
     205             :         /* Invalid argument value */
     206          10 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(SQL_TXN_REPEATABLE_READ | SQL_TXN_READ_COMMITTED), 0, "E");
     207          10 :         ReadErrorConn();
     208          10 :         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          10 :         odbc_command("IF OBJECT_ID('test_transaction') IS NOT NULL DROP TABLE test_transaction");
     216          10 :         odbc_command("CREATE TABLE test_transaction(n NUMERIC(18,0) PRIMARY KEY, t VARCHAR(30))");
     217             : 
     218          10 :         CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     219             : 
     220          10 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     221          10 :         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          10 :         EndTransaction(SQL_COMMIT);
     235             : 
     236          10 :         odbc_command("SELECT * FROM test_transaction");
     237             : 
     238             :         /* test setting with pending data */
     239          10 :         CHKSetConnectAttr(SQL_ATTR_TXN_ISOLATION, TDS_INT2PTR(SQL_TXN_REPEATABLE_READ), 0, "E");
     240          10 :         ReadErrorConn();
     241          10 :         if (strcmp(odbc_sqlstate, "HY011") != 0) {
     242           0 :                 odbc_disconnect();
     243           0 :                 fprintf(stderr, "Unexpected success\n");
     244           0 :                 return 1;
     245             :         }
     246             : 
     247          10 :         SQLMoreResults(odbc_stmt);
     248             : 
     249          10 :         EndTransaction(SQL_COMMIT);
     250             : 
     251             : 
     252             :         /* save this connection and do another */
     253          10 :         SWAP_CONN();
     254             : 
     255          10 :         odbc_connect();
     256             : 
     257          10 :         CHKSetStmtAttr(SQL_ATTR_QUERY_TIMEOUT, (SQLPOINTER) 2, 0, "S");
     258          10 :         AutoCommit(SQL_AUTOCOMMIT_OFF);
     259             : 
     260          10 :         SWAP_CONN();
     261             : 
     262          30 :         for (test_with_connect = 0; test_with_connect <= 1; ++test_with_connect) {
     263          20 :                 Test(SQL_TXN_READ_UNCOMMITTED, "dirty 1 non repeatable 1 phantom 1");
     264          20 :                 Test(SQL_TXN_READ_COMMITTED, "dirty 0 non repeatable 1 phantom 1");
     265          20 :                 if (odbc_db_is_microsoft()) {
     266          16 :                         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          20 :                 Test(SQL_TXN_SERIALIZABLE, "dirty 0 non repeatable 0 phantom 0");
     273             :         }
     274             : 
     275          10 :         odbc_disconnect();
     276             : 
     277          10 :         SWAP_CONN();
     278             : 
     279          10 :         EndTransaction(SQL_COMMIT);
     280             : 
     281             :         /* Sybase do not accept DROP TABLE during a transaction */
     282          10 :         AutoCommit(SQL_AUTOCOMMIT_ON);
     283          10 :         odbc_command("DROP TABLE test_transaction");
     284             : 
     285          10 :         odbc_disconnect();
     286          10 :         return 0;
     287             : }

Generated by: LCOV version 1.13