diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index 2c5c42ab8..c229ea27b 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -38,6 +38,7 @@ class Configuration extends \Doctrine\DBAL\Configuration public function __construct() { parent::__construct(); + $this->_attributes = array_merge($this->_attributes, array( 'resultCacheImpl' => null, 'queryCacheImpl' => null, @@ -46,7 +47,7 @@ class Configuration extends \Doctrine\DBAL\Configuration 'cacheDir' => null, 'allowPartialObjects' => true, 'useCExtension' => false - )); + )); //TODO: Move this to client code to avoid unnecessary work when a different metadata // driver is used. diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 499a84710..b0c8b2d62 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -117,6 +117,11 @@ class Parser */ private $_queryComponents = array(); + /** + * Sql tree walker + * + * @var SqlTreeWalker + */ private $_sqlTreeWalker; /** @@ -195,7 +200,9 @@ class Parser } // Create SqlWalker who creates the SQL from the AST - $sqlWalker = $this->_sqlTreeWalker ?: new SqlWalker($this->_query, $this->_parserResult, $this->_queryComponents); + $sqlWalker = $this->_sqlTreeWalker ?: new SqlWalker( + $this->_query, $this->_parserResult, $this->_queryComponents + ); // Assign an SQL executor to the parser result $this->_parserResult->setSqlExecutor(Exec\AbstractExecutor::create($AST, $sqlWalker)); @@ -261,10 +268,18 @@ class Parser public function semanticalError($message = '', $token = null) { if ($token === null) { - $token = $this->_lexer->token; + $token = $this->_lexer->lookahead; } + + // Find a position of a final word to display in error string + $dql = $this->_query->getDql(); + $pos = strpos($dql, ' ', $token['position'] + 10); + $length = ($pos !== false) ? $pos - $token['position'] : 10; + + // Building informative message + $message = 'line 0, col ' . (isset($token['position']) ? $token['position'] : '-1') + . " near '" . substr($dql, $token['position'], $length) . "'): Error: " . $message; - //TODO: Include $token in $message throw DoctrineException::updateMe($message); } @@ -337,7 +352,6 @@ class Parser default: $this->syntaxError('SELECT, UPDATE or DELETE'); - break; } } @@ -497,6 +511,7 @@ class Parser /** * NewValue ::= SimpleArithmeticExpression | StringPrimary | DatetimePrimary | BooleanPrimary | * EnumPrimary | SimpleEntityExpression | "NULL" + * * @todo Implementation still incomplete. */ public function NewValue() @@ -641,8 +656,13 @@ class Parser $fieldIdentificationVariable = $this->_lexer->token['value']; } } else { - //TODO: If hydration mode is OBJECT throw an exception ("partial object dangerous...") - // unless the doctrine.forcePartialLoad query hint is set + if ( + $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT && + ! $this->_em->getConfiguration()->getAllowPartialObjects() + ) { + $this->semanticalError('Cannot select partial object when using object hydration'); + } + $expression = $this->StateFieldPathExpression(); } diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index bdfa99171..a59ce1b25 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -15,10 +15,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase public function assertValidDql($dql, $debug = false) { try { - $query = $this->_em->createQuery($dql); - $parser = new \Doctrine\ORM\Query\Parser($query); - $parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker); - $parserResult = $parser->parse(); + $parserResult = $this->parseDql($dql); } catch (\Exception $e) { if ($debug) { echo $e->getTraceAsString() . PHP_EOL; @@ -30,11 +27,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase public function assertInvalidDql($dql, $debug = false) { try { - $query = $this->_em->createQuery($dql); - $query->setDql($dql); - $parser = new \Doctrine\ORM\Query\Parser($query); - $parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker); - $parserResult = $parser->parse(); + $parserResult = $this->parseDql($dql); $this->fail('No syntax errors were detected, when syntax errors were expected'); } catch (\Exception $e) { if ($debug) { @@ -43,6 +36,16 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase } } } + + public function parseDql($dql) + { + $query = $this->_em->createQuery($dql); + $query->setDql($dql); + $parser = new \Doctrine\ORM\Query\Parser($query); + $parser->setSqlTreeWalker(new \Doctrine\Tests\Mocks\MockTreeWalker); + + return $parser->parse(); + } public function testEmptyQueryString() { @@ -326,4 +329,19 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); } + + /** + * This checks for invalid attempt to hydrate a proxy. It should throw an exception + * + * @expectedException \Doctrine\Common\DoctrineException + */ + public function testPartialObjectLoad() + { + $oldValue = $this->_em->getConfiguration()->getAllowPartialObjects(); + $this->_em->getConfiguration()->setAllowPartialObjects(false); + + $this->parseDql('SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u'); + + $this->_em->getConfiguration()->setAllowPartialObjects($oldValue); + } } \ No newline at end of file