// postgres error codes are stored as alphanumeric string of // 5 characters. we convert them to an integer of maximum 30 bits; // each character is stored within 6 bits of ErrorCode private static unsafe int CreateErrorCode(IntPtr result) { sbyte *sqlState = PqsqlWrapper.PQresultErrorField(result, PqsqlDiag.PG_DIAG_SQLSTATE); int code = (int)PqsqlState.SUCCESSFUL_COMPLETION; // code=0: error code '00000' means successful_completion if (sqlState != null) { for (int j = 0, c = *sqlState; j < 5 && c != 0; j++, c = *(++sqlState)) { int i = 0; if (c >= 48 && c <= 57) // '0' ... '9' => 0 ... 9 { i = c - 48; } else if (c >= 65 && c <= 90) // 'A' ... 'Z' => 10 ... 35 { i = c - 55; } // store each character in 6 bits from code code |= (i << j * 6); } } return(code); }
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"); }
public PqsqlCopyFrom(PqsqlConnection conn) : base(conn) { mColBuf = IntPtr.Zero; mExpBuf = PqsqlWrapper.createPQExpBuffer(); mPos = 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); }
// write to LO public override void Write(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (!CanWrite) { throw new NotSupportedException("Writing to large object is turned off"); } int blen = buffer.Length; if (offset + count > blen) { throw new ArgumentException("offset or count larger than buffer"); } if (offset < 0) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (mFd < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Cannot write closed large object {0}", mOid)); } int ret = count; unsafe { while (count > 0 && ret > 0) { fixed(byte *b = &buffer[offset]) { ret = PqsqlWrapper.lo_write(mPGConn, mFd, b, (ulong)count); } if (ret > 0) { offset += ret; count -= ret; mPos += ret; } } } if (ret < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not write to large object {0}: {1}", mOid, mConn.GetErrorMessage())); } }
// // Summary: // Enlists in the specified transaction. TODO // // Parameters: // transaction: // A reference to an existing System.Transactions.Transaction in which to enlist. //public override void EnlistTransaction(PqsqlTransaction transaction) //{ // throw new NotImplementedException(); //} // // Summary: // Opens a database connection with the settings specified by the System.Data.Common.DbConnection.ConnectionString. public override void Open() { if (mConnection != IntPtr.Zero && !mNewConnectionString) { // close and open with current connection setting PqsqlWrapper.PQreset(mConnection); // update connection and transaction status if (Status == ConnStatusType.CONNECTION_BAD || TransactionStatus != PGTransactionStatusType.PQTRANS_IDLE) { string err = GetErrorMessage(); PqsqlWrapper.PQfinish(mConnection); // force release of mConnection memory Init(); var connStr = mConnectionStringBuilder.GetConnectionStringWithObfuscatedPassword(); throw new PqsqlException("Could not reset connection with connection string «" + connStr + "»: " + err, (int)PqsqlState.CONNECTION_FAILURE); } OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); // always set application_name, after a DISCARD ALL (usually issued by PqsqlConnectionPool / pgbouncer) // the session information is gone forever, and the shared connection will drop application_name SetApplicationName(); // successfully reestablished connection return; } if (mStatus != ConnStatusType.CONNECTION_BAD) { Close(); // force release of mConnection memory } // check connection pool for a connection mConnection = PqsqlConnectionPool.GetPGConn(mConnectionStringBuilder, out mStatus, out mTransStatus); if (mConnection == IntPtr.Zero) { throw new PqsqlException("libpq: unable to allocate struct PGconn"); } // check connection and transaction status if (mStatus == ConnStatusType.CONNECTION_BAD || mTransStatus != PGTransactionStatusType.PQTRANS_IDLE) { string err = GetErrorMessage(); PqsqlWrapper.PQfinish(mConnection); // force release of mConnection memory Init(); var connStr = mConnectionStringBuilder.GetConnectionStringWithObfuscatedPassword(); throw new PqsqlException("Could not create connection with connection string «" + connStr + "»: " + err); } mNewConnectionString = false; OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); // always set application_name, after a DISCARD ALL (usually issued by PqsqlConnectionPool / pgbouncer) // the session information is gone forever, and the shared connection will drop application_name SetApplicationName(); }
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 unsafe string CreateHint(IntPtr result) { sbyte *hint = PqsqlWrapper.PQresultErrorField(result, PqsqlDiag.PG_DIAG_MESSAGE_HINT); if (hint == null) { return(string.Empty); } return(new string(hint)); }
// truncates LO to value public override void SetLength(long value) { if (value < 0) { throw new ArgumentOutOfRangeException(nameof(value)); } if (mFd < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Cannot truncate closed large object {0}", mOid)); } long pos = PqsqlWrapper.lo_tell64(mPGConn, mFd); if (pos < 0 || mPos != pos) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not tell large object {0}: {1}", mOid, mConn.GetErrorMessage())); } long old = PqsqlWrapper.lo_lseek64(mPGConn, mFd, 0, (int)SeekOrigin.End); if (old < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not seek large object {0}: {1}", mOid, mConn.GetErrorMessage())); } if (old == value) // we are done { goto seekPos; } #if CODECONTRACTS Contract.Assert(value >= 0); #endif int ret = PqsqlWrapper.lo_truncate64(mPGConn, mFd, value); if (ret < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not truncate large object {0} to {1} bytes: {2}", mOid, value, mConn.GetErrorMessage())); } if (value < pos) // lo has been truncated, update position to last byte { pos = value; } seekPos: mPos = PqsqlWrapper.lo_lseek64(mPGConn, mFd, pos, (int)SeekOrigin.Begin); if (mPos != pos) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not seek large object {0}: {1}", mOid, mConn.GetErrorMessage())); } }
public int Unlink(uint oid) { // returns < 0 on error int ret = PqsqlWrapper.lo_unlink(mPGConn, oid); if (ret < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not unlink large object {0}: {1}", oid, mConn.GetErrorMessage())); } return(ret); }
public uint Create() { // returns 0 (InvalidOid) on error uint ret = PqsqlWrapper.lo_create(mPGConn, 0); if (ret == 0) { throw new PqsqlException("Could not create large object: " + mConn.GetErrorMessage()); } return(ret); }
// read from LO public override int Read(byte[] buffer, int offset, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (!CanRead) { throw new NotSupportedException("Reading from large object is turned off"); } int blen = buffer.Length; if (offset + count > blen) { throw new ArgumentException("offset or count larger than buffer"); } if (offset < 0 || offset >= blen) { throw new ArgumentOutOfRangeException(nameof(offset)); } if (count < 0) { throw new ArgumentOutOfRangeException(nameof(count)); } if (mFd < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Cannot read closed large object {0}", mOid)); } int read; unsafe { fixed(byte *b = &buffer[offset]) { read = PqsqlWrapper.lo_read(mPGConn, mFd, b, (ulong)count); } } if (read >= 0) { mPos += read; return(read); } throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not read large object {0}: {1}", mOid, mConn.GetErrorMessage())); }
public override void Close() { if (mColBuf != IntPtr.Zero) { PqsqlBinaryFormat.pqcb_free(mColBuf); mColBuf = IntPtr.Zero; } if (mExpBuf != IntPtr.Zero) { PqsqlWrapper.destroyPQExpBuffer(mExpBuf); mExpBuf = IntPtr.Zero; } }
// returns current position of mExpBuf private long LengthCheckReset() { long len = PqsqlBinaryFormat.pqbf_get_buflen(mExpBuf); if (len > 4096) { // if we exceed 4k write-boundary, we reset the buffer and // start to write from the beginning again PqsqlWrapper.resetPQExpBuffer(mExpBuf); len = 0; } return(len); }
public override void Close() { if (mFd < 0) { return; } if (PqsqlWrapper.lo_close(mPGConn, mFd) == -1) { // ignore error } mFd = -1; mMode = 0; mPos = -1; }
// sets new position in the LO public override long Seek(long offset, SeekOrigin origin) { if (mFd < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Cannot seek closed large object {0}", mOid)); } mPos = PqsqlWrapper.lo_lseek64(mPGConn, mFd, offset, (int)origin); if (mPos < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Could not seek large object {0}: {1}", mOid, mConn.GetErrorMessage())); } return(mPos); }
public static void ReleasePGConn(PqsqlConnectionStringBuilder connStringBuilder, IntPtr pgConnHandle) { #if CODECONTRACTS Contract.Requires <ArgumentNullException>(connStringBuilder != null); #else if (connStringBuilder == null) { throw new ArgumentNullException(nameof(connStringBuilder)); } #endif if (pgConnHandle == IntPtr.Zero) { return; } Queue <ConnectionInfo> queue; lock (mPooledConnsLock) { mPooledConns.TryGetValue(connStringBuilder, out queue); } bool closeConnection = true; if (queue == null || !DiscardConnection(pgConnHandle)) { goto close; // just cleanup connection and restart timer } lock (queue) { if (queue.Count < MaxQueue) { queue.Enqueue(new ConnectionInfo { pgconn = pgConnHandle, visited = 0 }); closeConnection = false; // keep connection } } close: if (closeConnection) { PqsqlWrapper.PQfinish(pgConnHandle); // close connection and release memory } }
// consume remaining results from connection internal void Consume(IntPtr res) { if (mConnection == IntPtr.Zero) { return; } if (res != IntPtr.Zero) { PqsqlWrapper.PQclear(res); } // consume all remaining results until we reach the NULL result while ((res = PqsqlWrapper.PQgetResult(mConnection)) != IntPtr.Zero) { // always free mResult PqsqlWrapper.PQclear(res); } }
// see https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS private string GetParameterStatus(byte[] param) { if (mConnection == IntPtr.Zero || param == null) { return(string.Empty); } unsafe { sbyte *parameterStatus = PqsqlWrapper.PQparameterStatus(mConnection, param); if (parameterStatus == null) { return(string.Empty); } return(new string(parameterStatus)); // TODO UTF-8 encoding ignored here! } }
// Summary: // Attempts to cancels the execution of a System.Data.Common.DbCommand. public override void Cancel() { if (mConn == null) { return; } ConnectionState s = mConn.State; // no cancel possible/necessary if connection is closed / open / connecting / broken if (s == ConnectionState.Closed || s == ConnectionState.Open || (s & (ConnectionState.Broken | ConnectionState.Connecting)) > 0) { return; } IntPtr cancel = PqsqlWrapper.PQgetCancel(mConn.PGConnection); if (cancel != IntPtr.Zero) { sbyte[] buf = new sbyte[256]; string err; unsafe { fixed(sbyte *b = buf) { int cret = PqsqlWrapper.PQcancel(cancel, b, 256); PqsqlWrapper.PQfreeCancel(cancel); if (cret == 1) { return; } err = PqsqlUTF8Statement.CreateStringFromUTF8(new IntPtr(b)); } } throw new PqsqlException("Could not cancel command «" + mCmdText + "»: " + err); } }
public int Open(uint oid, LoOpen mode) { #if CODECONTRACTS Contract.Requires <ArgumentException>(oid != 0, "Cannot open large object with InvalidOid (0)"); #else if (oid == 0) { throw new ArgumentException("Cannot open large object with InvalidOid (0)"); } #endif mFd = PqsqlWrapper.lo_open(mPGConn, oid, (int)mode); if (mFd < 0) { throw new PqsqlException(string.Format(CultureInfo.InvariantCulture, "Cannot open large object {0}: {1}", oid, mConn.GetErrorMessage())); } mOid = oid; mMode = mode; mPos = 0; return(mFd); }
// return current error message internal string GetErrorMessage() { #if CODECONTRACTS Contract.Ensures(Contract.Result <string>() != null); #endif string msg = string.Empty; if (mConnection != IntPtr.Zero) { unsafe { sbyte *b = PqsqlWrapper.PQerrorMessage(mConnection); if (b != null) { msg = PqsqlUTF8Statement.CreateStringFromUTF8(new IntPtr(b)); } } } return(msg); }
public void PqsqlConnectionTest10() { PqsqlConnectionPool.Clear(); PqsqlConnection connection = new PqsqlConnection(connectionString); connection.Open(); Assert.AreEqual(ConnectionState.Open, connection.State, "wrong connection state"); IntPtr pgConn = connection.PGConnection; int client_encoding = PqsqlWrapper.PQclientEncoding(pgConn); Assert.AreEqual((int)PgEnc.PG_UTF8, client_encoding, "wrong client_encoding"); // setup client_encoding to sqlascii, and leave it like that byte[] encoding = PqsqlUTF8Statement.CreateUTF8Statement("sqlascii"); client_encoding = PqsqlWrapper.PQsetClientEncoding(pgConn, encoding); Assert.AreEqual(0, client_encoding, "could not set client_encoding"); client_encoding = PqsqlWrapper.PQclientEncoding(pgConn); Assert.AreEqual(0, client_encoding, "wrong client_encoding"); connection.Close(); // now try to get the same connection again connection = new PqsqlConnection(connectionString); connection.Open(); Assert.AreEqual(ConnectionState.Open, connection.State, "wrong connection state"); pgConn = connection.PGConnection; client_encoding = PqsqlWrapper.PQclientEncoding(pgConn); Assert.AreEqual((int)PgEnc.PG_UTF8, client_encoding, "wrong client_encoding"); }
private static void PoolService(object o) { #if CODECONTRACTS Contract.Requires <ArgumentNullException>(o != null); #else if (o == null) { throw new ArgumentNullException(nameof(o)); } #endif List <IntPtr> closeConnections = o as List <IntPtr>; // we assume that we run PoolService in less than IdleTimeout msecs lock (mPooledConnsLock) { #if PQSQL_DEBUG mLogger.Debug("Running PoolService"); #endif using (Dictionary <PqsqlConnectionStringBuilder, Queue <ConnectionInfo> > .Enumerator e = mPooledConns.GetEnumerator()) { while (e.MoveNext()) { KeyValuePair <PqsqlConnectionStringBuilder, Queue <ConnectionInfo> > item = e.Current; #if PQSQL_DEBUG PqsqlConnectionStringBuilder csb = item.Key; #endif Queue <ConnectionInfo> queue = item.Value; lock (queue) { int count = queue.Count; #if PQSQL_DEBUG mLogger.DebugFormat("ConnectionPool {0}: {1} waiting connections", csb.ConnectionString, count); #endif if (count == 0) { continue; } int maxRelease = count / 2 + 1; ConnectionInfo i = queue.Peek(); #if CODECONTRACTS Contract.Assume(i != null); #endif i.visited++; #if PQSQL_DEBUG if (i.visited <= VisitedThreshold) { mLogger.DebugFormat("ConnectionPool {0}: {1} visits", csb.ConnectionString, i.visited); } #endif if (i.visited > VisitedThreshold) { #if PQSQL_DEBUG mLogger.DebugFormat("ConnectionPool {0}: visit threshold {1} reached, releasing {2} connections", csb.ConnectionString, i.visited, maxRelease); #endif while (maxRelease > 0) { // clean maxRelease connections i = queue.Dequeue(); #if CODECONTRACTS Contract.Assume(i != null); #endif closeConnections.Add(i.pgconn); // close connections outside of queue lock maxRelease--; } } } } } } // now close old connections foreach (IntPtr conn in closeConnections) { PqsqlWrapper.PQfinish(conn); // close connection and release memory } closeConnections.Clear(); }
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); }
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); }
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); }
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); }
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 }