Allow setting isolation level before the establishment of connection.
authorHiroshi Inoue <h-inoue@dream.email.ne.jp>
Tue, 18 Oct 2016 12:04:38 +0000 (21:04 +0900)
committerHiroshi Inoue <h-inoue@dream.email.ne.jp>
Tue, 18 Oct 2016 12:04:38 +0000 (21:04 +0900)
connection.c
connection.h
environ.c
info.c
options.c

index e00a32f97ae8143f840a30e9f0a7a2dab5962a55..88fbd04b224ffa2d3647f22de09cbee3178c6fce 100644 (file)
@@ -966,6 +966,12 @@ CC_connect(ConnectionClass *self, char *salt_para)
    }
 #endif /* UNICODE_SUPPORT */
 
+   if (!CC_set_transact(self, self->isolation))
+   {
+       ret = 0;
+       goto cleanup;
+   }
+
    ci->updatable_cursors = DISALLOW_UPDATABLE_CURSORS;
    if (ci->allow_keyset)
    {
@@ -3002,3 +3008,46 @@ PgDtc_isolate(void *self, DWORD option)
 }
 
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
+
+BOOL
+CC_set_transact(ConnectionClass *self, UInt4 isolation)
+{
+   char *query;
+   QResultClass *res;
+
+   if (PG_VERSION_LT(self, 8.0) &&
+       (isolation == SQL_TXN_READ_UNCOMMITTED ||
+        isolation == SQL_TXN_REPEATABLE_READ))
+   {
+       CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "READ_UNCOMMITTED or REPEATABLE_READ is not supported by the server", __FUNCTION__);
+       return FALSE;
+   }
+
+   switch (isolation)
+   {
+       case SQL_TXN_SERIALIZABLE:
+           query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE";
+                       break;
+       case SQL_TXN_REPEATABLE_READ:
+           query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ";
+           break;
+       case SQL_TXN_READ_UNCOMMITTED:
+           query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
+           break;
+       default:
+           query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED";
+           break;
+   }
+   res = CC_send_query(self, query, NULL, 0, NULL);
+   if (!QR_command_maybe_successful(res))
+   {
+       CC_set_error(self, CONN_EXEC_ERROR, "ISOLATION change request to the server error", __FUNCTION__);
+       QR_Destructor(res);
+       return FALSE;
+   }
+   QR_Destructor(res);
+   self->isolation_set_delay = 0;
+
+   return TRUE;
+}
+
index 77a110079a1ed28dc3fb8babb39b214b50c96304..1dcc396b4f30104b0aafd2431667b4cb65a6e606 100644 (file)
@@ -74,7 +74,7 @@ enum
 #define CONN_INVALID_AUTHENTICATION                    210
 #define CONN_AUTH_TYPE_UNSUPPORTED                 211
 #define CONN_UNABLE_TO_LOAD_DLL                        212
-
+#define CONN_ILLEGAL_TRANSACT_STATE                    213
 #define CONN_VALUE_OUT_OF_RANGE                        214
 
 #define CONN_OPTION_NOT_FOR_THE_DRIVER                 216
@@ -322,6 +322,7 @@ struct ConnectionClass_
                                         * current_schema == NULL means it's
                                         * really NULL, while FALSE means it's
                                         * unknown */
+   char        isolation_set_delay;
    StatementClass *unnamed_prepared_stmt;
    Int2        max_identifier_length;
    Int2        num_discardp;
@@ -433,6 +434,9 @@ char    *schema_strcat1(char *buf, const char *fmt, const char *s1,
 
 void   CC_examine_global_transaction(ConnectionClass *self);
 
+
+BOOL CC_set_transact(ConnectionClass *self, UInt4 isolation);
+
 /* CC_send_query options */
 enum {
    IGNORE_ABORT_ON_CONN    = 1L /* not set the error result even when  */
index 99ebfee6a8ad2c1ea2fb884512d13a90f6d4e25c..aa6fd92dc29323467c79b7cf5bf9d5575eef904a 100644 (file)
--- a/environ.c
+++ b/environ.c
@@ -370,13 +370,7 @@ PGAPI_ConnectError(HDBC hdbc,
                /* invalid argument value */
                break;
            case CONN_TRANSACT_IN_PROGRES:
-               pg_sqlstate_set(env, szSqlState, "HY010", "S1010");
-
-               /*
-                * when the user tries to switch commit mode in a
-                * transaction
-                */
-               /* -> function sequence error */
+               pg_sqlstate_set(env, szSqlState, "HY011", "S1011");
                break;
            case CONN_NO_MEMORY_ERROR:
                pg_sqlstate_set(env, szSqlState, "HY001", "S1001");
@@ -384,6 +378,9 @@ PGAPI_ConnectError(HDBC hdbc,
            case CONN_NOT_IMPLEMENTED_ERROR:
                pg_sqlstate_set(env, szSqlState, "HYC00", "S1C00");
                break;
+           case CONN_ILLEGAL_TRANSACT_STATE:
+               pg_sqlstate_set(env, szSqlState, "25000", "S1010");
+               break;
            case CONN_VALUE_OUT_OF_RANGE:
                pg_sqlstate_set(env, szSqlState, "HY019", "22003");
                break;
diff --git a/info.c b/info.c
index 21ff1878c7730a26d26f8980d1eb8d81412930b7..b5436454aec708f3d1c80d0e79ad5caafa6dfca9 100644 (file)
--- a/info.c
+++ b/info.c
@@ -672,7 +672,8 @@ mylog("CONVERT_FUNCTIONS=" FORMAT_ULEN "\n", value);
 
        case SQL_TXN_ISOLATION_OPTION:  /* ODBC 1.0 */
            len = 4;
-           value = SQL_TXN_READ_COMMITTED | SQL_TXN_SERIALIZABLE;
+           value = SQL_TXN_READ_UNCOMMITTED | SQL_TXN_READ_COMMITTED |
+               SQL_TXN_REPEATABLE_READ | SQL_TXN_SERIALIZABLE;
            break;
 
        case SQL_UNION: /* ODBC 2.0 */
index da6aef0e0efe7f489451f20dca8c6f6073286e8b..9812ed475f59f97906f4a92a4fd7d5d0c2a265f9 100644 (file)
--- a/options.c
+++ b/options.c
@@ -372,7 +372,7 @@ PGAPI_SetConnectOption(HDBC hdbc,
 #ifdef _HANDLE_ENLIST_IN_DTC_
            if (CC_is_in_global_trans(conn))
            {
-               CC_set_error(conn, CONN_TRANSACT_IN_PROGRES, "Don't change AUTOCOMMIT mode in a distributed transaction", func);
+               CC_set_error(conn, CONN_ILLEGAL_TRANSACT_STATE, "Don't change AUTOCOMMIT mode in a distributed transaction", func);
                return SQL_ERROR;
            }
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
@@ -393,73 +393,35 @@ PGAPI_SetConnectOption(HDBC hdbc,
            break;
 
        case SQL_TXN_ISOLATION:
-           retval = SQL_SUCCESS;
            if (conn->isolation == vParam)
                break;
-           switch (vParam)
-           {
-               case SQL_TXN_SERIALIZABLE:
-                   break;
-               case SQL_TXN_REPEATABLE_READ:
-                   if (PG_VERSION_LT(conn, 8.0))
-                       retval = SQL_ERROR;
-                   break;
-               case SQL_TXN_READ_COMMITTED:
-                   break;
-               case SQL_TXN_READ_UNCOMMITTED:
-                   if (PG_VERSION_LT(conn, 8.0))
-                       retval = SQL_ERROR;
-                   break;
-               default:
-                   retval = SQL_ERROR;
-           }
-           if (SQL_ERROR == retval)
+           /*
+            * If the connection is not established, just record the setting to
+            * reflect it upon connection.
+            */
+           if (conn->status == CONN_NOT_CONNECTED || conn->status == CONN_DOWN)
            {
-               CC_set_error(conn, CONN_INVALID_ARGUMENT_NO, "Illegal parameter value for SQL_TXN_ISOLATION", func);
-               return SQL_ERROR;
+               conn->isolation = (UInt4) vParam;
+               conn->isolation_set_delay = 1;
+               break;
            }
-           else
-           {
-               char *query;
-               QResultClass *res;
 
-               if (CC_is_in_trans(conn))
-               {
-                   if (CC_does_autocommit(conn) && !CC_is_in_error_trans(conn))
-                       CC_commit(conn);
-                   else
-                   {
-                       CC_set_error(conn, CONN_TRANSACT_IN_PROGRES, "Cannot switch isolation level while a transaction is in progress", func);
-                       return SQL_ERROR;
-                   }
-               }
-               switch (vParam)
-               {
-                   case SQL_TXN_SERIALIZABLE:
-                       query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE";
-                       break;
-                   case SQL_TXN_REPEATABLE_READ:
-                       query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL REPEATABLE READ";
-                       break;
-                   case SQL_TXN_READ_UNCOMMITTED:
-                       query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
-                       break;
-                   default:
-                       query = "SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL READ COMMITTED";
-                       break;
-               }
-               res = CC_send_query(conn, query, NULL, 0, NULL);
-               if (!QR_command_maybe_successful(res))
-                   retval = SQL_ERROR;
+           /* The ODBC spec prohibits changing the isolation level while in manual transaction. */
+           if (CC_is_in_trans(conn))
+           {
+               if (CC_does_autocommit(conn) && !CC_is_in_error_trans(conn))
+                   CC_commit(conn);
                else
-                   conn->isolation = (UInt4) vParam;
-               QR_Destructor(res);
-               if (SQL_ERROR == retval)
                {
-                   CC_set_error(conn, CONN_EXEC_ERROR, "ISOLATION change request to the server error", func);
+                   CC_set_error(conn, CONN_TRANSACT_IN_PROGRES, "Cannot switch isolation level while a transaction is in progress", func);
                    return SQL_ERROR;
                }
            }
+
+           if (!CC_set_transact(conn, (UInt4) vParam))
+               return SQL_ERROR;
+
+           conn->isolation = (UInt4) vParam;
            break;
 
        /* These options should be handled by driver manager */