From 705199e8976011121da7f0a4e0567733f0bd43cd Mon Sep 17 00:00:00 2001 From: romanb Date: Sat, 28 Mar 2009 17:10:41 +0000 Subject: [PATCH] [2.0] Parser work. Added support for functions in SelectExpressions. --- .../DBAL/Platforms/AbstractPlatform.php | 66 ++-- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 24 +- .../DBAL/Platforms/PostgreSqlPlatform.php | 77 ++++- .../DBAL/Platforms/SqlitePlatform.php | 6 +- lib/Doctrine/DBAL/Types/MediumIntType.php | 13 - lib/Doctrine/DBAL/Types/TinyIntType.php | 13 - lib/Doctrine/ORM/EntityManagerException.php | 2 +- .../Query/AST/Functions/ConcatFunction.php | 9 +- .../ORM/Query/AST/Functions/FunctionNode.php | 14 +- .../ORM/Query/AST/QuantifiedExpression.php | 69 +++++ lib/Doctrine/ORM/Query/Lexer.php | 8 +- lib/Doctrine/ORM/Query/Parser.php | 60 +++- lib/Doctrine/ORM/Query/SqlWalker.php | 285 +++++++++++++++++- .../ORM/Query/LanguageRecognitionTest.php | 24 +- .../ORM/Query/SelectSqlGenerationTest.php | 82 +++-- 15 files changed, 597 insertions(+), 155 deletions(-) delete mode 100644 lib/Doctrine/DBAL/Types/MediumIntType.php delete mode 100644 lib/Doctrine/DBAL/Types/TinyIntType.php create mode 100644 lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 2f8991984..2d9c8ad7b 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -131,7 +131,7 @@ abstract class AbstractPlatform */ public function getRegexpExpression() { - throw \Doctrine\Common\DoctrineException::updateMe('Regular expression operator is not supported by this database driver.'); + throw DoctrineException::updateMe('Regular expression operator is not supported by this database driver.'); } /** @@ -351,7 +351,7 @@ abstract class AbstractPlatform */ public function getSoundexExpression($value) { - throw \Doctrine\Common\DoctrineException::updateMe('SQL soundex function not supported by this driver.'); + throw DoctrineException::updateMe('SQL soundex function not supported by this driver.'); } /** @@ -701,7 +701,7 @@ abstract class AbstractPlatform $column = $this->getIdentifier($column); if (count($values) == 0) { - throw \Doctrine\Common\DoctrineException::updateMe('Values array for IN operator should not be empty.'); + throw DoctrineException::updateMe('Values array for IN operator should not be empty.'); } return $column . ' IN (' . implode(', ', $values) . ')'; } @@ -784,7 +784,7 @@ abstract class AbstractPlatform */ public function getGuidExpression() { - throw \Doctrine\Common\DoctrineException::updateMe('method not implemented'); + throw DoctrineException::updateMe('method not implemented'); } /** @@ -847,7 +847,7 @@ abstract class AbstractPlatform */ public function getListDatabasesSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List databases not supported by this driver.'); + throw DoctrineException::updateMe('List databases not supported by this driver.'); } /** @@ -857,7 +857,7 @@ abstract class AbstractPlatform */ public function getListFunctionsSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List functions not supported by this driver.'); + throw DoctrineException::updateMe('List functions not supported by this driver.'); } /** @@ -867,7 +867,7 @@ abstract class AbstractPlatform */ public function getListTriggersSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List triggers not supported by this driver.'); + throw DoctrineException::updateMe('List triggers not supported by this driver.'); } /** @@ -877,7 +877,7 @@ abstract class AbstractPlatform */ public function getListSequencesSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List sequences not supported by this driver.'); + throw DoctrineException::updateMe('List sequences not supported by this driver.'); } /** @@ -887,7 +887,7 @@ abstract class AbstractPlatform */ public function getListTableConstraintsSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List table constraints not supported by this driver.'); + throw DoctrineException::updateMe('List table constraints not supported by this driver.'); } /** @@ -897,7 +897,7 @@ abstract class AbstractPlatform */ public function getListTableColumnsSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List table columns not supported by this driver.'); + throw DoctrineException::updateMe('List table columns not supported by this driver.'); } /** @@ -907,7 +907,7 @@ abstract class AbstractPlatform */ public function getListTablesSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List tables not supported by this driver.'); + throw DoctrineException::updateMe('List tables not supported by this driver.'); } /** @@ -917,7 +917,7 @@ abstract class AbstractPlatform */ public function getListUsersSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List users not supported by this driver.'); + throw DoctrineException::updateMe('List users not supported by this driver.'); } /** @@ -927,7 +927,7 @@ abstract class AbstractPlatform */ public function getListViewsSql() { - throw \Doctrine\Common\DoctrineException::updateMe('List views not supported by this driver.'); + throw DoctrineException::updateMe('List views not supported by this driver.'); } /** @@ -967,7 +967,7 @@ abstract class AbstractPlatform */ public function getDropSequenceSql() { - throw \Doctrine\Common\DoctrineException::updateMe('Drop sequence not supported by this driver.'); + throw DoctrineException::updateMe('Drop sequence not supported by this driver.'); } /** @@ -975,7 +975,7 @@ abstract class AbstractPlatform */ public function getSequenceNextValSql($sequenceName) { - throw \Doctrine\Common\DoctrineException::updateMe('Sequences not supported by this driver.'); + throw DoctrineException::updateMe('Sequences not supported by this driver.'); } /** @@ -985,7 +985,7 @@ abstract class AbstractPlatform */ public function getCreateDatabaseSql($database) { - throw \Doctrine\Common\DoctrineException::updateMe('Create database not supported by this driver.'); + throw DoctrineException::updateMe('Create database not supported by this driver.'); } /** @@ -996,10 +996,10 @@ abstract class AbstractPlatform public function getCreateTableSql($table, array $columns, array $options = array()) { if ( ! $table) { - throw \Doctrine\Common\DoctrineException::updateMe('no valid table name specified'); + throw DoctrineException::updateMe('no valid table name specified'); } if (empty($columns)) { - throw \Doctrine\Common\DoctrineException::updateMe('no fields specified for table ' . $name); + throw DoctrineException::updateMe('no fields specified for table ' . $name); } $queryFields = $this->getFieldDeclarationListSql($columns); @@ -1043,7 +1043,7 @@ abstract class AbstractPlatform */ public function getCreateSequenceSql($sequenceName, $start = 1, array $options) { - throw \Doctrine\Common\DoctrineException::updateMe('Create sequence not supported by this driver.'); + throw DoctrineException::updateMe('Create sequence not supported by this driver.'); } /** @@ -1104,7 +1104,7 @@ abstract class AbstractPlatform $type = strtoupper($definition['type']) . ' '; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('Unknown index type ' . $definition['type']); + throw DoctrineException::updateMe('Unknown index type ' . $definition['type']); } } @@ -1200,7 +1200,7 @@ abstract class AbstractPlatform */ public function getAlterTableSql($name, array $changes, $check = false) { - throw \Doctrine\Common\DoctrineException::updateMe('Alter table not supported by this driver.'); + throw DoctrineException::updateMe('Alter table not supported by this driver.'); } /** @@ -1311,7 +1311,7 @@ abstract class AbstractPlatform * * @return string */ - abstract public function getTinyIntTypeDeclarationSql(array $columnDef); + //abstract public function getTinyIntTypeDeclarationSql(array $columnDef); /** * Gets the SQL snippet that declares a SMALLINT column. @@ -1325,7 +1325,7 @@ abstract class AbstractPlatform * * @return string */ - abstract public function getMediumIntTypeDeclarationSql(array $columnDef); + //abstract public function getMediumIntTypeDeclarationSql(array $columnDef); /** * Gets the SQL snippet that declares common properties of an integer column. @@ -1408,12 +1408,12 @@ abstract class AbstractPlatform if (strtolower($definition['type']) == 'unique') { $type = strtoupper($definition['type']) . ' '; } else { - throw \Doctrine\Common\DoctrineException::updateMe('Unknown index type ' . $definition['type']); + throw DoctrineException::updateMe('Unknown index type ' . $definition['type']); } } if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) { - throw \Doctrine\Common\DoctrineException::updateMe('No index columns given.'); + throw DoctrineException::updateMe('No index columns given.'); } $query = $type . 'INDEX ' . $name; @@ -1469,7 +1469,7 @@ abstract class AbstractPlatform */ public function getShowDatabasesSql() { - throw \Doctrine\Common\DoctrineException::updateMe('Show databases not supported by this driver.'); + throw DoctrineException::updateMe('Show databases not supported by this driver.'); } /** @@ -1562,7 +1562,7 @@ abstract class AbstractPlatform return $upper; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('Unknown foreign key referential action \'' . $upper . '\' given.'); + throw DoctrineException::updateMe('Unknown foreign key referential action \'' . $upper . '\' given.'); } } @@ -1582,13 +1582,13 @@ abstract class AbstractPlatform $sql .= 'FOREIGN KEY ('; if ( ! isset($definition['local'])) { - throw \Doctrine\Common\DoctrineException::updateMe('Local reference field missing from definition.'); + throw DoctrineException::updateMe('Local reference field missing from definition.'); } if ( ! isset($definition['foreign'])) { - throw \Doctrine\Common\DoctrineException::updateMe('Foreign reference field missing from definition.'); + throw DoctrineException::updateMe('Foreign reference field missing from definition.'); } if ( ! isset($definition['foreignTable'])) { - throw \Doctrine\Common\DoctrineException::updateMe('Foreign reference table missing from definition.'); + throw DoctrineException::updateMe('Foreign reference table missing from definition.'); } if ( ! is_array($definition['local'])) { @@ -1663,7 +1663,7 @@ abstract class AbstractPlatform */ public function getMatchPatternExpression($pattern, $operator = null, $field = null) { - throw \Doctrine\Common\DoctrineException::updateMe("Method not implemented."); + throw DoctrineException::updateMe("Method not implemented."); } /** @@ -1832,7 +1832,7 @@ abstract class AbstractPlatform case Connection::TRANSACTION_SERIALIZABLE: return 'SERIALIZABLE'; default: - throw \Doctrine\Common\DoctrineException::updateMe('isolation level is not supported: ' . $isolation); + throw DoctrineException::updateMe('isolation level is not supported: ' . $isolation); } } @@ -1843,7 +1843,7 @@ abstract class AbstractPlatform */ public function getSetTransactionIsolationSql($level) { - throw \Doctrine\Common\DoctrineException::updateMe('Set transaction isolation not supported by this platform.'); + throw DoctrineException::updateMe('Set transaction isolation not supported by this platform.'); } /** diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 9b8545c33..e41f0c380 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -16,7 +16,7 @@ * * This software consists of voluntary contributions made by many individuals * and is licensed under the LGPL. For more information, see - * . + * . */ namespace Doctrine\DBAL\Platforms; @@ -185,7 +185,7 @@ class MySqlPlatform extends AbstractPlatform $match = $field.'LIKE BINARY '; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('not a supported operator type:'. $operator); + throw DoctrineException::updateMe('not a supported operator type:'. $operator); } } $match.= "'"; @@ -520,7 +520,7 @@ class MySqlPlatform extends AbstractPlatform $length = null; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('unknown database attribute type: ' . $dbType); + throw DoctrineException::updateMe('unknown database attribute type: ' . $dbType); } $length = ((int) $length == 0) ? null : (int) $length; @@ -678,10 +678,10 @@ class MySqlPlatform extends AbstractPlatform public function getCreateTableSql($name, array $fields, array $options = array()) { if ( ! $name) { - throw \Doctrine\Common\DoctrineException::updateMe('no valid table name specified'); + throw DoctrineException::updateMe('no valid table name specified'); } if (empty($fields)) { - throw \Doctrine\Common\DoctrineException::updateMe('no fields specified for table "'.$name.'"'); + throw DoctrineException::updateMe('no fields specified for table "'.$name.'"'); } $queryFields = $this->getFieldDeclarationListSql($fields); @@ -866,7 +866,7 @@ class MySqlPlatform extends AbstractPlatform public function getAlterTableSql($name, array $changes, $check = false) { if ( ! $name) { - throw \Doctrine\Common\DoctrineException::updateMe('no valid table name specified'); + throw DoctrineException::updateMe('no valid table name specified'); } foreach ($changes as $changeName => $change) { switch ($changeName) { @@ -877,7 +877,7 @@ class MySqlPlatform extends AbstractPlatform case 'name': break; default: - throw \Doctrine\Common\DoctrineException::updateMe('change type "' . $changeName . '" not yet supported'); + throw DoctrineException::updateMe('change type "' . $changeName . '" not yet supported'); } } @@ -1003,7 +1003,7 @@ class MySqlPlatform extends AbstractPlatform $type = strtoupper($definition['type']) . ' '; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('Unknown index type ' . $definition['type']); + throw DoctrineException::updateMe('Unknown index type ' . $definition['type']); } } $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table; @@ -1050,10 +1050,10 @@ class MySqlPlatform extends AbstractPlatform } /** @override */ - public function getTinyIntTypeDeclarationSql(array $field) + /*public function getTinyIntTypeDeclarationSql(array $field) { return 'TINYINT' . $this->_getCommonIntegerTypeDeclarationSql($field); - } + }*/ /** @override */ public function getSmallIntTypeDeclarationSql(array $field) @@ -1062,10 +1062,10 @@ class MySqlPlatform extends AbstractPlatform } /** @override */ - public function getMediumIntTypeDeclarationSql(array $field) + /*public function getMediumIntTypeDeclarationSql(array $field) { return 'MEDIUMINT' . $this->_getCommonIntegerTypeDeclarationSql($field); - } + }*/ /** @override */ protected function _getCommonIntegerTypeDeclarationSql(array $columnDef) diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php index b1fbb3512..9fe0c555f 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -101,7 +101,7 @@ class PostgreSqlPlatform extends AbstractPlatform public function getNativeDeclaration(array $field) { if ( ! isset($field['type'])) { - throw \Doctrine\Common\DoctrineException::updateMe('Missing column type.'); + throw DoctrineException::updateMe('Missing column type.'); } switch ($field['type']) { case 'char': @@ -161,7 +161,7 @@ class PostgreSqlPlatform extends AbstractPlatform $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(Doctrine::ATTR_DECIMAL_PLACES); return 'NUMERIC('.$length.','.$scale.')'; } - throw \Doctrine\Common\DoctrineException::updateMe('Unknown field type \'' . $field['type'] . '\'.'); + throw DoctrineException::updateMe('Unknown field type \'' . $field['type'] . '\'.'); } /** @@ -293,7 +293,7 @@ class PostgreSqlPlatform extends AbstractPlatform $length = null; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('unknown database attribute type: '.$dbType); + throw DoctrineException::updateMe('unknown database attribute type: '.$dbType); } return array('type' => $type, @@ -461,7 +461,7 @@ class PostgreSqlPlatform extends AbstractPlatform $match = $field.'LIKE '; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('not a supported operator type:'. $operator); + throw DoctrineException::updateMe('not a supported operator type:'. $operator); } } $match.= "'"; @@ -766,7 +766,7 @@ class PostgreSqlPlatform extends AbstractPlatform case 'rename': break; default: - throw \Doctrine\Common\DoctrineException::updateMe('change type "' . $changeName . '\" not yet supported'); + throw DoctrineException::updateMe('change type "' . $changeName . '\" not yet supported'); } } @@ -798,7 +798,7 @@ class PostgreSqlPlatform extends AbstractPlatform $serverInfo = $this->getServerVersion(); if (is_array($serverInfo) && $serverInfo['major'] < 8) { - throw \Doctrine\Common\DoctrineException::updateMe('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above'); + throw DoctrineException::updateMe('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above'); } $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->getTypeDeclarationSql($field['definition']); $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; @@ -875,10 +875,10 @@ class PostgreSqlPlatform extends AbstractPlatform public function getCreateTableSql($name, array $fields, array $options = array()) { if ( ! $name) { - throw \Doctrine\Common\DoctrineException::updateMe('no valid table name specified'); + throw DoctrineException::updateMe('no valid table name specified'); } if (empty($fields)) { - throw \Doctrine\Common\DoctrineException::updateMe('no fields specified for table ' . $name); + throw DoctrineException::updateMe('no fields specified for table ' . $name); } $queryFields = $this->getFieldDeclarationListSql($fields); @@ -1001,4 +1001,65 @@ class PostgreSqlPlatform extends AbstractPlatform return 'SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL ' . $this->_getTransactionIsolationLevelSql($level); } + + /** + * @override + */ + public function getIntegerTypeDeclarationSql(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'SERIAL'; + } + return 'INT'; + } + + /** + * @override + */ + public function getBigIntTypeDeclarationSql(array $field) + { + if ( ! empty($field['autoincrement'])) { + return 'BIGSERIAL'; + } + return 'BIGINT'; + } + + /** + * @override + */ + public function getSmallIntTypeDeclarationSql(array $field) + { + return 'SMALLINT'; + } + + /** + * @override + */ + protected function _getCommonIntegerTypeDeclarationSql(array $columnDef) + { + return ''; + } + + /** + * Gets the SQL snippet used to declare a VARCHAR column on the MySql platform. + * + * @params array $field + * @override + */ + public function getVarcharDeclarationSql(array $field) + { + if ( ! isset($field['length'])) { + if (array_key_exists('default', $field)) { + $field['length'] = $this->getVarcharMaxLength(); + } else { + $field['length'] = false; + } + } + + $length = ($field['length'] <= $this->getVarcharMaxLength()) ? $field['length'] : false; + $fixed = (isset($field['fixed'])) ? $field['fixed'] : false; + + return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)') + : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT'); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index 8a3a0cc74..13e97e6db 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -194,7 +194,7 @@ class SqlitePlatform extends AbstractPlatform public function getNativeDeclaration(array $field) { if ( ! isset($field['type'])) { - throw \Doctrine\Common\DoctrineException::updateMe('Missing column type.'); + throw DoctrineException::updateMe('Missing column type.'); } switch ($field['type']) { case 'text': @@ -254,7 +254,7 @@ class SqlitePlatform extends AbstractPlatform $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(Doctrine::ATTR_DECIMAL_PLACES); return 'DECIMAL('.$length.','.$scale.')'; } - throw \Doctrine\Common\DoctrineException::updateMe('Unknown field type \'' . $field['type'] . '\'.'); + throw DoctrineException::updateMe('Unknown field type \'' . $field['type'] . '\'.'); } /** @@ -371,7 +371,7 @@ class SqlitePlatform extends AbstractPlatform $length = null; break; default: - throw \Doctrine\Common\DoctrineException::updateMe('unknown database attribute type: '.$dbType); + throw DoctrineException::updateMe('unknown database attribute type: '.$dbType); } return array('type' => $type, diff --git a/lib/Doctrine/DBAL/Types/MediumIntType.php b/lib/Doctrine/DBAL/Types/MediumIntType.php deleted file mode 100644 index c57897d47..000000000 --- a/lib/Doctrine/DBAL/Types/MediumIntType.php +++ /dev/null @@ -1,13 +0,0 @@ - * @author Roman Borschel diff --git a/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php index 583b9fe1f..070b222fb 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/ConcatFunction.php @@ -31,13 +31,18 @@ class ConcatFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { - //TODO: Use platform to get SQL + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); + return $platform->getConcatExpression( + $sqlWalker->walkStringPrimary($this->_firstStringPrimary), + $sqlWalker->walkStringPrimary($this->_secondStringPrimary) + ); + /* $sql = 'CONCAT(' . $sqlWalker->walkStringPrimary($this->_firstStringPrimary) . ', ' . $sqlWalker->walkStringPrimary($this->_secondStringPrimary) . ')'; - return $sql; + return $sql;*/ } /** diff --git a/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php b/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php index ce7733750..faa1e8df6 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/FunctionNode.php @@ -50,17 +50,5 @@ abstract class FunctionNode extends Node return $sqlWalker->walkFunction($this); } - //abstract public function parse(\Doctrine\ORM\Query\Parser $parser); - -/* - public function getExpressions() - { - return $this->_expressions; - } - - public function setExpressions(array $expressions) - { - $this->_expressions = $expressions; - } - */ + abstract public function parse(\Doctrine\ORM\Query\Parser $parser); } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php b/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php new file mode 100644 index 000000000..0c5a3a2ff --- /dev/null +++ b/lib/Doctrine/ORM/Query/AST/QuantifiedExpression.php @@ -0,0 +1,69 @@ +_subselect = $subselect; + } + + public function getSubselect() + { + return $this->_subselect; + } + + public function isAll() + { + return $this->_all; + } + + public function isAny() + { + return $this->_any; + } + + public function isSome() + { + return $this->_some; + } + + public function setAll($bool) + { + $this->_all = $bool; + } + + public function setAny($bool) + { + $this->_any = $bool; + } + + public function setSome($bool) + { + $this->_some = $bool; + } + + /** + * @override + */ + public function dispatch($sqlWalker) + { + return $sqlWalker->walkQuantifiedExpression($this); + } +} + diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 8126c11e5..6d935a9a6 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -336,7 +336,7 @@ class Lexer } /** - * @todo Doc + * Resets the peek pointer to 0. */ public function resetPeek() { @@ -351,6 +351,12 @@ class Lexer $this->_position = $position; } + /** + * Gets the literal for a given token. + * + * @param mixed $token + * @return string + */ public function getLiteral($token) { if ( ! $this->_keywordsTable) { diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index aef9e8dc0..c7db39e46 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -626,7 +626,8 @@ class Parser /** * SelectExpression ::= * IdentificationVariable | StateFieldPathExpression | - * (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] + * (AggregateExpression | "(" Subselect ")") [["AS"] FieldAliasIdentificationVariable] | + * Function */ public function _SelectExpression() { @@ -634,11 +635,15 @@ class Parser $fieldIdentificationVariable = null; $peek = $this->_lexer->glimpse(); // First we recognize for an IdentificationVariable (DQL class alias) - if ($peek['value'] != '.' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { + if ($peek['value'] != '.' && $peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { $expression = $this->_IdentificationVariable(); } else if (($isFunction = $this->_isFunction()) !== false || $this->_isSubselect()) { if ($isFunction) { - $expression = $this->_AggregateExpression(); + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + $expression = $this->_AggregateExpression(); + } else { + $expression = $this->_Function(); + } } else { $this->match('('); $expression = $this->_Subselect(); @@ -1290,7 +1295,7 @@ class Parser } } } - else if ($this->_isAggregateFunction($this->_lexer->lookahead)) { + /*else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { $leftExpr = $this->_StringExpression(); $operator = $this->_ComparisonOperator(); if ($this->_lexer->lookahead['type'] === Lexer::T_ALL || @@ -1300,7 +1305,7 @@ class Parser } else { $rightExpr = $this->_StringExpression(); } - } + }*/ else { $leftExpr = $this->_ArithmeticExpression(); $operator = $this->_ComparisonOperator(); @@ -1316,6 +1321,20 @@ class Parser return new AST\ComparisonExpression($leftExpr, $operator, $rightExpr); } + public function _Function() + { + $funcName = $this->_lexer->lookahead['value']; + if ($this->_isStringFunction($funcName)) { + return $this->_FunctionsReturningStrings(); + } else if ($this->_isNumericFunction($funcName)) { + return $this->_FunctionsReturningNumerics(); + } else if ($this->_isDatetimeFunction($funcName)) { + return $this->_FunctionsReturningDatetime(); + } else { + $this->syntaxError('Known function.'); + } + } + public function _isStringFunction($funcName) { return isset(self::$_STRING_FUNCTIONS[strtolower($funcName)]); @@ -1468,6 +1487,33 @@ class Parser return $existsExpression; } + /** + * QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")" + */ + public function _QuantifiedExpression() + { + $all = $any = $some = false; + if ($this->_lexer->isNextToken(Lexer::T_ALL)) { + $this->match(Lexer::T_ALL); + $all = true; + } else if ($this->_lexer->isNextToken(Lexer::T_ANY)) { + $this->match(Lexer::T_ANY); + $any = true; + } else if ($this->_lexer->isNextToken(Lexer::T_SOME)) { + $this->match(Lexer::T_SOME); + $some = true; + } else { + $this->syntaxError('ALL, ANY or SOME'); + } + $this->match('('); + $qExpr = new AST\QuantifiedExpression($this->_Subselect()); + $this->match(')'); + $qExpr->setAll($all); + $qExpr->setAny($any); + $qExpr->setSome($some); + return $qExpr; + } + /** * Subselect ::= SimpleSelectClause SubselectFromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause] */ @@ -1805,7 +1851,7 @@ class Parser if ($peek['value'] == '.') { return $this->_StateFieldPathExpression(); } else if ($peek['value'] == '(') { - //TODO... FunctionsReturningStrings or AggregateExpression + return $this->_FunctionsReturningStrings(); } else { $this->syntaxError("'.' or '('"); } @@ -1815,6 +1861,8 @@ class Parser } else if ($this->_lexer->lookahead['type'] === Lexer::T_INPUT_PARAMETER) { $this->match(Lexer::T_INPUT_PARAMETER); return new AST\InputParameter($this->_lexer->token['value']); + } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->_AggregateExpression(); } else { $this->syntaxError('StateFieldPathExpression | string | InputParameter | FunctionsReturningStrings | AggregateExpression'); } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index af9644e56..a918a6ee5 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -64,17 +64,34 @@ class SqlWalker $this->_dqlToSqlAliasMap = array_flip($sqlToDqlAliasMap); } + public function getConnection() + { + return $this->_em->getConnection(); + } + + /** + * Walks down a SelectStatement AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ public function walkSelectStatement(AST\SelectStatement $AST) { $sql = $this->walkSelectClause($AST->getSelectClause()); $sql .= $this->walkFromClause($AST->getFromClause()); $sql .= $AST->getWhereClause() ? $this->walkWhereClause($AST->getWhereClause()) : ''; $sql .= $AST->getGroupByClause() ? $this->walkGroupByClause($AST->getGroupByClause()) : ''; + $sql .= $AST->getHavingClause() ? $this->walkHavingClause($AST->getHavingClause()) : ''; + $sql .= $AST->getOrderByClause() ? $this->walkOrderByClause($AST->getOrderByClause()) : ''; //... more clauses return $sql; } + /** + * Walks down a SelectClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ public function walkSelectClause($selectClause) { return 'SELECT ' . (($selectClause->isDistinct()) ? 'DISTINCT ' : '') @@ -82,6 +99,11 @@ class SqlWalker $selectClause->getSelectExpressions())); } + /** + * Walks down a FromClause AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ public function walkFromClause($fromClause) { $sql = ' FROM '; @@ -98,11 +120,61 @@ class SqlWalker return $sql; } + /** + * Walks down a FunctionNode AST node, thereby generating the appropriate SQL. + * + * @return string The SQL. + */ public function walkFunction($function) { return $function->getSql($this); } + /** + * Walks down an OrderByClause AST node, thereby generating the appropriate SQL. + * + * @param OrderByClause + * @return string The SQL. + */ + public function walkOrderByClause($orderByClause) + { + // OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}* + return ' ORDER BY ' + . implode(', ', array_map(array($this, 'walkOrderByItem'), + $orderByClause->getOrderByItems())); + } + + /** + * Walks down an OrderByItem AST node, thereby generating the appropriate SQL. + * + * @param OrderByItem + * @return string The SQL. + */ + public function walkOrderByItem($orderByItem) + { + //TODO: support general SingleValuedPathExpression, not just state field + $pathExpr = $orderByItem->getStateFieldPathExpression(); + $parts = $pathExpr->getParts(); + $qComp = $this->_parserResult->getQueryComponent($parts[0]); + $columnName = $qComp['metadata']->getColumnName($parts[1]); + $sql = $this->_dqlToSqlAliasMap[$parts[0]] . '.' . $columnName; + $sql .= $orderByItem->isAsc() ? ' ASC' : ' DESC'; + return $sql; + } + + /** + * Walks down a HavingClause AST node, thereby generating the appropriate SQL. + * + * @param HavingClause + * @return string The SQL. + */ + public function walkHavingClause($havingClause) + { + // HavingClause ::= "HAVING" ConditionalExpression + return ' HAVING ' . implode(' OR ', array_map(array($this, 'walkConditionalTerm'), + $havingClause->getConditionalExpression()->getConditionalTerms())); + } + /** * Walks down a JoinVariableDeclaration AST node and creates the corresponding SQL. * @@ -155,8 +227,8 @@ class SqlWalker /** * Walks down a SelectExpression AST node and generates the corresponding SQL. * - * @param $selectExpression - * @return string + * @param SelectExpression $selectExpression + * @return string The SQL. */ public function walkSelectExpression($selectExpression) { @@ -197,6 +269,14 @@ class SqlWalker } else if ($selectExpression->getExpression() instanceof AST\Subselect) { $sql .= $this->walkSubselect($selectExpression->getExpression()); + } else if ($selectExpression->getExpression() instanceof AST\Functions\FunctionNode) { + $funcExpr = $selectExpression->getExpression(); + if ( ! $selectExpression->getFieldIdentificationVariable()) { + $alias = $this->_scalarAliasCounter++; + } else { + $alias = $selectExpression->getFieldIdentificationVariable(); + } + $sql .= $this->walkFunction($selectExpression->getExpression()) . ' AS dctrn__' . $alias; } else { $dqlAlias = $selectExpression->getExpression(); $queryComp = $this->_parserResult->getQueryComponent($dqlAlias); @@ -217,17 +297,45 @@ class SqlWalker return $sql; } + /** + * Walks down a QuantifiedExpression AST node, thereby generating the appropriate SQL. + * + * @param QuantifiedExpression + * @return string The SQL. + */ + public function walkQuantifiedExpression($qExpr) + { + $sql = ''; + if ($qExpr->isAll()) $sql .= ' ALL'; + else if ($qExpr->isAny()) $sql .= ' ANY'; + else if ($qExpr->isSome()) $sql .= ' SOME'; + return $sql .= '(' . $this->walkSubselect($qExpr->getSubselect()) . ')'; + } + + /** + * Walks down a Subselect AST node, thereby generating the appropriate SQL. + * + * @param Subselect + * @return string The SQL. + */ public function walkSubselect($subselect) { $sql = $this->walkSimpleSelectClause($subselect->getSimpleSelectClause()); $sql .= $this->walkSubselectFromClause($subselect->getSubselectFromClause()); $sql .= $subselect->getWhereClause() ? $this->walkWhereClause($subselect->getWhereClause()) : ''; $sql .= $subselect->getGroupByClause() ? $this->walkGroupByClause($subselect->getGroupByClause()) : ''; + $sql .= $subselect->getHavingClause() ? $this->walkHavingClause($subselect->getHavingClause()) : ''; + $sql .= $subselect->getOrderByClause() ? $this->walkOrderByClause($subselect->getOrderByClause()) : ''; - //... more clauses return $sql; } + /** + * Walks down a SubselectFromClause AST node, thereby generating the appropriate SQL. + * + * @param SubselectFromClause + * @return string The SQL. + */ public function walkSubselectFromClause($subselectFromClause) { $sql = ' FROM '; @@ -244,6 +352,12 @@ class SqlWalker return $sql; } + /** + * Walks down a SimpleSelectClause AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectClause + * @return string The SQL. + */ public function walkSimpleSelectClause($simpleSelectClause) { $sql = 'SELECT'; @@ -254,11 +368,18 @@ class SqlWalker return $sql; } + /** + * Walks down a SimpleSelectExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleSelectExpression + * @return string The SQL. + */ public function walkSimpleSelectExpression($simpleSelectExpression) { $sql = ''; $expr = $simpleSelectExpression->getExpression(); if ($expr instanceof AST\StateFieldPathExpression) { + $sql .= ' ' . $this->walkPathExpression($expr); //... } else if ($expr instanceof AST\AggregateExpression) { if ( ! $simpleSelectExpression->getFieldIdentificationVariable()) { @@ -274,6 +395,12 @@ class SqlWalker return $sql; } + /** + * Walks down an AggregateExpression AST node, thereby generating the appropriate SQL. + * + * @param AggregateExpression + * @return string The SQL. + */ public function walkAggregateExpression($aggExpression) { $sql = ''; @@ -291,13 +418,25 @@ class SqlWalker return $sql; } + /** + * Walks down a GroupByClause AST node, thereby generating the appropriate SQL. + * + * @param GroupByClause + * @return string The SQL. + */ public function walkGroupByClause($groupByClause) { return ' GROUP BY ' - . implode(', ', array_map(array(&$this, 'walkGroupByItem'), + . implode(', ', array_map(array($this, 'walkGroupByItem'), $groupByClause->getGroupByItems())); } + /** + * Walks down a GroupByItem AST node, thereby generating the appropriate SQL. + * + * @param GroupByItem + * @return string The SQL. + */ public function walkGroupByItem($pathExpr) { //TODO: support general SingleValuedPathExpression, not just state field @@ -307,6 +446,12 @@ class SqlWalker return $this->_dqlToSqlAliasMap[$parts[0]] . '.' . $columnName; } + /** + * Walks down an UpdateStatement AST node, thereby generating the appropriate SQL. + * + * @param UpdateStatement + * @return string The SQL. + */ public function walkUpdateStatement(AST\UpdateStatement $AST) { $sql = $this->walkUpdateClause($AST->getUpdateClause()); @@ -314,6 +459,12 @@ class SqlWalker return $sql; } + /** + * Walks down a DeleteStatement AST node, thereby generating the appropriate SQL. + * + * @param DeleteStatement + * @return string The SQL. + */ public function walkDeleteStatement(AST\DeleteStatement $AST) { $sql = $this->walkDeleteClause($AST->getDeleteClause()); @@ -321,6 +472,12 @@ class SqlWalker return $sql; } + /** + * Walks down a DeleteClause AST node, thereby generating the appropriate SQL. + * + * @param DeleteClause + * @return string The SQL. + */ public function walkDeleteClause(AST\DeleteClause $deleteClause) { $sql = 'DELETE FROM '; @@ -332,6 +489,12 @@ class SqlWalker return $sql; } + /** + * Walks down an UpdateClause AST node, thereby generating the appropriate SQL. + * + * @param UpdateClause + * @return string The SQL. + */ public function walkUpdateClause($updateClause) { $sql = 'UPDATE '; @@ -346,6 +509,12 @@ class SqlWalker return $sql; } + /** + * Walks down an UpdateItem AST node, thereby generating the appropriate SQL. + * + * @param UpdateItem + * @return string The SQL. + */ public function walkUpdateItem($updateItem) { $sql = ''; @@ -373,6 +542,12 @@ class SqlWalker return $sql; } + /** + * Walks down a WhereClause AST node, thereby generating the appropriate SQL. + * + * @param WhereClause + * @return string The SQL. + */ public function walkWhereClause($whereClause) { $sql = ' WHERE '; @@ -382,12 +557,24 @@ class SqlWalker return $sql; } + /** + * Walks down a ConditionalTerm AST node, thereby generating the appropriate SQL. + * + * @param ConditionalTerm + * @return string The SQL. + */ public function walkConditionalTerm($condTerm) { return implode(' AND ', array_map(array($this, 'walkConditionalFactor'), $condTerm->getConditionalFactors())); } + /** + * Walks down a ConditionalFactor AST node, thereby generating the appropriate SQL. + * + * @param ConditionalFactor + * @return string The SQL. + */ public function walkConditionalFactor($factor) { $sql = ''; @@ -402,6 +589,12 @@ class SqlWalker return $sql; } + /** + * Walks down an ExistsExpression AST node, thereby generating the appropriate SQL. + * + * @param ExistsExpression + * @return string The SQL. + */ public function walkExistsExpression($existsExpr) { $sql = ''; @@ -410,6 +603,12 @@ class SqlWalker return $sql; } + /** + * Walks down a NullComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param NullComparisonExpression + * @return string The SQL. + */ public function walkNullComparisonExpression($nullCompExpr) { $sql = ''; @@ -423,6 +622,12 @@ class SqlWalker return $sql; } + /** + * Walks down an InExpression AST node, thereby generating the appropriate SQL. + * + * @param InExpression + * @return string The SQL. + */ public function walkInExpression($inExpr) { $sql = $this->walkPathExpression($inExpr->getPathExpression()); @@ -437,6 +642,12 @@ class SqlWalker return $sql; } + /** + * Walks down a literal that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ public function walkLiteral($literal) { if ($literal instanceof AST\InputParameter) { @@ -446,6 +657,12 @@ class SqlWalker } } + /** + * Walks down a BetweenExpression AST node, thereby generating the appropriate SQL. + * + * @param BetweenExpression + * @return string The SQL. + */ public function walkBetweenExpression($betweenExpr) { $sql = $this->walkArithmeticExpression($betweenExpr->getBaseExpression()); @@ -455,6 +672,12 @@ class SqlWalker return $sql; } + /** + * Walks down a LikeExpression AST node, thereby generating the appropriate SQL. + * + * @param LikeExpression + * @return string The SQL. + */ public function walkLikeExpression($likeExpr) { $sql = ''; @@ -477,11 +700,23 @@ class SqlWalker return $sql; } + /** + * Walks down a StateFieldPathExpression AST node, thereby generating the appropriate SQL. + * + * @param StateFieldPathExpression + * @return string The SQL. + */ public function walkStateFieldPathExpression($stateFieldPathExpression) { return $this->walkPathExpression($stateFieldPathExpression); } + /** + * Walks down a ComparisonExpression AST node, thereby generating the appropriate SQL. + * + * @param ComparisonExpression + * @return string The SQL. + */ public function walkComparisonExpression($compExpr) { $sql = ''; @@ -505,11 +740,23 @@ class SqlWalker return $sql; } + /** + * Walks down an InputParameter AST node, thereby generating the appropriate SQL. + * + * @param InputParameter + * @return string The SQL. + */ public function walkInputParameter($inputParam) { return $inputParam->isNamed() ? ':' . $inputParam->getName() : '?'; } + /** + * Walks down an ArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param ArithmeticExpression + * @return string The SQL. + */ public function walkArithmeticExpression($arithmeticExpr) { $sql = ''; @@ -523,6 +770,12 @@ class SqlWalker return $sql; } + /** + * Walks down an ArithmeticTerm AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ public function walkArithmeticTerm($term) { if (is_string($term)) return $term; @@ -531,6 +784,12 @@ class SqlWalker $term->getArithmeticFactors())); } + /** + * Walks down a StringPrimary that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ public function walkStringPrimary($stringPrimary) { if (is_string($stringPrimary)) { @@ -540,6 +799,12 @@ class SqlWalker } } + /** + * Walks down an ArithmeticFactor that represents an AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ public function walkArithmeticFactor($factor) { if (is_string($factor)) return $factor; @@ -560,12 +825,24 @@ class SqlWalker return $sql; } + /** + * Walks down an SimpleArithmeticExpression AST node, thereby generating the appropriate SQL. + * + * @param SimpleArithmeticExpression + * @return string The SQL. + */ public function walkSimpleArithmeticExpression($simpleArithmeticExpr) { return implode(' ', array_map(array($this, 'walkArithmeticTerm'), $simpleArithmeticExpr->getArithmeticTerms())); } + /** + * Walks down an PathExpression AST node, thereby generating the appropriate SQL. + * + * @param mixed + * @return string The SQL. + */ public function walkPathExpression($pathExpr) { $sql = ''; diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 7fabd5176..12390ca82 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -89,7 +89,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase public function testFunctionalExpressionsSupportedInWherePart() { - //$this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'"); + $this->assertValidDql("SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'"); } public function testArithmeticExpressionsSupportedInWherePart() @@ -300,29 +300,23 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase $this->assertValidDql("SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.name = a.topic"); } -/* + public function testAllExpressionWithCorrelatedSubquery() + { + $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ALL (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); + } + public function testCustomJoinsAndWithKeywordSupported() { - // We need existant classes here, otherwise semantical will always fail - $this->assertValidDql('SELECT c.*, c2.*, d.* FROM Record_Country c INNER JOIN c.City c2 WITH c2.id = 2 WHERE c.id = 1'); - } -*/ -/* public function testAllExpressionWithCorrelatedSubquery() - { - // We need existant classes here, otherwise semantical will always fail - $this->assertValidDql('SELECT * FROM CompanyEmployee e WHERE e.salary > ALL (SELECT m.salary FROM CompanyManager m WHERE m.department = e.department)', true); + $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.phonenumbers p WITH p.phonenumber = 123 WHERE u.id = 1'); } public function testAnyExpressionWithCorrelatedSubquery() { - // We need existant classes here, otherwise semantical will always fail - $this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > ANY (SELECT m.salary FROM Manager m WHERE m.department = e.department)'); + $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > ANY (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } public function testSomeExpressionWithCorrelatedSubquery() { - // We need existant classes here, otherwise semantical will always fail - $this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > SOME (SELECT m.salary FROM Manager m WHERE m.department = e.department)'); + $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > SOME (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } -*/ } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index f5f9f6757..3bf9aaa6c 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -26,7 +26,6 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase } } - public function testPlainFromClauseWithoutAlias() { $this->assertSqlGeneration( @@ -165,54 +164,75 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } - /*public function testFunctionalExpressionsSupportedInWherePart() - { - $this->assertSqlGeneration( - "SELECT u.name FROM CmsUser u WHERE TRIM(u.name) = 'someone'", - // String quoting in the SQL usually depends on the database platform. - // This test works with a mock connection which uses ' for string quoting. - "SELECT cu.name AS cu__name FROM CmsUser cu WHERE TRIM(cu.name) = 'someone'" - ); - }*/ - - -/* - // Ticket #973 - public function testSingleInValueWithoutSpace() - { - $this->assertSqlGeneration( - "SELECT u.name FROM CmsUser u WHERE u.id IN(46)", - "SELECT cu.name AS cu__name FROM cms_user cu WHERE cu.id IN (46)" - ); - } - - // Ticket 894 public function testBetweenDeclarationWithInputParameter() { $this->assertSqlGeneration( - "SELECT u.name FROM CmsUser u WHERE u.id BETWEEN ? AND ?", - "SELECT cu.name AS cu__name FROM cms_user cu WHERE cu.id BETWEEN ? AND ?" + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id BETWEEN ?1 AND ?2", + "SELECT c0.name AS c0__name FROM cms_users c0 WHERE c0.id BETWEEN ? AND ?" ); } + public function testFunctionalExpressionsSupportedInWherePart() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE TRIM(u.name) = 'someone'", + // String quoting in the SQL usually depends on the database platform. + // This test works with a mock connection which uses ' for string quoting. + "SELECT c0.name AS c0__name FROM cms_users c0 WHERE TRIM(FROM c0.name) = 'someone'" + ); + } + + // Ticket #973 + public function testSingleInValueWithoutSpace() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN(46)", + "SELECT c0.name AS c0__name FROM cms_users c0 WHERE c0.id IN (46)" + ); + } public function testInExpressionSupportedInWherePart() { $this->assertSqlGeneration( - 'SELECT * FROM CmsUser WHERE CmsUser.id IN (1, 2)', - 'SELECT cu.id AS cu__id, cu.status AS cu__status, cu.username AS cu__username, cu.name AS cu__name FROM cms_user cu WHERE cu.id IN (1, 2)' + 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id IN (1, 2)', + 'SELECT c0.id AS c0__id, c0.status AS c0__status, c0.username AS c0__username, c0.name AS c0__name FROM cms_users c0 WHERE c0.id IN (1, 2)' ); } - public function testNotInExpressionSupportedInWherePart() { $this->assertSqlGeneration( - 'SELECT * FROM CmsUser WHERE CmsUser.id NOT IN (1)', - 'SELECT cu.id AS cu__id, cu.status AS cu__status, cu.username AS cu__username, cu.name AS cu__name FROM cms_user cu WHERE cu.id NOT IN (1)' + 'SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)', + 'SELECT c0.id AS c0__id, c0.status AS c0__status, c0.username AS c0__username, c0.name AS c0__name FROM cms_users c0 WHERE c0.id NOT IN (1)' ); } -*/ + public function testConcatFunction() + { + $connMock = $this->_em->getConnection(); + $orgPlatform = $connMock->getDatabasePlatform(); + + $connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\MySqlPlatform); + $this->assertSqlGeneration( + "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, 's') = ?1", + "SELECT c0.id AS c0__id FROM cms_users c0 WHERE CONCAT(c0.name, 's') = ?" + ); + $this->assertSqlGeneration( + "SELECT CONCAT(u.id, u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1", + "SELECT CONCAT(c0.id, c0.name) AS dctrn__0 FROM cms_users c0 WHERE c0.id = ?" + ); + + $connMock->setDatabasePlatform(new \Doctrine\DBAL\Platforms\PostgreSqlPlatform); + $this->assertSqlGeneration( + "SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(u.name, 's') = ?1", + "SELECT c0.id AS c0__id FROM cms_users c0 WHERE c0.name || 's' = ?" + ); + $this->assertSqlGeneration( + "SELECT CONCAT(u.id, u.name) FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1", + "SELECT c0.id || c0.name AS dctrn__0 FROM cms_users c0 WHERE c0.id = ?" + ); + + $connMock->setDatabasePlatform($orgPlatform); + } } \ No newline at end of file