Fix persistence exception on a table with a schema on a platform without schema support
This commit is contained in:
parent
c571f6f6a9
commit
54b3c0548d
@ -576,9 +576,11 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
// Platforms that do not have native IDENTITY support need a sequence to emulate this behaviour.
|
||||
if ($this->targetPlatform->usesSequenceEmulatedIdentityColumns()) {
|
||||
$columnName = $class->getSingleIdentifierColumnName();
|
||||
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
|
||||
$sequenceName = $this->targetPlatform->getIdentitySequenceName($class->getTableName(), $columnName);
|
||||
$columnName = $class->getSingleIdentifierColumnName();
|
||||
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
|
||||
$sequencePrefix = $class->getSequencePrefix($this->targetPlatform);
|
||||
|
||||
$sequenceName = $this->targetPlatform->getIdentitySequenceName($sequencePrefix, $columnName);
|
||||
$definition = array(
|
||||
'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName)
|
||||
);
|
||||
@ -608,10 +610,10 @@ class ClassMetadataFactory extends AbstractClassMetadataFactory
|
||||
|
||||
if ( ! $definition) {
|
||||
$fieldName = $class->getSingleIdentifierFieldName();
|
||||
$columnName = $class->getSingleIdentifierColumnName();
|
||||
$sequenceName = $class->getSequenceName($this->targetPlatform);
|
||||
$quoted = isset($class->fieldMappings[$fieldName]['quoted']) || isset($class->table['quoted']);
|
||||
$sequenceName = $class->getTableName() . '_' . $columnName . '_seq';
|
||||
$definition = array(
|
||||
|
||||
$definition = array(
|
||||
'sequenceName' => $this->targetPlatform->fixSchemaElementName($sequenceName),
|
||||
'allocationSize' => 1,
|
||||
'initialValue' => 1,
|
||||
|
@ -24,6 +24,7 @@ use Doctrine\Instantiator\Instantiator;
|
||||
use InvalidArgumentException;
|
||||
use RuntimeException;
|
||||
use Doctrine\DBAL\Types\Type;
|
||||
use Doctrine\DBAL\Platforms\AbstractPlatform;
|
||||
use ReflectionClass;
|
||||
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
|
||||
use Doctrine\Common\ClassLoader;
|
||||
@ -2014,6 +2015,16 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
return $this->table['name'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets primary table's schema name.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getSchemaName()
|
||||
{
|
||||
return isset($this->table['schema']) ? $this->table['schema'] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the table name to use for temporary identifier tables of this class.
|
||||
*
|
||||
@ -2243,6 +2254,10 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
$this->table['name'] = $table['name'];
|
||||
}
|
||||
|
||||
if (isset($table['schema'])) {
|
||||
$this->table['schema'] = $table['schema'];
|
||||
}
|
||||
|
||||
if (isset($table['indexes'])) {
|
||||
$this->table['indexes'] = $table['indexes'];
|
||||
}
|
||||
@ -3240,4 +3255,47 @@ class ClassMetadataInfo implements ClassMetadata
|
||||
throw MappingException::duplicateFieldMapping($this->name, $fieldName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sequence name based on class metadata.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*
|
||||
* @todo Sequence names should be computed in DBAL depending on the platform
|
||||
*/
|
||||
public function getSequenceName(AbstractPlatform $platform)
|
||||
{
|
||||
$sequencePrefix = $this->getSequencePrefix($platform);
|
||||
|
||||
$columnName = $this->getSingleIdentifierColumnName();
|
||||
$sequenceName = $sequencePrefix . '_' . $columnName . '_seq';
|
||||
|
||||
return $sequenceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the sequence name prefix based on class metadata.
|
||||
*
|
||||
* @param AbstractPlatform $platform
|
||||
* @return string
|
||||
*
|
||||
* @todo Sequence names should be computed in DBAL depending on the platform
|
||||
*/
|
||||
public function getSequencePrefix(AbstractPlatform $platform)
|
||||
{
|
||||
$tableName = $this->getTableName();
|
||||
$sequencePrefix = $tableName;
|
||||
|
||||
// Prepend the schema name to the table name if there is one
|
||||
if ($schemaName = $this->getSchemaName()) {
|
||||
$sequencePrefix = $schemaName . '.' . $tableName;
|
||||
|
||||
if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
|
||||
$sequencePrefix = $schemaName . '__' . $tableName;
|
||||
}
|
||||
}
|
||||
|
||||
return $sequencePrefix;
|
||||
}
|
||||
}
|
||||
|
@ -41,12 +41,24 @@ class DefaultQuoteStrategy implements QuoteStrategy
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @todo Table names should be computed in DBAL depending on the platform
|
||||
*/
|
||||
public function getTableName(ClassMetadata $class, AbstractPlatform $platform)
|
||||
{
|
||||
return isset($class->table['quoted'])
|
||||
? $platform->quoteIdentifier($class->table['name'])
|
||||
: $class->table['name'];
|
||||
$tableName = $class->table['name'];
|
||||
|
||||
if ( ! empty($class->table['schema'])) {
|
||||
$tableName = $class->table['schema'] . '.' . $class->table['name'];
|
||||
|
||||
if ( ! $platform->supportsSchemas() && $platform->canEmulateSchemas()) {
|
||||
$tableName = $class->table['schema'] . '__' . $class->table['name'];
|
||||
}
|
||||
}
|
||||
|
||||
return isset($class->table['quoted'])
|
||||
? $platform->quoteIdentifier($tableName)
|
||||
: $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -94,9 +94,18 @@ class AnnotationDriver extends AbstractAnnotationDriver
|
||||
// Evaluate Table annotation
|
||||
if (isset($classAnnotations['Doctrine\ORM\Mapping\Table'])) {
|
||||
$tableAnnot = $classAnnotations['Doctrine\ORM\Mapping\Table'];
|
||||
|
||||
$tableName = $tableAnnot->name;
|
||||
$schemaName = $tableAnnot->schema;
|
||||
|
||||
// Split schema and table name from a table name like "myschema.mytable"
|
||||
if (strpos($tableName, '.') !== false) {
|
||||
list($schemaName, $tableName) = explode('.', $tableName);
|
||||
}
|
||||
|
||||
$primaryTable = array(
|
||||
'name' => $tableAnnot->name,
|
||||
'schema' => $tableAnnot->schema
|
||||
'name' => $tableName,
|
||||
'schema' => $schemaName
|
||||
);
|
||||
|
||||
if ($tableAnnot->indexes !== null) {
|
||||
|
@ -77,8 +77,25 @@ class XmlDriver extends FileDriver
|
||||
|
||||
// Evaluate <entity...> attributes
|
||||
$table = array();
|
||||
|
||||
$tableName = null;
|
||||
$schemaName = null;
|
||||
|
||||
if (isset($xmlRoot['table'])) {
|
||||
$table['name'] = (string)$xmlRoot['table'];
|
||||
$tableName = (string)$xmlRoot['table'];
|
||||
|
||||
// Split schema and table name from a table name like "myschema.mytable"
|
||||
if (strpos($tableName, '.') !== false) {
|
||||
list($schemaName, $tableName) = explode('.', $tableName);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($xmlRoot['schema'])) {
|
||||
$schemaName = (string)$xmlRoot['schema'];
|
||||
}
|
||||
|
||||
if (null !== $tableName) {
|
||||
$table['name'] = $tableName;
|
||||
}
|
||||
|
||||
$metadata->setPrimaryTable($table);
|
||||
@ -150,11 +167,6 @@ class XmlDriver extends FileDriver
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($xmlRoot['schema'])) {
|
||||
$metadata->table['schema'] = (string)$xmlRoot['schema'];
|
||||
}*/
|
||||
|
||||
if (isset($xmlRoot['inheritance-type'])) {
|
||||
$inheritanceType = (string)$xmlRoot['inheritance-type'];
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . $inheritanceType));
|
||||
|
@ -75,8 +75,24 @@ class YamlDriver extends FileDriver
|
||||
// Evaluate root level properties
|
||||
$table = array();
|
||||
|
||||
$tableName = null;
|
||||
$schemaName = null;
|
||||
|
||||
if (isset($element['table'])) {
|
||||
$table['name'] = $element['table'];
|
||||
$tableName = $element['table'];
|
||||
|
||||
// Split schema and table name from a table name like "myschema.mytable"
|
||||
if (strpos($tableName, '.') !== false) {
|
||||
list($schemaName, $tableName) = explode('.', $tableName);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($element['schema'])) {
|
||||
$schemaName = $element['schema'];
|
||||
}
|
||||
|
||||
if (null !== $tableName) {
|
||||
$table['name'] = $tableName;
|
||||
}
|
||||
|
||||
// Evaluate second level cache
|
||||
@ -163,11 +179,6 @@ class YamlDriver extends FileDriver
|
||||
}
|
||||
}
|
||||
|
||||
/* not implemented specially anyway. use table = schema.table
|
||||
if (isset($element['schema'])) {
|
||||
$metadata->table['schema'] = $element['schema'];
|
||||
}*/
|
||||
|
||||
if (isset($element['inheritanceType'])) {
|
||||
$metadata->setInheritanceType(constant('Doctrine\ORM\Mapping\ClassMetadata::INHERITANCE_TYPE_' . strtoupper($element['inheritanceType'])));
|
||||
|
||||
|
157
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2825Test.php
Normal file
157
tests/Doctrine/Tests/ORM/Functional/Ticket/DDC2825Test.php
Normal file
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
namespace Doctrine\Tests\ORM\Functional\Ticket;
|
||||
|
||||
use Doctrine\ORM\Mapping\ClassMetadataFactory;
|
||||
|
||||
/**
|
||||
* This class makes tests on the correct use of a database schema when entities are stored
|
||||
*
|
||||
* @group DDC-2825
|
||||
*/
|
||||
class DDC2825Test extends \Doctrine\Tests\OrmFunctionalTestCase
|
||||
{
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
protected function setup()
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
$platform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
|
||||
if ( ! $platform->supportsSchemas() && ! $platform->canEmulateSchemas()) {
|
||||
$this->markTestSkipped("This test is only useful for databases that support schemas or can emulate them.");
|
||||
}
|
||||
|
||||
$this->_schemaTool->createSchema(array(
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaMyTable'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaMyTable2'),
|
||||
$this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaOrder'),
|
||||
));
|
||||
}
|
||||
|
||||
public function testIssue()
|
||||
{
|
||||
// Test with a table with a schema
|
||||
$myEntity = new DDC2825MySchemaMyTable();
|
||||
|
||||
$this->_em->persist($myEntity);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$entities = $this->_em->createQuery('SELECT mt FROM ' . __NAMESPACE__ . '\\DDC2825MySchemaMyTable mt')->execute();
|
||||
$this->assertEquals(count($entities), 1);
|
||||
|
||||
$classMetadata = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaMyTable');
|
||||
$this->checkClassMetadata($classMetadata, 'myschema', 'mytable');
|
||||
|
||||
// Test with schema defined directly as a table annotation property
|
||||
$myEntity2 = new DDC2825MySchemaMyTable2();
|
||||
|
||||
$this->_em->persist($myEntity2);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$entities = $this->_em->createQuery('SELECT mt2 FROM ' . __NAMESPACE__ . '\\DDC2825MySchemaMyTable2 mt2')->execute();
|
||||
$this->assertEquals(count($entities), 1);
|
||||
|
||||
$classMetadata = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaMyTable2');
|
||||
$this->checkClassMetadata($classMetadata, 'myschema', 'mytable2');
|
||||
|
||||
// Test with a table named "order" (which is a reserved keyword) to make sure the table name is not
|
||||
// incorrectly escaped when a schema is used and that the platform doesn't support schemas
|
||||
$order = new DDC2825MySchemaOrder();
|
||||
|
||||
$this->_em->persist($order);
|
||||
$this->_em->flush();
|
||||
$this->_em->clear();
|
||||
|
||||
$classMetadata = $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC2825MySchemaOrder');
|
||||
$this->checkClassMetadata($classMetadata, 'myschema', 'order');
|
||||
|
||||
$entities = $this->_em->createQuery('SELECT mso FROM ' . __NAMESPACE__ . '\\DDC2825MySchemaOrder mso')->execute();
|
||||
$this->assertEquals(count($entities), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that class metadata is correctly stored when a database schema is used and
|
||||
* checks that the table name is correctly converted whether the platform supports database
|
||||
* schemas or not
|
||||
*
|
||||
* @param ClassMetadata $classMetadata Class metadata
|
||||
* @param string $expectedSchemaName Expected schema name
|
||||
* @param string $expectedTableName Expected table name
|
||||
*/
|
||||
protected function checkClassMetadata($classMetadata, $expectedSchemaName, $expectedTableName)
|
||||
{
|
||||
$quoteStrategy = $this->_em->getConfiguration()->getQuoteStrategy();
|
||||
$platform = $this->_em->getConnection()->getDatabasePlatform();
|
||||
$quotedTableName = $quoteStrategy->getTableName($classMetadata, $platform);
|
||||
|
||||
// Check if table name and schema properties are defined in the class metadata
|
||||
$this->assertEquals($classMetadata->table['name'], $expectedTableName);
|
||||
$this->assertEquals($classMetadata->table['schema'], $expectedSchemaName);
|
||||
|
||||
if ($this->_em->getConnection()->getDatabasePlatform()->supportsSchemas()) {
|
||||
$fullTableName = sprintf('%s.%s', $expectedSchemaName, $expectedTableName);
|
||||
} else {
|
||||
$fullTableName = sprintf('%s__%s', $expectedSchemaName, $expectedTableName);
|
||||
}
|
||||
|
||||
$this->assertEquals($quotedTableName, $fullTableName);
|
||||
|
||||
// Checks sequence name validity
|
||||
$expectedSchemaName = $fullTableName . '_' . $classMetadata->getSingleIdentifierColumnName() . '_seq';
|
||||
$this->assertEquals($expectedSchemaName, $classMetadata->getSequenceName($platform));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="myschema.mytable")
|
||||
*/
|
||||
class DDC2825MySchemaMyTable
|
||||
{
|
||||
/**
|
||||
* Test with a quoted column name to check that sequence names are
|
||||
* correctly handled
|
||||
*
|
||||
* @Id @GeneratedValue
|
||||
* @Column(name="`number`", type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="mytable2",schema="myschema")
|
||||
*/
|
||||
class DDC2825MySchemaMyTable2
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @Entity
|
||||
* @Table(name="myschema.order")
|
||||
*/
|
||||
class DDC2825MySchemaOrder
|
||||
{
|
||||
/**
|
||||
* @Id @GeneratedValue
|
||||
* @Column(type="integer")
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
public $id;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user