Skip to content

Commit

Permalink
Add pqlib_info and memory_size features
Browse files Browse the repository at this point in the history
  • Loading branch information
Cito committed Apr 21, 2020
1 parent f0aef89 commit f4b0223
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 6 deletions.
7 changes: 6 additions & 1 deletion docs/contents/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ ChangeLog

Version 5.2 (to be released)
----------------------------
- We now Python version 2.7 or 3.5 and newer
- We now require Python version 2.7 or 3.5 and newer
- Changes to the classic PyGreSQL module (pg):
- New module level function `get_pqlib_version()` that gets the version
of the pqlib used by PyGreSQL (needs PostgreSQL >= 9.1 on the client).
- New query method `memsize()` that gets the memory size allocated by
the query (needs PostgreSQL >= 12 on the client).


Version 5.1.2 (2020-04-19)
Expand Down
9 changes: 9 additions & 0 deletions docs/contents/install.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ You can find out all possible build options with::
Alternatively, you can also use the corresponding C preprocessor macros like
``SSL_INFO`` directly (see the next section).

Note that if you build PyGreSQL with support for newer features that are not
available in the PQLib installed on the runtime system, you may get an error
when importing PyGreSQL, since these features are missing in the shared library
which will prevent Python from loading it.

Compiling Manually
~~~~~~~~~~~~~~~~~~

Expand Down Expand Up @@ -147,7 +152,9 @@ Stand-Alone
-DDIRECT_ACCESS direct access methods
-DLARGE_OBJECTS large object support
-DESCAPING_FUNCS support for newer escaping functions
-DPQLIB_INFO support PQLib information
-DSSL_INFO support SSL information
-DMEMORY_SIZE support memory size function

On some systems you may need to include ``-lcrypt`` in the list of libraries
to make it compile.
Expand Down Expand Up @@ -193,7 +200,9 @@ Built-in to Python interpreter
-DDIRECT_ACCESS direct access methods
-DLARGE_OBJECTS large object support
-DESCAPING_FUNCS support for newer escaping functions
-DPQLIB_INFO support PQLib information
-DSSL_INFO support SSL information
-DMEMORY_SIZE support memory size function

On some systems you may need to include ``-lcrypt`` in the list of libraries
to make it compile.
Expand Down
18 changes: 18 additions & 0 deletions docs/contents/pg/module.rst
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,24 @@ Example::
con3 = pg.connect('host=myhost user=bob dbname=testdb connect_timeout=10')
con4 = pg.connect('postgresql://bob@myhost/testdb?connect_timeout=10')


get_pqlib_version -- get the version of libpq
---------------------------------------------

.. function:: get_pqlib_version()

Get the version of libpq that is being used by PyGreSQL

:returns: the version of libpq
:rtype: int
:raises TypeError: too many arguments

The number is formed by converting the major, minor, and revision numbers of
the libpq version into two-decimal-digit numbers and appending them together.
For example, version 9.1.2 will be returned as 90102.

.. versionadded:: 5.2 (needs PostgreSQL >= 9.1)

get/set_defhost -- default server host [DV]
-------------------------------------------

Expand Down
15 changes: 15 additions & 0 deletions docs/contents/pg/query.rst
Original file line number Diff line number Diff line change
Expand Up @@ -380,3 +380,18 @@ This method returns the number of tuples in the query result.

.. deprecated:: 5.1
You can use the normal :func:`len` function instead.

memsize -- return number of bytes allocated by query result
-----------------------------------------------------------

.. method:: Query.memsize()

Return number of bytes allocated by query result

:returns: number of bytes allocated for the query result
:rtype: int
:raises TypeError: Too many arguments.

This method returns the number of bytes allocated for the query result.

.. versionadded:: 5.2 (needs PostgreSQL >= 12)
18 changes: 17 additions & 1 deletion pgmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,19 @@ pg_connect(PyObject *self, PyObject *args, PyObject *dict)
return (PyObject *) conn_obj;
}

#ifdef PQLIB_INFO

/* Get version of libpq that is being used */
static char pg_get_pqlib_version__doc__[] =
"get_pqlib_version() -- get the version of libpq that is being used";

static PyObject *
pg_get_pqlib_version(PyObject *self, PyObject *noargs) {
return PyLong_FromLong(PQlibVersion());
}

#endif /* PQLIB_INFO */

/* Escape string */
static char pg_escape_string__doc__[] =
"escape_string(string) -- escape a string for use within SQL";
Expand Down Expand Up @@ -1176,7 +1189,6 @@ static struct PyMethodDef pg_methods[] = {
METH_VARARGS|METH_KEYWORDS, pg_cast_record__doc__},
{"cast_hstore", (PyCFunction) pg_cast_hstore,
METH_O, pg_cast_hstore__doc__},

#ifdef DEFAULT_VARS
{"get_defhost", pg_get_defhost, METH_NOARGS, pg_get_defhost__doc__},
{"set_defhost", pg_set_defhost, METH_VARARGS, pg_set_defhost__doc__},
Expand All @@ -1190,6 +1202,10 @@ static struct PyMethodDef pg_methods[] = {
{"set_defuser", pg_set_defuser, METH_VARARGS, pg_set_defuser__doc__},
{"set_defpasswd", pg_set_defpasswd, METH_VARARGS, pg_set_defpasswd__doc__},
#endif /* DEFAULT_VARS */
#ifdef PQLIB_INFO
{"get_pqlib_version", (PyCFunction) pg_get_pqlib_version,
METH_NOARGS, pg_get_pqlib_version__doc__},
#endif /* PQLIB_INFO */
{NULL, NULL} /* sentinel */
};

Expand Down
17 changes: 17 additions & 0 deletions pgquery.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ query_next(queryObject *self, PyObject *noargs)
return row_tuple;
}

#ifdef MEMORY_SIZE

/* Get number of bytes allocated for PGresult object */
static char query_memsize__doc__[] =
"memsize() -- return number of bytes allocated by query result";
static PyObject *
query_memsize(queryObject *self, PyObject *noargs)
{
return PyLong_FromSize_t(PQresultMemorySize(self->result));
}

#endif /* MEMORY_SIZE */

/* Get number of rows. */
static char query_ntuples__doc__[] =
"ntuples() -- return number of tuples returned by query";
Expand Down Expand Up @@ -706,6 +719,10 @@ static struct PyMethodDef query_methods[] = {
METH_NOARGS, query_listfields__doc__},
{"ntuples", (PyCFunction) query_ntuples,
METH_NOARGS, query_ntuples__doc__},
#ifdef MEMORY_SIZE
{"memsize", (PyCFunction) query_memsize,
METH_NOARGS, query_memsize__doc__},
#endif /* MEMORY_SIZE */
{NULL, NULL}
};

Expand Down
18 changes: 17 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ def initialize_options(self):
self.large_objects = None
self.default_vars = None
self.escaping_funcs = None
self.pqlib_info = None
self.ssl_info = None
self.memory_size = None
if pg_version < (9, 0):
warnings.warn(
"PyGreSQL does not support the installed PostgreSQL version.")
Expand All @@ -163,13 +165,27 @@ def finalize_options(self):
(warnings.warn if self.escaping_funcs is None else sys.exit)(
"The installed PostgreSQL version"
" does not support the newer string escaping functions.")
if self.pqlib_info is None or self.pqlib_info:
if pg_version >= (9, 1):
define_macros.append(('PQLIB_INFO', None))
else:
(warnings.warn if self.pqlib_info is None else sys.exit)(
"The installed PostgreSQL version"
" does not support PQLib info functions.")
if self.ssl_info is None or self.ssl_info:
if pg_version >= (9, 5):
define_macros.append(('SSL_INFO', None))
else:
(warnings.warn if self.ssl_info is None else sys.exit)(
"The installed PostgreSQL version"
" does not support ssl info functions.")
" does not support SSL info functions.")
if self.memory_size is None or self.memory_size:
if pg_version >= (12, 0):
define_macros.append(('MEMORY_SIZE', None))
else:
(warnings.warn if self.memory_size is None else sys.exit)(
"The installed PostgreSQL version"
" does not support the memory size function.")
if sys.platform == 'win32':
libraries[0] = 'lib' + libraries[0]
if os.path.exists(os.path.join(
Expand Down
21 changes: 18 additions & 3 deletions tests/test_classic_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,10 @@ def testMethodQueryEmpty(self):
def testAllQueryMembers(self):
query = self.connection.query("select true where false")
members = '''
dictiter dictresult fieldname fieldnum getresult listfields
namediter namedresult ntuples one onedict onenamed onescalar
scalariter scalarresult single singledict singlenamed singlescalar
dictiter dictresult fieldname fieldnum getresult
listfields memsize namediter namedresult ntuples
one onedict onenamed onescalar scalariter scalarresult
single singledict singlenamed singlescalar
'''.split()
query_members = [a for a in dir(query)
if not a.startswith('__')
Expand Down Expand Up @@ -683,6 +684,20 @@ def testQueryWithOids(self):
self.assertIsInstance(r, str)
self.assertEqual(r, '5')

def testMemSize(self):
if pg.get_pqlib_version() < 120000:
self.skipTest("pqlib does not support memsize()")
query = self.c.query
q = query("select repeat('foo!', 8)")
size = q.memsize()
self.assertIsInstance(size, long)
self.assertGreaterEqual(size, 32)
self.assertLess(size, 8000)
q = query("select repeat('foo!', 2000)")
size = q.memsize()
self.assertGreaterEqual(size, 8000)
self.assertLess(size, 16000)


class TestUnicodeQueries(unittest.TestCase):
"""Test unicode strings as queries via a basic pg connection."""
Expand Down
6 changes: 6 additions & 0 deletions tests/test_classic_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ def testDefBase(self):
pg.set_defbase(d0)
self.assertEqual(pg.get_defbase(), d0)

def testPqlibVersion(self):
v = pg.get_pqlib_version()
self.assertIsInstance(v, long)
self.assertGreater(v, 90000)
self.assertLess(v, 130000)


class TestParseArray(unittest.TestCase):
"""Test the array parser."""
Expand Down

0 comments on commit f4b0223

Please sign in to comment.