Repository: edgedb/edgedb

Issue:
Identifier quoting clashes with fully-qualified names
The point of identifier quoting is that it allows using non-alphanumeric characters or reserved keywords as identifiers. So consider the following:
```
CREATE TYPE `foo::Bar`;
```
It will be interpreted as a single identifier, which then gets prefixed with the `default` module name to create a fully-qualified name with module `default` and name `foo::Bar`. Unfortunately, during processing it gets normalized into a single string `"default::foo::Bar"`, which then further down the line gets (mis-)interpreted as module `default::foo` and name `Bar`.
```
edgedb> CREATE TYPE `foo::Bar`;
CREATE
edgedb> WITH MODULE schema
....... SELECT ObjectType {name};
UnknownModuleError: module 'default::foo' is not in this schema
```

So the question is what is the right fix here?
- We could ban `::` inside quoted identifiers, for the same reasons we ban `@` and `__` at the beginning of them already. It causes clashes and it's problematic to disambiguate in some circumstances (such as in `__type__.name` string).
- We could review what it means to be a fully-qualified name and whether it really *has* to be 2 identifier tokens separated by a `::` token or whether it's OK to just have a single quoted identifier with the `::` inside of it. This would mean that the type from the example would simply be equivalent to a plain `foo::Bar`, meaning module `foo` type-name `Bar`.

Personally, I'm leaning towards a ban. @1st1 and @elprans , what do you think?


Discussion:
Let's ban them for now. I don't think it's possible to make them work given the time constraints we have now. We can think about how we can unban '::' later.

Resolving patch:
diff --git a/edb/edgeql/parser/grammar/lexer.py b/edb/edgeql/parser/grammar/lexer.py
--- a/edb/edgeql/parser/grammar/lexer.py
+++ b/edb/edgeql/parser/grammar/lexer.py
@@ -278,9 +278,13 @@ def token_from_text(self, rule_token, txt):
         elif rule_token == 'BADIDENT':
             self.handle_error(txt)
 
-        elif rule_token == 'QIDENT' and txt[1] == '@':
-            self.handle_error(f'Identifiers cannot start with "@"',
-                              exact_message=True)
+        elif rule_token == 'QIDENT':
+            if txt[1] == '@':
+                self.handle_error(f'Identifiers cannot start with "@"',
+                                  exact_message=True)
+            elif '::' in txt:
+                self.handle_error(f'Identifiers cannot contain "::"',
+                                  exact_message=True)
 
         tok = super().token_from_text(rule_token, txt)
 


Test patch:
diff --git a/tests/test_edgeql_syntax.py b/tests/test_edgeql_syntax.py
--- a/tests/test_edgeql_syntax.py
+++ b/tests/test_edgeql_syntax.py
@@ -761,7 +761,6 @@ def test_edgeql_syntax_name_01(self):
         SELECT `foo`::`bar`;
         SELECT `foo``bar`;
         SELECT `foo`::`bar```;
-        SELECT `foo::bar`;
 
 % OK %
 
@@ -773,7 +772,6 @@ def test_edgeql_syntax_name_01(self):
         SELECT foo::bar;
         SELECT `foo``bar`;
         SELECT foo::`bar```;
-        SELECT `foo::bar`;
         """
 
     def test_edgeql_syntax_name_02(self):
@@ -949,6 +947,13 @@ def test_edgeql_syntax_name_22(self):
         SELECT mod::Foo.bar.baz.boo;
         """
 
+    @tb.must_fail(errors.EdgeQLSyntaxError,
+                  r'Identifiers cannot contain "::"', line=2, col=16)
+    def test_edgeql_syntax_name_23(self):
+        """
+        SELECT `foo::bar`;
+        """
+
     def test_edgeql_syntax_shape_01(self):
         """
         SELECT Foo {bar};