# Coverage for Doctrine_Export_Pgsql

Back to coverage report

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