예제 #1
0
        // call PQexec
        internal ExecStatusType Exec(byte[] stmt, out IntPtr res)
        {
            if (mConnection == IntPtr.Zero)
            {
                res = IntPtr.Zero;
                return(ExecStatusType.PGRES_FATAL_ERROR);
            }

            unsafe
            {
                fixed(byte *st = stmt)
                {
                    res = PqsqlWrapper.PQexec(mConnection, st);
                }
            }

            ExecStatusType s = ExecStatusType.PGRES_FATAL_ERROR;

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

            return(s);
        }
예제 #2
0
        // executes SET parameter=value
        internal void SetSessionParameter(string parameter, object value)
        {
            if (mConnection == IntPtr.Zero)
            {
                throw new InvalidOperationException("cannot set session parameter on closed connection");
            }

            if (string.IsNullOrEmpty(parameter))
            {
                throw new ArgumentOutOfRangeException(nameof(parameter), "parameter out of range");
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            StringBuilder sb = new StringBuilder();

            sb.Append("set ");
            sb.Append(parameter);
            sb.Append('=');
            sb.Append(value);

            byte[]         stmt = PqsqlUTF8Statement.CreateUTF8Statement(sb);
            ExecStatusType s    = Exec(stmt);

            if (s != ExecStatusType.PGRES_COMMAND_OK)
            {
                string err = GetErrorMessage();
                string msg = string.Format(CultureInfo.InvariantCulture, "Could not set «{0}» to «{1}»: {2}", parameter, value, err);
                throw new PqsqlException(msg);
            }
        }
예제 #3
0
        // call PQexec and immediately discard PGresult struct
        internal ExecStatusType Exec(byte[] stmt)
        {
            IntPtr         res;
            ExecStatusType s = Exec(stmt, out res);

            Consume(res);
            return(s);
        }
예제 #4
0
        //
        // Summary:
        //     Starts a database transaction with the specified isolation level.
        //
        // Parameters:
        //   isolationLevel:
        //     Specifies the isolation level for the transaction.
        //
        // Returns:
        //     An object representing the new transaction.
        public new PqsqlTransaction BeginTransaction(IsolationLevel isolationLevel)
        {
#if CODECONTRACTS
            Contract.Requires <ArgumentException>(isolationLevel != IsolationLevel.Chaos);
            Contract.Ensures(Contract.Result <PqsqlTransaction>() != null);
#else
            if (isolationLevel == IsolationLevel.Chaos)
            {
                throw new ArgumentException("isolationLevel == IsolationLevel.Chaos");
            }
#endif

            if (mConnection == IntPtr.Zero)
            {
                Open();
            }

            PqsqlTransaction t = null;
            PqsqlTransaction txn;

            try
            {
                t = new PqsqlTransaction(this, isolationLevel);

                // get transaction start command
                byte[] txnString = t.TransactionStart;

                ExecStatusType s = Exec(txnString);

                if (s != ExecStatusType.PGRES_COMMAND_OK)
                {
                    string err = GetErrorMessage();
                    throw new PqsqlException("Transaction start failed: " + err);
                }

                // swap t with txn
                txn = t;
                t   = null;
            }
            finally
            {
                t?.Dispose();                 // only dispose PqsqlTransaction if s != ExecStatusType.PGRES_COMMAND_OK
            }

            return(txn);
        }
예제 #5
0
        //
        // Summary:
        //     Rolls back a transaction from a pending state.
        public override void Rollback()
        {
            ExecStatusType s = SaveTransaction(false);

            switch (s)
            {
            case ExecStatusType.PGRES_COMMAND_OK:
                return;

            case ExecStatusType.PGRES_EMPTY_QUERY:
                throw new PqsqlException("Cannot rollback: connection or transaction is closed");

            default:
                string err = mConn.GetErrorMessage();
                throw new PqsqlException("Could not rollback transaction: " + err);
            }
        }
예제 #6
0
        public void PqsqlConnectionTest1()
        {
            PqsqlConnection connection = new PqsqlConnection(string.Empty);

            Assert.AreEqual(ConnectionState.Closed, connection.State, "wrong connection state");
            Assert.AreEqual(string.Empty, connection.ConnectionString, "wrong connection string");
            Assert.AreEqual(0, connection.ConnectionTimeout, "wrong connection timeout");
            Assert.AreEqual(string.Empty, connection.Database, "wrong connection database");

            connection.ConnectionString = connectionString;

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

            connection.ChangeDatabase("postgres");
            Assert.AreEqual("postgres", connection.Database, "wrong connection database");

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

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

            ExecStatusType status = connection.Exec(null);

            Assert.AreEqual(ExecStatusType.PGRES_FATAL_ERROR, status);

            IntPtr res;

            status = connection.Exec(null, out res);
            Assert.AreEqual(ExecStatusType.PGRES_FATAL_ERROR, status);
            Assert.AreEqual(IntPtr.Zero, res);
            connection.Consume(res);

            connection.Dispose();
            connection.Dispose();
        }
예제 #7
0
 public static string PQresStatusW(ExecStatusType status)
 {
     return(Utf8PtrToString(PQresStatus(status)));
 }
예제 #8
0
 public static extern IntPtr PQresStatus(ExecStatusType status);
예제 #9
0
        public void End()
        {
            IntPtr res;
            string err = string.Empty;

#if CODECONTRACTS
            Contract.Assume(mConn != null);
#endif
            IntPtr conn = mConn.PGConnection;

            if (mColBuf == IntPtr.Zero)
            {
                return;
            }

            int ret = PqsqlBinaryFormat.pqcb_put_end(mColBuf);             // flush column buffer

            if (ret != 1)
            {
                err = err.Insert(0, "Could not send end-of-data indication: ");
                goto bailout;
            }

            res = PqsqlWrapper.PQgetResult(conn);

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

                PqsqlWrapper.PQclear(res);

                if (s == ExecStatusType.PGRES_COPY_IN)
                {
                    // still in COPY_IN mode? bail out!
                    byte[] b = PqsqlUTF8Statement.CreateUTF8Statement("COPY FROM cancelled by client");

                    unsafe
                    {
                        fixed(byte *bs = b)
                        {
                            ret = PqsqlWrapper.PQputCopyEnd(conn, bs);
                        }
                    }

                    res = PqsqlWrapper.PQgetResult(conn);

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

                    err = err.Insert(0, "Cancelled COPY FROM while still in COPY_IN mode (" + s + "," + ret + "): ");

                    goto bailout;
                }

                if (s != ExecStatusType.PGRES_COMMAND_OK)
                {
                    err = err.Insert(0, "COPY FROM failed (" + s + "): ");

                    goto bailout;
                }

                // consume all remaining results until we reach the NULL result
                while ((res = PqsqlWrapper.PQgetResult(conn)) != IntPtr.Zero)
                {
                    // always free mResult
                    PqsqlWrapper.PQclear(res);
                }

                return;
            }

bailout:
            err += mConn.GetErrorMessage();
            throw new PqsqlException(err);
        }
예제 #10
0
        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
        }
예제 #11
0
        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);
        }
예제 #12
0
        public virtual void Start()
        {
#if CODECONTRACTS
            Contract.Requires <InvalidOperationException>(
                !string.IsNullOrWhiteSpace(Table) || !string.IsNullOrWhiteSpace(QueryInternal),
                "Table and Query properties are null.");
#else
            if (string.IsNullOrWhiteSpace(Table) && string.IsNullOrWhiteSpace(QueryInternal))
            {
                throw new InvalidOperationException("Table and Query properties are null.");
            }
#endif

            // PQexec does not set field types in PQresult for COPY FROM statements,
            // just retrieve 0 rows for the field types of Table or Query

            // TODO use PqsqlCopyColumnsCollection for ColumnList
            StringBuilder sb = new StringBuilder();
            if (!SuppressSchemaQueryInternal)
            {
                if (QueryInternal == null)
                {
                    sb.AppendFormat("SELECT {0} FROM {1} LIMIT 0;", (object)ColumnList ?? '*', Table);
                }
                else
                {
                    sb.AppendFormat("{0} LIMIT 0;", QueryInternal);
                }

                // fetch number of columns and store column information
                using (PqsqlCommand cmd = new PqsqlCommand(mConn))
                {
                    cmd.CommandText    = sb.ToString();
                    cmd.CommandType    = CommandType.Text;
                    cmd.CommandTimeout = CopyTimeout;

                    using (PqsqlDataReader r = cmd.ExecuteReader(CommandBehavior.Default))
                    {
                        // just pick current row information
                        PqsqlColInfo[] src = r.RowInformation;

                        if (src == null)
                        {
                            throw new PqsqlException("Cannot retrieve RowInformation for '" +
                                                     QueryInternal ?? Table + "'.");
                        }

                        mColumns = src.Length;
                        mRowInfo = new PqsqlColInfo[mColumns];

                        Array.Copy(src, mRowInfo, mColumns);
                    }
                }

                sb.Clear();
            }

            // now build COPY FROM statement
            sb.Append("COPY ");

            if (QueryInternal == null)
            {
                sb.AppendFormat("{0} ", Table);

                // always create list of columns if not SuppressSchemaQuery
                if (string.IsNullOrEmpty(ColumnList))
                {
                    if (!SuppressSchemaQueryInternal)
                    {
                        // just assume that we use standard table order
                        sb.AppendFormat("({0})", string.Join(",", mRowInfo.Select(r => r.ColumnName)));
                    }
                }
                else
                {
                    // let user decide the column order
                    sb.AppendFormat("({0})", ColumnList);
                }
            }
            else
            {
                sb.AppendFormat("({0})", QueryInternal);
            }

            sb.AppendFormat(" {0} BINARY", CopyStmtDirection);

            byte[] q = PqsqlUTF8Statement.CreateUTF8Statement(sb);

            IntPtr         res;
            ExecStatusType s = mConn.Exec(q, out res);

            // result buffer should contain column information and PGconn should be in COPY_IN state
            if (res == IntPtr.Zero || s != QueryResultType)
            {
                mConn.Consume(res);                 // we might receive several results...
                throw new PqsqlException("Could not execute statement «" + sb + "»: " + mConn.GetErrorMessage());
            }

            // check first column format, current implementation will have all columns set to binary
            if (PqsqlWrapper.PQfformat(res, 0) == 0)
            {
                mConn.Consume(res);
                throw new PqsqlException("PqsqlCopyFrom only supports BINARY format.");
            }

            var nFields = PqsqlWrapper.PQnfields(res);
            if (SuppressSchemaQueryInternal)
            {
                mColumns = nFields;
            }
            else
            {
                // sanity check
                if (mColumns != nFields)
                {
                    mConn.Consume(res);
                    throw new PqsqlException("Received wrong number of columns for " + sb);
                }
            }

            // done with result inspection
            PqsqlWrapper.PQclear(res);
        }
예제 #13
0
        protected string Error()
        {
            IntPtr res;
            string err  = string.Empty;
            IntPtr conn = mConn.PGConnection;

            res = PqsqlWrapper.PQgetResult(conn);

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

                PqsqlWrapper.PQclear(res);

                if (s == ExecStatusType.PGRES_COPY_IN || s == ExecStatusType.PGRES_COPY_OUT)
                {
                    if (s == ExecStatusType.PGRES_COPY_IN)
                    {
                        // still in COPY_IN mode? bail out!
                        byte[] b = PqsqlUTF8Statement.CreateUTF8Statement("COPY cancelled by client");
                        int    end;

                        unsafe
                        {
                            fixed(byte *bs = b)
                            {
                                end = PqsqlWrapper.PQputCopyEnd(conn, bs);
                            }
                        }

                        if (end != 1)
                        {
                            err = err.Insert(0, "Cannot cancel COPY (" + s + "): ");

                            goto bailout;
                        }
                    }

                    res = PqsqlWrapper.PQgetResult(conn);

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

                if (s != ExecStatusType.PGRES_COMMAND_OK)
                {
                    err = err.Insert(0, "COPY failed (" + s + "): ");

                    goto bailout;
                }

                // consume all remaining results until we reach the NULL result
                while ((res = PqsqlWrapper.PQgetResult(conn)) != IntPtr.Zero)
                {
                    // always free mResult
                    PqsqlWrapper.PQclear(res);
                }

                return(err);
            }

bailout:
            err += mConn.GetErrorMessage();

            return(err);
        }