Source for file Oracle.php

Documentation is available at Oracle.php

  1. <?php
  2. /*
  3.  *  $Id: Oracle.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_Export');
  22. /**
  23.  * Doctrine_Export_Oracle
  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.     /**
  36.      * create a new database
  37.      *
  38.      * @param object $db database object that is extended by this class
  39.      * @param string $name name of the database that should be created
  40.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  41.      * @access public
  42.      */
  43.     public function createDatabase($name)
  44.     {
  45.         if $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE))
  46.             throw new Doctrine_Export_Exception('database creation is only supported if the "emulate_database" attribute is enabled');
  47.  
  48.         $username   sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT)$name);
  49.         $password   $this->conn->dsn['password'$this->conn->dsn['password'$name;
  50.  
  51.         $tablespace $this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT)
  52.                     ? ' DEFAULT TABLESPACE '.$this->conn->options['default_tablespace''';
  53.  
  54.         $query  'CREATE USER ' $username ' IDENTIFIED BY ' $password $tablespace;
  55.         $result $this->conn->exec($query);
  56.  
  57.         try {
  58.             $query 'GRANT CREATE SESSION, CREATE TABLE, UNLIMITED TABLESPACE, CREATE SEQUENCE, CREATE TRIGGER TO ' $username;
  59.             $result $this->conn->exec($query);
  60.         catch (Exception $e{
  61.             $query 'DROP USER '.$username.' CASCADE';
  62.             $result2 $this->conn->exec($query);
  63.         }
  64.         return true;
  65.     }
  66.     /**
  67.      * drop an existing database
  68.      *
  69.      * @param object $this->conn database object that is extended by this class
  70.      * @param string $name name of the database that should be dropped
  71.      * @return mixed MDB2_OK on success, a MDB2 error on failure
  72.      * @access public
  73.      */
  74.     public function dropDatabase($name)
  75.     {
  76.         if $this->conn->getAttribute(Doctrine::ATTR_EMULATE_DATABASE))
  77.             throw new Doctrine_Export_Exception('database dropping is only supported if the
  78.                                                        "emulate_database" option is enabled');
  79.  
  80.         $username sprintf($this->conn->getAttribute(Doctrine::ATTR_DB_NAME_FORMAT)$name);
  81.  
  82.         return $this->conn->exec('DROP USER ' $username ' CASCADE');
  83.     }
  84.     /**
  85.      * add an autoincrement sequence + trigger
  86.      *
  87.      * @param string $name  name of the PK field
  88.      * @param string $table name of the table
  89.      * @param string $start start value for the sequence
  90.      * @return mixed        MDB2_OK on success, a MDB2 error on failure
  91.      * @access private
  92.      */
  93.     public function _makeAutoincrement($name$table$start 1)
  94.     {
  95.         $sql   array();
  96.         $table strtoupper($table);
  97.         $indexName  $table '_AI_PK';
  98.         $definition array(
  99.             'primary' => true,
  100.             'fields' => array($name => true),
  101.         );
  102.  
  103.         $sql[$this->createConstraintSql($table$indexName$definition);
  104.  
  105.         if (is_null($start)) {
  106.             $query 'SELECT MAX(' $this->conn->quoteIdentifier($nametrue') FROM ' $this->conn->quoteIdentifier($tabletrue);
  107.             $start $this->conn->fetchOne($query);
  108.  
  109.             ++$start;
  110.         }
  111.  
  112.         $sql[$this->createSequenceSql($table$start);
  113.  
  114.         $sequenceName $this->conn->formatter->getSequenceName($table);
  115.         $triggerName  $this->conn->quoteIdentifier($table '_AI_PK'true);
  116.         $table $this->conn->quoteIdentifier($tabletrue);
  117.         $name  $this->conn->quoteIdentifier($nametrue);
  118.         $sql['CREATE TRIGGER ' $triggerName '
  119.    BEFORE INSERT
  120.    ON '.$table.'
  121.    FOR EACH ROW
  122. DECLARE
  123.    last_Sequence NUMBER;
  124.    last_InsertID NUMBER;
  125. BEGIN
  126.    SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL;
  127.    IF (:NEW.'.$name.' IS NULL OR :NEW.'.$name.' = 0) THEN
  128.       SELECT '.$sequenceName.'.NEXTVAL INTO :NEW.'.$name.' FROM DUAL;
  129.    ELSE
  130.       SELECT NVL(Last_Number, 0) INTO last_Sequence
  131.         FROM User_Sequences
  132.        WHERE UPPER(Sequence_Name) = UPPER(\''.$sequenceName.'\');
  133.       SELECT :NEW.id INTO last_InsertID FROM DUAL;
  134.       WHILE (last_InsertID > last_Sequence) LOOP
  135.          SELECT ' $sequenceName '.NEXTVAL INTO last_Sequence FROM DUAL;
  136.       END LOOP;
  137.    END IF;
  138. END;
  139. ';
  140.         return $sql;
  141.     }
  142.     /**
  143.      * drop an existing autoincrement sequence + trigger
  144.      *
  145.      * @param string $table name of the table
  146.      * @return void 
  147.      */
  148.     public function dropAutoincrement($table)
  149.     {
  150.         $table strtoupper($table);
  151.         $triggerName $table '_AI_PK';
  152.         $trigger_name_quoted $this->conn->quote($triggerName);
  153.         $query 'SELECT trigger_name FROM user_triggers';
  154.         $query.= ' WHERE trigger_name='.$trigger_name_quoted.' OR trigger_name='.strtoupper($trigger_name_quoted);
  155.         $trigger $this->conn->fetchOne($query);
  156.  
  157.         if ($trigger{
  158.             $trigger_name  $this->conn->quoteIdentifier($table '_AI_PK'true);
  159.             $trigger_sql 'DROP TRIGGER ' $trigger_name;
  160.  
  161.             // if throws exception, trigger for autoincrement PK could not be dropped
  162.             $this->conn->exec($trigger_sql);
  163.  
  164.             // if throws exception, sequence for autoincrement PK could not be dropped
  165.             $this->dropSequence($table);
  166.  
  167.             $indexName $table '_AI_PK';
  168.  
  169.             // if throws exception, primary key for autoincrement PK could not be dropped
  170.             $this->dropConstraint($table$indexName);
  171.         }
  172.     }
  173.    /**
  174.      * A method to return the required SQL string that fits between CREATE ... TABLE
  175.      * to create the table as a temporary table.
  176.      *
  177.      * @return string The string required to be placed between "CREATE" and "TABLE"
  178.      *                 to generate a temporary table, if possible.
  179.      */
  180.     public function getTemporaryTableQuery()
  181.     {
  182.         return 'GLOBAL TEMPORARY';
  183.     }
  184.     /**
  185.      * getAdvancedForeignKeyOptions
  186.      * Return the FOREIGN KEY query section dealing with non-standard options
  187.      * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
  188.      *
  189.      * @param array $definition         foreign key definition
  190.      * @return string 
  191.      * @access protected
  192.      */
  193.     public function getAdvancedForeignKeyOptions(array $definition)
  194.     {
  195.         $query '';
  196.         if (isset($definition['onDelete'])) {
  197.             $query .= ' ON DELETE ' $definition['onDelete'];
  198.         }
  199.         if (isset($definition['deferrable'])) {
  200.             $query .= ' DEFERRABLE';
  201.         else {
  202.             $query .= ' NOT DEFERRABLE';
  203.         }
  204.         if (isset($definition['feferred'])) {
  205.             $query .= ' INITIALLY DEFERRED';
  206.         else {
  207.             $query .= ' INITIALLY IMMEDIATE';
  208.         }
  209.         return $query;
  210.     }
  211.  
  212.     /**
  213.      * create a new table
  214.      *
  215.      * @param string $name     Name of the database that should be created
  216.      * @param array $fields Associative array that contains the definition of each field of the new table
  217.      *                         The indexes of the array entries are the names of the fields of the table an
  218.      *                         the array entry values are associative arrays like those that are meant to be
  219.      *                          passed with the field definitions to get[Type]Declaration() functions.
  220.      *
  221.      *                         Example
  222.      *                         array(
  223.      *
  224.      *                             'id' => array(
  225.      *                                 'type' => 'integer',
  226.      *                                 'unsigned' => 1
  227.      *                                 'notnull' => 1
  228.      *                                 'default' => 0
  229.      *                             ),
  230.      *                             'name' => array(
  231.      *                                 'type' => 'text',
  232.      *                                 'length' => 12
  233.      *                             ),
  234.      *                             'password' => array(
  235.      *                                 'type' => 'text',
  236.      *                                 'length' => 12
  237.      *                             )
  238.      *                         );
  239.      * @param array $options  An associative array of table options:
  240.      *
  241.      * @return void 
  242.      */
  243.     public function createTable($namearray $fieldsarray $options array())
  244.     {
  245.         $this->conn->beginTransaction();
  246.  
  247.         foreach ($this->createTableSql($name$fields$optionsas $sql{
  248.             $this->conn->exec($sql);
  249.         }
  250.  
  251.         $this->conn->commit();
  252.     }
  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.      *                         The indexes of the array entries are the names of the fields of the table an
  260.      *                         the array entry values are associative arrays like those that are meant to be
  261.      *                          passed with the field definitions to get[Type]Declaration() functions.
  262.      *
  263.      *                         Example
  264.      *                         array(
  265.      *
  266.      *                             'id' => array(
  267.      *                                 'type' => 'integer',
  268.      *                                 'unsigned' => 1
  269.      *                                 'notnull' => 1
  270.      *                                 'default' => 0
  271.      *                             ),
  272.      *                             'name' => array(
  273.      *                                 'type' => 'text',
  274.      *                                 'length' => 12
  275.      *                             ),
  276.      *                             'password' => array(
  277.      *                                 'type' => 'text',
  278.      *                                 'length' => 12
  279.      *                             )
  280.      *                         );
  281.      * @param array $options  An associative array of table options:
  282.      *
  283.      * @return void 
  284.      */
  285.     public function createTableSql($namearray $fieldsarray $options array())
  286.     {
  287.         $sql parent::createTableSql($name$fields$options);
  288.  
  289.         foreach ($fields as $fieldName => $field{
  290.             if (isset($field['autoincrement']&& $field['autoincrement'||
  291.                (isset($field['autoinc']&& $fields['autoinc'])) {           
  292.                 $sql array_merge($sql$this->_makeAutoincrement($fieldName$name));
  293.             }
  294.         }
  295.  
  296.         return $sql;
  297.     }
  298.     /**
  299.      * drop an existing table
  300.      *
  301.      * @param string $name name of the table that should be dropped
  302.      * @return void 
  303.      */
  304.     public function dropTable($name)
  305.     {
  306.         //$this->conn->beginNestedTransaction();
  307.         $result $this->dropAutoincrement($name);
  308.         $result parent::dropTable($name);
  309.         //$this->conn->completeNestedTransaction();
  310.         return $result;
  311.     }
  312.     /**
  313.      * alter an existing table
  314.      *
  315.      * @param string $name         name of the table that is intended to be changed.
  316.      * @param array $changes     associative array that contains the details of each type
  317.      *                              of change that is intended to be performed. The types of
  318.      *                              changes that are currently supported are defined as follows:
  319.      *
  320.      *                              name
  321.      *
  322.      *                                 New name for the table.
  323.      *
  324.      *                             add
  325.      *
  326.      *                                 Associative array with the names of fields to be added as
  327.      *                                  indexes of the array. The value of each entry of the array
  328.      *                                  should be set to another associative array with the properties
  329.      *                                  of the fields to be added. The properties of the fields should
  330.      *                                  be the same as defined by the MDB2 parser.
  331.      *
  332.      *
  333.      *                             remove
  334.      *
  335.      *                                 Associative array with the names of fields to be removed as indexes
  336.      *                                  of the array. Currently the values assigned to each entry are ignored.
  337.      *                                  An empty array should be used for future compatibility.
  338.      *
  339.      *                             rename
  340.      *
  341.      *                                 Associative array with the names of fields to be renamed as indexes
  342.      *                                  of the array. The value of each entry of the array should be set to
  343.      *                                  another associative array with the entry named name with the new
  344.      *                                  field name and the entry named Declaration that is expected to contain
  345.      *                                  the portion of the field declaration already in DBMS specific SQL code
  346.      *                                  as it is used in the CREATE TABLE statement.
  347.      *
  348.      *                             change
  349.      *
  350.      *                                 Associative array with the names of the fields to be changed as indexes
  351.      *                                  of the array. Keep in mind that if it is intended to change either the
  352.      *                                  name of a field and any other properties, the change array entries
  353.      *                                  should have the new names of the fields as array indexes.
  354.      *
  355.      *                                 The value of each entry of the array should be set to another associative
  356.      *                                  array with the properties of the fields to that are meant to be changed as
  357.      *                                  array entries. These entries should be assigned to the new values of the
  358.      *                                  respective properties. The properties of the fields should be the same
  359.      *                                  as defined by the MDB2 parser.
  360.      *
  361.      *                             Example
  362.      *                                 array(
  363.      *                                     'name' => 'userlist',
  364.      *                                     'add' => array(
  365.      *                                         'quota' => array(
  366.      *                                             'type' => 'integer',
  367.      *                                             'unsigned' => 1
  368.      *                                         )
  369.      *                                     ),
  370.      *                                     'remove' => array(
  371.      *                                         'file_limit' => array(),
  372.      *                                         'time_limit' => array()
  373.      *                                     ),
  374.      *                                     'change' => array(
  375.      *                                         'name' => array(
  376.      *                                             'length' => '20',
  377.      *                                             'definition' => array(
  378.      *                                                 'type' => 'text',
  379.      *                                                 'length' => 20,
  380.      *                                             ),
  381.      *                                         )
  382.      *                                     ),
  383.      *                                     'rename' => array(
  384.      *                                         'sex' => array(
  385.      *                                             'name' => 'gender',
  386.      *                                             'definition' => array(
  387.      *                                                 'type' => 'text',
  388.      *                                                 'length' => 1,
  389.      *                                                 'default' => 'M',
  390.      *                                             ),
  391.      *                                         )
  392.      *                                     )
  393.      *                                 )
  394.      *
  395.      * @param boolean $check     indicates whether the function should just check if the DBMS driver
  396.      *                              can perform the requested table alterations if the value is true or
  397.      *                              actually perform them otherwise.
  398.      * @return void 
  399.      */
  400.     public function alterTable($namearray $changes$check)
  401.     {
  402.  
  403.         foreach ($changes as $changeName => $change{
  404.             switch ($changeName{
  405.                 case 'add':
  406.                 case 'remove':
  407.                 case 'change':
  408.                 case 'name':
  409.                 case 'rename':
  410.                     break;
  411.                 default:
  412.                     throw new Doctrine_Export_Exception('change type "' $changeName '" not yet supported');
  413.             }
  414.         }
  415.  
  416.         if ($check{
  417.             return false;
  418.         }
  419.  
  420.         $name $this->conn->quoteIdentifier($nametrue);
  421.  
  422.         if empty($changes['add']&& is_array($changes['add'])) {
  423.             $fields array();
  424.             foreach ($changes['add'as $fieldName => $field{
  425.                 $fields[$this->conn->getDeclaration($field['type']$fieldName$field);
  426.             }
  427.             $result $this->conn->exec('ALTER TABLE ' $name ' ADD (' implode(', '$fields')');
  428.         }
  429.  
  430.         if empty($changes['change']&& is_array($changes['change'])) {
  431.             $fields array();
  432.             foreach ($changes['change'as $fieldName => $field{
  433.                 $fields[$fieldName' ' $this->conn->getDeclaration($field['definition']['type']''$field['definition']);
  434.             }
  435.             $result $this->conn->exec('ALTER TABLE ' $name ' MODIFY (' implode(', '$fields')');
  436.         }
  437.  
  438.         if empty($changes['rename']&& is_array($changes['rename'])) {
  439.             foreach ($changes['rename'as $fieldName => $field{
  440.                 $query 'ALTER TABLE ' $name ' RENAME COLUMN ' $this->conn->quoteIdentifier($fieldNametrue)
  441.                        . ' TO ' $this->conn->quoteIdentifier($field['name']);
  442.  
  443.                 $result $this->conn->exec($query);
  444.             }
  445.         }
  446.  
  447.         if empty($changes['remove']&& is_array($changes['remove'])) {
  448.             $fields array();
  449.             foreach ($changes['remove'as $fieldName => $field{
  450.                 $fields[$this->conn->quoteIdentifier($fieldNametrue);
  451.             }
  452.             $result $this->conn->exec('ALTER TABLE ' $name ' DROP COLUMN ' implode(', '$fields));
  453.         }
  454.  
  455.         if empty($changes['name'])) {
  456.             $changeName $this->conn->quoteIdentifier($changes['name']true);
  457.             $result $this->conn->exec('ALTER TABLE ' $name ' RENAME TO ' $changeName);
  458.         }
  459.     }
  460.     /**
  461.      * create sequence
  462.      *
  463.      * @param string $seqName name of the sequence to be created
  464.      * @param string $start start value of the sequence; default is 1
  465.      * @param array     $options  An associative array of table options:
  466.      *                           array(
  467.      *                               'comment' => 'Foo',
  468.      *                               'charset' => 'utf8',
  469.      *                               'collate' => 'utf8_unicode_ci',
  470.      *                           );
  471.      * @return string 
  472.      */
  473.     public function createSequenceSql($seqName$start 1array $options array())
  474.     {
  475.         $sequenceName $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName)true);
  476.         $query  'CREATE SEQUENCE ' $sequenceName ' START WITH ' $start ' INCREMENT BY 1 NOCACHE';
  477.         $query .= ($start ' MINVALUE ' $start '');
  478.         return $query;
  479.     }
  480.     /**
  481.      * drop existing sequence
  482.      *
  483.      * @param object $this->conn database object that is extended by this class
  484.      * @param string $seqName name of the sequence to be dropped
  485.      * @return string 
  486.      */
  487.     public function dropSequenceSql($seqName)
  488.     {
  489.         $sequenceName $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($seqName)true);
  490.         return 'DROP SEQUENCE ' $sequenceName;
  491.     }
  492. }