Repository: edgedb/edgedb

Issue:
InternalServerError: maximum recursion depth exceeded while calling a Python object
- EdgeDB Version: "2.9+b1d697b"
- EdgeDB CLI Version: EdgeDB CLI 2.2.6+af56767
- OS Version: Windows 10
Steps to Reproduce:

1. default.esdl
```
module default {
    type Requirement {
		required property level -> str {
			default := '';
		}
		required property function -> str {
			default := '';
		}
		required property feature -> str {
			default := '';
		}
		required property component -> str {
			default := '';
		}
		required property name -> str {
			default := '';
		}
		multi link children -> Requirement;
	}
}
```

2. data are 7 requirements like linkedList via children, no loop. The last requirement within list has component = 'component 1'.
The goal is to query requirements where child requirement.component = 'component 1'.
 
4. query

```
select Requirement {
  name,
  level,
  children: {name, level, component}
}
filter .children[is Requirement].component = 'component 1';
```

5. results in InternalServerError: maximum recursion depth exceeded while calling a Python object

- selecting all objects shows list of data:
- query:
```
select Requirement {
  name,
  level,
  children: {
    name, 
    level, 
    children: {
      name, 
      level,
      children: {
        name, 
        level,
        children: {
          name, 
          level,
          children: {
            name, 
            level,
            children: {
              name, 
              level,
              component
            }
          }
        }
      }
    }
  }
}
filter .level='SYS.1' and .name = 'requirement 1.1'
```

- result:
```
[
  {
    "name": "requirement 1.1",
    "level": "SYS.1",
    "children": [
      {
        "name": "requirement 2.1",
        "level": "SYS.2",
        "children": [
          {
            "name": "requirement 3.1",
            "level": "SYS.3",
            "children": [
              {
                "name": "requirement 4.1",
                "level": "SWE.1 CR",
                "children": [
                  {
                    "name": "requirement 5.1",
                    "level": "SWE.1 SR",
                    "children": [
                      {
                        "name": "requirement 6.1",
                        "level": "SWE.2",
                        "children": [
                          {
                            "name": "requirement 7.1",
                            "level": "SWE.3",
                            "component": "component 1"
                          }
                        ]
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
]
```

- query:
```
select Requirement {
  name,
  level,
}
filter .children.children.children.children.children.children.component = 'component 1'
```

- result:
```
[
  {
    "name": "requirement 1.1",
    "level": "SYS.1"
  }
]
```


Discussion:
Looks like an infinite recursion bug in the cardinality inference.

Workaround: drop the `[is Requirement]` from the filter. It isn't needed, and seems linked to the problem. (Though of course if this is a reduced test case, maybe it's necessary in the real thing)

Resolving patch:
diff --git a/edb/edgeql/compiler/inference/cardinality.py b/edb/edgeql/compiler/inference/cardinality.py
--- a/edb/edgeql/compiler/inference/cardinality.py
+++ b/edb/edgeql/compiler/inference/cardinality.py
@@ -557,6 +557,7 @@ def _infer_set_inner(
     if rptr is not None:
         rptrref = rptr.ptrref
 
+        assert ir is not rptr.source, "self-referential pointer"
         source_card = infer_cardinality(
             rptr.source, scope_tree=scope_tree, ctx=ctx,
         )
diff --git a/edb/edgeql/compiler/setgen.py b/edb/edgeql/compiler/setgen.py
--- a/edb/edgeql/compiler/setgen.py
+++ b/edb/edgeql/compiler/setgen.py
@@ -277,8 +277,8 @@ def compile_path(expr: qlast.Path, *, ctx: context.ContextLevel) -> irast.Set:
                 'could not resolve partial path ',
                 context=expr.context)
 
-    computables = []
-    path_sets = []
+    computables: list[irast.Set] = []
+    path_sets: list[irast.Set] = []
 
     for i, step in enumerate(expr.steps):
         is_computable = False
@@ -542,7 +542,10 @@ def compile_path(expr: qlast.Path, *, ctx: context.ContextLevel) -> irast.Set:
         if pathctx.path_is_inserting(path_tip.path_id, ctx=ctx):
             raise_self_insert_error(stype, step.context, ctx=ctx)
 
-        path_sets.append(path_tip)
+        # Don't track this step of the path if it didn't change the set
+        # (probably because of do-nothing intersection)
+        if not path_sets or path_sets[-1] != path_tip:
+            path_sets.append(path_tip)
 
     path_tip.context = expr.context
     pathctx.register_set_in_scope(path_tip, ctx=ctx)


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
@@ -1684,6 +1684,19 @@ async def test_edgeql_select_polymorphic_13(self):
             ],
         )
 
+    async def test_edgeql_select_polymorphic_14(self):
+        # This one isn't *really* polymorphic, and that caused some trouble
+        await self.assert_query_result(
+            r'''
+            select Issue { number, related_to }
+            filter exists .related_to;
+            ''',
+            tb.bag([
+                {'number': "3", 'related_to': [{}]},
+                {'number': "4", 'related_to': [{}]},
+            ]),
+        )
+
     async def test_edgeql_select_id_01(self):
         # allow assigning id to a computed (#4781)
         await self.con.query('SELECT schema::Type { XYZ := .id};')