IdoQuery: Fix that PostgreSQL queries use LOWER() on non-CI columns
refs #10364 refs #9954
This commit is contained in:
parent
e58d747d4a
commit
0b2b1c5d1e
|
@ -55,14 +55,14 @@ abstract class IdoQuery extends DbQuery
|
||||||
protected $prefix;
|
protected $prefix;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array to map aliases to table names
|
* An array to map aliases to column names
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $idxAliasColumn;
|
protected $idxAliasColumn;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An array to map aliases to column names
|
* An array to map aliases to table names
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
|
@ -400,14 +400,14 @@ abstract class IdoQuery extends DbQuery
|
||||||
protected static $idoVersion;
|
protected static $idoVersion;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of columns where the COLLATE SQL-instruction has been removed
|
* List of column aliases mapped to their table where the COLLATE SQL-instruction has been removed
|
||||||
*
|
*
|
||||||
* This list is being populated in case of a PostgreSQL backend only,
|
* This list is being populated in case of a PostgreSQL backend only,
|
||||||
* to ensure case-insensitive string comparison in WHERE clauses.
|
* to ensure case-insensitive string comparison in WHERE clauses.
|
||||||
*
|
*
|
||||||
* @var array
|
* @var array
|
||||||
*/
|
*/
|
||||||
protected $columnsWithoutCollation = array();
|
protected $caseInsensitiveColumns;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true when the column is an aggregate column
|
* Return true when the column is an aggregate column
|
||||||
|
@ -493,6 +493,15 @@ abstract class IdoQuery extends DbQuery
|
||||||
$column = $this->getCustomvarColumnName($alias);
|
$column = $this->getCustomvarColumnName($alias);
|
||||||
} else {
|
} else {
|
||||||
$column = $this->aliasToColumnName($alias);
|
$column = $this->aliasToColumnName($alias);
|
||||||
|
if (isset($this->caseInsensitiveColumns[$this->aliasToTableName($alias)][$alias])) {
|
||||||
|
$column = 'LOWER(' . $column . ')';
|
||||||
|
$expression = $filter->getExpression();
|
||||||
|
if (is_array($expression)) {
|
||||||
|
$filter->setExpression(array_map('strtolower', $expression));
|
||||||
|
} else {
|
||||||
|
$filter->setExpression(strtolower($expression));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$filter->setColumn($column);
|
$filter->setColumn($column);
|
||||||
|
@ -513,42 +522,6 @@ abstract class IdoQuery extends DbQuery
|
||||||
return parent::addFilter($filter);
|
return parent::addFilter($filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Recurse the given filter and ensure that any string conversion is case-insensitive
|
|
||||||
*
|
|
||||||
* @param Filter $filter
|
|
||||||
*/
|
|
||||||
protected function lowerColumnsWithoutCollation(Filter $filter)
|
|
||||||
{
|
|
||||||
if ($filter instanceof FilterExpression) {
|
|
||||||
if (
|
|
||||||
in_array($filter->getColumn(), $this->columnsWithoutCollation)
|
|
||||||
&& strpos($filter->getColumn(), 'LOWER') !== 0
|
|
||||||
) {
|
|
||||||
$filter->setColumn('LOWER(' . $filter->getColumn() . ')');
|
|
||||||
$expression = $filter->getExpression();
|
|
||||||
if (is_array($expression)) {
|
|
||||||
$filter->setExpression(array_map('strtolower', $expression));
|
|
||||||
} else {
|
|
||||||
$filter->setExpression(strtolower($expression));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
foreach ($filter->filters() as $chainedFilter) {
|
|
||||||
$this->lowerColumnsWithoutCollation($chainedFilter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyFilterSql($select)
|
|
||||||
{
|
|
||||||
if (! empty($this->columnsWithoutCollation)) {
|
|
||||||
$this->lowerColumnsWithoutCollation($this->filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::applyFilterSql($select);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function where($condition, $value = null)
|
public function where($condition, $value = null)
|
||||||
{
|
{
|
||||||
if ($value === '*') {
|
if ($value === '*') {
|
||||||
|
@ -582,28 +555,37 @@ abstract class IdoQuery extends DbQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return whether the given alias or column name provides case insensitive value comparison
|
* Return whether the given alias provides case insensitive value comparison
|
||||||
*
|
*
|
||||||
* @param string $aliasOrColumn
|
* @param string $alias
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function isCaseInsensitive($aliasOrColumn)
|
public function isCaseInsensitive($alias)
|
||||||
{
|
{
|
||||||
if ($this->isCustomVar($aliasOrColumn)) {
|
if ($this->isCustomVar($alias)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$column = $this->getMappedField($aliasOrColumn) ?: $aliasOrColumn;
|
$column = $this->getMappedField($alias);
|
||||||
if (! $column) {
|
if (! $column) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! empty($this->columnsWithoutCollation)) {
|
if (empty($this->caseInsensitiveColumns)) {
|
||||||
return in_array($column, $this->columnsWithoutCollation) || strpos($column, 'LOWER') === 0;
|
return preg_match('/ COLLATE .+$/', $column) === 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return preg_match('/ COLLATE .+$/', $column) === 1;
|
if (strpos($column, 'LOWER') === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$table = $this->aliasToTableName($alias);
|
||||||
|
if (! $table) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isset($this->caseInsensitiveColumns[$table][$alias]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -635,10 +617,12 @@ abstract class IdoQuery extends DbQuery
|
||||||
'%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s';
|
'%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s';
|
||||||
foreach ($this->columnMap as $table => & $columns) {
|
foreach ($this->columnMap as $table => & $columns) {
|
||||||
foreach ($columns as $alias => & $column) {
|
foreach ($columns as $alias => & $column) {
|
||||||
|
// Using a regex here because COLLATE may occur anywhere in the string
|
||||||
$column = preg_replace('/ COLLATE .+$/', '', $column, -1, $count);
|
$column = preg_replace('/ COLLATE .+$/', '', $column, -1, $count);
|
||||||
if ($count > 0) {
|
if ($count > 0) {
|
||||||
$this->columnsWithoutCollation[] = $this->getMappedField($alias);
|
$this->caseInsensitiveColumns[$table][$alias] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$column = preg_replace(
|
$column = preg_replace(
|
||||||
'/inet_aton\(([[:word:].]+)\)/i',
|
'/inet_aton\(([[:word:].]+)\)/i',
|
||||||
'(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)',
|
'(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)',
|
||||||
|
|
Loading…
Reference in New Issue