Skip to content

Commit ef28844

Browse files
committed
DB: warn on parameter number constraints
Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
1 parent b82a8f7 commit ef28844

File tree

2 files changed

+112
-6
lines changed

2 files changed

+112
-6
lines changed

lib/private/DB/QueryBuilder/QueryBuilder.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,36 @@ public function execute() {
253253
}
254254
}
255255

256+
$numberOfParameters = 0;
257+
$hasTooLargeArrayParameter = false;
258+
foreach ($this->getParameters() as $parameter) {
259+
if (is_array($parameter)) {
260+
$count = count($parameter);
261+
$numberOfParameters += $count;
262+
$hasTooLargeArrayParameter = $hasTooLargeArrayParameter || ($count > 1000);
263+
}
264+
}
265+
266+
if ($hasTooLargeArrayParameter) {
267+
$exception = new QueryException('More than 1000 expressions in a list are not allowed on Oracle.');
268+
$this->logger->logException($exception, [
269+
'message' => 'More than 1000 expressions in a list are not allowed on Oracle.',
270+
'query' => $this->getSQL(),
271+
'level' => ILogger::ERROR,
272+
'app' => 'core',
273+
]);
274+
}
275+
276+
if ($numberOfParameters > 65535) {
277+
$exception = new QueryException('The number of parameters must not exceed 65535. Restriction by PostgreSQL.');
278+
$this->logger->logException($exception, [
279+
'message' => 'The number of parameters must not exceed 65535. Restriction by PostgreSQL.',
280+
'query' => $this->getSQL(),
281+
'level' => ILogger::ERROR,
282+
'app' => 'core',
283+
]);
284+
}
285+
256286
$result = $this->queryBuilder->execute();
257287
if (is_int($result)) {
258288
return $result;

tests/lib/DB/QueryBuilder/QueryBuilderTest.php

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
namespace Test\DB\QueryBuilder;
2323

2424
use Doctrine\DBAL\Query\Expression\CompositeExpression;
25+
use Doctrine\DBAL\Query\QueryException;
26+
use Doctrine\DBAL\Result;
2527
use OC\DB\QueryBuilder\Literal;
2628
use OC\DB\QueryBuilder\Parameter;
2729
use OC\DB\QueryBuilder\QueryBuilder;
@@ -1261,6 +1263,10 @@ public function testExecuteWithoutLogger() {
12611263
->expects($this->once())
12621264
->method('execute')
12631265
->willReturn(3);
1266+
$queryBuilder
1267+
->expects($this->any())
1268+
->method('getParameters')
1269+
->willReturn([]);
12641270
$this->logger
12651271
->expects($this->never())
12661272
->method('debug');
@@ -1277,14 +1283,14 @@ public function testExecuteWithoutLogger() {
12771283
public function testExecuteWithLoggerAndNamedArray() {
12781284
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
12791285
$queryBuilder
1280-
->expects($this->at(0))
1286+
->expects($this->any())
12811287
->method('getParameters')
12821288
->willReturn([
12831289
'foo' => 'bar',
12841290
'key' => 'value',
12851291
]);
12861292
$queryBuilder
1287-
->expects($this->at(1))
1293+
->expects($this->any())
12881294
->method('getSQL')
12891295
->willReturn('SELECT * FROM FOO WHERE BAR = ?');
12901296
$queryBuilder
@@ -1315,11 +1321,11 @@ public function testExecuteWithLoggerAndNamedArray() {
13151321
public function testExecuteWithLoggerAndUnnamedArray() {
13161322
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
13171323
$queryBuilder
1318-
->expects($this->at(0))
1324+
->expects($this->any())
13191325
->method('getParameters')
13201326
->willReturn(['Bar']);
13211327
$queryBuilder
1322-
->expects($this->at(1))
1328+
->expects($this->any())
13231329
->method('getSQL')
13241330
->willReturn('SELECT * FROM FOO WHERE BAR = ?');
13251331
$queryBuilder
@@ -1350,11 +1356,11 @@ public function testExecuteWithLoggerAndUnnamedArray() {
13501356
public function testExecuteWithLoggerAndNoParams() {
13511357
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
13521358
$queryBuilder
1353-
->expects($this->at(0))
1359+
->expects($this->any())
13541360
->method('getParameters')
13551361
->willReturn([]);
13561362
$queryBuilder
1357-
->expects($this->at(1))
1363+
->expects($this->any())
13581364
->method('getSQL')
13591365
->willReturn('SELECT * FROM FOO WHERE BAR = ?');
13601366
$queryBuilder
@@ -1380,4 +1386,74 @@ public function testExecuteWithLoggerAndNoParams() {
13801386
$this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
13811387
$this->assertEquals(3, $this->queryBuilder->execute());
13821388
}
1389+
1390+
public function testExecuteWithParameterTooLarge() {
1391+
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
1392+
$p = array_fill(0, 1001, 'foo');
1393+
$queryBuilder
1394+
->expects($this->any())
1395+
->method('getParameters')
1396+
->willReturn([$p]);
1397+
$queryBuilder
1398+
->expects($this->any())
1399+
->method('getSQL')
1400+
->willReturn('SELECT * FROM FOO WHERE BAR IN (?)');
1401+
$queryBuilder
1402+
->expects($this->once())
1403+
->method('execute')
1404+
->willReturn($this->createMock(Result::class));
1405+
$this->logger
1406+
->expects($this->once())
1407+
->method('logException')
1408+
->willReturnCallback(function ($e, $parameters) {
1409+
$this->assertInstanceOf(QueryException::class, $e);
1410+
$this->assertSame(
1411+
'More than 1000 expressions in a list are not allowed on Oracle.',
1412+
$parameters['message']
1413+
);
1414+
});
1415+
$this->config
1416+
->expects($this->once())
1417+
->method('getValue')
1418+
->with('log_query', false)
1419+
->willReturn(false);
1420+
1421+
$this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
1422+
$this->queryBuilder->execute();
1423+
}
1424+
1425+
public function testExecuteWithParametersTooMany() {
1426+
$queryBuilder = $this->createMock(\Doctrine\DBAL\Query\QueryBuilder::class);
1427+
$p = array_fill(0, 999, 'foo');
1428+
$queryBuilder
1429+
->expects($this->any())
1430+
->method('getParameters')
1431+
->willReturn(array_fill(0, 66, $p));
1432+
$queryBuilder
1433+
->expects($this->any())
1434+
->method('getSQL')
1435+
->willReturn('SELECT * FROM FOO WHERE BAR IN (?) OR BAR IN (?)');
1436+
$queryBuilder
1437+
->expects($this->once())
1438+
->method('execute')
1439+
->willReturn($this->createMock(Result::class));
1440+
$this->logger
1441+
->expects($this->once())
1442+
->method('logException')
1443+
->willReturnCallback(function ($e, $parameters) {
1444+
$this->assertInstanceOf(QueryException::class, $e);
1445+
$this->assertSame(
1446+
'The number of parameters must not exceed 65535. Restriction by PostgreSQL.',
1447+
$parameters['message']
1448+
);
1449+
});
1450+
$this->config
1451+
->expects($this->once())
1452+
->method('getValue')
1453+
->with('log_query', false)
1454+
->willReturn(false);
1455+
1456+
$this->invokePrivate($this->queryBuilder, 'queryBuilder', [$queryBuilder]);
1457+
$this->queryBuilder->execute();
1458+
}
13831459
}

0 commit comments

Comments
 (0)