From fac33567390b9a58a700820a1de5278f4a3e4937 Mon Sep 17 00:00:00 2001 From: Guy Sartorelli <36352093+GuySartorelli@users.noreply.github.com> Date: Mon, 25 Sep 2023 12:32:40 +1300 Subject: [PATCH] ENH Enable allowing collisions for field statements (#10957) --- 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 fc1be4419..4f671508f 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 @@ class DataQuery 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 @@ class DataQuery $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); } } @@ -1379,6 +1390,25 @@ class DataQuery 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 cbdd85f56..96948a0d3 100644 --- a/tests/php/ORM/DataQueryTest.php +++ b/tests/php/ORM/DataQueryTest.php @@ -183,6 +183,33 @@ class DataQueryTest extends SapphireTest $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);