From 2c8cd20b9529c37a3109bafef3efd0ff049f3ab4 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli Date: Thu, 21 Sep 2023 15:54:35 +1200 Subject: [PATCH] ENH Enable allowing collisions for field statements --- src/ORM/DataQuery.php | 32 +++++++++++++++++++++++++++++++- tests/php/ORM/DataQueryTest.php | 27 +++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/ORM/DataQuery.php b/src/ORM/DataQuery.php index b37f1669938..dc022bf97d0 100644 --- a/src/ORM/DataQuery.php +++ b/src/ORM/DataQuery.php @@ -49,6 +49,11 @@ class DataQuery */ protected $collidingFields = []; + /** + * If true, collisions are allowed for statements aliased as db columns + */ + private $allowCollidingFieldStatements = false; + /** * Allows custom callback to be registered before getFinalisedQuery is called. * @@ -287,6 +292,7 @@ public function getFinalisedQuery($queriedColumns = null) if ($this->collidingFields) { foreach ($this->collidingFields as $collisionField => $collisions) { $caseClauses = []; + $lastClauses = []; foreach ($collisions as $collision) { if (preg_match('/^"(?[^"]+)"\./', $collision ?? '', $matches)) { $collisionTable = $matches['table']; @@ -298,9 +304,14 @@ public function getFinalisedQuery($queriedColumns = null) $caseClauses[] = "WHEN {$collisionClassColumn} IN ({$collisionClassesSQL}) THEN $collision"; } } else { - user_error("Bad collision item '$collision'", E_USER_WARNING); + if ($this->getAllowCollidingFieldStatements()) { + $lastClauses[] = "WHEN $collision IS NOT NULL THEN $collision"; + } else { + user_error("Bad collision item '$collision'", E_USER_WARNING); + } } } + $caseClauses = array_merge($caseClauses, $lastClauses); $query->selectField("CASE " . implode(" ", $caseClauses) . " ELSE NULL END", $collisionField); } } @@ -1358,6 +1369,25 @@ public function pushQueryManipulator(DataQueryManipulator $manipulator) return $this; } + /** + * Get whether field statements aliased as columns are allowed when that column is already + * being selected + */ + public function getAllowCollidingFieldStatements(): bool + { + return $this->allowCollidingFieldStatements; + } + + /** + * Set whether field statements aliased as columns are allowed when that column is already + * being selected + */ + public function setAllowCollidingFieldStatements(bool $value): static + { + $this->allowCollidingFieldStatements = $value; + return $this; + } + private function validateColumnField($field, SQLSelect $query) { // standard column - nothing to process here diff --git a/tests/php/ORM/DataQueryTest.php b/tests/php/ORM/DataQueryTest.php index 34ae7619d6b..9b4a835f3c8 100644 --- a/tests/php/ORM/DataQueryTest.php +++ b/tests/php/ORM/DataQueryTest.php @@ -172,6 +172,33 @@ public function testRelationOrderWithCustomJoin() $this->assertTrue(true); } + public function provideFieldCollision() + { + return [ + 'allow collisions' => [true], + 'disallow collisions' => [false], + ]; + } + + /** + * @dataProvider provideFieldCollision + */ + public function testFieldCollision($allowCollisions) + { + $dataQuery = new DataQuery(DataQueryTest\ObjectB::class); + $dataQuery->selectField('COALESCE(NULL, 1) AS "Title"'); + $dataQuery->setAllowCollidingFieldStatements($allowCollisions); + + if ($allowCollisions) { + $this->assertSQLContains('THEN "DataQueryTest_B"."Title" WHEN COALESCE(NULL, 1) AS "Title" IS NOT NULL THEN COALESCE(NULL, 1) AS "Title" ELSE NULL END AS "Title"', $dataQuery->sql()); + } else { + $this->expectError(); + $this->expectErrorMessageMatches('/^Bad collision item /'); + } + + $dataQuery->getFinalisedQuery(); + } + public function testDisjunctiveGroup() { $dq = new DataQuery(DataQueryTest\ObjectA::class);