mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-25 10:23:43 +03:00
Merge branch 'diagnostics' into devel
This commit is contained in:
commit
97311967e8
2
NEWS
2
NEWS
|
@ -6,6 +6,8 @@ What's new in psycopg 2.5
|
||||||
- Added support for Python 3.3.
|
- Added support for Python 3.3.
|
||||||
- 'connection' and 'cursor' objects can be used in 'with' statements
|
- 'connection' and 'cursor' objects can be used in 'with' statements
|
||||||
as context managers as specified by recent DBAPI extension.
|
as context managers as specified by recent DBAPI extension.
|
||||||
|
- Added Diagnostics object to get extended info from a database error.
|
||||||
|
Many thanks to Matthew Woodcraft for the implementation (ticket #149).
|
||||||
- Added support for backward scrollable cursors. Thanks to Jon Nelson
|
- Added support for backward scrollable cursors. Thanks to Jon Nelson
|
||||||
for the initial patch (ticket #108).
|
for the initial patch (ticket #108).
|
||||||
- Added a simple way to customize casting of composite types into Python
|
- Added a simple way to customize casting of composite types into Python
|
||||||
|
|
|
@ -139,6 +139,37 @@ functionalities defined by the |DBAPI|_.
|
||||||
.. automethod:: from_string(s)
|
.. automethod:: from_string(s)
|
||||||
|
|
||||||
|
|
||||||
|
.. autoclass:: Diagnostics(exception)
|
||||||
|
|
||||||
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
|
The attributes currently available are:
|
||||||
|
|
||||||
|
.. attribute::
|
||||||
|
column_name
|
||||||
|
constraint_name
|
||||||
|
context
|
||||||
|
datatype_name
|
||||||
|
internal_position
|
||||||
|
internal_query
|
||||||
|
message_detail
|
||||||
|
message_hint
|
||||||
|
message_primary
|
||||||
|
schema_name
|
||||||
|
severity
|
||||||
|
source_file
|
||||||
|
source_function
|
||||||
|
source_line
|
||||||
|
sqlstate
|
||||||
|
statement_position
|
||||||
|
table_name
|
||||||
|
|
||||||
|
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
|
||||||
|
includes the specified field and should remain available until the
|
||||||
|
cursor that generated the exception executes another query.
|
||||||
|
|
||||||
|
|
||||||
.. autofunction:: set_wait_callback(f)
|
.. autofunction:: set_wait_callback(f)
|
||||||
|
|
||||||
.. versionadded:: 2.2.0
|
.. versionadded:: 2.2.0
|
||||||
|
|
|
@ -157,10 +157,27 @@ available through the following exceptions:
|
||||||
|
|
||||||
The cursor the exception was raised from; `None` if not applicable.
|
The cursor the exception was raised from; `None` if not applicable.
|
||||||
|
|
||||||
|
.. attribute:: diag
|
||||||
|
|
||||||
|
A `~psycopg2.extensions.Diagnostics` object containing further
|
||||||
|
information about the error. ::
|
||||||
|
|
||||||
|
>>> try:
|
||||||
|
... cur.execute("SELECT * FROM barf")
|
||||||
|
... except Exception, e:
|
||||||
|
... pass
|
||||||
|
|
||||||
|
>>> e.diag.severity
|
||||||
|
'ERROR'
|
||||||
|
>>> e.diag.message_primary
|
||||||
|
'relation "barf" does not exist'
|
||||||
|
|
||||||
|
.. versionadded:: 2.5
|
||||||
|
|
||||||
.. extension::
|
.. extension::
|
||||||
|
|
||||||
The `~Error.pgerror`, `~Error.pgcode`, and `~Error.cursor` attributes
|
The `~Error.pgerror`, `~Error.pgcode`, `~Error.cursor`, and
|
||||||
are Psycopg extensions.
|
`~Error.diag` attributes are Psycopg extensions.
|
||||||
|
|
||||||
|
|
||||||
.. exception:: InterfaceError
|
.. exception:: InterfaceError
|
||||||
|
|
|
@ -58,7 +58,7 @@ except ImportError:
|
||||||
|
|
||||||
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
|
from psycopg2._psycopg import adapt, adapters, encodings, connection, cursor, lobject, Xid
|
||||||
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
|
from psycopg2._psycopg import string_types, binary_types, new_type, new_array_type, register_type
|
||||||
from psycopg2._psycopg import ISQLQuote, Notify
|
from psycopg2._psycopg import ISQLQuote, Notify, Diagnostics
|
||||||
|
|
||||||
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
|
from psycopg2._psycopg import QueryCanceledError, TransactionRollbackError
|
||||||
|
|
||||||
|
|
38
psycopg/diagnostics.h
Normal file
38
psycopg/diagnostics.h
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/* diagnostics.c - definition for the psycopg Diagnostics type
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
|
||||||
|
*
|
||||||
|
* 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_DIAGNOSTICS_H
|
||||||
|
#define PSYCOPG_DIAGNOSTICS_H 1
|
||||||
|
|
||||||
|
extern HIDDEN PyTypeObject diagnosticsType;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
PyObject_HEAD
|
||||||
|
|
||||||
|
PyObject *err; /* exception to retrieve the diagnostics from */
|
||||||
|
|
||||||
|
} diagnosticsObject;
|
||||||
|
|
||||||
|
#endif /* PSYCOPG_DIAGNOSTICS_H */
|
235
psycopg/diagnostics_type.c
Normal file
235
psycopg/diagnostics_type.c
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
/* diagnostics.c - present information from libpq error responses
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Matthew Woodcraft <matthew@woodcraft.me.uk>
|
||||||
|
*
|
||||||
|
* 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/diagnostics.h"
|
||||||
|
#include "psycopg/cursor.h"
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
#ifndef PG_DIAG_SCHEMA_NAME
|
||||||
|
#define PG_DIAG_SCHEMA_NAME 's'
|
||||||
|
#endif
|
||||||
|
#ifndef PG_DIAG_TABLE_NAME
|
||||||
|
#define PG_DIAG_TABLE_NAME 't'
|
||||||
|
#endif
|
||||||
|
#ifndef PG_DIAG_COLUMN_NAME
|
||||||
|
#define PG_DIAG_COLUMN_NAME 'c'
|
||||||
|
#endif
|
||||||
|
#ifndef PG_DIAG_DATATYPE_NAME
|
||||||
|
#define PG_DIAG_DATATYPE_NAME 'd'
|
||||||
|
#endif
|
||||||
|
#ifndef PG_DIAG_CONSTRAINT_NAME
|
||||||
|
#define PG_DIAG_CONSTRAINT_NAME 'n'
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* Retrieve an error string from the exception's cursor.
|
||||||
|
*
|
||||||
|
* If the cursor or its result isn't available, return None.
|
||||||
|
*/
|
||||||
|
static PyObject *
|
||||||
|
psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
|
||||||
|
{
|
||||||
|
// closure contains the field code.
|
||||||
|
PyObject *rv = NULL;
|
||||||
|
PyObject *curs = NULL;
|
||||||
|
const char* errortext;
|
||||||
|
if (!(curs = PyObject_GetAttrString(self->err, "cursor")) ||
|
||||||
|
!PyObject_TypeCheck(curs, &cursorType) ||
|
||||||
|
((cursorObject *)curs)->pgres == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
errortext = PQresultErrorField(
|
||||||
|
((cursorObject *)curs)->pgres, (Py_intptr_t) closure);
|
||||||
|
if (errortext) {
|
||||||
|
rv = conn_text_from_chars(((cursorObject *)curs)->conn, errortext);
|
||||||
|
}
|
||||||
|
exit:
|
||||||
|
if (!rv) {
|
||||||
|
rv = Py_None;
|
||||||
|
Py_INCREF(rv);
|
||||||
|
}
|
||||||
|
Py_XDECREF(curs);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* object calculated member list */
|
||||||
|
static struct PyGetSetDef diagnosticsObject_getsets[] = {
|
||||||
|
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SEVERITY },
|
||||||
|
{ "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SQLSTATE },
|
||||||
|
{ "message_primary", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_MESSAGE_PRIMARY },
|
||||||
|
{ "message_detail", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_MESSAGE_DETAIL },
|
||||||
|
{ "message_hint", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_MESSAGE_HINT },
|
||||||
|
{ "statement_position", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_STATEMENT_POSITION },
|
||||||
|
{ "internal_position", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_INTERNAL_POSITION },
|
||||||
|
{ "internal_query", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_INTERNAL_QUERY },
|
||||||
|
{ "context", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_CONTEXT },
|
||||||
|
{ "schema_name", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SCHEMA_NAME },
|
||||||
|
{ "table_name", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_TABLE_NAME },
|
||||||
|
{ "column_name", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_COLUMN_NAME },
|
||||||
|
{ "datatype_name", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_DATATYPE_NAME },
|
||||||
|
{ "constraint_name", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_CONSTRAINT_NAME },
|
||||||
|
{ "source_file", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SOURCE_FILE },
|
||||||
|
{ "source_line", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SOURCE_LINE },
|
||||||
|
{ "source_function", (getter)psyco_diagnostics_get_field, NULL,
|
||||||
|
NULL, (void*) PG_DIAG_SOURCE_FUNCTION },
|
||||||
|
{NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* initialization and finalization methods */
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
diagnostics_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
return type->tp_alloc(type, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
diagnostics_init(diagnosticsObject *self, PyObject *args, PyObject *kwds)
|
||||||
|
{
|
||||||
|
PyObject *err = NULL;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O", &err))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
Py_INCREF(err);
|
||||||
|
self->err = err;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
diagnostics_traverse(diagnosticsObject *self, visitproc visit, void *arg)
|
||||||
|
{
|
||||||
|
Py_VISIT(self->err);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
diagnostics_dealloc(diagnosticsObject* self)
|
||||||
|
{
|
||||||
|
Py_CLEAR(self->err);
|
||||||
|
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
diagnostics_del(PyObject* self)
|
||||||
|
{
|
||||||
|
PyObject_GC_Del(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* object type */
|
||||||
|
|
||||||
|
static const char diagnosticsType_doc[] =
|
||||||
|
"Details from a database error report.\n\n"
|
||||||
|
"The object is returned by the `~psycopg2.Error.diag` attribute of the\n"
|
||||||
|
"`!Error` object.\n"
|
||||||
|
"All the information available from the |PQresultErrorField|_ function\n"
|
||||||
|
"are exposed as attributes by the object, e.g. the `!severity` attribute\n"
|
||||||
|
"returns the `!PG_DIAG_SEVERITY` code. "
|
||||||
|
"Please refer to the `PostgreSQL documentation`__ for the meaning of all"
|
||||||
|
" the attributes.\n\n"
|
||||||
|
".. |PQresultErrorField| replace:: `!PQresultErrorField()`\n"
|
||||||
|
".. _PQresultErrorField: http://www.postgresql.org/docs/current/static/"
|
||||||
|
"libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
|
||||||
|
".. __: PQresultErrorField_\n";
|
||||||
|
|
||||||
|
PyTypeObject diagnosticsType = {
|
||||||
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
"psycopg2._psycopg.Diagnostics",
|
||||||
|
sizeof(diagnosticsObject),
|
||||||
|
0,
|
||||||
|
(destructor)diagnostics_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*/
|
||||||
|
diagnosticsType_doc, /*tp_doc*/
|
||||||
|
|
||||||
|
(traverseproc)diagnostics_traverse, /*tp_traverse*/
|
||||||
|
0, /*tp_clear*/
|
||||||
|
|
||||||
|
0, /*tp_richcompare*/
|
||||||
|
0, /*tp_weaklistoffset*/
|
||||||
|
|
||||||
|
0, /*tp_iter*/
|
||||||
|
0, /*tp_iternext*/
|
||||||
|
|
||||||
|
/* Attribute descriptor and subclassing stuff */
|
||||||
|
|
||||||
|
0, /*tp_methods*/
|
||||||
|
0, /*tp_members*/
|
||||||
|
diagnosticsObject_getsets, /*tp_getset*/
|
||||||
|
0, /*tp_base*/
|
||||||
|
0, /*tp_dict*/
|
||||||
|
|
||||||
|
0, /*tp_descr_get*/
|
||||||
|
0, /*tp_descr_set*/
|
||||||
|
0, /*tp_dictoffset*/
|
||||||
|
|
||||||
|
(initproc)diagnostics_init, /*tp_init*/
|
||||||
|
0, /*tp_alloc will be set to PyType_GenericAlloc in module init*/
|
||||||
|
diagnostics_new, /*tp_new*/
|
||||||
|
(freefunc)diagnostics_del, /*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*/
|
||||||
|
};
|
|
@ -1509,7 +1509,8 @@ 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);
|
||||||
IFCLEARPGRES(curs->pgres);
|
/* don't clear curs->pgres, because it contains detailed error
|
||||||
|
information */
|
||||||
ex = -1;
|
ex = -1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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/diagnostics.h"
|
||||||
|
|
||||||
#include "psycopg/adapter_qstring.h"
|
#include "psycopg/adapter_qstring.h"
|
||||||
#include "psycopg/adapter_binary.h"
|
#include "psycopg/adapter_binary.h"
|
||||||
|
@ -500,6 +501,7 @@ psyco_errors_init(void)
|
||||||
PyObject *base;
|
PyObject *base;
|
||||||
PyObject *str = NULL;
|
PyObject *str = NULL;
|
||||||
PyObject *descr = NULL;
|
PyObject *descr = NULL;
|
||||||
|
PyObject *diag_property = NULL;
|
||||||
int rv = -1;
|
int rv = -1;
|
||||||
|
|
||||||
#if PY_VERSION_HEX >= 0x02050000
|
#if PY_VERSION_HEX >= 0x02050000
|
||||||
|
@ -541,6 +543,12 @@ psyco_errors_init(void)
|
||||||
PyObject_SetAttrString(Error, "pgcode", Py_None);
|
PyObject_SetAttrString(Error, "pgcode", Py_None);
|
||||||
PyObject_SetAttrString(Error, "cursor", 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.
|
/* 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
|
* Don't install it on Py 2.4: it is not used by the pickle
|
||||||
|
@ -560,6 +568,7 @@ psyco_errors_init(void)
|
||||||
rv = 0;
|
rv = 0;
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
|
Py_XDECREF(diag_property);
|
||||||
Py_XDECREF(descr);
|
Py_XDECREF(descr);
|
||||||
Py_XDECREF(str);
|
Py_XDECREF(str);
|
||||||
Py_XDECREF(dict);
|
Py_XDECREF(dict);
|
||||||
|
@ -882,6 +891,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(&diagnosticsType) = &PyType_Type;
|
||||||
|
|
||||||
if (PyType_Ready(&connectionType) == -1) goto exit;
|
if (PyType_Ready(&connectionType) == -1) goto exit;
|
||||||
if (PyType_Ready(&cursorType) == -1) goto exit;
|
if (PyType_Ready(&cursorType) == -1) goto exit;
|
||||||
|
@ -898,6 +908,7 @@ 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;
|
||||||
|
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
|
||||||
|
|
||||||
#ifdef PSYCOPG_EXTENSIONS
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
Py_TYPE(&lobjectType) = &PyType_Type;
|
Py_TYPE(&lobjectType) = &PyType_Type;
|
||||||
|
@ -987,6 +998,7 @@ INIT_MODULE(_psycopg)(void)
|
||||||
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
|
||||||
PyModule_AddObject(module, "Notify", (PyObject*)&NotifyType);
|
PyModule_AddObject(module, "Notify", (PyObject*)&NotifyType);
|
||||||
PyModule_AddObject(module, "Xid", (PyObject*)&XidType);
|
PyModule_AddObject(module, "Xid", (PyObject*)&XidType);
|
||||||
|
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
|
||||||
#ifdef PSYCOPG_EXTENSIONS
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
|
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
|
||||||
#endif
|
#endif
|
||||||
|
@ -1031,6 +1043,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;
|
||||||
|
diagnosticsType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
|
||||||
#ifdef PSYCOPG_EXTENSIONS
|
#ifdef PSYCOPG_EXTENSIONS
|
||||||
lobjectType.tp_alloc = PyType_GenericAlloc;
|
lobjectType.tp_alloc = PyType_GenericAlloc;
|
||||||
|
|
3
setup.py
3
setup.py
|
@ -428,6 +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',
|
||||||
'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,7 +442,7 @@ sources = [
|
||||||
depends = [
|
depends = [
|
||||||
# headers
|
# headers
|
||||||
'config.h', 'pgtypes.h', 'psycopg.h', 'python.h',
|
'config.h', 'pgtypes.h', 'psycopg.h', 'python.h',
|
||||||
'connection.h', 'cursor.h', 'green.h', 'lobject.h',
|
'connection.h', 'cursor.h', 'diagnostics.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',
|
||||||
|
|
|
@ -22,7 +22,8 @@
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
from testutils import unittest, skip_before_python
|
from testutils import unittest, skip_before_python, skip_before_postgres
|
||||||
|
|
||||||
from testconfig import dsn
|
from testconfig import dsn
|
||||||
|
|
||||||
import psycopg2
|
import psycopg2
|
||||||
|
@ -154,6 +155,90 @@ class ExceptionsTestCase(unittest.TestCase):
|
||||||
self.assert_(e.pgerror)
|
self.assert_(e.pgerror)
|
||||||
self.assert_(e.cursor is cur)
|
self.assert_(e.cursor is cur)
|
||||||
|
|
||||||
|
def test_diagnostics_attributes(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute("select * from nonexist")
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
e = exc
|
||||||
|
|
||||||
|
diag = e.diag
|
||||||
|
self.assert_(isinstance(diag, psycopg2.extensions.Diagnostics))
|
||||||
|
for attr in [
|
||||||
|
'column_name', 'constraint_name', 'context', 'datatype_name',
|
||||||
|
'internal_position', 'internal_query', 'message_detail',
|
||||||
|
'message_hint', 'message_primary', 'schema_name', 'severity',
|
||||||
|
'source_file', 'source_function', 'source_line', 'sqlstate',
|
||||||
|
'statement_position', 'table_name', ]:
|
||||||
|
v = getattr(diag, attr)
|
||||||
|
if v is not None:
|
||||||
|
self.assert_(isinstance(v, str))
|
||||||
|
|
||||||
|
def test_diagnostics_values(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute("select * from nonexist")
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
e = exc
|
||||||
|
|
||||||
|
self.assertEqual(e.diag.sqlstate, '42P01')
|
||||||
|
self.assertEqual(e.diag.severity, 'ERROR')
|
||||||
|
self.assertEqual(e.diag.statement_position, '15')
|
||||||
|
|
||||||
|
def test_diagnostics_life(self):
|
||||||
|
import gc
|
||||||
|
from weakref import ref
|
||||||
|
|
||||||
|
def tmp():
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.execute("select * from nonexist")
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
return cur, exc
|
||||||
|
|
||||||
|
cur, e = tmp()
|
||||||
|
diag = e.diag
|
||||||
|
w = ref(cur)
|
||||||
|
|
||||||
|
del e, cur
|
||||||
|
gc.collect()
|
||||||
|
assert(w() is not None)
|
||||||
|
|
||||||
|
self.assertEqual(diag.sqlstate, '42P01')
|
||||||
|
|
||||||
|
del diag
|
||||||
|
gc.collect()
|
||||||
|
assert(w() is None)
|
||||||
|
|
||||||
|
def test_diagnostics_copy(self):
|
||||||
|
from StringIO import StringIO
|
||||||
|
f = StringIO()
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
try:
|
||||||
|
cur.copy_to(f, 'nonexist')
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
diag = exc.diag
|
||||||
|
|
||||||
|
self.assertEqual(diag.sqlstate, '42P01')
|
||||||
|
|
||||||
|
@skip_before_postgres(9, 3)
|
||||||
|
def test_9_3_diagnostics(self):
|
||||||
|
cur = self.conn.cursor()
|
||||||
|
cur.execute("""
|
||||||
|
create temp table test_exc (
|
||||||
|
data int constraint chk_eq1 check (data = 1)
|
||||||
|
)""")
|
||||||
|
try:
|
||||||
|
cur.execute("insert into test_exc values(2)")
|
||||||
|
except psycopg2.Error, exc:
|
||||||
|
e = exc
|
||||||
|
self.assertEqual(e.pgcode, '23514')
|
||||||
|
self.assertEqual(e.diag.schema_name[:7], "pg_temp")
|
||||||
|
self.assertEqual(e.diag.table_name, "test_exc")
|
||||||
|
self.assertEqual(e.diag.column_name, None)
|
||||||
|
self.assertEqual(e.diag.constraint_name, "chk_eq1")
|
||||||
|
self.assertEqual(e.diag.datatype_name, None)
|
||||||
|
|
||||||
@skip_before_python(2, 5)
|
@skip_before_python(2, 5)
|
||||||
def test_pickle(self):
|
def test_pickle(self):
|
||||||
import pickle
|
import pickle
|
||||||
|
|
Loading…
Reference in New Issue
Block a user