Skip to content

Commit

Permalink
fixed bug: cross joins from missing aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
mringler committed Apr 8, 2021
1 parent 16292d3 commit 0f16793
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/Propel/Runtime/ActiveQuery/Criteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ class Criteria
/**
* Default operator for combination of criterions
*
* @see addUsingOperator
* @see addUsingOperator()
* @var string Criteria::LOGICAL_AND or Criteria::LOGICAL_OR
*/
protected $defaultCombineOperator = Criteria::LOGICAL_AND;
Expand Down
51 changes: 24 additions & 27 deletions src/Propel/Runtime/ActiveQuery/ModelCriteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -2014,23 +2014,24 @@ protected function doReplaceNameInExpression($matches)
$key = $matches[0];
[$column, $realFullColumnName] = $this->getColumnFromName($key);

if ($column instanceof ColumnMap) {
$this->replacedColumns[] = $column;
$this->foundMatch = true;
if (!$column instanceof ColumnMap) {
return $this->quoteIdentifier($key);
}

$this->replacedColumns[] = $column;
$this->foundMatch = true;

if (strpos($key, '.') !== false) {
[$tableName, $columnName] = explode('.', $key);
if (strpos($key, '.') !== false) {
[$tableName, $columnName] = explode('.', $key);
if (isset($this->aliases[$tableName])) {
//don't replace a alias with their real table name
$realColumnName = substr($realFullColumnName, strrpos($realFullColumnName, '.') + 1);
if (isset($this->aliases[$tableName])) {
//don't replace a alias with their real table name
return $this->quoteIdentifier($tableName . '.' . $realColumnName);
}
}

return $this->quoteIdentifier($realFullColumnName);
return $this->quoteIdentifier($tableName . '.' . $realColumnName);
}
}

return $this->quoteIdentifier($key);
return $this->quoteIdentifier($realFullColumnName);
}

/**
Expand All @@ -2045,21 +2046,21 @@ protected function doReplaceNameInExpression($matches)
* => array($authorFirstNameColumnMap, 'a.first_name')
* </code>
*
* @param string $phpName String representing the column name in a pseudo SQL clause, e.g. 'Book.Title'
* @param string $columnName String representing the column name in a pseudo SQL clause, e.g. 'Book.Title'
* @param bool $failSilently
*
* @throws \Propel\Runtime\ActiveQuery\Exception\UnknownColumnException
* @throws \Propel\Runtime\ActiveQuery\Exception\UnknownModelException
*
* @return array List($columnMap, $realColumnName)
*/
protected function getColumnFromName($phpName, $failSilently = true)
protected function getColumnFromName($columnName, $failSilently = true)
{
if (strpos($phpName, '.') === false) {
if (strpos($columnName, '.') === false) {
$prefix = $this->getModelAliasOrName();
} else {
// $prefix could be either class name or table name
[$prefix, $phpName] = explode('.', $phpName);
[$prefix, $columnName] = explode('.', $columnName);
}

$shortClass = static::getShortName($prefix);
Expand All @@ -2080,7 +2081,7 @@ protected function getColumnFromName($phpName, $failSilently = true)
// column of a relations's model
$tableMap = $this->joins[$shortClass]->getTableMap();
} elseif ($this->hasSelectQuery($prefix)) {
return $this->getColumnFromSubQuery($prefix, $phpName, $failSilently);
return $this->getColumnFromSubQuery($prefix, $columnName, $failSilently);
} elseif ($modelJoin = $this->getModelJoinByTableName($prefix)) {
$tableMap = $modelJoin->getTableMap();
} elseif ($failSilently) {
Expand All @@ -2089,8 +2090,9 @@ protected function getColumnFromName($phpName, $failSilently = true)
throw new UnknownModelException(sprintf('Unknown model, alias or table "%s"', $prefix));
}

if ($tableMap->hasColumnByPhpName($phpName)) {
$column = $tableMap->getColumnByPhpName($phpName);
$column = $tableMap->findColumnByName($columnName);

if ($column !== null) {
if (isset($this->aliases[$prefix])) {
$this->currentAlias = $prefix;
$realColumnName = $prefix . '.' . $column->getName();
Expand All @@ -2099,18 +2101,13 @@ protected function getColumnFromName($phpName, $failSilently = true)
}

return [$column, $realColumnName];
} elseif ($tableMap->hasColumn($phpName)) {
$column = $tableMap->getColumn($phpName);
$realColumnName = $column->getFullyQualifiedName();

return [$column, $realColumnName];
} elseif (isset($this->asColumns[$phpName])) {
} elseif (isset($this->asColumns[$columnName])) {
// aliased column
return [null, $phpName];
return [null, $columnName];
} elseif ($failSilently) {
return [null, null];
} else {
throw new UnknownColumnException(sprintf('Unknown column "%s" on model, alias or table "%s"', $phpName, $prefix));
throw new UnknownColumnException(sprintf('Unknown column "%s" on model, alias or table "%s"', $columnName, $prefix));
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/Propel/Runtime/Map/TableMap.php
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,28 @@ public function getColumnByPhpName($phpName)
return $this->columnsByPhpName[$phpName];
}

/**
* Tries to find a column by name.
*
* @param string $name
*
* @return \Propel\Runtime\Map\ColumnMap|null
*/
public function findColumnByName(string $name): ?ColumnMap
{
if (isset($this->columnsByPhpName[$name])) {
return $this->getColumnByPhpName($name);
}
if ($this->hasColumn($name, false)) {
return $this->getColumn($name, false);
}
if ($this->hasColumn($name, true)) {
return $this->getColumn($name, true);
}

return null;
}

/**
* Get a ColumnMap[] of the columns in this table.
*
Expand Down
20 changes: 20 additions & 0 deletions tests/Propel/Tests/Runtime/ActiveQuery/ModelCriteriaTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,26 @@ public function testTrueTableAlias()
];
$this->assertCriteriaTranslation($c, $sql, $params, 'setModelAlias() allows the definition of a true SQL alias after construction');
}

/**
* @return void
*/
public function testTrueTableAliasWithOriginalColumnName()
{
$c = new ModelCriteria('bookstore', 'Propel\Tests\Bookstore\Book');
$c->setModelAlias('b', true);
$c->where('b.title = ?', 'foo');
$c->join('b.Author a');
$c->where('a.first_name = ?', 'john');

$sql = $this->getSql('SELECT FROM book b INNER JOIN author a ON (b.author_id=a.id) WHERE b.title = :p1 AND a.first_name = :p2');

$params = [
['table' => 'book', 'column' => 'title', 'value' => 'foo'],
['table' => 'author', 'column' => 'first_name', 'value' => 'john'],
];
$this->assertCriteriaTranslation($c, $sql, $params, 'setModelAlias() allows the definition of a true SQL alias after construction');
}

/**
* @return void
Expand Down
13 changes: 13 additions & 0 deletions tests/Propel/Tests/Runtime/Map/TableMapTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

namespace Propel\Tests\Runtime\Map;

use Exception;
use Propel\Runtime\Collection\ObjectCollection;
use Propel\Runtime\Map\ColumnMap;
use Propel\Runtime\Map\DatabaseMap;
Expand Down Expand Up @@ -138,6 +139,18 @@ public function testGetColumns()
$this->assertEquals(['BAR' => $column1, 'BAZ' => $column2], $this->tmap->getColumns(), 'getColumns returns the columns indexed by name');
}

/**
* @return void
*/
public function testFindColumnsByName()
{
$this->assertNull($this->tmap->findColumnByName('LeName'), 'findColumnByName() should return null on empty map');
$column = $this->tmap->addColumn('BAR', 'Bar', 'INTEGER');
$this->assertEquals($column, $this->tmap->findColumnByName('BAR'), 'findColumnByName() should find column if regular name matches');
$this->assertEquals($column, $this->tmap->findColumnByName('Bar'), 'findColumnByName() should find column if phpName matches');
$this->assertEquals($column, $this->tmap->findColumnByName('table.bar'), 'findColumnByName() should try normalizing input name');
}

/**
* @return void
*/
Expand Down

0 comments on commit 0f16793

Please sign in to comment.