Skip to content

Instantly share code, notes, and snippets.

@flip111
Created December 8, 2013 14:43
Show Gist options
  • Save flip111/7858442 to your computer and use it in GitHub Desktop.
Save flip111/7858442 to your computer and use it in GitHub Desktop.
Support for STRAIGHT_JOIN in Doctrine. This does not work (yet) because not having a clean way to implement it. But making these changes in Doctrine directly will work.
<?php
/**
* DoctrineExtensions Vendor Specific DQL Query Features
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so I can send you a copy immediately.
*/
namespace DoctrineExtensions\Query\AST;
use Doctrine\ORM\Query\AST\Join;
/**
* Join ::= [["LEFT" ["OUTER"] | "INNER"] "JOIN" | "STRAIGHT_JOIN"] JoinAssociationPathExpression
* ["AS"] AliasIdentificationVariable [("ON" | "WITH") ConditionalExpression]
*
* @link www.doctrine-project.org
* @since 2.0
* @author Guilherme Blanco <[email protected]>
* @author Jonathan Wage <[email protected]>
* @author Roman Borschel <[email protected]>
*/
class MysqlJoin extends Join
{
const JOIN_TYPE_STRAIGHT = 4;
}
<?php
/**
* DoctrineExtensions Vendor Specific DQL Query Features
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so I can send you a copy immediately.
*/
namespace DoctrineExtensions\Query;
use Doctrine\ORM\Query\Lexer;
/**
* Scans a DQL query for tokens.
*
* @author Guilherme Blanco <[email protected]>
* @author Janne Vanhala <[email protected]>
* @author Roman Borschel <[email protected]>
* @since 2.0
*/
class MysqlLexer extends Lexer
{
const T_STRAIGHT_JOIN = 158;
}
<?php
/**
* DoctrineExtensions Vendor Specific DQL Query Features
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so I can send you a copy immediately.
*/
namespace DoctrineExtensions\Query;
use Doctrine\ORM\Query\Parser;
/**
* An LL(*) recursive-descent parser for the context-free grammar of the Doctrine Query Language.
* Parses a DQL query, reports any errors in it, and generates an AST.
*
* @since 2.0
* @author Guilherme Blanco <[email protected]>
* @author Jonathan Wage <[email protected]>
* @author Roman Borschel <[email protected]>
* @author Janne Vanhala <[email protected]>
* @author Fabio B. Silva <[email protected]>
*/
class MysqlParser extends Parser
{
/**
* IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {Join}*
*
* @return \Doctrine\ORM\Query\AST\IdentificationVariableDeclaration
*/
public function IdentificationVariableDeclaration()
{
$rangeVariableDeclaration = $this->RangeVariableDeclaration();
$rangeVariableDeclaration->isRoot = true;
$indexBy = $this->lexer->isNextToken(Lexer::T_INDEX) ? $this->IndexBy() : null;
$joins = array();
while (
$this->lexer->isNextToken(Lexer::T_LEFT) ||
$this->lexer->isNextToken(Lexer::T_INNER) ||
$this->lexer->isNextToken(Lexer::T_JOIN) ||
$this->lexer->isNextToken(Lexer::T_STRAIGHT_JOIN)
) {
$joins[] = $this->Join();
}
return new AST\IdentificationVariableDeclaration(
$rangeVariableDeclaration, $indexBy, $joins
);
}
/**
* Join ::= [["LEFT" ["OUTER"] | "INNER"] "JOIN" | "STRAIGHT_JOIN"]
* (JoinAssociationDeclaration | RangeVariableDeclaration)
* ["WITH" ConditionalExpression]
*
* @return \Doctrine\ORM\Query\AST\Join
*/
public function Join()
{
// Check Join type
$joinType = AST\Join::JOIN_TYPE_INNER;
switch (true) {
case ($this->lexer->isNextToken(Lexer::T_LEFT)):
$this->match(Lexer::T_LEFT);
$joinType = AST\Join::JOIN_TYPE_LEFT;
// Possible LEFT OUTER join
if ($this->lexer->isNextToken(Lexer::T_OUTER)) {
$this->match(Lexer::T_OUTER);
$joinType = AST\Join::JOIN_TYPE_LEFTOUTER;
}
$this->match(Lexer::T_JOIN);
break;
case ($this->lexer->isNextToken(Lexer::T_INNER)):
$this->match(Lexer::T_INNER);
$this->match(Lexer::T_JOIN);
break;
case ($this->lexer->isNextToken(Lexer::T_STRAIGHT_JOIN)):
$joinType = AST\Join::JOIN_TYPE_STRAIGHT;
$this->match(Lexer::T_STRAIGHT_JOIN);
break;
default:
// Do nothing
}
$next = $this->lexer->glimpse();
$joinDeclaration = ($next['type'] === Lexer::T_DOT) ? $this->JoinAssociationDeclaration() : $this->RangeVariableDeclaration();
$adhocConditions = $this->lexer->isNextToken(Lexer::T_WITH);
$join = new AST\Join($joinType, $joinDeclaration);
// Describe non-root join declaration
if ($joinDeclaration instanceof AST\RangeVariableDeclaration) {
$joinDeclaration->isRoot = false;
$adhocConditions = true;
}
// Check for ad-hoc Join conditions
if ($adhocConditions) {
$this->match(Lexer::T_WITH);
$join->conditionalExpression = $this->ConditionalExpression();
}
return $join;
}
}
<?php
/**
* DoctrineExtensions Vendor Specific DQL Query Features
*
* LICENSE
*
* This source file is subject to the new BSD license that is bundled
* with this package in the file LICENSE.txt.
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to [email protected] so I can send you a copy immediately.
*/
namespace DoctrineExtensions\Query;
use Doctrine\ORM\Query\SqlWalker;
class MysqlWalker extends SqlWalker
{
/**
* Walks down a SelectClause AST node, thereby generating the appropriate SQL.
*
* @param $selectClause
* @return string The SQL.
*/
public function walkSelectClause($selectClause)
{
$sql = parent::walkSelectClause($selectClause);
if ($this->getQuery()->getHint('mysqlWalker.sqlNoCache') === true) {
if ($selectClause->isDistinct) {
$sql = str_replace('SELECT DISTINCT', 'SELECT DISTINCT SQL_NO_CACHE', $sql);
} else {
$sql = str_replace('SELECT', 'SELECT SQL_NO_CACHE', $sql);
}
}
return $sql;
}
/**
* Walks down a Join AST node and creates the corresponding SQL.
*
* @param AST\Join $join
*
* @return string The SQL.
*/
public function walkJoin($join)
{
$joinType = $join->joinType;
$joinDeclaration = $join->joinAssociationDeclaration;
if ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) {
$sql = ' LEFT JOIN ';
} elseif ($joinType == AST\Join::JOIN_TYPE_STRAIGHT) {
$sql = ' STRAIGHT_JOIN ';
} else {
$sql = ' INNER JOIN ';
}
switch (true) {
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\RangeVariableDeclaration):
$class = $this->em->getClassMetadata($joinDeclaration->abstractSchemaName);
$dqlAlias = $joinDeclaration->aliasIdentificationVariable;
$tableAlias = $this->getSQLTableAlias($class->table['name'], $dqlAlias);
$condition = '(' . $this->walkConditionalExpression($join->conditionalExpression) . ')';
$condExprConjunction = ($class->isInheritanceTypeJoined() && $joinType != AST\Join::JOIN_TYPE_LEFT && $joinType != AST\Join::JOIN_TYPE_LEFTOUTER)
? ' AND '
: ' ON ';
$sql .= $this->walkRangeVariableDeclaration($joinDeclaration);
$conditions = array($condition);
// Apply remaining inheritance restrictions
$discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($dqlAlias));
if ($discrSql) {
$conditions[] = $discrSql;
}
// Apply the filters
$filterExpr = $this->generateFilterConditionSQL($class, $tableAlias);
if ($filterExpr) {
$conditions[] = $filterExpr;
}
$sql .= $condExprConjunction . implode(' AND ', $conditions);
break;
case ($joinDeclaration instanceof \Doctrine\ORM\Query\AST\JoinAssociationDeclaration):
$sql .= $this->walkJoinAssociationDeclaration($joinDeclaration, $joinType, $join->conditionalExpression);
break;
}
return $sql;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment