Repository: edgedb/edgedb

Issue:
Prohibit pointer name shadowing in shapes
Currently, EdgeDB would silently accept queries like:

```
SELECT Foo { bar, bar }
```

Confusingly, even though the output contains two properties, internally the latter definition "wins", shadowing the first one:

```
db> SELECT Foo { bar, bar := 'spam' }
{Object { name: 'spam', name: 'spam' }}
```

This behavior is even more confusing in the polymorphic shapes.

We *could* fix this by adding internal disambiguation, but then the ambiguity will simply shift to the output, where one would have two identically named fields.  IMO, it would be best to simply disallow such queries.






Discussion:
Also, see the original report by @hsteindev on [Spectrum](https://spectrum.chat/edgedb/general/some-potential-issues-i-came-across-when-doing-my-first-experiments-with-edgedb~49e55a68-7013-4908-a14f-21c37e50e5a2).
I would try to take this. I changed the sources a bit, and now it's handled like this:

![image](https://user-images.githubusercontent.com/22559461/80282941-f8217e00-871c-11ea-90f4-b08ccf9d5f16.png)

If this is OK, then I can write some tests and open PR, right?
Hi @nsidnev, yes please add some tests and open a PR. Thanks!

Resolving patch:
diff --git a/edb/edgeql/compiler/viewgen.py b/edb/edgeql/compiler/viewgen.py
--- a/edb/edgeql/compiler/viewgen.py
+++ b/edb/edgeql/compiler/viewgen.py
@@ -149,11 +149,21 @@ def _process_view(
 
     for shape_el in elements:
         with ctx.newscope(fenced=True) as scopectx:
-            pointers.append(_normalize_view_ptr_expr(
+            pointer = _normalize_view_ptr_expr(
                 shape_el, view_scls, path_id=path_id,
                 path_id_namespace=path_id_namespace,
                 is_insert=is_insert, is_update=is_update,
-                view_rptr=view_rptr, ctx=scopectx))
+                view_rptr=view_rptr, ctx=scopectx)
+
+            if pointer in pointers:
+                schema = ctx.env.schema
+                vnp = pointer.get_verbosename(schema, with_parent=True)
+
+                raise errors.QueryError(
+                    f'duplicate definition of {vnp}',
+                    context=shape_el.context)
+
+            pointers.append(pointer)
 
     if is_insert:
         assert isinstance(stype, s_objtypes.ObjectType)


Test patch:
diff --git a/tests/test_edgeql_select.py b/tests/test_edgeql_select.py
--- a/tests/test_edgeql_select.py
+++ b/tests/test_edgeql_select.py
@@ -5560,3 +5560,48 @@ async def test_edgeql_select_is_13(self):
             ''',
             [True],
         )
+
+    async def test_edgeql_select_duplicate_definition_01(self):
+        with self.assertRaisesRegex(
+            edgedb.QueryError,
+            "duplicate definition of property 'name' of object type "
+            "'test::User'",
+            _position=110,
+        ):
+            await self.con.execute("""
+                WITH MODULE test
+                SELECT User {
+                    name,
+                    name
+                }
+            """)
+
+    async def test_edgeql_select_duplicate_definition_02(self):
+        with self.assertRaisesRegex(
+            edgedb.QueryError,
+            "duplicate definition of property 'name' of object type "
+            "'test::User'",
+            _position=110,
+        ):
+            await self.con.execute("""
+                WITH MODULE test
+                SELECT User {
+                    name,
+                    name := "new_name"
+                }
+            """)
+
+    async def test_edgeql_select_duplicate_definition_03(self):
+        with self.assertRaisesRegex(
+            edgedb.QueryError,
+            "duplicate definition of link 'todo' of object type "
+            "'test::User'",
+            _position=110,
+        ):
+            await self.con.execute("""
+                WITH MODULE test
+                SELECT User {
+                    todo,
+                    todo
+                }
+            """)