checkin of occasional work from the past weeks.
This commit is contained in:
parent
9c1c82cab9
commit
b5401ee1c5
@ -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.
|
||||
*
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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.');
|
||||
}
|
||||
|
||||
|
@ -177,15 +177,16 @@ class Doctrine_ClassMetadata implements Doctrine_Configurable, Serializable
|
||||
* - <b>fieldName</b> (string)
|
||||
* The name of the field in the Entity.
|
||||
*
|
||||
* - <b>type</b> (string)
|
||||
* The database type of the column. Can be one of Doctrine's portable types.
|
||||
* - <b>type</b> (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.
|
||||
*
|
||||
* - <b>columnName</b> (string, optional)
|
||||
* The column name. Optional. Defaults to the field name.
|
||||
*
|
||||
* - <b>length</b> (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.
|
||||
*
|
||||
* - <b>id</b> (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
|
||||
* - <b>nullable</b> (boolean, optional)
|
||||
* Whether the column is nullable. Defaults to TRUE.
|
||||
*
|
||||
* - <b>columnDefinition</b> (string, optional)
|
||||
* - <b>columnDefinition</b> (string, optional, schema-only)
|
||||
* The SQL fragment that is used when generating the DDL for the column.
|
||||
*
|
||||
* - <b>precision</b> (integer, optional)
|
||||
* - <b>precision</b> (integer, optional, schema-only)
|
||||
* The precision of a decimal column. Only valid if the column type is decimal.
|
||||
*
|
||||
* - <b>scale</b> (integer, optional)
|
||||
* - <b>scale</b> (integer, optional, schema-only)
|
||||
* The scale of a decimal column. Only valid if the column type is decimal.
|
||||
*
|
||||
* - <b>index (string, optional, schema-only)</b>
|
||||
* 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.
|
||||
*
|
||||
* - <b>unique (string, optional, schema-only)</b>
|
||||
* 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.
|
||||
*
|
||||
* - <b>foreignKey (string, optional, schema-only)</b>
|
||||
*
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 <kvesteri@cc.hut.fi>
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 <roman@code-factory.org>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}*/
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
78
lib/Doctrine/DataType.php
Normal file
78
lib/Doctrine/DataType.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::DBAL::Types;
|
||||
|
||||
abstract class Doctrine_DataType
|
||||
{
|
||||
private static $_typeObjects = array();
|
||||
private static $_typesMap = array(
|
||||
'integer' => '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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
18
lib/Doctrine/DataType/ArrayType.php
Normal file
18
lib/Doctrine/DataType/ArrayType.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps PHP arrays to VARCHAR SQL type.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class Doctrine_DataType_ArrayType extends Doctrine_DataType
|
||||
{
|
||||
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'array';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
33
lib/Doctrine/DataType/BooleanType.php
Normal file
33
lib/Doctrine/DataType/BooleanType.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps an SQL boolean to a PHP boolean.
|
||||
*
|
||||
*/
|
||||
class Doctrine_DataType_BooleanType extends Doctrine_DataType
|
||||
{
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param unknown_type $value
|
||||
* @override
|
||||
*/
|
||||
public function convertToDatabaseValue($value, Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
return $platform->convertBooleans($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param unknown_type $value
|
||||
* @return unknown
|
||||
* @override
|
||||
*/
|
||||
public function convertToObjectValue($value)
|
||||
{
|
||||
return (bool)$value;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
35
lib/Doctrine/DataType/DateTimeType.php
Normal file
35
lib/Doctrine/DataType/DateTimeType.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps an SQL DATETIME to a PHP DateTime object.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class Doctrine_DataType_DateTimeType extends Doctrine_DataType
|
||||
{
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param unknown_type $value
|
||||
* @param Doctrine_DatabasePlatform $platform
|
||||
* @override
|
||||
*/
|
||||
public function convertToDatabaseValue($value, Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
//TODO: howto? dbms specific? delegate to platform?
|
||||
}
|
||||
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param string $value
|
||||
* @return DateTime
|
||||
* @override
|
||||
*/
|
||||
public function convertToObjectValue($value)
|
||||
{
|
||||
return new DateTime($value);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
12
lib/Doctrine/DataType/DecimalType.php
Normal file
12
lib/Doctrine/DataType/DecimalType.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps an SQL DECIMAL to a PHP double.
|
||||
*
|
||||
*/
|
||||
class Doctrine_DataType_DecimalType extends Doctrine_DataType
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
?>
|
17
lib/Doctrine/DataType/IntegerType.php
Normal file
17
lib/Doctrine/DataType/IntegerType.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps an SQL INT/MEDIUMINT/BIGINT to a PHP integer.
|
||||
*
|
||||
*/
|
||||
class Doctrine_DataType_IntegerType extends Doctrine_DataType
|
||||
{
|
||||
|
||||
|
||||
public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
28
lib/Doctrine/DataType/StringType.php
Normal file
28
lib/Doctrine/DataType/StringType.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::DBAL::Types;
|
||||
|
||||
/**
|
||||
* Type that maps an SQL VARCHAR to a PHP string.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class Doctrine_DataType_StringType extends Doctrine_DataType
|
||||
{
|
||||
public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
return $platform->getVarcharDeclaration($fieldDeclaration);
|
||||
}
|
||||
|
||||
public function getDefaultLength(Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
return $platform->getVarcharDefaultLength();
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'string';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
25
lib/Doctrine/DataType/TextType.php
Normal file
25
lib/Doctrine/DataType/TextType.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Type that maps an SQL CLOB to a PHP string.
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
class Doctrine_DataType_TextType extends Doctrine_DataType
|
||||
{
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @param array $fieldDeclaration
|
||||
* @param Doctrine_DatabasePlatform $platform
|
||||
* @return unknown
|
||||
* @override
|
||||
*/
|
||||
public function getSqlDeclaration(array $fieldDeclaration, Doctrine_DatabasePlatform $platform)
|
||||
{
|
||||
return $platform->getClobDeclarationSql($fieldDeclaration);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -31,12 +31,38 @@
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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 (<kbd>`</kbd>) -- due to MySQL
|
||||
* + double quote (<kbd>"</kbd>) -- due to Oracle
|
||||
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- 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;
|
||||
|
9
lib/Doctrine/DatabasePlatform/MockPlatform.php
Normal file
9
lib/Doctrine/DatabasePlatform/MockPlatform.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
class Doctrine_DatabasePlatform_MockPlatform extends Doctrine_DatabasePlatform
|
||||
{
|
||||
public function getNativeDeclaration(array $field) {}
|
||||
public function getPortableDeclaration(array $field) {}
|
||||
}
|
||||
|
||||
?>
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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 . "')";
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) . ' = ?')
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
24
lib/Doctrine/Id/AbstractIdGenerator.php
Normal file
24
lib/Doctrine/Id/AbstractIdGenerator.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
#namespace Doctrine::DBAL::Id;
|
||||
|
||||
/**
|
||||
* Enter description here...
|
||||
*
|
||||
* @todo Rename to AbstractIdGenerator
|
||||
*/
|
||||
abstract class Doctrine_Id_AbstractIdGenerator
|
||||
{
|
||||
protected $_em;
|
||||
|
||||
public function __construct(Doctrine_EntityManager $em)
|
||||
{
|
||||
$this->_em = $em;
|
||||
}
|
||||
|
||||
abstract public function configureForClass(Doctrine_ClassMetadata $class);
|
||||
|
||||
abstract public function generate();
|
||||
}
|
||||
|
||||
?>
|
11
lib/Doctrine/Id/IdentityGenerator.php
Normal file
11
lib/Doctrine/Id/IdentityGenerator.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
class Doctrine_Id_IdentityGenerator extends Doctrine_Id_AbstractIdGenerator
|
||||
{
|
||||
public function generate(Doctrine_EntityManager $em)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
3
lib/Doctrine/Id/SequenceGenerator.php
Normal file
3
lib/Doctrine/Id/SequenceGenerator.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
?>
|
3
lib/Doctrine/Id/TableGenerator.php
Normal file
3
lib/Doctrine/Id/TableGenerator.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
?>
|
@ -19,7 +19,7 @@
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
#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);
|
||||
|
||||
|
@ -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 <guilhermeblanco@hotmail.com>
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
|
@ -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;
|
||||
|
@ -19,8 +19,6 @@
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
Doctrine::autoload('Doctrine_Query_AbstractResult');
|
||||
|
||||
/**
|
||||
* Doctrine_Query_ParserResult
|
||||
*
|
||||
|
@ -19,8 +19,6 @@
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
|
||||
Doctrine::autoload('Doctrine_Query_AbstractResult');
|
||||
|
||||
/**
|
||||
* Doctrine_Query_QueryResult
|
||||
*
|
||||
@ -30,7 +28,7 @@ Doctrine::autoload('Doctrine_Query_AbstractResult');
|
||||
* @author Janne Vanhala <jpvanhal@cc.hut.fi>
|
||||
* @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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
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
|
||||
{ }
|
@ -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;
|
||||
|
@ -31,7 +31,6 @@
|
||||
* @link http://www.phpdoctrine.org
|
||||
* @since 2.0
|
||||
* @version $Revision$
|
||||
* @todo Merge into DatabasePlatform.
|
||||
*/
|
||||
abstract class Doctrine_Query_SqlBuilder
|
||||
{
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -18,7 +18,7 @@
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.phpdoctrine.org>.
|
||||
*/
|
||||
Doctrine::autoload('Doctrine_Exception');
|
||||
|
||||
/**
|
||||
* Doctrine_Query_Exception
|
||||
*
|
||||
@ -29,6 +29,7 @@ Doctrine::autoload('Doctrine_Exception');
|
||||
* @since 1.0
|
||||
* @version $Revision: 2702 $
|
||||
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
||||
* @todo Remove
|
||||
*/
|
||||
class Doctrine_Query_Tokenizer_Exception extends Doctrine_Exception
|
||||
{ }
|
@ -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'],
|
||||
|
@ -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...
|
||||
|
@ -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'],
|
||||
|
@ -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 <kvesteri@cc.hut.fi>
|
||||
* @author Lukas Smith <smith@pooteeweet.org> (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 '';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
@ -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}*] ")"
|
||||
|
@ -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;
|
||||
}
|
||||
|
30
tests/Orm/Associations/AllTests.php
Normal file
30
tests/Orm/Associations/AllTests.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
if (!defined('PHPUnit_MAIN_METHOD')) {
|
||||
define('PHPUnit_MAIN_METHOD', 'Orm_Associations_AllTests::main');
|
||||
}
|
||||
|
||||
require_once 'lib/DoctrineTestInit.php';
|
||||
|
||||
// Tests
|
||||
require_once 'Orm/Associations/OneToOneMappingTest.php';
|
||||
|
||||
class Orm_Associations_AllTests
|
||||
{
|
||||
public static function main()
|
||||
{
|
||||
PHPUnit_TextUI_TestRunner::run(self::suite());
|
||||
}
|
||||
|
||||
public static function suite()
|
||||
{
|
||||
$suite = new Doctrine_TestSuite('Doctrine Orm Associations');
|
||||
|
||||
$suite->addTestSuite('Orm_Associations_OneToOneMappingTest');
|
||||
|
||||
return $suite;
|
||||
}
|
||||
}
|
||||
|
||||
if (PHPUnit_MAIN_METHOD == 'Orm_Associations_AllTests::main') {
|
||||
Orm_Associations_AllTests::main();
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
class Doctrine_DatabasePlatformMock extends Doctrine_DatabasePlatform
|
||||
{
|
||||
public function getNativeDeclaration($field) {}
|
||||
public function getNativeDeclaration(array $field) {}
|
||||
public function getPortableDeclaration(array $field) {}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user