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);