Repository: edgedb/edgedb

Issue:
ISE: Trying to apply a migration with a `multirange` having an invalid subtype
<!-- 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: 4.0-dev.7914+a750e1c
- EdgeDB CLI Version: EdgeDB CLI 3.5.0-dev

Steps to Reproduce:

1. `edgedb migration create` - this works, but it should probably provide a helpful error here
2. `edgedb migration apply` - fails here with:

```
edgedb error: InternalServerError: KeyError: ('int2',)
  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
  Server traceback:
      Traceback (most recent call last):
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler_pool/worker.py", line 203, in compile_in_tx
          units, cstate = COMPILER.compile_in_tx(cstate, *args, **kwargs)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 922, in compile_in_tx
          return compile(ctx=ctx, source=source), ctx.state
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2292, in compile
          return _try_compile(ctx=ctx, source=source)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2353, in _try_compile
          comp, capabilities = _compile_dispatch_ql(
                               ^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/compiler.py", line 2198, in _compile_dispatch_ql
          query = ddl.compile_dispatch_ql_migration(
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/ddl.py", line 375, in compile_dispatch_ql_migration
          return compile_and_apply_ddl_stmt(ctx, ql)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/ddl.py", line 209, in compile_and_apply_ddl_stmt
          block, new_types, config_ops = _process_delta(ctx, delta)
                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/server/compiler/ddl.py", line 322, in _process_delta
          schema = pgdelta.apply(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 7428, in apply
          schema = super().apply(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 1579, in apply
          schema = self.apply_subcommands(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 188, in apply_subcommands
          schema = super().apply_subcommands(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 1606, in apply_subcommands
          schema = objop.apply(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 3093, in apply
          schema = self._create_innards(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 3061, in _create_innards
          return self.apply_subcommands(schema, context)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 188, in apply_subcommands
          schema = super().apply_subcommands(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 976, in apply_subcommands
          schema = op.apply(schema, context=context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 3862, in apply
          schema = super().apply(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 3093, in apply
          schema = self._create_innards(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/inheriting.py", line 774, in _create_innards
          return super()._create_innards(schema, context)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 3061, in _create_innards
          return self.apply_subcommands(schema, context)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 188, in apply_subcommands
          schema = super().apply_subcommands(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 976, in apply_subcommands
          schema = op.apply(schema, context=context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/schema/delta.py", line 3089, in apply
          schema = self._create_begin(schema, context)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 5780, in _create_begin
          self._create_property(prop, src, schema, orig_schema, context)
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 5643, in _create_property
          cols = self.get_columns(
                 ^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/delta.py", line 4091, in get_columns
          ptr_stor_info = types.get_pointer_storage_info(pointer, schema=schema)
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/types.py", line 579, in get_pointer_storage_info
          column_type = _resolve_type(schema, pointer)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/types.py", line 492, in _resolve_type
          column_type = pg_type_from_object(
                        ^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/types.py", line 349, in pg_type_from_object
          return pg_type_multirange(tp)
                 ^^^^^^^^^^^^^^^^^^^^^^
        File "/Users/scotttrinh/Library/Application Support/edgedb/portable/4.0-dev.7914/lib/python3.11/site-packages/edb/pgsql/types.py", line 302, in pg_type_multirange
          return type_to_multirange_name_map[tp]
                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^
      KeyError: ('int2',)
edgedb error: error in one of the migrations
```

<!-- If the issue is about a query error, please also provide your schema -->

Schema:
```ruby
module default {
  type WithMultiRange {
    required ranges: multirange<int16>;
  };
}
```


Discussion:

Resolving patch:
diff --git a/edb/schema/types.py b/edb/schema/types.py
--- a/edb/schema/types.py
+++ b/edb/schema/types.py
@@ -3144,12 +3144,23 @@ def validate_object(
 
         if isinstance(self.scls, (Range, MultiRange)):
             from . import scalars as s_scalars
+            from edb.pgsql import types as pgtypes
 
             st = self.scls.get_subtypes(schema)[0]
 
-            if isinstance(st, s_scalars.ScalarType) and not st.is_base_type(
-                schema
-            ):
+            # general rule of what's supported
+            supported = (
+                isinstance(st, s_scalars.ScalarType) and st.is_base_type(schema)
+            )
+
+            if supported:
+                # actually test that it's supported
+                try:
+                    pgtypes.pg_type_from_object(schema, self.scls)
+                except Exception:
+                    supported = False
+
+            if not supported:
                 raise errors.UnsupportedFeatureError(
                     f'unsupported range subtype: {st.get_displayname(schema)}'
                 )


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
@@ -6121,6 +6121,39 @@ async def test_edgeql_ddl_scalar_14(self):
             '''
             )
 
+        async with self.assertRaisesRegexTx(
+            edgedb.UnsupportedFeatureError, r'unsupported range subtype'
+        ):
+            await self.con.execute(
+                '''
+                create type User {
+                    create property age -> range<schema::Object>;
+                };
+            '''
+            )
+
+        async with self.assertRaisesRegexTx(
+            edgedb.UnsupportedFeatureError, r'unsupported range subtype'
+        ):
+            await self.con.execute(
+                '''
+                create type User {
+                    create property age -> multirange<int16>;
+                };
+            '''
+            )
+
+        async with self.assertRaisesRegexTx(
+            edgedb.UnsupportedFeatureError, r'unsupported range subtype'
+        ):
+            await self.con.execute(
+                '''
+                create type User {
+                    create property age -> multirange<schema::Object>;
+                };
+            '''
+            )
+
     async def test_edgeql_ddl_cast_01(self):
         await self.con.execute('''
             CREATE SCALAR TYPE type_a EXTENDING std::str;