Skip to content

Commit

Permalink
API Ensure table aliases longer than max characters are safely re-wri…
Browse files Browse the repository at this point in the history
…tten
  • Loading branch information
Damian Mooyman committed May 25, 2017
1 parent 512f10d commit e941f0b
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 1 deletion.
1 change: 0 additions & 1 deletion .travis.yml
Expand Up @@ -3,7 +3,6 @@ language: php
sudo: false

php:
- 5.5
- 5.6

env:
Expand Down
57 changes: 57 additions & 0 deletions code/PostgreSQLQueryBuilder.php
Expand Up @@ -2,12 +2,20 @@

namespace SilverStripe\PostgreSQL;

use SilverStripe\ORM\Queries\SQLConditionalExpression;
use SilverStripe\ORM\Queries\SQLExpression;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\Connect\DBQueryBuilder;
use InvalidArgumentException;

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.
*
Expand Down Expand Up @@ -47,4 +55,53 @@ public function buildLimitFragment(SQLSelect $query, array &$parameters)
}
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;
}
}
41 changes: 41 additions & 0 deletions tests/PostgreSQLQueryBuilderTest.php
@@ -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);
}
}

0 comments on commit e941f0b

Please sign in to comment.