Coverage for Doctrine_Export_Pgsql

Back to coverage report

1 <?php
2 /*
3  *  $Id: Pgsql.php 2702 2007-10-03 21:43:22Z 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_Pgsql
24  *
25  * @package     Doctrine
26  * @subpackage  Export
27  * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
28  * @author      Lukas Smith <smith@pooteeweet.org> (PEAR MDB2 library)
29  * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
30  * @link        www.phpdoctrine.com
31  * @since       1.0
32  * @version     $Revision: 2702 $
33  */
34 class Doctrine_Export_Pgsql extends Doctrine_Export
35 {
36    /**
37      * create a new database
38      *
39      * @param string $name name of the database that should be created
40      * @throws PDOException
41      * @return void
42      */
43     public function createDatabaseSql($name)
44     {
45         $query  = 'CREATE DATABASE ' . $this->conn->quoteIdentifier($name);
46         
47         return $query;
48     }
49     /**
50      * drop an existing database
51      *
52      * @param string $name name of the database that should be dropped
53      * @throws PDOException
54      * @access public
55      */
56     public function dropDatabaseSql($name)
57     {
58         $query  = 'DROP DATABASE ' . $this->conn->quoteIdentifier($name);
59         
60         return $query;
61     }
62     /**
63      * getAdvancedForeignKeyOptions
64      * Return the FOREIGN KEY query section dealing with non-standard options
65      * as MATCH, INITIALLY DEFERRED, ON UPDATE, ...
66      *
67      * @param array $definition         foreign key definition
68      * @return string
69      * @access protected
70      */
71     public function getAdvancedForeignKeyOptions(array $definition)
72     {
73         $query = '';
74         if (isset($definition['match'])) {
75             $query .= ' MATCH ' . $definition['match'];
76         }
77         if (isset($definition['onUpdate'])) {
78             $query .= ' ON UPDATE ' . $definition['onUpdate'];
79         }
80         if (isset($definition['onDelete'])) {
81             $query .= ' ON DELETE ' . $definition['onDelete'];
82         }
83         if (isset($definition['deferrable'])) {
84             $query .= ' DEFERRABLE';
85         } else {
86             $query .= ' NOT DEFERRABLE';
87         }
88         if (isset($definition['feferred'])) {
89             $query .= ' INITIALLY DEFERRED';
90         } else {
91             $query .= ' INITIALLY IMMEDIATE';
92         }
93         return $query;
94     }
95
96     /**
97      * alter an existing table
98      *
99      * @param string $name         name of the table that is intended to be changed.
100      * @param array $changes     associative array that contains the details of each type
101      *                             of change that is intended to be performed. The types of
102      *                             changes that are currently supported are defined as follows:
103      *
104      *                             name
105      *
106      *                                New name for the table.
107      *
108      *                            add
109      *
110      *                                Associative array with the names of fields to be added as
111      *                                 indexes of the array. The value of each entry of the array
112      *                                 should be set to another associative array with the properties
113      *                                 of the fields to be added. The properties of the fields should
114      *                                 be the same as defined by the Metabase parser.
115      *
116      *
117      *                            remove
118      *
119      *                                Associative array with the names of fields to be removed as indexes
120      *                                 of the array. Currently the values assigned to each entry are ignored.
121      *                                 An empty array should be used for future compatibility.
122      *
123      *                            rename
124      *
125      *                                Associative array with the names of fields to be renamed as indexes
126      *                                 of the array. The value of each entry of the array should be set to
127      *                                 another associative array with the entry named name with the new
128      *                                 field name and the entry named Declaration that is expected to contain
129      *                                 the portion of the field declaration already in DBMS specific SQL code
130      *                                 as it is used in the CREATE TABLE statement.
131      *
132      *                            change
133      *
134      *                                Associative array with the names of the fields to be changed as indexes
135      *                                 of the array. Keep in mind that if it is intended to change either the
136      *                                 name of a field and any other properties, the change array entries
137      *                                 should have the new names of the fields as array indexes.
138      *
139      *                                The value of each entry of the array should be set to another associative
140      *                                 array with the properties of the fields to that are meant to be changed as
141      *                                 array entries. These entries should be assigned to the new values of the
142      *                                 respective properties. The properties of the fields should be the same
143      *                                 as defined by the Metabase parser.
144      *
145      *                            Example
146      *                                array(
147      *                                    'name' => 'userlist',
148      *                                    'add' => array(
149      *                                        'quota' => array(
150      *                                            'type' => 'integer',
151      *                                            'unsigned' => 1
152      *                                        )
153      *                                    ),
154      *                                    'remove' => array(
155      *                                        'file_limit' => array(),
156      *                                        'time_limit' => array()
157      *                                    ),
158      *                                    'change' => array(
159      *                                        'name' => array(
160      *                                            'length' => '20',
161      *                                            'definition' => array(
162      *                                                'type' => 'text',
163      *                                                'length' => 20,
164      *                                            ),
165      *                                        )
166      *                                    ),
167      *                                    'rename' => array(
168      *                                        'sex' => array(
169      *                                            'name' => 'gender',
170      *                                            'definition' => array(
171      *                                                'type' => 'text',
172      *                                                'length' => 1,
173      *                                                'default' => 'M',
174      *                                            ),
175      *                                        )
176      *                                    )
177      *                                )
178      *
179      * @param boolean $check     indicates whether the function should just check if the DBMS driver
180      *                             can perform the requested table alterations if the value is true or
181      *                             actually perform them otherwise.
182      * @throws Doctrine_Connection_Exception
183      * @return boolean
184      */
185     public function alterTable($name, array $changes, $check = false)
186     {
187         foreach ($changes as $changeName => $change) {
188             switch ($changeName) {
189                 case 'add':
190                 case 'remove':
191                 case 'change':
192                 case 'name':
193                 case 'rename':
194                     break;
195                 default:
196                     throw new Doctrine_Export_Exception('change type "' . $changeName . '\" not yet supported');
197             }
198         }
199
200         if ($check) {
201             return true;
202         }
203
204         if (isset($changes['add']) && is_array($changes['add'])) {
205             foreach ($changes['add'] as $fieldName => $field) {
206                 $query = 'ADD ' . $this->getDeclaration($fieldName, $field);
207                 $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);
208             }
209         }
210
211         if (isset($changes['remove']) && is_array($changes['remove'])) {
212             foreach ($changes['remove'] as $fieldName => $field) {
213                 $fieldName = $this->conn->quoteIdentifier($fieldName, true);
214                 $query = 'DROP ' . $fieldName;
215                 $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);
216             }
217         }
218
219         if (isset($changes['change']) && is_array($changes['change'])) {
220             foreach ($changes['change'] as $fieldName => $field) {
221                 $fieldName = $this->conn->quoteIdentifier($fieldName, true);
222                 if (isset($field['type'])) {
223                     $serverInfo = $this->conn->getServerVersion();
224
225                     if (is_array($serverInfo) && $serverInfo['major'] < 8) {
226                         throw new Doctrine_Export_Exception('changing column type for "'.$field['type'].'\" requires PostgreSQL 8.0 or above');
227                     }
228                     $query = 'ALTER ' . $fieldName . ' TYPE ' . $this->conn->datatype->getTypeDeclaration($field['definition']);
229                     $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);;
230                 }
231                 if (array_key_exists('default', $field)) {
232                     $query = 'ALTER ' . $fieldName . ' SET DEFAULT ' . $this->conn->quote($field['definition']['default'], $field['definition']['type']);
233                     $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);
234                 }
235                 if ( ! empty($field['notnull'])) {
236                     $query = 'ALTER ' . $fieldName . ' ' . ($field['definition']['notnull'] ? 'SET' : 'DROP') . ' NOT NULL';
237                     $this->conn->exec('ALTER TABLE ' . $name . ' ' . $query);
238                 }
239             }
240         }
241
242         if (isset($changes['rename']) && is_array($changes['rename'])) {
243             foreach ($changes['rename'] as $fieldName => $field) {
244                 $fieldName = $this->conn->quoteIdentifier($fieldName, true);
245                 $this->conn->exec('ALTER TABLE ' . $name . ' RENAME COLUMN ' . $fieldName . ' TO ' . $this->conn->quoteIdentifier($field['name'], true));
246             }
247         }
248
249         $name = $this->conn->quoteIdentifier($name, true);
250         if (isset($changes['name'])) {
251             $changeName = $this->conn->quoteIdentifier($changes['name'], true);
252             $this->conn->exec('ALTER TABLE ' . $name . ' RENAME TO ' . $changeName);
253         }
254     }
255     /**
256      * return RDBMS specific create sequence statement
257      *
258      * @throws Doctrine_Connection_Exception     if something fails at database level
259      * @param string    $seqName        name of the sequence to be created
260      * @param string    $start          start value of the sequence; default is 1
261      * @param array     $options  An associative array of table options:
262      *                          array(
263      *                              'comment' => 'Foo',
264      *                              'charset' => 'utf8',
265      *                              'collate' => 'utf8_unicode_ci',
266      *                          );
267      * @return string
268      */
269     public function createSequenceSql($sequenceName, $start = 1, array $options = array())
270     {
271         $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($sequenceName), true);
272         return $this->conn->exec('CREATE SEQUENCE ' . $sequenceName . ' INCREMENT 1' .
273                     ($start < 1 ? ' MINVALUE ' . $start : '') . ' START ' . $start);
274     }
275     /**
276      * drop existing sequence
277      *
278      * @param string $sequenceName name of the sequence to be dropped
279      */
280     public function dropSequenceSql($sequenceName)
281     {
282         $sequenceName = $this->conn->quoteIdentifier($this->conn->formatter->getSequenceName($sequenceName), true);
283         return 'DROP SEQUENCE ' . $sequenceName;
284     }
285
286     /**
287      * Creates a table.
288      *
289      * @param unknown_type $name
290      * @param array $fields
291      * @param array $options
292      * @return unknown
293      */
294     public function createTableSql($name, array $fields, array $options = array())
295     {
296         if ( ! $name) {
297             throw new Doctrine_Export_Exception('no valid table name specified');
298         }
299         
300         if (empty($fields)) {
301             throw new Doctrine_Export_Exception('no fields specified for table ' . $name);
302         }
303
304         $queryFields = $this->getFieldDeclarationList($fields);
305
306
307         if (isset($options['primary']) && ! empty($options['primary'])) {
308             $keyColumns = array_values($options['primary']);
309             $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns);
310             $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
311         }
312
313         $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name, true) . ' (' . $queryFields . ')';
314
315         $sql[] = $query;
316
317         if (isset($options['indexes']) && ! empty($options['indexes'])) {
318             foreach($options['indexes'] as $index => $definition) {
319                 $sql[] = $this->createIndexSql($name, $index, $definition);
320             }
321         }
322         
323         if (isset($options['foreignKeys'])) {
324
325             foreach ((array) $options['foreignKeys'] as $k => $definition) {
326                 if (is_array($definition)) {
327                     $sql[] = $this->createForeignKeySql($name, $definition);
328                 }
329             }
330         }
331         
332         return $sql;
333     }
334     
335     /**
336      * createForeignKeySql
337      *
338      * @param string    $table         name of the table on which the foreign key is to be created
339      * @param array     $definition    associative array that defines properties of the foreign key to be created.
340      * @return string
341      */
342     public function createForeignKeySql($table, array $definition)
343     {
344         $table = $this->conn->quoteIdentifier($table);
345
346         $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition);
347
348         return $query;
349     }
350 }