00001
00002
00003
00004
00005
00006
00007 #include "postgres.h"
00008
00009 #include "lib/stringinfo.h"
00010
00011 #include "plpython.h"
00012
00013 #include "plpy_elog.h"
00014
00015 #include "plpy_main.h"
00016 #include "plpy_procedure.h"
00017
00018
00019 PyObject *PLy_exc_error = NULL;
00020 PyObject *PLy_exc_fatal = NULL;
00021 PyObject *PLy_exc_spi_error = NULL;
00022
00023
00024 static void PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth);
00025 static void PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail,
00026 char **hint, char **query, int *position);
00027 static char *get_source_line(const char *src, int lineno);
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 void
00039 PLy_elog(int elevel, const char *fmt,...)
00040 {
00041 char *xmsg;
00042 char *tbmsg;
00043 int tb_depth;
00044 StringInfoData emsg;
00045 PyObject *exc,
00046 *val,
00047 *tb;
00048 const char *primary = NULL;
00049 int sqlerrcode = 0;
00050 char *detail = NULL;
00051 char *hint = NULL;
00052 char *query = NULL;
00053 int position = 0;
00054
00055 PyErr_Fetch(&exc, &val, &tb);
00056 if (exc != NULL)
00057 {
00058 if (PyErr_GivenExceptionMatches(val, PLy_exc_spi_error))
00059 PLy_get_spi_error_data(val, &sqlerrcode, &detail, &hint, &query, &position);
00060 else if (PyErr_GivenExceptionMatches(val, PLy_exc_fatal))
00061 elevel = FATAL;
00062 }
00063 PyErr_Restore(exc, val, tb);
00064
00065 PLy_traceback(&xmsg, &tbmsg, &tb_depth);
00066
00067 if (fmt)
00068 {
00069 initStringInfo(&emsg);
00070 for (;;)
00071 {
00072 va_list ap;
00073 bool success;
00074
00075 va_start(ap, fmt);
00076 success = appendStringInfoVA(&emsg, dgettext(TEXTDOMAIN, fmt), ap);
00077 va_end(ap);
00078 if (success)
00079 break;
00080 enlargeStringInfo(&emsg, emsg.maxlen);
00081 }
00082 primary = emsg.data;
00083
00084
00085 Assert(detail == NULL);
00086
00087
00088 if (xmsg)
00089 detail = xmsg;
00090 }
00091 else
00092 {
00093 if (xmsg)
00094 primary = xmsg;
00095 }
00096
00097 PG_TRY();
00098 {
00099 ereport(elevel,
00100 (errcode(sqlerrcode ? sqlerrcode : ERRCODE_INTERNAL_ERROR),
00101 errmsg_internal("%s", primary ? primary : "no exception data"),
00102 (detail) ? errdetail_internal("%s", detail) : 0,
00103 (tb_depth > 0 && tbmsg) ? errcontext("%s", tbmsg) : 0,
00104 (hint) ? errhint("%s", hint) : 0,
00105 (query) ? internalerrquery(query) : 0,
00106 (position) ? internalerrposition(position) : 0));
00107 }
00108 PG_CATCH();
00109 {
00110 if (fmt)
00111 pfree(emsg.data);
00112 if (xmsg)
00113 pfree(xmsg);
00114 if (tbmsg)
00115 pfree(tbmsg);
00116 PG_RE_THROW();
00117 }
00118 PG_END_TRY();
00119
00120 if (fmt)
00121 pfree(emsg.data);
00122 if (xmsg)
00123 pfree(xmsg);
00124 if (tbmsg)
00125 pfree(tbmsg);
00126 }
00127
00128
00129
00130
00131
00132
00133
00134
00135 static void
00136 PLy_traceback(char **xmsg, char **tbmsg, int *tb_depth)
00137 {
00138 PyObject *e,
00139 *v,
00140 *tb;
00141 PyObject *e_type_o;
00142 PyObject *e_module_o;
00143 char *e_type_s = NULL;
00144 char *e_module_s = NULL;
00145 PyObject *vob = NULL;
00146 char *vstr;
00147 StringInfoData xstr;
00148 StringInfoData tbstr;
00149
00150
00151
00152
00153 PyErr_Fetch(&e, &v, &tb);
00154
00155
00156
00157
00158 if (e == NULL)
00159 {
00160 *xmsg = NULL;
00161 *tbmsg = NULL;
00162 *tb_depth = 0;
00163
00164 return;
00165 }
00166
00167 PyErr_NormalizeException(&e, &v, &tb);
00168
00169
00170
00171
00172
00173 e_type_o = PyObject_GetAttrString(e, "__name__");
00174 e_module_o = PyObject_GetAttrString(e, "__module__");
00175 if (e_type_o)
00176 e_type_s = PyString_AsString(e_type_o);
00177 if (e_type_s)
00178 e_module_s = PyString_AsString(e_module_o);
00179
00180 if (v && ((vob = PyObject_Str(v)) != NULL))
00181 vstr = PyString_AsString(vob);
00182 else
00183 vstr = "unknown";
00184
00185 initStringInfo(&xstr);
00186 if (!e_type_s || !e_module_s)
00187 {
00188 if (PyString_Check(e))
00189
00190 appendStringInfoString(&xstr, PyString_AsString(e));
00191 else
00192
00193 appendStringInfoString(&xstr, "unrecognized exception");
00194 }
00195
00196 else if (strcmp(e_module_s, "builtins") == 0
00197 || strcmp(e_module_s, "__main__") == 0
00198 || strcmp(e_module_s, "exceptions") == 0)
00199 appendStringInfo(&xstr, "%s", e_type_s);
00200 else
00201 appendStringInfo(&xstr, "%s.%s", e_module_s, e_type_s);
00202 appendStringInfo(&xstr, ": %s", vstr);
00203
00204 *xmsg = xstr.data;
00205
00206
00207
00208
00209
00210 *tb_depth = 0;
00211 initStringInfo(&tbstr);
00212
00213 appendStringInfoString(&tbstr, "Traceback (most recent call last):");
00214 while (tb != NULL && tb != Py_None)
00215 {
00216 PyObject *volatile tb_prev = NULL;
00217 PyObject *volatile frame = NULL;
00218 PyObject *volatile code = NULL;
00219 PyObject *volatile name = NULL;
00220 PyObject *volatile lineno = NULL;
00221 PyObject *volatile filename = NULL;
00222
00223 PG_TRY();
00224 {
00225 lineno = PyObject_GetAttrString(tb, "tb_lineno");
00226 if (lineno == NULL)
00227 elog(ERROR, "could not get line number from Python traceback");
00228
00229 frame = PyObject_GetAttrString(tb, "tb_frame");
00230 if (frame == NULL)
00231 elog(ERROR, "could not get frame from Python traceback");
00232
00233 code = PyObject_GetAttrString(frame, "f_code");
00234 if (code == NULL)
00235 elog(ERROR, "could not get code object from Python frame");
00236
00237 name = PyObject_GetAttrString(code, "co_name");
00238 if (name == NULL)
00239 elog(ERROR, "could not get function name from Python code object");
00240
00241 filename = PyObject_GetAttrString(code, "co_filename");
00242 if (filename == NULL)
00243 elog(ERROR, "could not get file name from Python code object");
00244 }
00245 PG_CATCH();
00246 {
00247 Py_XDECREF(frame);
00248 Py_XDECREF(code);
00249 Py_XDECREF(name);
00250 Py_XDECREF(lineno);
00251 Py_XDECREF(filename);
00252 PG_RE_THROW();
00253 }
00254 PG_END_TRY();
00255
00256
00257 if (*tb_depth > 0)
00258 {
00259 PLyExecutionContext *exec_ctx = PLy_current_execution_context();
00260 char *proname;
00261 char *fname;
00262 char *line;
00263 char *plain_filename;
00264 long plain_lineno;
00265
00266
00267
00268
00269
00270 if (*tb_depth == 1)
00271 fname = "<module>";
00272 else
00273 fname = PyString_AsString(name);
00274
00275 proname = PLy_procedure_name(exec_ctx->curr_proc);
00276 plain_filename = PyString_AsString(filename);
00277 plain_lineno = PyInt_AsLong(lineno);
00278
00279 if (proname == NULL)
00280 appendStringInfo(
00281 &tbstr, "\n PL/Python anonymous code block, line %ld, in %s",
00282 plain_lineno - 1, fname);
00283 else
00284 appendStringInfo(
00285 &tbstr, "\n PL/Python function \"%s\", line %ld, in %s",
00286 proname, plain_lineno - 1, fname);
00287
00288
00289
00290
00291
00292 if (exec_ctx->curr_proc && plain_filename != NULL &&
00293 strcmp(plain_filename, "<string>") == 0)
00294 {
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304 line = get_source_line(exec_ctx->curr_proc->src, plain_lineno);
00305 if (line)
00306 {
00307 appendStringInfo(&tbstr, "\n %s", line);
00308 pfree(line);
00309 }
00310 }
00311 }
00312
00313 Py_DECREF(frame);
00314 Py_DECREF(code);
00315 Py_DECREF(name);
00316 Py_DECREF(lineno);
00317 Py_DECREF(filename);
00318
00319
00320 tb_prev = tb;
00321 tb = PyObject_GetAttrString(tb, "tb_next");
00322 Assert(tb_prev != Py_None);
00323 Py_DECREF(tb_prev);
00324 if (tb == NULL)
00325 elog(ERROR, "could not traverse Python traceback");
00326 (*tb_depth)++;
00327 }
00328
00329
00330 *tbmsg = tbstr.data;
00331
00332 Py_XDECREF(e_type_o);
00333 Py_XDECREF(e_module_o);
00334 Py_XDECREF(vob);
00335 Py_XDECREF(v);
00336 Py_DECREF(e);
00337 }
00338
00339
00340
00341
00342 static void
00343 PLy_get_spi_sqlerrcode(PyObject *exc, int *sqlerrcode)
00344 {
00345 PyObject *sqlstate;
00346 char *buffer;
00347
00348 sqlstate = PyObject_GetAttrString(exc, "sqlstate");
00349 if (sqlstate == NULL)
00350 return;
00351
00352 buffer = PyString_AsString(sqlstate);
00353 if (strlen(buffer) == 5 &&
00354 strspn(buffer, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") == 5)
00355 {
00356 *sqlerrcode = MAKE_SQLSTATE(buffer[0], buffer[1], buffer[2],
00357 buffer[3], buffer[4]);
00358 }
00359
00360 Py_DECREF(sqlstate);
00361 }
00362
00363
00364
00365
00366
00367 static void
00368 PLy_get_spi_error_data(PyObject *exc, int *sqlerrcode, char **detail, char **hint, char **query, int *position)
00369 {
00370 PyObject *spidata = NULL;
00371
00372 spidata = PyObject_GetAttrString(exc, "spidata");
00373
00374 if (spidata != NULL)
00375 {
00376 PyArg_ParseTuple(spidata, "izzzi", sqlerrcode, detail, hint, query, position);
00377 }
00378 else
00379 {
00380
00381
00382
00383
00384 PLy_get_spi_sqlerrcode(exc, sqlerrcode);
00385 }
00386
00387 PyErr_Clear();
00388
00389 Py_XDECREF(spidata);
00390 }
00391
00392
00393
00394
00395 static char *
00396 get_source_line(const char *src, int lineno)
00397 {
00398 const char *s = NULL;
00399 const char *next = src;
00400 int current = 0;
00401
00402
00403 if (lineno <= 0)
00404 return NULL;
00405
00406 while (current < lineno)
00407 {
00408 s = next;
00409 next = strchr(s + 1, '\n');
00410 current++;
00411 if (next == NULL)
00412 break;
00413 }
00414
00415 if (current != lineno)
00416 return NULL;
00417
00418 while (*s && isspace((unsigned char) *s))
00419 s++;
00420
00421 if (next == NULL)
00422 return pstrdup(s);
00423
00424
00425
00426
00427
00428
00429 if (next < s)
00430 return NULL;
00431
00432 return pnstrdup(s, next - s);
00433 }
00434
00435
00436
00437 void
00438 PLy_exception_set(PyObject *exc, const char *fmt,...)
00439 {
00440 char buf[1024];
00441 va_list ap;
00442
00443 va_start(ap, fmt);
00444 vsnprintf(buf, sizeof(buf), dgettext(TEXTDOMAIN, fmt), ap);
00445 va_end(ap);
00446
00447 PyErr_SetString(exc, buf);
00448 }
00449
00450
00451 void
00452 PLy_exception_set_plural(PyObject *exc,
00453 const char *fmt_singular, const char *fmt_plural,
00454 unsigned long n,...)
00455 {
00456 char buf[1024];
00457 va_list ap;
00458
00459 va_start(ap, n);
00460 vsnprintf(buf, sizeof(buf),
00461 dngettext(TEXTDOMAIN, fmt_singular, fmt_plural, n),
00462 ap);
00463 va_end(ap);
00464
00465 PyErr_SetString(exc, buf);
00466 }