/* * Adjust a syntax error occurring inside the function body of a CREATE * FUNCTION command. This can be used by any function validator, not only * for SQL-language functions. It is assumed that the syntax error position * is initially relative to the function body string (as passed in). If * possible, we adjust the position to reference the original CREATE command; * if we can't manage that, we set up an "internal query" syntax error instead. * * Returns true if a syntax error was processed, false if not. */ bool function_parse_error_transpose(const char *prosrc) { int origerrposition; int newerrposition; const char *queryText; /* * Nothing to do unless we are dealing with a syntax error that has a * cursor position. * * Some PLs may prefer to report the error position as an internal error * to begin with, so check that too. */ origerrposition = geterrposition(); if (origerrposition <= 0) { origerrposition = getinternalerrposition(); if (origerrposition <= 0) return false; } /* We can get the original query text from the active portal (hack...) */ Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE); queryText = ActivePortal->sourceText; /* Try to locate the prosrc in the original text */ newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition); if (newerrposition > 0) { /* Successful, so fix error position to reference original query */ errposition(newerrposition); /* Get rid of any report of the error as an "internal query" */ internalerrposition(0); internalerrquery(NULL); } else { /* * If unsuccessful, convert the position to an internal position * marker and give the function text as the internal query. */ errposition(0); internalerrposition(origerrposition); internalerrquery(prosrc); } return true; }
/* * Pass remote error/notice/warning through. */ void plproxy_remote_error(ProxyFunction *func, ProxyConnection *conn, const PGresult *res, bool iserr) { const char *ss = PQresultErrorField(res, PG_DIAG_SQLSTATE); const char *sev = PQresultErrorField(res, PG_DIAG_SEVERITY); const char *msg = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY); const char *det = PQresultErrorField(res, PG_DIAG_MESSAGE_DETAIL); const char *hint = PQresultErrorField(res, PG_DIAG_MESSAGE_HINT); const char *spos = PQresultErrorField(res, PG_DIAG_STATEMENT_POSITION); const char *ipos = PQresultErrorField(res, PG_DIAG_INTERNAL_POSITION); const char *iquery = PQresultErrorField(res, PG_DIAG_INTERNAL_QUERY); const char *ctx = PQresultErrorField(res, PG_DIAG_CONTEXT); int elevel; /* libpq errors may not have sqlstate */ if (!ss) ss = "XX000"; if (iserr) /* must ignore remote level, as it may be FATAL/PANIC */ elevel = ERROR; else /* cannot look at sev here, as it may be localized */ elevel = !strncmp(ss, "00", 2) ? NOTICE : WARNING; ereport(elevel, ( errcode(MAKE_SQLSTATE(ss[0], ss[1], ss[2], ss[3], ss[4])), errmsg("%s(%d): [%s] REMOTE %s: %s", func->name, func->arg_count, PQdb(conn->cur->db), sev, msg), det ? errdetail("Remote detail: %s", det) : 0, hint ? errhint("Remote hint: %s", hint) : 0, spos ? errposition(atoi(spos)) : 0, ipos ? internalerrposition(atoi(ipos)) : 0, iquery ? internalerrquery(iquery) : 0, ctx ? errcontext("Remote context: %s", ctx) : 0)); }
/* * plpgsql_scanner_errposition * Report an error cursor position, if possible. * * This is expected to be used within an ereport() call. The return value * is a dummy (always 0, in fact). * * Note that this can only be used for messages emitted during initial * parsing of a plpgsql function, since it requires the scanorig string * to still be available. */ int plpgsql_scanner_errposition(int location) { int pos; if (location < 0 || scanorig == NULL) return 0; /* no-op if location is unknown */ /* Convert byte offset to character number */ pos = pg_mbstrlen_with_len(scanorig, location) + 1; /* And pass it to the ereport mechanism */ (void) internalerrposition(pos); /* Also pass the function body string */ return internalerrquery(scanorig); }
/* * A function having everything to do with logging, which ought to be factored * out one day to make a start on the Thoughts-on-logging wiki ideas. */ static void reLogWithChangedLevel(int level) { ErrorData *edata = CopyErrorData(); int sqlstate = edata->sqlerrcode; int category = ERRCODE_TO_CATEGORY(sqlstate); FlushErrorState(); if ( WARNING > level ) { if ( ERRCODE_SUCCESSFUL_COMPLETION != category ) sqlstate = ERRCODE_SUCCESSFUL_COMPLETION; } else if ( WARNING == level ) { if ( ERRCODE_WARNING != category && ERRCODE_NO_DATA != category ) sqlstate = ERRCODE_WARNING; } else if ( ERRCODE_WARNING == category || ERRCODE_NO_DATA == category || ERRCODE_SUCCESSFUL_COMPLETION == category ) sqlstate = ERRCODE_INTERNAL_ERROR; #if PG_VERSION_NUM >= 90500 edata->elevel = level; edata->sqlerrcode = sqlstate; PG_TRY(); { ThrowErrorData(edata); } PG_CATCH(); { FreeErrorData(edata); /* otherwise this wouldn't happen in ERROR case */ PG_RE_THROW(); } PG_END_TRY(); FreeErrorData(edata); #else if (!errstart(level, edata->filename, edata->lineno, edata->funcname, NULL)) { FreeErrorData(edata); return; } errcode(sqlstate); if (edata->message) errmsg("%s", edata->message); if (edata->detail) errdetail("%s", edata->detail); if (edata->detail_log) errdetail_log("%s", edata->detail_log); if (edata->hint) errhint("%s", edata->hint); if (edata->context) errcontext("%s", edata->context); /* this may need to be trimmed */ #if PG_VERSION_NUM >= 90300 if (edata->schema_name) err_generic_string(PG_DIAG_SCHEMA_NAME, edata->schema_name); if (edata->table_name) err_generic_string(PG_DIAG_TABLE_NAME, edata->table_name); if (edata->column_name) err_generic_string(PG_DIAG_COLUMN_NAME, edata->column_name); if (edata->datatype_name) err_generic_string(PG_DIAG_DATATYPE_NAME, edata->datatype_name); if (edata->constraint_name) err_generic_string(PG_DIAG_CONSTRAINT_NAME, edata->constraint_name); #endif if (edata->internalquery) internalerrquery(edata->internalquery); FreeErrorData(edata); errfinish(0); #endif }
/* * Emit a PG error or notice, together with any available info about * the current Python error, previously set by PLy_exception_set(). * This should be used to propagate Python errors into PG. If fmt is * NULL, the Python error becomes the primary error message, otherwise * it becomes the detail. If there is a Python traceback, it is put * in the context. */ void PLy_elog(int elevel, const char *fmt,...) { char *xmsg; char *tbmsg; int tb_depth; StringInfoData emsg; PyObject *exc, *val, *tb; const char *primary = NULL; int sqlerrcode = 0; char *detail = NULL; char *hint = NULL; char *query = NULL; int position = 0; char *schema_name = NULL; char *table_name = NULL; char *column_name = NULL; char *datatype_name = NULL; char *constraint_name = NULL; PyErr_Fetch(&exc, &val, &tb); if (exc != NULL) { PyErr_NormalizeException(&exc, &val, &tb); if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error)) PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position, &schema_name, &table_name, &column_name, &datatype_name, &constraint_name); else if (PyErr_GivenExceptionMatches(val, PLy_exc_error)) PLy_get_error_data(val, &sqlerrcode, &detail, &hint, &schema_name, &table_name, &column_name, &datatype_name, &constraint_name); else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal)) elevel = FATAL; } /* this releases our refcount on tb! */ PLy_traceback(exc, val, tb, &xmsg, &tbmsg, &tb_depth); if (fmt) { initStringInfo(&emsg); for (;;) { va_list ap; int needed; va_start(ap, fmt); needed = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap); va_end(ap); if (needed == 0) break; enlargeStringInfo(&emsg, needed); } primary = emsg.data; /* Since we have a format string, we cannot have a SPI detail. */ Assert(detail == NULL); /* If there's an exception message, it goes in the detail. */ if (xmsg) detail = xmsg; } else { if (xmsg) primary = xmsg; } PG_TRY(); { ereport(elevel, (errcode(sqlerrcode ? sqlerrcode : ERRCODE_EXTERNAL_ROUTINE_EXCEPTION), errmsg_internal("%s", primary ? primary : "no exception data"), (detail) ? errdetail_internal("%s", detail) : 0, (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0, (hint) ? errhint("%s", hint) : 0, (query) ? internalerrquery(query) : 0, (position) ? internalerrposition(position) : 0, (schema_name) ? err_generic_string(PG_DIAG_SCHEMA_NAME, schema_name) : 0, (table_name) ? err_generic_string(PG_DIAG_TABLE_NAME, table_name) : 0, (column_name) ? err_generic_string(PG_DIAG_COLUMN_NAME, column_name) : 0, (datatype_name) ? err_generic_string(PG_DIAG_DATATYPE_NAME, datatype_name) : 0, (constraint_name) ? err_generic_string(PG_DIAG_CONSTRAINT_NAME, constraint_name) : 0)); } PG_CATCH(); { if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); Py_XDECREF(exc); Py_XDECREF(val); PG_RE_THROW(); } PG_END_TRY(); if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); Py_XDECREF(exc); Py_XDECREF(val); }
/* * Emit a PG error or notice, together with any available info about * the current Python error, previously set by PLy_exception_set(). * This should be used to propagate Python errors into PG. If fmt is * NULL, the Python error becomes the primary error message, otherwise * it becomes the detail. If there is a Python traceback, it is put * in the context. */ void PLy_elog(int elevel, const char *fmt,...) { char *xmsg; char *tbmsg; int tb_depth; StringInfoData emsg; PyObject *exc, *val, *tb; const char *primary = NULL; int sqlerrcode = 0; char *detail = NULL; char *hint = NULL; char *query = NULL; int position = 0; PyErr_Fetch(&exc, &val, &tb); if (exc != NULL) { if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error)) PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position); else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal)) elevel = FATAL; } PyErr_Restore(exc, val, tb); PLy_traceback(&xmsg, &tbmsg, &tb_depth); if (fmt) { initStringInfo(&emsg); for (;;) { va_list ap; bool success; va_start(ap, fmt); success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap); va_end(ap); if (success) break; enlargeStringInfo(&emsg, emsg.maxlen); } primary = emsg.data; /* Since we have a format string, we cannot have a SPI detail. */ Assert(detail == NULL); /* If there's an exception message, it goes in the detail. */ if (xmsg) detail = xmsg; } else { if (xmsg) primary = xmsg; } PG_TRY(); { ereport(elevel, (errcode(sqlerrcode ? sqlerrcode : ERRCODE_INTERNAL_ERROR), errmsg_internal("%s", primary ? primary : "no exception data"), (detail) ? errdetail_internal("%s", detail) : 0, (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0, (hint) ? errhint("%s", hint) : 0, (query) ? internalerrquery(query) : 0, (position) ? internalerrposition(position) : 0)); } PG_CATCH(); { if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); PG_RE_THROW(); } PG_END_TRY(); if (fmt) pfree(emsg.data); if (xmsg) pfree(xmsg); if (tbmsg) pfree(tbmsg); }