Source for file Export.php

Documentation is available at Export.php

  1. <?php
  2. /*
  3.  *  $Id: Export.php 2288 2007-08-29 21:51:49Z Jonathan.Wage $
  4.  *
  5.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  6.  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  7.  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  8.  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  9.  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  10.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  11.  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  12.  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  13.  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  14.  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  15.  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  16.  *
  17.  * This software consists of voluntary contributions made by many individuals
  18.  * and is licensed under the LGPL. For more information, see
  19.  * <http://www.phpdoctrine.com>.
  20.  */
  21. Doctrine::autoload('Doctrine_Connection_Module');
  22. /**
  23.  * Doctrine_Export
  24.  *
  25.  * @package     Doctrine
  26.  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
  27.  * @author      Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
  28.  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
  29.  * @category    Object Relational Mapping
  30.  * @link        www.phpdoctrine.com
  31.  * @since       1.0
  32.  * @version     $Revision: 2288 $
  33.  */
  34. {
  35.     protected $valid_default_values = array(
  36.         'text'      => '',
  37.         'boolean'   => true,
  38.         'integer'   => 0,
  39.         'decimal'   => 0.0,
  40.         'float'     => 0.0,
  41.         'timestamp' => '1970-01-01 00:00:00',
  42.         'time'      => '00:00:00',
  43.         'date'      => '1970-01-01',
  44.         'clob'      => '',
  45.         'blob'      => '',
  46.         'string'    => ''
  47.     );
  48.  
  49.     /**
  50.      * drop an existing database
  51.      * (this method is implemented by the drivers)
  52.      *
  53.      * @param string $name name of the database that should be dropped
  54.      * @return void 
  55.      */
  56.     public function dropDatabase($database)
  57.     {
  58.         $this->conn->execute($this->dropDatabaseSql($database));
  59.     }
  60.     /**
  61.      * drop an existing database
  62.      * (this method is implemented by the drivers)
  63.      *
  64.      * @param string $name name of the database that should be dropped
  65.      * @return void 
  66.      */
  67.     public function dropDatabaseSql($database)
  68.     {
  69.         throw new Doctrine_Export_Exception('Drop database not supported by this driver.');
  70.     }
  71.     /**
  72.      * dropTableSql
  73.      * drop an existing table
  74.      *
  75.      * @param string $table           name of table that should be dropped from the database
  76.      * @return string 
  77.      */
  78.     public function dropTableSql($table)
  79.     {
  80.         return 'DROP TABLE ' $this->conn->quoteIdentifier($table);
  81.     }
  82.     /**
  83.      * dropTable
  84.      * drop an existing table
  85.      *
  86.      * @param string $table           name of table that should be dropped from the database
  87.      * @return void 
  88.      */
  89.     public function dropTable($table)
  90.     {
  91.         $this->conn->execute($this->dropTableSql($table));
  92.     }
  93.  
  94.     /**
  95.      * drop existing index
  96.      *
  97.      * @param string    $table        name of table that should be used in method
  98.      * @param string    $name         name of the index to be dropped
  99.      * @return void 
  100.      */
  101.     public function dropIndex($table$name)
  102.     {
  103.         return $this->conn->exec($this->dropIndexSql($table$name));
  104.     }
  105.     
  106.     /**
  107.      * dropIndexSql
  108.      *
  109.      * @param string    $table        name of table that should be used in method
  110.      * @param string    $name         name of the index to be dropped
  111.      * @return string                 SQL that is used for dropping an index
  112.      */
  113.     public function dropIndexSql($table$name
  114.     {
  115.         $name $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
  116.         return 'DROP INDEX ' $name;
  117.     }
  118.     /**
  119.      * drop existing constraint
  120.      *
  121.      * @param string    $table        name of table that should be used in method
  122.      * @param string    $name         name of the constraint to be dropped
  123.      * @param string    $primary      hint if the constraint is primary
  124.      * @return void 
  125.      */
  126.     public function dropConstraint($table$name$primary false)
  127.     {
  128.         $table $this->conn->quoteIdentifier($table);
  129.         $name  $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
  130.         return $this->conn->exec('ALTER TABLE ' $table ' DROP CONSTRAINT ' $name);
  131.     }
  132.     /**
  133.      * dropSequenceSql
  134.      * drop existing sequence
  135.      * (this method is implemented by the drivers)
  136.      *
  137.      * @throws Doctrine_Connection_Exception     if something fails at database level
  138.      * @param string $sequenceName      name of the sequence to be dropped
  139.      * @return void 
  140.      */
  141.     public function dropSequence($sequenceName)
  142.     {
  143.         $this->conn->exec($this->dropSequenceSql($sequenceName));
  144.     }
  145.     /**
  146.      * dropSequenceSql
  147.      * drop existing sequence
  148.      *
  149.      * @throws Doctrine_Connection_Exception     if something fails at database level
  150.      * @param string $sequenceName name of the sequence to be dropped
  151.      * @return void 
  152.      */
  153.     public function dropSequenceSql($sequenceName)
  154.     {
  155.         throw new Doctrine_Export_Exception('Drop sequence not supported by this driver.');
  156.     }
  157.     /**
  158.      * create a new database
  159.      * (this method is implemented by the drivers)
  160.      *
  161.      * @param string $name name of the database that should be created
  162.      * @return void 
  163.      */
  164.     public function createDatabase($database)
  165.     {
  166.         $this->conn->execute($this->createDatabaseSql($database));
  167.     }
  168.     /**
  169.      * create a new database
  170.      * (this method is implemented by the drivers)
  171.      *
  172.      * @param string $name name of the database that should be created
  173.      * @return string 
  174.      */
  175.     public function createDatabaseSql($database)
  176.     {
  177.         throw new Doctrine_Export_Exception('Create database not supported by this driver.');
  178.     }
  179.     /**
  180.      * create a new table
  181.      *
  182.      * @param string $name   Name of the database that should be created
  183.      * @param array $fields  Associative array that contains the definition of each field of the new table
  184.      *                        The indexes of the array entries are the names of the fields of the table an
  185.      *                        the array entry values are associative arrays like those that are meant to be
  186.      *                        passed with the field definitions to get[Type]Declaration() functions.
  187.      *                           array(
  188.      *                               'id' => array(
  189.      *                                   'type' => 'integer',
  190.      *                                   'unsigned' => 1
  191.      *                                   'notnull' => 1
  192.      *                                   'default' => 0
  193.      *                               ),
  194.      *                               'name' => array(
  195.      *                                   'type' => 'text',
  196.      *                                   'length' => 12
  197.      *                               ),
  198.      *                               'password' => array(
  199.      *                                   'type' => 'text',
  200.      *                                   'length' => 12
  201.      *                               )
  202.      *                           );
  203.      * @param array $options  An associative array of table options:
  204.      *
  205.      * @return string 
  206.      */
  207.     public function createTableSql($namearray $fieldsarray $options array())
  208.     {
  209.         if $name{
  210.             throw new Doctrine_Export_Exception('no valid table name specified');
  211.         }
  212.         
  213.         if (empty($fields)) {
  214.             throw new Doctrine_Export_Exception('no fields specified for table ' $name);
  215.         }
  216.  
  217.         $queryFields $this->getFieldDeclarationList($fields);
  218.  
  219.  
  220.         if (isset($options['primary']&& empty($options['primary'])) {
  221.             $queryFields .= ', PRIMARY KEY(' implode(', 'array_values($options['primary'])) ')';
  222.         }
  223.  
  224.         if (isset($options['indexes']&& empty($options['indexes'])) {
  225.             foreach($options['indexes'as $index => $definition{
  226.                 $queryFields .= ', ' $this->getIndexDeclaration($index$definition);
  227.             }
  228.         }
  229.  
  230.         $query 'CREATE TABLE ' $this->conn->quoteIdentifier($nametrue' (' $queryFields;
  231.         
  232.         $check $this->getCheckDeclaration($fields);
  233.         
  234.         if empty($check)) {
  235.             $query .= ', ' $check;
  236.         }
  237.  
  238.         $query .= ')';
  239.  
  240.  
  241.  
  242.         $sql[$query;
  243.  
  244.         if (isset($options['foreignKeys'])) {
  245.  
  246.             foreach ((array) $options['foreignKeys'as $k => $definition{
  247.                 if (is_array($definition)) {
  248.                     $sql[$this->createForeignKeySql($name$definition);
  249.                 }
  250.             }
  251.         }   
  252.         return $sql;
  253.     }
  254.     /**
  255.      * create a new table
  256.      *
  257.      * @param string $name   Name of the database that should be created
  258.      * @param array $fields  Associative array that contains the definition of each field of the new table
  259.      * @param array $options  An associative array of table options:
  260.      * @see Doctrine_Export::createTableSql()
  261.      *
  262.      * @return void 
  263.      */
  264.     public function createTable($namearray $fieldsarray $options array())
  265.     {
  266.         $sql = (array) $this->createTableSql($name$fields$options);
  267.  
  268.         foreach ($sql as $query{
  269.             $this->conn->execute($query);
  270.         }
  271.     }
  272.     /**
  273.      * create sequence
  274.      *
  275.      * @throws Doctrine_Connection_Exception     if something fails at database level
  276.      * @param string    $seqName        name of the sequence to be created
  277.      * @param string    $start          start value of the sequence; default is 1
  278.      * @param array     $options  An associative array of table options:
  279.      *                           array(
  280.      *                               'comment' => 'Foo',
  281.      *                               'charset' => 'utf8',
  282.      *                               'collate' => 'utf8_unicode_ci',
  283.      *                           );
  284.      * @return void 
  285.      */
  286.     public function createSequence($seqName$start 1array $options array())
  287.     {
  288.         return $this->conn->execute($this->createSequenceSql($seqName$start 1$options));
  289.     }
  290.     /**
  291.      * return RDBMS specific create sequence statement
  292.      * (this method is implemented by the drivers)
  293.      *
  294.      * @throws Doctrine_Connection_Exception     if something fails at database level
  295.      * @param string    $seqName        name of the sequence to be created
  296.      * @param string    $start          start value of the sequence; default is 1
  297.      * @param array     $options  An associative array of table options:
  298.      *                           array(
  299.      *                               'comment' => 'Foo',
  300.      *                               'charset' => 'utf8',
  301.      *                               'collate' => 'utf8_unicode_ci',
  302.      *                           );
  303.      * @return string 
  304.      */
  305.     public function createSequenceSql($seqName$start 1array $options array())
  306.     {
  307.         throw new Doctrine_Export_Exception('Create sequence not supported by this driver.');
  308.     }
  309.     /**
  310.      * create a constraint on a table
  311.      *
  312.      * @param string    $table         name of the table on which the constraint is to be created
  313.      * @param string    $name          name of the constraint to be created
  314.      * @param array     $definition    associative array that defines properties of the constraint to be created.
  315.      *                                  Currently, only one property named FIELDS is supported. This property
  316.      *                                  is also an associative with the names of the constraint fields as array
  317.      *                                  constraints. Each entry of this array is set to another type of associative
  318.      *                                  array that specifies properties of the constraint that are specific to
  319.      *                                  each field.
  320.      *
  321.      *                                  Example
  322.      *                                     array(
  323.      *                                         'fields' => array(
  324.      *                                             'user_name' => array(),
  325.      *                                             'last_login' => array()
  326.      *                                         )
  327.      *                                     )
  328.      * @return void 
  329.      */
  330.     public function createConstraint($table$name$definition)
  331.     {
  332.         return $this->conn->exec($this->createConstraintSql($table$name$definition));
  333.     }
  334.     /**
  335.      * create a constraint on a table
  336.      *
  337.      * @param string    $table         name of the table on which the constraint is to be created
  338.      * @param string    $name          name of the constraint to be created
  339.      * @param array     $definition    associative array that defines properties of the constraint to be created.
  340.      *                                  Currently, only one property named FIELDS is supported. This property
  341.      *                                  is also an associative with the names of the constraint fields as array
  342.      *                                  constraints. Each entry of this array is set to another type of associative
  343.      *                                  array that specifies properties of the constraint that are specific to
  344.      *                                  each field.
  345.      *
  346.      *                                  Example
  347.      *                                     array(
  348.      *                                         'fields' => array(
  349.      *                                             'user_name' => array(),
  350.      *                                             'last_login' => array()
  351.      *                                         )
  352.      *                                     )
  353.      * @return void 
  354.      */
  355.     public function createConstraintSql($table$name$definition)
  356.     {
  357.         $table $this->conn->quoteIdentifier($table);
  358.         $name  $this->conn->quoteIdentifier($this->conn->formatter->getIndexName($name));
  359.         $query 'ALTER TABLE ' $table ' ADD CONSTRAINT ' $name;
  360.  
  361.         if (isset($definition['primary']&& $definition['primary']{
  362.             $query .= ' PRIMARY KEY';
  363.         elseif (isset($definition['unique']&& $definition['unique']{
  364.             $query .= ' UNIQUE';
  365.         }
  366.  
  367.         $fields array();
  368.         foreach (array_keys($definition['fields']as $field{
  369.             $fields[$this->conn->quoteIdentifier($fieldtrue);
  370.         }
  371.         $query .= ' ('implode(', '$fields')';
  372.  
  373.         return $query;
  374.     }
  375.     /**
  376.      * Get the stucture of a field into an array
  377.      *
  378.      * @param string    $table         name of the table on which the index is to be created
  379.      * @param string    $name          name of the index to be created
  380.      * @param array     $definition    associative array that defines properties of the index to be created.
  381.      *                                  Currently, only one property named FIELDS is supported. This property
  382.      *                                  is also an associative with the names of the index fields as array
  383.      *                                  indexes. Each entry of this array is set to another type of associative
  384.      *                                  array that specifies properties of the index that are specific to
  385.      *                                  each field.
  386.      *
  387.      *                                  Currently, only the sorting property is supported. It should be used
  388.      *                                  to define the sorting direction of the index. It may be set to either
  389.      *                                  ascending or descending.
  390.      *
  391.      *                                  Not all DBMS support index sorting direction configuration. The DBMS
  392.      *                                  drivers of those that do not support it ignore this property. Use the
  393.      *                                  function supports() to determine whether the DBMS driver can manage indexes.
  394.      *
  395.      *                                  Example
  396.      *                                     array(
  397.      *                                         'fields' => array(
  398.      *                                             'user_name' => array(
  399.      *                                                 'sorting' => 'ascending'
  400.      *                                             ),
  401.      *                                             'last_login' => array()
  402.      *                                         )
  403.      *                                     )
  404.      * @return void 
  405.      */
  406.     public function createIndex($table$namearray $definition)
  407.     {
  408.         return $this->conn->execute($this->createIndexSql($table$name$definition));
  409.     }
  410.     /**
  411.      * Get the stucture of a field into an array
  412.      *
  413.      * @param string    $table         name of the table on which the index is to be created
  414.      * @param string    $name          name of the index to be created
  415.      * @param array     $definition    associative array that defines properties of the index to be created.
  416.      * @see Doctrine_Export::createIndex()
  417.      * @return string 
  418.      */
  419.     public function createIndexSql($table$namearray $definition)
  420.     {
  421.         $table  $this->conn->quoteIdentifier($table);
  422.         $name   $this->conn->quoteIdentifier($name);
  423.         $type   '';
  424.  
  425.         if(isset($definition['type'])) {
  426.             switch (strtolower($definition['type'])) {
  427.                 case 'unique':
  428.                     $type strtoupper($definition['type']' ';
  429.                 break;
  430.                 default:
  431.                     throw new Doctrine_Export_Exception('Unknown index type ' $definition['type']);
  432.             }
  433.         }
  434.  
  435.         $query 'CREATE ' $type 'INDEX ' $name ' ON ' $table;
  436.  
  437.         $fields array();
  438.         foreach ($definition['fields'as $field{
  439.             $fields[$this->conn->quoteIdentifier($field);
  440.         }
  441.         $query .= ' (' implode(', '$fields')';
  442.  
  443.         return $query;
  444.     }
  445.     /**
  446.      * createForeignKeySql
  447.      *
  448.      * @param string    $table         name of the table on which the foreign key is to be created
  449.      * @param array     $definition    associative array that defines properties of the foreign key to be created.
  450.      * @return string 
  451.      */
  452.     public function createForeignKeySql($tablearray $definition)
  453.     {
  454.         $table $this->conn->quoteIdentifier($table);
  455.  
  456.         $query 'ALTER TABLE ' $table ' ADD CONSTRAINT ' $this->getForeignKeyDeclaration($definition);
  457.  
  458.         return $query;
  459.     
  460.     /**
  461.      * alter an existing table
  462.      * (this method is implemented by the drivers)
  463.      *
  464.      * @param string $name         name of the table that is intended to be changed.
  465.      * @param array $changes     associative array that contains the details of each type
  466.      *                              of change that is intended to be performed. The types of
  467.      *                              changes that are currently supported are defined as follows:
  468.      *
  469.      *                              name
  470.      *
  471.      *                                 New name for the table.
  472.      *
  473.      *                             add
  474.      *
  475.      *                                 Associative array with the names of fields to be added as
  476.      *                                  indexes of the array. The value of each entry of the array
  477.      *                                  should be set to another associative array with the properties
  478.      *                                  of the fields to be added. The properties of the fields should
  479.      *                                  be the same as defined by the MDB2 parser.
  480.      *
  481.      *
  482.      *                             remove
  483.      *
  484.      *                                 Associative array with the names of fields to be removed as indexes
  485.      *                                  of the array. Currently the values assigned to each entry are ignored.
  486.      *                                  An empty array should be used for future compatibility.
  487.      *
  488.      *                             rename
  489.      *
  490.      *                                 Associative array with the names of fields to be renamed as indexes
  491.      *                                  of the array. The value of each entry of the array should be set to
  492.      *                                  another associative array with the entry named name with the new
  493.      *                                  field name and the entry named Declaration that is expected to contain
  494.      *                                  the portion of the field declaration already in DBMS specific SQL code
  495.      *                                  as it is used in the CREATE TABLE statement.
  496.      *
  497.      *                             change
  498.      *
  499.      *                                 Associative array with the names of the fields to be changed as indexes
  500.      *                                  of the array. Keep in mind that if it is intended to change either the
  501.      *                                  name of a field and any other properties, the change array entries
  502.      *                                  should have the new names of the fields as array indexes.
  503.      *
  504.      *                                 The value of each entry of the array should be set to another associative
  505.      *                                  array with the properties of the fields to that are meant to be changed as
  506.      *                                  array entries. These entries should be assigned to the new values of the
  507.      *                                  respective properties. The properties of the fields should be the same
  508.      *                                  as defined by the MDB2 parser.
  509.      *
  510.      *                             Example
  511.      *                                 array(
  512.      *                                     'name' => 'userlist',
  513.      *                                     'add' => array(
  514.      *                                         'quota' => array(
  515.      *                                             'type' => 'integer',
  516.      *                                             'unsigned' => 1
  517.      *                                         )
  518.      *                                     ),
  519.      *                                     'remove' => array(
  520.      *                                         'file_limit' => array(),
  521.      *                                         'time_limit' => array()
  522.      *                                     ),
  523.      *                                     'change' => array(
  524.      *                                         'name' => array(
  525.      *                                             'length' => '20',
  526.      *                                             'definition' => array(
  527.      *                                                 'type' => 'text',
  528.      *                                                 'length' => 20,
  529.      *                                             ),
  530.      *                                         )
  531.      *                                     ),
  532.      *                                     'rename' => array(
  533.      *                                         'sex' => array(
  534.      *                                             'name' => 'gender',
  535.      *                                             'definition' => array(
  536.      *                                                 'type' => 'text',
  537.      *                                                 'length' => 1,
  538.      *                                                 'default' => 'M',
  539.      *                                             ),
  540.      *                                         )
  541.      *                                     )
  542.      *                                 )
  543.      *
  544.      * @param boolean $check     indicates whether the function should just check if the DBMS driver
  545.      *                              can perform the requested table alterations if the value is true or
  546.      *                              actually perform them otherwise.
  547.      * @return void 
  548.      */
  549.     public function alterTable($namearray $changes$check)
  550.     {
  551.         $this->conn->execute($this->alterTableSql($name$changes$check));
  552.     
  553.     /**
  554.      * generates the sql for altering an existing table
  555.      * (this method is implemented by the drivers)
  556.      *
  557.      * @param string $name          name of the table that is intended to be changed.
  558.      * @param array $changes        associative array that contains the details of each type      *
  559.      * @param boolean $check        indicates whether the function should just check if the DBMS driver
  560.      *                               can perform the requested table alterations if the value is true or
  561.      *                               actually perform them otherwise.
  562.      * @see Doctrine_Export::alterTable()
  563.      * @return string 
  564.      */
  565.     public function alterTableSql($namearray $changes$check)
  566.     {
  567.         throw new Doctrine_Export_Exception('Alter table not supported by this driver.');
  568.     
  569.     /**
  570.      * Get declaration of a number of field in bulk
  571.      *
  572.      * @param array $fields  a multidimensional associative array.
  573.      *       The first dimension determines the field name, while the second
  574.      *       dimension is keyed with the name of the properties
  575.      *       of the field being declared as array indexes. Currently, the types
  576.      *       of supported field properties are as follows:
  577.      *
  578.      *       length
  579.      *           Integer value that determines the maximum length of the text
  580.      *           field. If this argument is missing the field should be
  581.      *           declared to have the longest length allowed by the DBMS.
  582.      *
  583.      *       default
  584.      *           Text value to be used as default for this field.
  585.      *
  586.      *       notnull
  587.      *           Boolean flag that indicates whether this field is constrained
  588.      *           to not be set to null.
  589.      *       charset
  590.      *           Text value with the default CHARACTER SET for this field.
  591.      *       collation
  592.      *           Text value with the default COLLATION for this field.
  593.      *       unique
  594.      *           unique constraint
  595.      *
  596.      * @return string 
  597.      */
  598.     public function getFieldDeclarationList(array $fields)
  599.     {
  600.         foreach ($fields as $fieldName => $field{
  601.             $query $this->getDeclaration($fieldName$field);
  602.  
  603.             $queryFields[$query;
  604.         }
  605.         return implode(', '$queryFields);
  606.     }
  607.     /**
  608.      * Obtain DBMS specific SQL code portion needed to declare a generic type
  609.      * field to be used in statements like CREATE TABLE.
  610.      *
  611.      * @param string $name   name the field to be declared.
  612.      * @param array  $field  associative array with the name of the properties
  613.      *       of the field being declared as array indexes. Currently, the types
  614.      *       of supported field properties are as follows:
  615.      *
  616.      *       length
  617.      *           Integer value that determines the maximum length of the text
  618.      *           field. If this argument is missing the field should be
  619.      *           declared to have the longest length allowed by the DBMS.
  620.      *
  621.      *       default
  622.      *           Text value to be used as default for this field.
  623.      *
  624.      *       notnull
  625.      *           Boolean flag that indicates whether this field is constrained
  626.      *           to not be set to null.
  627.      *       charset
  628.      *           Text value with the default CHARACTER SET for this field.
  629.      *       collation
  630.      *           Text value with the default COLLATION for this field.
  631.      *       unique
  632.      *           unique constraint
  633.      *       check
  634.      *           column check constraint
  635.      *
  636.      * @return string  DBMS specific SQL code portion that should be used to
  637.      *       declare the specified field.
  638.      */
  639.     public function getDeclaration($namearray $field)
  640.     {
  641.  
  642.         $default   $this->getDefaultFieldDeclaration($field);
  643.  
  644.         $charset   (isset($field['charset']&& $field['charset']?
  645.                     ' ' $this->getCharsetFieldDeclaration($field['charset']'';
  646.  
  647.         $collation (isset($field['collation']&& $field['collation']?
  648.                     ' ' $this->getCollationFieldDeclaration($field['collation']'';
  649.  
  650.         $notnull   (isset($field['notnull']&& $field['notnull']' NOT NULL' '';
  651.  
  652.         $unique    (isset($field['unique']&& $field['unique']?
  653.                     ' ' $this->getUniqueFieldDeclaration('';
  654.                     
  655.         $check     (isset($field['check']&& $field['check']?
  656.                     ' ' $field['check''';
  657.  
  658.         $method 'get' $field['type''Declaration';
  659.  
  660.         if (method_exists($this->conn->dataDict$method)) {
  661.             return $this->conn->dataDict->$method($name$field);
  662.         else {
  663.             $dec $this->conn->dataDict->getNativeDeclaration($field);
  664.         }
  665.         return $this->conn->quoteIdentifier($nametrue' ' $dec $charset $default $notnull $unique $check $collation;
  666.     }
  667.     /**
  668.      * getDefaultDeclaration
  669.      * Obtain DBMS specific SQL code portion needed to set a default value
  670.      * declaration to be used in statements like CREATE TABLE.
  671.      *
  672.      * @param array $field      field definition array
  673.      * @return string           DBMS specific SQL code portion needed to set a default value
  674.      */
  675.     public function getDefaultFieldDeclaration($field)
  676.     {
  677.         $default '';
  678.         if (isset($field['default'])) {
  679.             if ($field['default'=== ''{
  680.                 $field['default'empty($field['notnull'])
  681.                     ? null $this->valid_default_values[$field['type']];
  682.  
  683.                 if ($field['default'=== '' && 
  684.                    ($this->conn->getAttribute(Doctrine::ATTR_PORTABILITYDoctrine::PORTABILITY_EMPTY_TO_NULL)) {
  685.                     $field['default'null;
  686.                 }
  687.             }
  688.     
  689.             if ($field['type'=== 'boolean'{
  690.                 $fields['default'$this->conn->convertBooleans($field['default']);                                     
  691.             }
  692.             $default ' DEFAULT ' $this->conn->quote($field['default']$field['type']);
  693.         }
  694.         return $default;
  695.     }
  696.     /**
  697.      * Obtain DBMS specific SQL code portion needed to set a CHECK constraint
  698.      * declaration to be used in statements like CREATE TABLE.
  699.      *
  700.      * @param array $definition     check definition
  701.      * @return string               DBMS specific SQL code portion needed to set a CHECK constraint
  702.      */
  703.     public function getCheckDeclaration(array $definition)
  704.     {
  705.         $constraints array();
  706.         foreach ($definition as $field => $def{
  707.             if (is_string($def)) {
  708.                 $constraints['CHECK (' $def ')';
  709.             else {
  710.                 if (isset($def['min'])) {
  711.                     $constraints['CHECK (' $field ' >= ' $def['min'')';
  712.                 }
  713.  
  714.                 if (isset($def['max'])) {
  715.                     $constraints['CHECK (' $field ' <= ' $def['max'')';
  716.                 }
  717.             }
  718.         }
  719.  
  720.         return implode(', '$constraints);
  721.     }
  722.     /**
  723.      * Obtain DBMS specific SQL code portion needed to set an index
  724.      * declaration to be used in statements like CREATE TABLE.
  725.      *
  726.      * @param string $name          name of the index
  727.      * @param array $definition     index definition
  728.      * @return string               DBMS specific SQL code portion needed to set an index
  729.      */
  730.     public function getIndexDeclaration($namearray $definition)
  731.     {
  732.         $name   $this->conn->quoteIdentifier($name);
  733.         $type   '';
  734.  
  735.         if (isset($definition['type'])) {
  736.             if (strtolower($definition['type']== 'unique'{
  737.                 $type strtoupper($definition['type']' ';
  738.             else {
  739.                 throw new Doctrine_Export_Exception('Unknown index type ' $definition['type']);
  740.             }
  741.         }
  742.  
  743.         if isset($definition['fields']|| is_array($definition['fields'])) {
  744.             throw new Doctrine_Export_Exception('No index columns given.');
  745.         }
  746.  
  747.         $query $type 'INDEX ' $name;
  748.  
  749.         $query .= ' (' $this->getIndexFieldDeclarationList($definition['fields']')';
  750.         
  751.         return $query;
  752.     }
  753.     /**
  754.      * getIndexFieldDeclarationList
  755.      * Obtain DBMS specific SQL code portion needed to set an index
  756.      * declaration to be used in statements like CREATE TABLE.
  757.      *
  758.      * @return string 
  759.      */
  760.     public function getIndexFieldDeclarationList(array $fields)
  761.     {
  762.         $ret array();
  763.         foreach ($fields as $field => $definition{
  764.             if(is_array($definition)) {
  765.                 $ret[$this->conn->quoteIdentifier($field);
  766.             else {
  767.                 $ret[$this->conn->quoteIdentifier($definition);
  768.             }
  769.         }
  770.         return implode(', '$ret);
  771.     }
  772.     /**
  773.      * A method to return the required SQL string that fits between CREATE ... TABLE
  774.      * to create the table as a temporary table.
  775.      *
  776.      * Should be overridden in driver classes to return the correct string for the
  777.      * specific database type.
  778.      *
  779.      * The default is to return the string "TEMPORARY" - this will result in a
  780.      * SQL error for any database that does not support temporary tables, or that
  781.      * requires a different SQL command from "CREATE TEMPORARY TABLE".
  782.      *
  783.      * @return string The string required to be placed between "CREATE" and "TABLE"
  784.      *                 to generate a temporary table, if possible.
  785.      */
  786.     public function getTemporaryTableQuery()
  787.     {
  788.         return 'TEMPORARY';
  789.     }
  790.     /**
  791.      * getForeignKeyDeclaration
  792.      * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  793.      * of a field declaration to be used in statements like CREATE TABLE.
  794.      *
  795.      * @param array $definition         an associative array with the following structure:
  796.      *           name                    optional constraint name
  797.      * 
  798.      *           local                   the local field(s)
  799.      *
  800.      *           foreign                 the foreign reference field(s)
  801.      *
  802.      *           foreignTable            the name of the foreign table
  803.      *
  804.      *           onDelete                referential delete action
  805.      *  
  806.      *           onUpdate                referential update action
  807.      *
  808.      *           deferred                deferred constraint checking
  809.      *
  810.      *  The onDelete and onUpdate keys accept the following values:
  811.      *
  812.      *  CASCADE: Delete or update the row from the parent table and automatically delete or
  813.      *           update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported.
  814.      *           Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column
  815.      *           in the parent table or in the child table.
  816.      *
  817.      *  SET NULL: Delete or update the row from the parent table and set the foreign key column or columns in the
  818.      *           child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier
  819.      *           specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.
  820.      *
  821.      *  NO ACTION: In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary
  822.      *            key value is not allowed to proceed if there is a related foreign key value in the referenced table.
  823.      *
  824.      *  RESTRICT: Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as
  825.      *            omitting the ON DELETE or ON UPDATE clause.
  826.      *
  827.      *  SET DEFAULT
  828.      *
  829.      * @return string  DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  830.      *                  of a field declaration.
  831.      */
  832.     public function getForeignKeyDeclaration(array $definition)
  833.     {
  834.         $sql  $this->getForeignKeyBaseDeclaration($definition);
  835.         $sql .= $this->getAdvancedForeignKeyOptions($definition);
  836.  
  837.         return $sql;
  838.     
  839.     /**
  840.      * getAdvancedForeignKeyOptions
  841.      * Return the FOREIGN KEY query section dealing with non-standard options
  842.      * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  843.      *
  844.      * @param array $definition     foreign key definition
  845.      * @return string 
  846.      */
  847.     public function getAdvancedForeignKeyOptions(array $definition)
  848.     {
  849.         $query '';
  850.         if empty($definition['onUpdate'])) {
  851.             $query .= ' ON UPDATE ' $this->getForeignKeyRefentialAction($definition['onUpdate']);
  852.         }
  853.         if empty($definition['onDelete'])) {
  854.             $query .= ' ON DELETE ' $this->getForeignKeyRefentialAction($definition['onDelete']);
  855.         }
  856.         return $query;
  857.     }
  858.     /**
  859.      * getForeignKeyReferentialAction
  860.      *
  861.      * returns given referential action in uppercase if valid, otherwise throws
  862.      * an exception
  863.      *
  864.      * @throws Doctrine_Exception_Exception     if unknown referential action given
  865.      * @param string $action    foreign key referential action
  866.      * @param string            foreign key referential action in uppercase
  867.      */
  868.     public function getForeignKeyReferentialAction($action)
  869.     {
  870.         $upper strtoupper($action);
  871.         switch ($upper{
  872.             case 'CASCADE':
  873.             case 'SET NULL':
  874.             case 'NO ACTION':
  875.             case 'RESTRICT':
  876.             case 'SET DEFAULT':
  877.                 return $upper;
  878.             break;
  879.             default:
  880.                 throw new Doctrine_Export_Exception('Unknown foreign key referential action \'' $upper '\' given.');
  881.         }
  882.     }
  883.     /**
  884.      * getForeignKeyBaseDeclaration
  885.      * Obtain DBMS specific SQL code portion needed to set the FOREIGN KEY constraint
  886.      * of a field declaration to be used in statements like CREATE TABLE.
  887.      *
  888.      * @param array $definition 
  889.      * @return string 
  890.      */
  891.     public function getForeignKeyBaseDeclaration(array $definition)
  892.     {
  893.         $sql '';
  894.         if (isset($definition['name'])) {
  895.             $sql .= 'CONSTRAINT ' $this->conn->quoteIdentifier($definition['name']' ';
  896.         }
  897.         $sql .= 'FOREIGN KEY (';
  898.  
  899.         if isset($definition['local'])) {
  900.             throw new Doctrine_Export_Exception('Local reference field missing from definition.');
  901.         }
  902.         if isset($definition['foreign'])) {
  903.             throw new Doctrine_Export_Exception('Foreign reference field missing from definition.');
  904.         }
  905.         if isset($definition['foreignTable'])) {
  906.             throw new Doctrine_Export_Exception('Foreign reference table missing from definition.');
  907.         }
  908.  
  909.         if is_array($definition['local'])) {
  910.             $definition['local'array($definition['local']);
  911.         }
  912.         if is_array($definition['foreign'])) {
  913.             $definition['foreign'array($definition['foreign']);
  914.         }
  915.  
  916.         $sql .= implode(', 'array_map(array($this->conn'quoteIdentifier')$definition['local']))
  917.               . ') REFERENCES '
  918.               . $definition['foreignTable''('
  919.               . implode(', 'array_map(array($this->conn'quoteIdentifier')$definition['foreign'])) ')';
  920.         
  921.         return $sql;
  922.     }
  923.     /**
  924.      * Obtain DBMS specific SQL code portion needed to set the UNIQUE constraint
  925.      * of a field declaration to be used in statements like CREATE TABLE.
  926.      *
  927.      * @return string  DBMS specific SQL code portion needed to set the UNIQUE constraint
  928.      *                  of a field declaration.
  929.      */
  930.     public function getUniqueFieldDeclaration()
  931.     {
  932.         return 'UNIQUE';
  933.     }
  934.     /**
  935.      * Obtain DBMS specific SQL code portion needed to set the CHARACTER SET
  936.      * of a field declaration to be used in statements like CREATE TABLE.
  937.      *
  938.      * @param string $charset   name of the charset
  939.      * @return string  DBMS specific SQL code portion needed to set the CHARACTER SET
  940.      *                  of a field declaration.
  941.      */
  942.     public function getCharsetFieldDeclaration($charset)
  943.     {
  944.         return '';
  945.     }
  946.     /**
  947.      * Obtain DBMS specific SQL code portion needed to set the COLLATION
  948.      * of a field declaration to be used in statements like CREATE TABLE.
  949.      *
  950.      * @param string $collation   name of the collation
  951.      * @return string  DBMS specific SQL code portion needed to set the COLLATION
  952.      *                  of a field declaration.
  953.      */
  954.     public function getCollationFieldDeclaration($collation)
  955.     {
  956.         return '';
  957.     
  958.     /**
  959.      * exportSchema
  960.      * method for exporting Doctrine_Record classes to a schema
  961.      *
  962.      * if the directory parameter is given this method first iterates
  963.      * recursively trhough the given directory in order to find any model classes
  964.      *
  965.      * Then it iterates through all declared classes and creates tables for the ones
  966.      * that extend Doctrine_Record and are not abstract classes
  967.      *
  968.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  969.      *                                           occurred during the create table operation
  970.      * @param string $directory     optional directory parameter
  971.      * @return void 
  972.      */
  973.     public function exportSchema($directory null)
  974.     {
  975.         $sql $this->exportSql($directory);
  976.  
  977.         $this->conn->beginTransaction();
  978.  
  979.         foreach ($sql as $query{
  980.             try {
  981.                 $this->conn->exec($query);
  982.             catch (Doctrine_Connection_Exception $e{
  983.                 // we only want to silence table already exists errors
  984.                 if($e->getPortableCode(!== Doctrine::ERR_ALREADY_EXISTS{
  985.                     $this->conn->rollback();
  986.                     throw $e;
  987.                 }                                           
  988.             }
  989.         }
  990.         $this->conn->commit();
  991.     }
  992.     /**
  993.      * exportClasses
  994.      * method for exporting Doctrine_Record classes to a schema
  995.      *
  996.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  997.      *                                           occurred during the create table operation
  998.      * @param array $classes 
  999.      * @return void 
  1000.      */
  1001.     public function exportClasses(array $classes)
  1002.     {
  1003.         $sql $this->exportClassesSql($classes);
  1004.  
  1005.         $this->conn->beginTransaction();
  1006.  
  1007.         foreach ($sql as $query{
  1008.             try {
  1009.                 $this->conn->exec($query);
  1010.             catch (Doctrine_Connection_Exception $e{
  1011.                 // we only want to silence table already exists errors
  1012.                 if($e->getPortableCode(!== Doctrine::ERR_ALREADY_EXISTS{
  1013.                     $this->conn->rollback();
  1014.                     throw $e;
  1015.                 }
  1016.             }
  1017.         }
  1018.         $this->conn->commit();
  1019.     }
  1020.     /**
  1021.      * exportClassesSql
  1022.      * method for exporting Doctrine_Record classes to a schema
  1023.      *
  1024.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  1025.      *                                           occurred during the create table operation
  1026.      * @param array $classes 
  1027.      * @return void 
  1028.      */
  1029.     public function exportClassesSql(array $classes)
  1030.     {
  1031.         $parent new ReflectionClass('Doctrine_Record');
  1032.  
  1033.         $sql array();
  1034.         $fks array();
  1035.  
  1036.         // we iterate trhough the diff of previously declared classes 
  1037.         // and currently declared classes
  1038.         foreach ($classes as $name{
  1039.             $class new ReflectionClass($name);
  1040.             $conn  Doctrine_Manager::getInstance()->getConnectionForComponent($name);
  1041.  
  1042.             // check if class is an instance of Doctrine_Record and not abstract
  1043.             // class must have method setTableDefinition (to avoid non-Record subclasses like symfony's sfDoctrineRecord)
  1044.             if ($class->isSubclassOf($parent&& $class->isAbstract(&& $class->hasMethod('setTableDefinition')
  1045.                 && $class->getMethod('setTableDefinition')->getDeclaringClass()->getName(== $class->getName()) {
  1046.                 $record new $name();
  1047.                 $table  $record->getTable();
  1048.                 $data $table->getExportableFormat();
  1049.                 
  1050.                 $query $this->conn->export->createTableSql($data['tableName']$data['columns']$data['options']);
  1051.  
  1052.                 if (is_array($query)) {
  1053.                     $sql array_merge($sql$query);
  1054.                 else {
  1055.                     $sql[$query;
  1056.                 }
  1057.             }
  1058.         }
  1059.         $sql array_unique($sql);
  1060.         rsort($sql);
  1061.  
  1062.         return $sql;
  1063.     }
  1064.     /**
  1065.      * exportSql
  1066.      * returns the sql for exporting Doctrine_Record classes to a schema
  1067.      *
  1068.      * if the directory parameter is given this method first iterates
  1069.      * recursively trhough the given directory in order to find any model classes
  1070.      *
  1071.      * Then it iterates through all declared classes and creates tables for the ones
  1072.      * that extend Doctrine_Record and are not abstract classes
  1073.      *
  1074.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  1075.      *                                           occurred during the create table operation
  1076.      * @param string $directory     optional directory parameter
  1077.      * @return void 
  1078.      */
  1079.     public function exportSql($directory null)
  1080.     {
  1081.         $declared get_declared_classes();
  1082.  
  1083.            if ($directory !== null{
  1084.                foreach ((array) $directory as $dir{
  1085.                 $it new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir),
  1086.                                                         RecursiveIteratorIterator::LEAVES_ONLY);
  1087.                                                         
  1088.                 foreach ($it as $file{
  1089.                     $e explode('.'$file->getFileName());
  1090.                     if (end($e=== 'php' && strpos($file->getFileName()'.inc'=== false{
  1091.                         require_once $file->getPathName();
  1092.                     }
  1093.                 }
  1094.             }
  1095.             $declared array_diff(get_declared_classes()$declared);
  1096.         }
  1097.  
  1098.         return $this->exportClassesSql($declared);
  1099.     }
  1100.     /**
  1101.      * exportTable
  1102.      * exports given table into database based on column and option definitions
  1103.      *
  1104.      * @throws Doctrine_Connection_Exception    if some error other than Doctrine::ERR_ALREADY_EXISTS
  1105.      *                                           occurred during the create table operation
  1106.      * @return boolean                          whether or not the export operation was successful
  1107.      *                                           false if table already existed in the database
  1108.      */
  1109.     public function exportTable(Doctrine_Table $table)
  1110.     {
  1111.         /**
  1112.         TODO: maybe there should be portability option for the following check
  1113.         if ( ! Doctrine::isValidClassname($table->getOption('declaringClass')->getName())) {
  1114.             throw new Doctrine_Export_Exception('Class name not valid.');
  1115.         }
  1116.         */
  1117.  
  1118.         try {
  1119.             $data $table->getExportableFormat();
  1120.  
  1121.             $this->conn->export->createTable($data['tableName']$data['columns']$data['options']);
  1122.         catch(Doctrine_Connection_Exception $e{
  1123.             // we only want to silence table already exists errors
  1124.             if($e->getPortableCode(!== Doctrine::ERR_ALREADY_EXISTS{
  1125.                 throw $e;
  1126.             }
  1127.         }
  1128.     }
  1129. }