// 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); } }
// sets val as string with Oid oid (PqsqlDbType.BPChar, PqsqlDbType.Text, PqsqlDbType.Varchar, PqsqlDbType.Name, PqsqlDbType.Char) // into pqparam_buffer pb internal static unsafe void SetText(IntPtr pb, object val, PqsqlDbType oid) { #if !WIN32 PqsqlUTF8Statement.AddText(pb, (string)val, (uint)oid); #else fixed(char *t = (string)val) { PqsqlBinaryFormat.pqbf_add_unicode_text(pb, t, (uint)oid); } #endif }
// 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 WriteText(string value) { long begin = LengthCheckReset(); unsafe { #if !WIN32 PqsqlUTF8Statement.SetText(mExpBuf, value); #else fixed(char *sp = value) { PqsqlBinaryFormat.pqbf_set_unicode_text(mExpBuf, sp); } #endif long end = PqsqlBinaryFormat.pqbf_get_buflen(mExpBuf); long len = end - begin; sbyte *val = PqsqlBinaryFormat.pqbf_get_bufval(mExpBuf) + begin; return(PutColumn(val, (uint)len)); } }
// 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); }
// adds o as string array element to PQExpBuffer a internal static void SetTextArray(IntPtr a, object o) { string v = (string)o; long len0 = PqsqlBinaryFormat.pqbf_get_buflen(a); // get start position PqsqlBinaryFormat.pqbf_set_array_itemlength(a, -2); // first set an invalid item length #if !WIN32 PqsqlUTF8Statement.SetText(a, v); #else unsafe { fixed(char *t = v) { PqsqlBinaryFormat.pqbf_set_unicode_text(a, t); // encode text value (variable length) } } #endif int len = (int)(PqsqlBinaryFormat.pqbf_get_buflen(a) - len0); // get new buffer length // update array item length == len - 4 bytes PqsqlBinaryFormat.pqbf_update_array_itemlength(a, -len, len - 4); }
/// <summary> /// split PqsqlCommand.CommandText into an array of sub-statements /// </summary> /// <returns></returns> private IEnumerable <string> ParseStatements() { #if CODECONTRACTS Contract.Ensures(Contract.Result <IEnumerable <string> >() != null); #endif IntPtr pstate = IntPtr.Zero; IntPtr varArray = IntPtr.Zero; try { unsafe { int offset = 0; varArray = Marshal.AllocHGlobal((mParams.Count + 1) * sizeof(sbyte *)); // always write NULL before we continue, so finally clause can clean up properly // if we get hit by an exception Marshal.WriteIntPtr(varArray, offset, IntPtr.Zero); foreach (PqsqlParameter param in mParams) { // always write NULL before we continue, so finally clause can clean up properly // if we get hit by an exception Marshal.WriteIntPtr(varArray, offset, IntPtr.Zero); string psqlParamName = param.PsqlParameterName; // psql-specific: characters allowed in variable names: [A-Za-z\200-\377_0-9] // we only allow lowercase [a-z0-9_], as PsqlParameter always stores parameter names in lowercase char invalid = psqlParamName.FirstOrDefault(c => !(c >= 'a' && c <= 'z') && !char.IsDigit(c) && c != '_'); if (invalid != default(char)) { string msg = string.Format(CultureInfo.InvariantCulture, "Parameter name «{0}» contains invalid character «{1}»", psqlParamName, invalid); throw new PqsqlException(msg, (int)PqsqlState.SYNTAX_ERROR); } // variable names are pure ascii byte[] paramNameArray = Encoding.ASCII.GetBytes(psqlParamName); int len = paramNameArray.Length; // we need a null-terminated variable string IntPtr varString = Marshal.AllocHGlobal(len + 1); Marshal.Copy(paramNameArray, 0, varString, len); Marshal.WriteByte(varString, len, 0); Marshal.WriteIntPtr(varArray, offset, varString); offset += sizeof(sbyte *); } Marshal.WriteIntPtr(varArray, offset, IntPtr.Zero); } // varArray pointers must be valid during parsing pstate = PqsqlBinaryFormat.pqparse_init(varArray); // always terminate CommandText with a ; (prevents unnecessary re-parsing) // we have the following cases: // 1) "select 1; select 2" // 2) "select 1; select 2 -- dash-dash comment forces newline for semicolon" // 3) "select 1; select 2; /* slash-star comment forces unnecessary semicolon */" // 4) "select 1; select 2 -- dash-dash comment triggers ;" // // For (1), (2), (3) we simply add a newline + semicolon. Case (4) is more tricky // and requires to re-start the parser for another round. string commands = CommandText.TrimEnd(); if (!commands.EndsWith(";", StringComparison.Ordinal)) { commands += "\n;"; } byte[] statementsString = PqsqlUTF8Statement.CreateUTF8Statement(commands); // add a semicolon-separated list of UTF-8 statements int parsingState = PqsqlBinaryFormat.pqparse_add_statements(pstate, statementsString); if (parsingState == -1) // syntax error or missing parameter { ParsingError(pstate); } else if (parsingState == 1) // incomplete input, continue with current parsing state and force final "\n;" { statementsString = PqsqlUTF8Statement.CreateUTF8Statement("\n;"); if (PqsqlBinaryFormat.pqparse_add_statements(pstate, statementsString) != 0) { ParsingError(pstate); // syntax error / missing parameter / incomplete input } } uint num = PqsqlBinaryFormat.pqparse_num_statements(pstate); string[] statements = new string[num]; // the null-terminated array of UTF-8 statement strings IntPtr sptr = PqsqlBinaryFormat.pqparse_get_statements(pstate); if (num > 0 && sptr != IntPtr.Zero) { unsafe { for (int i = 0; i < num; i++) { sbyte **stm = (sbyte **)sptr.ToPointer(); if (stm == null || *stm == null) { break; } // convert UTF-8 to UTF-16 statements[i] = PqsqlUTF8Statement.CreateStringFromUTF8(new IntPtr(*stm)); sptr = IntPtr.Add(sptr, sizeof(sbyte *)); } } } #if CODECONTRACTS Contract.Assert(statements != null); Contract.Assert(statements.Length == num); #endif return(from statement in statements where !string.IsNullOrWhiteSpace(statement) && statement != ";" select statement); } finally { if (pstate != IntPtr.Zero) { PqsqlBinaryFormat.pqparse_destroy(pstate); } if (varArray != IntPtr.Zero) { unsafe { for (int i = mParams.Count - 1; i >= 0; i--) { IntPtr varPtr = Marshal.ReadIntPtr(varArray, i * sizeof(sbyte *)); if (varPtr != IntPtr.Zero) { Marshal.FreeHGlobal(varPtr); } } } Marshal.FreeHGlobal(varArray); } } }
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); }
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); }
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); }