]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
Drop Cursor query, params attribute.
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 24 Aug 2021 17:05:50 +0000 (19:05 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 24 Aug 2021 17:19:09 +0000 (19:19 +0200)
Document instead a `_query` object (renamed from the previous `_pgq`) as
helpful but subject to change.

docs/api/cursors.rst
psycopg/psycopg/_queries.py
psycopg/psycopg/cursor.py
tests/test_copy.py
tests/test_copy_async.py
tests/test_cursor.py
tests/test_cursor_async.py
tests/test_server_cursor.py
tests/test_server_cursor_async.py

index f6841683a64f975a50b7ca35d24d083b9838d5df..e61857dc3bb312e7ab00ad55a5dce18fb88b7360 100644 (file)
@@ -180,15 +180,41 @@ The `!Cursor` class
     .. autoattribute:: rowcount
     .. autoattribute:: rownumber
 
-    .. autoattribute:: query
+    .. attribute:: _query
 
-        The query will be in PostgreSQL format (with ``$1``, ``$2``...
-        parameters), the parameters will *not* be merged to the query: see
-        `params`.
+        An helper object used to convert queries and parameters before sending
+        them to PostgreSQL.
 
-    .. autoattribute:: params
+        .. note::
+            This attribute is exposed because it might be helpful to debug
+            problems when the communication between Python and PostgreSQL
+            doesn't work as expected. For this reason, the attribute is
+            available when a query fails too.
+
+            .. warning::
+                You shouldn't consider it part of the public interface of the
+                object: it might change without warnings.
+
+                Except this warning, I guess.
+
+            If you would like to build reliable features using this object,
+            please get in touch so we can try and design an useful interface
+            for it.
+
+        Among the properties currently exposed by this object:
+
+        - `!query` (`!bytes`): the query effectively sent to PostgreSQL. It
+          will have Python placeholders (``%s``\-style) replaced with
+          PostgreSQL ones (``$1``, ``$2``\-style).
+
+        - `!params` (sequence of `!bytes`): the parameters passed to
+          PostgreSQL, adapted to the database format.
+
+        - `!types` (sequence of `!int`): the OID of the parameters passed to
+          PostgreSQL.
 
-        The parameters are adapted to PostgreSQL format.
+        - `!formats` (sequence of `pq.Format`): whether the parameter format
+          is text or binary.
 
 
 The `!ServerCursor` class
index 4a492bd3a65e4581faca7ee3372e49dd3ccd6761..25091f43821d041c9cfcddeef287be11bd964e33 100644 (file)
@@ -31,8 +31,8 @@ class PostgresQuery:
     """
 
     __slots__ = """
-        params types formats
-        _tx _want_formats _parts query _encoding _order
+        query params types formats
+        _tx _want_formats _parts _encoding _order
         """.split()
 
     def __init__(self, transformer: "Transformer"):
index b826c7e40e2a1f6695f84ad7b972347e72905311..1c24af237093f0876bd7fe4e6a07a98d12f7dbb8 100644 (file)
@@ -44,7 +44,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
     if sys.version_info >= (3, 7):
         __slots__ = """
             _conn format _adapters arraysize _closed _results pgresult _pos
-            _iresult _rowcount _pgq _tx _last_query _row_factory _make_row
+            _iresult _rowcount _query _tx _last_query _row_factory _make_row
             __weakref__
             """.split()
 
@@ -71,7 +71,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
         self._pos = 0
         self._iresult = 0
         self._rowcount = -1
-        self._pgq: Optional[PostgresQuery] = None
+        self._query: Optional[PostgresQuery] = None
 
     def __repr__(self) -> str:
         cls = f"{self.__class__.__module__}.{self.__class__.__qualname__}"
@@ -98,16 +98,6 @@ class BaseCursor(Generic[ConnectionType, Row]):
         """`True` if the cursor is closed."""
         return self._closed
 
-    @property
-    def query(self) -> Optional[bytes]:
-        """The last query sent to the server, if available."""
-        return self._pgq.query if self._pgq else None
-
-    @property
-    def params(self) -> Optional[List[Optional[bytes]]]:
-        """The last set of parameters sent to the server, if available."""
-        return self._pgq.params if self._pgq else None
-
     @property
     def description(self) -> Optional[List[Column]]:
         """
@@ -216,7 +206,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
         for params in params_seq:
             if first:
                 pgq = self._convert_query(query, params)
-                self._pgq = pgq
+                self._query = pgq
                 first = False
             else:
                 pgq.dump(params)
@@ -335,7 +325,7 @@ class BaseCursor(Generic[ConnectionType, Row]):
 
         This is not a generator, but a normal non-blocking function.
         """
-        self._pgq = query
+        self._query = query
         if query.params or no_pqexec or self.format == Format.BINARY:
             self._conn.pgconn.send_query_params(
                 query.query,
index f0d15dcf8a3cf01ba564883ba30a21e932f063cd..d955ced01ec4326e53c51ba30195bda706f86f54 100644 (file)
@@ -470,8 +470,8 @@ def test_copy_rowcount(conn):
 def test_copy_query(conn):
     cur = conn.cursor()
     with cur.copy("copy (select 1) to stdout") as copy:
-        assert cur.query == b"copy (select 1) to stdout"
-        assert cur.params is None
+        assert cur._query.query == b"copy (select 1) to stdout"
+        assert cur._query.params is None
         list(copy)
 
 
index 428258193dcd90a59103609b11d20677ff3fe681..9d1f76311c4ff8c8b94e411fdee36687e1ae7e88 100644 (file)
@@ -457,8 +457,8 @@ async def test_copy_rowcount(aconn):
 async def test_copy_query(aconn):
     cur = aconn.cursor()
     async with cur.copy("copy (select 1) to stdout") as copy:
-        assert cur.query == b"copy (select 1) to stdout"
-        assert cur.params is None
+        assert cur._query.query == b"copy (select 1) to stdout"
+        assert cur._query.params is None
         async for record in copy:
             pass
 
index 0f9173daf6ec76646b78911408297b1ed67c9cba..214971f0d318fc500b5b3598a67088fca3cd5126 100644 (file)
@@ -385,37 +385,29 @@ def test_scroll(conn):
 
 def test_query_params_execute(conn):
     cur = conn.cursor()
-    assert cur.query is None
-    assert cur.params is None
+    assert cur._query is None
 
     cur.execute("select %t, %s::text", [1, None])
-    assert cur.query == b"select $1, $2::text"
-    assert cur.params == [b"1", None]
+    assert cur._query.query == b"select $1, $2::text"
+    assert cur._query.params == [b"1", None]
 
     cur.execute("select 1")
-    assert cur.query == b"select 1"
-    assert cur.params is None
+    assert cur._query.query == b"select 1"
+    assert cur._query.params is None
 
     with pytest.raises(psycopg.DataError):
         cur.execute("select %t::int", ["wat"])
 
-    assert cur.query == b"select $1::int"
-    assert cur.params == [b"wat"]
+    assert cur._query.query == b"select $1::int"
+    assert cur._query.params == [b"wat"]
 
 
 def test_query_params_executemany(conn):
     cur = conn.cursor()
 
     cur.executemany("select %t, %t", [[1, 2], [3, 4]])
-    assert cur.query == b"select $1, $2"
-    assert cur.params == [b"3", b"4"]
-
-    with pytest.raises((psycopg.DataError, TypeError)):
-        cur.executemany("select %t::int", [[1], ["x"], [2]])
-    assert cur.query == b"select $1::int"
-    # TODO: cannot really check this: after introduced row_dumpers, this
-    # fails dumping, not query passing.
-    # assert cur.params == [b"x"]
+    assert cur._query.query == b"select $1, $2"
+    assert cur._query.params == [b"3", b"4"]
 
 
 def test_stream(conn):
index 78a411f403ec62020e4005e877ab0972dca70a23..7f3c1c22e54040ae5ba9f372dc4b08bea73e9899 100644 (file)
@@ -387,37 +387,29 @@ async def test_scroll(aconn):
 
 async def test_query_params_execute(aconn):
     cur = aconn.cursor()
-    assert cur.query is None
-    assert cur.params is None
+    assert cur._query is None
 
     await cur.execute("select %t, %s::text", [1, None])
-    assert cur.query == b"select $1, $2::text"
-    assert cur.params == [b"1", None]
+    assert cur._query.query == b"select $1, $2::text"
+    assert cur._query.params == [b"1", None]
 
     await cur.execute("select 1")
-    assert cur.query == b"select 1"
-    assert cur.params is None
+    assert cur._query.query == b"select 1"
+    assert cur._query.params is None
 
     with pytest.raises(psycopg.DataError):
         await cur.execute("select %t::int", ["wat"])
 
-    assert cur.query == b"select $1::int"
-    assert cur.params == [b"wat"]
+    assert cur._query.query == b"select $1::int"
+    assert cur._query.params == [b"wat"]
 
 
 async def test_query_params_executemany(aconn):
     cur = aconn.cursor()
 
     await cur.executemany("select %t, %t", [[1, 2], [3, 4]])
-    assert cur.query == b"select $1, $2"
-    assert cur.params == [b"3", b"4"]
-
-    with pytest.raises((psycopg.DataError, TypeError)):
-        await cur.executemany("select %t::int", [[1], ["x"], [2]])
-    assert cur.query == b"select $1::int"
-    # TODO: cannot really check this: after introduced row_dumpers, this
-    # fails dumping, not query passing.
-    # assert cur.params == [b"x"]
+    assert cur._query.query == b"select $1, $2"
+    assert cur._query.params == [b"3", b"4"]
 
 
 async def test_stream(aconn):
index dfd13b19a1f7b2930dbe82cbb349145b0225a4e9..b595658b6725aafbe797d9f4892b2e6539e000f0 100644 (file)
@@ -43,12 +43,11 @@ def test_format(conn):
 
 def test_query_params(conn):
     with conn.cursor("foo") as cur:
-        assert cur.query is None
-        assert cur.params is None
+        assert cur._query is None
         cur.execute("select generate_series(1, %s) as bar", (3,))
-        assert b"declare" in cur.query.lower()
-        assert b"(1, $1)" in cur.query.lower()
-        assert cur.params == [bytes([0, 3])]  # 3 as binary int2
+        assert b"declare" in cur._query.query.lower()
+        assert b"(1, $1)" in cur._query.query.lower()
+        assert cur._query.params == [bytes([0, 3])]  # 3 as binary int2
 
 
 def test_close(conn, recwarn, retries):
index 4b724af32c19be1e7225f23f70db7beeaa3146c9..bfaa959c41f4e12e00d9867242b53bf9272c4f59 100644 (file)
@@ -45,12 +45,11 @@ async def test_format(aconn):
 
 async def test_query_params(aconn):
     async with aconn.cursor("foo") as cur:
-        assert cur.query is None
-        assert cur.params is None
+        assert cur._query is None
         await cur.execute("select generate_series(1, %s) as bar", (3,))
-        assert b"declare" in cur.query.lower()
-        assert b"(1, $1)" in cur.query.lower()
-        assert cur.params == [bytes([0, 3])]  # 3 as binary int2
+        assert b"declare" in cur._query.query.lower()
+        assert b"(1, $1)" in cur._query.query.lower()
+        assert cur._query.params == [bytes([0, 3])]  # 3 as binary int2
 
 
 async def test_close(aconn, recwarn, retries):