Add connection.get_dsn_parameters()

This commit is contained in:
Oleksandr Shulgin 2015-10-30 11:10:41 +01:00
parent fe4cb0d493
commit a4cbb088fe
6 changed files with 92 additions and 19 deletions

View File

@ -568,6 +568,24 @@ The ``connection`` class
.. versionadded:: 2.0.12 .. versionadded:: 2.0.12
.. method:: get_dsn_parameters()
Get the effective dsn parameters for the connection as a dictionary.
The *password* parameter is removed from the result.
Example::
>>> conn.get_dsn_parameters()
{'dbname': 'test', 'user': 'postgres', 'port': '5432', 'sslmode': 'prefer'}
Requires libpq >= 9.3.
.. seealso:: libpq docs for `PQconninfo()`__ for details.
.. __: http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-PQCONNINFO
.. index:: .. index::
pair: Transaction; Status pair: Transaction; Status

View File

@ -733,6 +733,37 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args)
return conn_text_from_chars(self, val); return conn_text_from_chars(self, val);
} }
/* get_dsn_parameters method - Get connection parameters */
#define psyco_conn_get_dsn_parameters_doc \
"get_dsn_parameters() -- Get effective connection parameters.\n\n"
static PyObject *
psyco_conn_get_dsn_parameters(connectionObject *self)
{
#if PG_VERSION_NUM >= 90300
PyObject *res = NULL;
PQconninfoOption *options = NULL;
EXC_IF_CONN_CLOSED(self);
if (!(options = PQconninfo(self->pgconn))) {
PyErr_NoMemory();
goto exit;
}
res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 0);
exit:
PQconninfoFree(options);
return res;
#else
PyErr_SetString(NotSupportedError, "PQconninfo not available in libpq < 9.3");
return NULL;
#endif
}
/* lobject method - allocate a new lobject */ /* lobject method - allocate a new lobject */
@ -977,6 +1008,8 @@ static struct PyMethodDef connectionObject_methods[] = {
METH_NOARGS, psyco_conn_get_transaction_status_doc}, METH_NOARGS, psyco_conn_get_transaction_status_doc},
{"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status, {"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status,
METH_VARARGS, psyco_conn_get_parameter_status_doc}, METH_VARARGS, psyco_conn_get_parameter_status_doc},
{"get_dsn_parameters", (PyCFunction)psyco_conn_get_dsn_parameters,
METH_NOARGS, psyco_conn_get_dsn_parameters_doc},
{"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid, {"get_backend_pid", (PyCFunction)psyco_conn_get_backend_pid,
METH_NOARGS, psyco_conn_get_backend_pid_doc}, METH_NOARGS, psyco_conn_get_backend_pid_doc},
{"lobject", (PyCFunction)psyco_conn_lobject, {"lobject", (PyCFunction)psyco_conn_lobject,

View File

@ -131,6 +131,9 @@ STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(PyObject *obj);
STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj); STEALS(1) HIDDEN PyObject * psycopg_ensure_text(PyObject *obj);
HIDDEN PyObject *psycopg_dict_from_conninfo_options(PQconninfoOption *options,
int include_password);
/* Exceptions docstrings */ /* Exceptions docstrings */
#define Error_doc \ #define Error_doc \
"Base class for error exceptions." "Base class for error exceptions."

View File

@ -118,8 +118,8 @@ static PyObject *
psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
{ {
char *err = NULL; char *err = NULL;
PQconninfoOption *options = NULL, *o; PQconninfoOption *options = NULL;
PyObject *dict = NULL, *res = NULL, *dsn; PyObject *res = NULL, *dsn;
static char *kwlist[] = {"dsn", NULL}; static char *kwlist[] = {"dsn", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) { if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) {
@ -140,26 +140,10 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs)
goto exit; goto exit;
} }
if (!(dict = PyDict_New())) { goto exit; } res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1);
for (o = options; o->keyword != NULL; o++) {
if (o->val != NULL) {
PyObject *value;
if (!(value = Text_FromUTF8(o->val))) { goto exit; }
if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
Py_DECREF(value);
goto exit;
}
Py_DECREF(value);
}
}
/* success */
res = dict;
dict = NULL;
exit: exit:
PQconninfoFree(options); /* safe on null */ PQconninfoFree(options); /* safe on null */
Py_XDECREF(dict);
Py_XDECREF(dsn); Py_XDECREF(dsn);
return res; return res;

View File

@ -247,3 +247,32 @@ psycopg_is_text_file(PyObject *f)
} }
} }
/* Make a dict out of PQconninfoOption array */
PyObject *
psycopg_dict_from_conninfo_options(PQconninfoOption *options, int include_password)
{
PyObject *dict, *res = NULL;
PQconninfoOption *o;
if (!(dict = PyDict_New())) { goto exit; }
for (o = options; o->keyword != NULL; o++) {
if (o->val != NULL &&
(include_password || strcmp(o->keyword, "password") != 0)) {
PyObject *value;
if (!(value = Text_FromUTF8(o->val))) { goto exit; }
if (PyDict_SetItemString(dict, o->keyword, value) != 0) {
Py_DECREF(value);
goto exit;
}
Py_DECREF(value);
}
}
res = dict;
dict = NULL;
exit:
Py_XDECREF(dict);
return res;
}

View File

@ -381,6 +381,12 @@ class ParseDsnTestCase(ConnectingTestCase):
self.assertRaises(TypeError, parse_dsn, None) self.assertRaises(TypeError, parse_dsn, None)
self.assertRaises(TypeError, parse_dsn, 42) self.assertRaises(TypeError, parse_dsn, 42)
def test_get_dsn_paramaters(self):
conn = self.connect()
d = conn.get_dsn_parameters()
self.assertEqual(d['dbname'], dbname) # the only param we can check reliably
self.assertNotIn('password', d)
class IsolationLevelsTestCase(ConnectingTestCase): class IsolationLevelsTestCase(ConnectingTestCase):