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:

Patch:
diff --git a/docs/edgeql/lexical.rst b/docs/edgeql/lexical.rst
index 3263eda6d..e37024031 100644
--- a/docs/edgeql/lexical.rst
+++ b/docs/edgeql/lexical.rst
@@ -25,11 +25,12 @@ There are two ways of writing identifiers in EdgeQL: plain and quoted.
 The plain identifiers are similar to many other languages, they are
 alphanumeric with underscores and cannot start with a digit. The
 quoted identifiers start and end with a *backtick*
-```quoted.identifier``` and can contain any characters inside, but
-must not start with an ampersand (``@``). If there's a need to include
-a backtick character as part of the identifier name a double-backtick
-sequence (``````) should be used: ```quoted``identifier``` will result
-in the actual identifier being ``quoted`identifier``.
+```quoted.identifier``` and can contain any characters inside with a
+few exceptions. They must not start with an ampersand (``@``) or
+contain double-colon (``::``). If there's a need to include a backtick
+character as part of the identifier name a double-backtick sequence
+(``````) should be used: ```quoted``identifier``` will result in the
+actual identifier being ``quoted`identifier``.
 
 .. productionlist:: edgeql
     identifier: `plain_ident` | `quoted_ident`
diff --git a/docs/internals/dev.rst b/docs/internals/dev.rst
index 6c6717e90..b810e9163 100644
--- a/docs/internals/dev.rst
+++ b/docs/internals/dev.rst
@@ -20,7 +20,14 @@ Linux or macOS.  Windows is not currently supported.
 * Python 3.7 dev package;
 * Bison 1.875 or later;
 * Flex 2.5.31 or later;
-* Perl 5.8.3 or later.
+* Perl 5.8.3 or later;
+* Zlib (zlibg1-dev on Ubuntu);
+* Readline dev package;
+* Libuuid dev package.
+
+.. zlib, readline and libuuid are required to build postgres. Should be removed
+   when custom postgres build is no longer needed.
+
 
 .. rubric:: Instructions
 
diff --git a/edb/edgeql/parser/grammar/lexer.py b/edb/edgeql/parser/grammar/lexer.py
index 8613056fc..5dcdd6495 100644
--- a/edb/edgeql/parser/grammar/lexer.py
+++ b/edb/edgeql/parser/grammar/lexer.py
@@ -278,9 +278,13 @@ class EdgeQLLexer(lexer.Lexer):
         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
index 995cb839d..f7b5d4f90 100644
--- a/tests/test_edgeql_syntax.py
+++ b/tests/test_edgeql_syntax.py
@@ -761,7 +761,6 @@ aa';
         SELECT `foo`::`bar`;
         SELECT `foo``bar`;
         SELECT `foo`::`bar```;
-        SELECT `foo::bar`;
 
 % OK %
 
@@ -773,7 +772,6 @@ aa';
         SELECT foo::bar;
         SELECT `foo``bar`;
         SELECT foo::`bar```;
-        SELECT `foo::bar`;
         """
 
     def test_edgeql_syntax_name_02(self):
@@ -949,6 +947,13 @@ aa';
         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};