Repository: edgedb/edgedb

Issue:
Constraints on tuple types
Defining a constraint on a tuple property currently results in an error:

```
a> CREATE TYPE Transaction {
..     CREATE PROPERTY credit -> tuple<amount: decimal, currency: str> {
..         CREATE CONSTRAINT max_value(0) ON (__subject__.amount)
..     };
.. };
InternalServerError: unexpectedly long path in simple expr
```


Discussion:
@elprans I'm getting a different error with the latest Docker image:
```
InternalServerError: None
Hint: This is most likely a bug in EdgeDB. Please consider opening an issue ticket at https://github.com/edgedb/edgedb/issues/new?template=bug_report.md
```

Resolving patch:
diff --git a/edb/pgsql/compiler/expr.py b/edb/pgsql/compiler/expr.py
--- a/edb/pgsql/compiler/expr.py
+++ b/edb/pgsql/compiler/expr.py
@@ -641,6 +641,15 @@ def _compile_set_in_singleton_mode(
             ptrref = node.rptr.ptrref
             source = node.rptr.source
 
+            if isinstance(ptrref, irast.TupleIndirectionPointerRef):
+                tuple_val = dispatch.compile(source, ctx=ctx)
+                set_expr = astutils.tuple_getattr(
+                    tuple_val,
+                    source.typeref,
+                    ptrref.shortname.name,
+                )
+                return set_expr
+
             if ptrref.source_ptr is None and source.rptr is not None:
                 raise RuntimeError(
                     'unexpectedly long path in simple expr')
diff --git a/edb/pgsql/schemamech.py b/edb/pgsql/schemamech.py
--- a/edb/pgsql/schemamech.py
+++ b/edb/pgsql/schemamech.py
@@ -73,6 +73,7 @@ def _get_ref_storage_info(cls, schema, refs):
         objtype_biased = {}
 
         ref_ptrs = {}
+        refs = list(refs)
         for ref in refs:
             rptr = ref.rptr
             if rptr is not None:
@@ -87,6 +88,9 @@ def _get_ref_storage_info(cls, schema, refs):
                         # This specialized pointer was derived specifically
                         # for the purposes of constraint expr compilation.
                         src = src.get_bases(schema).first(schema)
+                elif ptr.is_tuple_indirection():
+                    refs.append(ref.rptr.source)
+                    continue
                 else:
                     schema, src = irtyputils.ir_typeref_to_type(
                         schema, ref.rptr.source.typeref)


Test patch:
diff --git a/tests/test_constraints.py b/tests/test_constraints.py
--- a/tests/test_constraints.py
+++ b/tests/test_constraints.py
@@ -1043,3 +1043,27 @@ async def test_constraints_ddl_error_06(self):
                         };
                     };
                 """)
+
+    async def test_constraints_tuple(self):
+        async with self._run_and_rollback():
+            await self.con.execute(r"""
+                CREATE TYPE Transaction {
+                    CREATE PROPERTY credit
+                      -> tuple<nest: tuple<amount: decimal, currency: str>> {
+                        CREATE CONSTRAINT max_value(0)
+                          ON (__subject__.nest.amount)
+                    };
+                };
+            """)
+            await self.con.execute(r"""
+                INSERT Transaction {
+                    credit := (nest := (amount := -1, currency := "usd")) };
+            """)
+
+            with self.assertRaisesRegex(
+                    edgedb.ConstraintViolationError,
+                    "Maximum allowed value for credit is 0."):
+                await self.con.execute(r"""
+                    INSERT Transaction {
+                        credit := (nest := (amount := 1, currency := "usd")) };
+                """)