From 87979219376ecba332d08dd820416035e9493379 Mon Sep 17 00:00:00 2001 From: romanb Date: Tue, 11 Aug 2009 10:51:38 +0000 Subject: [PATCH] [2.0] Removed AssociationMappings from ResultSetMapping for improved caching. Fixed caching issue with reflection classes and properties. Reimplemented and leaned up broken identifier quoting. Added support for named (native) queries. Fixed small hydration memory leak. --- lib/Doctrine/Common/Lexer.php | 2 +- lib/Doctrine/DBAL/Configuration.php | 3 +- lib/Doctrine/DBAL/Connection.php | 22 +- .../DBAL/Platforms/AbstractPlatform.php | 84 ++--- lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php | 30 +- lib/Doctrine/DBAL/Platforms/MySqlPlatform.php | 34 +- .../DBAL/Platforms/OraclePlatform.php | 31 +- .../DBAL/Platforms/PostgreSqlPlatform.php | 41 +- .../DBAL/Platforms/SqlitePlatform.php | 1 - .../DBAL/Schema/MsSqlSchemaManager.php | 14 +- .../DBAL/Schema/MySqlSchemaManager.php | 1 - lib/Doctrine/ORM/AbstractQuery.php | 2 +- lib/Doctrine/ORM/Configuration.php | 50 ++- lib/Doctrine/ORM/EntityManager.php | 12 +- .../ORM/Internal/Hydration/ArrayHydrator.php | 15 +- .../ORM/Internal/Hydration/ObjectHydrator.php | 42 ++- .../ORM/Mapping/AssociationMapping.php | 27 +- lib/Doctrine/ORM/Mapping/ClassMetadata.php | 213 +++++++---- .../ORM/Mapping/ClassMetadataFactory.php | 16 +- .../ORM/Mapping/ManyToManyMapping.php | 61 +-- lib/Doctrine/ORM/Mapping/OneToManyMapping.php | 2 +- lib/Doctrine/ORM/Mapping/OneToOneMapping.php | 39 +- lib/Doctrine/ORM/PersistentCollection.php | 4 +- .../Persisters/JoinedSubclassPersister.php | 29 +- .../ORM/Persisters/ManyToManyPersister.php | 4 +- .../Persisters/StandardEntityPersister.php | 212 ++++++----- .../ORM/Query/AST/AggregateExpression.php | 4 +- .../ORM/Query/AST/ArithmeticExpression.php | 4 +- lib/Doctrine/ORM/Query/AST/InputParameter.php | 4 +- .../Query/Exec/MultiTableDeleteExecutor.php | 5 +- .../Query/Exec/MultiTableUpdateExecutor.php | 5 +- lib/Doctrine/ORM/Query/Parser.php | 22 +- lib/Doctrine/ORM/Query/ResultSetMapping.php | 24 -- lib/Doctrine/ORM/Query/SqlWalker.php | 351 +++++++++--------- lib/Doctrine/ORM/Tools/SchemaTool.php | 53 +-- lib/Doctrine/ORM/UnitOfWork.php | 19 + .../DBAL/Platforms/MsSqlPlatformTest.php | 2 +- .../StandardEntityPersisterTest.php | 26 -- .../Tests/ORM/Hydration/ArrayHydratorTest.php | 21 +- .../ORM/Hydration/ObjectHydratorTest.php | 16 +- .../Performance/HydrationPerformanceTest.php | 4 +- .../ORM/Performance/InsertPerformanceTest.php | 11 +- 42 files changed, 833 insertions(+), 729 deletions(-) diff --git a/lib/Doctrine/Common/Lexer.php b/lib/Doctrine/Common/Lexer.php index 8451e4a36..f45bf2a2c 100644 --- a/lib/Doctrine/Common/Lexer.php +++ b/lib/Doctrine/Common/Lexer.php @@ -22,7 +22,7 @@ namespace Doctrine\Common; /** - * Simple generic lexical scanner + * Simple generic lexical scanner. * * @license http://www.opensource.org/licenses/lgpl-license.php LGPL * @link www.doctrine-project.org diff --git a/lib/Doctrine/DBAL/Configuration.php b/lib/Doctrine/DBAL/Configuration.php index 5157952eb..b6f914cd5 100644 --- a/lib/Doctrine/DBAL/Configuration.php +++ b/lib/Doctrine/DBAL/Configuration.php @@ -48,8 +48,7 @@ class Configuration protected $_attributes = array(); /** - * Creates a new configuration that can be used for Doctrine. - * + * Creates a new DBAL configuration instance. */ public function __construct() { diff --git a/lib/Doctrine/DBAL/Connection.php b/lib/Doctrine/DBAL/Connection.php index ec8185337..9be0165dc 100644 --- a/lib/Doctrine/DBAL/Connection.php +++ b/lib/Doctrine/DBAL/Connection.php @@ -21,8 +21,8 @@ namespace Doctrine\DBAL; -use Doctrine\Common\EventManager; -use Doctrine\Common\DoctrineException; +use Doctrine\Common\EventManager, + Doctrine\Common\DoctrineException; /** * A wrapper around a Doctrine\DBAL\Driver\Connection that adds features like @@ -376,11 +376,10 @@ class Connection $criteria = array(); foreach (array_keys($identifier) as $id) { - $criteria[] = $this->quoteIdentifier($id) . ' = ?'; + $criteria[] = $id . ' = ?'; } - $query = 'DELETE FROM ' . $this->quoteIdentifier($tableName) - . ' WHERE ' . implode(' AND ', $criteria); + $query = 'DELETE FROM ' . $tableName . ' WHERE ' . implode(' AND ', $criteria); return $this->executeUpdate($query, array_values($identifier)); } @@ -439,15 +438,14 @@ class Connection $set = array(); foreach ($data as $columnName => $value) { - $set[] = $this->quoteIdentifier($columnName) . ' = ?'; + $set[] = $columnName . ' = ?'; } $params = array_merge(array_values($data), array_values($identifier)); - $sql = 'UPDATE ' . $this->quoteIdentifier($tableName) - . ' SET ' . implode(', ', $set) - . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) - . ' = ?'; + $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) + . ' WHERE ' . implode(' = ? AND ', array_keys($identifier)) + . ' = ?'; return $this->executeUpdate($sql, $params); } @@ -473,11 +471,11 @@ class Connection $a = array(); foreach ($data as $columnName => $value) { - $cols[] = $this->quoteIdentifier($columnName); + $cols[] = $columnName; $a[] = '?'; } - $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName) + $query = 'INSERT INTO ' . $tableName . ' (' . implode(', ', $cols) . ')' . ' VALUES (' . implode(', ', $a) . ')'; diff --git a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php index 2c74be603..a04308ac5 100644 --- a/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php @@ -21,9 +21,9 @@ namespace Doctrine\DBAL\Platforms; -use Doctrine\Common\DoctrineException; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Types; +use Doctrine\Common\DoctrineException, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types; /** * Base class for all DatabasePlatforms. The DatabasePlatforms are the central @@ -114,8 +114,6 @@ abstract class AbstractPlatform */ public function getAvgExpression($column) { - $column = $this->quoteIdentifier($column); - return 'AVG(' . $column . ')'; } @@ -130,8 +128,6 @@ abstract class AbstractPlatform */ public function getCountExpression($column) { - $column = $this->quoteIdentifier($column); - return 'COUNT(' . $column . ')'; } @@ -143,8 +139,6 @@ abstract class AbstractPlatform */ public function getMaxExpression($column) { - $column = $this->quoteIdentifier($column); - return 'MAX(' . $column . ')'; } @@ -156,8 +150,6 @@ abstract class AbstractPlatform */ public function getMinExpression($column) { - $column = $this->quoteIdentifier($column); - return 'MIN(' . $column . ')'; } @@ -169,8 +161,6 @@ abstract class AbstractPlatform */ public function getSumExpression($column) { - $column = $this->quoteIdentifier($column); - return 'SUM(' . $column . ')'; } @@ -185,8 +175,6 @@ abstract class AbstractPlatform */ public function getMd5Expression($column) { - $column = $this->quoteIdentifier($column); - return 'MD5(' . $column . ')'; } @@ -199,8 +187,6 @@ abstract class AbstractPlatform */ public function getLengthExpression($column) { - $column = $this->quoteIdentifier($column); - return 'LENGTH(' . $column . ')'; } @@ -213,8 +199,6 @@ abstract class AbstractPlatform */ public function getRoundExpression($column, $decimals = 0) { - $column = $this->quoteIdentifier($column); - return 'ROUND(' . $column . ', ' . $decimals . ')'; } @@ -228,9 +212,6 @@ abstract class AbstractPlatform */ public function getModExpression($expression1, $expression2) { - $expression1 = $this->quoteIdentifier($expression1); - $expression2 = $this->quoteIdentifier($expression2); - return 'MOD(' . $expression1 . ', ' . $expression2 . ')'; } @@ -333,11 +314,9 @@ abstract class AbstractPlatform */ public function getSubstringExpression($value, $from, $len = null) { - $value = $this->quoteIdentifier($value); if ($len === null) return 'SUBSTRING(' . $value . ' FROM ' . $from . ')'; else { - $len = $this->quoteIdentifier($len); return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $len . ')'; } } @@ -371,7 +350,7 @@ abstract class AbstractPlatform */ public function getNotExpression($expression) { - return 'NOT(' . $this->quoteIdentifier($expression) . ')'; + return 'NOT(' . $expression . ')'; } /** @@ -401,7 +380,6 @@ abstract class AbstractPlatform $values = array($values); } $values = $this->getIdentifiers($values); - $column = $this->quoteIdentifier($column); if (count($values) == 0) { throw DoctrineException::updateMe('Values array for IN operator should not be empty.'); @@ -425,8 +403,6 @@ abstract class AbstractPlatform */ public function getIsNullExpression($expression) { - $expression = $this->quoteIdentifier($expression); - return $expression . ' IS NULL'; } @@ -446,8 +422,6 @@ abstract class AbstractPlatform */ public function getIsNotNullExpression($expression) { - $expression = $this->quoteIdentifier($expression); - return $expression . ' IS NOT NULL'; } @@ -476,10 +450,6 @@ abstract class AbstractPlatform */ public function getBetweenExpression($expression, $value1, $value2) { - $expression = $this->quoteIdentifier($expression); - $value1 = $this->quoteIdentifier($value1); - $value2 = $this->quoteIdentifier($value2); - return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2; } @@ -525,17 +495,11 @@ abstract class AbstractPlatform public function getDropConstraintSql($table, $name, $primary = false) { - $table = $this->quoteIdentifier($table); - $name = $this->quoteIdentifier($name); - return 'ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name; } public function getDropForeignKeySql($table, $name) { - $table = $this->quoteIdentifier($table); - $name = $this->quoteIdentifier($name); - return 'ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $name; } @@ -562,7 +526,7 @@ abstract class AbstractPlatform } } - $query = 'CREATE TABLE ' . $this->quoteIdentifier($table, true) . ' (' . $columnListSql; + $query = 'CREATE TABLE ' . $table . ' (' . $columnListSql; $check = $this->getCheckDeclarationSql($columns); if ( ! empty($check)) { @@ -682,12 +646,9 @@ abstract class AbstractPlatform */ public function quoteIdentifier($str) { - if ($str[0] != '`') { - return $str; - } $c = $this->getIdentifierQuoteCharacter(); - return $c . trim($str, '`') . $c; + return $c . $str . $c; } /** @@ -699,7 +660,6 @@ abstract class AbstractPlatform */ public function getCreateForeignKeySql($table, array $definition) { - $table = $this->quoteIdentifier($table); $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSql($definition); return $query; @@ -811,7 +771,7 @@ abstract class AbstractPlatform $typeDecl = $field['type']->getSqlDeclaration($field, $this); - return $this->quoteIdentifier($name) . ' ' . $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; + return $name . ' ' . $typeDecl . $charset . $default . $notnull . $unique . $check . $collation; } /** @@ -855,7 +815,7 @@ abstract class AbstractPlatform $default = empty($field['notnull']) ? ' DEFAULT NULL' : ''; if (isset($field['default'])) { - $default = ' DEFAULT ' . $this->quoteIdentifier($field['default'], $field['type']); + $default = ' DEFAULT ' . $field['default']; } return $default; } @@ -897,7 +857,6 @@ abstract class AbstractPlatform */ public function getIndexDeclarationSql($name, array $definition) { - $name = $this->quoteIdentifier($name); $type = ''; if (isset($definition['type'])) { @@ -931,9 +890,9 @@ abstract class AbstractPlatform $ret = array(); foreach ($fields as $field => $definition) { if (is_array($definition)) { - $ret[] = $this->quoteIdentifier($field); + $ret[] = $field; } else { - $ret[] = $this->quoteIdentifier($definition); + $ret[] = $definition; } } return implode(', ', $ret); @@ -1072,7 +1031,7 @@ abstract class AbstractPlatform { $sql = ''; if (isset($definition['name'])) { - $sql .= ' CONSTRAINT ' . $this->quoteIdentifier($definition['name']) . ' '; + $sql .= ' CONSTRAINT ' . $definition['name'] . ' '; } $sql .= 'FOREIGN KEY ('; @@ -1093,10 +1052,10 @@ abstract class AbstractPlatform $definition['foreign'] = array($definition['foreign']); } - $sql .= implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['local'])) + $sql .= implode(', ', $definition['local']) . ') REFERENCES ' - . $this->quoteIdentifier($definition['foreignTable']) . '(' - . implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['foreign'])) . ')'; + . $definition['foreignTable'] . '(' + . implode(', ', $definition['foreign']) . ')'; return $sql; } @@ -1491,6 +1450,16 @@ abstract class AbstractPlatform { return true; } + + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return false; + } /** * Whether the platform supports getting the affected rows of a recent @@ -1574,4 +1543,9 @@ abstract class AbstractPlatform * @return string */ abstract public function getName(); + + public function getSqlResultCasing($column) + { + return $column; + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php index 02e84b0e0..da6e73c2b 100644 --- a/lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MsSqlPlatform.php @@ -105,7 +105,7 @@ class MsSqlPlatform extends AbstractPlatform $query = ''; if ( ! empty($changes['name'])) { - $change_name = $this->quoteIdentifier($changes['name']); + $change_name = $changes['name']; $query .= 'RENAME TO ' . $change_name; } @@ -123,7 +123,6 @@ class MsSqlPlatform extends AbstractPlatform if ($query) { $query .= ', '; } - $field_name = $this->quoteIdentifier($fieldName, true); $query .= 'DROP COLUMN ' . $fieldName; } } @@ -146,7 +145,6 @@ class MsSqlPlatform extends AbstractPlatform } else { $oldFieldName = $fieldName; } - $oldFieldName = $this->quoteIdentifier($oldFieldName, true); $query .= 'CHANGE ' . $oldFieldName . ' ' . $this->getColumnDeclarationSql($fieldName, $field['definition']); } @@ -158,7 +156,6 @@ class MsSqlPlatform extends AbstractPlatform $query.= ', '; } $field = $changes['rename'][$renamedField]; - $renamedField = $this->quoteIdentifier($renamedField, true); $query .= 'CHANGE ' . $renamedField . ' ' . $this->getColumnDeclarationSql($field['name'], $field['definition']); } @@ -168,21 +165,8 @@ class MsSqlPlatform extends AbstractPlatform return false; } - $name = $this->quoteIdentifier($name, true); return 'ALTER TABLE ' . $name . ' ' . $query; } - - - /** - * Gets the character used for identifier quoting. - * - * @return string - * @override - */ - public function getIdentifierQuoteCharacter() - { - return '`'; - } /** * Returns the regular expression operator. @@ -321,7 +305,7 @@ class MsSqlPlatform extends AbstractPlatform */ public function getCreateDatabaseSql($name) { - return 'CREATE DATABASE ' . $this->quoteIdentifier($name); + return 'CREATE DATABASE ' . $name; } /** @@ -333,7 +317,7 @@ class MsSqlPlatform extends AbstractPlatform */ public function getDropDatabaseSql($name) { - return 'DROP DATABASE ' . $this->quoteIdentifier($name); + return 'DROP DATABASE ' . $name; } public function getSetTransactionIsolationSql($level) @@ -481,7 +465,7 @@ class MsSqlPlatform extends AbstractPlatform } $query = preg_replace('/^'.$selectRegExp.'/i', $selectReplace . 'TOP ' . ($count + $offset) . ' ', $query); - $query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . $this->quoteIdentifier('inner_tbl'); + $query = 'SELECT * FROM (SELECT TOP ' . $count . ' * FROM (' . $query . ') AS ' . 'inner_tbl'; if ($orderby !== false) { $query .= ' ORDER BY '; @@ -491,12 +475,12 @@ class MsSqlPlatform extends AbstractPlatform $query .= ', '; } - $query .= $this->quoteIdentifier('inner_tbl') . '.' . $aliases[$i] . ' '; + $query .= 'inner_tbl' . '.' . $aliases[$i] . ' '; $query .= (stripos($sorts[$i], 'ASC') !== false) ? 'DESC' : 'ASC'; } } - $query .= ') AS ' . $this->quoteIdentifier('outer_tbl'); + $query .= ') AS ' . 'outer_tbl'; if ($orderby !== false) { $query .= ' ORDER BY '; @@ -506,7 +490,7 @@ class MsSqlPlatform extends AbstractPlatform $query .= ', '; } - $query .= $this->quoteIdentifier('outer_tbl') . '.' . $aliases[$i] . ' ' . $sorts[$i]; + $query .= 'outer_tbl' . '.' . $aliases[$i] . ' ' . $sorts[$i]; } } } diff --git a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php index 81e0e4ce8..f8fda0e49 100644 --- a/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/MySqlPlatform.php @@ -156,19 +156,19 @@ class MySqlPlatform extends AbstractPlatform { $query = 'SHOW TABLES'; if ( ! is_null($database)) { - $query .= ' FROM ' . $this->quoteIdentifier($database); + $query .= ' FROM ' . $database; } return $query; } public function getListTableConstraintsSql($table) { - return 'SHOW INDEX FROM ' . $this->quoteIdentifier($table); + return 'SHOW INDEX FROM ' . $table; } public function getListTableIndexesSql($table) { - return 'SHOW INDEX FROM ' . $this->quoteIdentifier($table); + return 'SHOW INDEX FROM ' . $table; } public function getListUsersSql() @@ -335,7 +335,7 @@ class MySqlPlatform extends AbstractPlatform public function getListTableColumnsSql($table) { - return 'DESCRIBE ' . $this->quoteIdentifier($table); + return 'DESCRIBE ' . $table; } /** @@ -347,7 +347,7 @@ class MySqlPlatform extends AbstractPlatform */ public function getCreateDatabaseSql($name) { - return 'CREATE DATABASE ' . $this->quoteIdentifier($name); + return 'CREATE DATABASE ' . $name; } /** @@ -359,7 +359,7 @@ class MySqlPlatform extends AbstractPlatform */ public function getDropDatabaseSql($name) { - return 'DROP DATABASE ' . $this->quoteIdentifier($name); + return 'DROP DATABASE ' . $name; } /** @@ -445,7 +445,6 @@ class MySqlPlatform extends AbstractPlatform // attach all primary keys if (isset($options['primary']) && ! empty($options['primary'])) { $keyColumns = array_unique(array_values($options['primary'])); - $keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns); $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } @@ -453,7 +452,7 @@ class MySqlPlatform extends AbstractPlatform if (!empty($options['temporary'])) { $query .= 'TEMPORARY '; } - $query.= 'TABLE ' . $this->quoteIdentifier($name, true) . ' (' . $queryFields . ')'; + $query.= 'TABLE ' . $name . ' (' . $queryFields . ')'; $optionStrings = array(); @@ -609,8 +608,7 @@ class MySqlPlatform extends AbstractPlatform $query = ''; if ( ! empty($changes['name'])) { - $change_name = $this->quoteIdentifier($changes['name']); - $query .= 'RENAME TO ' . $change_name; + $query .= 'RENAME TO ' . $changes['name']; } if ( ! empty($changes['add']) && is_array($changes['add'])) { @@ -627,7 +625,6 @@ class MySqlPlatform extends AbstractPlatform if ($query) { $query .= ', '; } - $fieldName = $this->quoteIdentifier($fieldName); $query .= 'DROP ' . $fieldName; } } @@ -650,7 +647,6 @@ class MySqlPlatform extends AbstractPlatform } else { $oldFieldName = $fieldName; } - $oldFieldName = $this->quoteIdentifier($oldFieldName, true); $query .= 'CHANGE ' . $oldFieldName . ' ' . $this->getColumnDeclarationSql($fieldName, $field['definition']); } @@ -662,7 +658,6 @@ class MySqlPlatform extends AbstractPlatform $query.= ', '; } $field = $changes['rename'][$renamedField]; - $renamedField = $this->quoteIdentifier($renamedField, true); $query .= 'CHANGE ' . $renamedField . ' ' . $this->getColumnDeclarationSql($field['name'], $field['definition']); } @@ -672,8 +667,6 @@ class MySqlPlatform extends AbstractPlatform return false; } - $name = $this->quoteIdentifier($name, true); - return 'ALTER TABLE ' . $name . ' ' . $query; } @@ -715,7 +708,6 @@ class MySqlPlatform extends AbstractPlatform public function getCreateIndexSql($table, $name, array $definition) { $table = $table; - $name = $this->quoteIdentifier($name); $type = ''; if (isset($definition['type'])) { switch (strtolower($definition['type'])) { @@ -818,7 +810,7 @@ class MySqlPlatform extends AbstractPlatform $definition['fields'] = array($definition['fields']); } - $query = $type . 'INDEX ' . $this->quoteIdentifier($name); + $query = $type . 'INDEX ' . $name; $query .= ' (' . $this->getIndexFieldDeclarationListSql($definition['fields']) . ')'; @@ -838,7 +830,7 @@ class MySqlPlatform extends AbstractPlatform $declFields = array(); foreach ($fields as $fieldName => $field) { - $fieldString = $this->quoteIdentifier($fieldName); + $fieldString = $fieldName; if (is_array($field)) { if (isset($field['length'])) { @@ -857,7 +849,7 @@ class MySqlPlatform extends AbstractPlatform } } } else { - $fieldString = $this->quoteIdentifier($field); + $fieldString = $field; } $declFields[] = $fieldString; } @@ -896,8 +888,6 @@ class MySqlPlatform extends AbstractPlatform */ public function getDropIndexSql($table, $name) { - $table = $this->quoteIdentifier($table); - $name = $this->quoteIdentifier($name); return 'DROP INDEX ' . $name . ' ON ' . $table; } @@ -909,7 +899,7 @@ class MySqlPlatform extends AbstractPlatform */ public function getDropTableSql($table) { - return 'DROP TABLE ' . $this->quoteIdentifier($table); + return 'DROP TABLE ' . $table; } public function getSetTransactionIsolationSql($level) diff --git a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php index fe62bd2b1..65978c3d7 100644 --- a/lib/Doctrine/DBAL/Platforms/OraclePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/OraclePlatform.php @@ -112,7 +112,7 @@ class OraclePlatform extends AbstractPlatform */ public function getCreateSequenceSql($sequenceName, $start = 1, $allocationSize = 1) { - return 'CREATE SEQUENCE ' . $this->quoteIdentifier($sequenceName) + return 'CREATE SEQUENCE ' . $sequenceName . ' START WITH ' . $start . ' INCREMENT BY ' . $allocationSize; } @@ -124,7 +124,7 @@ class OraclePlatform extends AbstractPlatform */ public function getSequenceNextValSql($sequenceName) { - return 'SELECT ' . $this->quoteIdentifier($sequenceName) . '.nextval FROM DUAL'; + return 'SELECT ' . $sequenceName . '.nextval FROM DUAL'; } /** @@ -323,9 +323,7 @@ END;'; $sequenceName = $table . '_SEQ'; $sql[] = $this->getCreateSequenceSql($sequenceName, $start); - $triggerName = $this->quoteIdentifier($table . '_AI_PK', true); - $table = $this->quoteIdentifier($table, true); - $name = $this->quoteIdentifier($name, true); + $triggerName = $table . '_AI_PK'; $sql[] = 'CREATE TRIGGER ' . $triggerName . ' BEFORE INSERT ON ' . $table . ' @@ -334,16 +332,16 @@ DECLARE last_Sequence NUMBER; last_InsertID NUMBER; BEGIN - SELECT ' . $this->quoteIdentifier($sequenceName) . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; IF (:NEW.' . $name . ' IS NULL OR :NEW.'.$name.' = 0) THEN - SELECT ' . $this->quoteIdentifier($sequenceName) . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; + SELECT ' . $sequenceName . '.NEXTVAL INTO :NEW.' . $name . ' FROM DUAL; ELSE SELECT NVL(Last_Number, 0) INTO last_Sequence FROM User_Sequences WHERE Sequence_Name = \'' . $sequenceName . '\'; SELECT :NEW.' . $name . ' INTO last_InsertID FROM DUAL; WHILE (last_InsertID > last_Sequence) LOOP - SELECT ' . $this->quoteIdentifier($sequenceName) . '.NEXTVAL INTO last_Sequence FROM DUAL; + SELECT ' . $sequenceName . '.NEXTVAL INTO last_Sequence FROM DUAL; END LOOP; END IF; END;'; @@ -380,7 +378,7 @@ END;'; public function getDropSequenceSql($sequenceName) { - return 'DROP SEQUENCE ' . $this->quoteIdentifier($sequenceName); + return 'DROP SEQUENCE ' . $sequenceName; } public function getDropDatabaseSql($database) @@ -410,8 +408,6 @@ END;'; return false; } - $name = $this->quoteIdentifier($name); - if ( ! empty($changes['add']) && is_array($changes['add'])) { $fields = array(); foreach ($changes['add'] as $fieldName => $field) { @@ -430,21 +426,21 @@ END;'; if ( ! empty($changes['rename']) && is_array($changes['rename'])) { foreach ($changes['rename'] as $fieldName => $field) { - $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $this->quoteIdentifier($fieldName) - . ' TO ' . $this->quoteIdentifier($field['name']); + $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName + . ' TO ' . $field['name']; } } if ( ! empty($changes['remove']) && is_array($changes['remove'])) { $fields = array(); foreach ($changes['remove'] as $fieldName => $field) { - $fields[] = $this->quoteIdentifier($fieldName); + $fields[] = $fieldName; } $sql[] = 'ALTER TABLE ' . $name . ' DROP COLUMN ' . implode(', ', $fields); } if ( ! empty($changes['name'])) { - $changeName = $this->quoteIdentifier($changes['name']); + $changeName = $changes['name']; $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName; } @@ -504,4 +500,9 @@ END;'; } return $query; } + + public function getSqlResultCasing($column) + { + return strtoupper($column); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php index 05bb79343..0240fcb07 100644 --- a/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php +++ b/lib/Doctrine/DBAL/Platforms/PostgreSqlPlatform.php @@ -61,7 +61,6 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getMd5Expression($column) { - $column = $this->quoteIdentifier($column); if ($this->_version > 7) { return 'MD5(' . $column . ')'; } else { @@ -82,10 +81,7 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getSubstringExpression($value, $from, $len = null) { - $value = $this->quoteIdentifier($value); - if ($len === null) { - $len = $this->quoteIdentifier($len); return 'SUBSTR(' . $value . ', ' . $from . ')'; } else { return 'SUBSTR(' . $value . ', ' . $from . ', ' . $len . ')'; @@ -238,6 +234,16 @@ class PostgreSqlPlatform extends AbstractPlatform return true; } + /** + * Whether the platform supports database schemas. + * + * @return boolean + */ + public function supportsSchemas() + { + return true; + } + /** * Whether the platform supports identity columns. * Postgres supports these through the SERIAL keyword. @@ -321,7 +327,7 @@ class PostgreSqlPlatform extends AbstractPlatform WHERE trg.tgrelid = tbl.oid'; if ( ! is_null($table)) { - $sql .= " AND tbl.relname = ".$this->quoteIdentifier($table); + $sql .= " AND tbl.relname = " . $table; } return $sql; @@ -428,7 +434,7 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getCreateDatabaseSql($name) { - return 'CREATE DATABASE ' . $this->quoteIdentifier($name); + return 'CREATE DATABASE ' . $name; } /** @@ -440,7 +446,7 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getDropDatabaseSql($name) { - return 'DROP DATABASE ' . $this->quoteIdentifier($name); + return 'DROP DATABASE ' . $name; } /** @@ -519,7 +525,6 @@ class PostgreSqlPlatform extends AbstractPlatform if (isset($changes['remove']) && is_array($changes['remove'])) { foreach ($changes['remove'] as $fieldName => $field) { - $fieldName = $this->quoteIdentifier($fieldName, true); $query = 'DROP ' . $fieldName; $sql[] = 'ALTER TABLE ' . $name . ' ' . $query; } @@ -527,7 +532,6 @@ class PostgreSqlPlatform extends AbstractPlatform if (isset($changes['change']) && is_array($changes['change'])) { foreach ($changes['change'] as $fieldName => $field) { - $fieldName = $this->quoteIdentifier($fieldName, true); if (isset($field['type'])) { $serverInfo = $this->getServerVersion(); @@ -550,15 +554,12 @@ class PostgreSqlPlatform extends AbstractPlatform if (isset($changes['rename']) && is_array($changes['rename'])) { foreach ($changes['rename'] as $fieldName => $field) { - $fieldName = $this->quoteIdentifier($fieldName, true); - $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->quoteIdentifier($field['name'], true); + $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $field['name']; } } - $name = $this->quoteIdentifier($name, true); if (isset($changes['name'])) { - $changeName = $this->quoteIdentifier($changes['name'], true); - $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName; + $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changes['name']; } return $sql; @@ -572,7 +573,7 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getCreateSequenceSql($sequenceName, $start = 1, $allocationSize = 1) { - return 'CREATE SEQUENCE ' . $this->quoteIdentifier($sequenceName) + return 'CREATE SEQUENCE ' . $sequenceName . ' INCREMENT BY ' . $allocationSize . ' START ' . $start; } @@ -584,7 +585,7 @@ class PostgreSqlPlatform extends AbstractPlatform */ public function getDropSequenceSql($sequenceName) { - return 'DROP SEQUENCE ' . $this->quoteIdentifier($sequenceName); + return 'DROP SEQUENCE ' . $sequenceName; } /** @@ -601,11 +602,10 @@ class PostgreSqlPlatform extends AbstractPlatform if (isset($options['primary']) && ! empty($options['primary'])) { $keyColumns = array_unique(array_values($options['primary'])); - $keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns); $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')'; } - $query = 'CREATE TABLE ' . $this->quoteIdentifier($name) . ' (' . $queryFields . ')'; + $query = 'CREATE TABLE ' . $name . ' (' . $queryFields . ')'; $sql[] = $query; @@ -746,4 +746,9 @@ class PostgreSqlPlatform extends AbstractPlatform { return 'postgresql'; } + + public function getSqlResultCasing($column) + { + return strtolower($column); + } } \ No newline at end of file diff --git a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php index a7230dce8..6e1594125 100644 --- a/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php +++ b/lib/Doctrine/DBAL/Platforms/SqlitePlatform.php @@ -298,7 +298,6 @@ class SqlitePlatform extends AbstractPlatform $queryFields.= ', PRIMARY KEY('.implode(', ', $keyColumns).')'; } - $name = $this->quoteIdentifier($name, true); $sql = 'CREATE TABLE ' . $name . ' (' . $queryFields; /*if ($check = $this->getCheckDeclarationSql($fields)) { diff --git a/lib/Doctrine/DBAL/Schema/MsSqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MsSqlSchemaManager.php index bfae0013f..649bc3739 100644 --- a/lib/Doctrine/DBAL/Schema/MsSqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MsSqlSchemaManager.php @@ -40,7 +40,6 @@ class MsSqlSchemaManager extends AbstractSchemaManager */ public function createDatabase($name) { - $name = $this->conn->quoteIdentifier($name, true); $query = "CREATE DATABASE $name"; if ($this->conn->options['database_device']) { $query.= ' ON '.$this->conn->options['database_device']; @@ -58,7 +57,6 @@ class MsSqlSchemaManager extends AbstractSchemaManager */ public function dropDatabase($name) { - $name = $this->conn->quoteIdentifier($name, true); return $this->conn->standaloneQuery('DROP DATABASE ' . $name, null, true); } @@ -181,7 +179,6 @@ class MsSqlSchemaManager extends AbstractSchemaManager if ($query) { $query .= ', '; } - $field_name = $this->conn->quoteIdentifier($fieldName, true); $query .= 'DROP COLUMN ' . $fieldName; } } @@ -190,7 +187,6 @@ class MsSqlSchemaManager extends AbstractSchemaManager return false; } - $name = $this->conn->quoteIdentifier($name, true); return $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query); } @@ -199,9 +195,8 @@ class MsSqlSchemaManager extends AbstractSchemaManager */ public function createSequence($seqName, $start = 1, $allocationSize = 1) { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - $seqcolName = $this->conn->quoteIdentifier($this->conn->options['seqcol_name'], true); - $query = 'CREATE TABLE ' . $sequenceName . ' (' . $seqcolName . + $seqcolName = 'seq_col'; + $query = 'CREATE TABLE ' . $seqName . ' (' . $seqcolName . ' INT PRIMARY KEY CLUSTERED IDENTITY(' . $start . ', 1) NOT NULL)'; $res = $this->conn->exec($query); @@ -228,8 +223,7 @@ class MsSqlSchemaManager extends AbstractSchemaManager */ public function dropSequenceSql($seqName) { - $sequenceName = $this->conn->quoteIdentifier($this->conn->getSequenceName($seqName), true); - return 'DROP TABLE ' . $sequenceName; + return 'DROP TABLE ' . $seqName; } /** @@ -254,7 +248,7 @@ class MsSqlSchemaManager extends AbstractSchemaManager */ public function listTableColumns($table) { - $sql = 'EXEC sp_columns @table_name = ' . $this->conn->quoteIdentifier($table, true); + $sql = 'EXEC sp_columns @table_name = ' . $table; $result = $this->conn->fetchAssoc($sql); $columns = array(); diff --git a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php index 1f8507893..4dc29eae4 100644 --- a/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php +++ b/lib/Doctrine/DBAL/Schema/MySqlSchemaManager.php @@ -278,7 +278,6 @@ class MySqlSchemaManager extends AbstractSchemaManager */ public function createSequence($sequenceName, $start = 1, $allocationSize = 1) { - $sequenceName = $this->_conn->quoteIdentifier($sequenceName); $seqColumnName = 'mysql_sequence'; /* No support for options yet. Might add 4th options parameter later diff --git a/lib/Doctrine/ORM/AbstractQuery.php b/lib/Doctrine/ORM/AbstractQuery.php index d69ff989b..3479d673a 100644 --- a/lib/Doctrine/ORM/AbstractQuery.php +++ b/lib/Doctrine/ORM/AbstractQuery.php @@ -412,7 +412,7 @@ abstract class AbstractQuery public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) { return $this->_em->getHydrator($this->_hydrationMode)->iterate( - $this->_execute($params, $hydrationMode), $this->_resultSetMapping + $this->_doExecute($params, $hydrationMode), $this->_resultSetMapping ); } diff --git a/lib/Doctrine/ORM/Configuration.php b/lib/Doctrine/ORM/Configuration.php index c229ea27b..007082ed6 100644 --- a/lib/Doctrine/ORM/Configuration.php +++ b/lib/Doctrine/ORM/Configuration.php @@ -46,7 +46,9 @@ class Configuration extends \Doctrine\DBAL\Configuration 'metadataDriverImpl' => null, 'cacheDir' => null, 'allowPartialObjects' => true, - 'useCExtension' => false + 'useCExtension' => false, + 'namedQueries' => array(), + 'namedNativeQueries' => array() )); //TODO: Move this to client code to avoid unnecessary work when a different metadata @@ -203,4 +205,50 @@ class Configuration extends \Doctrine\DBAL\Configuration { $this->_attributes['useCExtension'] = $boolean; } + + /** + * Adds a named DQL query to the configuration. + * + * @param string $name The name of the query. + * @param string $dql The DQL query string. + */ + public function addNamedQuery($name, $dql) + { + $this->_attributes['namedQueries'][$name] = $dql; + } + + /** + * Gets a previously registered named DQL query. + * + * @param string $name The name of the query. + * @return string The DQL query. + */ + public function getNamedQuery($name) + { + return $this->_attributes['namedQueries'][$name]; + } + + /** + * Adds a named native query to the configuration. + * + * @param string $name The name of the query. + * @param string $sql The native SQL query string. + * @param ResultSetMapping $rsm The ResultSetMapping used for the results of the SQL query. + */ + public function addNamedNativeQuery($name, $sql, Query\ResultSetMapping $rsm) + { + $this->_attributes['namedNativeQueries'][$name] = array($sql, $rsm); + } + + /** + * Gets the components of a previously registered named native query. + * + * @param string $name The name of the query. + * @return array A tuple with the first element being the SQL string and the second + * element being the ResultSetMapping. + */ + public function getNamedNativeQuery($name) + { + return $this->_attributes['namedNativeQueries'][$name]; + } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/EntityManager.php b/lib/Doctrine/ORM/EntityManager.php index 06d8258a3..dbbef7c1e 100644 --- a/lib/Doctrine/ORM/EntityManager.php +++ b/lib/Doctrine/ORM/EntityManager.php @@ -228,14 +228,14 @@ class EntityManager } /** - * Creates a DQL query with the specified name. + * Creates a Query from a named query. * + * @param string $name * @todo Implementation. - * @throws DoctrineException If there is no query registered with the given name. */ public function createNamedQuery($name) { - //... + return $this->createQuery($this->_config->getNamedQuery($name)); } /** @@ -254,11 +254,15 @@ class EntityManager } /** + * Creates a NativeQuery from a named native query. + * + * @param string $name * @todo Implementation. */ public function createNamedNativeQuery($name) { - //... + list($sql, $rsm) = $this->_config->getNamedNativeQuery($name); + return $this->createNativeQuery($sql, $rsm); } /** diff --git a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php index f7ec56491..caf3caaf7 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ArrayHydrator.php @@ -32,6 +32,7 @@ use Doctrine\DBAL\Connection; */ class ArrayHydrator extends AbstractHydrator { + private $_ce = array(); private $_rootAliases = array(); private $_isSimpleQuery = false; private $_identifierMap = array(); @@ -104,10 +105,8 @@ class ArrayHydrator extends AbstractHydrator continue; } - $relation = $this->_rsm->relationMap[$dqlAlias]; - $relationAlias = $relation->sourceFieldName; - //$relationAlias = $this->_rsm->relationMap[$dqlAlias]; - //$relation = $this->_ce[$parentClass]->associationMappings[$relationField]; + $relationAlias = $this->_rsm->relationMap[$dqlAlias]; + $relation = $this->_getClassMetadata($this->_rsm->aliasMap[$parent])->associationMappings[$relationAlias]; // Check the type of the relation (many or single-valued) if ( ! $relation->isOneToOne()) { @@ -221,6 +220,14 @@ class ArrayHydrator extends AbstractHydrator } } } + + private function _getClassMetadata($className) + { + if ( ! isset($this->_ce[$className])) { + $this->_ce[$className] = $this->_em->getClassMetadata($className); + } + return $this->_ce[$className]; + } /** {@inheritdoc} */ protected function _getRowContainer() diff --git a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php index d97c22377..f1a78b510 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php @@ -91,21 +91,29 @@ class ObjectHydrator extends AbstractHydrator // Remember which associations are "fetch joined" if (isset($this->_rsm->relationMap[$dqlAlias])) { - $assoc = $this->_rsm->relationMap[$dqlAlias]; - //$assoc = $class->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; + $targetClassName = $this->_rsm->aliasMap[$this->_rsm->parentAliasMap[$dqlAlias]]; + $targetClass = $this->_em->getClassMetadata($targetClassName); + $this->_ce[$targetClassName] = $targetClass; + $assoc = $targetClass->associationMappings[$this->_rsm->relationMap[$dqlAlias]]; $this->_fetchedAssociations[$assoc->sourceEntityName][$assoc->sourceFieldName] = true; if ($assoc->mappedByFieldName) { $this->_fetchedAssociations[$assoc->targetEntityName][$assoc->mappedByFieldName] = true; } else { - $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); - if (isset($targetClass->inverseMappings[$assoc->sourceFieldName])) { - $inverseAssoc = $targetClass->inverseMappings[$assoc->sourceFieldName]; + if (isset($targetClass->inverseMappings[$className][$assoc->sourceFieldName])) { + $inverseAssoc = $targetClass->inverseMappings[$className][$assoc->sourceFieldName]; $this->_fetchedAssociations[$assoc->targetEntityName][$inverseAssoc->sourceFieldName] = true; } } } } } + + protected function _cleanup() + { + parent::_cleanup(); + $this->_identifierMap = + $this->_resultPointers = array(); + } /** * {@inheritdoc} @@ -141,7 +149,7 @@ class ObjectHydrator extends AbstractHydrator $class = $this->_ce[get_class($entity)]; $relation = $class->associationMappings[$name]; - $pColl = new PersistentCollection($this->_em, $this->_getClassMetadata($relation->targetEntityName), + $pColl = new PersistentCollection($this->_em, $this->_ce[$relation->targetEntityName], $class->reflFields[$name]->getValue($entity) ?: new ArrayCollection); $pColl->setOwner($entity, $relation); @@ -193,8 +201,9 @@ class ObjectHydrator extends AbstractHydrator } else { // Inject collection $reflField = $this->_ce[$className]->reflFields[$field]; - $pColl = new PersistentCollection($this->_em, $this->_getClassMetadata( - $assoc->targetEntityName), $reflField->getValue($entity) ?: new ArrayCollection + $pColl = new PersistentCollection($this->_em, + $this->_getClassMetadata($assoc->targetEntityName), + $reflField->getValue($entity) ?: new ArrayCollection ); $pColl->setOwner($entity, $assoc); $reflField->setValue($entity, $pColl); @@ -271,10 +280,8 @@ class ObjectHydrator extends AbstractHydrator $parentClass = get_class($baseElement); $oid = spl_object_hash($baseElement); - $relation = $this->_rsm->relationMap[$dqlAlias]; - //$relationField = $this->_rsm->relationMap[$dqlAlias]; - //$relation = $this->_ce[$parentClass]->associationMappings[$relationField]; - $relationField = $relation->sourceFieldName; + $relationField = $this->_rsm->relationMap[$dqlAlias]; + $relation = $this->_ce[$parentClass]->associationMappings[$relationField]; $reflField = $this->_ce[$parentClass]->reflFields[$relationField]; $reflFieldValue = $reflField->getValue($baseElement); @@ -296,12 +303,11 @@ class ObjectHydrator extends AbstractHydrator // If it's a bi-directional many-to-many, also initialize the reverse collection. if ($relation->isManyToMany()) { - if ($relation->isOwningSide && isset($this->_ce[$entityName]->inverseMappings[$relationField])) { - $inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relationField]->sourceFieldName; + if ($relation->isOwningSide && isset($this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField])) { + $inverseFieldName = $this->_ce[$entityName]->inverseMappings[$relation->sourceEntityName][$relationField]->sourceFieldName; // Only initialize reverse collection if it is not yet initialized. if ( ! isset($this->_initializedCollections[spl_object_hash($element) . $inverseFieldName])) { - $this->initRelatedCollection($element, $this->_ce[$entityName] - ->inverseMappings[$relationField]->sourceFieldName); + $this->initRelatedCollection($element, $inverseFieldName); } } else if ($relation->mappedByFieldName) { // Only initialize reverse collection if it is not yet initialized. @@ -345,8 +351,8 @@ class ObjectHydrator extends AbstractHydrator $targetClass = $this->_ce[$relation->targetEntityName]; if ($relation->isOwningSide) { // If there is an inverse mapping on the target class its bidirectional - if (isset($targetClass->inverseMappings[$relationField])) { - $sourceProp = $targetClass->inverseMappings[$relationField]->sourceFieldName; + if (isset($targetClass->inverseMappings[$relation->sourceEntityName][$relationField])) { + $sourceProp = $targetClass->inverseMappings[$relation->sourceEntityName][$relationField]->sourceFieldName; $targetClass->reflFields[$sourceProp]->setValue($element, $base); } else if ($this->_ce[$parentClass] === $targetClass && $relation->mappedByFieldName) { // Special case: bi-directional self-referencing one-one on the same class diff --git a/lib/Doctrine/ORM/Mapping/AssociationMapping.php b/lib/Doctrine/ORM/Mapping/AssociationMapping.php index bf417e7be..7e13e6ad3 100644 --- a/lib/Doctrine/ORM/Mapping/AssociationMapping.php +++ b/lib/Doctrine/ORM/Mapping/AssociationMapping.php @@ -57,12 +57,7 @@ abstract class AssociationMapping * @var array */ protected static $_cascadeTypes = array( - 'all', - 'none', - 'save', - 'delete', - 'refresh', - 'merge' + 'all', 'none', 'save', 'delete', 'refresh', 'merge' ); public $cascades = array(); @@ -173,6 +168,10 @@ abstract class AssociationMapping if ( ! isset($mapping['mappedBy'])) { // Optional if (isset($mapping['joinTable'])) { + if ($mapping['joinTable']['name'][0] == '`') { + $mapping['joinTable']['name'] = trim($mapping['joinTable']['name'], '`'); + $mapping['joinTable']['quoted'] = true; + } $this->joinTable = $mapping['joinTable']; } } else { @@ -402,16 +401,16 @@ abstract class AssociationMapping * association) of $sourceEntity, if needed */ abstract public function load($sourceEntity, $target, $em, array $joinColumnValues = array()); - + /** - * Uses reflection to access internal data of entities. - * @param ClassMetadata $class - * @param $entity a domain object - * @param $column name of private field - * @return mixed + * + * @param $platform + * @return unknown_type */ - protected function _getPrivateValue(ClassMetadata $class, $entity, $column) + public function getQuotedJoinTableName($platform) { - return $class->reflFields[$class->fieldNames[$column]]->getValue($entity); + return isset($this->joinTable['quoted']) ? + $platform->quoteIdentifier($this->joinTable['name']) : + $this->joinTable['name']; } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index fc240f37c..78dafc8c9 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -665,25 +665,31 @@ final class ClassMetadata } /** - * Gets the inverse association mapping for the given fieldname. + * Gets the inverse association mapping for the given target class name and + * owning fieldname. * - * @param string $mappedByFieldName - * @return Doctrine\ORM\Mapping\AssociationMapping The mapping. + * @param string $mappedByFieldName The field on the + * @return Doctrine\ORM\Mapping\AssociationMapping The mapping or NULL if there is no such + * inverse association mapping. */ - public function getInverseAssociationMapping($mappedByFieldName) + public function getInverseAssociationMapping($targetClassName, $mappedByFieldName) { - return $this->inverseMappings[$mappedByFieldName]; + return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]) ? + $this->inverseMappings[$targetClassName][$mappedByFieldName] : null; } /** - * Whether the class has an inverse association mapping on the given fieldname. + * Checks whether the class has an inverse association mapping that points to the + * specified class and ha the specified mappedBy field. * - * @param string $mappedByFieldName + * @param string $targetClassName The name of the target class. + * @param string $mappedByFieldName The name of the mappedBy field that points to the field on + * the target class that owns the association. * @return boolean */ - public function hasInverseAssociationMapping($mappedByFieldName) + public function hasInverseAssociationMapping($targetClassName, $mappedByFieldName) { - return isset($this->inverseMappings[$mappedByFieldName]); + return isset($this->inverseMappings[$targetClassName][$mappedByFieldName]); } /** @@ -740,6 +746,11 @@ final class ClassMetadata // Complete fieldName and columnName mapping if ( ! isset($mapping['columnName'])) { $mapping['columnName'] = $mapping['fieldName']; + } else { + if ($mapping['columnName'][0] == '`') { + $mapping['columnName'] = trim($mapping['columnName'], '`'); + $mapping['quoted'] = true; + } } $this->columnNames[$mapping['fieldName']] = $mapping['columnName']; @@ -762,36 +773,15 @@ final class ClassMetadata $this->reflFields[$mapping['fieldName']] = $refProp; } - /** - * @todo Implementation of Optimistic Locking. - */ - public function mapVersionField(array $mapping) - { - //... - } - - /** - * Overrides an existant field mapping. - * Used i.e. by Entity classes deriving from another Entity class that acts - * as a mapped superclass to refine the basic mapping. - * - * @param array $newMapping - * @todo Implementation. - */ - public function overrideFieldMapping(array $newMapping) - { - //... - } - /** * Maps an embedded value object. * * @todo Implementation. */ - public function mapEmbeddedValue() + /*public function mapEmbeddedValue() { //... - } + }*/ /** * Gets the identifier (primary key) field names of the class. @@ -964,7 +954,15 @@ final class ClassMetadata */ public function getIdentifierColumnNames() { - return $this->getColumnNames((array)$this->getIdentifierFieldNames()); + if ($this->isIdentifierComposite) { + $columnNames = array(); + foreach ($this->identifier as $idField) { + $columnNames[] = $this->fieldMappings[$idField]['columnName']; + } + return $columnNames; + } else { + return array($this->fieldMappings[$this->identifier[0]]['columnName']); + } } /** @@ -1007,7 +1005,7 @@ final class ClassMetadata /** * - * @return + * @return boolean */ public function isInheritanceTypeNone() { @@ -1110,14 +1108,6 @@ final class ClassMetadata return $this->getTypeOfField($this->getFieldName($columnName)); } - /** - * Gets the (maximum) length of a field. - */ - public function getFieldLength($fieldName) - { - return $this->fieldMappings[$fieldName]['length']; - } - /** * Gets the name of the primary table. * @@ -1138,23 +1128,6 @@ final class ClassMetadata return $this->primaryTable['name'] . '_id_tmp'; } - public function getInheritedFields() - { - - } - - /** - * Adds a named query. - * - * @param string $name The name under which the query gets registered. - * @param string $query The DQL query. - * @todo Implementation. - */ - public function addNamedQuery($name, $query) - { - //... - } - /** * Gets the inheritance mapping type used by the mapped class. * @@ -1339,7 +1312,7 @@ final class ClassMetadata /** * Adds a field mapping. * - * @param array $mapping + * @param array $mapping The field mapping. */ public function mapField(array $mapping) { @@ -1374,7 +1347,7 @@ final class ClassMetadata /** * INTERNAL: - * Adds an association mapping without completing/validating it. + * Adds a field mapping without completing/validating it. * This is mainly used to add inherited field mappings to derived classes. * * @param array $mapping @@ -1409,7 +1382,7 @@ final class ClassMetadata private function _registerMappingIfInverse(AssociationMapping $assoc) { if ($assoc->isInverseSide()) { - $this->inverseMappings[$assoc->getMappedByFieldName()] = $assoc; + $this->inverseMappings[$assoc->targetEntityName][$assoc->mappedByFieldName] = $assoc; } } @@ -1455,7 +1428,7 @@ final class ClassMetadata */ private function _storeAssociationMapping(AssociationMapping $assocMapping) { - $sourceFieldName = $assocMapping->getSourceFieldName(); + $sourceFieldName = $assocMapping->sourceFieldName; if (isset($this->associationMappings[$sourceFieldName])) { throw MappingException::duplicateFieldMapping(); } @@ -1499,10 +1472,10 @@ final class ClassMetadata * @param boolean $bool * @see getJoinSubClasses() */ - public function setJoinSubClasses($bool) + /*public function setJoinSubClasses($bool) { $this->joinSubclasses = (bool)$bool; - } + }*/ /** * Gets whether the class mapped by this instance should OUTER JOIN sub classes @@ -1511,10 +1484,10 @@ final class ClassMetadata * @return * @see setJoinSubClasses() */ - public function getJoinSubClasses() + /*public function getJoinSubClasses() { return $this->joinSubclasses; - } + }*/ /** * Dispatches the lifecycle event of the given entity to the registered @@ -1780,7 +1753,50 @@ final class ClassMetadata { $this->versionField = $versionField; } - + + /** + * Gets the (possibly quoted) column name of a mapped field for safe use + * in an SQL statement. + * + * @param string $field + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedColumnName($field, $platform) + { + return isset($this->fieldMappings[$field]['quoted']) ? + $platform->quoteIdentifier($this->fieldMappings[$field]['columnName']) : + $this->fieldMappings[$field]['columnName']; + } + + /** + * Gets the (possibly quoted) primary table name of this class for safe use + * in an SQL statement. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedTableName($platform) + { + return isset($this->primaryTable['quoted']) ? + $platform->quoteIdentifier($this->primaryTable['name']) : + $this->primaryTable['name']; + } + + /** + * Gets the (possibly quoted) name of the discriminator column for safe use + * in an SQL statement. + * + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedDiscriminatorColumnName($platform) + { + return isset($this->discriminatorColumn['quoted']) ? + $platform->quoteIdentifier($this->discriminatorColumn['name']) : + $this->discriminatorColumn['name']; + } + /** * Creates a string representation of this instance. * @@ -1791,4 +1807,65 @@ final class ClassMetadata { return __CLASS__ . '@' . spl_object_hash($this); } + + /** + * Determines which fields get serialized. + * + * Parts that are NOT serialized because they can not be properly unserialized: + * - reflClass (ReflectionClass) + * - reflFields (ReflectionProperty array) + * + * @return array The names of all the fields that should be serialized. + */ + public function __sleep() + { + return array( + 'associationMappings', + 'changeTrackingPolicy', + 'columnNames', + 'customRepositoryClassName', + 'discriminatorColumn', + 'discriminatorValue', + 'fieldMappings', + 'fieldNames', + 'generatorType', + 'identifier', + 'idGenerator', + 'inheritanceType', + 'inheritedAssociationFields', + 'insertSql', + 'inverseMappings', + 'isIdentifierComposite', + 'isMappedSuperclass', + 'isVersioned', + 'lifecycleCallbacks', + 'name', + 'namespace', + 'parentClasses', + 'primaryTable', + 'rootEntityName', + 'sequenceGeneratorDefinition', + 'subClasses', + 'versionField' + ); + } + + /** + * Restores some state that could not be serialized/unserialized. + * + * @return void + */ + public function __wakeup() + { + // Restore ReflectionClass and properties + $this->reflClass = new \ReflectionClass($this->name); + foreach ($this->fieldNames as $field) { + $this->reflFields[$field] = $this->reflClass->getProperty($field); + $this->reflFields[$field]->setAccessible(true); + } + foreach ($this->associationMappings as $field => $mapping) { + $this->reflFields[$field] = $this->reflClass->getProperty($field); + $this->reflFields[$field]->setAccessible(true); + } + } } diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index 903c69f4c..e4b74a6ca 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -98,8 +98,8 @@ class ClassMetadataFactory if ( ! isset($this->_loadedMetadata[$className])) { $cacheKey = "$className\$CLASSMETADATA"; if ($this->_cacheDriver) { - if ($this->_cacheDriver->contains($cacheKey)) { - $this->_loadedMetadata[$className] = $this->_cacheDriver->fetch($cacheKey); + if (($cached = $this->_cacheDriver->fetch($cacheKey)) !== false) { + $this->_loadedMetadata[$className] = $cached; } else { $this->_loadMetadata($className); $this->_cacheDriver->save($cacheKey, $this->_loadedMetadata[$className], null); @@ -298,12 +298,12 @@ class ClassMetadataFactory $assoc = $class->associationMappings[$name]; if ($assoc->isOneToOne() && $assoc->isOwningSide) { foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { - $columns[] = $this->_targetPlatform->quoteIdentifier($sourceCol); + $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); $values[] = '?'; } } } else if ($class->name != $class->rootEntityName || ! $class->isIdGeneratorIdentity() || $class->identifier[0] != $name) { - $columns[] = $this->_targetPlatform->quoteIdentifier($class->columnNames[$name]); + $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); $values[] = '?'; } } @@ -317,12 +317,12 @@ class ClassMetadataFactory $assoc = $class->associationMappings[$name]; if ($assoc->isOwningSide && $assoc->isOneToOne()) { foreach ($assoc->targetToSourceKeyColumns as $sourceCol) { - $columns[] = $this->_targetPlatform->quoteIdentifier($sourceCol); + $columns[] = $assoc->getQuotedJoinColumnName($sourceCol, $this->_targetPlatform); $values[] = '?'; } } } else if ($class->generatorType != ClassMetadata::GENERATOR_TYPE_IDENTITY || $class->identifier[0] != $name) { - $columns[] = $this->_targetPlatform->quoteIdentifier($class->columnNames[$name]); + $columns[] = $class->getQuotedColumnName($name, $this->_targetPlatform); $values[] = '?'; } } @@ -330,12 +330,12 @@ class ClassMetadataFactory // Add discriminator column to the INSERT SQL if necessary if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined() && $class->name == $class->rootEntityName) { - $columns[] = $class->discriminatorColumn['name']; + $columns[] = $class->getQuotedDiscriminatorColumnName($this->_targetPlatform); $values[] = '?'; } $class->insertSql = 'INSERT INTO ' . - $this->_targetPlatform->quoteIdentifier($class->primaryTable['name']) + $class->getQuotedTableName($this->_targetPlatform) . ' (' . implode(', ', $columns) . ') ' . 'VALUES (' . implode(', ', $values) . ')'; } diff --git a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php index b9eccad7a..7c75a7304 100644 --- a/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/ManyToManyMapping.php @@ -60,7 +60,7 @@ class ManyToManyMapping extends AssociationMapping public $targetToRelationKeyColumns = array(); /** - * The columns on the join table. + * List of aggregated column names on the join table. */ public $joinTableColumns = array(); @@ -91,22 +91,30 @@ class ManyToManyMapping extends AssociationMapping if ( ! isset($mapping['joinTable'])) { throw MappingException::joinTableRequired($mapping['fieldName']); } - // owning side MUST specify joinColumns if ( ! isset($mapping['joinTable']['joinColumns'])) { throw MappingException::invalidMapping($this->_sourceFieldName); } - foreach ($mapping['joinTable']['joinColumns'] as $joinColumn) { - $this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name']; - $this->joinTableColumns[] = $joinColumn['name']; - } - $this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns); - // owning side MUST specify inverseJoinColumns if ( ! isset($mapping['joinTable']['inverseJoinColumns'])) { throw MappingException::invalidMapping($this->_sourceFieldName); } - foreach ($mapping['joinTable']['inverseJoinColumns'] as $inverseJoinColumn) { + + foreach ($mapping['joinTable']['joinColumns'] as &$joinColumn) { + if ($joinColumn['name'][0] == '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } + $this->sourceToRelationKeyColumns[$joinColumn['referencedColumnName']] = $joinColumn['name']; + $this->joinTableColumns[] = $joinColumn['name']; + } + $this->sourceKeyColumns = array_keys($this->sourceToRelationKeyColumns); + + foreach ($mapping['joinTable']['inverseJoinColumns'] as &$inverseJoinColumn) { + if ($inverseJoinColumn['name'][0] == '`') { + $inverseJoinColumn['name'] = trim($inverseJoinColumn['name'], '`'); + $inverseJoinColumn['quoted'] = true; + } $this->targetToRelationKeyColumns[$inverseJoinColumn['referencedColumnName']] = $inverseJoinColumn['name']; $this->joinTableColumns[] = $inverseJoinColumn['name']; } @@ -114,7 +122,7 @@ class ManyToManyMapping extends AssociationMapping } } - public function getJoinTableColumns() + public function getJoinTableColumnNames() { return $this->joinTableColumns; } @@ -152,38 +160,30 @@ class ManyToManyMapping extends AssociationMapping { $sourceClass = $em->getClassMetadata($this->sourceEntityName); $joinTableConditions = array(); - if ($this->isOwningSide()) { - $joinTable = $this->joinTable; - $joinClauses = $this->targetToRelationKeyColumns; + if ($this->isOwningSide) { foreach ($this->sourceToRelationKeyColumns as $sourceKeyColumn => $relationKeyColumn) { // getting id if (isset($sourceClass->reflFields[$sourceKeyColumn])) { - $joinTableConditions[$relationKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn); + $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { $joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn]; } } } else { $owningAssoc = $em->getClassMetadata($this->targetEntityName)->associationMappings[$this->mappedByFieldName]; - $joinTable = $owningAssoc->joinTable; // TRICKY: since the association is inverted source and target are flipped - $joinClauses = $owningAssoc->sourceToRelationKeyColumns; foreach ($owningAssoc->targetToRelationKeyColumns as $sourceKeyColumn => $relationKeyColumn) { // getting id if (isset($sourceClass->reflFields[$sourceKeyColumn])) { - $joinTableConditions[$relationKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn); + $joinTableConditions[$relationKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { $joinTableConditions[$relationKeyColumn] = $joinColumnValues[$sourceKeyColumn]; } } } - $joinTableCriteria = array( - 'table' => $joinTable['name'], - 'join' => $joinClauses, - 'criteria' => $joinTableConditions - ); + $persister = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName); - $persister->loadManyToManyCollection(array($joinTableCriteria), $targetCollection); + $persister->loadManyToManyCollection($this, $joinTableConditions, $targetCollection); } /** @@ -193,4 +193,19 @@ class ManyToManyMapping extends AssociationMapping { return true; } + + /** + * Gets the (possibly quoted) column name of a join column that is safe to use + * in an SQL statement. + * + * @param string $joinColumn + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedJoinColumnName($joinColumn, $platform) + { + return isset($this->joinTable['joinColumns'][$joinColumn]['quoted']) ? + $platform->quoteIdentifier($joinColumn) : + $joinColumn; + } } diff --git a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php index 68a769a6a..c9c24eae5 100644 --- a/lib/Doctrine/ORM/Mapping/OneToManyMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToManyMapping.php @@ -124,7 +124,7 @@ class OneToManyMapping extends AssociationMapping foreach ($owningAssoc->getTargetToSourceKeyColumns() as $sourceKeyColumn => $targetKeyColumn) { // getting id if (isset($sourceClass->reflFields[$sourceKeyColumn])) { - $conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn); + $conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { $conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn]; } diff --git a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php index 7d6e83768..7368a4845 100644 --- a/lib/Doctrine/ORM/Mapping/OneToOneMapping.php +++ b/lib/Doctrine/ORM/Mapping/OneToOneMapping.php @@ -67,6 +67,12 @@ class OneToOneMapping extends AssociationMapping */ public $joinColumns = array(); + /** + * A map of join column names to field names that are used in cases + * when the join columns are fetched as part of the query result. + * + * @var array + */ public $joinColumnFieldNames = array(); /** @@ -98,12 +104,16 @@ class OneToOneMapping extends AssociationMapping if ( ! isset($mapping['joinColumns'])) { throw MappingException::invalidMapping($this->sourceFieldName); } - $this->joinColumns = $mapping['joinColumns']; - foreach ($mapping['joinColumns'] as $joinColumn) { + foreach ($mapping['joinColumns'] as &$joinColumn) { + if ($joinColumn['name'][0] == '`') { + $joinColumn['name'] = trim($joinColumn['name'], '`'); + $joinColumn['quoted'] = true; + } $this->sourceToTargetKeyColumns[$joinColumn['name']] = $joinColumn['referencedColumnName']; $this->joinColumnFieldNames[$joinColumn['name']] = isset($joinColumn['fieldName']) ? $joinColumn['fieldName'] : $joinColumn['name']; } + $this->joinColumns = $mapping['joinColumns']; $this->targetToSourceKeyColumns = array_flip($this->sourceToTargetKeyColumns); } @@ -154,6 +164,21 @@ class OneToOneMapping extends AssociationMapping return true; } + /** + * Gets the (possibly quoted) column name of a join column that is safe to use + * in an SQL statement. + * + * @param string $joinColumn + * @param AbstractPlatform $platform + * @return string + */ + public function getQuotedJoinColumnName($joinColumn, $platform) + { + return isset($this->joinColumns[$joinColumn]['quoted']) ? + $platform->quoteIdentifier($joinColumn) : + $joinColumn; + } + /** * {@inheritdoc} * @@ -174,7 +199,7 @@ class OneToOneMapping extends AssociationMapping foreach ($this->sourceToTargetKeyColumns as $sourceKeyColumn => $targetKeyColumn) { // getting customer_id if (isset($sourceClass->reflFields[$sourceKeyColumn])) { - $conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn); + $conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { $conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn]; } @@ -182,18 +207,18 @@ class OneToOneMapping extends AssociationMapping $targetEntity = $em->getUnitOfWork()->getEntityPersister($this->targetEntityName)->load($conditions, $targetEntity); - if ($targetEntity !== null && $targetClass->hasInverseAssociationMapping($this->sourceFieldName)) { + if ($targetEntity !== null && $targetClass->hasInverseAssociationMapping($this->sourceEntityName, $this->sourceFieldName)) { $targetClass->setFieldValue($targetEntity, - $targetClass->inverseMappings[$this->sourceFieldName]->sourceFieldName, + $targetClass->inverseMappings[$this->sourceEntityName][$this->sourceFieldName]->sourceFieldName, $sourceEntity); } } else { $owningAssoc = $em->getClassMetadata($this->targetEntityName)->getAssociationMapping($this->mappedByFieldName); // TRICKY: since the association is specular source and target are flipped - foreach ($owningAssoc->getTargetToSourceKeyColumns() as $sourceKeyColumn => $targetKeyColumn) { + foreach ($owningAssoc->targetToSourceKeyColumns as $sourceKeyColumn => $targetKeyColumn) { // getting id if (isset($sourceClass->reflFields[$sourceKeyColumn])) { - $conditions[$targetKeyColumn] = $this->_getPrivateValue($sourceClass, $sourceEntity, $sourceKeyColumn); + $conditions[$targetKeyColumn] = $sourceClass->reflFields[$sourceClass->fieldNames[$sourceKeyColumn]]->getValue($sourceEntity); } else { $conditions[$targetKeyColumn] = $joinColumnValues[$sourceKeyColumn]; } diff --git a/lib/Doctrine/ORM/PersistentCollection.php b/lib/Doctrine/ORM/PersistentCollection.php index 4bbe69ac5..aaa5d9d06 100644 --- a/lib/Doctrine/ORM/PersistentCollection.php +++ b/lib/Doctrine/ORM/PersistentCollection.php @@ -138,9 +138,9 @@ final class PersistentCollection implements \Doctrine\Common\Collections\Collect // For sure bi-directional $this->_backRefFieldName = $assoc->mappedByFieldName; } else { - if (isset($this->_typeClass->inverseMappings[$assoc->sourceFieldName])) { + if (isset($this->_typeClass->inverseMappings[$assoc->sourceEntityName][$assoc->sourceFieldName])) { // Bi-directional - $this->_backRefFieldName = $this->_typeClass->inverseMappings[$assoc->sourceFieldName]->sourceFieldName; + $this->_backRefFieldName = $this->_typeClass->inverseMappings[$assoc->sourceEntityName][$assoc->sourceFieldName]->sourceFieldName; } } } diff --git a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php index f7fb9d359..8636f089a 100644 --- a/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php +++ b/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php @@ -32,7 +32,6 @@ use Doctrine\Common\DoctrineException; * @version $Revision$ * @link www.doctrine-project.org * @since 2.0 - * @todo Reimplement. */ class JoinedSubclassPersister extends StandardEntityPersister { @@ -145,12 +144,12 @@ class JoinedSubclassPersister extends StandardEntityPersister $params = array(); foreach ($insertData[$rootTableName] as $columnName => $value) { $params[$paramIndex] = $value; - $stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/); + $stmt->bindValue($paramIndex++, $value); } $sqlLogger->logSql($sql[$rootTableName], $params); } else { foreach ($insertData[$rootTableName] as $columnName => $value) { - $stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/); + $stmt->bindValue($paramIndex++, $value); } } $stmt->execute(); @@ -168,31 +167,31 @@ class JoinedSubclassPersister extends StandardEntityPersister $stmt = $stmts[$tableName]; $paramIndex = 1; if ($sqlLogger) { - //TODO: Log type $params = array(); - foreach ((array)$id as $idVal) { + foreach ((array) $id as $idVal) { $params[$paramIndex] = $idVal; - $stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/); + $stmt->bindValue($paramIndex++, $idVal); } foreach ($data as $columnName => $value) { $params[$paramIndex] = $value; - $stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/); + $stmt->bindValue($paramIndex++, $value); } $sqlLogger->logSql($sql[$tableName], $params); } else { - foreach ((array)$id as $idVal) { - $stmt->bindValue($paramIndex++, $idVal/*, TODO: TYPE*/); + foreach ((array) $id as $idVal) { + $stmt->bindValue($paramIndex++, $idVal); } foreach ($data as $columnName => $value) { - $stmt->bindValue($paramIndex++, $value/*, TODO: TYPE*/); + $stmt->bindValue($paramIndex++, $value); } } $stmt->execute(); } } - foreach ($stmts as $stmt) - $stmt->closeCursor(); + foreach ($stmts as $stmt) { + $stmt->closeCursor(); + } if ($isVersioned) { $this->_assignDefaultVersionValue($versionedClass, $entity, $id); @@ -247,7 +246,7 @@ class JoinedSubclassPersister extends StandardEntityPersister public function delete($entity) { $id = array_combine( - $this->_class->getIdentifierFieldNames(), + $this->_class->identifier, $this->_em->getUnitOfWork()->getEntityIdentifier($entity) ); @@ -257,7 +256,7 @@ class JoinedSubclassPersister extends StandardEntityPersister $this->_conn->delete($this->_em->getClassMetadata($this->_class->rootEntityName) ->primaryTable['name'], $id); } else { - // Delete the parent tables, starting from this class' table up to the root table + // Delete from all tables individually, starting from this class' table up to the root table. $this->_conn->delete($this->_class->primaryTable['name'], $id); foreach ($this->_class->parentClasses as $parentClass) { $this->_conn->delete($this->_em->getClassMetadata($parentClass)->primaryTable['name'], $id); @@ -266,7 +265,7 @@ class JoinedSubclassPersister extends StandardEntityPersister } /** - * Gets the SELECT SQL to select a single entity by a set of field criteria. + * Gets the SELECT SQL to select one or more entities by a set of field criteria. * * @param array $criteria * @return string The SQL. diff --git a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php index 60a143921..7e729c0d9 100644 --- a/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php +++ b/lib/Doctrine/ORM/Persisters/ManyToManyPersister.php @@ -40,7 +40,7 @@ class ManyToManyPersister extends AbstractCollectionPersister { $mapping = $coll->getMapping(); $joinTable = $mapping->getJoinTable(); - $columns = $mapping->getJoinTableColumns(); + $columns = $mapping->getJoinTableColumnNames(); return 'DELETE FROM ' . $joinTable['name'] . ' WHERE ' . implode(' = ? AND ', $columns) . ' = ?'; } @@ -76,7 +76,7 @@ class ManyToManyPersister extends AbstractCollectionPersister { $mapping = $coll->getMapping(); $joinTable = $mapping->getJoinTable(); - $columns = $mapping->getJoinTableColumns(); + $columns = $mapping->getJoinTableColumnNames(); return 'INSERT INTO ' . $joinTable['name'] . ' (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')'; } diff --git a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php index 04457e420..538bf2489 100644 --- a/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php +++ b/lib/Doctrine/ORM/Persisters/StandardEntityPersister.php @@ -21,15 +21,15 @@ namespace Doctrine\ORM\Persisters; -use Doctrine\Common\DoctrineException; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Types\Type; -use Doctrine\ORM\EntityManager; -use Doctrine\ORM\UnitOfWork; -use Doctrine\ORM\PersistentCollection; -use Doctrine\ORM\Mapping\ClassMetadata; -use Doctrine\ORM\Events; +use Doctrine\Common\DoctrineException, + Doctrine\Common\Collections\ArrayCollection, + Doctrine\DBAL\Connection, + Doctrine\DBAL\Types\Type, + Doctrine\ORM\EntityManager, + Doctrine\ORM\UnitOfWork, + Doctrine\ORM\PersistentCollection, + Doctrine\ORM\Mapping\ClassMetadata, + Doctrine\ORM\Events; /** * Base class for all EntityPersisters. An EntityPersister is a class that knows @@ -64,6 +64,13 @@ class StandardEntityPersister * @var Doctrine\DBAL\Connection $conn */ protected $_conn; + + /** + * The database platform. + * + * @var AbstractPlatform + */ + protected $_platform; /** * The EntityManager instance. @@ -90,16 +97,16 @@ class StandardEntityPersister public function __construct(EntityManager $em, ClassMetadata $class) { $this->_em = $em; - $this->_platform = $em->getConnection()->getDatabasePlatform(); - $this->_entityName = $class->name; $this->_conn = $em->getConnection(); + $this->_platform = $this->_conn->getDatabasePlatform(); + $this->_entityName = $class->name; $this->_class = $class; } /** - * Adds an entity to the queued inserts. + * Adds an entity to the queued insertions. * - * @param object $entity + * @param object $entity The entitiy to queue for insertion. */ public function addInsert($entity) { @@ -179,7 +186,7 @@ class StandardEntityPersister $identifier = $this->_class->getIdentifierColumnNames(); $versionFieldColumnName = $this->_class->getColumnName($versionField); - $sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->primaryTable['name'] . + $sql = "SELECT " . $versionFieldColumnName . " FROM " . $class->getQuotedTableName($this->_platform) . " WHERE " . implode(' = ? AND ', $identifier) . " = ?"; $value = $this->_conn->fetchColumn($sql, (array) $id); $this->_class->setFieldValue($entity, $versionField, $value[0]); @@ -195,7 +202,7 @@ class StandardEntityPersister $updateData = array(); $this->_prepareData($entity, $updateData); $id = array_combine( - $this->_class->getIdentifierFieldNames(), + $this->_class->identifier, $this->_em->getUnitOfWork()->getEntityIdentifier($entity) ); $tableName = $this->_class->primaryTable['name']; @@ -217,23 +224,23 @@ class StandardEntityPersister */ protected function _doUpdate($entity, $tableName, $data, $where) { + // Note: $tableName and column names in $data are already quoted for SQL. + $set = array(); foreach ($data as $columnName => $value) { - $set[] = $this->_conn->quoteIdentifier($columnName) . ' = ?'; + $set[] = $columnName . ' = ?'; } if ($isVersioned = $this->_class->isVersioned) { $versionField = $this->_class->versionField; - $identifier = $this->_class->getIdentifier(); - $versionFieldColumnName = $this->_class->getColumnName($versionField); - $where[$versionFieldColumnName] = $entity->version; - $set[] = $this->_conn->quoteIdentifier($versionFieldColumnName) . ' = ' . - $this->_conn->quoteIdentifier($versionFieldColumnName) . ' + 1'; + $where[$this->_class->fieldNames[$versionField]] = $entity->version; + $versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform); + $set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1'; } $params = array_merge(array_values($data), array_values($where)); - $sql = 'UPDATE ' . $this->_conn->quoteIdentifier($tableName) + $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' = ? AND ', array_keys($where)) . ' = ?'; @@ -263,6 +270,7 @@ class StandardEntityPersister * Adds an entity to delete. * * @param object $entity + * @todo Impl. */ public function addDelete($entity) { @@ -273,6 +281,7 @@ class StandardEntityPersister * Executes all pending entity deletions. * * @see addDelete() + * @todo Impl. */ public function executeDeletions() { @@ -306,13 +315,12 @@ class StandardEntityPersister * * Notes to inheritors: Be sure to call parent::_prepareData($entity, $result, $isInsert); * - * @param object $entity + * @param object $entity The entity for which to prepare the data. * @param array $result The reference to the data array. * @param boolean $isInsert Whether the preparation is for an INSERT (or UPDATE, if FALSE). */ protected function _prepareData($entity, array &$result, $isInsert = false) { - $platform = $this->_conn->getDatabasePlatform(); $uow = $this->_em->getUnitOfWork(); if ($versioned = $this->_class->isVersioned) { @@ -323,11 +331,10 @@ class StandardEntityPersister if ($versioned && $versionField == $field) { continue; } + $oldVal = $change[0]; $newVal = $change[1]; - $columnName = $this->_class->getColumnName($field); - if (isset($this->_class->associationMappings[$field])) { $assocMapping = $this->_class->associationMappings[$field]; // Only owning side of x-1 associations can have a FK column. @@ -357,20 +364,24 @@ class StandardEntityPersister } foreach ($assocMapping->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { + $quotedSourceColumn = $assocMapping->getQuotedJoinColumnName($sourceColumn, $this->_platform); if ($newVal === null) { - $result[$this->getOwningTable($field)][$sourceColumn] = null; + $result[$this->getOwningTable($field)][$quotedSourceColumn] = null; } else { $otherClass = $this->_em->getClassMetadata($assocMapping->targetEntityName); - $result[$this->getOwningTable($field)][$sourceColumn] = + $result[$this->getOwningTable($field)][$quotedSourceColumn] = $otherClass->reflFields[$otherClass->fieldNames[$targetColumn]] ->getValue($newVal); } } } else if ($newVal === null) { + $columnName = $this->_class->getQuotedColumnName($field, $this->_platform); $result[$this->getOwningTable($field)][$columnName] = null; } else { + $columnName = $this->_class->getQuotedColumnName($field, $this->_platform); $result[$this->getOwningTable($field)][$columnName] = Type::getType( - $this->_class->fieldMappings[$field]['type'])->convertToDatabaseValue($newVal, $platform); + $this->_class->fieldMappings[$field]['type']) + ->convertToDatabaseValue($newVal, $this->_platform); } } } @@ -383,7 +394,7 @@ class StandardEntityPersister */ public function getOwningTable($fieldName) { - return $this->_class->primaryTable['name']; + return $this->_class->getQuotedTableName($this->_platform); } /** @@ -400,17 +411,19 @@ class StandardEntityPersister $stmt->execute(array_values($criteria)); $result = $stmt->fetch(Connection::FETCH_ASSOC); $stmt->closeCursor(); + return $this->_createEntity($result, $entity); } /** - * + * Loads a collection of entities into a one-to-many association. * + * @param array $criteria The criteria by which to select the entities. + * @param PersistentCollection The collection to fill. */ public function loadOneToManyCollection(array $criteria, PersistentCollection $collection) { - $sql = $this->_getSelectEntitiesSql($criteria); - $stmt = $this->_conn->prepare($sql); + $stmt = $this->_conn->prepare($this->_getSelectEntitiesSql($criteria)); $stmt->execute(array_values($criteria)); while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) { $collection->hydrateAdd($this->_createEntity($result)); @@ -419,39 +432,40 @@ class StandardEntityPersister } /** - * + * Loads a collection of entities of a many-to-many association. * + * @param array $criteria + * @param PersistentCollection $coll The collection to fill. */ - public function loadManyToManyCollection(array $criteria, PersistentCollection $collection) + public function loadManyToManyCollection($assoc, array $criteria, PersistentCollection $coll) { - $sql = $this->_getSelectManyToManyEntityCollectionSql($criteria); - $stmt = $this->_conn->prepare($sql); + $stmt = $this->_conn->prepare($this->_getSelectManyToManyEntityCollectionSql($assoc, $criteria)); $stmt->execute(array_values($criteria)); while ($result = $stmt->fetch(Connection::FETCH_ASSOC)) { - $collection->add($this->_createEntity($result)); + $coll->add($this->_createEntity($result)); } $stmt->closeCursor(); } /** + * Creates or fills a single entity object from an SQL result. * - * @param $result - * @param $entity - * @return object + * @param $result The SQL result. + * @param $entity The entity object to fill. + * @return object The filled and managed entity object. */ private function _createEntity($result, $entity = null) { - $data = array(); - $joinColumnValues = array(); - if ($result === false) { return null; } + $data = $joinColumnValues = array(); + foreach ($result as $column => $value) { if (isset($this->_class->fieldNames[$column])) { $fieldName = $this->_class->fieldNames[$column]; - $data[$fieldName] = Type::getType($this->_class->getTypeOfField($fieldName)) + $data[$fieldName] = Type::getType($this->_class->fieldMappings[$fieldName]['type']) ->convertToPHPValue($value, $this->_platform); } else { $joinColumnValues[$column] = $value; @@ -470,7 +484,7 @@ class StandardEntityPersister $id[] = $data[$fieldName]; } } else { - $id = array($data[$this->_class->getSingleIdentifierFieldName()]); + $id = array($data[$this->_class->identifier[0]]); } $this->_em->getUnitOfWork()->registerManaged($entity, $id, $data); } @@ -491,7 +505,9 @@ class StandardEntityPersister } } else { // Inject collection - $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc->targetEntityName), + $coll = new PersistentCollection( + $this->_em, + $this->_em->getClassMetadata($assoc->targetEntityName), new ArrayCollection); $coll->setOwner($entity, $assoc); $this->_class->reflFields[$field]->setValue($entity, $coll); @@ -509,20 +525,17 @@ class StandardEntityPersister } /** - * Gets the SELECT SQL to select a single entity by a set of field criteria. - * No joins are used but the query can return multiple rows. + * Gets the SELECT SQL to select one or more entities by a set of field criteria. * - * @param array $criteria every element can be a value or an joinClause - * if it is a joinClause, it will be removed and correspondent nested values will be added to $criteria. - * JoinClauses are used to restrict the result returned but only columns of this entity are selected (@see _getJoinSql()). + * @param array $criteria * @return string The SQL. */ protected function _getSelectEntitiesSql(array &$criteria) { $columnList = ''; - foreach ($this->_class->columnNames as $column) { + foreach ($this->_class->fieldNames as $field) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->_conn->quoteIdentifier($column); + $columnList .= $this->_class->getQuotedColumnName($field, $this->_platform); } $joinColumnNames = array(); @@ -531,7 +544,7 @@ class StandardEntityPersister if ($assoc->isOwningSide && $assoc->isOneToOne()) { foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { $joinColumnNames[] = $srcColumn; - $columnList .= ', ' . $this->_conn->quoteIdentifier($srcColumn); + $columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform); } } } @@ -543,30 +556,19 @@ class StandardEntityPersister if ($conditionSql != '') { $conditionSql .= ' AND '; } - - if (is_array($value)) { - $joinSql .= $this->_getJoinSql($value); - foreach ($value['criteria'] as $nestedField => $nestedValue) { - $columnName = "{$value['table']}.{$nestedField}"; - $conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?'; - $criteria[$columnName] = $nestedValue; - } - unset($criteria[$field]); - continue; - } if (isset($this->_class->columnNames[$field])) { - $columnName = $this->_class->columnNames[$field]; + $columnName = $this->_class->getQuotedColumnName($field, $this->_platform); } else if (in_array($field, $joinColumnNames)) { $columnName = $field; } else { throw DoctrineException::unrecognizedField($field); } - $conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?'; + $conditionSql .= $columnName . ' = ?'; } return 'SELECT ' . $columnList - . ' FROM ' . $this->_class->getTableName() + . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . $joinSql . ' WHERE ' . $conditionSql; } @@ -574,63 +576,59 @@ class StandardEntityPersister /** * Gets the SQL to select a collection of entities in a many-many association. * + * @param array $criteria * @return string */ - protected function _getSelectManyToManyEntityCollectionSql(array &$criteria) + protected function _getSelectManyToManyEntityCollectionSql($manyToMany, array &$criteria) { $columnList = ''; - foreach ($this->_class->columnNames as $column) { + foreach ($this->_class->fieldNames as $field) { if ($columnList != '') $columnList .= ', '; - $columnList .= $this->_conn->quoteIdentifier($column); + $columnList .= $this->_class->getQuotedColumnName($field, $this->_platform); } if ( ! $this->_em->getConfiguration()->getAllowPartialObjects()) { foreach ($this->_class->associationMappings as $assoc) { if ($assoc->isOwningSide && $assoc->isOneToOne()) { foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { - $columnList .= ', ' . $this->_conn->quoteIdentifier($srcColumn); + $columnList .= ', ' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform); } } } } - $joinSql = ''; - $conditionSql = ''; - foreach ($criteria as $field => $value) { - if ($conditionSql != '') { - $conditionSql .= ' AND '; - } - $joinSql .= $this->_getJoinSql($value); - foreach ($value['criteria'] as $nestedField => $nestedValue) { - $columnName = "{$value['table']}.{$nestedField}"; - $conditionSql .= $this->_conn->quoteIdentifier($columnName) . ' = ?'; - $criteria[$columnName] = $nestedValue; - } - unset($criteria[$field]); + if ($manyToMany->isOwningSide) { + $owningAssoc = $manyToMany; + $joinClauses = $manyToMany->targetToRelationKeyColumns; + } else { + $owningAssoc = $this->_em->getClassMetadata($manyToMany->targetEntityName)->associationMappings[$manyToMany->mappedByFieldName]; + $joinClauses = $owningAssoc->sourceToRelationKeyColumns; } - + + $joinTableName = $owningAssoc->getQuotedJoinTableName($this->_platform); + + $joinSql = ''; + foreach ($joinClauses as $sourceField => $joinTableField) { + if ($joinSql != '') $joinSql .= ' AND '; + $joinSql .= $this->_class->getQuotedTableName($this->_platform) . + '.' . $this->_class->getQuotedColumnName($sourceField, $this->_platform) . ' = ' + . $joinTableName + . '.' . $owningAssoc->getQuotedJoinColumnName($joinTableField, $this->_platform); + } + + $joinSql = ' INNER JOIN ' . $joinTableName . ' ON ' . $joinSql; + + + $conditionSql = ''; + foreach ($criteria as $joinColumn => $value) { + if ($conditionSql != '') $conditionSql .= ' AND '; + $columnName = $joinTableName . '.' . $owningAssoc->getQuotedJoinColumnName($joinColumn, $this->_platform); + $conditionSql .= $columnName . ' = ?'; + } + return 'SELECT ' . $columnList - . ' FROM ' . $this->_class->primaryTable['name'] + . ' FROM ' . $this->_class->getQuotedTableName($this->_platform) . $joinSql . ' WHERE ' . $conditionSql; } - - /** - * Builds an INNER JOIN sql query part using $joinClause. - * - * @param array $joinClause keys are: - * 'table' => table to join - * 'join' => array of [sourceField => joinTableField] - * 'criteria' => array of [joinTableField => value] - * @return string - */ - protected function _getJoinSql(array $joinClause) - { - $clauses = array(); - foreach ($joinClause['join'] as $sourceField => $joinTableField) { - $clauses[] = $this->_class->getTableName() . ".{$sourceField} = " - . "{$joinClause['table']}.{$joinTableField}"; - } - return ' INNER JOIN ' . $joinClause['table'] . ' ON ' . implode(' AND ', $clauses); - } } diff --git a/lib/Doctrine/ORM/Query/AST/AggregateExpression.php b/lib/Doctrine/ORM/Query/AST/AggregateExpression.php index b533eaa18..b5f98d7c4 100644 --- a/lib/Doctrine/ORM/Query/AST/AggregateExpression.php +++ b/lib/Doctrine/ORM/Query/AST/AggregateExpression.php @@ -45,8 +45,8 @@ class AggregateExpression extends Node $this->isDistinct = $isDistinct; } - public function dispatch($sqlWalker) + public function dispatch($walker) { - return $sqlWalker->walkAggregateExpression($this); + return $walker->walkAggregateExpression($this); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php b/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php index fcf0b0855..679cbcc7c 100644 --- a/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php +++ b/lib/Doctrine/ORM/Query/AST/ArithmeticExpression.php @@ -47,8 +47,8 @@ class ArithmeticExpression extends Node return (bool) $this->subselect; } - public function dispatch($sqlWalker) + public function dispatch($walker) { - return $sqlWalker->walkArithmeticExpression($this); + return $walker->walkArithmeticExpression($this); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/AST/InputParameter.php b/lib/Doctrine/ORM/Query/AST/InputParameter.php index c750c5119..abc958796 100644 --- a/lib/Doctrine/ORM/Query/AST/InputParameter.php +++ b/lib/Doctrine/ORM/Query/AST/InputParameter.php @@ -48,8 +48,8 @@ class InputParameter extends Node $this->name = $param; } - public function dispatch($sqlWalker) + public function dispatch($walker) { - return $sqlWalker->walkInputParameter($this); + return $walker->walkInputParameter($this); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php index afc02e12a..3b75c5218 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableDeleteExecutor.php @@ -51,6 +51,7 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor { $em = $sqlWalker->getEntityManager(); $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata( $AST->deleteClause->abstractSchemaName @@ -81,8 +82,8 @@ class MultiTableDeleteExecutor extends AbstractSqlExecutor // 3. Create and store DELETE statements $classNames = array_merge($primaryClass->parentClasses, array($primaryClass->name), $primaryClass->subClasses); foreach (array_reverse($classNames) as $className) { - $tableName = $em->getClassMetadata($className)->primaryTable['name']; - $this->_sqlStatements[] = 'DELETE FROM ' . $conn->quoteIdentifier($tableName) + $tableName = $em->getClassMetadata($className)->getQuotedTableName($platform); + $this->_sqlStatements[] = 'DELETE FROM ' . $tableName . ' WHERE (' . $idColumnList . ') IN (' . $idSubselect . ')'; } diff --git a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php index 2e2753621..6b4fcb350 100644 --- a/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php +++ b/lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php @@ -53,6 +53,8 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor { $em = $sqlWalker->getEntityManager(); $conn = $em->getConnection(); + $platform = $conn->getDatabasePlatform(); + $updateClause = $AST->updateClause; $primaryClass = $sqlWalker->getEntityManager()->getClassMetadata($updateClause->abstractSchemaName); @@ -82,8 +84,7 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor foreach (array_reverse($classNames) as $className) { $affected = false; $class = $em->getClassMetadata($className); - $tableName = $class->primaryTable['name']; - $updateSql = 'UPDATE ' . $conn->quoteIdentifier($tableName) . ' SET '; + $updateSql = 'UPDATE ' . $class->getQuotedTableName($platform) . ' SET '; foreach ($updateItems as $updateItem) { $field = $updateItem->field; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 55a4ae27a..4928748e3 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -22,9 +22,7 @@ namespace Doctrine\ORM\Query; use Doctrine\Common\DoctrineException, - Doctrine\ORM\Query, - Doctrine\ORM\Query\AST, - Doctrine\ORM\Query\Exec; + Doctrine\ORM\Query; /** * An LL(*) parser for the context-free grammar of the Doctrine Query Language. @@ -269,6 +267,18 @@ class Parser $treeWalker = new $this->_treeWalker( $this->_query, $this->_parserResult, $this->_queryComponents ); + /*if ($this->_treeWalkers) { + // We got additional walkers, so build a chain. + $treeWalker = new TreeWalkerChain($this->_query, $this->_parserResult, $this->_queryComponents); + foreach ($this->_treeWalkers as $walker) { + $treeWalker->addTreeWalker(new $walker($this->_query, $this->_parserResult, $this->_queryComponents)); + } + $treeWalker->setLastTreeWalker('Doctrine\ORM\Query\SqlWalker'); + } else { + $treeWalker = new SqlWalker( + $this->_query, $this->_parserResult, $this->_queryComponents + ); + }*/ // Assign an SQL executor to the parser result $this->_parserResult->setSqlExecutor($treeWalker->getExecutor($AST)); @@ -594,14 +604,14 @@ class Parser echo '[Query Components: ' . var_export($this->_queryComponents, true) . ']'; $this->semanticalError( - "Could not find '$identVariable' in query components", $token + "'$idVariable' is not defined", $token ); } // Validate if identification variable nesting level is lower or equal than the current one if ($this->_queryComponents[$identVariable]['nestingLevel'] > $nestingLevel) { $this->semanticalError( - "Query component '$identVariable' is not in the same nesting level of its declaration", + "'$idVariable' is used outside the scope of its declaration", $token ); } @@ -1259,7 +1269,7 @@ class Parser // ResultVariable exists in queryComponents, check nesting level if ($queryComponent['nestingLevel'] != $this->_nestingLevel) { $this->semanticalError( - "Query component '$expr' is not in the same nesting level of its declaration" + "'$expr' is used outside the scope of its declaration" ); } } else { diff --git a/lib/Doctrine/ORM/Query/ResultSetMapping.php b/lib/Doctrine/ORM/Query/ResultSetMapping.php index 247de1397..99e3701ea 100644 --- a/lib/Doctrine/ORM/Query/ResultSetMapping.php +++ b/lib/Doctrine/ORM/Query/ResultSetMapping.php @@ -33,8 +33,6 @@ namespace Doctrine\ORM\Query; * * @author Roman Borschel * @since 2.0 - * @todo Do not store AssociationMappings in $relationMap. These bloat serialized instances - * and in turn unserialize performance suffers which is important for most effective caching. */ class ResultSetMapping { @@ -58,8 +56,6 @@ class ResultSetMapping public $discriminatorColumns = array(); /** Maps alias names to field names that should be used for indexing. */ public $indexByMap = array(); - /** A list of columns that should be ignored/skipped during hydration. */ - //public $ignoredColumns = array(); /** * Adds an entity result to this ResultSetMapping. @@ -321,25 +317,5 @@ class ResultSetMapping $this->metaMappings[$columnName] = $fieldName; $this->columnOwnerMap[$columnName] = $alias; } - - /** - * Adds a column name that will be ignored during hydration. - * - * @param string $columnName - */ - /*public function addIgnoredColumn($columnName) - { - $this->ignoredColumns[$columnName] = true; - }*/ - - /** - * - * @param string $columnName - * @return boolean - */ - /*public function isIgnoredColumn($columnName) - { - return isset($this->ignoredColumns[$columnName]); - }*/ } diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 930bdc386..e432eeae3 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -78,6 +78,13 @@ class SqlWalker implements TreeWalker * These should only be generated for SELECT queries. */ private $_useSqlTableAliases = true; + + /** + * The database platform abstraction. + * + * @var AbstractPlatform + */ + private $_platform; /** * @inheritdoc @@ -88,6 +95,7 @@ class SqlWalker implements TreeWalker $this->_query = $query; $this->_em = $query->getEntityManager(); $this->_conn = $this->_em->getConnection(); + $this->_platform = $this->_conn->getDatabasePlatform(); $this->_parserResult = $parserResult; $this->_queryComponents = $queryComponents; } @@ -109,7 +117,7 @@ class SqlWalker implements TreeWalker */ public function getConnection() { - return $this->_em->getConnection(); + return $this->_conn; } /** @@ -123,9 +131,9 @@ class SqlWalker implements TreeWalker } /** - * Gets the Query Component related to the given DQL alias. - * - * @param string $dqlAlias DQL alias + * Gets the information about a single query component. + * + * @param string $dqlAlias The DQL alias. * @return array */ public function getQueryComponent($dqlAlias) @@ -205,7 +213,7 @@ class SqlWalker implements TreeWalker */ public function getSqlColumnAlias($columnName) { - return trim($columnName, '`') . $this->_aliasCounter++; + return $columnName . $this->_aliasCounter++; } /** @@ -221,22 +229,21 @@ class SqlWalker implements TreeWalker $sql = ''; $baseTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); - $idColumns = $class->getIdentifierColumnNames(); // INNER JOIN parent class tables foreach ($class->parentClasses as $parentClassName) { $parentClass = $this->_em->getClassMetadata($parentClassName); $tableAlias = $this->getSqlTableAlias($parentClass->primaryTable['name'], $dqlAlias); - $sql .= ' INNER JOIN ' . $this->_conn->quoteIdentifier($parentClass->primaryTable['name']) + $sql .= ' INNER JOIN ' . $parentClass->getQuotedTableName($this->_platform) . ' ' . $tableAlias . ' ON '; $first = true; - foreach ($idColumns as $idColumn) { + foreach ($class->identifier as $idField) { if ($first) $first = false; else $sql .= ' AND '; - - $sql .= $baseTableAlias . '.' . $this->_conn->quoteIdentifier($idColumn) + $columnName = $class->getQuotedColumnName($idField, $this->_platform); + $sql .= $baseTableAlias . '.' . $columnName . ' = ' - . $tableAlias . '.' . $this->_conn->quoteIdentifier($idColumn); + . $tableAlias . '.' . $columnName; } } @@ -247,12 +254,12 @@ class SqlWalker implements TreeWalker $sql .= ' LEFT JOIN ' . $subClass->primaryTable['name'] . ' ' . $tableAlias . ' ON '; $first = true; - foreach ($idColumns as $idColumn) { + foreach ($class->identifier as $idField) { if ($first) $first = false; else $sql .= ' AND '; - - $sql .= $baseTableAlias . '.' . $this->_conn->quoteIdentifier($idColumn) + $columnName = $class->getQuotedColumnName($idField, $this->_platform); + $sql .= $baseTableAlias . '.' . $columnName . ' = ' - . $tableAlias . '.' . $this->_conn->quoteIdentifier($idColumn); + . $tableAlias . '.' . $columnName; } } @@ -351,6 +358,7 @@ class SqlWalker implements TreeWalker /** * Walks down a SelectClause AST node, thereby generating the appropriate SQL. * + * @param $selectClause * @return string The SQL. */ public function walkSelectClause($selectClause) @@ -358,28 +366,70 @@ class SqlWalker implements TreeWalker $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode( ', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions) ); + + $addMetaColumns = ! $this->_em->getConfiguration()->getAllowPartialObjects() && + ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && + $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT + || + $this->_query->getHydrationMode() != Query::HYDRATE_OBJECT && + $this->_query->getHint(Query::HINT_INCLUDE_META_COLUMNS); - foreach ($this->_selectedClasses as $dqlAlias => $class) { + foreach ($this->_selectedClasses as $dqlAlias => $class) { + // Register as entity or joined entity result if ($this->_queryComponents[$dqlAlias]['relation'] === null) { $this->_rsm->addEntityResult($class->name, $dqlAlias); } else { $this->_rsm->addJoinedEntityResult( $class->name, $dqlAlias, $this->_queryComponents[$dqlAlias]['parent'], - $this->_queryComponents[$dqlAlias]['relation'] + $this->_queryComponents[$dqlAlias]['relation']->sourceFieldName ); } if ($class->isInheritanceTypeSingleTable() || $class->isInheritanceTypeJoined()) { + // Add discriminator columns to SQL $rootClass = $this->_em->getClassMetadata($class->rootEntityName); - $tblAlias = $this->getSqlTableAlias($rootClass->getTableName(), $dqlAlias); + $tblAlias = $this->getSqlTableAlias($rootClass->primaryTable['name'], $dqlAlias); $discrColumn = $rootClass->discriminatorColumn; $columnAlias = $this->getSqlColumnAlias($discrColumn['name']); - $sql .= ", $tblAlias." . $this->_conn->quoteIdentifier($discrColumn['name']) + $sql .= ", $tblAlias." . $rootClass->getQuotedDiscriminatorColumnName($this->_platform) . ' AS ' . $columnAlias; $this->_rsm->setDiscriminatorColumn($dqlAlias, $columnAlias); $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $discrColumn['fieldName']); + + // Add foreign key columns to SQL, if necessary + if ($addMetaColumns) { + foreach ($class->associationMappings as $assoc) { + if ($assoc->isOwningSide && $assoc->isOneToOne()) { + if (isset($class->inheritedAssociationFields[$assoc->sourceFieldName])) { + $owningClass = $this->_em->getClassMetadata($class->inheritedAssociationFields[$assoc->sourceFieldName]); + $sqlTableAlias = $this->getSqlTableAlias($owningClass->primaryTable['name'], $dqlAlias); + } else { + $sqlTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); + } + foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + $columnAlias = $this->getSqlColumnAlias($srcColumn); + $sql .= ', ' . $sqlTableAlias . '.' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform) . ' AS ' . $columnAlias; + $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); + } + } + } + } + } else { + // Add foreign key columns to SQL, if necessary + if ($addMetaColumns) { + $sqlTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); + foreach ($class->associationMappings as $assoc) { + if ($assoc->isOwningSide && $assoc->isOneToOne()) { + foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { + $columnAlias = $this->getSqlColumnAlias($srcColumn); + $sql .= ', ' . $sqlTableAlias . '.' . $assoc->getQuotedJoinColumnName($srcColumn, $this->_platform) . ' AS ' . $columnAlias; + $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); + } + } + } + } } } @@ -402,7 +452,7 @@ class SqlWalker implements TreeWalker $this->_currentRootAlias = $dqlAlias; $class = $rangeDecl->classMetadata; - $sql .= $this->_conn->quoteIdentifier($class->getTableName()) . ' ' + $sql .= $class->getQuotedTableName($this->_platform) . ' ' . $this->getSqlTableAlias($class->getTableName(), $dqlAlias); if ($class->isInheritanceTypeJoined()) { @@ -451,11 +501,11 @@ class SqlWalker implements TreeWalker $expr = $orderByItem->expression; $parts = $expr->parts; $dqlAlias = $expr->identificationVariable; - $qComp = $this->_queryComponents[$dqlAlias]; - $columnName = $qComp['metadata']->getColumnName($parts[0]); + $class = $this->_queryComponents[$dqlAlias]['metadata']; + $columnName = $class->getQuotedColumnName($parts[0], $this->_platform); - return $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' - . $this->_conn->quoteIdentifier($columnName) . ' ' . strtoupper($orderByItem->type); + return $this->getSqlTableAlias($class->getTableName(), $dqlAlias) . '.' + . $columnName . ' ' . strtoupper($orderByItem->type); } /** @@ -492,88 +542,84 @@ class SqlWalker implements TreeWalker $joinAssocPathExpr = $join->joinAssociationPathExpression; $joinedDqlAlias = $join->aliasIdentificationVariable; - $sourceQComp = $this->_queryComponents[$joinAssocPathExpr->identificationVariable]; $targetQComp = $this->_queryComponents[$joinedDqlAlias]; + $targetClass = $targetQComp['metadata']; + $relation = $targetQComp['relation']; + $sourceClass = $this->_queryComponents[$joinAssocPathExpr->identificationVariable]['metadata']; - $targetTableName = $targetQComp['metadata']->getTableName(); - $targetTableAlias = $this->getSqlTableAlias($targetTableName, $joinedDqlAlias); + $targetTableName = $targetClass->getQuotedTableName($this->_platform); + $targetTableAlias = $this->getSqlTableAlias($targetClass->getTableName(), $joinedDqlAlias); $sourceTableAlias = $this->getSqlTableAlias( - $sourceQComp['metadata']->getTableName(), $joinAssocPathExpr->identificationVariable + $sourceClass->getTableName(), $joinAssocPathExpr->identificationVariable ); // Ensure we got the owning side, since it has all mapping info - if ( ! $targetQComp['relation']->isOwningSide()) { - $assoc = $targetQComp['metadata']->getAssociationMapping($targetQComp['relation']->getMappedByFieldName()); + if ( ! $relation->isOwningSide) { + $assoc = $targetClass->associationMappings[$relation->mappedByFieldName]; } else { - $assoc = $targetQComp['relation']; + $assoc = $relation; } if ($assoc->isOneToOne()) { - $sql .= $this->_conn->quoteIdentifier($targetTableName) . ' ' . $targetTableAlias . ' ON '; - $joinColumns = $assoc->getSourceToTargetKeyColumns(); + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; $first = true; - foreach ($joinColumns as $sourceColumn => $targetColumn) { + foreach ($assoc->sourceToTargetKeyColumns as $sourceColumn => $targetColumn) { if ( ! $first) { $sql .= ' AND '; } else { $first = false; } - if ($targetQComp['relation']->isOwningSide()) { - $sql .= $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($sourceColumn) + $quotedSourceColumn = $assoc->getQuotedJoinColumnName($sourceColumn, $this->_platform); + $quotedTargetColumn = $sourceClass->getQuotedColumnName($sourceClass->fieldNames[$targetColumn], $this->_platform); + + if ($relation->isOwningSide) { + $sql .= $sourceTableAlias . '.' . $quotedSourceColumn . ' = ' - . $targetTableAlias . '.' . $this->_conn->quoteIdentifier($targetColumn); + . $targetTableAlias . '.' . $quotedTargetColumn; } else { - $sql .= $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($targetColumn) + $sql .= $sourceTableAlias . '.' . $quotedTargetColumn . ' = ' - . $targetTableAlias . '.' . $this->_conn->quoteIdentifier($sourceColumn); + . $targetTableAlias . '.' . $quotedSourceColumn; } } } else if ($assoc->isManyToMany()) { // Join relation table $joinTable = $assoc->getJoinTable(); $joinTableAlias = $this->getSqlTableAlias($joinTable['name']); - $sql .= $this->_conn->quoteIdentifier($joinTable['name']) . ' ' . $joinTableAlias . ' ON '; + $sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' ON '; - if ($targetQComp['relation']->isOwningSide) { - $sourceToRelationJoinColumns = $assoc->getSourceToRelationKeyColumns(); - - foreach ($sourceToRelationJoinColumns as $sourceColumn => $relationColumn) { - $sql .= $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($sourceColumn) + if ($relation->isOwningSide) { + foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) { + $sql .= $sourceTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceColumn, $this->_platform) . ' = ' - . $joinTableAlias . '.' . $this->_conn->quoteIdentifier($relationColumn); + . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); } } else { - $targetToRelationJoinColumns = $assoc->getTargetToRelationKeyColumns(); - - foreach ($targetToRelationJoinColumns as $targetColumn => $relationColumn) { - $sql .= $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($targetColumn) + foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) { + $sql .= $sourceTableAlias . '.' . $targetClass->getQuotedColumnName($targetColumn, $this->_platform) . ' = ' - . $joinTableAlias . '.' . $this->_conn->quoteIdentifier($relationColumn); + . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); } } // Join target table $sql .= ($joinType == AST\Join::JOIN_TYPE_LEFT || $joinType == AST\Join::JOIN_TYPE_LEFTOUTER) ? ' LEFT JOIN ' : ' INNER JOIN '; - $sql .= $this->_conn->quoteIdentifier($targetTableName) . ' ' . $targetTableAlias . ' ON '; + $sql .= $targetTableName . ' ' . $targetTableAlias . ' ON '; - if ($targetQComp['relation']->isOwningSide) { - $targetToRelationJoinColumns = $assoc->getTargetToRelationKeyColumns(); - - foreach ($targetToRelationJoinColumns as $targetColumn => $relationColumn) { - $sql .= $targetTableAlias . '.' . $this->_conn->quoteIdentifier($targetColumn) + if ($relation->isOwningSide) { + foreach ($assoc->targetToRelationKeyColumns as $targetColumn => $relationColumn) { + $sql .= $targetTableAlias . '.' . $targetClass->getQuotedColumnName($targetColumn, $this->_platform) . ' = ' - . $joinTableAlias . '.' . $this->_conn->quoteIdentifier($relationColumn); + . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); } } else { - $sourceToRelationJoinColumns = $assoc->getSourceToRelationKeyColumns(); - - foreach ($sourceToRelationJoinColumns as $sourceColumn => $relationColumn) { - $sql .= $targetTableAlias . '.' . $this->_conn->quoteIdentifier($sourceColumn) + foreach ($assoc->sourceToRelationKeyColumns as $sourceColumn => $relationColumn) { + $sql .= $targetTableAlias . '.' . $sourceClass->getQuotedColumnName($sourceColumn, $this->_platform) . ' = ' - . $joinTableAlias . '.' . $this->_conn->quoteIdentifier($relationColumn); + . $joinTableAlias . '.' . $assoc->getQuotedJoinColumnName($relationColumn, $this->_platform); } } } @@ -584,8 +630,8 @@ class SqlWalker implements TreeWalker $sql .= ' AND ' . $discrSql; } - if ($targetQComp['metadata']->isInheritanceTypeJoined()) { - $sql .= $this->_generateClassTableInheritanceJoins($targetQComp['metadata'], $joinedDqlAlias); + if ($targetClass->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($targetClass, $joinedDqlAlias); } return $sql; @@ -616,10 +662,9 @@ class SqlWalker implements TreeWalker } $sqlTableAlias = $this->getSqlTableAlias($class->getTableName(), $dqlAlias); - $columnName = $class->getColumnName($fieldName); - $columnAlias = $this->getSqlColumnAlias($columnName); - $sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($columnName) - . ' AS ' . $columnAlias; + $columnName = $class->getQuotedColumnName($fieldName, $this->_platform); + $columnAlias = $this->getSqlColumnAlias($class->columnNames[$fieldName]); + $sql .= $sqlTableAlias . '.' . $columnName . ' AS ' . $columnAlias; $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); } else { @@ -662,88 +707,42 @@ class SqlWalker implements TreeWalker } $beginning = true; - if ($class->isInheritanceTypeJoined()) { - // Select all fields from the queried class - foreach ($class->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited'])) { - $tableName = $this->_em->getClassMetadata($mapping['inherited'])->primaryTable['name']; - } else { - $tableName = $class->primaryTable['name']; - } - - if ($beginning) $beginning = false; else $sql .= ', '; - - $sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias); - $columnAlias = $this->getSqlColumnAlias($mapping['columnName']); - $sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) - . ' AS ' . $columnAlias; - - $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); + // Select all fields from the queried class + foreach ($class->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + $tableName = $this->_em->getClassMetadata($mapping['inherited'])->primaryTable['name']; + } else { + $tableName = $class->primaryTable['name']; } + + if ($beginning) $beginning = false; else $sql .= ', '; + + $sqlTableAlias = $this->getSqlTableAlias($tableName, $dqlAlias); + $columnAlias = $this->getSqlColumnAlias($mapping['columnName']); + $sql .= $sqlTableAlias . '.' . $class->getQuotedColumnName($fieldName, $this->_platform) + . ' AS ' . $columnAlias; + + $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); + } - // Add any additional fields of subclasses (not inherited fields) - foreach ($class->subClasses as $subClassName) { - $subClass = $this->_em->getClassMetadata($subClassName); - - foreach ($subClass->fieldMappings as $fieldName => $mapping) { - if (isset($mapping['inherited'])) { - continue; - } - - if ($beginning) $beginning = false; else $sql .= ', '; - - $sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias); - $columnAlias = $this->getSqlColumnAlias($mapping['columnName']); - $sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) - . ' AS ' . $columnAlias; - - $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); + // Add any additional fields of subclasses (not inherited fields) + foreach ($class->subClasses as $subClassName) { + $subClass = $this->_em->getClassMetadata($subClassName); + + foreach ($subClass->fieldMappings as $fieldName => $mapping) { + if (isset($mapping['inherited'])) { + continue; } - } - } else { - $fieldMappings = $class->fieldMappings; - - foreach ($class->subClasses as $subclassName) { - $fieldMappings = array_merge( - $fieldMappings, - $this->_em->getClassMetadata($subclassName)->fieldMappings - ); - } - - $sqlTableAlias = $this->getSqlTableAlias($class->getTableName(), $dqlAlias); - - foreach ($fieldMappings as $fieldName => $mapping) { + if ($beginning) $beginning = false; else $sql .= ', '; + $sqlTableAlias = $this->getSqlTableAlias($subClass->primaryTable['name'], $dqlAlias); $columnAlias = $this->getSqlColumnAlias($mapping['columnName']); - $sql .= $sqlTableAlias . '.' . $this->_conn->quoteIdentifier($mapping['columnName']) + $sql .= $sqlTableAlias . '.' . $subClass->getQuotedColumnName($fieldName, $this->_platform) . ' AS ' . $columnAlias; $this->_rsm->addFieldResult($dqlAlias, $columnAlias, $fieldName); } - - // Append foreign keys if necessary. - //FIXME: Evaluate HINT_INCLUDE_META_COLUMNS - //FIXME: Needs to be done in the case of Class Table Inheritance, too - // (see upper block of the if/else) - if ( - ! $this->_em->getConfiguration()->getAllowPartialObjects() && - ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) - ) { - foreach ($class->associationMappings as $assoc) { - if ($assoc->isOwningSide && $assoc->isOneToOne()) { - foreach ($assoc->targetToSourceKeyColumns as $srcColumn) { - $columnAlias = $this->getSqlColumnAlias($srcColumn); - $sql .= ', ' . $sqlTableAlias . '.' - . $this->_conn->quoteIdentifier($srcColumn) - . ' AS ' . $columnAlias; - - $this->_rsm->addMetaResult($dqlAlias, $columnAlias, $srcColumn); - } - } - } - } - } } @@ -798,7 +797,7 @@ class SqlWalker implements TreeWalker $rangeDecl = $firstIdentificationVarDecl->rangeVariableDeclaration; $tblName = $rangeDecl->classMetadata->getTableName(); - $sql = ' FROM ' . $this->_conn->quoteIdentifier($tblName) . ' ' + $sql = ' FROM ' . $rangeDecl->classMetadata->getQuotedTableName($this->_platform) . ' ' . $this->getSqlTableAlias($tblName, $rangeDecl->aliasIdentificationVariable); foreach ($firstIdentificationVarDecl->joinVariableDeclarations as $joinVarDecl) { @@ -848,7 +847,7 @@ class SqlWalker implements TreeWalker // in a subquery? $class = $this->_queryComponents[$expr]['metadata']; $sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.' - . $this->_conn->quoteIdentifier($class->getColumnName($class->identifier[0])); + . $class->getQuotedColumnName($class->identifier[0], $this->_platform); } return $sql; @@ -868,11 +867,11 @@ class SqlWalker implements TreeWalker $fieldName = $parts[0]; $qComp = $this->_queryComponents[$dqlAlias]; - $columnName = $qComp['metadata']->getColumnName($fieldName); + $columnName = $qComp['metadata']->getQuotedColumnName($fieldName, $this->_platform); return $aggExpression->functionName . '(' . ($aggExpression->isDistinct ? 'DISTINCT ' : '') . $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' - . $this->_conn->quoteIdentifier($columnName) . ')'; + . $columnName . ')'; } /** @@ -899,10 +898,9 @@ class SqlWalker implements TreeWalker $parts = $pathExpr->parts; $dqlAlias = $pathExpr->identificationVariable; $qComp = $this->_queryComponents[$dqlAlias]; - $columnName = $qComp['metadata']->getColumnName($parts[0]); + $columnName = $qComp['metadata']->getQuotedColumnName($parts[0], $this->_platform); - return $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' - . $this->_conn->quoteIdentifier($columnName); + return $this->getSqlTableAlias($qComp['metadata']->getTableName(), $dqlAlias) . '.' . $columnName; } /** @@ -915,7 +913,7 @@ class SqlWalker implements TreeWalker { $sql = 'DELETE FROM '; $class = $this->_em->getClassMetadata($deleteClause->abstractSchemaName); - $sql .= $this->_conn->quoteIdentifier($class->getTableName()); + $sql .= $class->getQuotedTableName($this->_platform); if ($this->_useSqlTableAliases) { $sql .= ' ' . $this->getSqlTableAlias($class->getTableName()); @@ -936,7 +934,7 @@ class SqlWalker implements TreeWalker { $sql = 'UPDATE '; $class = $this->_em->getClassMetadata($updateClause->abstractSchemaName); - $sql .= $this->_conn->quoteIdentifier($class->getTableName()); + $sql .= $class->getQuotedTableName($this->_platform); if ($this->_useSqlTableAliases) { $sql .= ' ' . $this->getSqlTableAlias($class->getTableName()); @@ -970,7 +968,7 @@ class SqlWalker implements TreeWalker $sql .= $this->getSqlTableAlias($qComp['metadata']->getTableName()) . '.'; } - $sql .= $this->_conn->quoteIdentifier($qComp['metadata']->getColumnName($updateItem->field)) . ' = '; + $sql .= $qComp['metadata']->getQuotedColumnName($updateItem->field, $this->_platform) . ' = '; $newValue = $updateItem->newValue; @@ -1041,7 +1039,8 @@ class SqlWalker implements TreeWalker $sql .= $this->getSqlTableAlias($class->getTableName(), $dqlAlias) . '.'; } - $sql .= $this->_conn->quoteIdentifier($discrColumn['name']) . ' IN (' . implode(', ', $values) . ')'; + $sql .= $class->getQuotedDiscriminatorColumnName($this->_platform) . + ' IN (' . implode(', ', $values) . ')'; } } @@ -1133,7 +1132,7 @@ class SqlWalker implements TreeWalker $targetTableAlias = $this->getSqlTableAlias($targetClass->primaryTable['name']); $sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); - $sql .= $this->_conn->quoteIdentifier($targetClass->primaryTable['name']) + $sql .= $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' WHERE '; $owningAssoc = $targetClass->associationMappings[$assoc->mappedByFieldName]; @@ -1143,9 +1142,9 @@ class SqlWalker implements TreeWalker foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { if ($first) $first = false; else $sql .= ' AND '; - $sql .= $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($targetColumn) + $sql .= $sourceTableAlias . '.' . $class->getQuotedColumnName($targetColumn, $this->_platform) . ' = ' - . $targetTableAlias . '.' . $this->_conn->quoteIdentifier($sourceColumn); + . $targetTableAlias . '.' . $owningAssoc->getQuotedJoinColumnName($sourceColumn, $this->_platform); } $sql .= ' AND '; @@ -1156,35 +1155,38 @@ class SqlWalker implements TreeWalker $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); $sql .= $targetTableAlias . '.' - . $this->_conn->quoteIdentifier($targetClass->columnNames[$idField]) . ' = ?'; + . $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?'; } } else { // many-to-many $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); - $sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); - $joinTable = $assoc->isOwningSide - ? $assoc->joinTable - : $targetClass->associationMappings[$assoc->mappedByFieldName]->joinTable; + + $owningAssoc = $assoc->isOwningSide ? $assoc : $targetClass->associationMappings[$assoc->mappedByFieldName]; + $joinTable = $assoc->isOwningSide ? $assoc->joinTable : $owningAssoc->joinTable; + + // SQL table aliases $joinTableAlias = $this->getSqlTableAlias($joinTable['name']); $targetTableAlias = $this->getSqlTableAlias($targetClass->primaryTable['name']); + $sourceTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); // join to target table - $sql .= $this->_conn->quoteIdentifier($joinTable['name']) + $sql .= $assoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' INNER JOIN ' - . $this->_conn->quoteIdentifier($targetClass->primaryTable['name']) + . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON '; // join conditions $joinColumns = $assoc->isOwningSide ? $joinTable['joinColumns'] : $joinTable['inverseJoinColumns']; - $first = true; + $referencedColumnClass = $assoc->isOwningSide ? $class : $targetClass; + $first = true; foreach ($joinColumns as $joinColumn) { if ($first) $first = false; else $sql .= ' AND '; - - $sql .= $joinTableAlias . '.' . $this->_conn->quoteIdentifier($joinColumn['name']) - . ' = ' - . $sourceTableAlias . '.' . $this->_conn->quoteIdentifier($joinColumn['referencedColumnName']); + + $sql .= $joinTableAlias . '.' . $owningAssoc->getQuotedJoinColumnName($joinColumn['name'], $this->_platform) + . ' = ' + . $sourceTableAlias . '.' . $referencedColumnClass->getQuotedColumnName($joinColumn['referencedColumnName'], $this->_platform); } $sql .= ' WHERE '; @@ -1192,14 +1194,14 @@ class SqlWalker implements TreeWalker $joinColumns = $assoc->isOwningSide ? $joinTable['inverseJoinColumns'] : $joinTable['joinColumns']; - $first = true; + $first = true; foreach ($joinColumns as $joinColumn) { if ($first) $first = false; else $sql .= ' AND '; - $sql .= $joinTableAlias . '.' . $this->_conn->quoteIdentifier($joinColumn['name']) + $sql .= $joinTableAlias . '.' . $owningAssoc->getQuotedJoinColumnName($joinColumn['name'], $this->_platform) . ' = ' - . $targetTableAlias . '.' . $this->_conn->quoteIdentifier($joinColumn['referencedColumnName']); + . $targetTableAlias . '.' . $referencedColumnClass->getQuotedColumnName($joinColumn['referencedColumnName'], $this->_platform); } $sql .= ' AND '; @@ -1210,7 +1212,7 @@ class SqlWalker implements TreeWalker $this->_parserResult->addParameterMapping($dqlParamKey, $this->_sqlParamIndex++); $sql .= $targetTableAlias . '.' - . $this->_conn->quoteIdentifier($targetClass->columnNames[$idField]) . ' = ?'; + . $targetClass->getQuotedColumnName($idField, $this->_platform) . ' = ?'; } } @@ -1503,11 +1505,10 @@ class SqlWalker implements TreeWalker if (isset($class->associationMappings[$fieldName])) { //FIXME: Inverse side support //FIXME: Throw exception on composite key - $sql .= $this->_conn->quoteIdentifier( - $class->associationMappings[$fieldName]->joinColumns[0]['name'] - ); + $assoc = $class->associationMappings[$fieldName]; + $sql .= $assoc->getQuotedJoinColumnName($assoc->joinColumns[0]['name'], $this->_platform); } else { - $sql .= $this->_conn->quoteIdentifier($class->getColumnName($fieldName)); + $sql .= $class->getQuotedColumnName($fieldName, $this->_platform); } } else if ($pathExprType == AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) { throw DoctrineException::updateMe("Not yet implemented."); diff --git a/lib/Doctrine/ORM/Tools/SchemaTool.php b/lib/Doctrine/ORM/Tools/SchemaTool.php index c9885467e..ae43a5332 100644 --- a/lib/Doctrine/ORM/Tools/SchemaTool.php +++ b/lib/Doctrine/ORM/Tools/SchemaTool.php @@ -113,7 +113,8 @@ class SchemaTool // Add all non-inherited fields as columns foreach ($class->fieldMappings as $fieldName => $mapping) { if ( ! isset($mapping['inherited'])) { - $columns[$mapping['columnName']] = $this->_gatherColumn($class, $mapping, $options); + $columnName = $class->getQuotedColumnName($mapping['columnName'], $this->_platform); + $columns[$columnName] = $this->_gatherColumn($class, $mapping, $options); } } @@ -128,17 +129,16 @@ class SchemaTool $idMapping = $class->fieldMappings[$class->identifier[0]]; $idColumn = $this->_gatherColumn($class, $idMapping, $options); unset($idColumn['autoincrement']); - $columns[$idMapping['columnName']] = $idColumn; + $columns[$idColumn['name']] = $idColumn; // Add a FK constraint on the ID column $constraint = array(); - $constraint['tableName'] = $class->getTableName(); - $constraint['foreignTable'] = $this->_em->getClassMetadata($class->rootEntityName)->getTableName(); - $constraint['local'] = array($idMapping['columnName']); - $constraint['foreign'] = array($idMapping['columnName']); + $constraint['tableName'] = $class->getQuotedTableName($this->_platform); + $constraint['foreignTable'] = $this->_em->getClassMetadata($class->rootEntityName)->getQuotedTableName($this->_platform); + $constraint['local'] = array($idColumn['name']); + $constraint['foreign'] = array($idColumn['name']); $constraint['onDelete'] = 'CASCADE'; $foreignKeyConstraints[] = $constraint; } - } else if ($class->isInheritanceTypeTablePerClass()) { //TODO } else { @@ -146,7 +146,8 @@ class SchemaTool $this->_gatherRelationsSql($class, $sql, $columns, $foreignKeyConstraints); } - $sql = array_merge($sql, $this->_platform->getCreateTableSql($class->getTableName(), $columns, $options)); + $sql = array_merge($sql, $this->_platform->getCreateTableSql( + $class->getQuotedTableName($this->_platform), $columns, $options)); $processedClasses[$class->name] = true; if ($class->isIdGeneratorSequence() && $class->name == $class->rootEntityName) { @@ -162,7 +163,7 @@ class SchemaTool // Append the foreign key constraints SQL if ($this->_platform->supportsForeignKeyConstraints()) { foreach ($foreignKeyConstraints as $fkConstraint) { - $sql = array_merge($sql, (array)$this->_platform->getCreateForeignKeySql($fkConstraint['tableName'], $fkConstraint)); + $sql = array_merge($sql, (array) $this->_platform->getCreateForeignKeySql($fkConstraint['tableName'], $fkConstraint)); } } @@ -176,7 +177,7 @@ class SchemaTool { $discrColumn = $class->discriminatorColumn; return array( - 'name' => $discrColumn['name'], + 'name' => $class->getQuotedDiscriminatorColumnName($this->_platform), 'type' => Type::getType($discrColumn['type']), 'length' => $discrColumn['length'], 'notnull' => true @@ -194,7 +195,8 @@ class SchemaTool { $columns = array(); foreach ($class->fieldMappings as $fieldName => $mapping) { - $columns[$mapping['columnName']] = $this->_gatherColumn($class, $mapping, $options); + $column = $this->_gatherColumn($class, $mapping, $options); + $columns[$column['name']] = $column; } return $columns; @@ -203,7 +205,7 @@ class SchemaTool private function _gatherColumn($class, array $mapping, array &$options) { $column = array(); - $column['name'] = $mapping['columnName']; + $column['name'] = $class->getQuotedColumnName($mapping['columnName'], $this->_platform); $column['type'] = Type::getType($mapping['type']); $column['length'] = $mapping['length']; $column['notnull'] = ! $mapping['nullable']; @@ -231,16 +233,16 @@ class SchemaTool $foreignClass = $this->_em->getClassMetadata($mapping->targetEntityName); if ($mapping->isOneToOne() && $mapping->isOwningSide) { $constraint = array(); - $constraint['tableName'] = $class->getTableName(); - $constraint['foreignTable'] = $foreignClass->getTableName(); + $constraint['tableName'] = $class->getQuotedTableName($this->_platform); + $constraint['foreignTable'] = $foreignClass->getQuotedTableName($this->_platform); $constraint['local'] = array(); $constraint['foreign'] = array(); foreach ($mapping->getJoinColumns() as $joinColumn) { $column = array(); - $column['name'] = $joinColumn['name']; + $column['name'] = $mapping->getQuotedJoinColumnName($joinColumn['name'], $this->_platform); $column['type'] = Type::getType($foreignClass->getTypeOfColumn($joinColumn['referencedColumnName'])); - $columns[$joinColumn['name']] = $column; - $constraint['local'][] = $joinColumn['name']; + $columns[$column['name']] = $column; + $constraint['local'][] = $column['name']; $constraint['foreign'][] = $joinColumn['referencedColumnName']; } $constraints[] = $constraint; @@ -253,8 +255,8 @@ class SchemaTool $joinTableOptions = array(); $joinTable = $mapping->getJoinTable(); $constraint1 = array( - 'tableName' => $joinTable['name'], - 'foreignTable' => $class->getTableName(), + 'tableName' => $mapping->getQuotedJoinTableName($this->_platform), + 'foreignTable' => $class->getQuotedTableName($this->_platform), 'local' => array(), 'foreign' => array() ); @@ -262,17 +264,17 @@ class SchemaTool $column = array(); $column['primary'] = true; $joinTableOptions['primary'][] = $joinColumn['name']; - $column['name'] = $joinColumn['name']; + $column['name'] = $mapping->getQuotedJoinColumnName($joinColumn['name'], $this->_platform); $column['type'] = Type::getType($class->getTypeOfColumn($joinColumn['referencedColumnName'])); - $joinTableColumns[$joinColumn['name']] = $column; - $constraint1['local'][] = $joinColumn['name']; + $joinTableColumns[$column['name']] = $column; + $constraint1['local'][] = $column['name']; $constraint1['foreign'][] = $joinColumn['referencedColumnName']; } $constraints[] = $constraint1; $constraint2 = array(); - $constraint2['tableName'] = $joinTable['name']; - $constraint2['foreignTable'] = $foreignClass->getTableName(); + $constraint2['tableName'] = $mapping->getQuotedJoinTableName($this->_platform); + $constraint2['foreignTable'] = $foreignClass->getQuotedTableName($this->_platform); $constraint2['local'] = array(); $constraint2['foreign'] = array(); foreach ($joinTable['inverseJoinColumns'] as $inverseJoinColumn) { @@ -289,7 +291,8 @@ class SchemaTool $constraints[] = $constraint2; $sql = array_merge($sql, $this->_platform->getCreateTableSql( - $joinTable['name'], $joinTableColumns, $joinTableOptions)); + $mapping->getQuotedJoinTableName($this->_platform), $joinTableColumns, $joinTableOptions) + ); } } } diff --git a/lib/Doctrine/ORM/UnitOfWork.php b/lib/Doctrine/ORM/UnitOfWork.php index 7975f14bf..8ac85a308 100644 --- a/lib/Doctrine/ORM/UnitOfWork.php +++ b/lib/Doctrine/ORM/UnitOfWork.php @@ -1924,4 +1924,23 @@ class UnitOfWork implements PropertyChangedListener $this->_entityUpdates[$oid] = $entity; } } + + public function dump() + { + var_dump($this->_identityMap); + var_dump($this->_entityIdentifiers); + var_dump($this->_originalEntityData); + var_dump($this->_entityChangeSets); + var_dump($this->_entityStates); + var_dump($this->_scheduledForDirtyCheck); + var_dump($this->_entityInsertions); + var_dump($this->_entityUpdates); + var_dump($this->_entityDeletions); + var_dump($this->_collectionDeletions); + //$this->_collectionCreations = + var_dump($this->_collectionUpdates); + var_dump($this->_orphanRemovals); + //var_dump($this->_commitOrderCalculator->clear(); + + } } diff --git a/tests/Doctrine/Tests/DBAL/Platforms/MsSqlPlatformTest.php b/tests/Doctrine/Tests/DBAL/Platforms/MsSqlPlatformTest.php index 390c90223..78cf12b1b 100644 --- a/tests/Doctrine/Tests/DBAL/Platforms/MsSqlPlatformTest.php +++ b/tests/Doctrine/Tests/DBAL/Platforms/MsSqlPlatformTest.php @@ -60,7 +60,7 @@ class MsSqlPlatformTest extends \Doctrine\Tests\DbalTestCase public function testGeneratesSqlSnippets() { $this->assertEquals('RLIKE', $this->_platform->getRegexpExpression(), 'Regular expression operator is not correct'); - $this->assertEquals('`', $this->_platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct'); + $this->assertEquals('"', $this->_platform->getIdentifierQuoteCharacter(), 'Identifier quote character is not correct'); $this->assertEquals('RAND()', $this->_platform->getRandomExpression(), 'Random function is not correct'); $this->assertEquals('(column1 + column2 + column3)', $this->_platform->getConcatExpression('column1', 'column2', 'column3'), 'Concatenation expression is not correct'); $this->assertEquals('CHARACTER SET utf8', $this->_platform->getCharsetFieldDeclaration('utf8'), 'Charset declaration is not correct'); diff --git a/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php b/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php index 9f9e09311..8fc708d87 100644 --- a/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/StandardEntityPersisterTest.php @@ -40,30 +40,4 @@ class StandardEntityPersisterTest extends \Doctrine\Tests\OrmFunctionalTestCase $persister->load(array('customer_id' => $customer->getId()), $newCart); $this->assertEquals('Credit card', $newCart->getPayment()); } - - public function testAcceptsJoinTableAsCriteria() - { - $this->_em->getConfiguration()->setAllowPartialObjects(false); - - $cart = new ECommerceCart(); - $product = new ECommerceProduct(); - $product->setName('Star Wars: A New Hope'); - $cart->addProduct($product); - $this->_em->persist($cart); - $this->_em->flush(); - $this->_em->clear(); - unset($product); - - $persister = $this->_em->getUnitOfWork()->getEntityPersister('Doctrine\Tests\Models\ECommerce\ECommerceProduct'); - $newProduct = new ECommerceProduct(); - $criteria = array( - array( - 'table' => 'ecommerce_carts_products', - 'join' => array('id' => 'product_id'), - 'criteria' => array('cart_id' => $cart->getId()) - ) - ); - $persister->load($criteria, $newProduct); - $this->assertEquals('Star Wars: A New Hope', $newProduct->getName()); - } } diff --git a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php index c5894ea0e..1fe79610c 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ArrayHydratorTest.php @@ -104,11 +104,8 @@ class ArrayHydratorTest extends HydrationTestCase $rsm = new ResultSetMapping; $rsm->addEntityResult('Doctrine\Tests\Models\CMS\CmsUser', 'u'); $rsm->addJoinedEntityResult( - 'Doctrine\Tests\Models\CMS\CmsPhonenumber', - 'p', - 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') - ); + 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', + 'u', 'phonenumbers'); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); $rsm->addScalarResult('sclr0', 'nameUpper'); @@ -219,7 +216,7 @@ class ArrayHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -294,13 +291,13 @@ class ArrayHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsArticle', 'a', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('articles') + 'articles' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -414,19 +411,19 @@ class ArrayHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsArticle', 'a', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('articles') + 'articles' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsComment', 'c', 'a', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsArticle')->getAssociationMapping('comments') + 'comments' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -571,7 +568,7 @@ class ArrayHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\Forum\ForumBoard', 'b', 'c', - $this->_em->getClassMetadata('Doctrine\Tests\Models\Forum\ForumCategory')->getAssociationMapping('boards') + 'boards' ); $rsm->addFieldResult('c', 'c__id', 'id'); $rsm->addFieldResult('c', 'c__position', 'position'); diff --git a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php index f1fd95627..c2f2a9639 100644 --- a/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php +++ b/tests/Doctrine/Tests/ORM/Hydration/ObjectHydratorTest.php @@ -159,7 +159,7 @@ class ObjectHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -280,7 +280,7 @@ class ObjectHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -359,13 +359,13 @@ class ObjectHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsArticle', 'a', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('articles') + 'articles' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -474,19 +474,19 @@ class ObjectHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsArticle', 'a', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('articles') + 'articles' ); $rsm->addJoinedEntityResult( 'Doctrine\Tests\Models\CMS\CmsComment', 'c', 'a', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsArticle')->getAssociationMapping('comments') + 'comments' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -625,7 +625,7 @@ class ObjectHydratorTest extends HydrationTestCase 'Doctrine\Tests\Models\Forum\ForumBoard', 'b', 'c', - $this->_em->getClassMetadata('Doctrine\Tests\Models\Forum\ForumCategory')->getAssociationMapping('boards') + 'boards' ); $rsm->addFieldResult('c', 'c__id', 'id'); $rsm->addFieldResult('c', 'c__position', 'position'); diff --git a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php index 9d01b88b7..8a6469c16 100644 --- a/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php +++ b/tests/Doctrine/Tests/ORM/Performance/HydrationPerformanceTest.php @@ -90,7 +90,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); @@ -218,7 +218,7 @@ class HydrationPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase 'Doctrine\Tests\Models\CMS\CmsPhonenumber', 'p', 'u', - $this->_em->getClassMetadata('Doctrine\Tests\Models\CMS\CmsUser')->getAssociationMapping('phonenumbers') + 'phonenumbers' ); $rsm->addFieldResult('u', 'u__id', 'id'); $rsm->addFieldResult('u', 'u__status', 'status'); diff --git a/tests/Doctrine/Tests/ORM/Performance/InsertPerformanceTest.php b/tests/Doctrine/Tests/ORM/Performance/InsertPerformanceTest.php index 2189b4620..97466e69f 100644 --- a/tests/Doctrine/Tests/ORM/Performance/InsertPerformanceTest.php +++ b/tests/Doctrine/Tests/ORM/Performance/InsertPerformanceTest.php @@ -29,8 +29,8 @@ class InsertPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase $this->setMaxRunningTime(10); - //$mem = memory_get_usage(); - //echo "Memory usage before: " . ($mem / 1024) . " KB" . PHP_EOL; + //echo "Memory usage before: " . (memory_get_usage() / 1024) . " KB" . PHP_EOL; + $batchSize = 20; for ($i=1; $i<=10000; ++$i) { $user = new CmsUser; @@ -43,12 +43,13 @@ class InsertPerformanceTest extends \Doctrine\Tests\OrmPerformanceTestCase $this->_em->clear(); } } - //$memAfter = memory_get_usage(); - //echo "Memory usage after: " . ($memAfter / 1024) . " KB" . PHP_EOL; + + //gc_collect_cycles(); + //echo "Memory usage after: " . (memory_get_usage() / 1024) . " KB" . PHP_EOL; $e = microtime(true); - echo ' Inserted 10000 objects in ' . ($e - $s) . ' seconds' . PHP_EOL; + echo ' Inserted 10000 objects in ' . ($e - $s) . ' seconds' . PHP_EOL; } }