Exemple #1
0
        public void PqsqlConnectionTest4()
        {
            PqsqlConnection connection = new PqsqlConnection(connectionString);

            connection.Open();
            Assert.AreEqual(ConnectionState.Open, connection.State, "wrong connection state");

            IntPtr conn_attempt1 = connection.PGConnection;

            int sock1 = PqsqlWrapper.PQsocket(conn_attempt1);

            Assert.AreNotEqual(-1, sock1, "wrong socket");

            // close the underlying socket without letting Pqsql and libpq know
            int closed = CloseSocket(sock1);

            Assert.AreEqual(0, closed, "closesocket failed");

            connection.Close();
            Assert.AreEqual(ConnectionState.Closed, connection.State, "wrong connection state");

            connection.Open();
            IntPtr conn_attempt2 = connection.PGConnection;

            ConnStatusType status = PqsqlWrapper.PQstatus(conn_attempt2);

            Assert.AreEqual(ConnStatusType.CONNECTION_OK, status, "connection broken");
            Assert.AreEqual(ConnectionState.Open, connection.State, "wrong connection state");

            int sock2 = PqsqlWrapper.PQsocket(conn_attempt2);

            // close the underlying socket without letting Pqsql and libpq know
            closed = CloseSocket(sock2);
            Assert.AreEqual(0, closed, "closesocket failed");
        }
        internal static IntPtr SetupPGConn(PqsqlConnectionStringBuilder connStringBuilder, out ConnStatusType connStatus, out PGTransactionStatusType tranStatus)
        {
#if CODECONTRACTS
            Contract.Requires <ArgumentNullException>(connStringBuilder != null);
#else
            if (connStringBuilder == null)
            {
                throw new ArgumentNullException(nameof(connStringBuilder));
            }
#endif

            // setup null-terminated key-value arrays for the connection
            string[] keys = new string[connStringBuilder.Keys.Count + 1];
            string[] vals = new string[connStringBuilder.Values.Count + 1];

            // get keys and values from PqsqlConnectionStringBuilder
            connStringBuilder.Keys.CopyTo(keys, 0);
            connStringBuilder.Values.CopyTo(vals, 0);

            // now create connection
            IntPtr conn = PqsqlWrapper.PQconnectdbParams(keys, vals, 0);

            if (conn == IntPtr.Zero)
            {
                connStatus = ConnStatusType.CONNECTION_BAD;
                tranStatus = PGTransactionStatusType.PQTRANS_UNKNOWN;
                return(conn);
            }

            // always force client_encoding to utf8
            int client_encoding = PqsqlWrapper.PQclientEncoding(conn);

            if (client_encoding == (int)PgEnc.PG_UTF8)              // done
            {
                connStatus = PqsqlWrapper.PQstatus(conn);
                tranStatus = PqsqlWrapper.PQtransactionStatus(conn);
            }
            else if (client_encoding == -1)             // bail out, but keep connection object
            {
                connStatus = ConnStatusType.CONNECTION_BAD;
                tranStatus = PGTransactionStatusType.PQTRANS_UNKNOWN;
            }
            else                                                                    // try to set client_encoding to utf8
            {
                if (PqsqlWrapper.PQsetClientEncoding(conn, PgEncName.PG_UTF8) == 0) // success
                {
                    connStatus = PqsqlWrapper.PQstatus(conn);
                    tranStatus = PqsqlWrapper.PQtransactionStatus(conn);
                }
                else                 // bail out, but keep connection object
                {
                    connStatus = ConnStatusType.CONNECTION_BAD;
                    tranStatus = PGTransactionStatusType.PQTRANS_UNKNOWN;
                }
            }

            return(conn);
        }
        private static bool DiscardConnection(IntPtr conn)
        {
            if (conn == IntPtr.Zero)
            {
                return(false);
            }

            bool rollback = false;

            ConnStatusType cs = PqsqlWrapper.PQstatus(conn);

            if (cs != ConnStatusType.CONNECTION_OK)
            {
                return(false);                // connection broken
            }
            PGTransactionStatusType ts = PqsqlWrapper.PQtransactionStatus(conn);

            switch (ts)
            {
            case PGTransactionStatusType.PQTRANS_INERROR:                     /* idle, within failed transaction */
            case PGTransactionStatusType.PQTRANS_INTRANS:                     /* idle, within transaction block */
                rollback = true;
                break;

            case PGTransactionStatusType.PQTRANS_IDLE:                     /* connection idle */
                // nothing to do
                break;

            // PGTransactionStatusType.PQTRANS_ACTIVE: /* command in progress */
            // PGTransactionStatusType.PQTRANS_UNKNOWN: /* cannot determine status */
            default:
                return(false);                        // connection broken
            }

            IntPtr         res;
            ExecStatusType s = ExecStatusType.PGRES_FATAL_ERROR;

            // we need to rollback before we can discard the connection
            if (rollback)
            {
                unsafe
                {
                    fixed(byte *st = PqsqlTransaction.RollbackStatement)
                    {
                        res = PqsqlWrapper.PQexec(conn, st);

                        if (res != IntPtr.Zero)
                        {
                            s = PqsqlWrapper.PQresultStatus(res);
                            PqsqlWrapper.PQclear(res);
                        }
                    }
                }

                if (s != ExecStatusType.PGRES_COMMAND_OK)
                {
                    return(false);                    // connection broken
                }

                s = ExecStatusType.PGRES_FATAL_ERROR;
            }

            // discard connection
            unsafe
            {
                fixed(byte *st = DiscardAllStatement)
                {
                    res = PqsqlWrapper.PQexec(conn, st);

                    if (res != IntPtr.Zero)
                    {
                        s = PqsqlWrapper.PQresultStatus(res);
                        PqsqlWrapper.PQclear(res);
                    }
                }
            }

            if (s != ExecStatusType.PGRES_COMMAND_OK)
            {
                return(false);                // connection broken
            }

            return(true);            // connection successfully resetted
        }
        private static bool CheckOrRelease(IntPtr pgConn, out ConnStatusType connStatus, out PGTransactionStatusType tranStatus)
        {
            if (pgConn == IntPtr.Zero)
            {
                connStatus = ConnStatusType.CONNECTION_BAD;
                tranStatus = PGTransactionStatusType.PQTRANS_UNKNOWN;
                return(false);
            }

            // is connection reusable?
            connStatus = PqsqlWrapper.PQstatus(pgConn);
            if (connStatus != ConnStatusType.CONNECTION_OK)
            {
                goto broken;
            }

            tranStatus = PqsqlWrapper.PQtransactionStatus(pgConn);
            if (tranStatus != PGTransactionStatusType.PQTRANS_IDLE)
            {
                goto broken;
            }

            //
            // now check the connection: first try to fix the client encoding if it is not utf8 for some reason.
            // if client_encoding is utf8, we send the empty query to check whether the socket is still usable
            // for the backend communiction. here, we could make use of tcp_keepalive settings.
            //
            int client_encoding = PqsqlWrapper.PQclientEncoding(pgConn);

            if (client_encoding == -1)
            {
                goto broken;
            }

            if (client_encoding == (int)PgEnc.PG_UTF8)              // client_encoding == utf8
            {
                // send empty query to test whether we are really connected (tcp_keepalive might have closed socket)
                unsafe
                {
                    byte[] empty = { 0 };                   // empty query string

                    fixed(byte *eq = empty)
                    {
                        if (PqsqlWrapper.PQsendQuery(pgConn, eq) == 0)                         // could not send query
                        {
                            goto broken;
                        }
                    }
                }

                // Reading result: consume and clear remaining results until we reach the NULL result.
                // PQgetResult will block here
                IntPtr         res;
                ExecStatusType st = ExecStatusType.PGRES_EMPTY_QUERY;
                while ((res = PqsqlWrapper.PQgetResult(pgConn)) != IntPtr.Zero)
                {
                    ExecStatusType st0 = PqsqlWrapper.PQresultStatus(res);

                    if (st0 != ExecStatusType.PGRES_EMPTY_QUERY)
                    {
                        st = st0;
                    }

                    // always free res
                    PqsqlWrapper.PQclear(res);
                }

                if (st != ExecStatusType.PGRES_EMPTY_QUERY)                 // received wrong exec status
                {
                    goto broken;
                }
            }
            else             // set client_encoding to utf8
            {
                // set client_encoding to test whether we are really connected (tcp_keepalive might have closed socket)
                if (PqsqlWrapper.PQsetClientEncoding(pgConn, PgEncName.PG_UTF8) != 0)
                {
                    goto broken;
                }
            }

            return(true);            // successfully reused connection

broken:
            // reconnect with current connection setting
            PqsqlWrapper.PQreset(pgConn);

            connStatus = PqsqlWrapper.PQstatus(pgConn);
            if (connStatus == ConnStatusType.CONNECTION_OK)
            {
                tranStatus = PqsqlWrapper.PQtransactionStatus(pgConn);
                if (tranStatus == PGTransactionStatusType.PQTRANS_IDLE)
                {
                    return(true);                    // successfully reconnected
                }
            }
            else
            {
                tranStatus = PGTransactionStatusType.PQTRANS_UNKNOWN;
            }

            // could not reconnect: finally give up and clean up memory
            PqsqlWrapper.PQfinish(pgConn);
            return(false);
        }