We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
InternalServerError on migration with new constraint
I figured this constraint probably wouldn't work but wanted to try it anyway. It gives me an ISE.
<!-- Please search existing issues to avoid creating duplicates. -->

<!--
For the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).
For the EdgeDB CLI Version: Run `edgedb --version` from anywhere
-->

- EdgeDB Version: 2.13+ad0eb0f
- EdgeDB CLI Version: 3.0.0-dev.946+93cec91
- OS Version: macOS 13.2.1

Steps to Reproduce:

1. Create a project with `edgedb project init`
2. Add the problematic schema to `dbschema/default.esdl`
3. Attempt to create migration with `edgedb migration create`

<!-- If the issue is about a query error, please also provide your schema -->

Schema:

```
module default {
  type Cause {
    required property name -> str;
    required multi link charities -> Charity {
      property weight -> float64;
      constraint expression (sum(.charities@weight) <= 10);
    }
  }
  type Charity {
    required property name -> str;
  }
}
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `edb/edgeql/utils.py`
Content:
```
1 #
2 # This source file is part of the EdgeDB open source project.
3 #
4 # Copyright 2015-present MagicStack Inc. and the EdgeDB authors.
5 #
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
9 #
10 #     http://www.apache.org/licenses/LICENSE-2.0
11 #
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
17 #
18 
19 
20 from __future__ import annotations
21 
22 import copy
23 import itertools
24 from typing import *
25 
26 from edb.common import ast
27 from edb.schema import schema as s_schema
28 from edb.schema import functions as s_func
29 
30 from . import ast as qlast
31 
32 
33 FREE_SHAPE_EXPR = qlast.DetachedExpr(
34     expr=qlast.Path(
35         steps=[qlast.ObjectRef(module='std', name='FreeObject')],
36     ),
37 )
38 
39 
40 class ParameterInliner(ast.NodeTransformer):
41 
42     def __init__(self, args_map: Mapping[str, qlast.Base]) -> None:
43         super().__init__()
44         self.args_map = args_map
45 
46     def visit_Path(self, node: qlast.Path) -> qlast.Base:
47         if (len(node.steps) != 1 or
48                 not isinstance(node.steps[0], qlast.ObjectRef)):
49             self.visit(node.steps[0])
50             return node
51 
52         ref: qlast.ObjectRef = node.steps[0]
53         try:
54             arg = self.args_map[ref.name]
55         except KeyError:
56             return node
57 
58         arg = copy.deepcopy(arg)
59         return arg
60 
61 
62 def inline_parameters(
63     ql_expr: qlast.Base,
64     args: Mapping[str, qlast.Base]
65 ) -> None:
66 
67     inliner = ParameterInliner(args)
68     inliner.visit(ql_expr)
69 
70 
71 def index_parameters(
72     ql_args: List[qlast.Base],
73     *,
74     parameters: s_func.ParameterLikeList,
75     schema: s_schema.Schema
76 ) -> Dict[str, qlast.Base]:
77 
78     result: Dict[str, qlast.Base] = {}
79     varargs: Optional[List[qlast.Expr]] = None
80     variadic = parameters.find_variadic(schema)
81     variadic_num = variadic.get_num(schema) if variadic else -1  # type: ignore
82 
83     e: qlast.Expr
84     p: s_func.ParameterLike
85     for iter in itertools.zip_longest(
86         enumerate(ql_args), parameters.objects(schema), fillvalue=None
87     ):
88         (i, e), p = iter  # type: ignore
89         if isinstance(e, qlast.SelectQuery):
90             e = e.result
91 
92         if variadic and variadic_num == i:
93             assert varargs is None
94             varargs = []
95             result[p.get_parameter_name(schema)] = qlast.Array(
96                 elements=varargs
97             )
98 
99         if varargs is not None:
100             varargs.append(e)
101         else:
102             result[p.get_parameter_name(schema)] = e
103 
104     return result
105 
106 
107 class AnchorInliner(ast.NodeTransformer):
108 
109     def __init__(self, anchors: Mapping[str, qlast.Base]) -> None:
110         super().__init__()
111         self.anchors = anchors
112 
113     def visit_Path(self, node: qlast.Path) -> qlast.Path:
114         if not node.steps:
115             return node
116 
117         step0 = node.steps[0]
118 
119         if isinstance(step0, qlast.Anchor):
120             node.steps[0] = self.anchors[step0.name]  # type: ignore
121         elif isinstance(step0, qlast.ObjectRef) and step0.name in self.anchors:
122             node.steps[0] = self.anchors[step0.name]  # type: ignore
123 
124         return node
125 
126 
127 def inline_anchors(
128     ql_expr: qlast.Base,
129     anchors: Mapping[Any, qlast.Base]
130 ) -> None:
131 
132     inliner = AnchorInliner(anchors)
133     inliner.visit(ql_expr)
134 
135 
136 def find_paths(ql: qlast.Base) -> List[qlast.Path]:
137     return ast.find_children(ql, qlast.Path)
138 
139 
140 def find_subject_ptrs(ast: qlast.Base) -> Set[str]:
141     ptrs = set()
142     for path in find_paths(ast):
143         if path.partial:
144             p = path.steps[0]
145         elif isinstance(path.steps[0], qlast.Subject) and len(path.steps) > 1:
146             p = path.steps[1]
147         else:
148             continue
149 
150         if isinstance(p, qlast.Ptr):
151             ptrs.add(p.ptr.name)
152     return ptrs
153 
154 
155 def subject_paths_substitute(
156     ast: qlast.Base_T,
157     subject_ptrs: Dict[str, qlast.Expr],
158 ) -> qlast.Base_T:
159     ast = copy.deepcopy(ast)
160     for path in find_paths(ast):
161         if path.partial and isinstance(path.steps[0], qlast.Ptr):
162             path.steps[0] = subject_paths_substitute(
163                 subject_ptrs[path.steps[0].ptr.name],
164                 subject_ptrs,
165             )
166         elif (
167             isinstance(path.steps[0], qlast.Subject)
168             and len(path.steps)
169             and isinstance(path.steps[1], qlast.Ptr)
170         ):
171             path.steps[0:2] = [subject_paths_substitute(
172                 subject_ptrs[path.steps[1].ptr.name],
173                 subject_ptrs,
174             )]
175     return ast
176 
177 
178 def subject_substitute(
179         ast: qlast.Base_T, new_subject: qlast.Expr) -> qlast.Base_T:
180     ast = copy.deepcopy(ast)
181     for path in find_paths(ast):
182         if isinstance(path.steps[0], qlast.Subject):
183             path.steps[0] = new_subject
184     return ast
185 
186 
187 def contains_dml(ql_expr: qlast.Base) -> bool:
188     """Check whether a expression contains any DML in a subtree."""
189     # If this ends up being a perf problem, we can use a visitor
190     # directly and cache.
191     dml_types = (qlast.InsertQuery, qlast.UpdateQuery, qlast.DeleteQuery)
192     if isinstance(ql_expr, dml_types):
193         return True
194 
195     res = ast.find_children(ql_expr, qlast.Query,
196                             lambda x: isinstance(x, dml_types),
197                             terminate_early=True)
198 
199     return bool(res)
200
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/edb/edgeql/utils.py b/edb/edgeql/utils.py
--- a/edb/edgeql/utils.py
+++ b/edb/edgeql/utils.py
@@ -23,6 +23,7 @@
 import itertools
 from typing import *
 
+from edb import errors
 from edb.common import ast
 from edb.schema import schema as s_schema
 from edb.schema import functions as s_func
@@ -80,10 +81,21 @@
     variadic = parameters.find_variadic(schema)
     variadic_num = variadic.get_num(schema) if variadic else -1  # type: ignore
 
+    params = parameters.objects(schema)
+
+    if not variadic and len(ql_args) > len(params):
+        # In error message we discount the implicit __subject__ param.
+        raise errors.SchemaDefinitionError(
+            f'Expected {len(params) - 1} arguments, but found '
+            f'{len(ql_args) - 1}',
+            context=ql_args[-1].context,
+            details='Did you mean to use ON (...) for specifying the subject?',
+        )
+
     e: qlast.Expr
     p: s_func.ParameterLike
     for iter in itertools.zip_longest(
-        enumerate(ql_args), parameters.objects(schema), fillvalue=None
+        enumerate(ql_args), params, fillvalue=None
     ):
         (i, e), p = iter  # type: ignore
         if isinstance(e, qlast.SelectQuery):

{"golden_diff": "diff --git a/edb/edgeql/utils.py b/edb/edgeql/utils.py\n--- a/edb/edgeql/utils.py\n+++ b/edb/edgeql/utils.py\n@@ -23,6 +23,7 @@\n import itertools\n from typing import *\n \n+from edb import errors\n from edb.common import ast\n from edb.schema import schema as s_schema\n from edb.schema import functions as s_func\n@@ -80,10 +81,21 @@\n     variadic = parameters.find_variadic(schema)\n     variadic_num = variadic.get_num(schema) if variadic else -1  # type: ignore\n \n+    params = parameters.objects(schema)\n+\n+    if not variadic and len(ql_args) > len(params):\n+        # In error message we discount the implicit __subject__ param.\n+        raise errors.SchemaDefinitionError(\n+            f'Expected {len(params) - 1} arguments, but found '\n+            f'{len(ql_args) - 1}',\n+            context=ql_args[-1].context,\n+            details='Did you mean to use ON (...) for specifying the subject?',\n+        )\n+\n     e: qlast.Expr\n     p: s_func.ParameterLike\n     for iter in itertools.zip_longest(\n-        enumerate(ql_args), parameters.objects(schema), fillvalue=None\n+        enumerate(ql_args), params, fillvalue=None\n     ):\n         (i, e), p = iter  # type: ignore\n         if isinstance(e, qlast.SelectQuery):\n", "issue": "InternalServerError on migration with new constraint\nI figured this constraint probably wouldn't work but wanted to try it anyway. It gives me an ISE.\r\n<!-- Please search existing issues to avoid creating duplicates. -->\r\n\r\n<!--\r\nFor the EdgeDB Version: run `edgedb query 'select sys::get_version_as_str()'` from your project directory (or run `select sys::get_version_as_str();` in the EdgeDB interactive shell).\r\nFor the EdgeDB CLI Version: Run `edgedb --version` from anywhere\r\n-->\r\n\r\n- EdgeDB Version: 2.13+ad0eb0f\r\n- EdgeDB CLI Version: 3.0.0-dev.946+93cec91\r\n- OS Version: macOS 13.2.1\r\n\r\nSteps to Reproduce:\r\n\r\n1. Create a project with `edgedb project init`\r\n2. Add the problematic schema to `dbschema/default.esdl`\r\n3. Attempt to create migration with `edgedb migration create`\r\n\r\n<!-- If the issue is about a query error, please also provide your schema -->\r\n\r\nSchema:\r\n\r\n```\r\nmodule default {\r\n  type Cause {\r\n    required property name -> str;\r\n    required multi link charities -> Charity {\r\n      property weight -> float64;\r\n      constraint expression (sum(.charities@weight) <= 10);\r\n    }\r\n  }\r\n  type Charity {\r\n    required property name -> str;\r\n  }\r\n}\r\n```\n", "before_files": [{"content": "#\n# This source file is part of the EdgeDB open source project.\n#\n# Copyright 2015-present MagicStack Inc. and the EdgeDB authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\nfrom __future__ import annotations\n\nimport copy\nimport itertools\nfrom typing import *\n\nfrom edb.common import ast\nfrom edb.schema import schema as s_schema\nfrom edb.schema import functions as s_func\n\nfrom . import ast as qlast\n\n\nFREE_SHAPE_EXPR = qlast.DetachedExpr(\n    expr=qlast.Path(\n        steps=[qlast.ObjectRef(module='std', name='FreeObject')],\n    ),\n)\n\n\nclass ParameterInliner(ast.NodeTransformer):\n\n    def __init__(self, args_map: Mapping[str, qlast.Base]) -> None:\n        super().__init__()\n        self.args_map = args_map\n\n    def visit_Path(self, node: qlast.Path) -> qlast.Base:\n        if (len(node.steps) != 1 or\n                not isinstance(node.steps[0], qlast.ObjectRef)):\n            self.visit(node.steps[0])\n            return node\n\n        ref: qlast.ObjectRef = node.steps[0]\n        try:\n            arg = self.args_map[ref.name]\n        except KeyError:\n            return node\n\n        arg = copy.deepcopy(arg)\n        return arg\n\n\ndef inline_parameters(\n    ql_expr: qlast.Base,\n    args: Mapping[str, qlast.Base]\n) -> None:\n\n    inliner = ParameterInliner(args)\n    inliner.visit(ql_expr)\n\n\ndef index_parameters(\n    ql_args: List[qlast.Base],\n    *,\n    parameters: s_func.ParameterLikeList,\n    schema: s_schema.Schema\n) -> Dict[str, qlast.Base]:\n\n    result: Dict[str, qlast.Base] = {}\n    varargs: Optional[List[qlast.Expr]] = None\n    variadic = parameters.find_variadic(schema)\n    variadic_num = variadic.get_num(schema) if variadic else -1  # type: ignore\n\n    e: qlast.Expr\n    p: s_func.ParameterLike\n    for iter in itertools.zip_longest(\n        enumerate(ql_args), parameters.objects(schema), fillvalue=None\n    ):\n        (i, e), p = iter  # type: ignore\n        if isinstance(e, qlast.SelectQuery):\n            e = e.result\n\n        if variadic and variadic_num == i:\n            assert varargs is None\n            varargs = []\n            result[p.get_parameter_name(schema)] = qlast.Array(\n                elements=varargs\n            )\n\n        if varargs is not None:\n            varargs.append(e)\n        else:\n            result[p.get_parameter_name(schema)] = e\n\n    return result\n\n\nclass AnchorInliner(ast.NodeTransformer):\n\n    def __init__(self, anchors: Mapping[str, qlast.Base]) -> None:\n        super().__init__()\n        self.anchors = anchors\n\n    def visit_Path(self, node: qlast.Path) -> qlast.Path:\n        if not node.steps:\n            return node\n\n        step0 = node.steps[0]\n\n        if isinstance(step0, qlast.Anchor):\n            node.steps[0] = self.anchors[step0.name]  # type: ignore\n        elif isinstance(step0, qlast.ObjectRef) and step0.name in self.anchors:\n            node.steps[0] = self.anchors[step0.name]  # type: ignore\n\n        return node\n\n\ndef inline_anchors(\n    ql_expr: qlast.Base,\n    anchors: Mapping[Any, qlast.Base]\n) -> None:\n\n    inliner = AnchorInliner(anchors)\n    inliner.visit(ql_expr)\n\n\ndef find_paths(ql: qlast.Base) -> List[qlast.Path]:\n    return ast.find_children(ql, qlast.Path)\n\n\ndef find_subject_ptrs(ast: qlast.Base) -> Set[str]:\n    ptrs = set()\n    for path in find_paths(ast):\n        if path.partial:\n            p = path.steps[0]\n        elif isinstance(path.steps[0], qlast.Subject) and len(path.steps) > 1:\n            p = path.steps[1]\n        else:\n            continue\n\n        if isinstance(p, qlast.Ptr):\n            ptrs.add(p.ptr.name)\n    return ptrs\n\n\ndef subject_paths_substitute(\n    ast: qlast.Base_T,\n    subject_ptrs: Dict[str, qlast.Expr],\n) -> qlast.Base_T:\n    ast = copy.deepcopy(ast)\n    for path in find_paths(ast):\n        if path.partial and isinstance(path.steps[0], qlast.Ptr):\n            path.steps[0] = subject_paths_substitute(\n                subject_ptrs[path.steps[0].ptr.name],\n                subject_ptrs,\n            )\n        elif (\n            isinstance(path.steps[0], qlast.Subject)\n            and len(path.steps)\n            and isinstance(path.steps[1], qlast.Ptr)\n        ):\n            path.steps[0:2] = [subject_paths_substitute(\n                subject_ptrs[path.steps[1].ptr.name],\n                subject_ptrs,\n            )]\n    return ast\n\n\ndef subject_substitute(\n        ast: qlast.Base_T, new_subject: qlast.Expr) -> qlast.Base_T:\n    ast = copy.deepcopy(ast)\n    for path in find_paths(ast):\n        if isinstance(path.steps[0], qlast.Subject):\n            path.steps[0] = new_subject\n    return ast\n\n\ndef contains_dml(ql_expr: qlast.Base) -> bool:\n    \"\"\"Check whether a expression contains any DML in a subtree.\"\"\"\n    # If this ends up being a perf problem, we can use a visitor\n    # directly and cache.\n    dml_types = (qlast.InsertQuery, qlast.UpdateQuery, qlast.DeleteQuery)\n    if isinstance(ql_expr, dml_types):\n        return True\n\n    res = ast.find_children(ql_expr, qlast.Query,\n                            lambda x: isinstance(x, dml_types),\n                            terminate_early=True)\n\n    return bool(res)\n", "path": "edb/edgeql/utils.py"}], "after_files": [{"content": "#\n# This source file is part of the EdgeDB open source project.\n#\n# Copyright 2015-present MagicStack Inc. and the EdgeDB authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n\nfrom __future__ import annotations\n\nimport copy\nimport itertools\nfrom typing import *\n\nfrom edb import errors\nfrom edb.common import ast\nfrom edb.schema import schema as s_schema\nfrom edb.schema import functions as s_func\n\nfrom . import ast as qlast\n\n\nFREE_SHAPE_EXPR = qlast.DetachedExpr(\n    expr=qlast.Path(\n        steps=[qlast.ObjectRef(module='std', name='FreeObject')],\n    ),\n)\n\n\nclass ParameterInliner(ast.NodeTransformer):\n\n    def __init__(self, args_map: Mapping[str, qlast.Base]) -> None:\n        super().__init__()\n        self.args_map = args_map\n\n    def visit_Path(self, node: qlast.Path) -> qlast.Base:\n        if (len(node.steps) != 1 or\n                not isinstance(node.steps[0], qlast.ObjectRef)):\n            self.visit(node.steps[0])\n            return node\n\n        ref: qlast.ObjectRef = node.steps[0]\n        try:\n            arg = self.args_map[ref.name]\n        except KeyError:\n            return node\n\n        arg = copy.deepcopy(arg)\n        return arg\n\n\ndef inline_parameters(\n    ql_expr: qlast.Base,\n    args: Mapping[str, qlast.Base]\n) -> None:\n\n    inliner = ParameterInliner(args)\n    inliner.visit(ql_expr)\n\n\ndef index_parameters(\n    ql_args: List[qlast.Base],\n    *,\n    parameters: s_func.ParameterLikeList,\n    schema: s_schema.Schema\n) -> Dict[str, qlast.Base]:\n\n    result: Dict[str, qlast.Base] = {}\n    varargs: Optional[List[qlast.Expr]] = None\n    variadic = parameters.find_variadic(schema)\n    variadic_num = variadic.get_num(schema) if variadic else -1  # type: ignore\n\n    params = parameters.objects(schema)\n\n    if not variadic and len(ql_args) > len(params):\n        # In error message we discount the implicit __subject__ param.\n        raise errors.SchemaDefinitionError(\n            f'Expected {len(params) - 1} arguments, but found '\n            f'{len(ql_args) - 1}',\n            context=ql_args[-1].context,\n            details='Did you mean to use ON (...) for specifying the subject?',\n        )\n\n    e: qlast.Expr\n    p: s_func.ParameterLike\n    for iter in itertools.zip_longest(\n        enumerate(ql_args), params, fillvalue=None\n    ):\n        (i, e), p = iter  # type: ignore\n        if isinstance(e, qlast.SelectQuery):\n            e = e.result\n\n        if variadic and variadic_num == i:\n            assert varargs is None\n            varargs = []\n            result[p.get_parameter_name(schema)] = qlast.Array(\n                elements=varargs\n            )\n\n        if varargs is not None:\n            varargs.append(e)\n        else:\n            result[p.get_parameter_name(schema)] = e\n\n    return result\n\n\nclass AnchorInliner(ast.NodeTransformer):\n\n    def __init__(self, anchors: Mapping[str, qlast.Base]) -> None:\n        super().__init__()\n        self.anchors = anchors\n\n    def visit_Path(self, node: qlast.Path) -> qlast.Path:\n        if not node.steps:\n            return node\n\n        step0 = node.steps[0]\n\n        if isinstance(step0, qlast.Anchor):\n            node.steps[0] = self.anchors[step0.name]  # type: ignore\n        elif isinstance(step0, qlast.ObjectRef) and step0.name in self.anchors:\n            node.steps[0] = self.anchors[step0.name]  # type: ignore\n\n        return node\n\n\ndef inline_anchors(\n    ql_expr: qlast.Base,\n    anchors: Mapping[Any, qlast.Base]\n) -> None:\n\n    inliner = AnchorInliner(anchors)\n    inliner.visit(ql_expr)\n\n\ndef find_paths(ql: qlast.Base) -> List[qlast.Path]:\n    return ast.find_children(ql, qlast.Path)\n\n\ndef find_subject_ptrs(ast: qlast.Base) -> Set[str]:\n    ptrs = set()\n    for path in find_paths(ast):\n        if path.partial:\n            p = path.steps[0]\n        elif isinstance(path.steps[0], qlast.Subject) and len(path.steps) > 1:\n            p = path.steps[1]\n        else:\n            continue\n\n        if isinstance(p, qlast.Ptr):\n            ptrs.add(p.ptr.name)\n    return ptrs\n\n\ndef subject_paths_substitute(\n    ast: qlast.Base_T,\n    subject_ptrs: Dict[str, qlast.Expr],\n) -> qlast.Base_T:\n    ast = copy.deepcopy(ast)\n    for path in find_paths(ast):\n        if path.partial and isinstance(path.steps[0], qlast.Ptr):\n            path.steps[0] = subject_paths_substitute(\n                subject_ptrs[path.steps[0].ptr.name],\n                subject_ptrs,\n            )\n        elif (\n            isinstance(path.steps[0], qlast.Subject)\n            and len(path.steps)\n            and isinstance(path.steps[1], qlast.Ptr)\n        ):\n            path.steps[0:2] = [subject_paths_substitute(\n                subject_ptrs[path.steps[1].ptr.name],\n                subject_ptrs,\n            )]\n    return ast\n\n\ndef subject_substitute(\n        ast: qlast.Base_T, new_subject: qlast.Expr) -> qlast.Base_T:\n    ast = copy.deepcopy(ast)\n    for path in find_paths(ast):\n        if isinstance(path.steps[0], qlast.Subject):\n            path.steps[0] = new_subject\n    return ast\n\n\ndef contains_dml(ql_expr: qlast.Base) -> bool:\n    \"\"\"Check whether a expression contains any DML in a subtree.\"\"\"\n    # If this ends up being a perf problem, we can use a visitor\n    # directly and cache.\n    dml_types = (qlast.InsertQuery, qlast.UpdateQuery, qlast.DeleteQuery)\n    if isinstance(ql_expr, dml_types):\n        return True\n\n    res = ast.find_children(ql_expr, qlast.Query,\n                            lambda x: isinstance(x, dml_types),\n                            terminate_early=True)\n\n    return bool(res)\n", "path": "edb/edgeql/utils.py"}]}