Repository: edgedb/edgedb

Issue:
Prohibit functions from sharing shortname with types
The following scenario should be prohibited:

```
type foo { ... }
create function foo { ... }
```

See also https://github.com/edgedb/edgedb/issues/1439 for additional context.


Discussion:

Resolving patch:
diff --git a/edb/schema/delta.py b/edb/schema/delta.py
--- a/edb/schema/delta.py
+++ b/edb/schema/delta.py
@@ -2118,6 +2118,16 @@ def _create_begin(
 
         props = self.get_resolved_attributes(schema, context)
         metaclass = self.get_schema_metaclass()
+
+        # Check if functions by this name exist
+        fn = props.get('name')
+        if fn is not None:
+            funcs = schema.get_functions(fn, tuple())
+            if funcs:
+                raise errors.SchemaError(
+                    f'{funcs[0].get_verbosename(schema)} is already present '
+                    f'in the schema {schema!r}')
+
         schema, self.scls = metaclass.create_in_schema(schema, **props)
 
         if not props.get('id'):
diff --git a/edb/schema/functions.py b/edb/schema/functions.py
--- a/edb/schema/functions.py
+++ b/edb/schema/functions.py
@@ -1301,14 +1301,21 @@ def _create_begin(
         schema, cp = self._get_param_desc_from_delta(schema, context, self)
         signature = f'{shortname}({", ".join(p.as_str(schema) for p in cp)})'
 
-        func = schema.get(fullname, None)
-        if func:
+        if func := schema.get(fullname, None):
             raise errors.DuplicateFunctionDefinitionError(
                 f'cannot create the `{signature}` function: '
                 f'a function with the same signature '
                 f'is already defined',
                 context=self.source_context)
 
+        # Check if other schema objects with the same name (ignoring
+        # signature, of course) exist.
+        if other := schema.get(
+                sn.SchemaName(shortname, fullname.module), None):
+            raise errors.SchemaError(
+                f'{other.get_verbosename(schema)} is already present '
+                f'in the schema {schema!r}')
+
         schema = super()._create_begin(schema, context)
 
         params: FuncParameterList = self.scls.get_params(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
@@ -2622,6 +2622,26 @@ async def test_edgeql_ddl_function_27(self):
                 DROP FUNCTION test::constant_decimal();
             """)
 
+    async def test_edgeql_ddl_function_28(self):
+        with self.assertRaisesRegex(
+                edgedb.SchemaError,
+                r"'test::foo' is already present in the schema"):
+
+            await self.con.execute('''\
+                CREATE TYPE test::foo;
+                CREATE FUNCTION test::foo() -> str USING ('a');
+            ''')
+
+    async def test_edgeql_ddl_function_29(self):
+        with self.assertRaisesRegex(
+                edgedb.SchemaError,
+                r"'test::foo\(\)' is already present in the schema"):
+
+            await self.con.execute('''\
+                CREATE FUNCTION test::foo() -> str USING ('a');
+                CREATE TYPE test::foo;
+            ''')
+
     async def test_edgeql_ddl_module_01(self):
         with self.assertRaisesRegex(
                 edgedb.SchemaError,
diff --git a/tests/test_schema.py b/tests/test_schema.py
--- a/tests/test_schema.py
+++ b/tests/test_schema.py
@@ -5460,8 +5460,6 @@ def test_schema_describe_escape(self):
     def test_schema_describe_poly_01(self):
         self._assert_describe(
             """
-            scalar type all extending str;
-
             type Object {
                 property real -> bool;
             }
@@ -5479,8 +5477,6 @@ def test_schema_describe_poly_01(self):
             function test::all() -> std::bool using (SELECT
                 true
             );
-            scalar type test::all extending std::str;
-
             # The following builtins are masked by the above:
 
             # function std::all(vals: SET OF std::bool) ->  std::bool {