From a4cbb088fe2b1b3441b249a06e6498d17c3e56d9 Mon Sep 17 00:00:00 2001 From: Oleksandr Shulgin Date: Fri, 30 Oct 2015 11:10:41 +0100 Subject: [PATCH] Add connection.get_dsn_parameters() --- doc/src/connection.rst | 18 ++++++++++++++++++ psycopg/connection_type.c | 33 +++++++++++++++++++++++++++++++++ psycopg/psycopg.h | 3 +++ psycopg/psycopgmodule.c | 22 +++------------------- psycopg/utils.c | 29 +++++++++++++++++++++++++++++ tests/test_connection.py | 6 ++++++ 6 files changed, 92 insertions(+), 19 deletions(-) diff --git a/doc/src/connection.rst b/doc/src/connection.rst index cceef1e5..3d38180a 100644 --- a/doc/src/connection.rst +++ b/doc/src/connection.rst @@ -568,6 +568,24 @@ The ``connection`` class .. 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:: pair: Transaction; Status diff --git a/psycopg/connection_type.c b/psycopg/connection_type.c index 2c1dddf2..5c74c301 100644 --- a/psycopg/connection_type.c +++ b/psycopg/connection_type.c @@ -733,6 +733,37 @@ psyco_conn_get_parameter_status(connectionObject *self, PyObject *args) 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 */ @@ -977,6 +1008,8 @@ static struct PyMethodDef connectionObject_methods[] = { METH_NOARGS, psyco_conn_get_transaction_status_doc}, {"get_parameter_status", (PyCFunction)psyco_conn_get_parameter_status, 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, METH_NOARGS, psyco_conn_get_backend_pid_doc}, {"lobject", (PyCFunction)psyco_conn_lobject, diff --git a/psycopg/psycopg.h b/psycopg/psycopg.h index eb406fd2..13326ccf 100644 --- a/psycopg/psycopg.h +++ b/psycopg/psycopg.h @@ -131,6 +131,9 @@ STEALS(1) HIDDEN PyObject * psycopg_ensure_bytes(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 */ #define Error_doc \ "Base class for error exceptions." diff --git a/psycopg/psycopgmodule.c b/psycopg/psycopgmodule.c index cf70a4ad..38dd539b 100644 --- a/psycopg/psycopgmodule.c +++ b/psycopg/psycopgmodule.c @@ -118,8 +118,8 @@ static PyObject * psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) { char *err = NULL; - PQconninfoOption *options = NULL, *o; - PyObject *dict = NULL, *res = NULL, *dsn; + PQconninfoOption *options = NULL; + PyObject *res = NULL, *dsn; static char *kwlist[] = {"dsn", NULL}; if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O", kwlist, &dsn)) { @@ -140,26 +140,10 @@ psyco_parse_dsn(PyObject *self, PyObject *args, PyObject *kwargs) goto exit; } - if (!(dict = PyDict_New())) { goto exit; } - 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; + res = psycopg_dict_from_conninfo_options(options, /* include_password = */ 1); exit: PQconninfoFree(options); /* safe on null */ - Py_XDECREF(dict); Py_XDECREF(dsn); return res; diff --git a/psycopg/utils.c b/psycopg/utils.c index ec8e47c8..1b10c4aa 100644 --- a/psycopg/utils.c +++ b/psycopg/utils.c @@ -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; +} diff --git a/tests/test_connection.py b/tests/test_connection.py index 68bb6f05..7e183a82 100755 --- a/tests/test_connection.py +++ b/tests/test_connection.py @@ -381,6 +381,12 @@ class ParseDsnTestCase(ConnectingTestCase): self.assertRaises(TypeError, parse_dsn, None) 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):