diff --git a/drift/lib/src/runtime/query_builder/expressions/exists.dart b/drift/lib/src/runtime/query_builder/expressions/exists.dart index 853ebfed..2b48dbe1 100644 --- a/drift/lib/src/runtime/query_builder/expressions/exists.dart +++ b/drift/lib/src/runtime/query_builder/expressions/exists.dart @@ -22,6 +22,10 @@ class _ExistsExpression extends Expression { @override void writeInto(GenerationContext context) { + final outerHasMultipleTables = context.hasMultipleTables; + // Inside this subquery, we want to reference columns with their table + // to avoid ambiguities when an outer table is referenced. + context.hasMultipleTables = true; if (_not) { context.buffer.write('NOT '); } @@ -30,6 +34,7 @@ class _ExistsExpression extends Expression { context.buffer.write('('); _select.writeInto(context); context.buffer.write(')'); + context.hasMultipleTables = outerHasMultipleTables; } @override diff --git a/drift/test/database/expressions/exists_test.dart b/drift/test/database/expressions/exists_test.dart index dd81c72e..1af1c1de 100644 --- a/drift/test/database/expressions/exists_test.dart +++ b/drift/test/database/expressions/exists_test.dart @@ -13,7 +13,7 @@ void main() { expect( existsExpression, - generates('EXISTS (SELECT * FROM users WHERE is_awesome = ?)', [1]), + generates('EXISTS (SELECT * FROM users WHERE users.is_awesome = ?)', [1]), ); }); diff --git a/drift/test/integration_tests/regress_1894_test.dart b/drift/test/integration_tests/regress_1894_test.dart new file mode 100644 index 00000000..bab09ef0 --- /dev/null +++ b/drift/test/integration_tests/regress_1894_test.dart @@ -0,0 +1,33 @@ +import 'package:drift/drift.dart'; +import 'package:test/test.dart'; + +import '../generated/todos.dart'; +import '../test_utils/test_utils.dart'; + +void main() { + test('exists subqueries properly reference columns', () async { + final db = TodoDb.connect(testInMemoryDatabase()); + addTearDown(db.close); + + final nonEmptyId = await db.categories + .insertOne(CategoriesCompanion.insert(description: 'category')); + await db.todosTable.insertOne(TodosTableCompanion.insert( + content: 'entry', category: Value(nonEmptyId))); + + final emptyId = await db.categories.insertOne( + CategoriesCompanion.insert(description: 'this category empty YEET')); + + final emptyCategories = await db.emptyCategories(); + + expect(emptyCategories, hasLength(1)); + expect(emptyCategories.single.id, emptyId); + }); +} + +extension on TodoDb { + Future> emptyCategories() { + final hasNoTodo = notExistsQuery(select(todosTable) + ..where((row) => row.category.equalsExp(categories.id))); + return (select(categories)..where((row) => hasNoTodo)).get(); + } +}