]> git.ipfire.org Git - thirdparty/psycopg.git/commitdiff
fix: don't add a cast to text literals
authorDaniele Varrazzo <daniele.varrazzo@gmail.com>
Sun, 22 May 2022 00:08:03 +0000 (02:08 +0200)
committerDaniele Varrazzo <daniele.varrazzo@gmail.com>
Tue, 24 May 2022 08:58:15 +0000 (10:58 +0200)
Normally cast is not added to unknown literals, and the text oid is not
usually used. However, when it is, it causes problems. Often Postgres
wants just a literal, not a cast type; for instance, this is not valid:

    NOTIFY foo: 'bar'::text;

psycopg/psycopg/_transform.py
psycopg_c/psycopg_c/_psycopg/transform.pyx
tests/test_adapt.py
tests/test_sql.py

index 91577f92dd3d49321b4bc6e785b15dd62daa3a9d..200f31c45d905712b770875da41068a7be2987f3 100644 (file)
@@ -14,7 +14,7 @@ from . import errors as e
 from .abc import Buffer, LoadFunc, AdaptContext, PyFormat, DumperKey
 from .rows import Row, RowMaker
 from ._compat import TypeAlias
-from .postgres import INVALID_OID
+from .postgres import INVALID_OID, TEXT_OID
 from ._encodings import pgconn_encoding
 
 if TYPE_CHECKING:
@@ -189,11 +189,11 @@ class Transformer(AdaptContext):
     def as_literal(self, obj: Any) -> Buffer:
         dumper = self.get_dumper(obj, PyFormat.TEXT)
         rv = dumper.quote(obj)
-        # If the result is quoted, and the oid not unknown,
+        # If the result is quoted, and the oid not unknown or text,
         # add an explicit type cast.
         # Check the last char because the first one might be 'E'.
         oid = dumper.oid
-        if oid and rv and rv[-1] == b"'"[0]:
+        if oid and rv and rv[-1] == b"'"[0] and oid != TEXT_OID:
             try:
                 type_sql = self._oid_types[oid]
             except KeyError:
index e290c3adbf67fd291bcbd853fddcbf175e9eb3d5..8f2b5f3ee202e570dacacab45395d440abe041e6 100644 (file)
@@ -215,10 +215,10 @@ cdef class Transformer:
 
         rv = dumper.quote(obj)
         oid = dumper.oid
-        # If the result is quoted and the oid not unknown,
+        # If the result is quoted and the oid not unknown or text,
         # add an explicit type cast.
         # Check the last char because the first one might be 'E'.
-        if oid and rv and rv[-1] == 39:
+        if oid and oid != oids.TEXT_OID and rv and rv[-1] == 39:
             if self._oid_types is None:
                 self._oid_types = {}
             type_ptr = PyDict_GetItem(<object>self._oid_types, oid)
index e43038c9b67e24415fd1f71e42a04bafd18ad1e4..7437a71ab47e720a4c12cb60a12362851641ca20 100644 (file)
@@ -142,7 +142,7 @@ def test_dumper_protocol(conn):
     assert cur.fetchone()[0] == "hellohello"
     cur = conn.execute("select %s", [["hi", "ha"]])
     assert cur.fetchone()[0] == ["hihi", "haha"]
-    assert sql.Literal("hello").as_string(conn) == "'qelloqello'::text"
+    assert sql.Literal("hello").as_string(conn) == "'qelloqello'"
 
 
 def test_loader_protocol(conn):
index 304bcc779eb1886f77ed869670a9f70ddffa1d15..43a58aa27e83c2e581be10c3674ebc9df43cf913 100644 (file)
@@ -356,6 +356,10 @@ class TestLiteral:
             == "'{\"2000-01-01 00:00:00\"}'::timestamp[]"
         )
 
+    def test_text_literal(self, conn):
+        conn.adapters.register_dumper(str, StrDumper)
+        assert sql.Literal("foo").as_string(conn) == "'foo'"
+
     @pytest.mark.parametrize("name", ["a-b", f"{eur}", "order", "foo bar"])
     def test_invalid_name(self, conn, name):
         conn.execute(