mirror of
				https://github.com/psycopg/psycopg2.git
				synced 2025-11-04 09:47:30 +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
 | 
			
		||||
  information (:ticket:`#726`).
 | 
			
		||||
- 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
 | 
			
		||||
  (:ticket:`#732`).
 | 
			
		||||
- 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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .. 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()
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
        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.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    .. 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::
 | 
			
		||||
    :hide:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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__)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,21 @@ 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')
 | 
			
		||||
        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):
 | 
			
		||||
    """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue
	
	Block a user