mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 08:56:34 +03:00
Merge branch 'libpq-ptrs'
This commit is contained in:
commit
d08be18671
4
NEWS
4
NEWS
|
@ -17,7 +17,9 @@ New features:
|
||||||
- Added `connection.info` object to retrieve various PostgreSQL connection
|
- Added `connection.info` object to retrieve various PostgreSQL connection
|
||||||
information (:ticket:`#726`).
|
information (:ticket:`#726`).
|
||||||
- Added `~connection.get_native_connection()` to expose the raw ``PGconn``
|
- Added `~connection.get_native_connection()` to expose the raw ``PGconn``
|
||||||
structure (:ticket:`#782`).
|
structure to C extensions via Capsule (:ticket:`#782`).
|
||||||
|
- Added `~connection.pgconn_ptr` and `~cursor.pgresult_ptr` to expose raw
|
||||||
|
C structures to Python and interact with libpq via ctypes (:ticket:`#782`).
|
||||||
- `~psycopg2.sql.Identifier` can represent qualified names in SQL composition
|
- `~psycopg2.sql.Identifier` can represent qualified names in SQL composition
|
||||||
(:ticket:`#732`).
|
(:ticket:`#732`).
|
||||||
- Added *fetch* parameter to `~psycopg2.extras.execute_values()` function
|
- Added *fetch* parameter to `~psycopg2.extras.execute_values()` function
|
||||||
|
|
|
@ -738,11 +738,27 @@ The ``connection`` class
|
||||||
|
|
||||||
Return `!True` if the connection is executing an asynchronous operation.
|
Return `!True` if the connection is executing an asynchronous operation.
|
||||||
|
|
||||||
|
|
||||||
.. rubric:: Interoperation with other C API modules
|
.. rubric:: Interoperation with other C API modules
|
||||||
|
|
||||||
|
.. attribute:: pgconn_ptr
|
||||||
|
|
||||||
|
Return the internal `!PGconn*` as integer. Useful to pass the libpq
|
||||||
|
raw connection structure to C functions, e.g. via `ctypes`::
|
||||||
|
|
||||||
|
>>> import ctypes
|
||||||
|
>>> libpq = ctypes.pydll.LoadLibrary(ctypes.util.find_library('pq'))
|
||||||
|
>>> libpq.PQserverVersion.argtypes = [ctypes.c_void_p]
|
||||||
|
>>> libpq.PQserverVersion.restype = ctypes.c_int
|
||||||
|
>>> libpq.PQserverVersion(conn.pgconn_ptr)
|
||||||
|
90611
|
||||||
|
|
||||||
|
.. versionadded:: 2.8
|
||||||
|
|
||||||
|
|
||||||
.. method:: get_native_connection()
|
.. method:: get_native_connection()
|
||||||
|
|
||||||
Return the internal `PGconn*` wrapped in a PyCapsule object. This is
|
Return the internal `!PGconn*` wrapped in a PyCapsule object. This is
|
||||||
only useful for passing the `libpq` raw connection associated to this
|
only useful for passing the `libpq` raw connection associated to this
|
||||||
connection object to other C-level modules that may have a use for it.
|
connection object to other C-level modules that may have a use for it.
|
||||||
|
|
||||||
|
|
|
@ -632,6 +632,24 @@ The ``cursor`` class
|
||||||
using Unicode data instead of bytes.
|
using Unicode data instead of bytes.
|
||||||
|
|
||||||
|
|
||||||
|
.. rubric:: Interoperation with other C API modules
|
||||||
|
|
||||||
|
.. attribute:: pgresult_ptr
|
||||||
|
|
||||||
|
Return the cursor's internal `!PGresult*` as integer. Useful to pass
|
||||||
|
the libpq raw result structure to C functions, e.g. via `ctypes`::
|
||||||
|
|
||||||
|
>>> import ctypes
|
||||||
|
>>> libpq = ctypes.pydll.LoadLibrary(ctypes.util.find_library('pq'))
|
||||||
|
>>> libpq.PQcmdStatus.argtypes = [ctypes.c_void_p]
|
||||||
|
>>> libpq.PQcmdStatus.restype = ctypes.c_char_p
|
||||||
|
|
||||||
|
>>> curs.execute("select 'x'")
|
||||||
|
>>> libpq.PQcmdStatus(curs.pgresult_ptr)
|
||||||
|
b'SELECT 1'
|
||||||
|
|
||||||
|
.. versionadded:: 2.8
|
||||||
|
|
||||||
.. testcode::
|
.. testcode::
|
||||||
:hide:
|
:hide:
|
||||||
|
|
||||||
|
|
|
@ -1008,7 +1008,7 @@ psyco_conn_get_backend_pid(connectionObject *self, PyObject *dummy)
|
||||||
|
|
||||||
/* get info about the connection */
|
/* get info about the connection */
|
||||||
|
|
||||||
#define psyco_conn_info_get_doc \
|
#define psyco_conn_info_doc \
|
||||||
"info -- Get connection info."
|
"info -- Get connection info."
|
||||||
|
|
||||||
static PyObject *
|
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 */
|
/* reset the currect connection */
|
||||||
|
|
||||||
#define psyco_conn_reset_doc \
|
#define psyco_conn_reset_doc \
|
||||||
|
@ -1270,7 +1287,10 @@ static struct PyGetSetDef connectionObject_getsets[] = {
|
||||||
psyco_conn_deferrable_doc },
|
psyco_conn_deferrable_doc },
|
||||||
{ "info",
|
{ "info",
|
||||||
(getter)psyco_conn_info_get, NULL,
|
(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}
|
{NULL}
|
||||||
};
|
};
|
||||||
#undef EXCEPTION_GETTER
|
#undef EXCEPTION_GETTER
|
||||||
|
|
|
@ -107,6 +107,8 @@ psyco_curs_close(cursorObject *self, PyObject *dummy)
|
||||||
}
|
}
|
||||||
|
|
||||||
close:
|
close:
|
||||||
|
CLEARPGRES(self->pgres);
|
||||||
|
|
||||||
self->closed = 1;
|
self->closed = 1;
|
||||||
Dprintf("psyco_curs_close: cursor at %p closed", self);
|
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 **/
|
/** the cursor object **/
|
||||||
|
|
||||||
/* iterator protocol */
|
/* iterator protocol */
|
||||||
|
@ -1842,6 +1859,9 @@ static struct PyGetSetDef cursorObject_getsets[] = {
|
||||||
(getter)psyco_curs_scrollable_get,
|
(getter)psyco_curs_scrollable_get,
|
||||||
(setter)psyco_curs_scrollable_set,
|
(setter)psyco_curs_scrollable_set,
|
||||||
psyco_curs_scrollable_doc, NULL },
|
psyco_curs_scrollable_doc, NULL },
|
||||||
|
{ "pgresult_ptr",
|
||||||
|
(getter)psyco_curs_pgresult_ptr_get, NULL,
|
||||||
|
psyco_curs_pgresult_ptr_doc, NULL },
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
import ctypes
|
||||||
import threading
|
import threading
|
||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
from operator import attrgetter
|
from operator import attrgetter
|
||||||
|
@ -346,6 +347,20 @@ class ConnectionTests(ConnectingTestCase):
|
||||||
# we can't do anything else in Python
|
# we can't do anything else in Python
|
||||||
self.assertIsNotNone(capsule)
|
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):
|
class ParseDsnTestCase(ConnectingTestCase):
|
||||||
def test_parse_dsn(self):
|
def test_parse_dsn(self):
|
||||||
from psycopg2 import ProgrammingError
|
from psycopg2 import ProgrammingError
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
# License for more details.
|
# License for more details.
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
import ctypes
|
||||||
import pickle
|
import pickle
|
||||||
import psycopg2
|
import psycopg2
|
||||||
import psycopg2.extensions
|
import psycopg2.extensions
|
||||||
|
@ -650,6 +651,23 @@ class CursorTests(ConnectingTestCase):
|
||||||
[(i,) for i in range(5)])
|
[(i,) for i in range(5)])
|
||||||
self.assertEqual(cur.rowcount, 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():
|
def test_suite():
|
||||||
return unittest.TestLoader().loadTestsFromName(__name__)
|
return unittest.TestLoader().loadTestsFromName(__name__)
|
||||||
|
|
|
@ -26,10 +26,12 @@ import re
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import types
|
import types
|
||||||
|
import ctypes
|
||||||
import select
|
import select
|
||||||
import platform
|
import platform
|
||||||
import unittest
|
import unittest
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from ctypes.util import find_library
|
||||||
from .testconfig import dsn, repl_dsn
|
from .testconfig import dsn, repl_dsn
|
||||||
from psycopg2.compat import text_type
|
from psycopg2.compat import text_type
|
||||||
|
|
||||||
|
@ -174,6 +176,21 @@ class ConnectingTestCase(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
raise Exception("Unexpected result from poll: %r", state)
|
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')
|
||||||
|
if libname is None and platform.system() == 'Windows':
|
||||||
|
raise self.skipTest("can't import libpq on windows")
|
||||||
|
|
||||||
|
rv = ConnectingTestCase._libpq = ctypes.pydll.LoadLibrary(libname)
|
||||||
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def decorate_all_tests(obj, *decorators):
|
def decorate_all_tests(obj, *decorators):
|
||||||
"""
|
"""
|
||||||
|
|
Loading…
Reference in New Issue
Block a user