API Ensure table aliases longer than max characters are safely re-written

This commit is contained in:
Damian Mooyman 2017-05-19 18:02:28 +12:00
parent 512f10d745
commit e941f0b122
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
3 changed files with 98 additions and 1 deletions

View File

@ -3,7 +3,6 @@ language: php
sudo: false sudo: false
php: php:
- 5.5
- 5.6 - 5.6
env: env:

View File

@ -2,12 +2,20 @@
namespace SilverStripe\PostgreSQL; namespace SilverStripe\PostgreSQL;
use SilverStripe\ORM\Queries\SQLConditionalExpression;
use SilverStripe\ORM\Queries\SQLExpression;
use SilverStripe\ORM\Queries\SQLSelect; use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\Connect\DBQueryBuilder; use SilverStripe\ORM\Connect\DBQueryBuilder;
use InvalidArgumentException; use InvalidArgumentException;
class PostgreSQLQueryBuilder extends DBQueryBuilder class PostgreSQLQueryBuilder extends DBQueryBuilder
{ {
/**
* Max table length.
* Aliases longer than this will be re-written
*/
const MAX_TABLE = 63;
/** /**
* Return the LIMIT clause ready for inserting into a query. * Return the LIMIT clause ready for inserting into a query.
* *
@ -47,4 +55,53 @@ class PostgreSQLQueryBuilder extends DBQueryBuilder
} }
return $clause; return $clause;
} }
public function buildSQL(SQLExpression $query, &$parameters)
{
$sql = parent::buildSQL($query, $parameters);
return $this->rewriteLongIdentifiers($query, $sql);
}
/**
* Find and generate table aliases necessary in the given query
*
* @param SQLConditionalExpression $query
* @return array List of replacements
*/
protected function findRewrites(SQLConditionalExpression $query)
{
$rewrites = [];
foreach ($query->getFrom() as $alias => $from) {
$table = is_array($from) ? $from['table'] : $from;
if ($alias === $table || "\"{$alias}\"" === $table) {
continue;
}
// Don't complain about aliases shorter than max length
if (strlen($alias) <= self::MAX_TABLE) {
continue;
}
$replacement = substr(sha1($alias), 0, 7) . '_' . substr($alias, 8 - self::MAX_TABLE);
$rewrites["\"{$alias}\""] = "\"{$replacement}\"";
}
return $rewrites;
}
/**
* Rewrite all ` AS "Identifier"` with strlen(Identifier) > 63
*
* @param SQLExpression $query
* @param string $sql
* @return string
*/
protected function rewriteLongIdentifiers(SQLExpression $query, $sql)
{
// Check if this query has aliases
if ($query instanceof SQLConditionalExpression) {
$rewrites = $this->findRewrites($query);
if ($rewrites) {
return str_replace(array_keys($rewrites), array_values($rewrites), $sql);
}
}
return $sql;
}
} }

View File

@ -0,0 +1,41 @@
<?php
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\PostgreSQL\PostgreSQLQueryBuilder;
class PostgreSQLQueryBuilderTest extends SapphireTest
{
public function testLongAliases()
{
$query = new SQLSelect();
$longstring = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$alias2 = $longstring . $longstring;
$query->selectField('*');
$query->addFrom('"Base"');
$query->addLeftJoin(
'Joined',
"\"Base\".\"ID\" = \"{$alias2}\".\"ID\"",
$alias2
);
$query->addWhere([
"\"{$alias2}\".\"Title\" = ?" => 'Value',
]);
$identifier = "c4afb43_hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
$this->assertEquals(PostgreSQLQueryBuilder::MAX_TABLE, strlen($identifier));
$expected = <<<SQL
SELECT *
FROM "Base" LEFT JOIN "Joined" AS "c4afb43_hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
ON "Base"."ID" = "c4afb43_hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"."ID"
WHERE ("c4afb43_hijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"."Title" = ?)
SQL;
$builder = new PostgreSQLQueryBuilder();
$sql = $builder->buildSQL($query, $params);
$this->assertSQLEquals($expected, $sql);
$this->assertEquals(['Value'], $params);
}
}