Merge branch 'connection-info'

This commit is contained in:
Daniele Varrazzo 2018-10-15 01:24:00 +01:00
commit 0236c68da9
24 changed files with 1060 additions and 203 deletions

3
NEWS
View File

@ -12,7 +12,8 @@ New features:
- Added `~psycopg2.extensions.Column.table_oid` and
`~psycopg2.extensions.Column.table_column` attributes on `cursor.description`
items (:ticket:`#661`).
- Added `connection.host` property (:ticket:`#726`).
- Added `connection.info` object to retrieve various PostgreSQL connection
information (:ticket:`#726`).
- `~psycopg2.sql.Identifier` can represent qualified names in SQL composition
(:ticket:`#732`).
- `!str()` on `~psycopg2.extras.Range` produces a human-readable representation

View File

@ -342,6 +342,9 @@ The ``connection`` class
obscured.
.. rubric:: Transaction control methods and attributes.
.. index::
pair: Transaction; Autocommit
pair: Transaction; Isolation level
@ -600,62 +603,16 @@ The ``connection`` class
.. index::
pair: Backend; Host
pair: Connection; Info
.. attribute:: host
.. attribute:: info
The server host name of the active connection.
This can be a host name, an IP address, or a directory path if the
connection is via Unix socket. (The path case can be distinguished
because it will always be an absolute path, beginning with ``/``.)
.. seealso:: libpq docs for `PQhost()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQHOST
A `~psycopg2.extensions.ConnectionInfo` object exposing information
about the native libpq connection.
.. versionadded:: 2.8.0
.. index::
pair: Backend; PID
.. method:: get_backend_pid()
Returns the process ID (PID) of the backend server process handling
this connection.
Note that the PID belongs to a process executing on the database
server host, not the local host!
.. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8
.. index::
pair: Server; Parameters
.. method:: get_parameter_status(parameter)
Look up a current parameter setting of the server.
Potential values for ``parameter`` are: ``server_version``,
``server_encoding``, ``client_encoding``, ``is_superuser``,
``session_authorization``, ``DateStyle``, ``TimeZone``,
``integer_datetimes``, and ``standard_conforming_strings``.
If server did not report requested parameter, return `!None`.
.. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12
.. index::
pair: Connection; Parameters
@ -679,56 +636,6 @@ The ``connection`` class
.. versionadded:: 2.7
.. index::
pair: Transaction; Status
.. method:: get_transaction_status()
Return the current session transaction status as an integer. Symbolic
constants for the values are defined in the module
`psycopg2.extensions`: see :ref:`transaction-status-constants`
for the available values.
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index::
pair: Protocol; Version
.. attribute:: protocol_version
A read-only integer representing frontend/backend protocol being used.
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12
.. index::
pair: Server; Version
.. attribute:: server_version
A read-only integer representing the backend version.
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
For example, version 8.1.5 will be returned as ``80105``.
.. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12
.. index::
pair: Connection; Status
@ -786,6 +693,7 @@ The ``connection`` class
support.
.. rubric:: Methods related to asynchronous support.
.. versionadded:: 2.2.0
@ -832,6 +740,119 @@ The ``connection`` class
Return `!True` if the connection is executing an asynchronous operation.
.. rubric:: informative methods of the native connection
.. note::
These methods are better accessed using the `~connection.info`
attributes and may be dropped in future versions.
.. index::
pair: Transaction; Status
.. method:: get_transaction_status()
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.transaction_status`.
Return the current session transaction status as an integer. Symbolic
constants for the values are defined in the module
`psycopg2.extensions`: see :ref:`transaction-status-constants`
for the available values.
.. seealso:: libpq docs for `PQtransactionStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQTRANSACTIONSTATUS
.. index::
pair: Protocol; Version
.. attribute:: protocol_version
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.protocol_version`.
A read-only integer representing frontend/backend protocol being used.
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. seealso:: libpq docs for `PQprotocolVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPROTOCOLVERSION
.. versionadded:: 2.0.12
.. index::
pair: Server; Version
.. attribute:: server_version
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.server_version`.
A read-only integer representing the backend version.
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
For example, version 8.1.5 will be returned as ``80105``.
.. seealso:: libpq docs for `PQserverVersion()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQSERVERVERSION
.. versionadded:: 2.0.12
.. index::
pair: Backend; PID
.. method:: get_backend_pid()
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.backend_pid`.
Returns the process ID (PID) of the backend server process handling
this connection.
Note that the PID belongs to a process executing on the database
server host, not the local host!
.. seealso:: libpq docs for `PQbackendPID()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQBACKENDPID
.. versionadded:: 2.0.8
.. index::
pair: Server; Parameters
.. method:: get_parameter_status(parameter)
Also available as `~connection.info`\ `!.`\
`~psycopg2.extensions.ConnectionInfo.parameter_status()`.
Look up a current parameter setting of the server.
Potential values for ``parameter`` are: ``server_version``,
``server_encoding``, ``client_encoding``, ``is_superuser``,
``session_authorization``, ``DateStyle``, ``TimeZone``,
``integer_datetimes``, and ``standard_conforming_strings``.
If server did not report requested parameter, return `!None`.
.. seealso:: libpq docs for `PQparameterStatus()`__ for details.
.. __: https://www.postgresql.org/docs/current/static/libpq-status.html#LIBPQ-PQPARAMETERSTATUS
.. versionadded:: 2.0.12
.. testcode::
:hide:

View File

@ -154,7 +154,46 @@ introspection etc.
Close the object and remove it from the database.
.. class:: Column
.. autoclass:: ConnectionInfo(connection)
.. versionadded:: 2.8
.. autoattribute:: dbname
.. autoattribute:: user
.. autoattribute:: password
.. autoattribute:: host
.. autoattribute:: port
.. autoattribute:: options
.. autoattribute:: status
.. autoattribute:: transaction_status
.. automethod:: parameter_status(name)
.. autoattribute:: protocol_version
Currently Psycopg supports only protocol 3, which allows connection
to PostgreSQL server from version 7.4. Psycopg versions previous than
2.3 support both protocols 2 and 3.
.. autoattribute:: server_version
The number is formed by converting the major, minor, and revision
numbers into two-decimal-digit numbers and appending them together.
After PostgreSQL 10 the minor version was dropped, so the second group
of digits is always ``00``. For example, version 9.3.5 will be
returned as ``90305``, version 10.2 as ``100002``.
.. autoattribute:: error_message
.. autoattribute:: socket
.. autoattribute:: backend_pid
.. autoattribute:: needs_password
.. autoattribute:: used_password
.. autoattribute:: ssl_in_use
.. automethod:: ssl_attribute(name)
.. autoattribute:: ssl_attribute_names
.. class:: Column(\*args, \*\*kwargs)
Description of one result column, exposed as items of the
`cursor.description` sequence.
@ -788,7 +827,7 @@ Transaction status constants
----------------------------
These values represent the possible status of a transaction: the current value
can be read using the `connection.get_transaction_status()` method.
can be read using the `connection.info.transaction_status` property.
.. data:: TRANSACTION_STATUS_IDLE

View File

@ -117,9 +117,10 @@ The module interface respects the standard defined in the |DBAPI|_.
Integer constant reporting the version of the ``libpq`` library this
``psycopg2`` module was compiled with (in the same format of
`~connection.server_version`). If this value is greater or equal than
``90100`` then you may query the version of the actually loaded library
using the `~psycopg2.extensions.libpq_version()` function.
`~psycopg2.extensions.ConnectionInfo.server_version`). If this value is
greater or equal than ``90100`` then you may query the version of the
actually loaded library using the `~psycopg2.extensions.libpq_version()`
function.
.. index::

View File

@ -185,7 +185,7 @@ def _get_json_oids(conn_or_curs, name='json'):
conn_status = conn.status
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
# get the oid for the hstore
curs.execute(

View File

@ -352,9 +352,9 @@ class RangeCaster(object):
from psycopg2.extras import _solve_conn_curs
conn, curs = _solve_conn_curs(conn_or_curs)
if conn.server_version < 90200:
if conn.info.server_version < 90200:
raise ProgrammingError("range types not available in version %s"
% conn.server_version)
% conn.info.server_version)
# Store the transaction status of the connection to revert it after use
conn_status = conn.status

View File

@ -61,7 +61,7 @@ from psycopg2._psycopg import ( # noqa
adapt, adapters, encodings, connection, cursor,
lobject, Xid, libpq_version, parse_dsn, quote_ident,
string_types, binary_types, new_type, new_array_type, register_type,
ISQLQuote, Notify, Diagnostics, Column,
ISQLQuote, Notify, Diagnostics, Column, ConnectionInfo,
QueryCanceledError, TransactionRollbackError,
set_wait_callback, get_wait_callback, encrypt_password, )

View File

@ -806,7 +806,7 @@ class HstoreAdapter(object):
self.conn = conn
# use an old-style getquoted implementation if required
if conn.server_version < 90000:
if conn.info.server_version < 90000:
self.getquoted = self._getquoted_8
def _getquoted_8(self):
@ -911,7 +911,7 @@ class HstoreAdapter(object):
conn_status = conn.status
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
rv0, rv1 = [], []
@ -1097,7 +1097,7 @@ class CompositeCaster(object):
schema = 'public'
# column typarray not available before PG 8.3
typarray = conn.server_version >= 80300 and "typarray" or "NULL"
typarray = conn.info.server_version >= 80300 and "typarray" or "NULL"
# get the type oid and attributes
curs.execute("""\

View File

@ -105,7 +105,7 @@ class AbstractConnectionPool(object):
# Return the connection into a consistent state before putting
# it back into the pool
if not conn.closed:
status = conn.get_transaction_status()
status = conn.info.transaction_status
if status == _ext.TRANSACTION_STATUS_UNKNOWN:
# server connection lost
conn.close()

View File

@ -29,6 +29,7 @@
#include "psycopg/connection.h"
#include "psycopg/cursor.h"
#include "psycopg/pqpath.h"
#include "psycopg/conninfo.h"
#include "psycopg/lobject.h"
#include "psycopg/green.h"
#include "psycopg/xid.h"
@ -994,25 +995,20 @@ 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."
/* get info about the connection */
#define psyco_conn_info_get_doc \
"info -- Get connection info."
static PyObject *
psyco_conn_host_get(connectionObject *self)
psyco_conn_info_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);
return PyObject_CallFunctionObjArgs(
(PyObject *)&connInfoType, (PyObject *)self, NULL);
}
/* reset the currect connection */
#define psyco_conn_reset_doc \
@ -1264,9 +1260,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 },
{ "info",
(getter)psyco_conn_info_get, NULL,
psyco_conn_info_get_doc },
{NULL}
};
#undef EXCEPTION_GETTER

40
psycopg/conninfo.h Normal file
View File

@ -0,0 +1,40 @@
/* connection.h - definition for the psycopg ConnectionInfo 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_CONNINFO_H
#define PSYCOPG_CONNINFO_H 1
#include "psycopg/connection.h"
extern HIDDEN PyTypeObject connInfoType;
typedef struct {
PyObject_HEAD
connectionObject *conn;
} connInfoObject;
#endif /* PSYCOPG_CONNINFO_H */

614
psycopg/conninfo_type.c Normal file
View File

@ -0,0 +1,614 @@
/* conninfo_type.c - present information about the libpq connection
*
* 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/conninfo.h"
static const char connInfoType_doc[] =
"Details about the native PostgreSQL database connection.\n"
"\n"
"This class exposes several `informative functions`__ about the status\n"
"of the libpq connection.\n"
"\n"
"Objects of this class are exposed as the `connection.info` attribute.\n"
"\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html";
static const char dbname_doc[] =
"The database name of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQdb()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQDB";
static PyObject *
dbname_get(connInfoObject *self)
{
const char *val;
val = PQdb(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char user_doc[] =
"The user name of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQuser()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQUSER";
static PyObject *
user_get(connInfoObject *self)
{
const char *val;
val = PQuser(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char password_doc[] =
"The password of the connection.\n"
"\n"
".. seealso:: libpq docs for `PQpass()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPASS";
static PyObject *
password_get(connInfoObject *self)
{
const char *val;
val = PQpass(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char host_doc[] =
"The server host name of the connection.\n"
"\n"
"This can be a host name, an IP address, or a directory path if the\n"
"connection is via Unix socket. (The path case can be distinguished\n"
"because it will always be an absolute path, beginning with ``/``.)\n"
"\n"
".. seealso:: libpq docs for `PQhost()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQHOST";
static PyObject *
host_get(connInfoObject *self)
{
const char *val;
val = PQhost(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char port_doc[] =
"The port of the connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQport()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPORT";
static PyObject *
port_get(connInfoObject *self)
{
const char *val;
val = PQport(self->conn->pgconn);
if (!val || !val[0]) {
Py_RETURN_NONE;
}
return PyInt_FromString((char *)val, NULL, 10);
}
static const char options_doc[] =
"The command-line options passed in the the connection request.\n"
"\n"
".. seealso:: libpq docs for `PQoptions()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQOPTIONS";
static PyObject *
options_get(connInfoObject *self)
{
const char *val;
val = PQoptions(self->conn->pgconn);
if (!val) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char status_doc[] =
"The status of the connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQstatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSTATUS";
static PyObject *
status_get(connInfoObject *self)
{
ConnStatusType val;
val = PQstatus(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char transaction_status_doc[] =
"The current in-transaction status of the connection.\n"
"\n"
"Symbolic constants for the values are defined in the module\n"
"`psycopg2.extensions`: see :ref:`transaction-status-constants` for the\n"
"available values.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQtransactionStatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQTRANSACTIONSTATUS";
static PyObject *
transaction_status_get(connInfoObject *self)
{
PGTransactionStatusType val;
val = PQtransactionStatus(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char parameter_status_doc[] =
"Looks up a current parameter setting of the server.\n"
"\n"
":param name: The name of the parameter to return.\n"
":type name: `!str`\n"
":return: The parameter value, `!None` if the parameter is unknown.\n"
":rtype: `!str`\n"
"\n"
".. seealso:: libpq docs for `PQparameterStatus()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPARAMETERSTATUS";
static PyObject *
parameter_status(connInfoObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"name", NULL};
const char *name;
const char *val;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) {
return NULL;
}
val = PQparameterStatus(self->conn->pgconn, name);
if (!val) {
Py_RETURN_NONE;
}
else {
return conn_text_from_chars(self->conn, val);
}
}
static const char protocol_version_doc[] =
"The frontend/backend protocol being used.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQprotocolVersion()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQPROTOCOLVERSION";
static PyObject *
protocol_version_get(connInfoObject *self)
{
int val;
val = PQprotocolVersion(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char server_version_doc[] =
"Returns an integer representing the server version.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQserverVersion()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSERVERVERSION";
static PyObject *
server_version_get(connInfoObject *self)
{
int val;
val = PQserverVersion(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char error_message_doc[] =
"The error message most recently generated by an operation on the connection.\n"
"\n"
"`!None` if there is no current message.\n"
"\n"
".. seealso:: libpq docs for `PQerrorMessage()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQERRORMESSAGE";
static PyObject *
error_message_get(connInfoObject *self)
{
const char *val;
val = PQerrorMessage(self->conn->pgconn);
if (!val || !val[0]) {
Py_RETURN_NONE;
}
return conn_text_from_chars(self->conn, val);
}
static const char socket_doc[] =
"The file descriptor number of the connection socket to the server.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQsocket()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSOCKET";
static PyObject *
socket_get(connInfoObject *self)
{
int val;
val = PQsocket(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char backend_pid_doc[] =
"The process ID (PID) of the backend process handling this connection.\n"
"\n"
":type: `!int`\n"
"\n"
".. seealso:: libpq docs for `PQbackendPID()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQBACKENDPID";
static PyObject *
backend_pid_get(connInfoObject *self)
{
int val;
val = PQbackendPID(self->conn->pgconn);
return PyInt_FromLong((long)val);
}
static const char needs_password_doc[] =
"The connection authentication method required a password, but none was available.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQconnectionNeedsPassword()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQCONNECTIONNEEDSPASSWORD";
static PyObject *
needs_password_get(connInfoObject *self)
{
PyObject *rv;
rv = PQconnectionNeedsPassword(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
return rv;
}
static const char used_password_doc[] =
"The connection authentication method used a password.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQconnectionUsedPassword()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQCONNECTIONUSEDPASSWORD";
static PyObject *
used_password_get(connInfoObject *self)
{
PyObject *rv;
rv = PQconnectionUsedPassword(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
return rv;
}
static const char ssl_in_use_doc[] =
"`!True` if the connection uses SSL, `!False` if not.\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
":type: `!bool`\n"
"\n"
".. seealso:: libpq docs for `PQsslInUse()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLINUSE";
static PyObject *
ssl_in_use_get(connInfoObject *self)
{
PyObject *rv = NULL;
#if PG_VERSION_NUM >= 90500
rv = PQsslInUse(self->conn->pgconn) ? Py_True : Py_False;
Py_INCREF(rv);
#else
PyErr_SetString(NotSupportedError,
"'ssl_in_use' not available in libpq < 9.5");
#endif
return rv;
}
static const char ssl_attribute_doc[] =
"Returns SSL-related information about the connection.\n"
"\n"
":param name: The name of the attribute to return.\n"
":type name: `!str`\n"
":return: The attribute value, `!None` if unknown.\n"
":rtype: `!str`\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
"Valid names are available in `ssl_attribute_names`.\n"
"\n"
".. seealso:: libpq docs for `PQsslAttribute()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLATTRIBUTE";
static PyObject *
ssl_attribute(connInfoObject *self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = {"name", NULL};
const char *name;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s", kwlist, &name)) {
return NULL;
}
#if PG_VERSION_NUM >= 90500
{
const char *val;
val = PQsslAttribute(self->conn->pgconn, name);
if (!val) {
Py_RETURN_NONE;
}
else {
return conn_text_from_chars(self->conn, val);
}
}
#else
PyErr_SetString(NotSupportedError,
"'ssl_attribute()' not available in libpq < 9.5");
return NULL;
#endif
}
static const char ssl_attribute_names_doc[] =
"The list of the SSL attribute names available.\n"
"\n"
":type: `!list` of `!str`\n"
"\n"
"Only available if psycopg was built with libpq >= 9.5; raise\n"
"`~psycopg2.NotSupportedError` otherwise.\n"
"\n"
".. seealso:: libpq docs for `PQsslAttributeNames()`__ for details.\n"
".. __: https://www.postgresql.org/docs/current/static/libpq-status.html"
"#LIBPQ-PQSSLATTRIBUTENAMES";
static PyObject *
ssl_attribute_names_get(connInfoObject *self)
{
#if PG_VERSION_NUM >= 90500
const char* const* names;
int i;
PyObject *l = NULL, *s = NULL, *rv = NULL;
names = PQsslAttributeNames(self->conn->pgconn);
if (!(l = PyList_New(0))) { goto exit; }
for (i = 0; names[i]; i++) {
if (!(s = conn_text_from_chars(self->conn, names[i]))) { goto exit; }
if (0 != PyList_Append(l, s)) { goto exit; }
Py_CLEAR(s);
}
rv = l;
l = NULL;
exit:
Py_XDECREF(l);
Py_XDECREF(s);
return rv;
#else
PyErr_SetString(NotSupportedError,
"'ssl_attribute_names not available in libpq < 9.5");
return NULL;
#endif
}
static struct PyGetSetDef connInfoObject_getsets[] = {
{ "dbname", (getter)dbname_get, NULL, (char *)dbname_doc },
{ "user", (getter)user_get, NULL, (char *)user_doc },
{ "password", (getter)password_get, NULL, (char *)password_doc },
{ "host", (getter)host_get, NULL, (char *)host_doc },
{ "port", (getter)port_get, NULL, (char *)port_doc },
{ "options", (getter)options_get, NULL, (char *)options_doc },
{ "status", (getter)status_get, NULL, (char *)status_doc },
{ "transaction_status", (getter)transaction_status_get, NULL,
(char *)transaction_status_doc },
{ "protocol_version", (getter)protocol_version_get, NULL,
(char *)protocol_version_doc },
{ "server_version", (getter)server_version_get, NULL,
(char *)server_version_doc },
{ "error_message", (getter)error_message_get, NULL,
(char *)error_message_doc },
{ "socket", (getter)socket_get, NULL, (char *)socket_doc },
{ "backend_pid", (getter)backend_pid_get, NULL, (char *)backend_pid_doc },
{ "used_password", (getter)used_password_get, NULL,
(char *)used_password_doc },
{ "needs_password", (getter)needs_password_get, NULL,
(char *)needs_password_doc },
{ "ssl_in_use", (getter)ssl_in_use_get, NULL,
(char *)ssl_in_use_doc },
{ "ssl_attribute_names", (getter)ssl_attribute_names_get, NULL,
(char *)ssl_attribute_names_doc },
{NULL}
};
static struct PyMethodDef connInfoObject_methods[] = {
{"ssl_attribute", (PyCFunction)ssl_attribute,
METH_VARARGS|METH_KEYWORDS, ssl_attribute_doc},
{"parameter_status", (PyCFunction)parameter_status,
METH_VARARGS|METH_KEYWORDS, parameter_status_doc},
{NULL}
};
/* initialization and finalization methods */
static PyObject *
conninfo_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
return type->tp_alloc(type, 0);
}
static int
conninfo_init(connInfoObject *self, PyObject *args, PyObject *kwds)
{
PyObject *conn = NULL;
if (!PyArg_ParseTuple(args, "O", &conn))
return -1;
if (!PyObject_TypeCheck(conn, &connectionType)) {
PyErr_SetString(PyExc_TypeError,
"The argument must be a psycopg2 connection");
return -1;
}
Py_INCREF(conn);
self->conn = (connectionObject *)conn;
return 0;
}
static void
conninfo_dealloc(connInfoObject* self)
{
Py_CLEAR(self->conn);
Py_TYPE(self)->tp_free((PyObject *)self);
}
/* object type */
PyTypeObject connInfoType = {
PyVarObject_HEAD_INIT(NULL, 0)
"psycopg2.extensions.ConnectionInfo",
sizeof(connInfoObject), 0,
(destructor)conninfo_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, /*tp_flags*/
connInfoType_doc, /*tp_doc*/
0, /*tp_traverse*/
0, /*tp_clear*/
0, /*tp_richcompare*/
0, /*tp_weaklistoffset*/
0, /*tp_iter*/
0, /*tp_iternext*/
connInfoObject_methods, /*tp_methods*/
0, /*tp_members*/
connInfoObject_getsets, /*tp_getset*/
0, /*tp_base*/
0, /*tp_dict*/
0, /*tp_descr_get*/
0, /*tp_descr_set*/
0, /*tp_dictoffset*/
(initproc)conninfo_init, /*tp_init*/
0, /*tp_alloc*/
conninfo_new, /*tp_new*/
};

View File

@ -40,7 +40,7 @@ static const char notify_doc[] =
static const char pid_doc[] =
"The ID of the backend process that sent the notification.\n\n"
"Note: if the sending session was handled by Psycopg, you can use\n"
"`~connection.get_backend_pid()` to know its PID.";
"`~connection.info.backend_pid` to know its PID.";
static const char channel_doc[] =
"The name of the channel to which the notification was sent.";

View File

@ -40,6 +40,7 @@
#include "psycopg/microprotocols.h"
#include "psycopg/microprotocols_proto.h"
#include "psycopg/error.h"
#include "psycopg/conninfo.h"
#include "psycopg/diagnostics.h"
#include "psycopg/adapter_qstring.h"
@ -995,6 +996,9 @@ INIT_MODULE(_psycopg)(void)
errorType.tp_base = (PyTypeObject *)PyExc_StandardError;
if (PyType_Ready(&errorType) == -1) goto exit;
Py_TYPE(&connInfoType) = &PyType_Type;
if (PyType_Ready(&connInfoType) == -1) goto exit;
Py_TYPE(&diagnosticsType) = &PyType_Type;
if (PyType_Ready(&diagnosticsType) == -1) goto exit;
@ -1084,6 +1088,7 @@ INIT_MODULE(_psycopg)(void)
PyModule_AddObject(module, "Column", (PyObject*)&columnType);
PyModule_AddObject(module, "Notify", (PyObject*)&notifyType);
PyModule_AddObject(module, "Xid", (PyObject*)&xidType);
PyModule_AddObject(module, "ConnectionInfo", (PyObject*)&connInfoType);
PyModule_AddObject(module, "Diagnostics", (PyObject*)&diagnosticsType);
PyModule_AddObject(module, "AsIs", (PyObject*)&asisType);
PyModule_AddObject(module, "Binary", (PyObject*)&binaryType);

View File

@ -78,6 +78,7 @@ typedef unsigned long Py_uhash_t;
#define PyInt_Check PyLong_Check
#define PyInt_AsLong PyLong_AsLong
#define PyInt_FromLong PyLong_FromLong
#define PyInt_FromString PyLong_FromString
#define PyInt_FromSsize_t PyLong_FromSsize_t
#define PyExc_StandardError PyExc_Exception
#define PyString_FromFormat PyUnicode_FromFormat

View File

@ -490,7 +490,7 @@ sources = [
'replication_connection_type.c',
'replication_cursor_type.c',
'replication_message_type.c',
'diagnostics_type.c', 'error_type.c',
'diagnostics_type.c', 'error_type.c', 'conninfo_type.c',
'lobject_int.c', 'lobject_type.c',
'notify_type.c', 'xid_type.c',
@ -508,7 +508,7 @@ depends = [
'replication_connection.h',
'replication_cursor.h',
'replication_message.h',
'notify.h', 'pqpath.h', 'xid.h', 'column.h',
'notify.h', 'pqpath.h', 'xid.h', 'column.h', 'conninfo.h',
'libpq_support.h', 'win32_support.h',
'adapter_asis.h', 'adapter_binary.h', 'adapter_datetime.h',

View File

@ -192,7 +192,7 @@ class AsyncTests(ConnectingTestCase):
self.assertTrue(self.conn.isexecuting())
# getting transaction status works
self.assertEquals(self.conn.get_transaction_status(),
self.assertEquals(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_ACTIVE)
self.assertTrue(self.conn.isexecuting())
@ -359,7 +359,7 @@ class AsyncTests(ConnectingTestCase):
self.assertEquals(self.sync_conn.notifies, [])
pid = self.conn.get_backend_pid()
pid = self.conn.info.backend_pid
for _ in range(5):
self.wait(self.sync_conn)
if not self.sync_conn.notifies:
@ -418,7 +418,7 @@ class AsyncTests(ConnectingTestCase):
def test_notices(self):
del self.conn.notices[:]
cur = self.conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
self.wait(cur)
cur.execute("create temp table chatty (id serial primary key);")

View File

@ -87,13 +87,13 @@ class ConnectionTests(ConnectingTestCase):
conn.autocommit = True
conn.isolation_level = 'serializable'
conn.readonly = True
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
conn.deferrable = False
self.assert_(conn.autocommit)
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_SERIALIZABLE)
self.assert_(conn.readonly is True)
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
self.assert_(conn.deferrable is False)
conn.reset()
@ -101,13 +101,13 @@ class ConnectionTests(ConnectingTestCase):
self.assert_(not conn.autocommit)
self.assertEqual(conn.isolation_level, ext.ISOLATION_LEVEL_DEFAULT)
self.assert_(conn.readonly is None)
if self.conn.server_version >= 90100:
if self.conn.info.server_version >= 90100:
self.assert_(conn.deferrable is None)
def test_notices(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("create temp table chatty (id serial primary key);")
self.assertEqual("CREATE TABLE", cur.statusmessage)
@ -116,7 +116,7 @@ class ConnectionTests(ConnectingTestCase):
def test_notices_consistent_order(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("""
create temp table table1 (id serial);
@ -136,7 +136,7 @@ class ConnectionTests(ConnectingTestCase):
def test_notices_limited(self):
conn = self.conn
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
for i in range(0, 100, 10):
sql = " ".join(["create temp table table%d (id serial);" % j
@ -153,7 +153,7 @@ class ConnectionTests(ConnectingTestCase):
conn = self.conn
self.conn.notices = deque()
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("""
@ -183,7 +183,7 @@ class ConnectionTests(ConnectingTestCase):
conn = self.conn
self.conn.notices = None # will make an error swallowes ok
cur = conn.cursor()
if self.conn.server_version >= 90300:
if self.conn.info.server_version >= 90300:
cur.execute("set client_min_messages=debug1")
cur.execute("create temp table table1 (id serial);")
@ -199,7 +199,7 @@ class ConnectionTests(ConnectingTestCase):
def test_tpc_unsupported(self):
cnn = self.conn
if cnn.server_version >= 80100:
if cnn.info.server_version >= 80100:
return self.skipTest("tpc is supported")
self.assertRaises(psycopg2.NotSupportedError,
@ -533,7 +533,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
conn.set_isolation_level(level)
# the only values available on prehistoric PG versions
if conn.server_version < 80000:
if conn.info.server_version < 80000:
if level in (
ext.ISOLATION_LEVEL_READ_UNCOMMITTED,
ext.ISOLATION_LEVEL_REPEATABLE_READ):
@ -589,35 +589,35 @@ class IsolationLevelsTestCase(ConnectingTestCase):
cur = conn.cursor()
self.assertEqual(ext.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("insert into isolevel values (10);")
self.assertEqual(ext.TRANSACTION_STATUS_INTRANS,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_SERIALIZABLE)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(0, cur.fetchone()[0])
cur.execute("insert into isolevel values (10);")
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_INTRANS,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_AUTOCOMMIT)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(0, cur.fetchone()[0])
cur.execute("insert into isolevel values (10);")
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
conn.set_isolation_level(
psycopg2.extensions.ISOLATION_LEVEL_READ_COMMITTED)
self.assertEqual(psycopg2.extensions.TRANSACTION_STATUS_IDLE,
conn.get_transaction_status())
conn.info.transaction_status)
cur.execute("select count(*) from isolevel;")
self.assertEqual(1, cur.fetchone()[0])
self.assertEqual(conn.isolation_level,
@ -713,7 +713,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = ext.ISOLATION_LEVEL_REPEATABLE_READ
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_REPEATABLE_READ)
self.assertEqual(cur.fetchone()[0], 'repeatable read')
@ -732,7 +732,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = ext.ISOLATION_LEVEL_READ_UNCOMMITTED
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
@ -761,7 +761,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = "repeatable read"
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_REPEATABLE_READ)
self.assertEqual(cur.fetchone()[0], 'repeatable read')
@ -780,7 +780,7 @@ class IsolationLevelsTestCase(ConnectingTestCase):
self.conn.isolation_level = "read uncommitted"
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(self.conn.isolation_level,
ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
@ -1211,7 +1211,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session(
ext.ISOLATION_LEVEL_REPEATABLE_READ)
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'repeatable read')
else:
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1226,7 +1226,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session(
isolation_level=ext.ISOLATION_LEVEL_READ_UNCOMMITTED)
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
else:
self.assertEqual(cur.fetchone()[0], 'read committed')
@ -1241,7 +1241,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session("repeatable read")
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'repeatable read')
else:
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1254,7 +1254,7 @@ class TransactionControlTests(ConnectingTestCase):
self.conn.set_session("read uncommitted")
cur.execute("SHOW transaction_isolation;")
if self.conn.server_version > 80000:
if self.conn.info.server_version > 80000:
self.assertEqual(cur.fetchone()[0], 'read uncommitted')
else:
self.assertEqual(cur.fetchone()[0], 'read committed')
@ -1510,42 +1510,42 @@ class AutocommitTests(ConnectingTestCase):
def test_default_no_autocommit(self):
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
self.conn.rollback()
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
def test_set_autocommit(self):
self.conn.autocommit = True
self.assert_(self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
self.conn.autocommit = False
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
def test_set_intrans_error(self):
@ -1558,24 +1558,24 @@ class AutocommitTests(ConnectingTestCase):
self.conn.set_session(autocommit=True)
self.assert_(self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur = self.conn.cursor()
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
self.conn.set_session(autocommit=False)
self.assert_(not self.conn.autocommit)
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_INTRANS)
self.conn.rollback()
@ -1583,7 +1583,7 @@ class AutocommitTests(ConnectingTestCase):
self.assert_(self.conn.autocommit)
cur.execute('select 1;')
self.assertEqual(self.conn.status, ext.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
ext.TRANSACTION_STATUS_IDLE)
cur.execute("SHOW transaction_isolation;")
self.assertEqual(cur.fetchone()[0], 'serializable')
@ -1690,17 +1690,156 @@ while True:
self.assert_(not err, err)
class TestConnectionProps(ConnectingTestCase):
class TestConnectionInfo(ConnectingTestCase):
def setUp(self):
ConnectingTestCase.setUp(self)
class BrokenConn(psycopg2.extensions.connection):
def __init__(self, *args, **kwargs):
# don't call superclass
pass
# A "broken" connection
self.bconn = self.connect(connection_factory=BrokenConn)
def test_dbname(self):
self.assert_(isinstance(self.conn.info.dbname, str))
self.assert_(self.bconn.info.dbname is None)
def test_user(self):
cur = self.conn.cursor()
cur.execute("select user")
self.assertEqual(self.conn.info.user, cur.fetchone()[0])
self.assert_(self.bconn.info.user is None)
def test_password(self):
self.assert_(isinstance(self.conn.info.password, str))
self.assert_(self.bconn.info.password is None)
def test_host(self):
self.assertFalse(self.conn.closed)
expected = dbhost if dbhost else "/"
self.assertIn(expected, self.conn.host)
self.assertIn(expected, self.conn.info.host)
self.assert_(self.bconn.info.host is None)
def test_host_readonly(self):
self.assertFalse(self.conn.closed)
with self.assertRaises(AttributeError):
self.conn.host = 'override'
self.conn.info.host = 'override'
def test_port(self):
self.assert_(isinstance(self.conn.info.port, int))
self.assert_(self.bconn.info.port is None)
def test_options(self):
self.assert_(isinstance(self.conn.info.options, str))
self.assert_(self.bconn.info.options is None)
def test_status(self):
self.assertEqual(self.conn.info.status, 0)
self.assertEqual(self.bconn.info.status, 1)
def test_transaction_status(self):
self.assertEqual(self.conn.info.transaction_status, 0)
cur = self.conn.cursor()
cur.execute("select 1")
self.assertEqual(self.conn.info.transaction_status, 2)
self.assertEqual(self.bconn.info.transaction_status, 4)
def test_parameter_status(self):
cur = self.conn.cursor()
try:
cur.execute("show server_version")
except psycopg2.DatabaseError:
self.assertIsInstance(
self.conn.info.parameter_status('server_version'), str)
else:
self.assertEqual(
self.conn.info.parameter_status('server_version'),
cur.fetchone()[0])
self.assertIsNone(self.conn.info.parameter_status('wat'))
self.assertIsNone(self.bconn.info.parameter_status('server_version'))
def test_protocol_version(self):
self.assertEqual(self.conn.info.protocol_version, 3)
self.assertEqual(self.bconn.info.protocol_version, 0)
def test_server_version(self):
cur = self.conn.cursor()
try:
cur.execute("show server_version_num")
except psycopg2.DatabaseError:
self.assert_(isinstance(self.conn.info.server_version, int))
else:
self.assertEqual(
self.conn.info.server_version, int(cur.fetchone()[0]))
self.assertEqual(self.bconn.info.server_version, 0)
def test_error_message(self):
self.assertIsNone(self.conn.info.error_message)
self.assertIsNotNone(self.bconn.info.error_message)
cur = self.conn.cursor()
try:
cur.execute("select 1 from nosuchtable")
except psycopg2.DatabaseError:
pass
self.assert_('nosuchtable' in self.conn.info.error_message)
def test_socket(self):
self.assert_(self.conn.info.socket >= 0)
self.assert_(self.bconn.info.socket < 0)
def test_backend_pid(self):
cur = self.conn.cursor()
try:
cur.execute("select pg_backend_pid()")
except psycopg2.DatabaseError:
self.assert_(self.conn.info.backend_pid > 0)
else:
self.assertEqual(
self.conn.info.backend_pid, int(cur.fetchone()[0]))
self.assert_(self.bconn.info.backend_pid == 0)
def test_needs_password(self):
self.assertIs(self.conn.info.needs_password, False)
self.assertIs(self.bconn.info.needs_password, False)
def test_used_password(self):
self.assertIsInstance(self.conn.info.used_password, bool)
self.assertIs(self.bconn.info.used_password, False)
@skip_before_libpq(9, 5)
def test_ssl_in_use(self):
self.assertIsInstance(self.conn.info.ssl_in_use, bool)
self.assertIs(self.bconn.info.ssl_in_use, False)
@skip_after_libpq(9, 5)
def test_ssl_not_supported(self):
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_in_use
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_attribute_names
with self.assertRaises(psycopg2.NotSupportedError):
self.conn.info.ssl_attribute('wat')
@skip_before_libpq(9, 5)
def test_ssl_attribute(self):
attribs = self.conn.info.ssl_attribute_names
self.assert_(attribs)
if self.conn.info.ssl_in_use:
for attrib in attribs:
self.assertIsInstance(self.conn.info.ssl_attribute(attrib), str)
else:
for attrib in attribs:
self.assertIsNone(self.conn.info.ssl_attribute(attrib))
self.assertIsNone(self.conn.info.ssl_attribute('wat'))
for attrib in attribs:
self.assertIsNone(self.bconn.info.ssl_attribute(attrib))
def test_suite():

View File

@ -229,22 +229,22 @@ class CursorTests(ConnectingTestCase):
curs.execute("select data from withhold order by data")
self.assertEqual(curs.fetchone(), (10,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_BEGIN)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_INTRANS)
self.conn.commit()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
self.assertEqual(curs.fetchone(), (20,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
curs.close()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
def test_withhold_autocommit(self):
@ -256,17 +256,17 @@ class CursorTests(ConnectingTestCase):
self.assertEqual(curs.fetchone(), (10,))
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
self.conn.commit()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
curs.close()
self.assertEqual(self.conn.status, psycopg2.extensions.STATUS_READY)
self.assertEqual(self.conn.get_transaction_status(),
self.assertEqual(self.conn.info.transaction_status,
psycopg2.extensions.TRANSACTION_STATUS_IDLE)
def test_scrollable(self):

View File

@ -37,7 +37,7 @@ from .testutils import (decorate_all_tests, skip_if_tpc_disabled,
def skip_if_no_lo(f):
@wraps(f)
def skip_if_no_lo_(self):
if self.conn.server_version < 80100:
if self.conn.info.server_version < 80100:
return self.skipTest("large objects only supported from PG 8.1")
else:
return f(self)
@ -403,7 +403,7 @@ decorate_all_tests(LargeObjectTests, skip_if_no_lo, skip_lo_if_green)
def skip_if_no_truncate(f):
@wraps(f)
def skip_if_no_truncate_(self):
if self.conn.server_version < 80300:
if self.conn.info.server_version < 80300:
return self.skipTest(
"the server doesn't support large object truncate")
@ -459,9 +459,9 @@ decorate_all_tests(LargeObjectTruncateTests,
def _has_lo64(conn):
"""Return (bool, msg) about the lo64 support"""
if conn.server_version < 90300:
if conn.info.server_version < 90300:
return (False, "server version %s doesn't support the lo64 API"
% conn.server_version)
% conn.info.server_version)
if 'lo64' not in psycopg2.__version__:
return False, "this psycopg build doesn't support the lo64 API"

View File

@ -61,7 +61,7 @@ import %(module)s as psycopg2
import %(module)s.extensions as ext
conn = psycopg2.connect(%(dsn)r)
conn.set_isolation_level(ext.ISOLATION_LEVEL_AUTOCOMMIT)
print(conn.get_backend_pid())
print(conn.info.backend_pid)
curs = conn.cursor()
curs.execute("NOTIFY " %(name)r %(payload)r)
curs.close()
@ -147,9 +147,9 @@ conn.close()
@slow
def test_notify_payload(self):
if self.conn.server_version < 90000:
if self.conn.info.server_version < 90000:
return self.skipTest("server version %s doesn't support notify payload"
% self.conn.server_version)
% self.conn.info.server_version)
self.autocommit(self.conn)
self.listen('foo')
pid = int(self.notify('foo', payload="Hello, world!").communicate()[0])

View File

@ -90,7 +90,7 @@ class QuotingTestCase(ConnectingTestCase):
else:
res = curs.fetchone()[0].tobytes()
if res[0] in (b'x', ord(b'x')) and self.conn.server_version >= 90000:
if res[0] in (b'x', ord(b'x')) and self.conn.info.server_version >= 90000:
return self.skipTest(
"bytea broken with server >= 9.0, libpq < 9")

View File

@ -137,7 +137,7 @@ def skip_if_no_hstore(f):
class HstoreTestCase(ConnectingTestCase):
def test_adapt_8(self):
if self.conn.server_version >= 90000:
if self.conn.info.server_version >= 90000:
return self.skipTest("skipping dict adaptation with PG pre-9 syntax")
from psycopg2.extras import HstoreAdapter
@ -163,7 +163,7 @@ class HstoreTestCase(ConnectingTestCase):
self.assertQuotedEqual(ii[3], b"('d' => '" + encc + b"')")
def test_adapt_9(self):
if self.conn.server_version < 90000:
if self.conn.info.server_version < 90000:
return self.skipTest("skipping dict adaptation with PG 9 syntax")
from psycopg2.extras import HstoreAdapter
@ -448,10 +448,10 @@ class HstoreTestCase(ConnectingTestCase):
def skip_if_no_composite(f):
@wraps(f)
def skip_if_no_composite_(self):
if self.conn.server_version < 80000:
if self.conn.info.server_version < 80000:
return self.skipTest(
"server version %s doesn't support composite types"
% self.conn.server_version)
% self.conn.info.server_version)
return f(self)
@ -1436,10 +1436,10 @@ class RangeTestCase(unittest.TestCase):
def skip_if_no_range(f):
@wraps(f)
def skip_if_no_range_(self):
if self.conn.server_version < 90200:
if self.conn.info.server_version < 90200:
return self.skipTest(
"server version %s doesn't support range types"
% self.conn.server_version)
% self.conn.info.server_version)
return f(self)

View File

@ -234,9 +234,9 @@ def skip_before_postgres(*ver):
def skip_before_postgres_(f):
@wraps(f)
def skip_before_postgres__(self):
if self.conn.server_version < int("%d%02d%02d" % ver):
if self.conn.info.server_version < int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
% self.conn.server_version)
% self.conn.info.server_version)
else:
return f(self)
@ -251,9 +251,9 @@ def skip_after_postgres(*ver):
def skip_after_postgres_(f):
@wraps(f)
def skip_after_postgres__(self):
if self.conn.server_version >= int("%d%02d%02d" % ver):
if self.conn.info.server_version >= int("%d%02d%02d" % ver):
return self.skipTest("skipped because PostgreSQL %s"
% self.conn.server_version)
% self.conn.info.server_version)
else:
return f(self)