mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 17:06:33 +03:00
Merge branch 'diagnostics' into devel
This commit is contained in:
commit
73949cd1b8
|
@ -165,9 +165,9 @@ functionalities defined by the |DBAPI|_.
|
||||||
table_name
|
table_name
|
||||||
|
|
||||||
A string with the error field if available; `!None` if not available.
|
A string with the error field if available; `!None` if not available.
|
||||||
The attribute value is available only if the error sent by the server
|
The attribute value is available only if the error sent by the server:
|
||||||
includes the specified field and should remain available until the
|
not all the fields are available for all the errors and for all the
|
||||||
cursor that generated the exception executes another query.
|
server versions.
|
||||||
|
|
||||||
|
|
||||||
.. autofunction:: set_wait_callback(f)
|
.. autofunction:: set_wait_callback(f)
|
||||||
|
|
|
@ -118,7 +118,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
|
||||||
if (kind == 2) {
|
if (kind == 2) {
|
||||||
Py_XDECREF(n);
|
Py_XDECREF(n);
|
||||||
psyco_set_error(ProgrammingError, curs,
|
psyco_set_error(ProgrammingError, curs,
|
||||||
"argument formats can't be mixed", NULL, NULL);
|
"argument formats can't be mixed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
kind = 1;
|
kind = 1;
|
||||||
|
@ -190,7 +190,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
|
||||||
/* we found %( but not a ) */
|
/* we found %( but not a ) */
|
||||||
Py_XDECREF(n);
|
Py_XDECREF(n);
|
||||||
psyco_set_error(ProgrammingError, curs,
|
psyco_set_error(ProgrammingError, curs,
|
||||||
"incomplete placeholder: '%(' without ')'", NULL, NULL);
|
"incomplete placeholder: '%(' without ')'");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
c = d + 1; /* after the ) */
|
c = d + 1; /* after the ) */
|
||||||
|
@ -205,7 +205,7 @@ _mogrify(PyObject *var, PyObject *fmt, cursorObject *curs, PyObject **new)
|
||||||
if (kind == 1) {
|
if (kind == 1) {
|
||||||
Py_XDECREF(n);
|
Py_XDECREF(n);
|
||||||
psyco_set_error(ProgrammingError, curs,
|
psyco_set_error(ProgrammingError, curs,
|
||||||
"argument formats can't be mixed", NULL, NULL);
|
"argument formats can't be mixed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
kind = 2;
|
kind = 2;
|
||||||
|
@ -267,7 +267,7 @@ static PyObject *_psyco_curs_validate_sql_basic(
|
||||||
|
|
||||||
if (!sql || !PyObject_IsTrue(sql)) {
|
if (!sql || !PyObject_IsTrue(sql)) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"can't execute an empty query", NULL, NULL);
|
"can't execute an empty query");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -338,8 +338,7 @@ _psyco_curs_merge_query_args(cursorObject *self,
|
||||||
if (!strcmp(s, "not enough arguments for format string")
|
if (!strcmp(s, "not enough arguments for format string")
|
||||||
|| !strcmp(s, "not all arguments converted")) {
|
|| !strcmp(s, "not all arguments converted")) {
|
||||||
Dprintf("psyco_curs_execute: -> got a match");
|
Dprintf("psyco_curs_execute: -> got a match");
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self, s);
|
||||||
s, NULL, NULL);
|
|
||||||
pe = 1;
|
pe = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,13 +481,12 @@ psyco_curs_execute(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
if (self->name != NULL) {
|
if (self->name != NULL) {
|
||||||
if (self->query != Py_None) {
|
if (self->query != Py_None) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"can't call .execute() on named cursors more than once",
|
"can't call .execute() on named cursors more than once");
|
||||||
NULL, NULL);
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (self->conn->autocommit) {
|
if (self->conn->autocommit) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"can't use a named cursor outside of transactions", NULL, NULL);
|
"can't use a named cursor outside of transactions");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
EXC_IF_NO_MARK(self);
|
EXC_IF_NO_MARK(self);
|
||||||
|
@ -533,7 +531,7 @@ psyco_curs_executemany(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
|
||||||
if (self->name != NULL) {
|
if (self->name != NULL) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"can't call .executemany() on named cursors", NULL, NULL);
|
"can't call .executemany() on named cursors");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1038,7 +1036,7 @@ psyco_curs_callproc(cursorObject *self, PyObject *args)
|
||||||
|
|
||||||
if (self->name != NULL) {
|
if (self->name != NULL) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"can't call .callproc() on named cursors", NULL, NULL);
|
"can't call .callproc() on named cursors");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1165,13 +1163,13 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
newpos = value;
|
newpos = value;
|
||||||
} else {
|
} else {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"scroll mode must be 'relative' or 'absolute'", NULL, NULL);
|
"scroll mode must be 'relative' or 'absolute'");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newpos < 0 || newpos >= self->rowcount ) {
|
if (newpos < 0 || newpos >= self->rowcount ) {
|
||||||
psyco_set_error(ProgrammingError, self,
|
psyco_set_error(ProgrammingError, self,
|
||||||
"scroll destination out of bounds", NULL, NULL);
|
"scroll destination out of bounds");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,12 +26,14 @@
|
||||||
#ifndef PSYCOPG_DIAGNOSTICS_H
|
#ifndef PSYCOPG_DIAGNOSTICS_H
|
||||||
#define PSYCOPG_DIAGNOSTICS_H 1
|
#define PSYCOPG_DIAGNOSTICS_H 1
|
||||||
|
|
||||||
|
#include "psycopg/error.h"
|
||||||
|
|
||||||
extern HIDDEN PyTypeObject diagnosticsType;
|
extern HIDDEN PyTypeObject diagnosticsType;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
PyObject_HEAD
|
PyObject_HEAD
|
||||||
|
|
||||||
PyObject *err; /* exception to retrieve the diagnostics from */
|
errorObject *err; /* exception to retrieve the diagnostics from */
|
||||||
|
|
||||||
} diagnosticsObject;
|
} diagnosticsObject;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
#include "psycopg/psycopg.h"
|
#include "psycopg/psycopg.h"
|
||||||
|
|
||||||
#include "psycopg/diagnostics.h"
|
#include "psycopg/diagnostics.h"
|
||||||
#include "psycopg/cursor.h"
|
#include "psycopg/error.h"
|
||||||
|
|
||||||
/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
|
/* These are new in PostgreSQL 9.3. Defining them here so that psycopg2 can
|
||||||
* use them with a 9.3+ server even if compiled against pre-9.3 headers. */
|
* use them with a 9.3+ server even if compiled against pre-9.3 headers. */
|
||||||
|
@ -55,27 +55,15 @@
|
||||||
static PyObject *
|
static PyObject *
|
||||||
psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
|
psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
|
||||||
{
|
{
|
||||||
// closure contains the field code.
|
|
||||||
PyObject *rv = NULL;
|
|
||||||
PyObject *curs = NULL;
|
|
||||||
const char *errortext;
|
const char *errortext;
|
||||||
if (!(curs = PyObject_GetAttrString(self->err, "cursor")) ||
|
|
||||||
!PyObject_TypeCheck(curs, &cursorType) ||
|
if (!self->err->pgres) {
|
||||||
((cursorObject *)curs)->pgres == NULL) {
|
Py_INCREF(Py_None);
|
||||||
goto exit;
|
return Py_None;
|
||||||
}
|
}
|
||||||
errortext = PQresultErrorField(
|
|
||||||
((cursorObject *)curs)->pgres, (Py_intptr_t) closure);
|
errortext = PQresultErrorField(self->err->pgres, (Py_intptr_t) closure);
|
||||||
if (errortext) {
|
return error_text_from_chars(self->err, errortext);
|
||||||
rv = conn_text_from_chars(((cursorObject *)curs)->conn, errortext);
|
|
||||||
}
|
|
||||||
exit:
|
|
||||||
if (!rv) {
|
|
||||||
rv = Py_None;
|
|
||||||
Py_INCREF(rv);
|
|
||||||
}
|
|
||||||
Py_XDECREF(curs);
|
|
||||||
return rv;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -134,8 +122,14 @@ diagnostics_init(diagnosticsObject *self, PyObject *args, PyObject *kwds)
|
||||||
if (!PyArg_ParseTuple(args, "O", &err))
|
if (!PyArg_ParseTuple(args, "O", &err))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if (!PyObject_TypeCheck(err, &errorType)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError,
|
||||||
|
"The argument must be a psycopg2.Error");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
Py_INCREF(err);
|
Py_INCREF(err);
|
||||||
self->err = err;
|
self->err = (errorObject *)err;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
psycopg/error.h
Normal file
43
psycopg/error.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/* error.h - definition for the psycopg base Error type
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of psycopg.
|
||||||
|
*
|
||||||
|
* psycopg2 is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give
|
||||||
|
* permission to link this program with the OpenSSL library (or with
|
||||||
|
* modified versions of OpenSSL that use the same license as OpenSSL),
|
||||||
|
* and distribute linked combinations including the two.
|
||||||
|
*
|
||||||
|
* You must obey the GNU Lesser General Public License in all respects for
|
||||||
|
* all of the code used other than OpenSSL.
|
||||||
|
*
|
||||||
|
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
* License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PSYCOPG_ERROR_H
|
||||||
|
#define PSYCOPG_ERROR_H 1
|
||||||
|
|
||||||
|
extern HIDDEN PyTypeObject errorType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyBaseExceptionObject exc;
|
||||||
|
|
||||||
|
PyObject *pgerror;
|
||||||
|
PyObject *pgcode;
|
||||||
|
cursorObject *cursor;
|
||||||
|
char *codec;
|
||||||
|
PGresult *pgres;
|
||||||
|
} errorObject;
|
||||||
|
|
||||||
|
HIDDEN PyObject *error_text_from_chars(errorObject *self, const char *str);
|
||||||
|
|
||||||
|
#endif /* PSYCOPG_ERROR_H */
|
291
psycopg/error_type.c
Normal file
291
psycopg/error_type.c
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
/* error_type.c - python interface to the Error objects
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Daniele Varrazzo <daniele.varrazzo@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of psycopg.
|
||||||
|
*
|
||||||
|
* psycopg2 is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Lesser General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* In addition, as a special exception, the copyright holders give
|
||||||
|
* permission to link this program with the OpenSSL library (or with
|
||||||
|
* modified versions of OpenSSL that use the same license as OpenSSL),
|
||||||
|
* and distribute linked combinations including the two.
|
||||||
|
*
|
||||||
|
* You must obey the GNU Lesser General Public License in all respects for
|
||||||
|
* all of the code used other than OpenSSL.
|
||||||
|
*
|
||||||
|
* psycopg2 is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
* License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define PSYCOPG_MODULE
|
||||||
|
#include "psycopg/psycopg.h"
|
||||||
|
|
||||||
|
#include "psycopg/error.h"
|
||||||
|
#include "psycopg/diagnostics.h"
|
||||||
|
#include "psycopg/pqpath.h"
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
error_text_from_chars(errorObject *self, const char *str)
|
||||||
|
{
|
||||||
|
if (str == NULL) {
|
||||||
|
Py_INCREF(Py_None);
|
||||||
|
return (Py_None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if PY_MAJOR_VERSION < 3
|
||||||
|
return PyString_FromString(str);
|
||||||
|
#else
|
||||||
|
return PyUnicode_Decode(str, strlen(str),
|
||||||
|
self->codec ? self->codec : "ascii", "replace");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static const char pgerror_doc[] =
|
||||||
|
"The error message returned by the backend, if available, else None";
|
||||||
|
|
||||||
|
static const char pgcode_doc[] =
|
||||||
|
"The error code returned by the backend, if available, else None";
|
||||||
|
|
||||||
|
static const char cursor_doc[] =
|
||||||
|
"The cursor that raised the exception, if available, else None";
|
||||||
|
|
||||||
|
static const char diag_doc[] =
|
||||||
|
"A Diagnostics object to get further information about the error";
|
||||||
|
|
||||||
|
static PyMemberDef error_members[] = {
|
||||||
|
{ "pgerror", T_OBJECT, offsetof(errorObject, pgerror),
|
||||||
|
READONLY, (char *)pgerror_doc },
|
||||||
|
{ "pgcode", T_OBJECT, offsetof(errorObject, pgcode),
|
||||||
|
READONLY, (char *)pgcode_doc },
|
||||||
|
{ "cursor", T_OBJECT, offsetof(errorObject, cursor),
|
||||||
|
READONLY, (char *)cursor_doc },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
error_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
return ((PyTypeObject *)PyExc_StandardError)->tp_new(
|
||||||
|
type, args, kwargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
error_init(errorObject *self, PyObject *args, PyObject *kwargs)
|
||||||
|
{
|
||||||
|
if (((PyTypeObject *)PyExc_StandardError)->tp_init(
|
||||||
|
(PyObject *)self, args, kwargs) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
error_traverse(errorObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->cursor);
|
||||||
|
return ((PyTypeObject *)PyExc_StandardError)->tp_traverse(
|
||||||
|
(PyObject *)self, visit, arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
error_clear(errorObject *self)
|
||||||
|
{
|
||||||
|
Py_CLEAR(self->pgerror);
|
||||||
|
Py_CLEAR(self->pgcode);
|
||||||
|
Py_CLEAR(self->cursor);
|
||||||
|
PyMem_Free(self->codec);
|
||||||
|
IFCLEARPGRES(self->pgres);
|
||||||
|
return ((PyTypeObject *)PyExc_StandardError)->tp_clear((PyObject *)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
error_dealloc(errorObject *self)
|
||||||
|
{
|
||||||
|
error_clear(self);
|
||||||
|
return Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
error_get_diag(errorObject *self, void *closure)
|
||||||
|
{
|
||||||
|
return PyObject_CallFunctionObjArgs(
|
||||||
|
(PyObject *)&diagnosticsType, (PyObject *)self, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct PyGetSetDef error_getsets[] = {
|
||||||
|
{ "diag", (getter)error_get_diag, NULL, (char *)diag_doc },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#if PY_VERSION_HEX >= 0x02050000
|
||||||
|
|
||||||
|
/* Error.__reduce__
|
||||||
|
*
|
||||||
|
* The method is required to make exceptions picklable: set the cursor
|
||||||
|
* attribute to None. Only working from Py 2.5: previous versions
|
||||||
|
* would require implementing __getstate__, and as of 2012 it's a little
|
||||||
|
* bit too late to care. */
|
||||||
|
static PyObject *
|
||||||
|
psyco_error_reduce(errorObject *self)
|
||||||
|
{
|
||||||
|
PyObject *meth = NULL;
|
||||||
|
PyObject *tuple = NULL;
|
||||||
|
PyObject *dict = NULL;
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
|
||||||
|
if (!(meth = PyObject_GetAttrString(PyExc_StandardError, "__reduce__"))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
if (!(tuple = PyObject_CallFunctionObjArgs(meth, self, NULL))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tuple is (type, args): convert it to (type, args, dict)
|
||||||
|
* with our extra items in the dict.
|
||||||
|
*
|
||||||
|
* If these checks fail, we can still return a valid object. Pickle
|
||||||
|
* will likely fail downstream, but there's nothing else we can do here */
|
||||||
|
if (!PyTuple_Check(tuple)) { goto exit; }
|
||||||
|
if (2 != PyTuple_GET_SIZE(tuple)) { goto exit; }
|
||||||
|
|
||||||
|
if (!(dict = PyDict_New())) { goto error; }
|
||||||
|
if (0 != PyDict_SetItemString(dict, "pgerror", self->pgerror)) { goto error; }
|
||||||
|
if (0 != PyDict_SetItemString(dict, "pgcode", self->pgcode)) { goto error; }
|
||||||
|
|
||||||
|
{
|
||||||
|
PyObject *newtuple;
|
||||||
|
if (!(newtuple = PyTuple_Pack(3,
|
||||||
|
PyTuple_GET_ITEM(tuple, 0),
|
||||||
|
PyTuple_GET_ITEM(tuple, 1),
|
||||||
|
dict))) {
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
Py_DECREF(tuple);
|
||||||
|
tuple = newtuple;
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rv = tuple;
|
||||||
|
tuple = NULL;
|
||||||
|
|
||||||
|
error:
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
Py_XDECREF(tuple);
|
||||||
|
Py_XDECREF(meth);
|
||||||
|
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
psyco_error_setstate(errorObject *self, PyObject *state)
|
||||||
|
{
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
|
||||||
|
/* we don't call the StandartError's setstate as it would try to load the
|
||||||
|
* dict content as attributes */
|
||||||
|
|
||||||
|
if (state == Py_None) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
if (!PyDict_Check(state)) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "state is not a dictionary");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* load the dict content in the structure */
|
||||||
|
Py_CLEAR(self->pgerror);
|
||||||
|
self->pgerror = PyDict_GetItemString(state, "pgerror");
|
||||||
|
Py_XINCREF(self->pgerror);
|
||||||
|
|
||||||
|
Py_CLEAR(self->pgcode);
|
||||||
|
self->pgcode = PyDict_GetItemString(state, "pgcode");
|
||||||
|
Py_XINCREF(self->pgcode);
|
||||||
|
|
||||||
|
Py_CLEAR(self->cursor);
|
||||||
|
/* We never expect a cursor in the state as it's not picklable.
|
||||||
|
* at most there could be a None here, coming from Psycopg < 2.5 */
|
||||||
|
|
||||||
|
exit:
|
||||||
|
rv = Py_None;
|
||||||
|
Py_INCREF(rv);
|
||||||
|
|
||||||
|
error:
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* PY_VERSION_HEX >= 0x02050000 */
|
||||||
|
|
||||||
|
static PyMethodDef error_methods[] = {
|
||||||
|
#if PY_VERSION_HEX >= 0x02050000
|
||||||
|
/* Make Error and all its subclasses picklable.
|
||||||
|
*
|
||||||
|
* TODO: this comment applied to the __reduce_ex__ implementation: now
|
||||||
|
* pickling may work on Py 2.4 too... but it's 2013 now.
|
||||||
|
*
|
||||||
|
* Don't do it it on Py 2.4: [__reduce_ex__] it is not used by the pickle
|
||||||
|
* protocol, and if called manually fails in an unsettling way,
|
||||||
|
* probably because the exceptions were old-style classes. */
|
||||||
|
{"__reduce__", (PyCFunction)psyco_error_reduce, METH_NOARGS },
|
||||||
|
{"__setstate__", (PyCFunction)psyco_error_setstate, METH_O },
|
||||||
|
#endif
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
PyTypeObject errorType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"psycopg2.Error",
|
||||||
|
sizeof(errorObject),
|
||||||
|
0,
|
||||||
|
(destructor)error_dealloc, /* tp_dealloc */
|
||||||
|
0, /*tp_print*/
|
||||||
|
0, /*tp_getattr*/
|
||||||
|
0, /*tp_setattr*/
|
||||||
|
0, /*tp_compare*/
|
||||||
|
0, /*tp_repr*/
|
||||||
|
0, /*tp_as_number*/
|
||||||
|
0, /*tp_as_sequence*/
|
||||||
|
0, /*tp_as_mapping*/
|
||||||
|
0, /*tp_hash */
|
||||||
|
0, /*tp_call*/
|
||||||
|
0, /*tp_str*/
|
||||||
|
0, /*tp_getattro*/
|
||||||
|
0, /*tp_setattro*/
|
||||||
|
0, /*tp_as_buffer*/
|
||||||
|
Py_TPFLAGS_DEFAULT|Py_TPFLAGS_BASETYPE|Py_TPFLAGS_HAVE_GC, /*tp_flags*/
|
||||||
|
Error_doc, /*tp_doc*/
|
||||||
|
(traverseproc)error_traverse, /*tp_traverse*/
|
||||||
|
(inquiry)error_clear, /*tp_clear*/
|
||||||
|
0, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
error_methods, /*tp_methods*/
|
||||||
|
error_members, /*tp_members*/
|
||||||
|
error_getsets, /*tp_getset*/
|
||||||
|
0, /*tp_base Will be set to StandardError in module init */
|
||||||
|
0, /*tp_dict*/
|
||||||
|
0, /*tp_descr_get*/
|
||||||
|
0, /*tp_descr_set*/
|
||||||
|
0, /*tp_dictoffset*/
|
||||||
|
(initproc)error_init, /*tp_init*/
|
||||||
|
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
|
||||||
|
error_new, /*tp_new*/
|
||||||
|
0, /*tp_free Low-level free-memory routine */
|
||||||
|
0, /*tp_is_gc For PyObject_IS_GC */
|
||||||
|
0, /*tp_bases*/
|
||||||
|
0, /*tp_mro method resolution order */
|
||||||
|
0, /*tp_cache*/
|
||||||
|
0, /*tp_subclasses*/
|
||||||
|
0 /*tp_weaklist*/
|
||||||
|
};
|
|
@ -78,13 +78,13 @@ RAISES_NEG HIDDEN int lobject_close(lobjectObject *self);
|
||||||
#define EXC_IF_LOBJ_LEVEL0(self) \
|
#define EXC_IF_LOBJ_LEVEL0(self) \
|
||||||
if (self->conn->autocommit) { \
|
if (self->conn->autocommit) { \
|
||||||
psyco_set_error(ProgrammingError, NULL, \
|
psyco_set_error(ProgrammingError, NULL, \
|
||||||
"can't use a lobject outside of transactions", NULL, NULL); \
|
"can't use a lobject outside of transactions"); \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
#define EXC_IF_LOBJ_UNMARKED(self) \
|
#define EXC_IF_LOBJ_UNMARKED(self) \
|
||||||
if (self->conn->mark != self->mark) { \
|
if (self->conn->mark != self->mark) { \
|
||||||
psyco_set_error(ProgrammingError, NULL, \
|
psyco_set_error(ProgrammingError, NULL, \
|
||||||
"lobject isn't valid anymore", NULL, NULL); \
|
"lobject isn't valid anymore"); \
|
||||||
return NULL; \
|
return NULL; \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -333,7 +333,7 @@ lobject_setup(lobjectObject *self, connectionObject *conn,
|
||||||
|
|
||||||
if (conn->autocommit) {
|
if (conn->autocommit) {
|
||||||
psyco_set_error(ProgrammingError, NULL,
|
psyco_set_error(ProgrammingError, NULL,
|
||||||
"can't use a lobject outside of transactions", NULL, NULL);
|
"can't use a lobject outside of transactions");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,7 +203,7 @@ microprotocols_adapt(PyObject *obj, PyObject *proto, PyObject *alt)
|
||||||
/* else set the right exception and return NULL */
|
/* else set the right exception and return NULL */
|
||||||
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
|
PyOS_snprintf(buffer, 255, "can't adapt type '%s'",
|
||||||
Py_TYPE(obj)->tp_name);
|
Py_TYPE(obj)->tp_name);
|
||||||
psyco_set_error(ProgrammingError, NULL, buffer, NULL, NULL);
|
psyco_set_error(ProgrammingError, NULL, buffer);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include "psycopg/green.h"
|
#include "psycopg/green.h"
|
||||||
#include "psycopg/typecast.h"
|
#include "psycopg/typecast.h"
|
||||||
#include "psycopg/pgtypes.h"
|
#include "psycopg/pgtypes.h"
|
||||||
|
#include "psycopg/error.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
@ -149,15 +150,20 @@ exception_from_sqlstate(const char *sqlstate)
|
||||||
|
|
||||||
/* pq_raise - raise a python exception of the right kind
|
/* pq_raise - raise a python exception of the right kind
|
||||||
|
|
||||||
This function should be called while holding the GIL. */
|
This function should be called while holding the GIL.
|
||||||
|
|
||||||
|
The function passes the ownership of the pgres to the returned exception,
|
||||||
|
wherer the pgres was the explicit argument or taken from the cursor.
|
||||||
|
So, after calling it curs->pgres will be set to null */
|
||||||
|
|
||||||
RAISES static void
|
RAISES static void
|
||||||
pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
|
pq_raise(connectionObject *conn, cursorObject *curs, PGresult **pgres)
|
||||||
{
|
{
|
||||||
PyObject *exc = NULL;
|
PyObject *exc = NULL;
|
||||||
const char *err = NULL;
|
const char *err = NULL;
|
||||||
const char *err2 = NULL;
|
const char *err2 = NULL;
|
||||||
const char *code = NULL;
|
const char *code = NULL;
|
||||||
|
PyObject *pyerr = NULL;
|
||||||
|
|
||||||
if (conn == NULL) {
|
if (conn == NULL) {
|
||||||
PyErr_SetString(DatabaseError,
|
PyErr_SetString(DatabaseError,
|
||||||
|
@ -171,13 +177,13 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
|
||||||
conn->closed = 2;
|
conn->closed = 2;
|
||||||
|
|
||||||
if (pgres == NULL && curs != NULL)
|
if (pgres == NULL && curs != NULL)
|
||||||
pgres = curs->pgres;
|
pgres = &curs->pgres;
|
||||||
|
|
||||||
if (pgres) {
|
if (pgres && *pgres) {
|
||||||
err = PQresultErrorMessage(pgres);
|
err = PQresultErrorMessage(*pgres);
|
||||||
if (err != NULL) {
|
if (err != NULL) {
|
||||||
Dprintf("pq_raise: PQresultErrorMessage: err=%s", err);
|
Dprintf("pq_raise: PQresultErrorMessage: err=%s", err);
|
||||||
code = PQresultErrorField(pgres, PG_DIAG_SQLSTATE);
|
code = PQresultErrorField(*pgres, PG_DIAG_SQLSTATE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (err == NULL) {
|
if (err == NULL) {
|
||||||
|
@ -210,7 +216,26 @@ pq_raise(connectionObject *conn, cursorObject *curs, PGresult *pgres)
|
||||||
err2 = strip_severity(err);
|
err2 = strip_severity(err);
|
||||||
Dprintf("pq_raise: err2=%s", err2);
|
Dprintf("pq_raise: err2=%s", err2);
|
||||||
|
|
||||||
psyco_set_error(exc, curs, err2, err, code);
|
pyerr = psyco_set_error(exc, curs, err2);
|
||||||
|
|
||||||
|
if (pyerr && PyObject_TypeCheck(pyerr, &errorType)) {
|
||||||
|
errorObject *perr = (errorObject *)pyerr;
|
||||||
|
|
||||||
|
PyMem_Free(perr->codec);
|
||||||
|
psycopg_strdup(&perr->codec, conn->codec, 0);
|
||||||
|
|
||||||
|
Py_CLEAR(perr->pgerror);
|
||||||
|
perr->pgerror = error_text_from_chars(perr, err);
|
||||||
|
|
||||||
|
Py_CLEAR(perr->pgcode);
|
||||||
|
perr->pgcode = error_text_from_chars(perr, code);
|
||||||
|
|
||||||
|
IFCLEARPGRES(perr->pgres);
|
||||||
|
if (pgres && *pgres) {
|
||||||
|
perr->pgres = *pgres;
|
||||||
|
*pgres = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pq_set_critical, pq_resolve_critical - manage critical errors
|
/* pq_set_critical, pq_resolve_critical - manage critical errors
|
||||||
|
@ -388,14 +413,16 @@ pq_complete_error(connectionObject *conn, PGresult **pgres, char **error)
|
||||||
{
|
{
|
||||||
Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s",
|
Dprintf("pq_complete_error: pgconn = %p, pgres = %p, error = %s",
|
||||||
conn->pgconn, *pgres, *error ? *error : "(null)");
|
conn->pgconn, *pgres, *error ? *error : "(null)");
|
||||||
if (*pgres != NULL)
|
if (*pgres != NULL) {
|
||||||
pq_raise(conn, NULL, *pgres);
|
pq_raise(conn, NULL, pgres);
|
||||||
|
/* now *pgres is null */
|
||||||
|
}
|
||||||
else if (*error != NULL) {
|
else if (*error != NULL) {
|
||||||
PyErr_SetString(OperationalError, *error);
|
PyErr_SetString(OperationalError, *error);
|
||||||
} else {
|
} else {
|
||||||
PyErr_SetString(OperationalError, "unknown error");
|
PyErr_SetString(OperationalError, "unknown error");
|
||||||
}
|
}
|
||||||
IFCLEARPGRES(*pgres);
|
|
||||||
if (*error) {
|
if (*error) {
|
||||||
free(*error);
|
free(*error);
|
||||||
*error = NULL;
|
*error = NULL;
|
||||||
|
@ -1509,8 +1536,6 @@ pq_fetch(cursorObject *curs, int no_result)
|
||||||
default:
|
default:
|
||||||
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
|
Dprintf("pq_fetch: uh-oh, something FAILED: pgconn = %p", curs->conn);
|
||||||
pq_raise(curs->conn, curs, NULL);
|
pq_raise(curs->conn, curs, NULL);
|
||||||
/* don't clear curs->pgres, because it contains detailed error
|
|
||||||
information */
|
|
||||||
ex = -1;
|
ex = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,8 +120,7 @@ HIDDEN PyObject *psyco_GetDecimalType(void);
|
||||||
typedef struct cursorObject cursorObject;
|
typedef struct cursorObject cursorObject;
|
||||||
|
|
||||||
/* some utility functions */
|
/* some utility functions */
|
||||||
RAISES HIDDEN void psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
|
RAISES HIDDEN PyObject *psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg);
|
||||||
const char *pgerror, const char *pgcode);
|
|
||||||
|
|
||||||
HIDDEN char *psycopg_escape_string(PyObject *conn,
|
HIDDEN char *psycopg_escape_string(PyObject *conn,
|
||||||
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
|
const char *from, Py_ssize_t len, char *to, Py_ssize_t *tolen);
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "psycopg/typecast.h"
|
#include "psycopg/typecast.h"
|
||||||
#include "psycopg/microprotocols.h"
|
#include "psycopg/microprotocols.h"
|
||||||
#include "psycopg/microprotocols_proto.h"
|
#include "psycopg/microprotocols_proto.h"
|
||||||
|
#include "psycopg/error.h"
|
||||||
#include "psycopg/diagnostics.h"
|
#include "psycopg/diagnostics.h"
|
||||||
|
|
||||||
#include "psycopg/adapter_qstring.h"
|
#include "psycopg/adapter_qstring.h"
|
||||||
|
@ -409,8 +410,8 @@ static struct {
|
||||||
PyObject **base;
|
PyObject **base;
|
||||||
const char *docstr;
|
const char *docstr;
|
||||||
} exctable[] = {
|
} exctable[] = {
|
||||||
{ "psycopg2.Error", &Error, 0, Error_doc },
|
{ "psycopg2.Error", &Error, &PyExc_StandardError, Error_doc },
|
||||||
{ "psycopg2.Warning", &Warning, 0, Warning_doc },
|
{ "psycopg2.Warning", &Warning, &PyExc_StandardError, Warning_doc },
|
||||||
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
|
{ "psycopg2.InterfaceError", &InterfaceError, &Error, InterfaceError_doc },
|
||||||
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
|
{ "psycopg2.DatabaseError", &DatabaseError, &Error, DatabaseError_doc },
|
||||||
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc },
|
{ "psycopg2.InternalError", &InternalError, &DatabaseError, InternalError_doc },
|
||||||
|
@ -434,61 +435,6 @@ static struct {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#if PY_VERSION_HEX >= 0x02050000
|
|
||||||
|
|
||||||
/* Error.__reduce_ex__
|
|
||||||
*
|
|
||||||
* The method is required to make exceptions picklable: set the cursor
|
|
||||||
* attribute to None. Only working from Py 2.5: previous versions
|
|
||||||
* would require implementing __getstate__, and as of 2012 it's a little
|
|
||||||
* bit too late to care. */
|
|
||||||
static PyObject *
|
|
||||||
psyco_error_reduce_ex(PyObject *self, PyObject *args)
|
|
||||||
{
|
|
||||||
PyObject *proto = NULL;
|
|
||||||
PyObject *super = NULL;
|
|
||||||
PyObject *tuple = NULL;
|
|
||||||
PyObject *dict = NULL;
|
|
||||||
PyObject *rv = NULL;
|
|
||||||
|
|
||||||
/* tuple = Exception.__reduce_ex__(self, proto) */
|
|
||||||
if (!PyArg_ParseTuple(args, "O", &proto)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (!(super = PyObject_GetAttrString(PyExc_Exception, "__reduce_ex__"))) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
if (!(tuple = PyObject_CallFunctionObjArgs(super, self, proto, NULL))) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* tuple[2]['cursor'] = None
|
|
||||||
*
|
|
||||||
* If these checks fail, we can still return a valid object. Pickle
|
|
||||||
* will likely fail downstream, but there's nothing else we can do here */
|
|
||||||
if (!PyTuple_Check(tuple)) { goto exit; }
|
|
||||||
if (3 > PyTuple_GET_SIZE(tuple)) { goto exit; }
|
|
||||||
dict = PyTuple_GET_ITEM(tuple, 2); /* borrowed */
|
|
||||||
if (!PyDict_Check(dict)) { goto exit; }
|
|
||||||
|
|
||||||
/* Modify the tuple inplace and return it */
|
|
||||||
if (0 != PyDict_SetItemString(dict, "cursor", Py_None)) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit:
|
|
||||||
rv = tuple;
|
|
||||||
tuple = NULL;
|
|
||||||
|
|
||||||
error:
|
|
||||||
Py_XDECREF(tuple);
|
|
||||||
Py_XDECREF(super);
|
|
||||||
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* PY_VERSION_HEX >= 0x02050000 */
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
psyco_errors_init(void)
|
psyco_errors_init(void)
|
||||||
{
|
{
|
||||||
|
@ -498,18 +444,13 @@ psyco_errors_init(void)
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
PyObject *dict = NULL;
|
PyObject *dict = NULL;
|
||||||
PyObject *base;
|
|
||||||
PyObject *str = NULL;
|
PyObject *str = NULL;
|
||||||
PyObject *descr = NULL;
|
|
||||||
PyObject *diag_property = NULL;
|
|
||||||
int rv = -1;
|
int rv = -1;
|
||||||
|
|
||||||
#if PY_VERSION_HEX >= 0x02050000
|
/* 'Error' has been defined elsewhere: only init the other classes */
|
||||||
static PyMethodDef psyco_error_reduce_ex_def =
|
Error = (PyObject *)&errorType;
|
||||||
{"__reduce_ex__", psyco_error_reduce_ex, METH_VARARGS, "pickle helper"};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
for (i=0; exctable[i].name; i++) {
|
for (i = 1; exctable[i].name; i++) {
|
||||||
if (!(dict = PyDict_New())) { goto exit; }
|
if (!(dict = PyDict_New())) { goto exit; }
|
||||||
|
|
||||||
if (exctable[i].docstr) {
|
if (exctable[i].docstr) {
|
||||||
|
@ -518,58 +459,16 @@ psyco_errors_init(void)
|
||||||
Py_CLEAR(str);
|
Py_CLEAR(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exctable[i].base == 0) {
|
|
||||||
#if PY_MAJOR_VERSION < 3
|
|
||||||
base = PyExc_StandardError;
|
|
||||||
#else
|
|
||||||
/* StandardError is gone in 3.0 */
|
|
||||||
base = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else
|
|
||||||
base = *exctable[i].base;
|
|
||||||
|
|
||||||
if (!(*exctable[i].exc = PyErr_NewException(
|
if (!(*exctable[i].exc = PyErr_NewException(
|
||||||
exctable[i].name, base, dict))) {
|
exctable[i].name, *exctable[i].base, dict))) {
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
Py_CLEAR(dict);
|
Py_CLEAR(dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Make pgerror, pgcode and cursor default to None on psycopg
|
|
||||||
error objects. This simplifies error handling code that checks
|
|
||||||
these attributes. */
|
|
||||||
PyObject_SetAttrString(Error, "pgerror", Py_None);
|
|
||||||
PyObject_SetAttrString(Error, "pgcode", Py_None);
|
|
||||||
PyObject_SetAttrString(Error, "cursor", Py_None);
|
|
||||||
|
|
||||||
if (!(diag_property = PyObject_CallFunctionObjArgs(
|
|
||||||
(PyObject *) &PyProperty_Type, &diagnosticsType, NULL))) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
PyObject_SetAttrString(Error, "diag", diag_property);
|
|
||||||
|
|
||||||
/* install __reduce_ex__ on Error to make all the subclasses picklable.
|
|
||||||
*
|
|
||||||
* Don't install it on Py 2.4: it is not used by the pickle
|
|
||||||
* protocol, and if called manually fails in an unsettling way,
|
|
||||||
* probably because the exceptions were old-style classes. */
|
|
||||||
#if PY_VERSION_HEX >= 0x02050000
|
|
||||||
if (!(descr = PyDescr_NewMethod((PyTypeObject *)Error,
|
|
||||||
&psyco_error_reduce_ex_def))) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
if (0 != PyObject_SetAttrString(Error,
|
|
||||||
psyco_error_reduce_ex_def.ml_name, descr)) {
|
|
||||||
goto exit;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
Py_XDECREF(diag_property);
|
|
||||||
Py_XDECREF(descr);
|
|
||||||
Py_XDECREF(str);
|
Py_XDECREF(str);
|
||||||
Py_XDECREF(dict);
|
Py_XDECREF(dict);
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -613,11 +512,10 @@ psyco_errors_set(PyObject *type)
|
||||||
|
|
||||||
Create a new error of the given type with extra attributes. */
|
Create a new error of the given type with extra attributes. */
|
||||||
|
|
||||||
RAISES void
|
/* TODO: may have been changed to BORROWED */
|
||||||
psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
|
RAISES PyObject *
|
||||||
const char *pgerror, const char *pgcode)
|
psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg)
|
||||||
{
|
{
|
||||||
PyObject *t;
|
|
||||||
PyObject *pymsg;
|
PyObject *pymsg;
|
||||||
PyObject *err = NULL;
|
PyObject *err = NULL;
|
||||||
connectionObject *conn = NULL;
|
connectionObject *conn = NULL;
|
||||||
|
@ -633,31 +531,24 @@ psyco_set_error(PyObject *exc, cursorObject *curs, const char *msg,
|
||||||
else {
|
else {
|
||||||
/* what's better than an error in an error handler in the morning?
|
/* what's better than an error in an error handler in the morning?
|
||||||
* Anyway, some error was set, refcount is ok... get outta here. */
|
* Anyway, some error was set, refcount is ok... get outta here. */
|
||||||
return;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err && PyObject_TypeCheck(err, &errorType)) {
|
||||||
|
errorObject *perr = (errorObject *)err;
|
||||||
|
if (curs) {
|
||||||
|
Py_CLEAR(perr->cursor);
|
||||||
|
Py_INCREF(curs);
|
||||||
|
perr->cursor = curs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
if (curs) {
|
|
||||||
PyObject_SetAttrString(err, "cursor", (PyObject *)curs);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pgerror) {
|
|
||||||
if ((t = conn_text_from_chars(conn, pgerror))) {
|
|
||||||
PyObject_SetAttrString(err, "pgerror", t);
|
|
||||||
Py_DECREF(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pgcode) {
|
|
||||||
if ((t = conn_text_from_chars(conn, pgcode))) {
|
|
||||||
PyObject_SetAttrString(err, "pgcode", t);
|
|
||||||
Py_DECREF(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PyErr_SetObject(exc, err);
|
PyErr_SetObject(exc, err);
|
||||||
Py_DECREF(err);
|
Py_DECREF(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -891,6 +782,7 @@ INIT_MODULE(_psycopg)(void)
|
||||||
Py_TYPE(&chunkType) = &PyType_Type;
|
Py_TYPE(&chunkType) = &PyType_Type;
|
||||||
Py_TYPE(&NotifyType) = &PyType_Type;
|
Py_TYPE(&NotifyType) = &PyType_Type;
|
||||||
Py_TYPE(&XidType) = &PyType_Type;
|
Py_TYPE(&XidType) = &PyType_Type;
|
||||||
|
Py_TYPE(&errorType) = &PyType_Type;
|
||||||
Py_TYPE(&diagnosticsType) = &PyType_Type;
|
Py_TYPE(&diagnosticsType) = &PyType_Type;
|
||||||
|
|
||||||
if (PyType_Ready(&connectionType) == -1) goto exit;
|
if (PyType_Ready(&connectionType) == -1) goto exit;
|
||||||
|
@ -908,6 +800,8 @@ INIT_MODULE(_psycopg)(void)
|
||||||
if (PyType_Ready(&chunkType) == -1) goto exit;
|
if (PyType_Ready(&chunkType) == -1) goto exit;
|
||||||
if (PyType_Ready(&NotifyType) == -1) goto exit;
|
if (PyType_Ready(&NotifyType) == -1) goto exit;
|
||||||
if (PyType_Ready(&XidType) == -1) goto exit;
|
if (PyType_Ready(&XidType) == -1) goto exit;
|
||||||
|
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
|
||||||
|
if (PyType_Ready(&errorType) == -1) goto exit;
|
||||||
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
|
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
|
||||||
|
|
||||||
#ifdef PSYCOPG_EXTENSIONS
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
|
@ -1043,6 +937,7 @@ INIT_MODULE(_psycopg)(void)
|
||||||
pydatetimeType.tp_alloc = PyType_GenericAlloc;
|
pydatetimeType.tp_alloc = PyType_GenericAlloc;
|
||||||
NotifyType.tp_alloc = PyType_GenericAlloc;
|
NotifyType.tp_alloc = PyType_GenericAlloc;
|
||||||
XidType.tp_alloc = PyType_GenericAlloc;
|
XidType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
errorType.tp_alloc = PyType_GenericAlloc;
|
||||||
diagnosticsType.tp_alloc = PyType_GenericAlloc;
|
diagnosticsType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
|
||||||
#ifdef PSYCOPG_EXTENSIONS
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
|
|
|
@ -114,6 +114,7 @@ typedef unsigned long Py_uhash_t;
|
||||||
#define PyInt_AsLong PyLong_AsLong
|
#define PyInt_AsLong PyLong_AsLong
|
||||||
#define PyInt_FromLong PyLong_FromLong
|
#define PyInt_FromLong PyLong_FromLong
|
||||||
#define PyInt_FromSsize_t PyLong_FromSsize_t
|
#define PyInt_FromSsize_t PyLong_FromSsize_t
|
||||||
|
#define PyExc_StandardError PyExc_Exception
|
||||||
#define PyString_FromFormat PyUnicode_FromFormat
|
#define PyString_FromFormat PyUnicode_FromFormat
|
||||||
#define Py_TPFLAGS_HAVE_ITER 0L
|
#define Py_TPFLAGS_HAVE_ITER 0L
|
||||||
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L
|
#define Py_TPFLAGS_HAVE_RICHCOMPARE 0L
|
||||||
|
|
|
@ -115,10 +115,16 @@ psycopg_escape_identifier_easy(const char *from, Py_ssize_t len)
|
||||||
*
|
*
|
||||||
* Store the return in 'to' and return 0 in case of success, else return -1
|
* Store the return in 'to' and return 0 in case of success, else return -1
|
||||||
* and raise an exception.
|
* and raise an exception.
|
||||||
|
*
|
||||||
|
* If from is null, store null into to.
|
||||||
*/
|
*/
|
||||||
RAISES_NEG int
|
RAISES_NEG int
|
||||||
psycopg_strdup(char **to, const char *from, Py_ssize_t len)
|
psycopg_strdup(char **to, const char *from, Py_ssize_t len)
|
||||||
{
|
{
|
||||||
|
if (!from) {
|
||||||
|
*to = NULL;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (!len) { len = strlen(from); }
|
if (!len) { len = strlen(from); }
|
||||||
if (!(*to = PyMem_Malloc(len + 1))) {
|
if (!(*to = PyMem_Malloc(len + 1))) {
|
||||||
PyErr_NoMemory();
|
PyErr_NoMemory();
|
||||||
|
|
6
setup.py
6
setup.py
|
@ -428,7 +428,7 @@ sources = [
|
||||||
|
|
||||||
'connection_int.c', 'connection_type.c',
|
'connection_int.c', 'connection_type.c',
|
||||||
'cursor_int.c', 'cursor_type.c',
|
'cursor_int.c', 'cursor_type.c',
|
||||||
'diagnostics_type.c',
|
'diagnostics_type.c', 'error_type.c',
|
||||||
'lobject_int.c', 'lobject_type.c',
|
'lobject_int.c', 'lobject_type.c',
|
||||||
'notify_type.c', 'xid_type.c',
|
'notify_type.c', 'xid_type.c',
|
||||||
|
|
||||||
|
@ -441,8 +441,8 @@ sources = [
|
||||||
|
|
||||||
depends = [
|
depends = [
|
||||||
# headers
|
# headers
|
||||||
'config.h', 'pgtypes.h', 'psycopg.h', 'python.h',
|
'config.h', 'pgtypes.h', 'psycopg.h', 'python.h', 'connection.h',
|
||||||
'connection.h', 'cursor.h', 'diagnostics.h', 'green.h', 'lobject.h',
|
'cursor.h', 'diagnostics.h', 'error.h', 'green.h', 'lobject.h',
|
||||||
'notify.h', 'pqpath.h', 'xid.h',
|
'notify.h', 'pqpath.h', 'xid.h',
|
||||||
|
|
||||||
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
|
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',
|
||||||
|
|
|
@ -222,6 +222,38 @@ class ExceptionsTestCase(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEqual(diag.sqlstate, '42P01')
|
self.assertEqual(diag.sqlstate, '42P01')
|
||||||
|
|
||||||
|
def test_diagnostics_independent(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute("l'acqua e' poca e 'a papera nun galleggia")
|
||||||
|
except Exception, exc:
|
||||||
|
diag1 = exc.diag
|
||||||
|
|
||||||
|
self.conn.rollback()
|
||||||
|
|
||||||
|
try:
|
||||||
|
cur.execute("select level from water where ducks > 1")
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
diag2 = exc.diag
|
||||||
|
|
||||||
|
self.assertEqual(diag1.sqlstate, '42601')
|
||||||
|
self.assertEqual(diag2.sqlstate, '42P01')
|
||||||
|
|
||||||
|
def test_diagnostics_from_commit(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("""
|
||||||
|
create temp table test_deferred (
|
||||||
|
data int primary key,
|
||||||
|
ref int references test_deferred (data)
|
||||||
|
deferrable initially deferred)
|
||||||
|
""")
|
||||||
|
cur.execute("insert into test_deferred values (1,2)")
|
||||||
|
try:
|
||||||
|
self.conn.commit()
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
e = exc
|
||||||
|
self.assertEqual(e.diag.sqlstate, '23503')
|
||||||
|
|
||||||
@skip_before_postgres(9, 3)
|
@skip_before_postgres(9, 3)
|
||||||
def test_9_3_diagnostics(self):
|
def test_9_3_diagnostics(self):
|
||||||
cur = self.conn.cursor()
|
cur = self.conn.cursor()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user