summaryrefslogtreecommitdiff
path: root/psycopg
diff options
context:
space:
mode:
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-15 00:58:32 +0100
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>2018-10-15 00:58:32 +0100
commitb205764fdde4549c48c27841aa17e6c7f499e808 (patch)
tree1475eb57dc854ea4a1dc93c1c6a567e6fc584e5c /psycopg
parente7227ce87b8da75fef1a3376ebb47e2bf20f6063 (diff)
parent7a5edff6c66a0410d6fecd4445980aabafc3ab4a (diff)
downloadpsycopg2-errors-module.tar.gz
Merge branch 'master' into errors-moduleerrors-module
Diffstat (limited to 'psycopg')
-rw-r--r--psycopg/column.h48
-rw-r--r--psycopg/column_type.c375
-rw-r--r--psycopg/config.h3
-rw-r--r--psycopg/connection_type.c24
-rw-r--r--psycopg/cursor_type.c58
-rw-r--r--psycopg/diagnostics_type.c16
-rw-r--r--psycopg/green.h2
-rw-r--r--psycopg/pqpath.c76
-rw-r--r--psycopg/psycopgmodule.c67
-rw-r--r--psycopg/typecast_binary.c4
10 files changed, 551 insertions, 122 deletions
diff --git a/psycopg/column.h b/psycopg/column.h
new file mode 100644
index 0000000..59e9d9c
--- /dev/null
+++ b/psycopg/column.h
@@ -0,0 +1,48 @@
+/* column.h - definition for a column in cursor.description type
+ *
+ * Copyright (C) 2018 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_COLUMN_H
+#define PSYCOPG_COLUMN_H 1
+
+extern HIDDEN PyTypeObject columnType;
+
+typedef struct {
+ PyObject_HEAD
+
+ PyObject *name;
+ PyObject *type_code;
+ PyObject *display_size;
+ PyObject *internal_size;
+ PyObject *precision;
+ PyObject *scale;
+ PyObject *null_ok;
+
+ /* Extensions to the DBAPI */
+ PyObject *table_oid;
+ PyObject *table_column;
+
+} columnObject;
+
+#endif /* PSYCOPG_COLUMN_H */
diff --git a/psycopg/column_type.c b/psycopg/column_type.c
new file mode 100644
index 0000000..8ee7d4c
--- /dev/null
+++ b/psycopg/column_type.c
@@ -0,0 +1,375 @@
+/* column_type.c - python interface to cursor.description objects
+ *
+ * Copyright (C) 2018 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/column.h"
+
+
+static const char column_doc[] =
+ "Description of a column returned by a query.\n\n"
+ "The DBAPI demands this object to be a 7-items sequence. This object\n"
+ "respects this interface, but adds names for the exposed attributes\n"
+ "and adds attribute not requested by the DBAPI.";
+
+static const char name_doc[] =
+ "The name of the column returned.";
+
+static const char type_code_doc[] =
+ "The PostgreSQL OID of the column.\n\n"
+ "You can use the pg_type system table to get more informations about the\n"
+ "type. This is the value used by Psycopg to decide what Python type use\n"
+ "to represent the value";
+
+static const char display_size_doc[] =
+ "The actual length of the column in bytes.\n\n"
+ "Obtaining this value is computationally intensive, so it is always None\n"
+ "unless the PSYCOPG_DISPLAY_SIZE parameter is set at compile time.";
+
+static const char internal_size_doc[] =
+ "The size in bytes of the column associated to this column on the server.\n\n"
+ "Set to a negative value for variable-size types.";
+
+static const char precision_doc[] =
+ "Total number of significant digits in columns of type NUMERIC.\n\n"
+ "None for other types.";
+
+static const char scale_doc[] =
+ "Count of decimal digits in the fractional part in columns of type NUMERIC.\n\n"
+ "None for other types.";
+
+static const char null_ok_doc[] =
+ "Always none.";
+
+static const char table_oid_doc[] =
+ "The OID of the table from which the column was fetched.\n\n"
+ "None if not available";
+
+static const char table_column_doc[] =
+ "The number (within its table) of the column making up the result\n\n"
+ "None if not available. Note that PostgreSQL column numbers start at 1";
+
+
+static PyMemberDef column_members[] = {
+ { "name", T_OBJECT, offsetof(columnObject, name), READONLY, (char *)name_doc },
+ { "type_code", T_OBJECT, offsetof(columnObject, type_code), READONLY, (char *)type_code_doc },
+ { "display_size", T_OBJECT, offsetof(columnObject, display_size), READONLY, (char *)display_size_doc },
+ { "internal_size", T_OBJECT, offsetof(columnObject, internal_size), READONLY, (char *)internal_size_doc },
+ { "precision", T_OBJECT, offsetof(columnObject, precision), READONLY, (char *)precision_doc },
+ { "scale", T_OBJECT, offsetof(columnObject, scale), READONLY, (char *)scale_doc },
+ { "null_ok", T_OBJECT, offsetof(columnObject, null_ok), READONLY, (char *)null_ok_doc },
+ { "table_oid", T_OBJECT, offsetof(columnObject, table_oid), READONLY, (char *)table_oid_doc },
+ { "table_column", T_OBJECT, offsetof(columnObject, table_column), READONLY, (char *)table_column_doc },
+ { NULL }
+};
+
+
+static PyObject *
+column_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+{
+ return type->tp_alloc(type, 0);
+}
+
+
+static int
+column_init(columnObject *self, PyObject *args, PyObject *kwargs)
+{
+ static char *kwlist[] = {
+ "name", "type_code", "display_size", "internal_size",
+ "precision", "scale", "null_ok", "table_oid", "table_column", NULL};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|OOOOOOOOO", kwlist,
+ &self->name, &self->type_code, &self->display_size,
+ &self->internal_size, &self->precision, &self->scale,
+ &self->null_ok, &self->table_oid, &self->table_column)) {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void
+column_dealloc(columnObject *self)
+{
+ Py_CLEAR(self->name);
+ Py_CLEAR(self->type_code);
+ Py_CLEAR(self->display_size);
+ Py_CLEAR(self->internal_size);
+ Py_CLEAR(self->precision);
+ Py_CLEAR(self->scale);
+ Py_CLEAR(self->null_ok);
+ Py_CLEAR(self->table_oid);
+ Py_CLEAR(self->table_column);
+
+ Py_TYPE(self)->tp_free((PyObject *)self);
+}
+
+
+static PyObject*
+column_repr(columnObject *self)
+{
+ PyObject *rv = NULL;
+ PyObject *format = NULL;
+ PyObject *args = NULL;
+ PyObject *tmp;
+
+ if (!(format = Text_FromUTF8("Column(name=%r, type_code=%r)"))) {
+ goto exit;
+ }
+
+ if (!(args = PyTuple_New(2))) { goto exit; }
+
+ tmp = self->name ? self->name : Py_None;
+ Py_INCREF(tmp);
+ PyTuple_SET_ITEM(args, 0, tmp);
+
+ tmp = self->type_code ? self->type_code : Py_None;
+ Py_INCREF(tmp);
+ PyTuple_SET_ITEM(args, 1, tmp);
+
+ rv = Text_Format(format, args);
+
+exit:
+ Py_XDECREF(args);
+ Py_XDECREF(format);
+
+ return rv;
+}
+
+
+static PyObject *
+column_richcompare(columnObject *self, PyObject *other, int op)
+{
+ PyObject *rv = NULL;
+ PyObject *tself = NULL;
+
+ if (!(tself = PyObject_CallFunctionObjArgs(
+ (PyObject *)&PyTuple_Type, (PyObject *)self, NULL))) {
+ goto exit;
+ }
+
+ rv = PyObject_RichCompare(tself, other, op);
+
+exit:
+ Py_XDECREF(tself);
+ return rv;
+}
+
+
+/* column description can be accessed as a 7 items tuple for DBAPI compatibility */
+
+static Py_ssize_t
+column_len(columnObject *self)
+{
+ return 7;
+}
+
+
+static PyObject *
+column_getitem(columnObject *self, Py_ssize_t item)
+{
+ PyObject *rv = NULL;
+
+ if (item < 0)
+ item += 7;
+
+ switch (item) {
+ case 0:
+ rv = self->name;
+ break;
+ case 1:
+ rv = self->type_code;
+ break;
+ case 2:
+ rv = self->display_size;
+ break;
+ case 3:
+ rv = self->internal_size;
+ break;
+ case 4:
+ rv = self->precision;
+ break;
+ case 5:
+ rv = self->scale;
+ break;
+ case 6:
+ rv = self->null_ok;
+ break;
+ default:
+ PyErr_SetString(PyExc_IndexError, "index out of range");
+ return NULL;
+ }
+
+ if (!rv) {
+ rv = Py_None;
+ }
+
+ Py_INCREF(rv);
+ return rv;
+}
+
+
+static PySequenceMethods column_sequence = {
+ (lenfunc)column_len, /* sq_length */
+ 0, /* sq_concat */
+ 0, /* sq_repeat */
+ (ssizeargfunc)column_getitem, /* sq_item */
+ 0, /* sq_slice */
+ 0, /* sq_ass_item */
+ 0, /* sq_ass_slice */
+ 0, /* sq_contains */
+ 0, /* sq_inplace_concat */
+ 0, /* sq_inplace_repeat */
+};
+
+
+static PyObject *
+column_getstate(columnObject *self)
+{
+ return PyObject_CallFunctionObjArgs(
+ (PyObject *)&PyTuple_Type, (PyObject *)self, NULL);
+}
+
+
+PyObject *
+column_setstate(columnObject *self, PyObject *state)
+{
+ Py_ssize_t size;
+ PyObject *rv = NULL;
+
+ if (state == Py_None) {
+ goto exit;
+ }
+ if (!PyTuple_Check(state)) {
+ PyErr_SetString(PyExc_TypeError, "state is not a tuple");
+ goto error;
+ }
+
+ size = PyTuple_GET_SIZE(state);
+
+ if (size > 0) {
+ Py_CLEAR(self->name);
+ self->name = PyTuple_GET_ITEM(state, 0);
+ Py_INCREF(self->name);
+ }
+ if (size > 1) {
+ Py_CLEAR(self->type_code);
+ self->type_code = PyTuple_GET_ITEM(state, 1);
+ Py_INCREF(self->type_code);
+ }
+ if (size > 2) {
+ Py_CLEAR(self->display_size);
+ self->display_size = PyTuple_GET_ITEM(state, 2);
+ Py_INCREF(self->display_size);
+ }
+ if (size > 3) {
+ Py_CLEAR(self->internal_size);
+ self->internal_size = PyTuple_GET_ITEM(state, 3);
+ Py_INCREF(self->internal_size);
+ }
+ if (size > 4) {
+ Py_CLEAR(self->precision);
+ self->precision = PyTuple_GET_ITEM(state, 4);
+ Py_INCREF(self->precision);
+ }
+ if (size > 5) {
+ Py_CLEAR(self->scale);
+ self->scale = PyTuple_GET_ITEM(state, 5);
+ Py_INCREF(self->scale);
+ }
+ if (size > 6) {
+ Py_CLEAR(self->null_ok);
+ self->null_ok = PyTuple_GET_ITEM(state, 6);
+ Py_INCREF(self->null_ok);
+ }
+ if (size > 7) {
+ Py_CLEAR(self->table_oid);
+ self->table_oid = PyTuple_GET_ITEM(state, 7);
+ Py_INCREF(self->table_oid);
+ }
+ if (size > 8) {
+ Py_CLEAR(self->table_column);
+ self->table_column = PyTuple_GET_ITEM(state, 8);
+ Py_INCREF(self->table_column);
+ }
+
+exit:
+ rv = Py_None;
+ Py_INCREF(rv);
+
+error:
+ return rv;
+}
+
+
+static PyMethodDef column_methods[] = {
+ /* Make Column picklable. */
+ {"__getstate__", (PyCFunction)column_getstate, METH_NOARGS },
+ {"__setstate__", (PyCFunction)column_setstate, METH_O },
+ {NULL}
+};
+
+
+PyTypeObject columnType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "psycopg2.extensions.Column",
+ sizeof(columnObject), 0,
+ (destructor)column_dealloc, /* tp_dealloc */
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ (reprfunc)column_repr, /*tp_repr*/
+ 0, /*tp_as_number*/
+ &column_sequence, /*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, /*tp_flags*/
+ column_doc, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ (richcmpfunc)column_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ column_methods, /*tp_methods*/
+ column_members, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ (initproc)column_init, /*tp_init*/
+ 0, /*tp_alloc*/
+ column_new, /*tp_new*/
+};
diff --git a/psycopg/config.h b/psycopg/config.h
index a96864f..6b33c2c 100644
--- a/psycopg/config.h
+++ b/psycopg/config.h
@@ -154,8 +154,7 @@ typedef unsigned __int64 uint64_t;
#endif
/* what's this, we have no round function either? */
-#if (defined(__FreeBSD__) && __FreeBSD_version < 503000) \
- || (defined(_WIN32) && !defined(__GNUC__)) \
+#if (defined(_WIN32) && !defined(__GNUC__)) \
|| (defined(sun) || defined(__sun__)) \
&& (defined(__SunOS_5_8) || defined(__SunOS_5_9))
diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c
index 6a66d48..6ac0226 100644
--- a/psycopg/connection_type.c
+++ b/psycopg/connection_type.c
@@ -757,6 +757,7 @@ psyco_conn_readonly_get(connectionObject *self)
break;
}
+ Py_XINCREF(rv);
return rv;
}
@@ -803,6 +804,7 @@ psyco_conn_deferrable_get(connectionObject *self)
break;
}
+ Py_XINCREF(rv);
return rv;
}
@@ -992,6 +994,25 @@ psyco_conn_get_backend_pid(connectionObject *self)
return PyInt_FromLong((long)PQbackendPID(self->pgconn));
}
+/* get the current host */
+
+#define psyco_conn_host_get_doc \
+"host -- Get the host name."
+
+static PyObject *
+psyco_conn_host_get(connectionObject *self)
+{
+ const char *val = NULL;
+
+ EXC_IF_CONN_CLOSED(self);
+
+ val = PQhost(self->pgconn);
+ if (!val) {
+ Py_RETURN_NONE;
+ }
+ return conn_text_from_chars(self, val);
+}
+
/* reset the currect connection */
#define psyco_conn_reset_doc \
@@ -1243,6 +1264,9 @@ static struct PyGetSetDef connectionObject_getsets[] = {
(getter)psyco_conn_deferrable_get,
(setter)psyco_conn_deferrable_set,
psyco_conn_deferrable_doc },
+ { "host",
+ (getter)psyco_conn_host_get, NULL,
+ psyco_conn_host_get_doc },
{NULL}
};
#undef EXCEPTION_GETTER
diff --git a/psycopg/cursor_type.c b/psycopg/cursor_type.c
index d73bc3a..5920c12 100644
--- a/psycopg/cursor_type.c
+++ b/psycopg/cursor_type.c
@@ -49,21 +49,21 @@
static PyObject *
psyco_curs_close(cursorObject *self)
{
+ PyObject *rv = NULL;
+ char *lname = NULL;
+
EXC_IF_ASYNC_IN_PROGRESS(self, close);
if (self->closed) {
+ rv = Py_None;
+ Py_INCREF(rv);
goto exit;
}
if (self->qname != NULL) {
- char buffer[128];
+ char buffer[256];
PGTransactionStatusType status;
- if (!self->query) {
- Dprintf("skipping named cursor close because unused");
- goto close;
- }
-
if (self->conn) {
status = PQtransactionStatus(self->conn->pgconn);
}
@@ -77,17 +77,45 @@ psyco_curs_close(cursorObject *self)
goto close;
}
+ /* We should close a server-side cursor only if exists, or we get an
+ * error (#716). If we execute()d the cursor should exist alright, but
+ * if we didn't there is still the expectation that the cursor is
+ * closed (#746).
+ *
+ * So if we didn't execute() check for the cursor existence before
+ * closing it (the view exists since PG 8.2 according to docs).
+ */
+ if (!self->query && self->conn->server_version >= 80200) {
+ if (!(lname = psycopg_escape_string(
+ self->conn, self->name, -1, NULL, NULL))) {
+ goto exit;
+ }
+ PyOS_snprintf(buffer, sizeof(buffer),
+ "SELECT 1 FROM pg_catalog.pg_cursors where name = %s",
+ lname);
+ if (pq_execute(self, buffer, 0, 0, 1) == -1) { goto exit; }
+
+ if (self->rowcount == 0) {
+ Dprintf("skipping named cursor close because not existing");
+ goto close;
+ }
+ }
+
EXC_IF_NO_MARK(self);
- PyOS_snprintf(buffer, 127, "CLOSE %s", self->qname);
- if (pq_execute(self, buffer, 0, 0, 1) == -1) return NULL;
+ PyOS_snprintf(buffer, sizeof(buffer), "CLOSE %s", self->qname);
+ if (pq_execute(self, buffer, 0, 0, 1) == -1) { goto exit; }
}
close:
self->closed = 1;
Dprintf("psyco_curs_close: cursor at %p closed", self);
+ rv = Py_None;
+ Py_INCREF(rv);
+
exit:
- Py_RETURN_NONE;
+ PyMem_Free(lname);
+ return rv;
}
@@ -742,7 +770,7 @@ psyco_curs_fetchone(cursorObject *self)
EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchone);
EXC_IF_TPC_PREPARED(self->conn, fetchone);
- PyOS_snprintf(buffer, 127, "FETCH FORWARD 1 FROM %s", self->qname);
+ PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD 1 FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL;
}
@@ -791,7 +819,7 @@ psyco_curs_next_named(cursorObject *self)
if (self->row >= self->rowcount) {
char buffer[128];
- PyOS_snprintf(buffer, 127, "FETCH FORWARD %ld FROM %s",
+ PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD %ld FROM %s",
self->itersize, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL;
@@ -860,7 +888,7 @@ psyco_curs_fetchmany(cursorObject *self, PyObject *args, PyObject *kwords)
EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchmany);
EXC_IF_TPC_PREPARED(self->conn, fetchone);
- PyOS_snprintf(buffer, 127, "FETCH FORWARD %d FROM %s",
+ PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD %d FROM %s",
(int)size, self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; }
@@ -936,7 +964,7 @@ psyco_curs_fetchall(cursorObject *self)
EXC_IF_NO_MARK(self);
EXC_IF_ASYNC_IN_PROGRESS(self, fetchall);
EXC_IF_TPC_PREPARED(self->conn, fetchall);
- PyOS_snprintf(buffer, 127, "FETCH FORWARD ALL FROM %s", self->qname);
+ PyOS_snprintf(buffer, sizeof(buffer), "FETCH FORWARD ALL FROM %s", self->qname);
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) { goto exit; }
if (_psyco_curs_prefetch(self) < 0) { goto exit; }
}
@@ -1233,11 +1261,11 @@ psyco_curs_scroll(cursorObject *self, PyObject *args, PyObject *kwargs)
EXC_IF_TPC_PREPARED(self->conn, scroll);
if (strcmp(mode, "absolute") == 0) {
- PyOS_snprintf(buffer, 127, "MOVE ABSOLUTE %d FROM %s",
+ PyOS_snprintf(buffer, sizeof(buffer), "MOVE ABSOLUTE %d FROM %s",
value, self->qname);
}
else {
- PyOS_snprintf(buffer, 127, "MOVE %d FROM %s", value, self->qname);
+ PyOS_snprintf(buffer, sizeof(buffer), "MOVE %d FROM %s", value, self->qname);
}
if (pq_execute(self, buffer, 0, 0, self->withhold) == -1) return NULL;
if (_psyco_curs_prefetch(self) < 0) return NULL;
diff --git a/psycopg/diagnostics_type.c b/psycopg/diagnostics_type.c
index 0af6526..13e545f 100644
--- a/psycopg/diagnostics_type.c
+++ b/psycopg/diagnostics_type.c
@@ -29,8 +29,11 @@
#include "psycopg/diagnostics.h"
#include "psycopg/error.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. */
+
+/* These constants are defined in src/include/postgres_ext.h but some may not
+ * be available with the libpq we currently support at compile time. */
+
+/* Available from PG 9.3 */
#ifndef PG_DIAG_SCHEMA_NAME
#define PG_DIAG_SCHEMA_NAME 's'
#endif
@@ -47,6 +50,11 @@
#define PG_DIAG_CONSTRAINT_NAME 'n'
#endif
+/* Available from PG 9.6 */
+#ifndef PG_DIAG_SEVERITY_NONLOCALIZED
+#define PG_DIAG_SEVERITY_NONLOCALIZED 'V'
+#endif
+
/* Retrieve an error string from the exception's cursor.
*
@@ -70,6 +78,8 @@ psyco_diagnostics_get_field(diagnosticsObject *self, void *closure)
static struct PyGetSetDef diagnosticsObject_getsets[] = {
{ "severity", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SEVERITY },
+ { "severity_nonlocalized", (getter)psyco_diagnostics_get_field, NULL,
+ NULL, (void*) PG_DIAG_SEVERITY_NONLOCALIZED },
{ "sqlstate", (getter)psyco_diagnostics_get_field, NULL,
NULL, (void*) PG_DIAG_SQLSTATE },
{ "message_primary", (getter)psyco_diagnostics_get_field, NULL,
@@ -152,7 +162,7 @@ static const char diagnosticsType_doc[] =
"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/"
+ ".. _PQresultErrorField: https://www.postgresql.org/docs/current/static/"
"libpq-exec.html#LIBPQ-PQRESULTERRORFIELD\n"
".. __: PQresultErrorField_\n";
diff --git a/psycopg/green.h b/psycopg/green.h
index 4057ff7..7d95f9c 100644
--- a/psycopg/green.h
+++ b/psycopg/green.h
@@ -48,7 +48,7 @@ extern "C" {
"See `~psycopg2.extras.wait_select()` for an example of a wait callback\n" \
"implementation.\n" \
"\n" \
-".. _Eventlet: http://eventlet.net/\n" \
+".. _Eventlet: https://eventlet.net/\n" \
".. _gevent: http://www.gevent.org/\n"
HIDDEN PyObject *psyco_set_wait_callback(PyObject *self, PyObject *obj);
diff --git a/psycopg/pqpath.c b/psycopg/pqpath.c
index 75ab268..04a34a2 100644
--- a/psycopg/pqpath.c
+++ b/psycopg/pqpath.c
@@ -41,6 +41,7 @@
#include "psycopg/typecast.h"
#include "psycopg/pgtypes.h"
#include "psycopg/error.h"
+#include "psycopg/column.h"
#include "psycopg/libpq_support.h"
#include "libpq-fe.h"
@@ -108,7 +109,7 @@ exit:
/* Returns the Python exception corresponding to an SQLSTATE error
code. A list of error codes can be found at:
- http://www.postgresql.org/docs/current/static/errcodes-appendix.html */
+ https://www.postgresql.org/docs/current/static/errcodes-appendix.html */
BORROWED static PyObject *
exception_from_sqlstate(const char *sqlstate)
{
@@ -1150,12 +1151,13 @@ pq_send_query(connectionObject *conn, const char *query)
* The function will block only if a command is active and the
* necessary response data has not yet been read by PQconsumeInput.
*
- * The result should be disposed using PQclear()
+ * The result should be disposed of using PQclear()
*/
PGresult *
pq_get_last_result(connectionObject *conn)
{
PGresult *result = NULL, *res;
+ ExecStatusType status;
/* Read until PQgetResult gives a NULL */
while (NULL != (res = PQgetResult(conn->pgconn))) {
@@ -1168,11 +1170,15 @@ pq_get_last_result(connectionObject *conn)
PQclear(result);
}
result = res;
+ status = PQresultStatus(result);
+ Dprintf("pq_get_last_result: got result %s", PQresStatus(status));
- /* After entering copy both mode, libpq will make a phony
+ /* After entering copy mode, libpq will make a phony
* PGresult for us every time we query for it, so we need to
* break out of this endless loop. */
- if (PQresultStatus(result) == PGRES_COPY_BOTH) {
+ if (status == PGRES_COPY_BOTH
+ || status == PGRES_COPY_OUT
+ || status == PGRES_COPY_IN) {
break;
}
}
@@ -1245,12 +1251,17 @@ _pq_fetch_tuples(cursorObject *curs)
Oid ftype = PQftype(curs->pgres, i);
int fsize = PQfsize(curs->pgres, i);
int fmod = PQfmod(curs->pgres, i);
+ Oid ftable = PQftable(curs->pgres, i);
+ int ftablecol = PQftablecol(curs->pgres, i);
- PyObject *dtitem = NULL;
+ columnObject *column = NULL;
PyObject *type = NULL;
PyObject *cast = NULL;
- if (!(dtitem = PyTuple_New(7))) { goto exit; }
+ if (!(column = (columnObject *)PyObject_CallObject(
+ (PyObject *)&columnType, NULL))) {
+ goto exit;
+ }
/* fill the right cast function by accessing three different dictionaries:
- the per-cursor dictionary, if available (can be NULL or None)
@@ -1287,20 +1298,16 @@ _pq_fetch_tuples(cursorObject *curs)
curs->conn, PQfname(curs->pgres, i)))) {
goto err_for;
}
- PyTuple_SET_ITEM(dtitem, 0, tmp);
+ column->name = tmp;
}
- PyTuple_SET_ITEM(dtitem, 1, type);
+ column->type_code = type;
type = NULL;
/* 2/ display size is the maximum size of this field result tuples. */
if (dsize && dsize[i] >= 0) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(dsize[i]))) { goto err_for; }
- PyTuple_SET_ITEM(dtitem, 2, tmp);
- }
- else {
- Py_INCREF(Py_None);
- PyTuple_SET_ITEM(dtitem, 2, Py_None);
+ column->display_size = tmp;
}
/* 3/ size on the backend */
@@ -1309,18 +1316,18 @@ _pq_fetch_tuples(cursorObject *curs)
if (ftype == NUMERICOID) {
PyObject *tmp;
if (!(tmp = PyInt_FromLong((fmod >> 16)))) { goto err_for; }
- PyTuple_SET_ITEM(dtitem, 3, tmp);
+ column->internal_size = tmp;
}
else { /* If variable length record, return maximum size */
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fmod))) { goto err_for; }
- PyTuple_SET_ITEM(dtitem, 3, tmp);
+ column->internal_size = tmp;
}
}
else {
PyObject *tmp;
if (!(tmp = PyInt_FromLong(fsize))) { goto err_for; }
- PyTuple_SET_ITEM(dtitem, 3, tmp);
+ column->internal_size = tmp;
}
/* 4,5/ scale and precision */
@@ -1330,40 +1337,35 @@ _pq_fetch_tuples(cursorObject *curs)
if (!(tmp = PyInt_FromLong((fmod >> 16) & 0xFFFF))) {
goto err_for;
}
- PyTuple_SET_ITEM(dtitem, 4, tmp);
+ column->precision = tmp;
if (!(tmp = PyInt_FromLong(fmod & 0xFFFF))) {
- PyTuple_SET_ITEM(dtitem, 5, tmp);
+ goto err_for;
}
- PyTuple_SET_ITEM(dtitem, 5, tmp);
- }
- else {
- Py_INCREF(Py_None);
- PyTuple_SET_ITEM(dtitem, 4, Py_None);
- Py_INCREF(Py_None);
- PyTuple_SET_ITEM(dtitem, 5, Py_None);
+ column->scale = tmp;
}
- /* 6/ FIXME: null_ok??? */
- Py_INCREF(Py_None);
- PyTuple_SET_ITEM(dtitem, 6, Py_None);
+ /* table_oid, table_column */
+ if (ftable != InvalidOid) {
+ PyObject *tmp;
+ if (!(tmp = PyInt_FromLong((long)ftable))) { goto err_for; }
+ column->table_oid = tmp;
+ }
- /* Convert into a namedtuple if available */
- if (Py_None != psyco_DescriptionType) {
- PyObject *tmp = dtitem;
- dtitem = PyObject_CallObject(psyco_DescriptionType, tmp);
- Py_DECREF(tmp);
- if (NULL == dtitem) { goto err_for; }
+ if (ftablecol > 0) {
+ PyObject *tmp;
+ if (!(tmp = PyInt_FromLong((long)ftablecol))) { goto err_for; }
+ column->table_column = tmp;
}
- PyTuple_SET_ITEM(description, i, dtitem);
- dtitem = NULL;
+ PyTuple_SET_ITEM(description, i, (PyObject *)column);
+ column = NULL;
continue;
err_for:
Py_XDECREF(type);
- Py_XDECREF(dtitem);
+ Py_XDECREF(column);
goto exit;
}
diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c
index 23e648d..ff04b25 100644
--- a/psycopg/psycopgmodule.c
+++ b/psycopg/psycopgmodule.c
@@ -32,6 +32,7 @@
#include "psycopg/replication_cursor.h"
#include "psycopg/replication_message.h"
#include "psycopg/green.h"
+#include "psycopg/column.h"
#include "psycopg/lobject.h"
#include "psycopg/notify.h"
#include "psycopg/xid.h"
@@ -69,9 +70,6 @@ HIDDEN int psycopg_debug_enabled = 0;
/* Python representation of SQL NULL */
HIDDEN PyObject *psyco_null = NULL;
-/* The type of the cursor.description items */
-HIDDEN PyObject *psyco_DescriptionType = NULL;
-
/* macro trick to stringify a macro expansion */
#define xstr(s) str(s)
#define str(s) #s
@@ -839,63 +837,6 @@ psyco_GetDecimalType(void)
}
-/* Create a namedtuple for cursor.description items
- *
- * Return None in case of expected errors (e.g. namedtuples not available)
- * NULL in case of errors to propagate.
- */
-static PyObject *
-psyco_make_description_type(void)
-{
- PyObject *coll = NULL;
- PyObject *nt = NULL;
- PyTypeObject *t = NULL;
- PyObject *s = NULL;
- PyObject *rv = NULL;
-
- /* Try to import collections.namedtuple */
- if (!(coll = PyImport_ImportModule("collections"))) {
- Dprintf("psyco_make_description_type: collections import failed");
- goto error;
- }
- if (!(nt = PyObject_GetAttrString(coll, "namedtuple"))) {
- Dprintf("psyco_make_description_type: no collections.namedtuple");
- goto error;
- }
-
- /* Build the namedtuple */
- if(!(t = (PyTypeObject *)PyObject_CallFunction(nt, "ss", "Column",
- "name type_code display_size internal_size precision scale null_ok"))) {
- goto exit;
- }
-
- /* Export the tuple on the extensions module
- * Required to guarantee picklability on Py > 3.3 (see Python issue 21374)
- * for previous Py version the module is psycopg2 anyway but for consistency
- * we'd rather expose it from the extensions module. */
- if (!(s = Text_FromUTF8("psycopg2.extensions"))) { goto exit; }
- if (0 > PyDict_SetItemString(t->tp_dict, "__module__", s)) { goto exit; }
-
- rv = (PyObject *)t;
- t = NULL;
-
-exit:
- Py_XDECREF(coll);
- Py_XDECREF(nt);
- Py_XDECREF((PyObject *)t);
- Py_XDECREF(s);
-
- return rv;
-
-error:
- /* controlled error: we will fall back to regular tuples. Return None. */
- PyErr_Clear();
- rv = Py_None;
- Py_INCREF(rv);
- goto exit;
-}
-
-
/** method table and module initialization **/
static PyMethodDef psycopgMethods[] = {
@@ -1041,6 +982,9 @@ INIT_MODULE(_psycopg)(void)
Py_TYPE(&chunkType) = &PyType_Type;
if (PyType_Ready(&chunkType) == -1) goto exit;
+ Py_TYPE(&columnType) = &PyType_Type;
+ if (PyType_Ready(&columnType) == -1) goto exit;
+
Py_TYPE(&notifyType) = &PyType_Type;
if (PyType_Ready(&notifyType) == -1) goto exit;
@@ -1119,7 +1063,6 @@ INIT_MODULE(_psycopg)(void)
if (!(psycoEncodings = PyDict_New())) { goto exit; }
if (0 != psyco_encodings_fill(psycoEncodings)) { goto exit; }
psyco_null = Bytes_FromString("NULL");
- if (!(psyco_DescriptionType = psyco_make_description_type())) { goto exit; }
/* set some module's parameters */
PyModule_AddStringConstant(module, "__version__", xstr(PSYCOPG_VERSION));
@@ -1138,6 +1081,7 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "ReplicationCursor", (PyObject*)&replicationCursorType);
PyModule_AddObject(module, "ReplicationMessage", (PyObject*)&replicationMessageType);
PyModule_AddObject(module, "ISQLQuote", (PyObject*)&isqlquoteType);
+ PyModule_AddObject(module, "Column", (PyObject*)&columnType);
PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
@@ -1150,7 +1094,6 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "List", (PyObject*)&listType);
PyModule_AddObject(module, "QuotedString", (PyObject*)&qstringType);
PyModule_AddObject(module, "lobject", (PyObject*)&lobjectType);
- PyModule_AddObject(module, "Column", psyco_DescriptionType);
/* encodings dictionary in module dictionary */
PyModule_AddObject(module, "encodings", psycoEncodings);
diff --git a/psycopg/typecast_binary.c b/psycopg/typecast_binary.c
index e4839da..8226d3f 100644
--- a/psycopg/typecast_binary.c
+++ b/psycopg/typecast_binary.c
@@ -212,7 +212,7 @@ static const char hex_lut[128] = {
/* Parse a bytea output buffer encoded in 'hex' format.
*
* the format is described in
- * http://www.postgresql.org/docs/current/static/datatype-binary.html
+ * https://www.postgresql.org/docs/current/static/datatype-binary.html
*
* Parse the buffer in 'bufin', whose length is 'sizein'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.
@@ -262,7 +262,7 @@ exit:
/* Parse a bytea output buffer encoded in 'escape' format.
*
* the format is described in
- * http://www.postgresql.org/docs/current/static/datatype-binary.html
+ * https://www.postgresql.org/docs/current/static/datatype-binary.html
*
* Parse the buffer in 'bufin', whose length is 'sizein'.
* Return a new buffer allocated by PyMem_Malloc and set 'sizeout' to its size.