From fb8caecbb0a51d54759aa0cc17b8ff31b8f46c4e Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Tue, 18 Nov 2025 08:38:56 +0100 Subject: [PATCH] fix(db): Fix JSON handling in WHERE statements for postgres Signed-off-by: Joas Schilling --- .../Version33000Date20251106131209.php | 2 +- .../PgSqlExpressionBuilder.php | 14 ++++++++ .../QueryBuilder/ExpressionBuilderDBTest.php | 35 +++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/core/Migrations/Version33000Date20251106131209.php b/core/Migrations/Version33000Date20251106131209.php index 488477694f4..b8f0a92736e 100644 --- a/core/Migrations/Version33000Date20251106131209.php +++ b/core/Migrations/Version33000Date20251106131209.php @@ -26,7 +26,7 @@ class Version33000Date20251106131209 extends SimpleMigrationStep { $qb->update('share') ->set('attributes', $qb->createNamedParameter('[["permissions","download",true]]')) ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_CIRCLE, IQueryBuilder::PARAM_INT))) - ->andWhere($qb->expr()->eq('attributes', $qb->createNamedParameter('[["permissions","download",null]]', IQueryBuilder::PARAM_STR))); + ->andWhere($qb->expr()->eq('attributes', $qb->createNamedParameter('[["permissions","download",null]]'), IQueryBuilder::PARAM_JSON)); $qb->executeStatement(); } } diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php index 53a566a7eb6..f30fccadb1f 100644 --- a/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php +++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/PgSqlExpressionBuilder.php @@ -8,6 +8,8 @@ namespace OC\DB\QueryBuilder\ExpressionBuilder; use OC\DB\QueryBuilder\QueryFunction; +use OCP\DB\QueryBuilder\ILiteral; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; @@ -25,12 +27,24 @@ class PgSqlExpressionBuilder extends ExpressionBuilder { case IQueryBuilder::PARAM_INT: return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS BIGINT)'); case IQueryBuilder::PARAM_STR: + case IQueryBuilder::PARAM_JSON: return new QueryFunction('CAST(' . $this->helper->quoteColumnName($column) . ' AS TEXT)'); default: return parent::castColumn($column, $type); } } + /** + * @inheritdoc + */ + protected function prepareColumn($column, $type) { + if ($type === IQueryBuilder::PARAM_JSON && !is_array($column) && !($column instanceof IParameter) && !($column instanceof ILiteral)) { + $column = $this->castColumn($column, $type); + } + + return parent::prepareColumn($column, $type); + } + /** * @inheritdoc */ diff --git a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php index 112bfe2ca16..d4dc530a963 100644 --- a/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php +++ b/tests/lib/DB/QueryBuilder/ExpressionBuilderDBTest.php @@ -141,6 +141,41 @@ class ExpressionBuilderDBTest extends TestCase { self::assertEquals('myvalue', $entries[0]['configvalue']); } + public function testJson(): void { + $appId = $this->getUniqueID('testing'); + $query = $this->connection->getQueryBuilder(); + $query->insert('share') + ->values([ + 'uid_owner' => $query->createNamedParameter('uid_owner'), + 'item_type' => $query->createNamedParameter('item_type'), + 'permissions' => $query->createNamedParameter(0), + 'stime' => $query->createNamedParameter(0), + 'accepted' => $query->createNamedParameter(0), + 'mail_send' => $query->createNamedParameter(0), + 'share_type' => $query->createNamedParameter(0), + 'share_with' => $query->createNamedParameter($appId), + 'attributes' => $query->createNamedParameter('[["permissions","before"]]'), + ]) + ->executeStatement(); + + $query = $this->connection->getQueryBuilder(); + $query->update('share') + ->set('attributes', $query->createNamedParameter('[["permissions","after"]]')) + ->where($query->expr()->eq('attributes', $query->createNamedParameter('[["permissions","before"]]'), IQueryBuilder::PARAM_JSON)); + $query->executeStatement(); + + $query = $this->connection->getQueryBuilder(); + $query->select('attributes') + ->from('share') + ->where($query->expr()->eq('share_with', $query->createNamedParameter($appId))); + + $result = $query->executeQuery(); + $entries = $result->fetchAll(); + $result->closeCursor(); + self::assertCount(1, $entries); + self::assertEquals('[["permissions","after"]]', $entries[0]['attributes']); + } + public function testDateTimeEquals(): void { $dateTime = new \DateTime('2023-01-01'); $insert = $this->connection->getQueryBuilder();