Added connection.pgconn_ptr and cursor.pgresult_ptr

Allow interacting with libpq in Python via ctypes.

See #782.
This commit is contained in:
Daniele Varrazzo 2019-02-16 17:30:34 +01:00
parent 1dd8c7435f
commit 7c7bbb9742
5 changed files with 89 additions and 2 deletions

View File

@ -1008,7 +1008,7 @@ psyco_conn_get_backend_pid(connectionObject *self, PyObject *dummy)
/* get info about the connection */
#define psyco_conn_info_get_doc \
#define psyco_conn_info_doc \
"info -- Get connection info."
static PyObject *
@ -1019,6 +1019,23 @@ psyco_conn_info_get(connectionObject *self)
}
/* return the pointer to the PGconn structure */
#define psyco_conn_pgconn_ptr_doc \
"pgconn_ptr -- Get the PGconn structure pointer."
static PyObject *
psyco_conn_pgconn_ptr_get(connectionObject *self)
{
if (self->pgconn) {
return PyLong_FromVoidPtr((void *)self->pgconn);
}
else {
Py_RETURN_NONE;
}
}
/* reset the currect connection */
#define psyco_conn_reset_doc \
@ -1270,7 +1287,10 @@ static struct PyGetSetDef connectionObject_getsets[] = {
psyco_conn_deferrable_doc },
{ "info",
(getter)psyco_conn_info_get, NULL,
psyco_conn_info_get_doc },
psyco_conn_info_doc },
{ "pgconn_ptr",
(getter)psyco_conn_pgconn_ptr_get, NULL,
psyco_conn_pgconn_ptr_doc },
{NULL}
};
#undef EXCEPTION_GETTER

View File

@ -107,6 +107,8 @@ psyco_curs_close(cursorObject *self, PyObject *dummy)
}
close:
CLEARPGRES(self->pgres);
self->closed = 1;
Dprintf("psyco_curs_close: cursor at %p closed", self);
@ -1716,6 +1718,21 @@ psyco_curs_scrollable_set(cursorObject *self, PyObject *pyvalue)
}
#define psyco_curs_pgresult_ptr_doc \
"pgresult_ptr -- Get the PGresult structure pointer."
static PyObject *
psyco_curs_pgresult_ptr_get(cursorObject *self)
{
if (self->pgres) {
return PyLong_FromVoidPtr((void *)self->pgres);
}
else {
Py_RETURN_NONE;
}
}
/** the cursor object **/
/* iterator protocol */
@ -1842,6 +1859,9 @@ static struct PyGetSetDef cursorObject_getsets[] = {
(getter)psyco_curs_scrollable_get,
(setter)psyco_curs_scrollable_set,
psyco_curs_scrollable_doc, NULL },
{ "pgresult_ptr",
(getter)psyco_curs_pgresult_ptr_get, NULL,
psyco_curs_pgresult_ptr_doc, NULL },
{NULL}
};

View File

@ -26,6 +26,7 @@ import re
import os
import sys
import time
import ctypes
import threading
import subprocess as sp
from operator import attrgetter
@ -346,6 +347,20 @@ class ConnectionTests(ConnectingTestCase):
# we can't do anything else in Python
self.assertIsNotNone(capsule)
def test_pgconn_ptr(self):
conn = self.connect()
self.assert_(conn.pgconn_ptr is not None)
f = self.libpq.PQserverVersion
f.argtypes = [ctypes.c_void_p]
f.restype = ctypes.c_int
ver = f(conn.pgconn_ptr)
self.assertEqual(ver, conn.server_version)
conn.close()
self.assert_(conn.pgconn_ptr is None)
class ParseDsnTestCase(ConnectingTestCase):
def test_parse_dsn(self):
from psycopg2 import ProgrammingError

View File

@ -23,6 +23,7 @@
# License for more details.
import time
import ctypes
import pickle
import psycopg2
import psycopg2.extensions
@ -650,6 +651,23 @@ class CursorTests(ConnectingTestCase):
[(i,) for i in range(5)])
self.assertEqual(cur.rowcount, 5)
@skip_before_postgres(9)
def test_pgresult_ptr(self):
curs = self.conn.cursor()
self.assert_(curs.pgresult_ptr is None)
f = self.libpq.PQcmdStatus
f.argtypes = [ctypes.c_void_p]
f.restype = ctypes.c_char_p
curs.execute("select 'x'")
self.assert_(curs.pgresult_ptr is not None)
status = f(curs.pgresult_ptr)
self.assertEqual(status, b'SELECT 1')
curs.close()
self.assert_(curs.pgresult_ptr is None)
def test_suite():
return unittest.TestLoader().loadTestsFromName(__name__)

View File

@ -26,10 +26,12 @@ import re
import os
import sys
import types
import ctypes
import select
import platform
import unittest
from functools import wraps
from ctypes.util import find_library
from .testconfig import dsn, repl_dsn
from psycopg2.compat import text_type
@ -174,6 +176,18 @@ class ConnectingTestCase(unittest.TestCase):
else:
raise Exception("Unexpected result from poll: %r", state)
_libpq = None
@property
def libpq(self):
"""Return a ctypes wrapper for the libpq library"""
if ConnectingTestCase._libpq is not None:
return ConnectingTestCase._libpq
libname = find_library('pq')
rv = ConnectingTestCase._libpq = ctypes.pydll.LoadLibrary(libname)
return rv
def decorate_all_tests(obj, *decorators):
"""