From c0acf1448396ef140fe0b87b1266e2a5f1b50914 Mon Sep 17 00:00:00 2001 From: Ruben van der Linde Date: Sun, 18 Oct 2026 09:33:29 +0200 Subject: [PATCH] fix(query): a _related or _referenced block on a schema with no table matches nothing A schema that has never stored an object has no magic table yet, and the EXISTS subquery against it answered HTTP 500 (relation does not exist). The block now narrows to the empty set, the same answer the list path gives for a missing table, using MagicTableHandler::existsTableForRegisterSchema. Assisted-by: ClaudeCode:claude-opus-5-5 --- lib/Service/Query/RelatedRowQueryApplier.php | 9 ++++ .../Service/Query/ReferencedRowFilterTest.php | 1 + .../Query/RelatedRowQueryApplierTest.php | 43 ++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/lib/Service/Query/RelatedRowQueryApplier.php b/lib/Service/Query/RelatedRowQueryApplier.php index 9b707471d3..66f9cbe36a 100644 --- a/lib/Service/Query/RelatedRowQueryApplier.php +++ b/lib/Service/Query/RelatedRowQueryApplier.php @@ -111,6 +111,15 @@ public function apply(IQueryBuilder $qb, array $query, Register $register, strin foreach (array_values($filters) as $position => $filter) { $alias = sprintf('rel%d', $position); $schema = $this->resolveSchema(name: $filter->schema); + + // A schema that has never stored an object has no table yet, so no + // row can match. Same answer the list path gives for a missing table. + if ($this->tableHandler->existsTableForRegisterSchema(register: $register, schema: $schema) === false) { + $qb->andWhere($qb->createFunction('1 = 0')); + $applied++; + continue; + } + $rendered = $clause->render( filter: $filter, engine: $engine, diff --git a/tests/Unit/Service/Query/ReferencedRowFilterTest.php b/tests/Unit/Service/Query/ReferencedRowFilterTest.php index bedd8adfa1..f5e71a361b 100644 --- a/tests/Unit/Service/Query/ReferencedRowFilterTest.php +++ b/tests/Unit/Service/Query/ReferencedRowFilterTest.php @@ -131,6 +131,7 @@ public function testTheApplierAddsForwardBlocksWithTheReferencedSchemasOwnAccess $tableHandler = $this->createMock(MagicTableHandler::class); $tableHandler->method('getTableNameForRegisterSchema')->willReturn('oc_openregister_table_1_7'); + $tableHandler->method('existsTableForRegisterSchema')->willReturn(true); $rbac = $this->createMock(MagicRbacHandler::class); $rbac->expects($this->once())->method('buildRbacPredicateForAlias') diff --git a/tests/Unit/Service/Query/RelatedRowQueryApplierTest.php b/tests/Unit/Service/Query/RelatedRowQueryApplierTest.php index fdfdac6007..2e14f713fa 100644 --- a/tests/Unit/Service/Query/RelatedRowQueryApplierTest.php +++ b/tests/Unit/Service/Query/RelatedRowQueryApplierTest.php @@ -53,16 +53,18 @@ class RelatedRowQueryApplierTest extends TestCase { /** * Build an applier whose schema lookup returns the given rows. * - * @param array $found What findBySlug returns. + * @param array $found What findBySlug returns. + * @param bool $tableExists Whether the related schema's table exists. * * @return RelatedRowQueryApplier The applier. */ - private function applierFinding(array $found): RelatedRowQueryApplier { + private function applierFinding(array $found, bool $tableExists=true): RelatedRowQueryApplier { $schemaMapper = $this->createMock(SchemaMapper::class); $schemaMapper->method('findBySlug')->willReturn($found); $tableHandler = $this->createMock(MagicTableHandler::class); $tableHandler->method('getTableNameForRegisterSchema')->willReturn('oc_openregister_table_1_2'); + $tableHandler->method('existsTableForRegisterSchema')->willReturn($tableExists); $rbac = $this->createMock(MagicRbacHandler::class); $rbac->method('buildRbacPredicateForAlias')->willReturn('rel0._owner = \'alice\''); @@ -229,4 +231,41 @@ public function testTheRegisterMayComeFromTheQueryItself(): void { $this->assertSame(1, $applied); }//end testTheRegisterMayComeFromTheQueryItself() + + /** + * 🔴 A REFERENCED SCHEMA WITHOUT A TABLE MATCHES NOTHING, IT DOES NOT FAIL. + * + * A schema that has never stored an object has no magic table. Rendering + * the subquery against it answered HTTP 500 (`relation does not exist`). + * No row can exist there, so the block narrows to the empty set. + * + * @return void + */ + public function testAReferencedSchemaWithoutATableMatchesNothing(): void { + $conditions = []; + $qb = $this->createMock(originalClassName: IQueryBuilder::class); + $qb->method('createFunction')->willReturnArgument(0); + $qb->expects($this->never())->method('setParameter'); + $qb->method('andWhere')->willReturnCallback( + function ($condition) use (&$conditions, $qb) { + $conditions[] = $condition; + return $qb; + } + ); + + $applied = $this->applierFinding(found: [$this->schema()], tableExists: false)->apply( + qb: $qb, + query: [ + '_referenced' => [ + 'subsidieAanvraag' => [ + 'subsidieaanvraag' => ['case' => '9eab0293-7ce5-4bcf-b7aa-0ab79a1eab4f'], + ], + ], + ], + register: $this->register() + ); + + $this->assertSame(expected: 1, actual: $applied); + $this->assertSame(expected: ['1 = 0'], actual: $conditions); + }//end testAReferencedSchemaWithoutATableMatchesNothing() }//end class