Skip to content

Commit

Permalink
pkp#8700 Updated DAO::countRecords() to optimize the query before enc…
Browse files Browse the repository at this point in the history
…losing it with a SELECT
  • Loading branch information
jonasraoni committed Mar 23, 2023
1 parent fd7eb9b commit a4a9733
Showing 1 changed file with 63 additions and 2 deletions.
65 changes: 63 additions & 2 deletions classes/db/DAO.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,15 @@ function retrieveRange($sql, $params = [], $dbResultRange = null, $callHooks = t
* @return int
*/
public function countRecords($sql, $params = []) {
$result = $this->retrieve('SELECT COUNT(*) AS row_count FROM (' . $sql . ') AS count_subquery', $params);
return $result->current()->row_count;
foreach ([$this->_optimizeCountQuery($sql, $params), [$sql, $params]] as [$sql, $params]) {
try {
$result = $this->retrieve("SELECT COUNT(*) AS row_count FROM ({$sql}) AS count_subquery", $params);
return $result->current()->row_count;
} catch (Exception $e) {
error_log($e);
}
}
throw $e;
}

/**
Expand Down Expand Up @@ -583,4 +590,58 @@ function formatDateToDB($date, $defaultNumWeeks = null, $acceptPastDate = true)
return null;
}
}

/**
* Retrieves a SELECT statement without the SELECT and ORDER BY clauses for optimization purposes
* @return array The SQL query at the index 0 and the updated parameters at the index 1
*/
private static function _optimizeCountQuery(string $s, array $params): array {
$findTopLevelExpression = static function (string $s, string $expression, int $index, ?int &$foundParams = null): int {
static
$beginLevel = '(',
$endLevel = ')',
$delimiters = ["'" => 0, '`' => 0, '"' => 0],
$escape = '\\';

if ($index < 0) {
return -1;
}
$levels = 0;
$delimiter = null;
for ($l = strlen($s), $i = $index; $i < $l; ) {
$c = $s[$i];
if ($c === $beginLevel) {
++$levels;
} elseif ($c === $endLevel) {
if (!$levels--) {
return -1;
}
} elseif (($newDelimiter = $delimiters[$c] ?? null)) {
if ($delimiter === $newDelimiter) {
$delimiter = null;
} elseif (!$delimiter) {
$delimiter = $c;
}
} else if ($c === $escape && $delimiter) {
$i += 2;
continue;
} elseif ($c === '?') {
++$foundParams;
} elseif (!$delimiter && !$levels && preg_match("/\G{$expression}/i", $s, $m, 0, $i)) {
return $i;
}
++$i;
}
return -1;
};

$selectParams = 0;
// Abort if there's a UNION clause or if there's no "FROM"
if (~$findTopLevelExpression($s, 'UNION', 0) || !~($from = $findTopLevelExpression($s, '\bFROM\b', 0, $selectParams))) {
return [$s, $params];
}
// Slice the statement up to the ORDER BY clause
$order = ~($order = $findTopLevelExpression($s, '\bORDER\s+BY\b', $from)) ? $order - $from : null;
return ['SELECT 0 ' . substr($s, $from, $order), array_slice($params, $selectParams)];
}
}

0 comments on commit a4a9733

Please sign in to comment.