diff --git a/integration-tests/lts/update.test.ts b/integration-tests/lts/update.test.ts index a8df1fb79..f8ba7f09e 100644 --- a/integration-tests/lts/update.test.ts +++ b/integration-tests/lts/update.test.ts @@ -83,6 +83,28 @@ describe("update", () => { })).toEdgeQL(); }); + test("nested update and explicit with", async () => { + e.params({ movieId: e.uuid }, (params) => { + const movie = e.select(e.Movie, (m) => ({ + filter: e.op(m.id, "=", params.movieId), + })); + + const updateChar = e.update(movie.characters, (c) => ({ + set: { + name: e.str_lower(c.name), + }, + })); + + const updateProfile = e.update(movie.profile, (p) => ({ + set: { + a: e.str_upper(p.a), + }, + })); + + return e.with([updateChar, updateProfile], e.select(movie)); + }).toEdgeQL(); + }); + test("scoped update", async () => { const query = e.update(e.Hero, (hero) => ({ filter_single: e.op(hero.name, "=", data.spidey.name), diff --git a/packages/generate/src/syntax/toEdgeQL.ts b/packages/generate/src/syntax/toEdgeQL.ts index 70c827f33..c2861df2c 100644 --- a/packages/generate/src/syntax/toEdgeQL.ts +++ b/packages/generate/src/syntax/toEdgeQL.ts @@ -278,6 +278,8 @@ export function $toEdgeQL(this: any) { // check all references and aliases are within this block const validScopes = new Set([ withBlock, + // expressions already explictly bound to with block are also valid scopes + ...(withBlocks.get(withBlock) ?? []), ...walkExprCtx.seen.get(withBlock)!.childExprs, ]); for (const scope of [ @@ -432,6 +434,13 @@ function walkExprTree( for (const refExpr of expr.__refs__) { walkExprTree(refExpr, expr.__expr__, ctx); const seenRef = ctx.seen.get(refExpr as any)!; + if (seenRef.childExprs.includes(expr.__expr__)) { + throw new Error( + `Ref expressions in with() cannot reference the expression to ` + + `which the 'WITH' block is being attached. ` + + `Consider wrapping the expression in a select.`, + ); + } if (seenRef.boundScope) { throw new Error(`Expression bound to multiple 'WITH' blocks`); }