From: Daniele Varrazzo Date: Tue, 9 Feb 2021 00:52:50 +0000 (+0100) Subject: Added pgconn.send_describe_prepared, send_describe_portal X-Git-Tag: 3.0.dev0~115^2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4162ab7672a3ceb46ef264194f989feb1d6c9fcd;p=thirdparty%2Fpsycopg.git Added pgconn.send_describe_prepared, send_describe_portal --- diff --git a/psycopg3/psycopg3/pq/_pq_ctypes.py b/psycopg3/psycopg3/pq/_pq_ctypes.py index 1f9846b97..280038041 100644 --- a/psycopg3/psycopg3/pq/_pq_ctypes.py +++ b/psycopg3/psycopg3/pq/_pq_ctypes.py @@ -472,7 +472,13 @@ PQsendQueryPrepared.argtypes = [ ] PQsendQueryPrepared.restype = c_int -# TODO: PQsendDescribePrepared PQsendDescribePortal +PQsendDescribePrepared = pq.PQsendDescribePrepared +PQsendDescribePrepared.argtypes = [PGconn_ptr, c_char_p] +PQsendDescribePrepared.restype = c_int + +PQsendDescribePortal = pq.PQsendDescribePortal +PQsendDescribePortal.argtypes = [PGconn_ptr, c_char_p] +PQsendDescribePortal.restype = c_int PQgetResult = pq.PQgetResult PQgetResult.argtypes = [PGconn_ptr] diff --git a/psycopg3/psycopg3/pq/_pq_ctypes.pyi b/psycopg3/psycopg3/pq/_pq_ctypes.pyi index 39485f91b..e6728339f 100644 --- a/psycopg3/psycopg3/pq/_pq_ctypes.pyi +++ b/psycopg3/psycopg3/pq/_pq_ctypes.pyi @@ -174,6 +174,8 @@ def PQescapeBytea(arg1: bytes, arg2: int, arg3: pointer[c_ulong]) -> pointer[c_u def PQunescapeBytea(arg1: bytes, arg2: pointer[c_ulong]) -> pointer[c_ubyte]: ... def PQsendQuery(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... def PQsendQueryParams(arg1: Optional[PGconn_struct], arg2: bytes, arg3: int, arg4: pointer[c_uint], arg5: pointer[c_char_p], arg6: pointer[c_int], arg7: pointer[c_int], arg8: int) -> int: ... +def PQsendDescribePrepared(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... +def PQsendDescribePortal(arg1: Optional[PGconn_struct], arg2: bytes) -> int: ... def PQgetResult(arg1: Optional[PGconn_struct]) -> PGresult_struct: ... def PQconsumeInput(arg1: Optional[PGconn_struct]) -> int: ... def PQisBusy(arg1: Optional[PGconn_struct]) -> int: ... diff --git a/psycopg3/psycopg3/pq/pq_ctypes.py b/psycopg3/psycopg3/pq/pq_ctypes.py index 75d901552..f017beb42 100644 --- a/psycopg3/psycopg3/pq/pq_ctypes.py +++ b/psycopg3/psycopg3/pq/pq_ctypes.py @@ -463,6 +463,15 @@ class PGconn: raise MemoryError("couldn't allocate PGresult") return PGresult(rv) + def send_describe_prepared(self, name: bytes) -> None: + if not isinstance(name, bytes): + raise TypeError(f"bytes expected, got {type(name)} instead") + self._ensure_pgconn() + if not impl.PQsendDescribePrepared(self.pgconn_ptr, name): + raise PQerror( + f"sending describe prepared failed: {error_message(self)}" + ) + def describe_portal(self, name: bytes) -> "PGresult": if not isinstance(name, bytes): raise TypeError(f"'name' must be bytes, got {type(name)} instead") @@ -472,6 +481,15 @@ class PGconn: raise MemoryError("couldn't allocate PGresult") return PGresult(rv) + def send_describe_portal(self, name: bytes) -> None: + if not isinstance(name, bytes): + raise TypeError(f"bytes expected, got {type(name)} instead") + self._ensure_pgconn() + if not impl.PQsendDescribePortal(self.pgconn_ptr, name): + raise PQerror( + f"sending describe portal failed: {error_message(self)}" + ) + def get_result(self) -> Optional["PGresult"]: rv = impl.PQgetResult(self.pgconn_ptr) return PGresult(rv) if rv else None diff --git a/psycopg3_c/psycopg3_c/pq/pgconn.pyx b/psycopg3_c/psycopg3_c/pq/pgconn.pyx index a979fcdeb..f277d13ef 100644 --- a/psycopg3_c/psycopg3_c/pq/pgconn.pyx +++ b/psycopg3_c/psycopg3_c/pq/pgconn.pyx @@ -369,6 +369,14 @@ cdef class PGconn: raise MemoryError("couldn't allocate PGresult") return PGresult._from_ptr(rv) + def send_describe_prepared(self, const char *name) -> None: + _ensure_pgconn(self) + cdef int rv = libpq.PQsendDescribePrepared(self.pgconn_ptr, name) + if not rv: + raise PQerror( + f"sending describe prepared failed: {error_message(self)}" + ) + def describe_portal(self, const char *name) -> PGresult: _ensure_pgconn(self) cdef libpq.PGresult *rv = libpq.PQdescribePortal(self.pgconn_ptr, name) @@ -376,6 +384,14 @@ cdef class PGconn: raise MemoryError("couldn't allocate PGresult") return PGresult._from_ptr(rv) + def send_describe_portal(self, const char *name) -> None: + _ensure_pgconn(self) + cdef int rv = libpq.PQsendDescribePortal(self.pgconn_ptr, name) + if not rv: + raise PQerror( + f"sending describe prepared failed: {error_message(self)}" + ) + def get_result(self) -> Optional["PGresult"]: cdef libpq.PGresult *pgresult = libpq.PQgetResult(self.pgconn_ptr) if pgresult is NULL: diff --git a/tests/pq/test_async.py b/tests/pq/test_async.py index 0e648b373..68d95383b 100644 --- a/tests/pq/test_async.py +++ b/tests/pq/test_async.py @@ -172,3 +172,40 @@ def test_send_prepared_binary_out(pgconn, fmt, out): (res,) = execute_wait(pgconn) assert res.status == pq.ExecStatus.TUPLES_OK assert res.get_value(0, 0) == out + + +def test_send_describe_prepared(pgconn): + pgconn.send_prepare(b"prep", b"select $1::int + $2::int as fld") + (res,) = execute_wait(pgconn) + assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message + + pgconn.send_describe_prepared(b"prep") + (res,) = execute_wait(pgconn) + assert res.nfields == 1 + assert res.ntuples == 0 + assert res.fname(0) == b"fld" + assert res.ftype(0) == 23 + + pgconn.finish() + with pytest.raises(psycopg3.OperationalError): + pgconn.send_describe_prepared(b"prep") + + +def test_send_describe_portal(pgconn): + res = pgconn.exec_( + b""" + begin; + declare cur cursor for select * from generate_series(1,10) foo; + """ + ) + assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message + + pgconn.send_describe_portal(b"cur") + (res,) = execute_wait(pgconn) + assert res.status == pq.ExecStatus.COMMAND_OK, res.error_message + assert res.nfields == 1 + assert res.fname(0) == b"foo" + + pgconn.finish() + with pytest.raises(psycopg3.OperationalError): + pgconn.send_describe_portal(b"cur")