Repository: edgedb/edgedb

Issue:
Cannot get item data: item *x* is not present in the schema
<!-- Please search existing issues to avoid creating duplicates. -->
- EdgeDB Version: 2.0
- OS Version: Ubuntu 18.04 LTS

Steps to Reproduce:

1. Get the schema provided below
2. Create a migration using the schema

<!-- If the issue is about a query error, please also provide your schema -->
Schema:

```
module stats {
    type RoundStats {
        required property rounds_played -> int32 {
            default := 0;
        }

        required property rounds_completed -> int32 {
            default := 0;
        }

        required property win_ratio := .rounds_completed / .rounds_played;
    }

    type DailyStats extending RoundStats {
        required property max_streak -> int32 {
            default := 0;
        }

        required multi property days_completed -> int32 {
            default := <array<int32>>[];
        }
    }
    
    type ContinuousStats {
        required property easy -> RoundStats {
            constraint exclusive;
        }

        required property intermediate -> RoundStats {
            constraint exclusive;
        }

        required property hard -> RoundStats {
            constraint exclusive;
        }

        required property rounds_played := sum(.easy.rounds_played, .intermediate.rounds_played, .hard.rounds_played);
        required property rounds_completed := sum(.easy.rounds_completed, .intermediate.rounds_completed, .hard.rounds_completed);
        required property win_ratio := sum(.easy.win_ratio, .intermediate.win_ratio, .hard.win_ratio) / 3;
    }

    type Stats {
        required link daily -> DailyStats {
            constraint exclusive;
        }

        required link continuous -> ContinuousStats {
            constraint exclusive;
        }

        required property rounds_played := sum(.daily.rounds_played, .continuous.rounds_played);
        required property rounds_completed := sum(.daily.rounds_completed, .continuous.rounds_played);
        required property win_ratio := sum(.daily.win_ratio, .continuous.win_ratio) / 2;
    }
}

module submission {
    type Submission {
        required property beatmap_id -> int32 {
            constraint exclusive;
        }

        required multi property hints -> str {
            constraint expression on (
                len(.hints) >= 2
            );
        }

        required property level -> str;

        required link submitted_by -> user::User;
    }
}

module user {
    type User {
        required property username -> str {
            constraint min_len_value(3);
            constraint max_len_value(16);
            constraint exclusive;
        }

        required property safe_username := str_replace(str_lower(str_trim(.name)), ' ', '_');

        required property email -> str {
            constraint exclusive;
            constraint regexp(r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$");
        }

        required property password_bcrypt -> str;
        optional property discord_id -> int32;

        required property created_at -> datetime {
            readonly := true;
            default := datetime_current();
        }

        required property can_submit -> bool {
            default := false;
        }

        required link stats -> stats::Stats {
            constraint exclusive;
        }

        required multi link submissions := .<submitted_by[is submission::Submission];
    }
}
```


Discussion:
Aha:
We should produce an error on this schema, because `array<int32>` is the wrong type for `days_completed`.

But `array<int32>` isn't in the schema, so trying to pretty print its name using the real schema doesn't work---we need to use the schema produce from compilng the default
hm, maybe i'm misunderstanding the use of `multi` then? should i be using `array<int32>` without `multi` as my property?
`multi` does not mean array, it means a set, so `multi property foo -> array<int32>` is a set of arrays, `multi property foo -> int32` is a set of 32-bit integers, and `[single] property foo -> array<int32>` is a single array of 32-bit integers.
To clarify, in your case you should either use `multi property days_completed -> int32` or `property days_completed -> array<int32>` depending on what's more convenient for you (sets are easier to mutate, arrays are a bit faster, and are easier to index)

Resolving patch:
diff --git a/edb/schema/constraints.py b/edb/schema/constraints.py
--- a/edb/schema/constraints.py
+++ b/edb/schema/constraints.py
@@ -799,11 +799,12 @@ def _populate_concrete_constraint_attrs(
         assert isinstance(final_expr.irast, ir_ast.Statement)
 
         expr_type = final_expr.irast.stype
-        if not expr_type.issubclass(schema, bool_t):
+        expr_schema = final_expr.irast.schema
+        if not expr_type.issubclass(expr_schema, bool_t):
             raise errors.InvalidConstraintDefinitionError(
                 f'{name} constraint expression expected '
                 f'to return a bool value, got '
-                f'{expr_type.get_verbosename(schema)}',
+                f'{expr_type.get_verbosename(expr_schema)}',
                 context=sourcectx
             )
 
diff --git a/edb/schema/globals.py b/edb/schema/globals.py
--- a/edb/schema/globals.py
+++ b/edb/schema/globals.py
@@ -231,6 +231,7 @@ def validate_object(
 
             assert isinstance(default_expr.irast, irast.Statement)
 
+            default_schema = default_expr.irast.schema
             default_type = default_expr.irast.stype
 
             source_context = self.get_attribute_source_context('default')
@@ -241,10 +242,10 @@ def validate_object(
                     context=source_context,
                 )
 
-            if not default_type.assignment_castable_to(target, schema):
+            if not default_type.assignment_castable_to(target, default_schema):
                 raise errors.SchemaDefinitionError(
                     f'default expression is of invalid type: '
-                    f'{default_type.get_displayname(schema)}, '
+                    f'{default_type.get_displayname(default_schema)}, '
                     f'expected {target.get_displayname(schema)}',
                     context=source_context,
                 )
diff --git a/edb/schema/pointers.py b/edb/schema/pointers.py
--- a/edb/schema/pointers.py
+++ b/edb/schema/pointers.py
@@ -1595,21 +1595,23 @@ def validate_object(
 
             assert isinstance(default_expr.irast, irast.Statement)
 
+            default_schema = default_expr.irast.schema
             default_type = default_expr.irast.stype
             assert default_type is not None
             ptr_target = scls.get_target(schema)
             assert ptr_target is not None
 
             source_context = self.get_attribute_source_context('default')
-            if default_type.is_view(default_expr.irast.schema):
+            if default_type.is_view(default_schema):
                 raise errors.SchemaDefinitionError(
                     f'default expression may not include a shape',
                     context=source_context,
                 )
-            if not default_type.assignment_castable_to(ptr_target, schema):
+            if not default_type.assignment_castable_to(
+                    ptr_target, default_schema):
                 raise errors.SchemaDefinitionError(
                     f'default expression is of invalid type: '
-                    f'{default_type.get_displayname(schema)}, '
+                    f'{default_type.get_displayname(default_schema)}, '
                     f'expected {ptr_target.get_displayname(schema)}',
                     context=source_context,
                 )


Test patch:
diff --git a/tests/test_edgeql_ddl.py b/tests/test_edgeql_ddl.py
--- a/tests/test_edgeql_ddl.py
+++ b/tests/test_edgeql_ddl.py
@@ -1935,6 +1935,30 @@ async def test_edgeql_ddl_default_09(self):
                 };
             ''')
 
+    async def test_edgeql_ddl_default_10(self):
+        with self.assertRaisesRegex(
+                edgedb.SchemaDefinitionError,
+                'default expression is of invalid type: array<std::int32>, '
+                'expected array<std::int16>'):
+            await self.con.execute(r"""
+                CREATE TYPE X {
+                    CREATE PROPERTY y -> array<int16> {
+                        SET default := <array<int32>>[]
+                    };
+                };
+            """)
+
+    async def test_edgeql_ddl_default_11(self):
+        with self.assertRaisesRegex(
+                edgedb.SchemaDefinitionError,
+                'default expression is of invalid type: array<std::int32>, '
+                'expected array<std::int16>'):
+            await self.con.execute(r"""
+                CREATE GLOBAL y -> array<int16> {
+                    SET default := <array<int32>>[]
+                };
+            """)
+
     async def test_edgeql_ddl_default_circular(self):
         await self.con.execute(r"""
             CREATE TYPE TestDefaultCircular {
@@ -9563,6 +9587,18 @@ async def test_edgeql_ddl_constraint_21(self):
                 insert B { x := "!" }
             """)
 
+    async def test_edgeql_ddl_constraint_22(self):
+        async with self.assertRaisesRegexTx(
+                edgedb.InvalidConstraintDefinitionError,
+                r'expected to return a bool value, got collection'):
+            await self.con.execute("""
+                create type X {
+                    create property y -> str {
+                        create constraint expression on (<array<int32>>[]);
+                    }
+                };
+            """)
+
     async def test_edgeql_ddl_constraint_check_01a(self):
         await self.con.execute(r"""
             create type Foo {