diff --git a/lib/Doctrine/Association.php b/lib/Doctrine/Association.php
index 4c55a853e..878c8cd16 100644
--- a/lib/Doctrine/Association.php
+++ b/lib/Doctrine/Association.php
@@ -103,13 +103,21 @@ class Doctrine_Association implements Serializable
protected $_sourceFieldName;
/**
- * Identifies the field on the owning side that has the mapping for the
+ * Identifies the field on the owning side that controls the mapping for the
* association. This is only set on the inverse side of an association.
*
* @var string
*/
protected $_mappedByFieldName;
+ /**
+ * Identifies the field on the inverse side of a bidirectional association.
+ * This is only set on the owning side of an association.
+ *
+ * @var string
+ */
+ //protected $_inverseSideFieldName;
+
/**
* The name of the join table, if any.
*
@@ -127,7 +135,16 @@ class Doctrine_Association implements Serializable
*/
public function __construct(array $mapping)
{
- /*$this->_mapping = array(
+ //$this->_initMappingArray();
+ //$mapping = $this->_validateAndCompleteMapping($mapping);
+ //$this->_mapping = array_merge($this->_mapping, $mapping);*/
+
+ $this->_validateAndCompleteMapping($mapping);
+ }
+
+ protected function _initMappingArray()
+ {
+ $this->_mapping = array(
'fieldName' => null,
'sourceEntity' => null,
'targetEntity' => null,
@@ -137,10 +154,8 @@ class Doctrine_Association implements Serializable
'accessor' => null,
'mutator' => null,
'optional' => true,
- 'cascade' => array()
+ 'cascades' => array()
);
- $this->_mapping = array_merge($this->_mapping, $mapping);*/
- $this->_validateAndCompleteMapping($mapping);
}
/**
@@ -342,6 +357,36 @@ class Doctrine_Association implements Serializable
return $this->_mappedByFieldName;
}
+ /*public function getInverseSideFieldName()
+ {
+ return $this->_inverseSideFieldName;
+ }*/
+ /**
+ * Marks the association as bidirectional, specifying the field name of
+ * the inverse side.
+ * This is called on the owning side, when an inverse side is discovered.
+ * This does only make sense to call on the owning side.
+ *
+ * @param string $inverseSideFieldName
+ */
+ /*public function setBidirectional($inverseSideFieldName)
+ {
+ if ( ! $this->_isOwningSide) {
+ return; //TODO: exception?
+ }
+ $this->_inverseSideFieldName = $inverseSideFieldName;
+ }*/
+
+ /**
+ * Whether the association is bidirectional.
+ *
+ * @return boolean
+ */
+ /*public function isBidirectional()
+ {
+ return $this->_mappedByFieldName || $this->_inverseSideFieldName;
+ }*/
+
/**
* Whether the source field of the association has a custom accessor.
*
diff --git a/lib/Doctrine/Association/OneToOne.php b/lib/Doctrine/Association/OneToOne.php
index f9751602e..69f576bb9 100644
--- a/lib/Doctrine/Association/OneToOne.php
+++ b/lib/Doctrine/Association/OneToOne.php
@@ -65,6 +65,12 @@ class Doctrine_Association_OneToOne extends Doctrine_Association
parent::__construct($mapping);
}
+ protected function _initMappingArray()
+ {
+ parent::_initMappingArray();
+ $this->_mapping['deleteOrphans'] = false;
+ }
+
/**
* Validates & completes the mapping. Mapping defaults are applied here.
*
diff --git a/lib/Doctrine/Builder/Record.php b/lib/Doctrine/Builder/Record.php
index d075a44cc..d7d703fc1 100644
--- a/lib/Doctrine/Builder/Record.php
+++ b/lib/Doctrine/Builder/Record.php
@@ -666,7 +666,7 @@ END;
*/
public function buildRecord(array $definition)
{
- if ( !isset($definition['className'])) {
+ if ( ! isset($definition['className'])) {
throw new Doctrine_Builder_Exception('Missing class name.');
}
diff --git a/lib/Doctrine/ClassMetadata.php b/lib/Doctrine/ClassMetadata.php
index d1b0d295d..4eb80bbb1 100644
--- a/lib/Doctrine/ClassMetadata.php
+++ b/lib/Doctrine/ClassMetadata.php
@@ -177,15 +177,16 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* - fieldName (string)
* The name of the field in the Entity.
*
- * - type (string)
- * The database type of the column. Can be one of Doctrine's portable types.
+ * - type (object Doctrine::DBAL::Types::* or custom type)
+ * The database type of the column. Can be one of Doctrine's portable types
+ * or a custom type.
*
* - columnName (string, optional)
* The column name. Optional. Defaults to the field name.
*
* - length (integer, optional)
- * The database length of the column. Optional. Defaults to Doctrine's
- * default values for the portable types.
+ * The database length of the column. Optional. Default value taken from
+ * the type.
*
* - id (boolean, optional)
* Marks the field as the primary key of the Entity. Multiple fields of an
@@ -200,14 +201,26 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
* - nullable (boolean, optional)
* Whether the column is nullable. Defaults to TRUE.
*
- * - columnDefinition (string, optional)
+ * - columnDefinition (string, optional, schema-only)
* The SQL fragment that is used when generating the DDL for the column.
*
- * - precision (integer, optional)
+ * - precision (integer, optional, schema-only)
* The precision of a decimal column. Only valid if the column type is decimal.
*
- * - scale (integer, optional)
+ * - scale (integer, optional, schema-only)
* The scale of a decimal column. Only valid if the column type is decimal.
+ *
+ * - index (string, optional, schema-only)
+ * Whether an index should be generated for the column.
+ * The value specifies the name of the index. To create a multi-column index,
+ * just use the same name for several mappings.
+ *
+ * - unique (string, optional, schema-only)
+ * Whether a unique constraint should be generated for the column.
+ * The value specifies the name of the unique constraint. To create a multi-column
+ * unique constraint, just use the same name for several mappings.
+ *
+ * - foreignKey (string, optional, schema-only)
*
* @var array
*/
@@ -275,25 +288,13 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
*
* -- charset character set
*
- * -- checks the check constraints of this table, eg. 'price > dicounted_price'
- *
- * -- collation collation attribute
- *
- * -- indexes the index definitions of this table
- *
- * -- sequenceName Some databases need sequences instead of auto incrementation primary keys,
- * you can set specific sequence for your table by calling setOption('sequenceName', $seqName)
- * where $seqName is the name of the desired sequence
+ * -- collate collation attribute
*/
protected $_tableOptions = array(
- 'tableName' => null,
- 'sequenceName' => null,
- 'type' => null,
- 'charset' => null,
- 'collation' => null,
- 'collate' => null,
- 'indexes' => array(),
- 'checks' => array()
+ 'tableName' => null,
+ 'type' => null,
+ 'charset' => null,
+ 'collate' => null
);
/**
@@ -319,12 +320,19 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
protected $_lifecycleListeners = array();
/**
- * The association mappings.
+ * The association mappings. All mappings, inverse and owning side.
*
* @var array
*/
protected $_associationMappings = array();
+ /**
+ * List of inverse association mappings, indexed by mappedBy field name.
+ *
+ * @var array
+ */
+ protected $_inverseMappings = array();
+
/**
* Flag indicating whether the identifier/primary key of the class is composite.
*
@@ -553,6 +561,32 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
return $this->_associationMappings[$fieldName];
}
+ /**
+ * Gets the inverse association mapping for the given fieldname.
+ *
+ * @param string $mappedByFieldName
+ * @return Doctrine::ORM::Mapping::AssociationMapping The mapping.
+ */
+ public function getInverseAssociationMapping($mappedByFieldName)
+ {
+ if ( ! isset($this->_associationMappings[$fieldName])) {
+ throw Doctrine_MappingException::mappingNotFound($fieldName);
+ }
+
+ return $this->_inverseMappings[$mappedByFieldName];
+ }
+
+ /**
+ * Whether the class has an inverse association mapping on the given fieldname.
+ *
+ * @param string $mappedByFieldName
+ * @return boolean
+ */
+ public function hasInverseAssociationMapping($mappedByFieldName)
+ {
+ return isset($this->_inverseMappings[$mappedByFieldName]);
+ }
+
public function addAssociationMapping($fieldName, Doctrine_Association $assoc)
{
$this->_associationMappings[$fieldName] = $assoc;
@@ -718,7 +752,6 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
if ( ! $this->_isIdentifierComposite && count($this->_identifier) > 1) {
$this->_isIdentifierComposite = true;
}
-
}
return $mapping;
@@ -1272,7 +1305,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
} else if ($this->_inheritanceType == self::INHERITANCE_TYPE_JOINED) {
// Remove inherited, non-pk fields. They're not in the table of this class
foreach ($allColumns as $name => $definition) {
- if (isset($definition['primary']) && $definition['primary'] === true) {
+ if (isset($definition['id']) && $definition['id'] === true) {
if ($this->getParentClasses() && isset($definition['autoincrement'])) {
unset($allColumns[$name]['autoincrement']);
}
@@ -1286,7 +1319,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
// If this is a subclass, just remove existing autoincrement options on the pk
if ($this->getParentClasses()) {
foreach ($allColumns as $name => $definition) {
- if (isset($definition['primary']) && $definition['primary'] === true) {
+ if (isset($definition['id']) && $definition['id'] === true) {
if (isset($definition['autoincrement'])) {
unset($allColumns[$name]['autoincrement']);
}
@@ -1311,7 +1344,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
$columns[$name] = $definition;
- if (isset($definition['primary']) && $definition['primary']) {
+ if (isset($definition['id']) && $definition['id']) {
$primary[] = $name;
}
}
@@ -1493,12 +1526,23 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToOneMapping = new Doctrine_Association_OneToOne($mapping);
- $sourceFieldName = $oneToOneMapping->getSourceFieldName();
- if (isset($this->_associationMappings[$sourceFieldName])) {
- throw Doctrine_MappingException::duplicateFieldMapping();
+ $this->_storeAssociationMapping($oneToOneMapping);
+
+ /*if ($oneToOneMapping->isInverseSide()) {
+ //FIXME: infinite recursion possible?
+ // Alternative: Store inverse side mappings indexed by mappedBy fieldname
+ // ($this->_inverseMappings). Then look it up.
+ $owningClass = $this->_em->getClassMetadata($oneToOneMapping->getTargetEntityName());
+ $owningClass->getAssociationMapping($oneToOneMapping->getMappedByFieldName())
+ ->setBidirectional($oneToOneMapping->getSourceFieldName());
+ }*/
+ }
+
+ private function _registerMappingIfInverse(Doctrine_Association $assoc)
+ {
+ if ($assoc->isInverseSide()) {
+ $this->_inverseMappings[$assoc->getMappedByFieldName()] = $assoc;
}
- $this->_associationMappings[$sourceFieldName] = $oneToOneMapping;
- $this->_registerCustomAssociationAccessors($oneToOneMapping, $sourceFieldName);
}
/**
@@ -1510,12 +1554,7 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
{
$mapping = $this->_completeAssociationMapping($mapping);
$oneToManyMapping = new Doctrine_Association_OneToMany($mapping);
- $sourceFieldName = $oneToManyMapping->getSourceFieldName();
- if (isset($this->_associationMappings[$sourceFieldName])) {
- throw Doctrine_MappingException::duplicateFieldMapping();
- }
- $this->_associationMappings[$sourceFieldName] = $oneToManyMapping;
- $this->_registerCustomAssociationAccessors($oneToManyMapping, $sourceFieldName);
+ $this->_storeAssociationMapping($oneToManyMapping);
}
/**
@@ -1530,11 +1569,31 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
}
/**
- * @todo Implementation.
+ * Adds a many-to-many mapping.
+ *
+ * @param array $mapping The mapping.
*/
public function mapManyToMany(array $mapping)
{
-
+ $mapping = $this->_completeAssociationMapping($mapping);
+ $manyToManyMapping = new Doctrine_Association_ManyToManyMapping($mapping);
+ $this->_storeAssociationMapping($manyToManyMapping);
+ }
+
+ /**
+ * Stores the association mapping.
+ *
+ * @param Doctrine_Association $assocMapping
+ */
+ private function _storeAssociationMapping(Doctrine_Association $assocMapping)
+ {
+ $sourceFieldName = $assocMapping->getSourceFieldName();
+ if (isset($this->_associationMappings[$sourceFieldName])) {
+ throw Doctrine_MappingException::duplicateFieldMapping();
+ }
+ $this->_associationMappings[$sourceFieldName] = $assocMapping;
+ $this->_registerCustomAssociationAccessors($assocMapping, $sourceFieldName);
+ $this->_registerMappingIfInverse($assocMapping);
}
/**
diff --git a/lib/Doctrine/Collection.php b/lib/Doctrine/Collection.php
index 60edbfe4d..8e871e72b 100644
--- a/lib/Doctrine/Collection.php
+++ b/lib/Doctrine/Collection.php
@@ -31,9 +31,9 @@
*
* A collection of entities represents only the associations (links) to those entities.
* That means, if the collection is part of a many-many mapping and you remove
- * entities from the collection, only the links in the xref table are removed.
+ * entities from the collection, only the links in the xref table are removed (on flush).
* Similarly, if you remove entities from a collection that is part of a one-many
- * mapping this will only result in the nulling out of the foreign keys
+ * mapping this will only result in the nulling out of the foreign keys on flush
* (or removal of the links in the xref table if the one-many is mapped through an
* xref table). If you want entities in a one-many collection to be removed when
* they're removed from the collection, use deleteOrphans => true on the one-many
@@ -44,6 +44,7 @@
* @version $Revision$
* @author Konsta Vesterinen
* @author Roman Borschel
+ * @todo Add more typical Collection methods.
*/
class Doctrine_Collection implements Countable, IteratorAggregate, Serializable, ArrayAccess
{
@@ -86,7 +87,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
protected $_association;
/**
- * The name of the column that is used for collection key mapping.
+ * The name of the field that is used for collection key mapping.
*
* @var string
*/
@@ -100,14 +101,31 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
//protected static $null;
/**
- * The EntityManager.
+ * The EntityManager that manages the persistence of the collection.
*
- * @var EntityManager
+ * @var Doctrine::ORM::EntityManager
*/
protected $_em;
+
+ /**
+ * The name of the field on the target entities that points to the owner
+ * of the collection. This is only set if the association is bidirectional.
+ *
+ * @var string
+ */
+ protected $_backRefFieldName;
+
+ /**
+ * Hydration flag.
+ *
+ * @var boolean
+ * @see _setHydrationFlag()
+ */
+ protected $_hydrationFlag;
/**
* Constructor.
+ * Creates a new persistent collection.
*/
public function __construct($entityBaseType, $keyField = null)
{
@@ -126,6 +144,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
* setData
*
* @param array $data
+ * @todo Remove?
*/
public function setData(array $data)
{
@@ -133,56 +152,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
}
/**
- * Serializes the collection.
- * This method is automatically called when the Collection is serialized.
- *
- * Part of the implementation of the Serializable interface.
- *
- * @return array
- */
- public function serialize()
- {
- $vars = get_object_vars($this);
-
- unset($vars['reference']);
- unset($vars['relation']);
- unset($vars['expandable']);
- unset($vars['expanded']);
- unset($vars['generator']);
-
- return serialize($vars);
- }
-
- /**
- * Reconstitutes the collection object from it's serialized form.
- * This method is automatically called everytime the Collection object is unserialized.
- *
- * Part of the implementation of the Serializable interface.
- *
- * @param string $serialized The serialized data
- *
- * @return void
- */
- public function unserialize($serialized)
- {
- $manager = Doctrine_EntityManagerFactory::getManager();
- $connection = $manager->getConnection();
-
- $array = unserialize($serialized);
-
- foreach ($array as $name => $values) {
- $this->$name = $values;
- }
-
- $keyColumn = isset($array['keyField']) ? $array['keyField'] : null;
-
- if ($keyColumn !== null) {
- $this->_keyField = $keyColumn;
- }
- }
-
- /**
- * Sets the key column for this collection
+ * INTERNAL: Sets the key column for this collection
*
* @param string $column
* @return Doctrine_Collection
@@ -194,7 +164,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
}
/**
- * returns the name of the key column
+ * INTERNAL: returns the name of the key column
*
* @return string
*/
@@ -208,7 +178,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
*
* @return array
*/
- public function getData()
+ public function unwrap()
{
return $this->_data;
}
@@ -255,29 +225,25 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
/**
* INTERNAL:
- * sets a reference pointer
+ * Sets the collection owner. Used (only?) during hydration.
*
* @return void
*/
- public function setReference(Doctrine_Entity $entity, Doctrine_Association $relation)
+ public function _setOwner(Doctrine_Entity $entity, Doctrine_Association $relation)
{
$this->_owner = $entity;
$this->_association = $relation;
-
- /*if ($relation instanceof Doctrine_Relation_ForeignKey ||
- $relation instanceof Doctrine_Relation_LocalKey) {
- $this->referenceField = $relation->getForeignFieldName();
- $value = $entity->get($relation->getLocalFieldName());
- foreach ($this->_data as $entity) {
- if ($value !== null) {
- $entity->set($this->referenceField, $value, false);
- } else {
- $entity->set($this->referenceField, $this->_owner, false);
- }
+ if ($relation->isInverseSide()) {
+ // for sure bidirectional
+ $this->_backRefFieldName = $relation->getMappedByFieldName();
+ } else {
+ $targetClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
+ if ($targetClass->hasInverseAssociationMapping($relation->getSourceFieldName())) {
+ // bidirectional
+ $this->_backRefFieldName = $targetClass->getInverseAssociationMapping(
+ $relation->getSourceFieldName())->getSourceFieldName();
}
- } else if ($relation instanceof Doctrine_Relation_Association) {
-
- }*/
+ }
}
/**
@@ -286,7 +252,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
*
* @return mixed
*/
- public function getReference()
+ public function _getOwner()
{
return $this->_owner;
}
@@ -508,8 +474,9 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
*/
public function add($value, $key = null)
{
+ //TODO: really only allow entities?
if ( ! $value instanceof Doctrine_Entity) {
- throw new Doctrine_Record_Exception('Value variable in set is not an instance of Doctrine_Entity.');
+ throw new Doctrine_Record_Exception('Value variable in collection is not an instance of Doctrine_Entity.');
}
// TODO: Really prohibit duplicates?
@@ -526,8 +493,15 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
$this->_data[] = $value;
}
- //TODO: Register collection as dirty with the UoW if necessary
- $this->_changed();
+ if ($this->_hydrationFlag) {
+ if ($this->_backRefFieldName) {
+ // set back reference to owner
+ $value->_internalSetReference($this->_backRefFieldName, $this->_owner);
+ }
+ } else {
+ //TODO: Register collection as dirty with the UoW if necessary
+ $this->_changed();
+ }
return true;
}
@@ -553,7 +527,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
* @return boolean
* @todo New implementation & maybe move elsewhere.
*/
- public function loadRelated($name = null)
+ /*public function loadRelated($name = null)
{
$list = array();
$query = new Doctrine_Query($this->_mapper->getConnection());
@@ -595,7 +569,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
$coll = $query->query($dql, $list);
$this->populateRelated($name, $coll);
- }
+ }*/
/**
* INTERNAL:
@@ -606,7 +580,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
* @return void
* @todo New implementation & maybe move elsewhere.
*/
- protected function populateRelated($name, Doctrine_Collection $coll)
+ /*protected function populateRelated($name, Doctrine_Collection $coll)
{
$rel = $this->_mapper->getTable()->getRelation($name);
$table = $rel->getTable();
@@ -658,6 +632,23 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
}
}
+ }*/
+
+ /**
+ * INTERNAL:
+ * Sets a flag that indicates whether the collection is currently being hydrated.
+ * This has the following consequences:
+ * 1) During hydration, bidirectional associations are completed automatically
+ * by setting the back reference.
+ * 2) During hydration no change notifications are reported to the UnitOfWork.
+ * I.e. that means add() etc. do not cause the collection to be scheduled
+ * for an update.
+ *
+ * @param boolean $bool
+ */
+ public function _setHydrationFlag($bool)
+ {
+ $this->_hydrationFlag = $bool;
}
/**
@@ -671,7 +662,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
*
* @return void
*/
- public function takeSnapshot()
+ public function _takeSnapshot()
{
$this->_snapshot = $this->_data;
}
@@ -681,13 +672,13 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
*
* @return array returns the data in last snapshot
*/
- public function getSnapshot()
+ public function _getSnapshot()
{
return $this->_snapshot;
}
/**
- * Processes the difference of the last snapshot and the current data.
+ * INTERNAL: Processes the difference of the last snapshot and the current data.
*
* an example:
* Snapshot with the objects 1, 2 and 4
@@ -696,6 +687,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
* The process would remove objects 1 and 4
*
* @return Doctrine_Collection
+ * @todo Move elsewhere
*/
public function processDiff()
{
@@ -706,9 +698,10 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
}
/**
- * Mimics the result of a $query->execute(array(), Doctrine::FETCH_ARRAY);
+ * Creates an array representation of the collection.
*
* @param boolean $deep
+ * @return array
*/
public function toArray($deep = false, $prefixKey = false)
{
@@ -836,7 +829,6 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
if ($a->getOid() == $b->getOid()) {
return 0;
}
-
return ($a->getOid() > $b->getOid()) ? 1 : -1;
}
@@ -916,6 +908,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
/**
* getIterator
+ *
* @return object ArrayIterator
*/
public function getIterator()
@@ -933,7 +926,7 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
}
/**
- * Gets the association mapping of the collection.
+ * INTERNAL: Gets the association mapping of the collection.
*
* @return Doctrine::ORM::Mapping::AssociationMapping
*/
@@ -989,4 +982,55 @@ class Doctrine_Collection implements Countable, IteratorAggregate, Serializable,
$this->_em->getUnitOfWork()->scheduleCollectionUpdate($this);
}*/
}
+
+ /* Serializable implementation */
+
+ /**
+ * Serializes the collection.
+ * This method is automatically called when the Collection is serialized.
+ *
+ * Part of the implementation of the Serializable interface.
+ *
+ * @return array
+ */
+ public function serialize()
+ {
+ $vars = get_object_vars($this);
+
+ unset($vars['reference']);
+ unset($vars['relation']);
+ unset($vars['expandable']);
+ unset($vars['expanded']);
+ unset($vars['generator']);
+
+ return serialize($vars);
+ }
+
+ /**
+ * Reconstitutes the collection object from it's serialized form.
+ * This method is automatically called everytime the Collection object is unserialized.
+ *
+ * Part of the implementation of the Serializable interface.
+ *
+ * @param string $serialized The serialized data
+ *
+ * @return void
+ */
+ public function unserialize($serialized)
+ {
+ $manager = Doctrine_EntityManagerFactory::getManager();
+ $connection = $manager->getConnection();
+
+ $array = unserialize($serialized);
+
+ foreach ($array as $name => $values) {
+ $this->$name = $values;
+ }
+
+ $keyColumn = isset($array['keyField']) ? $array['keyField'] : null;
+
+ if ($keyColumn !== null) {
+ $this->_keyField = $keyColumn;
+ }
+ }
}
diff --git a/lib/Doctrine/Configuration.php b/lib/Doctrine/Configuration.php
index cfc897fdc..0ec57c482 100644
--- a/lib/Doctrine/Configuration.php
+++ b/lib/Doctrine/Configuration.php
@@ -26,6 +26,9 @@
/**
* The Configuration is the container for all configuration options of Doctrine.
* It combines all configuration options from DBAL & ORM.
+ *
+ * INTERNAL: When adding a new configuration option just write a getter/setter
+ * combination and add the option to the _attributes array with a proper default value.
*
* @author Roman Borschel
* @since 2.0
@@ -36,6 +39,8 @@ class Doctrine_Configuration
/**
* The attributes that are contained in the configuration.
+ * Values are default values. PHP null is replaced by a reference to the Null
+ * object on instantiation in order to use isset() instead of array_key_exists().
*
* @var array
*/
@@ -44,12 +49,9 @@ class Doctrine_Configuration
'indexNameFormat' => '%s_idx',
'sequenceNameFormat' => '%s_seq',
'tableNameFormat' => '%s',
- 'resultCache' => null,
- 'resultCacheLifeSpan' => null,
- 'queryCache' => null,
- 'queryCacheLifeSpan' => null,
- 'metadataCache' => null,
- 'metadataCacheLifeSpan' => null
+ 'resultCacheImpl' => null,
+ 'queryCacheImpl' => null,
+ 'metadataCacheImpl' => null,
);
/**
@@ -77,38 +79,6 @@ class Doctrine_Configuration
}
}
- /**
- * Gets the value of a configuration attribute.
- *
- * @param string $name
- * @return mixed
- */
- public function get($name)
- {
- if ( ! $this->has($name)) {
- throw Doctrine_Configuration_Exception::unknownAttribute($name);
- }
- if ($this->_attributes[$name] === $this->_nullObject) {
- return null;
- }
- return $this->_attributes[$name];
- }
-
- /**
- * Sets the value of a configuration attribute.
- *
- * @param string $name
- * @param mixed $value
- */
- public function set($name, $value)
- {
- if ( ! $this->has($name)) {
- throw Doctrine_Configuration_Exception::unknownAttribute($name);
- }
- // TODO: do some value checking depending on the attribute
- $this->_attributes[$name] = $value;
- }
-
/**
* Checks whether the configuration contains/supports an attribute.
*
@@ -119,4 +89,91 @@ class Doctrine_Configuration
{
return isset($this->_attributes[$name]);
}
+
+ public function getQuoteIdentifiers()
+ {
+ return $this->_attributes['quoteIdentifiers'];
+ }
+
+ public function setQuoteIdentifiers($bool)
+ {
+ $this->_attributes['quoteIdentifiers'] = (bool)$bool;
+ }
+
+ public function getIndexNameFormat()
+ {
+ return $this->_attributes['indexNameFormat'];
+ }
+
+ public function setIndexNameFormat($format)
+ {
+ //TODO: check format?
+ $this->_attributes['indexNameFormat'] = $format;
+ }
+
+ public function getSequenceNameFormat()
+ {
+ return $this->_attributes['sequenceNameFormat'];
+ }
+
+ public function setSequenceNameFormat($format)
+ {
+ //TODO: check format?
+ $this->_attributes['sequenceNameFormat'] = $format;
+ }
+
+ public function getTableNameFormat()
+ {
+ return $this->_attributes['tableNameFormat'];
+ }
+
+ public function setTableNameFormat($format)
+ {
+ //TODO: check format?
+ $this->_attributes['tableNameFormat'] = $format;
+ }
+
+ public function getResultCacheImpl()
+ {
+ return $this->_attributes['resultCacheImpl'];
+ }
+
+ public function setResultCacheImpl(Doctrine_Cache_Interface $cacheImpl)
+ {
+ $this->_attributes['resultCacheImpl'] = $cacheImpl;
+ }
+
+ public function getQueryCacheImpl()
+ {
+ return $this->_attributes['queryCacheImpl'];
+ }
+
+ public function setQueryCacheImpl(Doctrine_Cache_Interface $cacheImpl)
+ {
+ $this->_attributes['queryCacheImpl'] = $cacheImpl;
+ }
+
+ public function getMetadataCacheImpl()
+ {
+ return $this->_attributes['metadataCacheImpl'];
+ }
+
+ public function setMetadataCacheImpl(Doctrine_Cache_Interface $cacheImpl)
+ {
+ $this->_attributes['metadataCacheImpl'] = $cacheImpl;
+ }
+
+ public function setCustomTypes(array $types)
+ {
+ foreach ($types as $name => $typeClassName) {
+ Doctrine_DataType::addCustomType($name, $typeClassName);
+ }
+ }
+
+ public function setTypeOverrides(array $overrides)
+ {
+ foreach ($override as $name => $typeClassName) {
+ Doctrine_DataType::overrideType($name, $typeClassName);
+ }
+ }
}
\ No newline at end of file
diff --git a/lib/Doctrine/Connection.php b/lib/Doctrine/Connection.php
index 06a416416..5a4e57f53 100644
--- a/lib/Doctrine/Connection.php
+++ b/lib/Doctrine/Connection.php
@@ -180,13 +180,26 @@ abstract class Doctrine_Connection
*
* @param array $params The connection parameters.
*/
- public function __construct(array $params)
+ public function __construct(array $params, $config = null, $eventManager = null)
{
if (isset($params['pdo'])) {
$this->_pdo = $params['pdo'];
$this->_isConnected = true;
}
$this->_params = $params;
+
+ // Create default config and event manager if none given
+ if ( ! $config) {
+ $this->_config = new Doctrine_Configuration();
+ }
+ if ( ! $eventManager) {
+ $this->_eventManager = new Doctrine_EventManager();
+ }
+
+ // create platform
+ $class = "Doctrine_DatabasePlatform_" . $this->_driverName . "Platform";
+ $this->_platform = new $class();
+ $this->_platform->setQuoteIdentifiers($this->_config->getQuoteIdentifiers());
}
/**
@@ -242,8 +255,7 @@ abstract class Doctrine_Connection
*/
public function getDatabasePlatform()
{
- throw new Doctrine_Connection_Exception("No DatabasePlatform available "
- . "for connection " . get_class($this));
+ return $this->_platform;
}
/**
@@ -558,10 +570,12 @@ abstract class Doctrine_Connection
* @param bool $checkOption check the 'quote_identifier' option
*
* @return string quoted identifier string
+ * @todo Moved to DatabasePlatform
+ * @deprecated
*/
- public function quoteIdentifier($str, $checkOption = true)
+ public function quoteIdentifier($str)
{
- if (is_null($this->_quoteIdentifiers)) {
+ /*if (is_null($this->_quoteIdentifiers)) {
$this->_quoteIdentifiers = $this->_config->get('quoteIdentifiers');
}
if ( ! $this->_quoteIdentifiers) {
@@ -579,7 +593,8 @@ abstract class Doctrine_Connection
$c = $this->_platform->getIdentifierQuoteCharacter();
$str = str_replace($c, $c . $c, $str);
- return $c . $str . $c;
+ return $c . $str . $c;*/
+ return $this->getDatabasePlatform()->quoteIdentifier($str);
}
/**
@@ -590,6 +605,7 @@ abstract class Doctrine_Connection
*
* @param array $item
* @return void
+ * @deprecated Moved to DatabasePlatform
*/
public function convertBooleans($item)
{
diff --git a/lib/Doctrine/Connection/Mysql.php b/lib/Doctrine/Connection/Mysql.php
index df7e7b83c..5161b220a 100644
--- a/lib/Doctrine/Connection/Mysql.php
+++ b/lib/Doctrine/Connection/Mysql.php
@@ -190,18 +190,5 @@ class Doctrine_Connection_Mysql extends Doctrine_Connection_Common
}
return $dsn;
- }
-
- /**
- * Gets the DatabasePlatform for the connection.
- *
- * @return Doctrine::DBAL::Platforms::MySqlPlatform
- */
- public function getDatabasePlatform()
- {
- if ( ! $this->_platform) {
- $this->_platform = new Doctrine_DatabasePlatform_MySqlPlatform();
- }
- return $this->_platform;
}
}
diff --git a/lib/Doctrine/Connection/Pgsql.php b/lib/Doctrine/Connection/Pgsql.php
index 481a370b0..566923b4b 100644
--- a/lib/Doctrine/Connection/Pgsql.php
+++ b/lib/Doctrine/Connection/Pgsql.php
@@ -70,7 +70,8 @@ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common
* This method takes care of that conversion
*
* @param array $item
- * @return void
+ * @return void
+ * @deprecated Moved to PostgreSqlPlatform
*/
public function convertBooleans($item)
{
@@ -94,7 +95,7 @@ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common
* @param string $native determines if the raw version string should be returned
* @return array|string an array or string with version information
*/
- public function getServerVersion($native = false)
+ /*public function getServerVersion($native = false)
{
$query = 'SHOW SERVER_VERSION';
@@ -124,5 +125,5 @@ class Doctrine_Connection_Pgsql extends Doctrine_Connection_Common
}
}
return $serverInfo;
- }
+ }*/
}
\ No newline at end of file
diff --git a/lib/Doctrine/ConnectionFactory.php b/lib/Doctrine/ConnectionFactory.php
index 8f61cc243..56c467e05 100644
--- a/lib/Doctrine/ConnectionFactory.php
+++ b/lib/Doctrine/ConnectionFactory.php
@@ -46,12 +46,46 @@ class Doctrine_ConnectionFactory
'firebird' => 'Doctrine_Connection_Firebird',
'informix' => 'Doctrine_Connection_Informix',
'mock' => 'Doctrine_Connection_Mock');
+
+ /**
+ * The EventManager that is injected into all created Connections.
+ *
+ * @var EventManager
+ */
+ private $_eventManager;
+
+ /**
+ * The Configuration that is injected into all created Connections.
+ *
+ * @var Configuration
+ */
+ private $_config;
public function __construct()
{
}
+ /**
+ * Sets the Configuration that is injected into all Connections.
+ *
+ * @param Doctrine_Configuration $config
+ */
+ public function setConfiguration(Doctrine_Configuration $config)
+ {
+ $this->_config = $config;
+ }
+
+ /**
+ * Sets the EventManager that is injected into all Connections.
+ *
+ * @param Doctrine_EventManager $eventManager
+ */
+ public function setEventManager(Doctrine_EventManager $eventManager)
+ {
+ $this->_eventManager = $eventManager;
+ }
+
/**
* Creates a connection object with the specified parameters.
*
@@ -60,6 +94,14 @@ class Doctrine_ConnectionFactory
*/
public function createConnection(array $params)
{
+ // create default config and event manager, if not set
+ if ( ! $this->_config) {
+ $this->_config = new Doctrine_Configuration();
+ }
+ if ( ! $this->_eventManager) {
+ $this->_eventManager = new Doctrine_EventManager();
+ }
+
// check for existing pdo object
if (isset($params['pdo']) && ! $params['pdo'] instanceof PDO) {
throw Doctrine_ConnectionFactory_Exception::invalidPDOInstance();
@@ -70,7 +112,7 @@ class Doctrine_ConnectionFactory
}
$className = $this->_drivers[$params['driver']];
- return new $className($params);
+ return new $className($params, $this->_config, $this->_eventManager);
}
/**
diff --git a/lib/Doctrine/DataType.php b/lib/Doctrine/DataType.php
new file mode 100644
index 000000000..471165f5e
--- /dev/null
+++ b/lib/Doctrine/DataType.php
@@ -0,0 +1,78 @@
+ 'Doctrine_DataType_IntegerType',
+ 'string' => 'Doctrine_DataType_StringType',
+ 'text' => 'Doctrine_DataType_TextType',
+ 'datetime' => 'Doctrine_DataType_DateTimeType',
+ 'decimal' => 'Doctrine_DataType_DecimalType',
+ 'double' => 'Doctrine_DataType_DoubleType'
+ );
+
+ public function convertToDatabaseValue($value, Doctrine_DatabasePlatform $platform)
+ {
+ return $value;
+ }
+
+ public function convertToObjectValue($value)
+ {
+ return $value;
+ }
+
+ abstract public function getDefaultLength(Doctrine_DatabasePlatform $platform);
+ abstract public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DatabasePlatform $platform);
+ abstract public function getName();
+
+ /**
+ * Factory method.
+ *
+ * @param string $name The name of the type (as returned by getName()).
+ * @return Doctrine::DBAL::Types::Type
+ */
+ public static function getType($name)
+ {
+ if ( ! isset(self::$_typeObjects[$name])) {
+ if ( ! isset(self::$_typesMap[$name])) {
+ throw Doctrine_Exception::unknownType($name);
+ }
+ self::$_typeObjects[$name] = new self::$_typesMap[$name]();
+ }
+ return self::$_typeObjects[$name];
+ }
+
+ /**
+ * Adds a custom type to the type map.
+ *
+ * @param string $name Name of the type. This should correspond to what
+ * getName() returns.
+ * @param string $className The class name of the custom type.
+ */
+ public static function addCustomType($name, $className)
+ {
+ if (isset(self::$_typesMap[$name])) {
+ throw Doctrine_Exception::typeExists($name);
+ }
+ self::$_typesMap[$name] = $className;
+ }
+
+ /**
+ * Overrides an already defined type to use a different implementation.
+ *
+ * @param string $name
+ * @param string $className
+ */
+ public static function overrideType($name, $className)
+ {
+ if ( ! isset(self::$_typesMap[$name])) {
+ throw Doctrine_Exception::typeNotFound($name);
+ }
+ self::$_typesMap[$name] = $className;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/ArrayType.php b/lib/Doctrine/DataType/ArrayType.php
new file mode 100644
index 000000000..4a218b2c4
--- /dev/null
+++ b/lib/Doctrine/DataType/ArrayType.php
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/BooleanType.php b/lib/Doctrine/DataType/BooleanType.php
new file mode 100644
index 000000000..82b127452
--- /dev/null
+++ b/lib/Doctrine/DataType/BooleanType.php
@@ -0,0 +1,33 @@
+convertBooleans($value);
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param unknown_type $value
+ * @return unknown
+ * @override
+ */
+ public function convertToObjectValue($value)
+ {
+ return (bool)$value;
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/DateTimeType.php b/lib/Doctrine/DataType/DateTimeType.php
new file mode 100644
index 000000000..40ddecab3
--- /dev/null
+++ b/lib/Doctrine/DataType/DateTimeType.php
@@ -0,0 +1,35 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/DecimalType.php b/lib/Doctrine/DataType/DecimalType.php
new file mode 100644
index 000000000..c7ffd766f
--- /dev/null
+++ b/lib/Doctrine/DataType/DecimalType.php
@@ -0,0 +1,12 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/IntegerType.php b/lib/Doctrine/DataType/IntegerType.php
new file mode 100644
index 000000000..5de7624b9
--- /dev/null
+++ b/lib/Doctrine/DataType/IntegerType.php
@@ -0,0 +1,17 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/StringType.php b/lib/Doctrine/DataType/StringType.php
new file mode 100644
index 000000000..1f1f566d6
--- /dev/null
+++ b/lib/Doctrine/DataType/StringType.php
@@ -0,0 +1,28 @@
+getVarcharDeclaration($fieldDeclaration);
+ }
+
+ public function getDefaultLength(Doctrine_DatabasePlatform $platform)
+ {
+ return $platform->getVarcharDefaultLength();
+ }
+
+ public function getName()
+ {
+ return 'string';
+ }
+}
+
+?>
\ No newline at end of file
diff --git a/lib/Doctrine/DataType/TextType.php b/lib/Doctrine/DataType/TextType.php
new file mode 100644
index 000000000..51cd9ad57
--- /dev/null
+++ b/lib/Doctrine/DataType/TextType.php
@@ -0,0 +1,25 @@
+getClobDeclarationSql($fieldDeclaration);
+ }
+
+}
+
+?>
\ No newline at end of file
diff --git a/lib/Doctrine/DatabasePlatform.php b/lib/Doctrine/DatabasePlatform.php
index 7f7ef14b6..b32dbbb26 100644
--- a/lib/Doctrine/DatabasePlatform.php
+++ b/lib/Doctrine/DatabasePlatform.php
@@ -31,12 +31,38 @@
* @author Lukas Smith (PEAR MDB2 library)
*/
abstract class Doctrine_DatabasePlatform
-{
+{
+ protected $_quoteIdentifiers = false;
+
+ protected $valid_default_values = array(
+ 'text' => '',
+ 'boolean' => true,
+ 'integer' => 0,
+ 'decimal' => 0.0,
+ 'float' => 0.0,
+ 'timestamp' => '1970-01-01 00:00:00',
+ 'time' => '00:00:00',
+ 'date' => '1970-01-01',
+ 'clob' => '',
+ 'blob' => '',
+ 'string' => ''
+ );
+
/**
* Constructor.
*/
public function __construct() {}
+ public function setQuoteIdentifiers($bool)
+ {
+ $this->_quoteIdentifiers = (bool)$bool;
+ }
+
+ public function getQuoteIdentifiers()
+ {
+ return $this->_quoteIdentifiers;
+ }
+
/**
* Gets the character used for identifier quoting.
*
@@ -793,7 +819,790 @@ abstract class Doctrine_DatabasePlatform
return 'COS(' . $value . ')';
}
- /**
+ /**
+ * Enter description here...
+ *
+ * @return string
+ */
+ public function getForUpdateSql()
+ {
+ return 'FOR UPDATE';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListDatabasesSql()
+ {
+ throw new Doctrine_Export_Exception('List databases not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListFunctionsSql()
+ {
+ throw new Doctrine_Export_Exception('List functions not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListTriggersSql()
+ {
+ throw new Doctrine_Export_Exception('List triggers not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListSequencesSql()
+ {
+ throw new Doctrine_Export_Exception('List sequences not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListTableConstraintsSql()
+ {
+ throw new Doctrine_Export_Exception('List table constraints not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListTableColumnsSql()
+ {
+ throw new Doctrine_Export_Exception('List table columns not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListTablesSql()
+ {
+ throw new Doctrine_Export_Exception('List tables not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListUsersSql()
+ {
+ throw new Doctrine_Export_Exception('List users not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getListViewsSql()
+ {
+ throw new Doctrine_Export_Exception('List views not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getDropDatabaseSql($database)
+ {
+ return 'DROP DATABASE ' . $database;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getDropTableSql($table)
+ {
+ return 'DROP TABLE ' . $table;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getDropIndexSql($index)
+ {
+ return 'DROP INDEX ' . $index;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getDropSequenceSql()
+ {
+ throw new Doctrine_Export_Exception('Drop sequence not supported by this driver.');
+ }
+
+ /**
+ * Gets the SQL for acquiring the next value from a sequence.
+ */
+ public function getSequenceNextValSql($sequenceName)
+ {
+ throw new Doctrine_Export_Exception('Sequences not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getCreateDatabaseSql($database)
+ {
+ throw new Doctrine_Export_Exception('Create database not supported by this driver.');
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getCreateTableSql($table, array $columns, array $options)
+ {
+ if ( ! $table) {
+ throw new Doctrine_Export_Exception('no valid table name specified');
+ }
+ if (empty($columns)) {
+ throw new Doctrine_Export_Exception('no fields specified for table ' . $name);
+ }
+
+ $queryFields = $this->getFieldDeclarationList($columns);
+
+ if (isset($options['primary']) && ! empty($options['primary'])) {
+ $queryFields .= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')';
+ }
+
+ if (isset($options['indexes']) && ! empty($options['indexes'])) {
+ foreach($options['indexes'] as $index => $definition) {
+ $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition);
+ }
+ }
+
+ $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields;
+
+ $check = $this->getCheckDeclaration($columns);
+
+ if ( ! empty($check)) {
+ $query .= ', ' . $check;
+ }
+
+ $query .= ')';
+
+ $sql[] = $query;
+
+ if (isset($options['foreignKeys'])) {
+ foreach ((array) $options['foreignKeys'] as $k => $definition) {
+ if (is_array($definition)) {
+ $sql[] = $this->getCreateForeignKeySql($name, $definition);
+ }
+ }
+ }
+ return $sql;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ */
+ public function getCreateSequenceSql($sequenceName, $start = 1, array $options)
+ {
+ throw new Doctrine_Export_Exception('Create sequence not supported by this driver.');
+ }
+
+ /**
+ * create a constraint on a table
+ *
+ * @param string $table name of the table on which the constraint is to be created
+ * @param string $name name of the constraint to be created
+ * @param array $definition associative array that defines properties of the constraint to be created.
+ * Currently, only one property named FIELDS is supported. This property
+ * is also an associative with the names of the constraint fields as array
+ * constraints. Each entry of this array is set to another type of associative
+ * array that specifies properties of the constraint that are specific to
+ * each field.
+ *
+ * Example
+ * array(
+ * 'fields' => array(
+ * 'user_name' => array(),
+ * 'last_login' => array()
+ * )
+ * )
+ * @return void
+ */
+ public function getCreateConstraintSql($table, $name, $definition)
+ {
+ $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name;
+
+ if (isset($definition['primary']) && $definition['primary']) {
+ $query .= ' PRIMARY KEY';
+ } elseif (isset($definition['unique']) && $definition['unique']) {
+ $query .= ' UNIQUE';
+ }
+
+ $fields = array();
+ foreach (array_keys($definition['fields']) as $field) {
+ $fields[] = $field;
+ }
+ $query .= ' ('. implode(', ', $fields) . ')';
+
+ return $query;
+ }
+
+ /**
+ * Get the stucture of a field into an array
+ *
+ * @param string $table name of the table on which the index is to be created
+ * @param string $name name of the index to be created
+ * @param array $definition associative array that defines properties of the index to be created.
+ * @see Doctrine_Export::createIndex()
+ * @return string
+ */
+ public function getCreateIndexSql($table, $name, array $definition)
+ {
+ $type = '';
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
+
+ $fields = array();
+ foreach ($definition['fields'] as $field) {
+ $fields[] = $field;
+ }
+ $query .= ' (' . implode(', ', $fields) . ')';
+
+ return $query;
+ }
+
+ /**
+ * Quote a string so it can be safely used as a table or column name.
+ *
+ * Delimiting style depends on which database driver is being used.
+ *
+ * NOTE: just because you CAN use delimited identifiers doesn't mean
+ * you SHOULD use them. In general, they end up causing way more
+ * problems than they solve.
+ *
+ * Portability is broken by using the following characters inside
+ * delimited identifiers:
+ * + backtick (`) -- due to MySQL
+ * + double quote (") -- due to Oracle
+ * + brackets ([ or ]) -- due to Access
+ *
+ * Delimited identifiers are known to generally work correctly under
+ * the following drivers:
+ * + mssql
+ * + mysql
+ * + mysqli
+ * + oci8
+ * + pgsql
+ * + sqlite
+ *
+ * InterBase doesn't seem to be able to use delimited identifiers
+ * via PHP 4. They work fine under PHP 5.
+ *
+ * @param string $str identifier name to be quoted
+ * @param bool $checkOption check the 'quote_identifier' option
+ *
+ * @return string quoted identifier string
+ */
+ public function quoteIdentifier($str)
+ {
+ if ( ! $this->_quoteIdentifiers) {
+ return $str;
+ }
+
+ // quick fix for the identifiers that contain a dot
+ if (strpos($str, '.')) {
+ $e = explode('.', $str);
+ return $this->quoteIdentifier($e[0])
+ . '.'
+ . $this->quoteIdentifier($e[1]);
+ }
+
+ $c = $this->getIdentifierQuoteCharacter();
+ $str = str_replace($c, $c . $c, $str);
+
+ return $c . $str . $c;
+ }
+
+ /**
+ * createForeignKeySql
+ *
+ * @param string $table name of the table on which the foreign key is to be created
+ * @param array $definition associative array that defines properties of the foreign key to be created.
+ * @return string
+ */
+ public function getCreateForeignKeySql($table, array $definition)
+ {
+ $table = $this->quoteIdentifier($table);
+ $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclarationSql($definition);
+
+ return $query;
+ }
+
+ /**
+ * generates the sql for altering an existing table
+ * (this method is implemented by the drivers)
+ *
+ * @param string $name name of the table that is intended to be changed.
+ * @param array $changes associative array that contains the details of each type *
+ * @param boolean $check indicates whether the function should just check if the DBMS driver
+ * can perform the requested table alterations if the value is true or
+ * actually perform them otherwise.
+ * @see Doctrine_Export::alterTable()
+ * @return string
+ */
+ public function getAlterTableSql($name, array $changes, $check = false)
+ {
+ throw new Doctrine_Export_Exception('Alter table not supported by this driver.');
+ }
+
+ /**
+ * Get declaration of a number of field in bulk
+ *
+ * @param array $fields a multidimensional associative array.
+ * The first dimension determines the field name, while the second
+ * dimension is keyed with the name of the properties
+ * of the field being declared as array indexes. Currently, the types
+ * of supported field properties are as follows:
+ *
+ * length
+ * Integer value that determines the maximum length of the text
+ * field. If this argument is missing the field should be
+ * declared to have the longest length allowed by the DBMS.
+ *
+ * default
+ * Text value to be used as default for this field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is constrained
+ * to not be set to null.
+ * charset
+ * Text value with the default CHARACTER SET for this field.
+ * collation
+ * Text value with the default COLLATION for this field.
+ * unique
+ * unique constraint
+ *
+ * @return string
+ */
+ public function getFieldDeclarationListSql(array $fields)
+ {
+ foreach ($fields as $fieldName => $field) {
+ $query = $this->getDeclarationSql($fieldName, $field);
+
+ $queryFields[] = $query;
+ }
+ return implode(', ', $queryFields);
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to declare a generic type
+ * field to be used in statements like CREATE TABLE.
+ *
+ * @param string $name name the field to be declared.
+ * @param array $field associative array with the name of the properties
+ * of the field being declared as array indexes. Currently, the types
+ * of supported field properties are as follows:
+ *
+ * length
+ * Integer value that determines the maximum length of the text
+ * field. If this argument is missing the field should be
+ * declared to have the longest length allowed by the DBMS.
+ *
+ * default
+ * Text value to be used as default for this field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is constrained
+ * to not be set to null.
+ * charset
+ * Text value with the default CHARACTER SET for this field.
+ * collation
+ * Text value with the default COLLATION for this field.
+ * unique
+ * unique constraint
+ * check
+ * column check constraint
+ *
+ * @return string DBMS specific SQL code portion that should be used to
+ * declare the specified field.
+ */
+ public function getDeclarationSql($name, array $field)
+ {
+ $default = $this->getDefaultFieldDeclarationSql($field);
+
+ $charset = (isset($field['charset']) && $field['charset']) ?
+ ' ' . $this->getCharsetFieldDeclarationSql($field['charset']) : '';
+
+ $collation = (isset($field['collation']) && $field['collation']) ?
+ ' ' . $this->getCollationFieldDeclarationSql($field['collation']) : '';
+
+ $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
+
+ $unique = (isset($field['unique']) && $field['unique']) ?
+ ' ' . $this->getUniqueFieldDeclarationSql() : '';
+
+ $check = (isset($field['check']) && $field['check']) ?
+ ' ' . $field['check'] : '';
+
+ $method = 'get' . $field['type'] . 'Declaration';
+
+ if (method_exists($this, $method)) {
+ return $this->$method($name, $field);
+ } else {
+ $dec = $this->getNativeDeclaration($field);
+ }
+
+ return $this->quoteIdentifier($name, true) . ' ' . $dec . $charset . $default . $notnull . $unique . $check . $collation;
+ }
+
+ /**
+ * getDefaultDeclaration
+ * Obtain DBMS specific SQL code portion needed to set a default value
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @param array $field field definition array
+ * @return string DBMS specific SQL code portion needed to set a default value
+ */
+ public function getDefaultFieldDeclarationSql($field)
+ {
+ $default = '';
+ if (isset($field['default'])) {
+ if ($field['default'] === '') {
+ $field['default'] = empty($field['notnull'])
+ ? null : $this->valid_default_values[$field['type']];
+
+ if ($field['default'] === '' &&
+ ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL)) {
+ $field['default'] = null;
+ }
+ }
+
+ if ($field['type'] === 'boolean') {
+ $field['default'] = $this->convertBooleans($field['default']);
+ }
+ $default = ' DEFAULT ' . $this->quote($field['default'], $field['type']);
+ }
+ return $default;
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set a CHECK constraint
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @param array $definition check definition
+ * @return string DBMS specific SQL code portion needed to set a CHECK constraint
+ */
+ public function getCheckDeclarationSql(array $definition)
+ {
+ $constraints = array();
+ foreach ($definition as $field => $def) {
+ if (is_string($def)) {
+ $constraints[] = 'CHECK (' . $def . ')';
+ } else {
+ if (isset($def['min'])) {
+ $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
+ }
+
+ if (isset($def['max'])) {
+ $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
+ }
+ }
+ }
+
+ return implode(', ', $constraints);
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set an index
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @param string $name name of the index
+ * @param array $definition index definition
+ * @return string DBMS specific SQL code portion needed to set an index
+ */
+ public function getIndexDeclarationSql($name, array $definition)
+ {
+ $name = $this->quoteIdentifier($name);
+ $type = '';
+
+ if (isset($definition['type'])) {
+ if (strtolower($definition['type']) == 'unique') {
+ $type = strtoupper($definition['type']) . ' ';
+ } else {
+ throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) {
+ throw new Doctrine_Export_Exception('No index columns given.');
+ }
+
+ $query = $type . 'INDEX ' . $name;
+
+ $query .= ' (' . $this->getIndexFieldDeclarationListSql($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ /**
+ * getIndexFieldDeclarationList
+ * Obtain DBMS specific SQL code portion needed to set an index
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @return string
+ */
+ public function getIndexFieldDeclarationListSql(array $fields)
+ {
+ $ret = array();
+ foreach ($fields as $field => $definition) {
+ if (is_array($definition)) {
+ $ret[] = $this->quoteIdentifier($field);
+ } else {
+ $ret[] = $this->quoteIdentifier($definition);
+ }
+ }
+ return implode(', ', $ret);
+ }
+
+ /**
+ * A method to return the required SQL string that fits between CREATE ... TABLE
+ * to create the table as a temporary table.
+ *
+ * Should be overridden in driver classes to return the correct string for the
+ * specific database type.
+ *
+ * The default is to return the string "TEMPORARY" - this will result in a
+ * SQL error for any database that does not support temporary tables, or that
+ * requires a different SQL command from "CREATE TEMPORARY TABLE".
+ *
+ * @return string The string required to be placed between "CREATE" and "TABLE"
+ * to generate a temporary table, if possible.
+ */
+ public function getTemporaryTableSql()
+ {
+ return 'TEMPORARY';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @return unknown
+ */
+ public function getShowDatabasesSql()
+ {
+ throw new Doctrine_Export_Exception('Show databases not supported by this driver.');
+ }
+
+ /**
+ * getForeignKeyDeclaration
+ * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @param array $definition an associative array with the following structure:
+ * name optional constraint name
+ *
+ * local the local field(s)
+ *
+ * foreign the foreign reference field(s)
+ *
+ * foreignTable the name of the foreign table
+ *
+ * onDelete referential delete action
+ *
+ * onUpdate referential update action
+ *
+ * deferred deferred constraint checking
+ *
+ * The onDelete and onUpdate keys accept the following values:
+ *
+ * CASCADE: Delete or update the row from the parent table and automatically delete or
+ * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.
+ * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column
+ * in the parent table or in the child table.
+ *
+ * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the
+ * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier
+ * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
+ *
+ * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary
+ * key value is not allowed to proceed if there is a related foreign key value in the referenced table.
+ *
+ * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as
+ * omitting the ON DELETE or ON UPDATE clause.
+ *
+ * SET DEFAULT
+ *
+ * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
+ * of a field declaration.
+ */
+ public function getForeignKeyDeclarationSql(array $definition)
+ {
+ $sql = $this->getForeignKeyBaseDeclarationSql($definition);
+ $sql .= $this->getAdvancedForeignKeyOptionsSql($definition);
+
+ return $sql;
+ }
+
+ /**
+ * getAdvancedForeignKeyOptions
+ * Return the FOREIGN KEY query section dealing with non-standard options
+ * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
+ *
+ * @param array $definition foreign key definition
+ * @return string
+ */
+ public function getAdvancedForeignKeyOptionsSql(array $definition)
+ {
+ $query = '';
+ if ( ! empty($definition['onUpdate'])) {
+ $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSql($definition['onUpdate']);
+ }
+ if ( ! empty($definition['onDelete'])) {
+ $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSql($definition['onDelete']);
+ }
+ return $query;
+ }
+
+ /**
+ * returns given referential action in uppercase if valid, otherwise throws
+ * an exception
+ *
+ * @throws Doctrine_Exception_Exception if unknown referential action given
+ * @param string $action foreign key referential action
+ * @param string foreign key referential action in uppercase
+ */
+ public function getForeignKeyReferentialActionSql($action)
+ {
+ $upper = strtoupper($action);
+ switch ($upper) {
+ case 'CASCADE':
+ case 'SET NULL':
+ case 'NO ACTION':
+ case 'RESTRICT':
+ case 'SET DEFAULT':
+ return $upper;
+ break;
+ default:
+ throw new Doctrine_Export_Exception('Unknown foreign key referential action \'' . $upper . '\' given.');
+ }
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @param array $definition
+ * @return string
+ */
+ public function getForeignKeyBaseDeclarationSql(array $definition)
+ {
+ $sql = '';
+ if (isset($definition['name'])) {
+ $sql .= ' CONSTRAINT ' . $this->quoteIdentifier($definition['name']) . ' ';
+ }
+ $sql .= 'FOREIGN KEY (';
+
+ if ( ! isset($definition['local'])) {
+ throw new Doctrine_Export_Exception('Local reference field missing from definition.');
+ }
+ if ( ! isset($definition['foreign'])) {
+ throw new Doctrine_Export_Exception('Foreign reference field missing from definition.');
+ }
+ if ( ! isset($definition['foreignTable'])) {
+ throw new Doctrine_Export_Exception('Foreign reference table missing from definition.');
+ }
+
+ if ( ! is_array($definition['local'])) {
+ $definition['local'] = array($definition['local']);
+ }
+ if ( ! is_array($definition['foreign'])) {
+ $definition['foreign'] = array($definition['foreign']);
+ }
+
+ $sql .= implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['local']))
+ . ') REFERENCES '
+ . $this->_conn->quoteIdentifier($definition['foreignTable']) . '('
+ . implode(', ', array_map(array($this, 'quoteIdentifier'), $definition['foreign'])) . ')';
+
+ return $sql;
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
+ * of a field declaration.
+ */
+ public function getUniqueFieldDeclarationSql()
+ {
+ return 'UNIQUE';
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @param string $charset name of the charset
+ * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
+ * of a field declaration.
+ */
+ public function getCharsetFieldDeclarationSql($charset)
+ {
+ return '';
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set the COLLATION
+ * of a field declaration to be used in statements like CREATE TABLE.
+ *
+ * @param string $collation name of the collation
+ * @return string DBMS specific SQL code portion needed to set the COLLATION
+ * of a field declaration.
+ */
+ public function getCollationFieldDeclarationSql($collation)
+ {
+ return '';
+ }
+
+ /**
* build a pattern matching string
*
* EXPERIMENTAL
@@ -838,7 +1647,7 @@ abstract class Doctrine_DatabasePlatform
* @return string DBMS specific SQL code portion that should be used to
* declare the specified field.
*/
- abstract public function getNativeDeclaration($field);
+ abstract public function getNativeDeclaration(array $field);
/**
* Maps a native array description of a field to a Doctrine datatype and length
@@ -931,6 +1740,31 @@ abstract class Doctrine_DatabasePlatform
return $this->_properties[$name];
}
+ /**
+ * Some platforms need the boolean values to be converted.
+ * Default conversion defined here converts to integers.
+ *
+ * @param array $item
+ * @return void
+ */
+ public function convertBooleans($item)
+ {
+ if (is_array($item)) {
+ foreach ($item as $k => $value) {
+ if (is_bool($value)) {
+ $item[$k] = (int) $value;
+ }
+ }
+ } else {
+ if (is_bool($item)) {
+ $item = (int) $item;
+ }
+ }
+ return $item;
+ }
+
+ /* supports*() metods */
+
public function supportsSequences()
{
return false;
diff --git a/lib/Doctrine/DatabasePlatform/MockPlatform.php b/lib/Doctrine/DatabasePlatform/MockPlatform.php
new file mode 100644
index 000000000..a00a6ded5
--- /dev/null
+++ b/lib/Doctrine/DatabasePlatform/MockPlatform.php
@@ -0,0 +1,9 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php
index 90cd6503e..8cdddeddc 100644
--- a/lib/Doctrine/DatabasePlatform/MySqlPlatform.php
+++ b/lib/Doctrine/DatabasePlatform/MySqlPlatform.php
@@ -230,7 +230,7 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform
/**
* @TEST
*/
- public function getVarcharDeclaration(array $field)
+ public function getVarcharDeclarationSql(array $field)
{
if ( ! isset($field['length'])) {
if (array_key_exists('default', $field)) {
@@ -247,6 +247,26 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform
: ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
}
+ /**
+ * Enter description here...
+ *
+ * @param array $field
+ */
+ public function getClobDeclarationSql(array $field)
+ {
+ if ( ! empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 255) {
+ return 'TINYTEXT';
+ } else if ($length <= 65532) {
+ return 'TEXT';
+ } else if ($length <= 16777215) {
+ return 'MEDIUMTEXT';
+ }
+ }
+ return 'LONGTEXT';
+ }
+
/**
* Obtain DBMS specific SQL code portion needed to declare an text type
* field to be used in statements like CREATE TABLE.
@@ -271,7 +291,7 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform
* declare the specified field.
* @override
*/
- public function getNativeDeclaration($field)
+ public function getNativeDeclaration(array $field)
{
if ( ! isset($field['type'])) {
throw new Doctrine_DataDict_Exception('Missing column type.');
@@ -287,31 +307,9 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform
case 'object':
case 'string':
case 'gzip':
- if ( ! isset($field['length'])) {
- if (array_key_exists('default', $field)) {
- $field['length'] = $this->conn->varchar_max_length;
- } else {
- $field['length'] = false;
- }
- }
-
- $length = ($field['length'] <= $this->conn->varchar_max_length) ? $field['length'] : false;
- $fixed = (isset($field['fixed'])) ? $field['fixed'] : false;
-
- return $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
- : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
+ return $this->getVarcharDeclarationSql($field);
case 'clob':
- if ( ! empty($field['length'])) {
- $length = $field['length'];
- if ($length <= 255) {
- return 'TINYTEXT';
- } elseif ($length <= 65532) {
- return 'TEXT';
- } elseif ($length <= 16777215) {
- return 'MEDIUMTEXT';
- }
- }
- return 'LONGTEXT';
+ return $this->getClobDeclarationSql($field);
case 'blob':
if ( ! empty($field['length'])) {
$length = $field['length'];
@@ -592,6 +590,648 @@ class Doctrine_DatabasePlatform_MySqlPlatform extends Doctrine_DatabasePlatform
{
return false;
}
+
+ /**
+ * Enter description here...
+ *
+ * @return unknown
+ * @override
+ */
+ public function getShowDatabasesSql()
+ {
+ return 'SHOW DATABASES';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @todo Throw exception by default?
+ * @override
+ */
+ public function getListTablesSql()
+ {
+ return 'SHOW TABLES';
+ }
+
+ /**
+ * create a new database
+ *
+ * @param string $name name of the database that should be created
+ * @return string
+ * @override
+ */
+ public function getCreateDatabaseSql($name)
+ {
+ return 'CREATE DATABASE ' . $this->quoteIdentifier($name);
+ }
+
+ /**
+ * drop an existing database
+ *
+ * @param string $name name of the database that should be dropped
+ * @return string
+ * @override
+ */
+ public function getDropDatabaseSql($name)
+ {
+ return 'DROP DATABASE ' . $this->quoteIdentifier($name);
+ }
+
+ /**
+ * create a new table
+ *
+ * @param string $name Name of the database that should be created
+ * @param array $fields Associative array that contains the definition of each field of the new table
+ * The indexes of the array entries are the names of the fields of the table an
+ * the array entry values are associative arrays like those that are meant to be
+ * passed with the field definitions to get[Type]Declaration() functions.
+ * array(
+ * 'id' => array(
+ * 'type' => 'integer',
+ * 'unsigned' => 1
+ * 'notnull' => 1
+ * 'default' => 0
+ * ),
+ * 'name' => array(
+ * 'type' => 'text',
+ * 'length' => 12
+ * ),
+ * 'password' => array(
+ * 'type' => 'text',
+ * 'length' => 12
+ * )
+ * );
+ * @param array $options An associative array of table options:
+ * array(
+ * 'comment' => 'Foo',
+ * 'charset' => 'utf8',
+ * 'collate' => 'utf8_unicode_ci',
+ * 'type' => 'innodb',
+ * );
+ *
+ * @return void
+ * @override
+ */
+ public function getCreateTableSql($name, array $fields, array $options = array())
+ {
+ if ( ! $name) {
+ throw new Doctrine_Export_Exception('no valid table name specified');
+ }
+ if (empty($fields)) {
+ throw new Doctrine_Export_Exception('no fields specified for table "'.$name.'"');
+ }
+ $queryFields = $this->getFieldDeclarationListSql($fields);
+
+ // build indexes for all foreign key fields (needed in MySQL!!)
+ if (isset($options['foreignKeys'])) {
+ foreach ($options['foreignKeys'] as $fk) {
+ $local = $fk['local'];
+ $found = false;
+ if (isset($options['indexes'])) {
+ foreach ($options['indexes'] as $definition) {
+ if (is_string($definition['fields'])) {
+ // Check if index already exists on the column
+ $found = ($local == $definition['fields']);
+ } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) {
+ // Index already exists on the column
+ $found = true;
+ }
+ }
+ }
+ if (isset($options['primary']) && !empty($options['primary']) &&
+ in_array($local, $options['primary'])) {
+ // field is part of the PK and therefore already indexed
+ $found = true;
+ }
+
+ if ( ! $found) {
+ $options['indexes'][$local] = array('fields' => array($local => array()));
+ }
+ }
+ }
+
+ // add all indexes
+ if (isset($options['indexes']) && ! empty($options['indexes'])) {
+ foreach($options['indexes'] as $index => $definition) {
+ $queryFields .= ', ' . $this->getIndexDeclarationSql($index, $definition);
+ }
+ }
+
+ // attach all primary keys
+ if (isset($options['primary']) && ! empty($options['primary'])) {
+ $keyColumns = array_values($options['primary']);
+ $keyColumns = array_map(array($this->_conn, 'quoteIdentifier'), $keyColumns);
+ $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
+ }
+
+ $query = 'CREATE ';
+ if (!empty($options['temporary'])) {
+ $query .= 'TEMPORARY ';
+ }
+ $query.= 'TABLE ' . $this->quoteIdentifier($name, true) . ' (' . $queryFields . ')';
+
+ $optionStrings = array();
+
+ if (isset($options['comment'])) {
+ $optionStrings['comment'] = 'COMMENT = ' . $this->quote($options['comment'], 'text');
+ }
+ if (isset($options['charset'])) {
+ $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset'];
+ if (isset($options['collate'])) {
+ $optionStrings['charset'] .= ' COLLATE ' . $options['collate'];
+ }
+ }
+
+ $type = false;
+
+ // get the type of the table
+ if (isset($options['type'])) {
+ $type = $options['type'];
+ } else {
+ $type = $this->getAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE);
+ }
+
+ if ($type) {
+ $optionStrings[] = 'ENGINE = ' . $type;
+ }
+
+ if ( ! empty($optionStrings)) {
+ $query.= ' '.implode(' ', $optionStrings);
+ }
+ $sql[] = $query;
+
+ if (isset($options['foreignKeys'])) {
+ foreach ((array) $options['foreignKeys'] as $k => $definition) {
+ if (is_array($definition)) {
+ $sql[] = $this->getCreateForeignKeySql($name, $definition);
+ }
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * alter an existing table
+ *
+ * @param string $name name of the table that is intended to be changed.
+ * @param array $changes associative array that contains the details of each type
+ * of change that is intended to be performed. The types of
+ * changes that are currently supported are defined as follows:
+ *
+ * name
+ *
+ * New name for the table.
+ *
+ * add
+ *
+ * Associative array with the names of fields to be added as
+ * indexes of the array. The value of each entry of the array
+ * should be set to another associative array with the properties
+ * of the fields to be added. The properties of the fields should
+ * be the same as defined by the Metabase parser.
+ *
+ *
+ * remove
+ *
+ * Associative array with the names of fields to be removed as indexes
+ * of the array. Currently the values assigned to each entry are ignored.
+ * An empty array should be used for future compatibility.
+ *
+ * rename
+ *
+ * Associative array with the names of fields to be renamed as indexes
+ * of the array. The value of each entry of the array should be set to
+ * another associative array with the entry named name with the new
+ * field name and the entry named Declaration that is expected to contain
+ * the portion of the field declaration already in DBMS specific SQL code
+ * as it is used in the CREATE TABLE statement.
+ *
+ * change
+ *
+ * Associative array with the names of the fields to be changed as indexes
+ * of the array. Keep in mind that if it is intended to change either the
+ * name of a field and any other properties, the change array entries
+ * should have the new names of the fields as array indexes.
+ *
+ * The value of each entry of the array should be set to another associative
+ * array with the properties of the fields to that are meant to be changed as
+ * array entries. These entries should be assigned to the new values of the
+ * respective properties. The properties of the fields should be the same
+ * as defined by the Metabase parser.
+ *
+ * Example
+ * array(
+ * 'name' => 'userlist',
+ * 'add' => array(
+ * 'quota' => array(
+ * 'type' => 'integer',
+ * 'unsigned' => 1
+ * )
+ * ),
+ * 'remove' => array(
+ * 'file_limit' => array(),
+ * 'time_limit' => array()
+ * ),
+ * 'change' => array(
+ * 'name' => array(
+ * 'length' => '20',
+ * 'definition' => array(
+ * 'type' => 'text',
+ * 'length' => 20,
+ * ),
+ * )
+ * ),
+ * 'rename' => array(
+ * 'sex' => array(
+ * 'name' => 'gender',
+ * 'definition' => array(
+ * 'type' => 'text',
+ * 'length' => 1,
+ * 'default' => 'M',
+ * ),
+ * )
+ * )
+ * )
+ *
+ * @param boolean $check indicates whether the function should just check if the DBMS driver
+ * can perform the requested table alterations if the value is true or
+ * actually perform them otherwise.
+ * @return boolean
+ * @override
+ */
+ public function getAlterTableSql($name, array $changes, $check = false)
+ {
+ if ( ! $name) {
+ throw new Doctrine_Export_Exception('no valid table name specified');
+ }
+ foreach ($changes as $changeName => $change) {
+ switch ($changeName) {
+ case 'add':
+ case 'remove':
+ case 'change':
+ case 'rename':
+ case 'name':
+ break;
+ default:
+ throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported');
+ }
+ }
+
+ if ($check) {
+ return true;
+ }
+
+ $query = '';
+ if ( ! empty($changes['name'])) {
+ $change_name = $this->quoteIdentifier($changes['name']);
+ $query .= 'RENAME TO ' . $change_name;
+ }
+
+ if ( ! empty($changes['add']) && is_array($changes['add'])) {
+ foreach ($changes['add'] as $fieldName => $field) {
+ if ($query) {
+ $query.= ', ';
+ }
+ $query.= 'ADD ' . $this->getDeclarationSql($fieldName, $field);
+ }
+ }
+
+ if ( ! empty($changes['remove']) && is_array($changes['remove'])) {
+ foreach ($changes['remove'] as $fieldName => $field) {
+ if ($query) {
+ $query .= ', ';
+ }
+ $fieldName = $this->quoteIdentifier($fieldName);
+ $query .= 'DROP ' . $fieldName;
+ }
+ }
+
+ $rename = array();
+ if ( ! empty($changes['rename']) && is_array($changes['rename'])) {
+ foreach ($changes['rename'] as $fieldName => $field) {
+ $rename[$field['name']] = $fieldName;
+ }
+ }
+
+ if ( ! empty($changes['change']) && is_array($changes['change'])) {
+ foreach ($changes['change'] as $fieldName => $field) {
+ if ($query) {
+ $query.= ', ';
+ }
+ if (isset($rename[$fieldName])) {
+ $oldFieldName = $rename[$fieldName];
+ unset($rename[$fieldName]);
+ } else {
+ $oldFieldName = $fieldName;
+ }
+ $oldFieldName = $this->quoteIdentifier($oldFieldName, true);
+ $query .= 'CHANGE ' . $oldFieldName . ' '
+ . $this->getDeclarationSql($fieldName, $field['definition']);
+ }
+ }
+
+ if ( ! empty($rename) && is_array($rename)) {
+ foreach ($rename as $renameName => $renamedField) {
+ if ($query) {
+ $query.= ', ';
+ }
+ $field = $changes['rename'][$renamedField];
+ $renamedField = $this->quoteIdentifier($renamedField, true);
+ $query .= 'CHANGE ' . $renamedField . ' '
+ . $this->getDeclarationSql($field['name'], $field['definition']);
+ }
+ }
+
+ if ( ! $query) {
+ return false;
+ }
+
+ $name = $this->quoteIdentifier($name, true);
+
+ return 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+
+ /**
+ * Get the stucture of a field into an array
+ *
+ * @author Leoncx
+ * @param string $table name of the table on which the index is to be created
+ * @param string $name name of the index to be created
+ * @param array $definition associative array that defines properties of the index to be created.
+ * Currently, only one property named FIELDS is supported. This property
+ * is also an associative with the names of the index fields as array
+ * indexes. Each entry of this array is set to another type of associative
+ * array that specifies properties of the index that are specific to
+ * each field.
+ *
+ * Currently, only the sorting property is supported. It should be used
+ * to define the sorting direction of the index. It may be set to either
+ * ascending or descending.
+ *
+ * Not all DBMS support index sorting direction configuration. The DBMS
+ * drivers of those that do not support it ignore this property. Use the
+ * function supports() to determine whether the DBMS driver can manage indexes.
+ *
+ * Example
+ * array(
+ * 'fields' => array(
+ * 'user_name' => array(
+ * 'sorting' => 'ASC'
+ * 'length' => 10
+ * ),
+ * 'last_login' => array()
+ * )
+ * )
+ * @throws PDOException
+ * @return void
+ * @override
+ */
+ public function getCreateIndexSql($table, $name, array $definition)
+ {
+ $table = $table;
+ $name = $this->formatter->getIndexName($name);
+ $name = $this->quoteIdentifier($name);
+ $type = '';
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'fulltext':
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+ $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
+ $query .= ' (' . $this->getIndexFieldDeclarationListSql($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to declare an integer type
+ * field to be used in statements like CREATE TABLE.
+ *
+ * @param string $name name the field to be declared.
+ * @param string $field associative array with the name of the properties
+ * of the field being declared as array indexes.
+ * Currently, the types of supported field
+ * properties are as follows:
+ *
+ * unsigned
+ * Boolean flag that indicates whether the field
+ * should be declared as unsigned integer if
+ * possible.
+ *
+ * default
+ * Integer value to be used as default for this
+ * field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is
+ * constrained to not be set to null.
+ * @return string DBMS specific SQL code portion that should be used to
+ * declare the specified field.
+ * @override
+ */
+ public function getIntegerDeclarationSql($name, $field)
+ {
+ $default = $autoinc = '';
+ if ( ! empty($field['autoincrement'])) {
+ $autoinc = ' AUTO_INCREMENT';
+ } elseif (array_key_exists('default', $field)) {
+ if ($field['default'] === '') {
+ $field['default'] = empty($field['notnull']) ? null : 0;
+ }
+ if (is_null($field['default'])) {
+ $default = ' DEFAULT NULL';
+ } else {
+ $default = ' DEFAULT '.$this->quote($field['default']);
+ }
+ } elseif (empty($field['notnull'])) {
+ $default = ' DEFAULT NULL';
+ }
+
+ $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
+ $unsigned = (isset($field['unsigned']) && $field['unsigned']) ? ' UNSIGNED' : '';
+
+ $name = $this->quoteIdentifier($name, true);
+
+ return $name . ' ' . $this->getNativeDeclaration($field) . $unsigned . $default . $notnull . $autoinc;
+ }
+
+ /**
+ * getDefaultDeclaration
+ * Obtain DBMS specific SQL code portion needed to set a default value
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @param array $field field definition array
+ * @return string DBMS specific SQL code portion needed to set a default value
+ * @override
+ */
+ public function getDefaultFieldDeclarationSql($field)
+ {
+ $default = empty($field['notnull']) && !in_array($field['type'], array('clob', 'blob'))
+ ? ' DEFAULT NULL' : '';
+
+ if (isset($field['default']) && ( ! isset($field['length']) || $field['length'] <= 255)) {
+ if ($field['default'] === '') {
+ $field['default'] = null;
+ if (! empty($field['notnull']) && array_key_exists($field['type'], $this->valid_default_values)) {
+ $field['default'] = $this->valid_default_values[$field['type']];
+ }
+
+ if ($field['default'] === ''
+ && ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL)
+ ) {
+ $field['default'] = ' ';
+ }
+ }
+
+ if ($field['type'] == 'enum' && $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) {
+ $fieldType = 'varchar';
+ } else {
+ if ($field['type'] === 'boolean') {
+ $fields['default'] = $this->convertBooleans($field['default']);
+ }
+ $fieldType = $field['type'];
+ }
+
+ $default = ' DEFAULT ' . $this->quote($field['default'], $fieldType);
+ }
+ return $default;
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to set an index
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @param string $charset name of the index
+ * @param array $definition index definition
+ * @return string DBMS specific SQL code portion needed to set an index
+ * @override
+ */
+ public function getIndexDeclarationSql($name, array $definition)
+ {
+ $name = $this->formatter->getIndexName($name);
+ $type = '';
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'fulltext':
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ if ( ! isset($definition['fields'])) {
+ throw new Doctrine_Export_Exception('No index columns given.');
+ }
+ if ( ! is_array($definition['fields'])) {
+ $definition['fields'] = array($definition['fields']);
+ }
+
+ $query = $type . 'INDEX ' . $this->quoteIdentifier($name);
+
+ $query .= ' (' . $this->getIndexFieldDeclarationListSql($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ /**
+ * getIndexFieldDeclarationList
+ * Obtain DBMS specific SQL code portion needed to set an index
+ * declaration to be used in statements like CREATE TABLE.
+ *
+ * @return string
+ * @override
+ */
+ public function getIndexFieldDeclarationListSql(array $fields)
+ {
+ $declFields = array();
+
+ foreach ($fields as $fieldName => $field) {
+ $fieldString = $this->quoteIdentifier($fieldName);
+
+ if (is_array($field)) {
+ if (isset($field['length'])) {
+ $fieldString .= '(' . $field['length'] . ')';
+ }
+
+ if (isset($field['sorting'])) {
+ $sort = strtoupper($field['sorting']);
+ switch ($sort) {
+ case 'ASC':
+ case 'DESC':
+ $fieldString .= ' ' . $sort;
+ break;
+ default:
+ throw new Doctrine_Export_Exception('Unknown index sorting option given.');
+ }
+ }
+ } else {
+ $fieldString = $this->quoteIdentifier($field);
+ }
+ $declFields[] = $fieldString;
+ }
+ return implode(', ', $declFields);
+ }
+
+ /**
+ * getAdvancedForeignKeyOptions
+ * Return the FOREIGN KEY query section dealing with non-standard options
+ * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
+ *
+ * @param array $definition
+ * @return string
+ * @override
+ */
+ public function getAdvancedForeignKeyOptionsSql(array $definition)
+ {
+ $query = '';
+ if ( ! empty($definition['match'])) {
+ $query .= ' MATCH ' . $definition['match'];
+ }
+ if ( ! empty($definition['onUpdate'])) {
+ $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialActionSql($definition['onUpdate']);
+ }
+ if ( ! empty($definition['onDelete'])) {
+ $query .= ' ON DELETE ' . $this->getForeignKeyReferentialActionSql($definition['onDelete']);
+ }
+ return $query;
+ }
+
+ /**
+ * drop existing index
+ *
+ * @param string $table name of table that should be used in method
+ * @param string $name name of the index to be dropped
+ * @return void
+ * @override
+ */
+ public function getDropIndexSql($table, $name)
+ {
+ $table = $this->quoteIdentifier($table, true);
+ $name = $this->quoteIdentifier($this->formatter->getIndexName($name), true);
+ return 'DROP INDEX ' . $name . ' ON ' . $table;
+ }
+
+ /**
+ * dropTable
+ *
+ * @param string $table name of table that should be dropped from the database
+ * @throws PDOException
+ * @return void
+ * @override
+ */
+ public function getDropTableSql($table)
+ {
+ $table = $this->quoteIdentifier($table, true);
+ return 'DROP TABLE ' . $table;
+ }
}
?>
\ No newline at end of file
diff --git a/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php b/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php
index 75b30ed88..8fafe54ee 100644
--- a/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php
+++ b/lib/Doctrine/DatabasePlatform/PostgreSqlPlatform.php
@@ -520,6 +520,474 @@ class Doctrine_DatabasePlatform_PostgreSqlPlatform extends Doctrine_DatabasePlat
{
return true;
}
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListDatabasesSql()
+ {
+ return 'SELECT datname FROM pg_database';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListFunctionsSql()
+ {
+ return "SELECT
+ proname
+ FROM
+ pg_proc pr, pg_type tp
+ WHERE
+ tp.oid = pr.prorettype
+ AND pr.proisagg = FALSE
+ AND tp.typname <> 'trigger'
+ AND pr.pronamespace IN
+ (SELECT oid FROM pg_namespace
+ WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema'";
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListSequencesSql()
+ {
+ return "SELECT
+ relname
+ FROM
+ pg_class
+ WHERE relkind = 'S' AND relnamespace IN
+ (SELECT oid FROM pg_namespace
+ WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')";
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListTablesSql()
+ {
+ return "SELECT
+ c.relname AS table_name
+ FROM pg_class c, pg_user u
+ WHERE c.relowner = u.usesysid
+ AND c.relkind = 'r'
+ AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname)
+ AND c.relname !~ '^(pg_|sql_)'
+ UNION
+ SELECT c.relname AS table_name
+ FROM pg_class c
+ WHERE c.relkind = 'r'
+ AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname)
+ AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner)
+ AND c.relname !~ '^pg_'";
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListViewsSql()
+ {
+ return 'SELECT viewname FROM pg_views';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListUsersSql()
+ {
+ return 'SELECT usename FROM pg_user';
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListTableConstraintsSql()
+ {
+ return "SELECT
+ relname
+ FROM
+ pg_class
+ WHERE oid IN (
+ SELECT indexrelid
+ FROM pg_index, pg_class
+ WHERE pg_class.relname = %s
+ AND pg_class.oid = pg_index.indrelid
+ AND (indisunique = 't' OR indisprimary = 't')
+ )";
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListTableIndexesSql()
+ {
+ return "SELECT
+ relname
+ FROM
+ pg_class
+ WHERE oid IN (
+ SELECT indexrelid
+ FROM pg_index, pg_class
+ WHERE pg_class.relname = %s
+ AND pg_class.oid=pg_index.indrelid
+ AND indisunique != 't'
+ AND indisprimary != 't'
+ )";
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @override
+ */
+ public function getListTableColumnsSql()
+ {
+ return "SELECT
+ a.attnum,
+ a.attname AS field,
+ t.typname AS type,
+ format_type(a.atttypid, a.atttypmod) AS complete_type,
+ a.attnotnull AS isnotnull,
+ (SELECT 't'
+ FROM pg_index
+ WHERE c.oid = pg_index.indrelid
+ AND pg_index.indkey[0] = a.attnum
+ AND pg_index.indisprimary = 't'
+ ) AS pri,
+ (SELECT pg_attrdef.adsrc
+ FROM pg_attrdef
+ WHERE c.oid = pg_attrdef.adrelid
+ AND pg_attrdef.adnum=a.attnum
+ ) AS default
+ FROM pg_attribute a, pg_class c, pg_type t
+ WHERE c.relname = %s
+ AND a.attnum > 0
+ AND a.attrelid = c.oid
+ AND a.atttypid = t.oid
+ ORDER BY a.attnum";
+ }
+
+ /**
+ * create a new database
+ *
+ * @param string $name name of the database that should be created
+ * @throws PDOException
+ * @return void
+ * @override
+ */
+ public function getCreateDatabaseSql($name)
+ {
+ return 'CREATE DATABASE ' . $this->quoteIdentifier($name);
+ }
+
+ /**
+ * drop an existing database
+ *
+ * @param string $name name of the database that should be dropped
+ * @throws PDOException
+ * @access public
+ */
+ public function getDropDatabaseSql($name)
+ {
+ return 'DROP DATABASE ' . $this->quoteIdentifier($name);
+ }
+
+ /**
+ * getAdvancedForeignKeyOptions
+ * Return the FOREIGN KEY query section dealing with non-standard options
+ * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
+ *
+ * @param array $definition foreign key definition
+ * @return string
+ * @override
+ */
+ public function getAdvancedForeignKeyOptionsSql(array $definition)
+ {
+ $query = '';
+ if (isset($definition['match'])) {
+ $query .= ' MATCH ' . $definition['match'];
+ }
+ if (isset($definition['onUpdate'])) {
+ $query .= ' ON UPDATE ' . $definition['onUpdate'];
+ }
+ if (isset($definition['onDelete'])) {
+ $query .= ' ON DELETE ' . $definition['onDelete'];
+ }
+ if (isset($definition['deferrable'])) {
+ $query .= ' DEFERRABLE';
+ } else {
+ $query .= ' NOT DEFERRABLE';
+ }
+ if (isset($definition['feferred'])) {
+ $query .= ' INITIALLY DEFERRED';
+ } else {
+ $query .= ' INITIALLY IMMEDIATE';
+ }
+ return $query;
+ }
+
+ /**
+ * generates the sql for altering an existing table on postgresql
+ *
+ * @param string $name name of the table that is intended to be changed.
+ * @param array $changes associative array that contains the details of each type *
+ * @param boolean $check indicates whether the function should just check if the DBMS driver
+ * can perform the requested table alterations if the value is true or
+ * actually perform them otherwise.
+ * @see Doctrine_Export::alterTable()
+ * @return array
+ * @override
+ */
+ public function getAlterTableSql($name, array $changes, $check = false)
+ {
+ foreach ($changes as $changeName => $change) {
+ switch ($changeName) {
+ case 'add':
+ case 'remove':
+ case 'change':
+ case 'name':
+ case 'rename':
+ break;
+ default:
+ throw new Doctrine_Export_Exception('change type "' . $changeName . '\" not yet supported');
+ }
+ }
+
+ if ($check) {
+ return true;
+ }
+
+ $sql = array();
+
+ if (isset($changes['add']) && is_array($changes['add'])) {
+ foreach ($changes['add'] as $fieldName => $field) {
+ $query = 'ADD ' . $this->getDeclarationSql($fieldName, $field);
+ $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+ }
+
+ 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;
+ }
+ }
+
+ 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();
+
+ if (is_array($serverInfo) && $serverInfo['major'] < 8) {
+ throw new Doctrine_Export_Exception('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above');
+ }
+ $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->getTypeDeclarationSql($field['definition']);
+ $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+ if (array_key_exists('default', $field)) {
+ $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->quote($field['definition']['default'], $field['definition']['type']);
+ $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+ if ( ! empty($field['notnull'])) {
+ $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL';
+ $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+ }
+ }
+
+ 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);
+ }
+ }
+
+ $name = $this->quoteIdentifier($name, true);
+ if (isset($changes['name'])) {
+ $changeName = $this->quoteIdentifier($changes['name'], true);
+ $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName;
+ }
+
+ return $sql;
+ }
+
+ /**
+ * return RDBMS specific create sequence statement
+ *
+ * @throws Doctrine_Connection_Exception if something fails at database level
+ * @param string $seqName name of the sequence to be created
+ * @param string $start start value of the sequence; default is 1
+ * @param array $options An associative array of table options:
+ * array(
+ * 'comment' => 'Foo',
+ * 'charset' => 'utf8',
+ * 'collate' => 'utf8_unicode_ci',
+ * );
+ * @return string
+ * @override
+ */
+ public function getCreateSequenceSql($sequenceName, $start = 1, array $options = array())
+ {
+ $sequenceName = $this->quoteIdentifier($this->formatter->getSequenceName($sequenceName), true);
+ return 'CREATE SEQUENCE ' . $sequenceName . ' INCREMENT 1' .
+ ($start < 1 ? ' MINVALUE ' . $start : '') . ' START ' . $start;
+ }
+
+ /**
+ * drop existing sequence
+ *
+ * @param string $sequenceName name of the sequence to be dropped
+ * @override
+ */
+ public function getDropSequenceSql($sequenceName)
+ {
+ $sequenceName = $this->quoteIdentifier($this->formatter->getSequenceName($sequenceName), true);
+ return 'DROP SEQUENCE ' . $sequenceName;
+ }
+
+ /**
+ * Gets the SQL used to create a table.
+ *
+ * @param unknown_type $name
+ * @param array $fields
+ * @param array $options
+ * @return unknown
+ */
+ public function getCreateTableSql($name, array $fields, array $options = array())
+ {
+ if ( ! $name) {
+ throw new Doctrine_Export_Exception('no valid table name specified');
+ }
+ if (empty($fields)) {
+ throw new Doctrine_Export_Exception('no fields specified for table ' . $name);
+ }
+
+ $queryFields = $this->getFieldDeclarationListSql($fields);
+
+ if (isset($options['primary']) && ! empty($options['primary'])) {
+ $keyColumns = array_values($options['primary']);
+ $keyColumns = array_map(array($this, 'quoteIdentifier'), $keyColumns);
+ $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
+ }
+
+ $query = 'CREATE TABLE ' . $this->quoteIdentifier($name, true) . ' (' . $queryFields . ')';
+
+ $sql[] = $query;
+
+ if (isset($options['indexes']) && ! empty($options['indexes'])) {
+ foreach($options['indexes'] as $index => $definition) {
+ $sql[] = $this->getCreateIndexSql($name, $index, $definition);
+ }
+ }
+
+ if (isset($options['foreignKeys'])) {
+
+ foreach ((array) $options['foreignKeys'] as $k => $definition) {
+ if (is_array($definition)) {
+ $sql[] = $this->getCreateForeignKeySql($name, $definition);
+ }
+ }
+ }
+
+ return $sql;
+ }
+
+ /**
+ * Obtain DBMS specific SQL code portion needed to declare an integer type
+ * field to be used in statements like CREATE TABLE.
+ *
+ * @param string $name name the field to be declared.
+ * @param array $field associative array with the name of the properties
+ * of the field being declared as array indexes. Currently, the types
+ * of supported field properties are as follows:
+ *
+ * unsigned
+ * Boolean flag that indicates whether the field should be
+ * declared as unsigned integer if possible.
+ *
+ * default
+ * Integer value to be used as default for this field.
+ *
+ * notnull
+ * Boolean flag that indicates whether this field is constrained
+ * to not be set to null.
+ * @return string DBMS specific SQL code portion that should be used to
+ * declare the specified field.
+ */
+ public function getIntegerDeclarationSql($name, $field)
+ {
+ if ( ! empty($field['autoincrement'])) {
+ $name = $this->quoteIdentifier($name, true);
+ return $name . ' ' . $this->getNativeDeclaration($field);
+ }
+
+ $default = '';
+ if (array_key_exists('default', $field)) {
+ if ($field['default'] === '') {
+ $field['default'] = empty($field['notnull']) ? null : 0;
+ }
+ $default = ' DEFAULT '.$this->quote($field['default'], $field['type']);
+ } elseif (empty($field['notnull'])) {
+ $default = ' DEFAULT NULL';
+ }
+
+ $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
+ $name = $this->quoteIdentifier($name, true);
+
+ return $name . ' ' . $this->getNativeDeclaration($field) . $default . $notnull;
+ }
+
+ /**
+ * Postgres wants boolean values converted to the strings 'true'/'false'.
+ *
+ * @param array $item
+ * @return void
+ * @override
+ */
+ public function convertBooleans($item)
+ {
+ if (is_array($item)) {
+ foreach ($item as $key => $value) {
+ if (is_bool($value) || is_numeric($item)) {
+ $item[$key] = ($value) ? 'true' : 'false';
+ }
+ }
+ } else {
+ if (is_bool($item) || is_numeric($item)) {
+ $item = ($item) ? 'true' : 'false';
+ }
+ }
+ return $item;
+ }
+
+ /**
+ * Enter description here...
+ *
+ * @param string $sequenceName
+ * @override
+ */
+ public function getSequenceNextValSql($sequenceName)
+ {
+ return "SELECT NEXTVAL('" . $sequenceName . "')";
+ }
}
?>
\ No newline at end of file
diff --git a/lib/Doctrine/Entity.php b/lib/Doctrine/Entity.php
index 3b9c8f520..71498c692 100644
--- a/lib/Doctrine/Entity.php
+++ b/lib/Doctrine/Entity.php
@@ -429,8 +429,8 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable
if ($this->_class->hasField($fieldName)) {
$old = isset($this->_data[$fieldName]) ? $this->_data[$fieldName] : null;
// NOTE: Common case: $old != $value. Special case: null == 0 (TRUE), which
- // is addressed by the type comparison.
- if ($old != $value || gettype($old) != gettype($value)) {
+ // is addressed by xor.
+ if ($old != $value || (is_null($old) xor is_null($value))) {
$this->_data[$fieldName] = $value;
$this->_dataChangeSet[$fieldName] = array($old => $value);
if ($this->isNew() && $this->_class->isIdentifier($fieldName)) {
@@ -456,7 +456,10 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable
throw Doctrine_Entity_Exception::invalidField($fieldName);
}
}
-
+
+ /**
+ * Registers the entity as dirty with the UnitOfWork.
+ */
private function _registerDirty()
{
if ($this->_state == self::STATE_MANAGED &&
@@ -531,35 +534,33 @@ abstract class Doctrine_Entity implements ArrayAccess, Serializable
*/
final public function _internalSetReference($name, $value, $completeBidirectional = false)
{
- if ($value === Doctrine_Null::$INSTANCE) {
+ if (is_null($value) || $value === Doctrine_Null::$INSTANCE) {
$this->_references[$name] = $value;
- return;
+ return; // early exit!
}
$rel = $this->_class->getAssociationMapping($name);
if ($rel->isOneToOne() && ! $value instanceof Doctrine_Entity) {
throw Doctrine_Entity_Exception::invalidValueForOneToOneReference();
- }
- if ($rel->isOneToMany() && ! $value instanceof Doctrine_Collection) {
+ } else if (($rel->isOneToMany() || $rel->isManyToMany()) && ! $value instanceof Doctrine_Collection) {
throw Doctrine_Entity_Exception::invalidValueForOneToManyReference();
}
- if ($rel->isManyToMany() && ! $value instanceof Doctrine_Collection) {
- throw Doctrine_Entity_Exception::invalidValueForManyToManyReference();
- }
$this->_references[$name] = $value;
if ($completeBidirectional && $rel->isOneToOne()) {
- //TODO: check if $rel is bidirectional, if yes create the back-reference
if ($rel->isOwningSide()) {
- //TODO: how to check if its bidirectional? should be as efficient as possible
- /*$targetClass = $this->_em->getClassMetadata($rel->getTargetEntityName());
- if (($invAssoc = $targetClass->getInverseAssociation($name)) !== null) {
- $value->_internalSetReference($invAssoc->getSourceFieldName(), $this);
- }*/
+ // If there is an inverse mapping on the target class its bidirectional
+ $targetClass = $this->_em->getClassMetadata($rel->getTargetEntityName());
+ if ($targetClass->hasInverseAssociationMapping($name)) {
+ $value->_internalSetReference(
+ $targetClass->getInverseAssociationMapping($name)->getSourceFieldName(),
+ $this
+ );
+ }
} else {
- // for sure bi-directional, as there is no inverse side in unidirectional
+ // for sure bidirectional, as there is no inverse side in unidirectional
$value->_internalSetReference($rel->getMappedByFieldName(), $this);
}
}
diff --git a/lib/Doctrine/EntityManager.php b/lib/Doctrine/EntityManager.php
index eaeedf0fd..9351e5cd2 100644
--- a/lib/Doctrine/EntityManager.php
+++ b/lib/Doctrine/EntityManager.php
@@ -653,6 +653,35 @@ class Doctrine_EntityManager
return $this->_unitOfWork;
}
+ /**
+ * Enter description here...
+ *
+ * @param unknown_type $type
+ * @param unknown_type $class
+ */
+ /*public function getIdGenerator($class)
+ {
+ $type = $class->getIdGeneratorType();
+ if ($type == Doctrine_ClassMetadata::GENERATOR_TYPE_IDENTITY) {
+ if ( ! isset($this->_idGenerators[$type])) {
+ $this->_idGenerators[$type] = new Doctrine_Id_IdentityGenerator($this);
+ }
+ } else if ($type == Doctrine_ClassMetadata::GENERATOR_TYPE_SEQUENCE) {
+ if ( ! isset($this->_idGenerators[$type])) {
+ $this->_idGenerators[$type] = new Doctrine_Id_SequenceGenerator($this);
+ }
+ } else if ($type == Doctrine_ClassMetadata::GENERATOR_TYPE_TABLE) {
+ if ( ! isset($this->_idGenerators[$type])) {
+ $this->_idGenerators[$type] = new Doctrine_Id_TableGenerator($this);
+ }
+ }
+
+ $generator = $this->_idGenerators[$type];
+ $generator->configureForClass($class);
+
+ return $generator;
+ }*/
+
}
?>
\ No newline at end of file
diff --git a/lib/Doctrine/EntityManagerFactory.php b/lib/Doctrine/EntityManagerFactory.php
index 72d371aca..ef1e4c3de 100644
--- a/lib/Doctrine/EntityManagerFactory.php
+++ b/lib/Doctrine/EntityManagerFactory.php
@@ -59,7 +59,7 @@ class Doctrine_EntityManagerFactory
*/
public function __construct()
{
- $this->_connFactory = new Doctrine_ConnectionFactory();
+
}
/**
@@ -93,16 +93,20 @@ class Doctrine_EntityManagerFactory
*/
public function createEntityManager($connParams, $name = null)
{
- if ( ! $this->_config) {
- $this->_config = new Doctrine_Configuration();
- }
- if ( ! $this->_eventManager) {
- $this->_eventManager = new Doctrine_EventManager();
+ if ( ! $this->_connFactory) {
+ // Initialize connection factory
+ $this->_connFactory = new Doctrine_ConnectionFactory();
+ if ( ! $this->_config) {
+ $this->_config = new Doctrine_Configuration();
+ }
+ if ( ! $this->_eventManager) {
+ $this->_eventManager = new Doctrine_EventManager();
+ }
+ $this->_connFactory->setConfiguration($this->_config);
+ $this->_connFactory->setEventManager($this->_eventManager);
}
$conn = $this->_connFactory->createConnection($connParams);
- $conn->setEventManager($this->_eventManager);
- $conn->setConfiguration($this->_config);
$em = new Doctrine_EntityManager($conn);
$em->setEventManager($this->_eventManager);
diff --git a/lib/Doctrine/EntityPersister/Abstract.php b/lib/Doctrine/EntityPersister/Abstract.php
index d5738800a..6dfeb36b0 100644
--- a/lib/Doctrine/EntityPersister/Abstract.php
+++ b/lib/Doctrine/EntityPersister/Abstract.php
@@ -123,6 +123,7 @@ abstract class Doctrine_EntityPersister_Abstract
if ($class->isIdGeneratorIdentity()) {
//TODO: Postgres IDENTITY columns (SERIAL) use a sequence, so we need to pass the
// sequence name to lastInsertId().
+ //TODO: $this->_em->getIdGenerator($class)->generate();
$entity->_assignIdentifier($this->_conn->lastInsertId());
}
}
@@ -312,6 +313,8 @@ abstract class Doctrine_EntityPersister_Abstract
default:
$result[$columnName] = $newVal;
}
+ /*$result[$columnName] = $type->convertToDatabaseValue(
+ $newVal, $this->_em->getConnection()->getDatabasePlatform());*/
}
// @todo Cleanup
diff --git a/lib/Doctrine/EntityRepository.php b/lib/Doctrine/EntityRepository.php
index 496addf48..1fb7b30d5 100644
--- a/lib/Doctrine/EntityRepository.php
+++ b/lib/Doctrine/EntityRepository.php
@@ -96,6 +96,8 @@ class Doctrine_EntityRepository
$values = is_array($id) ? array_values($id) : array($id);
$keys = $this->_classMetadata->getIdentifier();
}
+
+ //TODO: check identity map?
return $this->_createQuery()
->where(implode(' = ? AND ', $keys) . ' = ?')
diff --git a/lib/Doctrine/Hydrator/RecordDriver.php b/lib/Doctrine/Hydrator/RecordDriver.php
index 894d5563e..77383eb0a 100644
--- a/lib/Doctrine/Hydrator/RecordDriver.php
+++ b/lib/Doctrine/Hydrator/RecordDriver.php
@@ -73,7 +73,8 @@ class Doctrine_Hydrator_RecordDriver
$relation = $entity->getClass()->getAssociationMapping($name);
$relatedClass = $this->_em->getClassMetadata($relation->getTargetEntityName());
$coll = $this->getElementCollection($relatedClass->getClassName());
- $coll->setReference($entity, $relation);
+ $coll->_setOwner($entity, $relation);
+ $coll->_setHydrationFlag(true);
$entity->_internalSetReference($name, $coll, true);
$this->_initializedRelations[$entity->getOid()][$name] = true;
}
@@ -145,7 +146,8 @@ class Doctrine_Hydrator_RecordDriver
{
// take snapshots from all initialized collections
foreach ($this->_collections as $coll) {
- $coll->takeSnapshot();
+ $coll->_takeSnapshot();
+ $coll->_setHydrationFlag(false);
}
$this->_collections = array();
$this->_initializedRelations = array();
diff --git a/lib/Doctrine/HydratorNew.php b/lib/Doctrine/HydratorNew.php
index 6facb152e..e05f35411 100644
--- a/lib/Doctrine/HydratorNew.php
+++ b/lib/Doctrine/HydratorNew.php
@@ -400,6 +400,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$rowData[$dqlAlias][$fieldName] = $this->prepareValue(
$class, $fieldName, $value, $cache[$key]['type']);
}
+ //$rowData[$dqlAlias][$fieldName] = $cache[$key]['type']->convertToObjectValue($value);
if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
$nonemptyComponents[$dqlAlias] = true;
@@ -468,6 +469,7 @@ class Doctrine_HydratorNew extends Doctrine_Hydrator_Abstract
$rowData[$dqlAlias . '_' . $fieldName] = $this->prepareValue(
$class, $fieldName, $value, $cache[$key]['type']);
}
+ //$rowData[$dqlAlias . '_' . $fieldName] = $cache[$key]['type']->convertToObjectValue($value);
}
return $rowData;
diff --git a/lib/Doctrine/Id/AbstractIdGenerator.php b/lib/Doctrine/Id/AbstractIdGenerator.php
new file mode 100644
index 000000000..1e55c7dd1
--- /dev/null
+++ b/lib/Doctrine/Id/AbstractIdGenerator.php
@@ -0,0 +1,24 @@
+_em = $em;
+ }
+
+ abstract public function configureForClass(Doctrine_ClassMetadata $class);
+
+ abstract public function generate();
+}
+
+?>
\ No newline at end of file
diff --git a/lib/Doctrine/Id/IdentityGenerator.php b/lib/Doctrine/Id/IdentityGenerator.php
new file mode 100644
index 000000000..6c9b33c3f
--- /dev/null
+++ b/lib/Doctrine/Id/IdentityGenerator.php
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/Id/SequenceGenerator.php b/lib/Doctrine/Id/SequenceGenerator.php
new file mode 100644
index 000000000..15c5adc7f
--- /dev/null
+++ b/lib/Doctrine/Id/SequenceGenerator.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/Id/TableGenerator.php b/lib/Doctrine/Id/TableGenerator.php
new file mode 100644
index 000000000..15c5adc7f
--- /dev/null
+++ b/lib/Doctrine/Id/TableGenerator.php
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/lib/Doctrine/Import.php b/lib/Doctrine/Import.php
index 4588559d3..67cf73f0e 100644
--- a/lib/Doctrine/Import.php
+++ b/lib/Doctrine/Import.php
@@ -19,7 +19,7 @@
* .
*/
-#namespace Doctrine::DBAL::Import;
+#namespace Doctrine::ORM::Import;
/**
* class Doctrine_Import
@@ -64,11 +64,11 @@ class Doctrine_Import extends Doctrine_Connection_Module
$builder->setOptions($options);
$classes = array();
- foreach ($connection->import->listTables() as $table) {
+ foreach ($connection->getSchemaManager()->listTables() as $table) {
$definition = array();
$definition['tableName'] = $table;
$definition['className'] = Doctrine_Inflector::classify($table);
- $definition['columns'] = $connection->import->listTableColumns($table);
+ $definition['columns'] = $connection->getSchemaManager()->listTableColumns($table);
$builder->buildRecord($definition);
diff --git a/lib/Doctrine/Query/AbstractResult.php b/lib/Doctrine/Query/AbstractResult.php
index d537777a5..1cf451006 100755
--- a/lib/Doctrine/Query/AbstractResult.php
+++ b/lib/Doctrine/Query/AbstractResult.php
@@ -27,7 +27,7 @@
* @subpackage Query
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.com
- * @since 1.0
+ * @since 2.0
* @version $Revision: 1393 $
* @author Guilherme Blanco
* @author Konsta Vesterinen
diff --git a/lib/Doctrine/Query/Parser.php b/lib/Doctrine/Query/Parser.php
index 1266f1aef..f3e0ab5d6 100644
--- a/lib/Doctrine/Query/Parser.php
+++ b/lib/Doctrine/Query/Parser.php
@@ -238,7 +238,7 @@ class Doctrine_Query_Parser
throw new Doctrine_Query_Parser_Exception(implode("\r\n", $this->_errors));
}
- // Assign the SQL executor in parser result
+ // Assign the executor in parser result
$this->_parserResult->setSqlExecutor(Doctrine_Query_SqlExecutor_Abstract::create($AST));
return $this->_parserResult;
diff --git a/lib/Doctrine/Query/ParserResult.php b/lib/Doctrine/Query/ParserResult.php
index 85afdaf7e..4a9213223 100755
--- a/lib/Doctrine/Query/ParserResult.php
+++ b/lib/Doctrine/Query/ParserResult.php
@@ -19,8 +19,6 @@
* .
*/
-Doctrine::autoload('Doctrine_Query_AbstractResult');
-
/**
* Doctrine_Query_ParserResult
*
diff --git a/lib/Doctrine/Query/QueryResult.php b/lib/Doctrine/Query/QueryResult.php
index fb09e3474..6bd17e497 100755
--- a/lib/Doctrine/Query/QueryResult.php
+++ b/lib/Doctrine/Query/QueryResult.php
@@ -19,8 +19,6 @@
* .
*/
-Doctrine::autoload('Doctrine_Query_AbstractResult');
-
/**
* Doctrine_Query_QueryResult
*
@@ -30,7 +28,7 @@ Doctrine::autoload('Doctrine_Query_AbstractResult');
* @author Janne Vanhala
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://www.phpdoctrine.org
- * @since 1.0
+ * @since 2.0
* @version $Revision$
*/
class Doctrine_Query_QueryResult extends Doctrine_Query_AbstractResult
diff --git a/lib/Doctrine/Query/Registry.php b/lib/Doctrine/Query/Registry.php
index 8cffec318..c89d7cdf0 100644
--- a/lib/Doctrine/Query/Registry.php
+++ b/lib/Doctrine/Query/Registry.php
@@ -28,7 +28,8 @@
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
- * @version $Revision$
+ * @version $Revision$
+ * @todo Remove
*/
class Doctrine_Query_Registry
{
diff --git a/lib/Doctrine/Query/Registry/Exception.php b/lib/Doctrine/Query/Registry/Exception.php
index e138bdfa6..33e31d5bd 100644
--- a/lib/Doctrine/Query/Registry/Exception.php
+++ b/lib/Doctrine/Query/Registry/Exception.php
@@ -18,7 +18,7 @@
* and is licensed under the LGPL. For more information, see
* .
*/
-Doctrine::autoload('Doctrine_Query_Exception');
+
/**
* Doctrine_Query_Exception
*
@@ -28,7 +28,8 @@ Doctrine::autoload('Doctrine_Query_Exception');
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.phpdoctrine.org
* @since 1.0
- * @version $Revision$
+ * @version $Revision$
+ * @todo Remove
*/
class Doctrine_Query_Registry_Exception extends Doctrine_Query_Exception
{ }
\ No newline at end of file
diff --git a/lib/Doctrine/Query/Scanner.php b/lib/Doctrine/Query/Scanner.php
index 634f0f47c..d14c4a102 100644
--- a/lib/Doctrine/Query/Scanner.php
+++ b/lib/Doctrine/Query/Scanner.php
@@ -102,9 +102,7 @@ class Doctrine_Query_Scanner
foreach ($matches as $match) {
$value = $match[0];
-
$type = $this->_getType($value);
-
$this->_tokens[] = array(
'value' => $value,
'type' => $type,
@@ -130,7 +128,6 @@ class Doctrine_Query_Scanner
} else {
$type = Doctrine_Query_Token::T_INTEGER;
}
-
}
if ($value[0] === "'" && $value[strlen($value) - 1] === "'") {
$type = Doctrine_Query_Token::T_STRING;
diff --git a/lib/Doctrine/Query/SqlBuilder.php b/lib/Doctrine/Query/SqlBuilder.php
index b8f2c75e3..c664f5f73 100755
--- a/lib/Doctrine/Query/SqlBuilder.php
+++ b/lib/Doctrine/Query/SqlBuilder.php
@@ -31,7 +31,6 @@
* @link http://www.phpdoctrine.org
* @since 2.0
* @version $Revision$
- * @todo Merge into DatabasePlatform.
*/
abstract class Doctrine_Query_SqlBuilder
{
diff --git a/lib/Doctrine/Query/Tokenizer.php b/lib/Doctrine/Query/Tokenizer.php
index 5267ab52e..a68d77c4f 100644
--- a/lib/Doctrine/Query/Tokenizer.php
+++ b/lib/Doctrine/Query/Tokenizer.php
@@ -28,10 +28,7 @@
* @link www.phpdoctrine.org
* @since 1.0
* @version $Revision$
- * @todo Give the tokenizer state, make it better work together with Doctrine_Query and maybe
- * take out commonly used string manipulation methods
- * into a stateless StringUtil? class. This tokenizer should be concerned with tokenizing
- * DQL strings.
+ * @todo Remove.
*/
class Doctrine_Query_Tokenizer
{
diff --git a/lib/Doctrine/Query/Tokenizer/Exception.php b/lib/Doctrine/Query/Tokenizer/Exception.php
index c569d5b59..6a936d72a 100644
--- a/lib/Doctrine/Query/Tokenizer/Exception.php
+++ b/lib/Doctrine/Query/Tokenizer/Exception.php
@@ -18,7 +18,7 @@
* and is licensed under the LGPL. For more information, see
* .
*/
-Doctrine::autoload('Doctrine_Exception');
+
/**
* Doctrine_Query_Exception
*
@@ -29,6 +29,7 @@ Doctrine::autoload('Doctrine_Exception');
* @since 1.0
* @version $Revision: 2702 $
* @author Konsta Vesterinen
+ * @todo Remove
*/
class Doctrine_Query_Tokenizer_Exception extends Doctrine_Exception
{ }
\ No newline at end of file
diff --git a/lib/Doctrine/Schema/MsSqlSchemaManager.php b/lib/Doctrine/Schema/MsSqlSchemaManager.php
index 08d72a6a3..42500d8c8 100644
--- a/lib/Doctrine/Schema/MsSqlSchemaManager.php
+++ b/lib/Doctrine/Schema/MsSqlSchemaManager.php
@@ -299,7 +299,7 @@ class Doctrine_Schema_MsSqlSchemaManager extends Doctrine_Schema_SchemaManager
$val['type'] = $type;
$val['identity'] = $identity;
- $decl = $this->conn->dataDict->getPortableDeclaration($val);
+ $decl = $this->conn->getDatabasePlatform()->getPortableDeclaration($val);
$description = array(
'name' => $val['column_name'],
diff --git a/lib/Doctrine/Schema/MySqlSchemaManger.php b/lib/Doctrine/Schema/MySqlSchemaManger.php
index 17d959867..27c59d450 100644
--- a/lib/Doctrine/Schema/MySqlSchemaManger.php
+++ b/lib/Doctrine/Schema/MySqlSchemaManger.php
@@ -32,14 +32,14 @@
*/
class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager
{
- protected $_sql = array(
+ /*protected $_sql = array(
'showDatabases' => 'SHOW DATABASES',
'listTableFields' => 'DESCRIBE %s',
'listSequences' => 'SHOW TABLES',
'listTables' => 'SHOW TABLES',
'listUsers' => 'SELECT DISTINCT USER FROM USER',
'listViews' => "SHOW FULL TABLES %s WHERE Table_type = 'VIEW'",
- );
+ );*/
public function __construct(Doctrine_Connection_MySql $conn)
{
@@ -150,7 +150,7 @@ class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager
$val = array_change_key_case($val, CASE_LOWER);
- $decl = $this->_conn->dataDict->getPortableDeclaration($val);
+ $decl = $this->_conn->getDatabasePlatform()->getPortableDeclaration($val);
$values = isset($decl['values']) ? $decl['values'] : array();
@@ -218,7 +218,8 @@ class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager
*/
public function listTables($database = null)
{
- return $this->_conn->fetchColumn($this->sql['listTables']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTablesSql());
}
/**
@@ -236,346 +237,6 @@ class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager
return $this->_conn->fetchColumn($query);
}
-
- /**
- * create a new database
- *
- * @param string $name name of the database that should be created
- * @return string
- * @override
- */
- public function createDatabaseSql($name)
- {
- return 'CREATE DATABASE ' . $this->_conn->quoteIdentifier($name, true);
- }
-
- /**
- * drop an existing database
- *
- * @param string $name name of the database that should be dropped
- * @return string
- * @override
- */
- public function dropDatabaseSql($name)
- {
- return 'DROP DATABASE ' . $this->_conn->quoteIdentifier($name);
- }
-
- /**
- * create a new table
- *
- * @param string $name Name of the database that should be created
- * @param array $fields Associative array that contains the definition of each field of the new table
- * The indexes of the array entries are the names of the fields of the table an
- * the array entry values are associative arrays like those that are meant to be
- * passed with the field definitions to get[Type]Declaration() functions.
- * array(
- * 'id' => array(
- * 'type' => 'integer',
- * 'unsigned' => 1
- * 'notnull' => 1
- * 'default' => 0
- * ),
- * 'name' => array(
- * 'type' => 'text',
- * 'length' => 12
- * ),
- * 'password' => array(
- * 'type' => 'text',
- * 'length' => 12
- * )
- * );
- * @param array $options An associative array of table options:
- * array(
- * 'comment' => 'Foo',
- * 'charset' => 'utf8',
- * 'collate' => 'utf8_unicode_ci',
- * 'type' => 'innodb',
- * );
- *
- * @return void
- * @override
- */
- public function createTableSql($name, array $fields, array $options = array())
- {
- if ( ! $name) {
- throw new Doctrine_Export_Exception('no valid table name specified');
- }
-
- if (empty($fields)) {
- throw new Doctrine_Export_Exception('no fields specified for table "'.$name.'"');
- }
- $queryFields = $this->getFieldDeclarationList($fields);
-
- // build indexes for all foreign key fields (needed in MySQL!!)
- if (isset($options['foreignKeys'])) {
- foreach ($options['foreignKeys'] as $fk) {
- $local = $fk['local'];
- $found = false;
- if (isset($options['indexes'])) {
- foreach ($options['indexes'] as $definition) {
- if (is_string($definition['fields'])) {
- // Check if index already exists on the column
- $found = ($local == $definition['fields']);
- } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) {
- // Index already exists on the column
- $found = true;
- }
- }
- }
- if (isset($options['primary']) && !empty($options['primary']) &&
- in_array($local, $options['primary'])) {
- // field is part of the PK and therefore already indexed
- $found = true;
- }
-
- if ( ! $found) {
- $options['indexes'][$local] = array('fields' => array($local => array()));
- }
- }
- }
-
- // add all indexes
- if (isset($options['indexes']) && ! empty($options['indexes'])) {
- foreach($options['indexes'] as $index => $definition) {
- $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition);
- }
- }
-
- // attach all primary keys
- if (isset($options['primary']) && ! empty($options['primary'])) {
- $keyColumns = array_values($options['primary']);
- $keyColumns = array_map(array($this->_conn, 'quoteIdentifier'), $keyColumns);
- $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
- }
-
- $query = 'CREATE ';
- if (!empty($options['temporary'])) {
- $query .= 'TEMPORARY ';
- }
- $query.= 'TABLE ' . $this->_conn->quoteIdentifier($name, true) . ' (' . $queryFields . ')';
-
- $optionStrings = array();
-
- if (isset($options['comment'])) {
- $optionStrings['comment'] = 'COMMENT = ' . $this->dbh->quote($options['comment'], 'text');
- }
- if (isset($options['charset'])) {
- $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset'];
- if (isset($options['collate'])) {
- $optionStrings['charset'] .= ' COLLATE ' . $options['collate'];
- }
- }
-
- $type = false;
-
- // get the type of the table
- if (isset($options['type'])) {
- $type = $options['type'];
- } else {
- $type = $this->_conn->getAttribute(Doctrine::ATTR_DEFAULT_TABLE_TYPE);
- }
-
- if ($type) {
- $optionStrings[] = 'ENGINE = ' . $type;
- }
-
- if ( ! empty($optionStrings)) {
- $query.= ' '.implode(' ', $optionStrings);
- }
- $sql[] = $query;
-
- if (isset($options['foreignKeys'])) {
-
- foreach ((array) $options['foreignKeys'] as $k => $definition) {
- if (is_array($definition)) {
- $sql[] = $this->createForeignKeySql($name, $definition);
- }
- }
- }
- return $sql;
- }
-
- /**
- * alter an existing table
- *
- * @param string $name name of the table that is intended to be changed.
- * @param array $changes associative array that contains the details of each type
- * of change that is intended to be performed. The types of
- * changes that are currently supported are defined as follows:
- *
- * name
- *
- * New name for the table.
- *
- * add
- *
- * Associative array with the names of fields to be added as
- * indexes of the array. The value of each entry of the array
- * should be set to another associative array with the properties
- * of the fields to be added. The properties of the fields should
- * be the same as defined by the Metabase parser.
- *
- *
- * remove
- *
- * Associative array with the names of fields to be removed as indexes
- * of the array. Currently the values assigned to each entry are ignored.
- * An empty array should be used for future compatibility.
- *
- * rename
- *
- * Associative array with the names of fields to be renamed as indexes
- * of the array. The value of each entry of the array should be set to
- * another associative array with the entry named name with the new
- * field name and the entry named Declaration that is expected to contain
- * the portion of the field declaration already in DBMS specific SQL code
- * as it is used in the CREATE TABLE statement.
- *
- * change
- *
- * Associative array with the names of the fields to be changed as indexes
- * of the array. Keep in mind that if it is intended to change either the
- * name of a field and any other properties, the change array entries
- * should have the new names of the fields as array indexes.
- *
- * The value of each entry of the array should be set to another associative
- * array with the properties of the fields to that are meant to be changed as
- * array entries. These entries should be assigned to the new values of the
- * respective properties. The properties of the fields should be the same
- * as defined by the Metabase parser.
- *
- * Example
- * array(
- * 'name' => 'userlist',
- * 'add' => array(
- * 'quota' => array(
- * 'type' => 'integer',
- * 'unsigned' => 1
- * )
- * ),
- * 'remove' => array(
- * 'file_limit' => array(),
- * 'time_limit' => array()
- * ),
- * 'change' => array(
- * 'name' => array(
- * 'length' => '20',
- * 'definition' => array(
- * 'type' => 'text',
- * 'length' => 20,
- * ),
- * )
- * ),
- * 'rename' => array(
- * 'sex' => array(
- * 'name' => 'gender',
- * 'definition' => array(
- * 'type' => 'text',
- * 'length' => 1,
- * 'default' => 'M',
- * ),
- * )
- * )
- * )
- *
- * @param boolean $check indicates whether the function should just check if the DBMS driver
- * can perform the requested table alterations if the value is true or
- * actually perform them otherwise.
- * @return boolean
- * @override
- */
- public function alterTableSql($name, array $changes, $check = false)
- {
- if ( ! $name) {
- throw new Doctrine_Export_Exception('no valid table name specified');
- }
- foreach ($changes as $changeName => $change) {
- switch ($changeName) {
- case 'add':
- case 'remove':
- case 'change':
- case 'rename':
- case 'name':
- break;
- default:
- throw new Doctrine_Export_Exception('change type "' . $changeName . '" not yet supported');
- }
- }
-
- if ($check) {
- return true;
- }
-
- $query = '';
- if ( ! empty($changes['name'])) {
- $change_name = $this->_conn->quoteIdentifier($changes['name']);
- $query .= 'RENAME TO ' . $change_name;
- }
-
- if ( ! empty($changes['add']) && is_array($changes['add'])) {
- foreach ($changes['add'] as $fieldName => $field) {
- if ($query) {
- $query.= ', ';
- }
- $query.= 'ADD ' . $this->getDeclaration($fieldName, $field);
- }
- }
-
- if ( ! empty($changes['remove']) && is_array($changes['remove'])) {
- foreach ($changes['remove'] as $fieldName => $field) {
- if ($query) {
- $query .= ', ';
- }
- $fieldName = $this->_conn->quoteIdentifier($fieldName);
- $query .= 'DROP ' . $fieldName;
- }
- }
-
- $rename = array();
- if ( ! empty($changes['rename']) && is_array($changes['rename'])) {
- foreach ($changes['rename'] as $fieldName => $field) {
- $rename[$field['name']] = $fieldName;
- }
- }
-
- if ( ! empty($changes['change']) && is_array($changes['change'])) {
- foreach ($changes['change'] as $fieldName => $field) {
- if ($query) {
- $query.= ', ';
- }
- if (isset($rename[$fieldName])) {
- $oldFieldName = $rename[$fieldName];
- unset($rename[$fieldName]);
- } else {
- $oldFieldName = $fieldName;
- }
- $oldFieldName = $this->_conn->quoteIdentifier($oldFieldName, true);
- $query .= 'CHANGE ' . $oldFieldName . ' '
- . $this->getDeclaration($fieldName, $field['definition']);
- }
- }
-
- if ( ! empty($rename) && is_array($rename)) {
- foreach ($rename as $renameName => $renamedField) {
- if ($query) {
- $query.= ', ';
- }
- $field = $changes['rename'][$renamedField];
- $renamedField = $this->_conn->quoteIdentifier($renamedField, true);
- $query .= 'CHANGE ' . $renamedField . ' '
- . $this->getDeclaration($field['name'], $field['definition']);
- }
- }
-
- if ( ! $query) {
- return false;
- }
-
- $name = $this->_conn->quoteIdentifier($name, true);
-
- return 'ALTER TABLE ' . $name . ' ' . $query;
- }
/**
* create sequence
@@ -654,287 +315,6 @@ class Doctrine_Schema_MySqlSchemaManager extends Doctrine_Schema_SchemaManager
return $res;
}
-
- /**
- * Get the stucture of a field into an array
- *
- * @author Leoncx
- * @param string $table name of the table on which the index is to be created
- * @param string $name name of the index to be created
- * @param array $definition associative array that defines properties of the index to be created.
- * Currently, only one property named FIELDS is supported. This property
- * is also an associative with the names of the index fields as array
- * indexes. Each entry of this array is set to another type of associative
- * array that specifies properties of the index that are specific to
- * each field.
- *
- * Currently, only the sorting property is supported. It should be used
- * to define the sorting direction of the index. It may be set to either
- * ascending or descending.
- *
- * Not all DBMS support index sorting direction configuration. The DBMS
- * drivers of those that do not support it ignore this property. Use the
- * function supports() to determine whether the DBMS driver can manage indexes.
- *
- * Example
- * array(
- * 'fields' => array(
- * 'user_name' => array(
- * 'sorting' => 'ASC'
- * 'length' => 10
- * ),
- * 'last_login' => array()
- * )
- * )
- * @throws PDOException
- * @return void
- * @override
- */
- public function createIndexSql($table, $name, array $definition)
- {
- $table = $table;
- $name = $this->_conn->formatter->getIndexName($name);
- $name = $this->_conn->quoteIdentifier($name);
- $type = '';
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'fulltext':
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
- }
- }
- $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to declare an integer type
- * field to be used in statements like CREATE TABLE.
- *
- * @param string $name name the field to be declared.
- * @param string $field associative array with the name of the properties
- * of the field being declared as array indexes.
- * Currently, the types of supported field
- * properties are as follows:
- *
- * unsigned
- * Boolean flag that indicates whether the field
- * should be declared as unsigned integer if
- * possible.
- *
- * default
- * Integer value to be used as default for this
- * field.
- *
- * notnull
- * Boolean flag that indicates whether this field is
- * constrained to not be set to null.
- * @return string DBMS specific SQL code portion that should be used to
- * declare the specified field.
- * @override
- */
- public function getIntegerDeclaration($name, $field)
- {
- $default = $autoinc = '';
- if ( ! empty($field['autoincrement'])) {
- $autoinc = ' AUTO_INCREMENT';
- } elseif (array_key_exists('default', $field)) {
- if ($field['default'] === '') {
- $field['default'] = empty($field['notnull']) ? null : 0;
- }
- if (is_null($field['default'])) {
- $default = ' DEFAULT NULL';
- } else {
- $default = ' DEFAULT '.$this->_conn->quote($field['default']);
- }
- } elseif (empty($field['notnull'])) {
- $default = ' DEFAULT NULL';
- }
-
- $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
- $unsigned = (isset($field['unsigned']) && $field['unsigned']) ? ' UNSIGNED' : '';
-
- $name = $this->_conn->quoteIdentifier($name, true);
-
- return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field) . $unsigned . $default . $notnull . $autoinc;
- }
-
- /**
- * getDefaultDeclaration
- * Obtain DBMS specific SQL code portion needed to set a default value
- * declaration to be used in statements like CREATE TABLE.
- *
- * @param array $field field definition array
- * @return string DBMS specific SQL code portion needed to set a default value
- * @override
- */
- public function getDefaultFieldDeclaration($field)
- {
- $default = empty($field['notnull']) && !in_array($field['type'], array('clob', 'blob'))
- ? ' DEFAULT NULL' : '';
-
- if (isset($field['default']) && ( ! isset($field['length']) || $field['length'] <= 255)) {
- if ($field['default'] === '') {
- $field['default'] = null;
- if (! empty($field['notnull']) && array_key_exists($field['type'], $this->valid_default_values)) {
- $field['default'] = $this->valid_default_values[$field['type']];
- }
-
- if ($field['default'] === ''
- && ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL)
- ) {
- $field['default'] = ' ';
- }
- }
-
- if ($field['type'] == 'enum' && $this->_conn->getAttribute(Doctrine::ATTR_USE_NATIVE_ENUM)) {
- $fieldType = 'varchar';
- } else {
- if ($field['type'] === 'boolean') {
- $fields['default'] = $this->_conn->convertBooleans($field['default']);
- }
- $fieldType = $field['type'];
- }
-
- $default = ' DEFAULT ' . $this->_conn->quote($field['default'], $fieldType);
- }
- return $default;
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set an index
- * declaration to be used in statements like CREATE TABLE.
- *
- * @param string $charset name of the index
- * @param array $definition index definition
- * @return string DBMS specific SQL code portion needed to set an index
- * @override
- */
- public function getIndexDeclaration($name, array $definition)
- {
- $name = $this->_conn->formatter->getIndexName($name);
- $type = '';
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'fulltext':
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- if ( ! isset($definition['fields'])) {
- throw new Doctrine_Export_Exception('No index columns given.');
- }
- if ( ! is_array($definition['fields'])) {
- $definition['fields'] = array($definition['fields']);
- }
-
- $query = $type . 'INDEX ' . $this->_conn->quoteIdentifier($name);
-
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- /**
- * getIndexFieldDeclarationList
- * Obtain DBMS specific SQL code portion needed to set an index
- * declaration to be used in statements like CREATE TABLE.
- *
- * @return string
- * @override
- */
- public function getIndexFieldDeclarationList(array $fields)
- {
- $declFields = array();
-
- foreach ($fields as $fieldName => $field) {
- $fieldString = $this->_conn->quoteIdentifier($fieldName);
-
- if (is_array($field)) {
- if (isset($field['length'])) {
- $fieldString .= '(' . $field['length'] . ')';
- }
-
- if (isset($field['sorting'])) {
- $sort = strtoupper($field['sorting']);
- switch ($sort) {
- case 'ASC':
- case 'DESC':
- $fieldString .= ' ' . $sort;
- break;
- default:
- throw new Doctrine_Export_Exception('Unknown index sorting option given.');
- }
- }
- } else {
- $fieldString = $this->_conn->quoteIdentifier($field);
- }
- $declFields[] = $fieldString;
- }
- return implode(', ', $declFields);
- }
-
- /**
- * getAdvancedForeignKeyOptions
- * Return the FOREIGN KEY query section dealing with non-standard options
- * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
- *
- * @param array $definition
- * @return string
- * @override
- */
- public function getAdvancedForeignKeyOptions(array $definition)
- {
- $query = '';
- if ( ! empty($definition['match'])) {
- $query .= ' MATCH ' . $definition['match'];
- }
- if ( ! empty($definition['onUpdate'])) {
- $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
- }
- if ( ! empty($definition['onDelete'])) {
- $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
- }
- return $query;
- }
-
- /**
- * drop existing index
- *
- * @param string $table name of table that should be used in method
- * @param string $name name of the index to be dropped
- * @return void
- * @override
- */
- public function dropIndexSql($table, $name)
- {
- $table = $this->_conn->quoteIdentifier($table, true);
- $name = $this->_conn->quoteIdentifier($this->_conn->formatter->getIndexName($name), true);
- return 'DROP INDEX ' . $name . ' ON ' . $table;
- }
-
- /**
- * dropTable
- *
- * @param string $table name of table that should be dropped from the database
- * @throws PDOException
- * @return void
- * @override
- */
- public function dropTableSql($table)
- {
- $table = $this->_conn->quoteIdentifier($table, true);
- return 'DROP TABLE ' . $table;
- }
/**
* Enter description here...
diff --git a/lib/Doctrine/Schema/PostgreSqlSchemaManager.php b/lib/Doctrine/Schema/PostgreSqlSchemaManager.php
index c1f0ec7af..a47c15144 100644
--- a/lib/Doctrine/Schema/PostgreSqlSchemaManager.php
+++ b/lib/Doctrine/Schema/PostgreSqlSchemaManager.php
@@ -31,246 +31,16 @@
* @since 2.0
*/
class Doctrine_Schema_PostgreSqlSchemaManager extends Doctrine_Schema_SchemaManager
-{
- protected $sql = array(
- 'listDatabases' => 'SELECT datname FROM pg_database',
- 'listFunctions' => "SELECT
- proname
- FROM
- pg_proc pr,
- pg_type tp
- WHERE
- tp.oid = pr.prorettype
- AND pr.proisagg = FALSE
- AND tp.typname <> 'trigger'
- AND pr.pronamespace IN
- (SELECT oid FROM pg_namespace
- WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema'",
- 'listSequences' => "SELECT
- relname
- FROM
- pg_class
- WHERE relkind = 'S' AND relnamespace IN
- (SELECT oid FROM pg_namespace
- WHERE nspname NOT LIKE 'pg_%' AND nspname != 'information_schema')",
- 'listTables' => "SELECT
- c.relname AS table_name
- FROM pg_class c, pg_user u
- WHERE c.relowner = u.usesysid
- AND c.relkind = 'r'
- AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname)
- AND c.relname !~ '^(pg_|sql_)'
- UNION
- SELECT c.relname AS table_name
- FROM pg_class c
- WHERE c.relkind = 'r'
- AND NOT EXISTS (SELECT 1 FROM pg_views WHERE viewname = c.relname)
- AND NOT EXISTS (SELECT 1 FROM pg_user WHERE usesysid = c.relowner)
- AND c.relname !~ '^pg_'",
- 'listViews' => 'SELECT viewname FROM pg_views',
- 'listUsers' => 'SELECT usename FROM pg_user',
- 'listTableConstraints' => "SELECT
- relname
- FROM
- pg_class
- WHERE oid IN (
- SELECT indexrelid
- FROM pg_index, pg_class
- WHERE pg_class.relname = %s
- AND pg_class.oid = pg_index.indrelid
- AND (indisunique = 't' OR indisprimary = 't')
- )",
- 'listTableIndexes' => "SELECT
- relname
- FROM
- pg_class
- WHERE oid IN (
- SELECT indexrelid
- FROM pg_index, pg_class
- WHERE pg_class.relname = %s
- AND pg_class.oid=pg_index.indrelid
- AND indisunique != 't'
- AND indisprimary != 't'
- )",
- 'listTableColumns' => "SELECT
- a.attnum,
- a.attname AS field,
- t.typname AS type,
- format_type(a.atttypid, a.atttypmod) AS complete_type,
- a.attnotnull AS isnotnull,
- (SELECT 't'
- FROM pg_index
- WHERE c.oid = pg_index.indrelid
- AND pg_index.indkey[0] = a.attnum
- AND pg_index.indisprimary = 't'
- ) AS pri,
- (SELECT pg_attrdef.adsrc
- FROM pg_attrdef
- WHERE c.oid = pg_attrdef.adrelid
- AND pg_attrdef.adnum=a.attnum
- ) AS default
- FROM pg_attribute a, pg_class c, pg_type t
- WHERE c.relname = %s
- AND a.attnum > 0
- AND a.attrelid = c.oid
- AND a.atttypid = t.oid
- ORDER BY a.attnum",
- );
-
-
-
+{
+ /**
+ * Enter description here...
+ *
+ * @param Doctrine_Connection_Pgsql $conn
+ */
public function __construct(Doctrine_Connection_Pgsql $conn)
{
$this->_conn = $conn;
}
-
- /**
- * create a new database
- *
- * @param string $name name of the database that should be created
- * @throws PDOException
- * @return void
- */
- public function createDatabaseSql($name)
- {
- $query = 'CREATE DATABASE ' . $this->_conn->quoteIdentifier($name);
-
- return $query;
- }
-
- /**
- * drop an existing database
- *
- * @param string $name name of the database that should be dropped
- * @throws PDOException
- * @access public
- */
- public function dropDatabaseSql($name)
- {
- $query = 'DROP DATABASE ' . $this->_conn->quoteIdentifier($name);
-
- return $query;
- }
-
- /**
- * getAdvancedForeignKeyOptions
- * Return the FOREIGN KEY query section dealing with non-standard options
- * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
- *
- * @param array $definition foreign key definition
- * @return string
- * @access protected
- */
- public function getAdvancedForeignKeyOptions(array $definition)
- {
- $query = '';
- if (isset($definition['match'])) {
- $query .= ' MATCH ' . $definition['match'];
- }
- if (isset($definition['onUpdate'])) {
- $query .= ' ON UPDATE ' . $definition['onUpdate'];
- }
- if (isset($definition['onDelete'])) {
- $query .= ' ON DELETE ' . $definition['onDelete'];
- }
- if (isset($definition['deferrable'])) {
- $query .= ' DEFERRABLE';
- } else {
- $query .= ' NOT DEFERRABLE';
- }
- if (isset($definition['feferred'])) {
- $query .= ' INITIALLY DEFERRED';
- } else {
- $query .= ' INITIALLY IMMEDIATE';
- }
- return $query;
- }
-
- /**
- * generates the sql for altering an existing table on postgresql
- *
- * @param string $name name of the table that is intended to be changed.
- * @param array $changes associative array that contains the details of each type *
- * @param boolean $check indicates whether the function should just check if the DBMS driver
- * can perform the requested table alterations if the value is true or
- * actually perform them otherwise.
- * @see Doctrine_Export::alterTable()
- * @return array
- */
- public function alterTableSql($name, array $changes, $check = false)
- {
- foreach ($changes as $changeName => $change) {
- switch ($changeName) {
- case 'add':
- case 'remove':
- case 'change':
- case 'name':
- case 'rename':
- break;
- default:
- throw new Doctrine_Export_Exception('change type "' . $changeName . '\" not yet supported');
- }
- }
-
- if ($check) {
- return true;
- }
-
- $sql = array();
-
- if (isset($changes['add']) && is_array($changes['add'])) {
- foreach ($changes['add'] as $fieldName => $field) {
- $query = 'ADD ' . $this->getDeclaration($fieldName, $field);
- $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
- }
- }
-
- if (isset($changes['remove']) && is_array($changes['remove'])) {
- foreach ($changes['remove'] as $fieldName => $field) {
- $fieldName = $this->_conn->quoteIdentifier($fieldName, true);
- $query = 'DROP ' . $fieldName;
- $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
- }
- }
-
- if (isset($changes['change']) && is_array($changes['change'])) {
- foreach ($changes['change'] as $fieldName => $field) {
- $fieldName = $this->_conn->quoteIdentifier($fieldName, true);
- if (isset($field['type'])) {
- $serverInfo = $this->_conn->getServerVersion();
-
- if (is_array($serverInfo) && $serverInfo['major'] < 8) {
- throw new Doctrine_Export_Exception('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above');
- }
- $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->_conn->datatype->getTypeDeclaration($field['definition']);
- $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
- }
- if (array_key_exists('default', $field)) {
- $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->_conn->quote($field['definition']['default'], $field['definition']['type']);
- $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
- }
- if ( ! empty($field['notnull'])) {
- $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL';
- $sql[] = 'ALTER TABLE ' . $name . ' ' . $query;
- }
- }
- }
-
- if (isset($changes['rename']) && is_array($changes['rename'])) {
- foreach ($changes['rename'] as $fieldName => $field) {
- $fieldName = $this->_conn->quoteIdentifier($fieldName, true);
- $sql[] = 'ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->_conn->quoteIdentifier($field['name'], true);
- }
- }
-
- $name = $this->_conn->quoteIdentifier($name, true);
- if (isset($changes['name'])) {
- $changeName = $this->_conn->quoteIdentifier($changes['name'], true);
- $sql[] = 'ALTER TABLE ' . $name . ' RENAME TO ' . $changeName;
- }
-
- return $sql;
- }
/**
* alter an existing table
@@ -369,137 +139,6 @@ class Doctrine_Schema_PostgreSqlSchemaManager extends Doctrine_Schema_SchemaMana
}
return true;
}
-
- /**
- * return RDBMS specific create sequence statement
- *
- * @throws Doctrine_Connection_Exception if something fails at database level
- * @param string $seqName name of the sequence to be created
- * @param string $start start value of the sequence; default is 1
- * @param array $options An associative array of table options:
- * array(
- * 'comment' => 'Foo',
- * 'charset' => 'utf8',
- * 'collate' => 'utf8_unicode_ci',
- * );
- * @return string
- */
- public function createSequenceSql($sequenceName, $start = 1, array $options = array())
- {
- $sequenceName = $this->_conn->quoteIdentifier($this->_conn->formatter->getSequenceName($sequenceName), true);
- return $this->_conn->exec('CREATE SEQUENCE ' . $sequenceName . ' INCREMENT 1' .
- ($start < 1 ? ' MINVALUE ' . $start : '') . ' START ' . $start);
- }
-
- /**
- * drop existing sequence
- *
- * @param string $sequenceName name of the sequence to be dropped
- */
- public function dropSequenceSql($sequenceName)
- {
- $sequenceName = $this->_conn->quoteIdentifier($this->_conn->formatter->getSequenceName($sequenceName), true);
- return 'DROP SEQUENCE ' . $sequenceName;
- }
-
- /**
- * Creates a table.
- *
- * @param unknown_type $name
- * @param array $fields
- * @param array $options
- * @return unknown
- */
- public function createTableSql($name, array $fields, array $options = array())
- {
- if ( ! $name) {
- throw new Doctrine_Export_Exception('no valid table name specified');
- }
-
- if (empty($fields)) {
- throw new Doctrine_Export_Exception('no fields specified for table ' . $name);
- }
-
- $queryFields = $this->getFieldDeclarationList($fields);
-
-
- if (isset($options['primary']) && ! empty($options['primary'])) {
- $keyColumns = array_values($options['primary']);
- $keyColumns = array_map(array($this->_conn, 'quoteIdentifier'), $keyColumns);
- $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
- }
-
- $query = 'CREATE TABLE ' . $this->_conn->quoteIdentifier($name, true) . ' (' . $queryFields . ')';
-
- $sql[] = $query;
-
- if (isset($options['indexes']) && ! empty($options['indexes'])) {
- foreach($options['indexes'] as $index => $definition) {
- $sql[] = $this->createIndexSql($name, $index, $definition);
- }
- }
-
- if (isset($options['foreignKeys'])) {
-
- foreach ((array) $options['foreignKeys'] as $k => $definition) {
- if (is_array($definition)) {
- $sql[] = $this->createForeignKeySql($name, $definition);
- }
- }
- }
-
- return $sql;
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to declare an integer type
- * field to be used in statements like CREATE TABLE.
- *
- * @param string $name name the field to be declared.
- * @param array $field associative array with the name of the properties
- * of the field being declared as array indexes. Currently, the types
- * of supported field properties are as follows:
- *
- * unsigned
- * Boolean flag that indicates whether the field should be
- * declared as unsigned integer if possible.
- *
- * default
- * Integer value to be used as default for this field.
- *
- * notnull
- * Boolean flag that indicates whether this field is constrained
- * to not be set to null.
- * @return string DBMS specific SQL code portion that should be used to
- * declare the specified field.
- */
- public function getIntegerDeclaration($name, $field)
- {
- /**
- if ( ! empty($field['unsigned'])) {
- $this->_conn->warnings[] = "unsigned integer field \"$name\" is being declared as signed integer";
- }
- */
-
- if ( ! empty($field['autoincrement'])) {
- $name = $this->_conn->quoteIdentifier($name, true);
- return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field);
- }
-
- $default = '';
- if (array_key_exists('default', $field)) {
- if ($field['default'] === '') {
- $field['default'] = empty($field['notnull']) ? null : 0;
- }
- $default = ' DEFAULT '.$this->_conn->quote($field['default'], $field['type']);
- } elseif (empty($field['notnull'])) {
- $default = ' DEFAULT NULL';
- }
-
- $notnull = empty($field['notnull']) ? '' : ' NOT NULL';
- $name = $this->_conn->quoteIdentifier($name, true);
- return $name . ' ' . $this->_conn->dataDict->getNativeDeclaration($field) . $default . $notnull;
- }
/**
* lists all database triggers
@@ -548,7 +187,7 @@ class Doctrine_Schema_PostgreSqlSchemaManager extends Doctrine_Schema_SchemaMana
$val['length'] = $length;
}
- $decl = $this->_conn->dataDict->getPortableDeclaration($val);
+ $decl = $this->_conn->getDatabasePlatform()->getPortableDeclaration($val);
$description = array(
'name' => $val['field'],
diff --git a/lib/Doctrine/Schema/SchemaManager.php b/lib/Doctrine/Schema/SchemaManager.php
index 3f1e7e8fd..f84bccf7d 100644
--- a/lib/Doctrine/Schema/SchemaManager.php
+++ b/lib/Doctrine/Schema/SchemaManager.php
@@ -22,31 +22,19 @@
#namespace Doctrine::DBAL::Schema;
/**
- * xxx
+ * Base class for schema managers. Schema managers are used to inspect and/or
+ * modify the database schema.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @author Konsta Vesterinen
* @author Lukas Smith (PEAR MDB2 library)
* @version $Revision$
* @since 2.0
+ * @todo Rename to AbstractSchemaManager
*/
abstract class Doctrine_Schema_SchemaManager
{
protected $_conn;
- protected $sql = array();
- protected $valid_default_values = array(
- 'text' => '',
- 'boolean' => true,
- 'integer' => 0,
- 'decimal' => 0.0,
- 'float' => 0.0,
- 'timestamp' => '1970-01-01 00:00:00',
- 'time' => '00:00:00',
- 'date' => '1970-01-01',
- 'clob' => '',
- 'blob' => '',
- 'string' => ''
- );
/**
* lists all databases
@@ -55,11 +43,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listDatabases()
{
- if ( ! isset($this->sql['listDatabases'])) {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- return $this->_conn->fetchColumn($this->sql['listDatabases']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListDatabasesSql());
}
/**
@@ -69,11 +54,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listFunctions()
{
- if ( ! isset($this->sql['listFunctions'])) {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- return $this->_conn->fetchColumn($this->sql['listFunctions']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListFunctionsSql());
}
/**
@@ -84,7 +66,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listTriggers($database = null)
{
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTriggersSql());
}
/**
@@ -95,11 +78,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listSequences($database = null)
{
- if ( ! isset($this->_sql['listSequences'])) {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- return $this->_conn->fetchColumn($this->sql['listSequences']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListSequencesSql());
}
/**
@@ -110,7 +90,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listTableConstraints($table)
{
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTableConstraintsSql());
}
/**
@@ -121,7 +102,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listTableColumns($table)
{
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTableColumnsSql());
}
/**
@@ -132,7 +114,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listTableIndexes($table)
{
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTableIndexesSql());
}
/**
@@ -143,29 +126,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listTables($database = null)
{
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- /**
- * Lists table triggers.
- *
- * @param string $table database table name
- * @return array
- */
- public function listTableTriggers($table)
- {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- /**
- * Lists table views.
- *
- * @param string $table database table name
- * @return array
- */
- public function listTableViews($table)
- {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListTablesSql());
}
/**
@@ -175,11 +137,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listUsers()
{
- if ( ! isset($this->_sql['listUsers'])) {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- return $this->_conn->fetchColumn($this->sql['listUsers']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListUsersSql());
}
/**
@@ -190,11 +149,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function listViews($database = null)
{
- if ( ! isset($this->_sql['listViews'])) {
- throw new Doctrine_Import_Exception(__FUNCTION__ . ' not supported by this driver.');
- }
-
- return $this->_conn->fetchColumn($this->sql['listViews']);
+ return $this->_conn->fetchColumn($this->_conn->getDatabasePlatform()
+ ->getListViewsSql());
}
/**
@@ -206,31 +162,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function dropDatabase($database)
{
- $this->_conn->execute($this->dropDatabaseSql($database));
- }
-
- /**
- * drop an existing database
- * (this method is implemented by the drivers)
- *
- * @param string $name name of the database that should be dropped
- * @return void
- */
- public function dropDatabaseSql($database)
- {
- throw new Doctrine_Export_Exception('Drop database not supported by this driver.');
- }
-
- /**
- * dropTableSql
- * drop an existing table
- *
- * @param string $table name of table that should be dropped from the database
- * @return string
- */
- public function dropTableSql($table)
- {
- return 'DROP TABLE ' . $this->conn->quoteIdentifier($table);
+ $this->_conn->execute($this->_conn->getDatabasePlatform()
+ ->getDropDatabaseSql($database));
}
/**
@@ -242,7 +175,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function dropTable($table)
{
- $this->_conn->execute($this->dropTableSql($table));
+ $this->_conn->execute($this->_conn->getDatabasePlatform()
+ ->getDropTableSql($table));
}
/**
@@ -254,21 +188,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function dropIndex($table, $name)
{
- return $this->_conn->exec($this->dropIndexSql($table, $name));
- }
-
- /**
- * dropIndexSql
- *
- * @param string $table name of table that should be used in method
- * @param string $name name of the index to be dropped
- * @return string SQL that is used for dropping an index
- */
- public function dropIndexSql($table, $name)
- {
- $name = $this->_conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
-
- return 'DROP INDEX ' . $name;
+ return $this->_conn->exec($this->_conn->getDatabasePlatform()
+ ->getDropIndexSql($table, $name));
}
/**
@@ -281,8 +202,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function dropConstraint($table, $name, $primary = false)
{
- $table = $this->_conn->quoteIdentifier($table);
- $name = $this->_conn->quoteIdentifier($name);
+ $table = $this->_conn->getDatabasePlatform()->quoteIdentifier($table);
+ $name = $this->_conn->getDatabasePlatform()->quoteIdentifier($name);
return $this->_conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name);
}
@@ -300,7 +221,6 @@ abstract class Doctrine_Schema_SchemaManager
}
/**
- * dropSequenceSql
* drop existing sequence
* (this method is implemented by the drivers)
*
@@ -310,20 +230,7 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function dropSequence($sequenceName)
{
- $this->_conn->exec($this->dropSequenceSql($sequenceName));
- }
-
- /**
- * dropSequenceSql
- * drop existing sequence
- *
- * @throws Doctrine_Connection_Exception if something fails at database level
- * @param string $sequenceName name of the sequence to be dropped
- * @return void
- */
- public function dropSequenceSql($sequenceName)
- {
- throw new Doctrine_Export_Exception('Drop sequence not supported by this driver.');
+ $this->_conn->exec($this->_conn->getDatabasePlatform()->getDropSequenceSql($sequenceName));
}
/**
@@ -335,93 +242,7 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function createDatabase($database)
{
- $this->_conn->execute($this->createDatabaseSql($database));
- }
-
- /**
- * create a new database
- * (this method is implemented by the drivers)
- *
- * @param string $name name of the database that should be created
- * @return string
- */
- public function createDatabaseSql($database)
- {
- throw new Doctrine_Export_Exception('Create database not supported by this driver.');
- }
-
- /**
- * create a new table
- *
- * @param string $name Name of the database that should be created
- * @param array $fields Associative array that contains the definition of each field of the new table
- * The indexes of the array entries are the names of the fields of the table an
- * the array entry values are associative arrays like those that are meant to be
- * passed with the field definitions to get[Type]Declaration() functions.
- * array(
- * 'id' => array(
- * 'type' => 'integer',
- * 'unsigned' => 1
- * 'notnull' => 1
- * 'default' => 0
- * ),
- * 'name' => array(
- * 'type' => 'text',
- * 'length' => 12
- * ),
- * 'password' => array(
- * 'type' => 'text',
- * 'length' => 12
- * )
- * );
- * @param array $options An associative array of table options:
- *
- * @return string
- */
- public function createTableSql($name, array $fields, array $options = array())
- {
- if ( ! $name) {
- throw new Doctrine_Export_Exception('no valid table name specified');
- }
-
- if (empty($fields)) {
- throw new Doctrine_Export_Exception('no fields specified for table ' . $name);
- }
-
- $queryFields = $this->getFieldDeclarationList($fields);
-
-
- if (isset($options['primary']) && ! empty($options['primary'])) {
- $queryFields .= ', PRIMARY KEY(' . implode(', ', array_values($options['primary'])) . ')';
- }
-
- if (isset($options['indexes']) && ! empty($options['indexes'])) {
- foreach($options['indexes'] as $index => $definition) {
- $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition);
- }
- }
-
- $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields;
-
- $check = $this->getCheckDeclaration($fields);
-
- if ( ! empty($check)) {
- $query .= ', ' . $check;
- }
-
- $query .= ')';
-
- $sql[] = $query;
-
- if (isset($options['foreignKeys'])) {
-
- foreach ((array) $options['foreignKeys'] as $k => $definition) {
- if (is_array($definition)) {
- $sql[] = $this->createForeignKeySql($name, $definition);
- }
- }
- }
- return $sql;
+ $this->_conn->execute($this->_conn->getDatabasePlatform()->getCreateDatabaseSql($database));
}
/**
@@ -434,22 +255,23 @@ abstract class Doctrine_Schema_SchemaManager
*
* @return void
*/
- public function createTable($name, array $fields, array $options = array())
+ public function createTable($name, array $columns, array $options = array())
{
// Build array of the primary keys if any of the individual field definitions
// specify primary => true
$count = 0;
- foreach ($fields as $fieldName => $field) {
- if (isset($field['primary']) && $field['primary']) {
+ foreach ($columns as $columnName => $definition) {
+ if (isset($definition['primary']) && $definition['primary']) {
if ($count == 0) {
$options['primary'] = array();
}
$count++;
- $options['primary'][] = $fieldName;
+ $options['primary'][] = $columnName;
}
}
- $sql = (array) $this->createTableSql($name, $fields, $options);
+ $sql = (array) $this->_conn->getDatabasePlatform()->getCreateTableSql(
+ $name, $columns, $options);
foreach ($sql as $query) {
$this->_conn->execute($query);
@@ -472,27 +294,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function createSequence($seqName, $start = 1, array $options = array())
{
- return $this->_conn->execute($this->createSequenceSql($seqName, $start = 1, $options));
- }
-
- /**
- * return RDBMS specific create sequence statement
- * (this method is implemented by the drivers)
- *
- * @throws Doctrine_Connection_Exception if something fails at database level
- * @param string $seqName name of the sequence to be created
- * @param string $start start value of the sequence; default is 1
- * @param array $options An associative array of table options:
- * array(
- * 'comment' => 'Foo',
- * 'charset' => 'utf8',
- * 'collate' => 'utf8_unicode_ci',
- * );
- * @return string
- */
- public function createSequenceSql($seqName, $start = 1, array $options = array())
- {
- throw new Doctrine_Export_Exception('Create sequence not supported by this driver.');
+ return $this->_conn->execute($this->_conn->getDatabasePlatform()
+ ->getCreateSequenceSql($seqName, $start, $options));
}
/**
@@ -518,53 +321,10 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function createConstraint($table, $name, $definition)
{
- $sql = $this->createConstraintSql($table, $name, $definition);
-
+ $sql = $this->_conn->getDatabasePlatform()->getCreateConstraintSql($table, $name, $definition);
return $this->_conn->exec($sql);
}
- /**
- * create a constraint on a table
- *
- * @param string $table name of the table on which the constraint is to be created
- * @param string $name name of the constraint to be created
- * @param array $definition associative array that defines properties of the constraint to be created.
- * Currently, only one property named FIELDS is supported. This property
- * is also an associative with the names of the constraint fields as array
- * constraints. Each entry of this array is set to another type of associative
- * array that specifies properties of the constraint that are specific to
- * each field.
- *
- * Example
- * array(
- * 'fields' => array(
- * 'user_name' => array(),
- * 'last_login' => array()
- * )
- * )
- * @return void
- */
- public function createConstraintSql($table, $name, $definition)
- {
- $table = $this->_conn->quoteIdentifier($table);
- $name = $this->_conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
- $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name;
-
- if (isset($definition['primary']) && $definition['primary']) {
- $query .= ' PRIMARY KEY';
- } elseif (isset($definition['unique']) && $definition['unique']) {
- $query .= ' UNIQUE';
- }
-
- $fields = array();
- foreach (array_keys($definition['fields']) as $field) {
- $fields[] = $this->_conn->quoteIdentifier($field, true);
- }
- $query .= ' ('. implode(', ', $fields) . ')';
-
- return $query;
- }
-
/**
* Get the stucture of a field into an array
*
@@ -598,58 +358,8 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function createIndex($table, $name, array $definition)
{
- return $this->_conn->execute($this->createIndexSql($table, $name, $definition));
- }
-
- /**
- * Get the stucture of a field into an array
- *
- * @param string $table name of the table on which the index is to be created
- * @param string $name name of the index to be created
- * @param array $definition associative array that defines properties of the index to be created.
- * @see Doctrine_Export::createIndex()
- * @return string
- */
- public function createIndexSql($table, $name, array $definition)
- {
- $table = $this->_conn->quoteIdentifier($table);
- $name = $this->_conn->quoteIdentifier($name);
- $type = '';
-
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
-
- $fields = array();
- foreach ($definition['fields'] as $field) {
- $fields[] = $this->_conn->quoteIdentifier($field);
- }
- $query .= ' (' . implode(', ', $fields) . ')';
-
- return $query;
- }
- /**
- * createForeignKeySql
- *
- * @param string $table name of the table on which the foreign key is to be created
- * @param array $definition associative array that defines properties of the foreign key to be created.
- * @return string
- */
- public function createForeignKeySql($table, array $definition)
- {
- $table = $this->_conn->quoteIdentifier($table);
-
- $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition);
-
- return $query;
+ return $this->_conn->execute($this->_conn->getDatabasePlatform()
+ ->getCreateIndexSql($table, $name, $definition));
}
/**
@@ -661,8 +371,7 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function createForeignKey($table, array $definition)
{
- $sql = $this->createForeignKeySql($table, $definition);
-
+ $sql = $this->_conn->getDatabasePlatform()->getCreateForeignKeySql($table, $definition);
return $this->_conn->execute($sql);
}
@@ -757,433 +466,12 @@ abstract class Doctrine_Schema_SchemaManager
*/
public function alterTable($name, array $changes, $check = false)
{
- $sql = $this->alterTableSql($name, $changes, $check);
+ $sql = $this->_conn->getDatabasePlatform()->getAlterTableSql($name, $changes, $check);
if (is_string($sql) && $sql) {
$this->_conn->execute($sql);
}
}
-
- /**
- * generates the sql for altering an existing table
- * (this method is implemented by the drivers)
- *
- * @param string $name name of the table that is intended to be changed.
- * @param array $changes associative array that contains the details of each type *
- * @param boolean $check indicates whether the function should just check if the DBMS driver
- * can perform the requested table alterations if the value is true or
- * actually perform them otherwise.
- * @see Doctrine_Export::alterTable()
- * @return string
- */
- public function alterTableSql($name, array $changes, $check = false)
- {
- throw new Doctrine_Export_Exception('Alter table not supported by this driver.');
- }
-
- /**
- * Get declaration of a number of field in bulk
- *
- * @param array $fields a multidimensional associative array.
- * The first dimension determines the field name, while the second
- * dimension is keyed with the name of the properties
- * of the field being declared as array indexes. Currently, the types
- * of supported field properties are as follows:
- *
- * length
- * Integer value that determines the maximum length of the text
- * field. If this argument is missing the field should be
- * declared to have the longest length allowed by the DBMS.
- *
- * default
- * Text value to be used as default for this field.
- *
- * notnull
- * Boolean flag that indicates whether this field is constrained
- * to not be set to null.
- * charset
- * Text value with the default CHARACTER SET for this field.
- * collation
- * Text value with the default COLLATION for this field.
- * unique
- * unique constraint
- *
- * @return string
- */
- public function getFieldDeclarationList(array $fields)
- {
- foreach ($fields as $fieldName => $field) {
- $query = $this->getDeclaration($fieldName, $field);
-
- $queryFields[] = $query;
- }
- return implode(', ', $queryFields);
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to declare a generic type
- * field to be used in statements like CREATE TABLE.
- *
- * @param string $name name the field to be declared.
- * @param array $field associative array with the name of the properties
- * of the field being declared as array indexes. Currently, the types
- * of supported field properties are as follows:
- *
- * length
- * Integer value that determines the maximum length of the text
- * field. If this argument is missing the field should be
- * declared to have the longest length allowed by the DBMS.
- *
- * default
- * Text value to be used as default for this field.
- *
- * notnull
- * Boolean flag that indicates whether this field is constrained
- * to not be set to null.
- * charset
- * Text value with the default CHARACTER SET for this field.
- * collation
- * Text value with the default COLLATION for this field.
- * unique
- * unique constraint
- * check
- * column check constraint
- *
- * @return string DBMS specific SQL code portion that should be used to
- * declare the specified field.
- */
- public function getDeclaration($name, array $field)
- {
-
- $default = $this->getDefaultFieldDeclaration($field);
-
- $charset = (isset($field['charset']) && $field['charset']) ?
- ' ' . $this->getCharsetFieldDeclaration($field['charset']) : '';
-
- $collation = (isset($field['collation']) && $field['collation']) ?
- ' ' . $this->getCollationFieldDeclaration($field['collation']) : '';
-
- $notnull = (isset($field['notnull']) && $field['notnull']) ? ' NOT NULL' : '';
-
- $unique = (isset($field['unique']) && $field['unique']) ?
- ' ' . $this->getUniqueFieldDeclaration() : '';
-
- $check = (isset($field['check']) && $field['check']) ?
- ' ' . $field['check'] : '';
-
- $method = 'get' . $field['type'] . 'Declaration';
-
- if (method_exists($this->conn->dataDict, $method)) {
- return $this->_conn->dataDict->$method($name, $field);
- } else {
- $dec = $this->_conn->dataDict->getNativeDeclaration($field);
- }
- return $this->_conn->quoteIdentifier($name, true) . ' ' . $dec . $charset . $default . $notnull . $unique . $check . $collation;
- }
-
- /**
- * getDefaultDeclaration
- * Obtain DBMS specific SQL code portion needed to set a default value
- * declaration to be used in statements like CREATE TABLE.
- *
- * @param array $field field definition array
- * @return string DBMS specific SQL code portion needed to set a default value
- */
- public function getDefaultFieldDeclaration($field)
- {
- $default = '';
- if (isset($field['default'])) {
- if ($field['default'] === '') {
- $field['default'] = empty($field['notnull'])
- ? null : $this->valid_default_values[$field['type']];
-
- if ($field['default'] === '' &&
- ($this->_conn->getAttribute(Doctrine::ATTR_PORTABILITY) & Doctrine::PORTABILITY_EMPTY_TO_NULL)) {
- $field['default'] = null;
- }
- }
-
- if ($field['type'] === 'boolean') {
- $field['default'] = $this->_conn->convertBooleans($field['default']);
- }
- $default = ' DEFAULT ' . $this->_conn->quote($field['default'], $field['type']);
- }
- return $default;
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set a CHECK constraint
- * declaration to be used in statements like CREATE TABLE.
- *
- * @param array $definition check definition
- * @return string DBMS specific SQL code portion needed to set a CHECK constraint
- */
- public function getCheckDeclaration(array $definition)
- {
- $constraints = array();
- foreach ($definition as $field => $def) {
- if (is_string($def)) {
- $constraints[] = 'CHECK (' . $def . ')';
- } else {
- if (isset($def['min'])) {
- $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
- }
-
- if (isset($def['max'])) {
- $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
- }
- }
- }
-
- return implode(', ', $constraints);
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set an index
- * declaration to be used in statements like CREATE TABLE.
- *
- * @param string $name name of the index
- * @param array $definition index definition
- * @return string DBMS specific SQL code portion needed to set an index
- */
- public function getIndexDeclaration($name, array $definition)
- {
- $name = $this->_conn->quoteIdentifier($name);
- $type = '';
-
- if (isset($definition['type'])) {
- if (strtolower($definition['type']) == 'unique') {
- $type = strtoupper($definition['type']) . ' ';
- } else {
- throw new Doctrine_Export_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) {
- throw new Doctrine_Export_Exception('No index columns given.');
- }
-
- $query = $type . 'INDEX ' . $name;
-
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- /**
- * getIndexFieldDeclarationList
- * Obtain DBMS specific SQL code portion needed to set an index
- * declaration to be used in statements like CREATE TABLE.
- *
- * @return string
- */
- public function getIndexFieldDeclarationList(array $fields)
- {
- $ret = array();
- foreach ($fields as $field => $definition) {
- if (is_array($definition)) {
- $ret[] = $this->_conn->quoteIdentifier($field);
- } else {
- $ret[] = $this->_conn->quoteIdentifier($definition);
- }
- }
- return implode(', ', $ret);
- }
-
- /**
- * A method to return the required SQL string that fits between CREATE ... TABLE
- * to create the table as a temporary table.
- *
- * Should be overridden in driver classes to return the correct string for the
- * specific database type.
- *
- * The default is to return the string "TEMPORARY" - this will result in a
- * SQL error for any database that does not support temporary tables, or that
- * requires a different SQL command from "CREATE TEMPORARY TABLE".
- *
- * @return string The string required to be placed between "CREATE" and "TABLE"
- * to generate a temporary table, if possible.
- */
- public function getTemporaryTableQuery()
- {
- return 'TEMPORARY';
- }
-
- /**
- * getForeignKeyDeclaration
- * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
- * of a field declaration to be used in statements like CREATE TABLE.
- *
- * @param array $definition an associative array with the following structure:
- * name optional constraint name
- *
- * local the local field(s)
- *
- * foreign the foreign reference field(s)
- *
- * foreignTable the name of the foreign table
- *
- * onDelete referential delete action
- *
- * onUpdate referential update action
- *
- * deferred deferred constraint checking
- *
- * The onDelete and onUpdate keys accept the following values:
- *
- * CASCADE: Delete or update the row from the parent table and automatically delete or
- * update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.
- * Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column
- * in the parent table or in the child table.
- *
- * SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the
- * child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier
- * specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
- *
- * NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary
- * key value is not allowed to proceed if there is a related foreign key value in the referenced table.
- *
- * RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as
- * omitting the ON DELETE or ON UPDATE clause.
- *
- * SET DEFAULT
- *
- * @return string DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
- * of a field declaration.
- */
- public function getForeignKeyDeclaration(array $definition)
- {
- $sql = $this->getForeignKeyBaseDeclaration($definition);
- $sql .= $this->getAdvancedForeignKeyOptions($definition);
-
- return $sql;
- }
-
- /**
- * getAdvancedForeignKeyOptions
- * Return the FOREIGN KEY query section dealing with non-standard options
- * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
- *
- * @param array $definition foreign key definition
- * @return string
- */
- public function getAdvancedForeignKeyOptions(array $definition)
- {
- $query = '';
- if ( ! empty($definition['onUpdate'])) {
- $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
- }
- if ( ! empty($definition['onDelete'])) {
- $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
- }
- return $query;
- }
-
- /**
- * getForeignKeyReferentialAction
- *
- * returns given referential action in uppercase if valid, otherwise throws
- * an exception
- *
- * @throws Doctrine_Exception_Exception if unknown referential action given
- * @param string $action foreign key referential action
- * @param string foreign key referential action in uppercase
- */
- public function getForeignKeyReferentialAction($action)
- {
- $upper = strtoupper($action);
- switch ($upper) {
- case 'CASCADE':
- case 'SET NULL':
- case 'NO ACTION':
- case 'RESTRICT':
- case 'SET DEFAULT':
- return $upper;
- break;
- default:
- throw new Doctrine_Export_Exception('Unknown foreign key referential action \'' . $upper . '\' given.');
- }
- }
-
- /**
- * getForeignKeyBaseDeclaration
- * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
- * of a field declaration to be used in statements like CREATE TABLE.
- *
- * @param array $definition
- * @return string
- */
- public function getForeignKeyBaseDeclaration(array $definition)
- {
- $sql = '';
- if (isset($definition['name'])) {
- $sql .= ' CONSTRAINT ' . $this->conn->quoteIdentifier($definition['name']) . ' ';
- }
- $sql .= 'FOREIGN KEY (';
-
- if ( ! isset($definition['local'])) {
- throw new Doctrine_Export_Exception('Local reference field missing from definition.');
- }
- if ( ! isset($definition['foreign'])) {
- throw new Doctrine_Export_Exception('Foreign reference field missing from definition.');
- }
- if ( ! isset($definition['foreignTable'])) {
- throw new Doctrine_Export_Exception('Foreign reference table missing from definition.');
- }
-
- if ( ! is_array($definition['local'])) {
- $definition['local'] = array($definition['local']);
- }
- if ( ! is_array($definition['foreign'])) {
- $definition['foreign'] = array($definition['foreign']);
- }
-
- $sql .= implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['local']))
- . ') REFERENCES '
- . $this->_conn->quoteIdentifier($definition['foreignTable']) . '('
- . implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['foreign'])) . ')';
-
- return $sql;
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint
- * of a field declaration to be used in statements like CREATE TABLE.
- *
- * @return string DBMS specific SQL code portion needed to set the UNIQUE constraint
- * of a field declaration.
- */
- public function getUniqueFieldDeclaration()
- {
- return 'UNIQUE';
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
- * of a field declaration to be used in statements like CREATE TABLE.
- *
- * @param string $charset name of the charset
- * @return string DBMS specific SQL code portion needed to set the CHARACTER SET
- * of a field declaration.
- */
- public function getCharsetFieldDeclaration($charset)
- {
- return '';
- }
-
- /**
- * Obtain DBMS specific SQL code portion needed to set the COLLATION
- * of a field declaration to be used in statements like CREATE TABLE.
- *
- * @param string $collation name of the collation
- * @return string DBMS specific SQL code portion needed to set the COLLATION
- * of a field declaration.
- */
- public function getCollationFieldDeclaration($collation)
- {
- return '';
- }
-
}
?>
\ No newline at end of file
diff --git a/query-language.txt b/query-language.txt
index 4762e9de8..a45bd6d37 100644
--- a/query-language.txt
+++ b/query-language.txt
@@ -5,7 +5,7 @@
* - terminals begin with a lower case character
* - parentheses (...) are used for grouping
* - square brackets [...] are used for defining an optional part, eg. zero or
- * one time time
+ * one time
* - curly brackets {...} are used for repetion, eg. zero or more times
* - double quotation marks "..." define a terminal string
* - a vertical bar | represents an alternative
@@ -14,69 +14,69 @@
* Initially Select and Sub-select DQL will not support LIMIT and OFFSET (due to limit-subquery algorithm)
*/
-QueryLanguage = SelectStatement | UpdateStatement | DeleteStatement
+QueryLanguage ::= SelectStatement | UpdateStatement | DeleteStatement
-SelectStatement = SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
-UpdateStatement = UpdateClause [WhereClause]
-DeleteStatement = DeleteClause [WhereClause]
+SelectStatement ::= SelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
+UpdateStatement ::= UpdateClause [WhereClause]
+DeleteStatement ::= DeleteClause [WhereClause]
-Subselect = SimpleSelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
-SelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}
-SimpleSelectClause = "SELECT" ["ALL" | "DISTINCT"] SelectExpression
-DeleteClause = "DELETE" ["FROM"] VariableDeclaration
-WhereClause = "WHERE" ConditionalExpression
-FromClause = "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}
-HavingClause = "HAVING" ConditionalExpression
-GroupByClause = "GROUP" "BY" GroupByItem {"," GroupByItem}
-OrderByClause = "ORDER" "BY" OrderByItem {"," OrderByItem}
-LimitClause = "LIMIT" integer
-OffsetClause = "OFFSET" integer
-UpdateClause = "UPDATE" VariableDeclaration "SET" UpdateItem {"," UpdateItem}
+Subselect ::= SimpleSelectClause FromClause [WhereClause] [GroupByClause] [HavingClause] [OrderByClause]
+SelectClause ::= "SELECT" ["ALL" | "DISTINCT"] SelectExpression {"," SelectExpression}*
+SimpleSelectClause ::= "SELECT" ["ALL" | "DISTINCT"] SelectExpression
+DeleteClause ::= "DELETE" ["FROM"] VariableDeclaration
+WhereClause ::= "WHERE" ConditionalExpression
+FromClause ::= "FROM" IdentificationVariableDeclaration {"," IdentificationVariableDeclaration}*
+HavingClause ::= "HAVING" ConditionalExpression
+GroupByClause ::= "GROUP" "BY" GroupByItem {"," GroupByItem}*
+OrderByClause ::= "ORDER" "BY" OrderByItem {"," OrderByItem}*
+LimitClause ::= "LIMIT" integer
+OffsetClause ::= "OFFSET" integer
+UpdateClause ::= "UPDATE" VariableDeclaration "SET" UpdateItem {"," UpdateItem}*
-OrderByItem = Expression ["ASC" | "DESC"]
-GroupByItem = PathExpression
-UpdateItem = PathExpression "=" (Expression | "NULL")
+OrderByItem ::= Expression ["ASC" | "DESC"]
+GroupByItem ::= PathExpression
+UpdateItem ::= PathExpression "=" (Expression | "NULL")
-IdentificationVariableDeclaration = RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}
-JoinVariableDeclaration = Join [IndexBy]
-RangeVariableDeclaration = identifier {"." identifier} [["AS"] IdentificationVariable]
-VariableDeclaration = identifier [["AS"] IdentificationVariable]
-IdentificationVariable = identifier
+IdentificationVariableDeclaration ::= RangeVariableDeclaration [IndexBy] {JoinVariableDeclaration}*
+JoinVariableDeclaration ::= Join [IndexBy]
+RangeVariableDeclaration ::= identifier {"." identifier}* [["AS"] IdentificationVariable]
+VariableDeclaration ::= identifier [["AS"] IdentificationVariable]
+IdentificationVariable ::= identifier
-Join = ["LEFT" | "INNER"] "JOIN" RangeVariableDeclaration [("ON" | "WITH") ConditionalExpression]
-IndexBy = "INDEX" "BY" identifier
+Join ::= ["LEFT" | "INNER"] "JOIN" RangeVariableDeclaration [("ON" | "WITH") ConditionalExpression]
+IndexBy ::= "INDEX" "BY" identifier
-ConditionalExpression = ConditionalTerm {"OR" ConditionalTerm}
-ConditionalTerm = ConditionalFactor {"AND" ConditionalFactor}
-ConditionalFactor = ["NOT"] ConditionalPrimary
-ConditionalPrimary = SimpleConditionalExpression | "(" ConditionalExpression ")"
+ConditionalExpression ::= ConditionalTerm {"OR" ConditionalTerm}*
+ConditionalTerm ::= ConditionalFactor {"AND" ConditionalFactor}*
+ConditionalFactor ::= ["NOT"] ConditionalPrimary
+ConditionalPrimary ::= SimpleConditionalExpression | "(" ConditionalExpression ")"
SimpleConditionalExpression
- = Expression (ComparisonExpression | BetweenExpression | LikeExpression
+ ::= Expression (ComparisonExpression | BetweenExpression | LikeExpression
| InExpression | NullComparisonExpression) | ExistsExpression
-Atom = string | integer | float | boolean | input_parameter
+Atom ::= string | integer | float | boolean | input_parameter
-Expression = Term {("+" | "-") Term}
-Term = Factor {("*" | "/") Factor}
-Factor = [("+" | "-")] Primary
-Primary = PathExpression | Atom | "(" Expression ")" | Function | AggregateExpression
+Expression ::= Term {("+" | "-") Term}*
+Term ::= Factor {("*" | "/") Factor}*
+Factor ::= [("+" | "-")] Primary
+Primary ::= PathExpression | Atom | "(" Expression ")" | Function | AggregateExpression
-SelectExpression = (PathExpressionEndingWithAsterisk | Expression | "(" Subselect ")" ) [["AS"] FieldIdentificationVariable]
-PathExpression = identifier {"." identifier}
-PathExpressionEndingWithAsterisk = {identifier "."} "*"
-FieldIdentificationVariable = identifier
+SelectExpression ::= (PathExpressionEndingWithAsterisk | Expression | "(" Subselect ")" ) [["AS"] FieldIdentificationVariable]
+PathExpression ::= identifier {"." identifier}*
+PathExpressionEndingWithAsterisk ::= {identifier "."}* "*"
+FieldIdentificationVariable ::= identifier
-AggregateExpression = ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] Expression ")"
+AggregateExpression ::= ("AVG" | "MAX" | "MIN" | "SUM") "(" ["DISTINCT"] Expression ")"
| "COUNT" "(" ["DISTINCT"] (Expression | "*") ")"
-QuantifiedExpression = ("ALL" | "ANY" | "SOME") "(" Subselect ")"
-BetweenExpression = ["NOT"] "BETWEEN" Expression "AND" Expression
-ComparisonExpression = ComparisonOperator ( QuantifiedExpression | Expression | "(" Subselect ")" )
-InExpression = ["NOT"] "IN" "(" (Atom {"," Atom} | Subselect) ")"
-LikeExpression = ["NOT"] "LIKE" Expression ["ESCAPE" string]
-NullComparisonExpression = "IS" ["NOT"] "NULL"
-ExistsExpression = "EXISTS" "(" Subselect ")"
+QuantifiedExpression ::= ("ALL" | "ANY" | "SOME") "(" Subselect ")"
+BetweenExpression ::= ["NOT"] "BETWEEN" Expression "AND" Expression
+ComparisonExpression ::= ComparisonOperator ( QuantifiedExpression | Expression | "(" Subselect ")" )
+InExpression ::= ["NOT"] "IN" "(" (Atom {"," Atom}* | Subselect) ")"
+LikeExpression ::= ["NOT"] "LIKE" Expression ["ESCAPE" string]
+NullComparisonExpression ::= "IS" ["NOT"] "NULL"
+ExistsExpression ::= "EXISTS" "(" Subselect ")"
-ComparisonOperator = "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
+ComparisonOperator ::= "=" | "<" | "<=" | "<>" | ">" | ">=" | "!="
-Function = identifier "(" [Expression {"," Expression}] ")"
+Function ::= identifier "(" [Expression {"," Expression}*] ")"
diff --git a/tests/Orm/AllTests.php b/tests/Orm/AllTests.php
index a212b6a68..dd18a37b7 100644
--- a/tests/Orm/AllTests.php
+++ b/tests/Orm/AllTests.php
@@ -11,6 +11,7 @@ require_once 'Orm/Query/AllTests.php';
require_once 'Orm/Hydration/AllTests.php';
require_once 'Orm/Ticket/AllTests.php';
require_once 'Orm/Entity/AllTests.php';
+require_once 'Orm/Associations/AllTests.php';
// Tests
require_once 'Orm/UnitOfWorkTest.php';
@@ -39,6 +40,7 @@ class Orm_AllTests
$suite->addTest(Orm_Hydration_AllTests::suite());
$suite->addTest(Orm_Entity_AllTests::suite());
$suite->addTest(Orm_Ticket_AllTests::suite());
+ $suite->addTest(Orm_Associations_AllTests::suite());
return $suite;
}
diff --git a/tests/Orm/Associations/AllTests.php b/tests/Orm/Associations/AllTests.php
new file mode 100644
index 000000000..dab2eb871
--- /dev/null
+++ b/tests/Orm/Associations/AllTests.php
@@ -0,0 +1,30 @@
+addTestSuite('Orm_Associations_OneToOneMappingTest');
+
+ return $suite;
+ }
+}
+
+if (PHPUnit_MAIN_METHOD == 'Orm_Associations_AllTests::main') {
+ Orm_Associations_AllTests::main();
+}
diff --git a/tests/Orm/Query/LanguageRecognitionTest.php b/tests/Orm/Query/LanguageRecognitionTest.php
index 51a9cfbfd..eb6d6bcb4 100755
--- a/tests/Orm/Query/LanguageRecognitionTest.php
+++ b/tests/Orm/Query/LanguageRecognitionTest.php
@@ -38,18 +38,21 @@ require_once 'lib/DoctrineTestInit.php';
*/
class Orm_Query_LanguageRecognitionTest extends Doctrine_OrmTestCase
{
- public function assertValidDql($dql)
+ public function assertValidDql($dql, $debug = false)
{
try {
$entityManager = $this->_em;
$query = $entityManager->createQuery($dql);
$parserResult = $query->parse();
} catch (Doctrine_Exception $e) {
+ if ($debug) {
+ echo $e->getTraceAsString() . PHP_EOL;
+ }
$this->fail($e->getMessage());
}
}
- public function assertInvalidDql($dql)
+ public function assertInvalidDql($dql, $debug = false)
{
try {
$entityManager = $this->_em;
@@ -59,6 +62,11 @@ class Orm_Query_LanguageRecognitionTest extends Doctrine_OrmTestCase
$this->fail('No syntax errors were detected, when syntax errors were expected');
} catch (Doctrine_Exception $e) {
+ //echo $e->getMessage() . PHP_EOL;
+ if ($debug) {
+ echo $e->getMessage() . PHP_EOL;
+ echo $e->getTraceAsString() . PHP_EOL;
+ }
// It was expected!
}
}
@@ -351,20 +359,20 @@ class Orm_Query_LanguageRecognitionTest extends Doctrine_OrmTestCase
{
$this->assertValidDql("SELECT * FROM CmsUser u WHERE u.name NOT BETWEEN 'jepso' AND 'zYne'");
}
-/*
- public function testAllExpression()
+
+/* public function testAllExpressionWithCorrelatedSubquery()
{
// We need existant classes here, otherwise semantical will always fail
- $this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > ALL (SELECT m.salary FROM Manager m WHERE m.department = e.department)');
+ $this->assertValidDql('SELECT * FROM CompanyEmployee e WHERE e.salary > ALL (SELECT m.salary FROM CompanyManager m WHERE m.department = e.department)', true);
}
- public function testAnyExpression()
+ public function testAnyExpressionWithCorrelatedSubquery()
{
// We need existant classes here, otherwise semantical will always fail
$this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > ANY (SELECT m.salary FROM Manager m WHERE m.department = e.department)');
}
- public function testSomeExpression()
+ public function testSomeExpressionWithCorrelatedSubquery()
{
// We need existant classes here, otherwise semantical will always fail
$this->assertValidDql('SELECT * FROM Employee e WHERE e.salary > SOME (SELECT m.salary FROM Manager m WHERE m.department = e.department)');
@@ -385,14 +393,47 @@ class Orm_Query_LanguageRecognitionTest extends Doctrine_OrmTestCase
$this->assertValidDql("SELECT u.id FROM CmsUser u WHERE u.name LIKE 'z|%' ESCAPE '|'");
}
+ /**
+ * TODO: Hydration can't deal with this currently but it should be allowed.
+ * Also, generated SQL looks like: "... FROM cms_user, cms_article ..." which
+ * may not work on all dbms.
+ *
+ * The main use case for this generalized style of join is when a join condition
+ * does not involve a foreign key relationship that is mapped to an entity relationship.
+ */
+ public function testImplicitJoinWithCartesianProductAndConditionInWhere()
+ {
+ $this->assertValidDql("SELECT u.*, a.* FROM CmsUser u, CmsArticle a WHERE u.name = a.topic");
+ }
+
+ public function testImplicitJoinInWhereOnSingleValuedAssociationPathExpression()
+ {
+ // This should be allowed because avatar is a single-value association.
+ // SQL: SELECT ... FROM forum_user fu INNER JOIN forum_avatar fa ON fu.avatar_id = fa.id WHERE fa.id = ?
+ $this->assertValidDql("SELECT u.* FROM ForumUser u WHERE u.avatar.id = ?");
+ }
+
+ public function testImplicitJoinInWhereOnCollectionValuedPathExpression()
+ {
+ // This should be forbidden, because articles is a collection
+ $this->assertInvalidDql("SELECT u.* FROM CmsUser u WHERE u.articles.title = ?");
+ }
public function testInvalidSyntaxIsRejected()
{
$this->assertInvalidDql("FOOBAR CmsUser");
-
$this->assertInvalidDql("DELETE FROM CmsUser.articles");
-
$this->assertInvalidDql("DELETE FROM CmsUser cu WHERE cu.articles.id > ?");
+ $this->assertInvalidDql("SELECT user FROM CmsUser user");
+
+ // Error message here is: Relation 'comments' does not exist in component 'CmsUser'
+ // This means it is intepreted as:
+ // SELECT u.* FROM CmsUser u JOIN u.articles JOIN u.comments
+ // This seems wrong. "JOIN u.articles.comments" should not be allowed.
+ $this->assertInvalidDql("SELECT u.* FROM CmsUser u JOIN u.articles.comments");
+
+ // Currently UNDEFINED OFFSET error
+ $this->assertInvalidDql("SELECT * FROM CmsUser.articles.comments");
}
}
diff --git a/tests/lib/mocks/Doctrine_DatabasePlatformMock.php b/tests/lib/mocks/Doctrine_DatabasePlatformMock.php
index 7f2eff934..df71b005c 100644
--- a/tests/lib/mocks/Doctrine_DatabasePlatformMock.php
+++ b/tests/lib/mocks/Doctrine_DatabasePlatformMock.php
@@ -2,7 +2,7 @@
class Doctrine_DatabasePlatformMock extends Doctrine_DatabasePlatform
{
- public function getNativeDeclaration($field) {}
+ public function getNativeDeclaration(array $field) {}
public function getPortableDeclaration(array $field) {}
}