]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Added PQescapeString wrapper
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 27 Oct 2020 23:32:07 +0000 (00:32 +0100)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 27 Oct 2020 23:55:33 +0000 (00:55 +0100)
`psycopg3.pq.Escaping.escape_string()` can now work without a connection
too, with the limitation explained in the libpq docs.

Also fixed memory leak. Oops.

psycopg3/psycopg3/pq/_pq_ctypes.py
psycopg3/psycopg3/pq/_pq_ctypes.pyi
psycopg3/psycopg3/pq/pq_ctypes.py
psycopg3_c/psycopg3_c/libpq.pxd
psycopg3_c/psycopg3_c/pq_cython.pyx
tests/pq/test_escaping.py

index fa45ef7febac1faea4c974d3093d516def4d535d..9aaa39fb893dd7ec7e6bda75955efad54b92ef26 100644 (file)
@@ -402,7 +402,10 @@ PQescapeStringConn = pq.PQescapeStringConn
 # ]
 PQescapeStringConn.restype = c_size_t
 
-# won't wrap: PQescapeString
+PQescapeString = pq.PQescapeString
+# TODO: raises "wrong type" error
+# PQescapeString.argtypes = [c_char_p, c_char_p, c_size_t]
+PQescapeString.restype = c_size_t
 
 PQescapeByteaConn = pq.PQescapeByteaConn
 PQescapeByteaConn.argtypes = [
index 90804f538ea8b17a89d8804ef2d73389aa2eca2e..ea443727bb4dc91033de9a6600df6a6bb5b67bda 100644 (file)
@@ -67,6 +67,7 @@ def PQescapeStringConn(
     arg4: int,
     arg5: pointer[c_int],
 ) -> int: ...
+def PQescapeString(arg1: c_char_p, arg2: bytes, arg3: int) -> int: ...
 def PQsendPrepare(
     arg1: Optional[PGconn_struct],
     arg2: bytes,
index 588b9a83bd9a2d5a6c6ac69a659da93f972bcec5..4147436d9fa1c3b2a5ec97a09904f5d94de821ce 100644 (file)
@@ -797,7 +797,13 @@ class Escaping:
             return out.value
 
         else:
-            raise PQerror("escape_identifier failed: no connection provided")
+            out = create_string_buffer(len(data) * 2 + 1)
+            impl.PQescapeString(
+                pointer(out),  # type: ignore
+                data,
+                len(data),
+            )
+            return out.value
 
     def escape_bytea(self, data: bytes) -> bytes:
         len_out = c_size_t()
index 908f4e3f258dccf663b217748943cbcc8cf99f1b..a9e241d59ae262f73d49f010b6b5c8a443a8160d 100644 (file)
@@ -183,12 +183,12 @@ cdef extern from "libpq-fe.h":
     Oid PQoidValue(const PGresult *res)
 
     # 33.3.4. Escaping Strings for Inclusion in SQL Commands
-    # TODO: PQescapeStringConn PQescapeString
     char *PQescapeIdentifier(PGconn *conn, const char *str, size_t length)
     char *PQescapeLiteral(PGconn *conn, const char *str, size_t length)
     size_t PQescapeStringConn(PGconn *conn,
                               char *to, const char *from_, size_t length,
                               int *error)
+    size_t PQescapeString(char *to, const char *from_, size_t length)
     unsigned char *PQescapeByteaConn(PGconn *conn,
                                      const unsigned char *src,
                                      size_t from_length,
index ca0b0c576b4705dd3cdc2027ba08385d0fa0973a..567c3d8c82906e9766fd4ca4a2c123012058121d 100644 (file)
@@ -838,6 +838,8 @@ cdef class Escaping:
         cdef size_t len_data = len(data)
         cdef char *out
         cdef size_t len_out
+        cdef bytes rv
+
         if self.conn is not None:
             if self.conn.pgconn_ptr is NULL:
                 raise PQerror("the connection is closed")
@@ -848,13 +850,22 @@ cdef class Escaping:
             )
 
             if error:
+                PyMem_Free(out)
                 raise PQerror(
                     f"escape_string failed: {error_message(self.conn)}"
                 )
-            return out[:len_out]
+
+            rv = out[:len_out]
+            PyMem_Free(out)
+            return rv
 
         else:
-            raise PQerror("escape_identifier failed: no connection provided")
+            out = <char *>PyMem_Malloc(len_data * 2 + 1)
+            len_out = impl.PQescapeString(out, data, len_data)
+            rv = out[:len_out]
+            PyMem_Free(out)
+            return rv
+
 
     def escape_bytea(self, data: bytes) -> bytes:
         cdef size_t len_out
index 25fd1e07248bea31540065b040698ada010b62be..b55c5132e0fb0856f97c3d28893ab15bd8faa8b7 100644 (file)
@@ -116,11 +116,22 @@ def test_escape_string_1char(pgconn, scs):
         assert rv == exp
 
 
-def test_escape_string_noconn(pgconn):
+@pytest.mark.parametrize(
+    "data, want",
+    [
+        (b"", b""),
+        (b"hello", b"hello"),
+        (b"foo'bar", b"foo''bar"),
+        (b"foo\\bar", b"foo\\\\bar"),
+    ],
+)
+def test_escape_string_noconn(data, want):
     esc = pq.Escaping()
-    with pytest.raises(psycopg3.OperationalError):
-        esc.escape_string(b"hi")
+    out = esc.escape_string(data)
+    assert out == want
+
 
+def test_escape_string_badconn(pgconn):
     esc = pq.Escaping(pgconn)
     pgconn.finish()
     with pytest.raises(psycopg3.OperationalError):