Repository: edgedb/edgedb

Issue:
Mangled `InvalidValueError` message when casting to `<uuid>`
<!-- Please search existing issues to avoid creating duplicates. -->

<!--
For the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).
For the EdgeDB CLI Version: Run `edgedb --version` from anywhere
-->

- EdgeDB Version: 2.9+b1d697b
- EdgeDB CLI Version: 2.2.6+b869667
- OS Version: linux-nixos (local binary)

Steps to Reproduce:

1. Start `edgedb` command connecting to any instance.
2. type query
```bash
edgedb>select <uuid>"a-non-valid-string";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "a-non-valid-string"
```
So far so good. Things get wired when the invalid string contain the sequence "uuid" delimited with space or punctuations except underscore("_"). The invalid value reported in the error message has the sequence "uuid" replaced with "std::uuid"
```config
# single occurrence
edgedb> select <uuid>"uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::uuid"
# not exact sequence
edgedb> select <uuid>"xuuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "xuuid"
edgedb> select <uuid>"uuidx";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "uuidx"
edgedb> select <uuid>"uuiduuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "uuiduuid"
# delimited with punctuation
edgedb> select <uuid>"uuid-uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::uuid-std::uuid"
edgedb> select <uuid>"uuid uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::uuid std::uuid"
edgedb> select <uuid>"uuid  uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::uuid  std::uuid"
edgedb> select <uuid>"uuid|uuid@uuid#uuid$uuid%uuid^uuid&uuid*uuid(uuid)uuid[uuid]uuid{uuid}uuid'uuid\"uuid,uuid.uuid<uuid>uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::uuid|std::uuid@std::uuid#std::uuid$std::uuid%std::uuid^std::uuid&std::uuid*std::uuid(std::uuid)std::uuid[std::uuid]std::uuid{std::uuid}std::uuid'std::uuid"std::uuid,std::uuid.std::uuid<std::uuid>std::uuid"
# the sequence "std::uuid"
edgedb> select <uuid>"std::uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "std::std::uuid"
# except for underscore
edgedb> select <uuid>"uuid_uuid";
edgedb error: InvalidValueError: invalid input syntax for type std::uuid: "uuid_uuid"
```
Similar behaviour when the value contains these type words

`bool` -> `std::bool`
`bigint` -> `std::int64`

This is unexpected, inconsistent, and makes testing for expected errors difficult. E.g. in client libraries.


Discussion:
Ha. This is due to https://github.com/edgedb/edgedb/blob/master/edb/server/compiler/errormech.py#L120-L145, which does some pretty simple-minded find-replacing to fix up postgres errors into edgedb ones.

Maybe we want to just apply the regex before the first colon.

I'm not sure how principled of an approach is possible here, since the input we are getting is not fully structured

Resolving patch:
diff --git a/edb/server/compiler/errormech.py b/edb/server/compiler/errormech.py
--- a/edb/server/compiler/errormech.py
+++ b/edb/server/compiler/errormech.py
@@ -123,7 +123,8 @@ class ErrorDetails(NamedTuple):
     r'(?P<p>enum) (?P<v>edgedb([\w-]+)."(?P<id>[\w-]+)_domain")')
 
 
-def translate_pgtype(schema, msg):
+def translate_pgtype_inner(schema, msg):
+    """Try to replace any internal pg type name with an edgedb type name"""
     translated = pgtype_re.sub(
         lambda r: str(types.base_type_name_map_r.get(r.group(0), r.group(0))),
         msg,
@@ -145,6 +146,22 @@ def replace(r):
     return translated
 
 
+def translate_pgtype(schema, msg):
+    """Try to translate a message that might refer to internal pg types.
+
+    We *want* to replace internal pg type names with edgedb names, but only
+    when they actually refer to types.
+    The messages aren't really structured well enough to support this properly,
+    so we approximate it by only doing the replacement *before* the first colon
+    in the message, so if a user does `<int64>"bigint"`, and we get the message
+    'invalid input syntax for type bigint: "bigint"', we do the right thing.
+    """
+
+    leading, *rest = msg.split(':')
+    leading_translated = translate_pgtype_inner(schema, leading)
+    return ':'.join([leading_translated, *rest])
+
+
 def get_error_details(fields):
     # See https://www.postgresql.org/docs/current/protocol-error-fields.html
     # for the full list of PostgreSQL error message fields.


Test patch:
diff --git a/tests/test_edgeql_casts.py b/tests/test_edgeql_casts.py
--- a/tests/test_edgeql_casts.py
+++ b/tests/test_edgeql_casts.py
@@ -1377,6 +1377,12 @@ async def test_edgeql_casts_numeric_08(self):
             await self.con.query_single(
                 'SELECT <decimal>"12313.132n"')
 
+        async with self.assertRaisesRegexTx(
+                edgedb.InvalidValueError,
+                r"invalid input syntax for type std::bigint: 'bigint'"):
+            await self.con.query_single(
+                'SELECT <bigint>"bigint"')
+
     async def test_edgeql_casts_collections_01(self):
         await self.assert_query_result(
             r'''SELECT <array<str>>[1, 2, 3];''',