thomascube
2006-04-04 f3704e18d89e4065cede8509256d7fbf483b7fe6
commit | author | age
c9462d 1 <?php
S 2 // +----------------------------------------------------------------------+
3 // | PHP versions 4 and 5                                                 |
4 // +----------------------------------------------------------------------+
5 // | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
6 // | Stig. S. Bakken, Lukas Smith                                         |
7 // | All rights reserved.                                                 |
8 // +----------------------------------------------------------------------+
9 // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
10 // | API as well as database abstraction for PHP applications.            |
11 // | This LICENSE is in the BSD license style.                            |
12 // |                                                                      |
13 // | Redistribution and use in source and binary forms, with or without   |
14 // | modification, are permitted provided that the following conditions   |
15 // | are met:                                                             |
16 // |                                                                      |
17 // | Redistributions of source code must retain the above copyright       |
18 // | notice, this list of conditions and the following disclaimer.        |
19 // |                                                                      |
20 // | Redistributions in binary form must reproduce the above copyright    |
21 // | notice, this list of conditions and the following disclaimer in the  |
22 // | documentation and/or other materials provided with the distribution. |
23 // |                                                                      |
24 // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
25 // | Lukas Smith nor the names of his contributors may be used to endorse |
26 // | or promote products derived from this software without specific prior|
27 // | written permission.                                                  |
28 // |                                                                      |
29 // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
30 // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
31 // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
32 // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
33 // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
34 // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35 // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36 // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
37 // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
38 // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39 // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
40 // | POSSIBILITY OF SUCH DAMAGE.                                          |
41 // +----------------------------------------------------------------------+
42 // | Author: Lorenzo Alberton <l.alberton@quipo.it>                       |
43 // +----------------------------------------------------------------------+
44 //
45 // $Id$
46
47 require_once 'MDB2/Driver/Manager/Common.php';
48
49 /**
50  * MDB2 FireBird/InterBase driver for the management modules
51  *
52  * @package MDB2
53  * @category Database
54  * @author  Lorenzo Alberton <l.alberton@quipo.it>
55  */
56 class MDB2_Driver_Manager_ibase extends MDB2_Driver_Manager_Common
57 {
58     // {{{ createDatabase()
59
60     /**
61      * create a new database
62      *
63      * @param string $name  name of the database that should be created
64      * @return mixed        MDB2_OK on success, a MDB2 error on failure
65      * @access public
66      */
67     function createDatabase($name)
68     {
69         $db =& $this->getDBInstance();
70         if (PEAR::isError($db)) {
71             return $db;
72         }
73
74         return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'Create database',
75                 'createDatabase: PHP Interbase API does not support direct queries. You have to '.
76                 'create the db manually by using isql command or a similar program');
77     }
78
79     // }}}
80     // {{{ dropDatabase()
81
82     /**
83      * drop an existing database
84      *
85      * @param string $name  name of the database that should be dropped
86      * @return mixed        MDB2_OK on success, a MDB2 error on failure
87      * @access public
88      */
89     function dropDatabase($name)
90     {
91         $db =& $this->getDBInstance();
92         if (PEAR::isError($db)) {
93             return $db;
94         }
95
96         return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, 'Drop database',
97                 'dropDatabase: PHP Interbase API does not support direct queries. You have '.
98                 'to drop the db manually by using isql command or a similar program');
99     }
100
101     // }}}
102     // {{{ _makeAutoincrement()
103
104     /**
105      * add an autoincrement sequence + trigger
106      *
107      * @param string $name  name of the PK field
108      * @param string $table name of the table
109      * @return mixed        MDB2_OK on success, a MDB2 error on failure
110      * @access private
111      */
112     function _makeAutoincrement($name, $table, $start = 1)
113     {
114         $db =& $this->getDBInstance();
115         if (PEAR::isError($db)) {
116             return $db;
117         }
118
119         $result = $db->manager->createSequence($table, $start);
120         if (PEAR::isError($result)) {
121             return $db->raiseError(MDB2_ERROR, null, null,
122                 '_makeAutoincrement: sequence for autoincrement PK could not be created');
123         }
124
125         $sequence_name = $db->getSequenceName($table);
126         $trigger_name  = $table . '_autoincrement_pk';
127         $trigger_sql = 'CREATE TRIGGER ' . $trigger_name . ' FOR ' . $table . '
128                         ACTIVE BEFORE INSERT POSITION 0
129                         AS
130                         BEGIN
131                         IF (NEW.' . $name . ' IS NULL) THEN
132                             NEW.' . $name . ' = GEN_ID('.strtoupper($sequence_name).', 1);
133                         END';
134
135         return $db->query($trigger_sql);
136     }
137
138     // }}}
139     // {{{ _dropAutoincrement()
140
141     /**
142      * drop an existing autoincrement PK / trigger
143      *
144      * @param string $name  name of the PK field
145      * @param string $table name of the table
146      * @return mixed        MDB2_OK on success, a MDB2 error on failure
147      * @access private
148      */
149     function _dropAutoincrement($name, $table)
150     {
151         $db =& $this->getDBInstance();
152         if (PEAR::isError($db)) {
153             return $db;
154         }
155
156         $result = $db->manager->dropSequence($table);
157         if (PEAR::isError($result)) {
158             return $db->raiseError(MDB2_ERROR, null, null,
159                 '_dropAutoincrement: sequence for autoincrement PK could not be dropped');
160         }
161         return MDB2_OK;
162     }
163
164     // }}}
165     // {{{ createTable()
166
167     /**
168      * create a new table
169      *
170      * @param string $name     Name of the database that should be created
171      * @param array $fields Associative array that contains the definition of each field of the new table
172      *                        The indexes of the array entries are the names of the fields of the table an
173      *                        the array entry values are associative arrays like those that are meant to be
174      *                         passed with the field definitions to get[Type]Declaration() functions.
175      *
176      *                        Example
177      *                        array(
178      *
179      *                            'id' => array(
180      *                                'type' => 'integer',
181      *                                'unsigned' => 1,
182      *                                'notnull' => 1,
183      *                                'default' => 0,
184      *                            ),
185      *                            'name' => array(
186      *                                'type' => 'text',
187      *                                'length' => 12,
188      *                            ),
189      *                            'description' => array(
190      *                                'type' => 'text',
191      *                                'length' => 12,
192      *                            )
193      *                        );
194      * @return mixed MDB2_OK on success, a MDB2 error on failure
195      * @access public
196      */
197     function createTable($name, $fields)
198     {
199         $result = parent::createTable($name, $fields);
200         if (PEAR::isError($result)) {
201             return $result;
202         }
203         foreach($fields as $field_name => $field) {
204             if (array_key_exists('autoincrement', $field) && $field['autoincrement']) {
205                 return $this->_makeAutoincrement($field_name, $name);
206             }
207         }
208     }
209
210     // }}}
211     // {{{ checkSupportedChanges()
212
213     /**
214      * check if planned changes are supported
215      *
216      * @param string $name name of the database that should be dropped
217      * @return mixed MDB2_OK on success, a MDB2 error on failure
218      * @access public
219      */
220     function checkSupportedChanges(&$changes)
221     {
222         $db =& $this->getDBInstance();
223         if (PEAR::isError($db)) {
224             return $db;
225         }
226
227         foreach ($changes as $change_name => $change) {
228             switch ($change_name) {
229             case 'notnull':
230                 return $db->raiseError(MDB2_ERROR, null, null,
231                     'checkSupportedChanges: it is not supported changes to field not null constraint');
232             case 'default':
233                 return $db->raiseError(MDB2_ERROR, null, null,
234                     'checkSupportedChanges: it is not supported changes to field default value');
235             case 'length':
236                 return $db->raiseError(MDB2_ERROR, null, null,
237                     'checkSupportedChanges: it is not supported changes to field default length');
238             case 'unsigned':
239             case 'type':
240             case 'declaration':
241             case 'definition':
242                 break;
243             default:
244                 return $db->raiseError(MDB2_ERROR, null, null,
245                     'checkSupportedChanges: it is not supported change of type' . $change_name);
246             }
247         }
248         return MDB2_OK;
249     }
250
251     // }}}
252     // {{{ dropTable()
253
254     /**
255      * drop an existing table
256      *
257      * @param string $name name of the table that should be dropped
258      * @return mixed MDB2_OK on success, a MDB2 error on failure
259      * @access public
260      */
261     function dropTable($name)
262     {
263         //remove triggers associated with the table
264         $name = strtoupper($name);
265         $triggers = $db->queryCol("SELECT RDB\$TRIGGER_NAME FROM RDB\$TRIGGERS WHERE RDB\$RELATION_NAME='$name'");
266         if (PEAR::isError($triggers)) {
267             return $triggers;
268         }
269         foreach ($triggers as $trigger) {
270             $result = $db->query('DROP TRIGGER ' . $trigger);
271             if (PEAR::isError($result)) {
272                 return $result;
273             }
274         }
275         
276         return parent::dropTable($name);
277     }
278
279     // }}}
280     // {{{ alterTable()
281
282     /**
283      * alter an existing table
284      *
285      * @param string $name name of the table that is intended to be changed.
286      * @param array $changes associative array that contains the details of each type
287      *                              of change that is intended to be performed. The types of
288      *                              changes that are currently supported are defined as follows:
289      *
290      *                              name
291      *
292      *                                 New name for the table.
293      *
294      *                             add
295      *
296      *                                 Associative array with the names of fields to be added as
297      *                                  indexes of the array. The value of each entry of the array
298      *                                  should be set to another associative array with the properties
299      *                                  of the fields to be added. The properties of the fields should
300      *                                  be the same as defined by the Metabase parser.
301      *
302      *
303      *                             remove
304      *
305      *                                 Associative array with the names of fields to be removed as indexes
306      *                                  of the array. Currently the values assigned to each entry are ignored.
307      *                                  An empty array should be used for future compatibility.
308      *
309      *                             rename
310      *
311      *                                 Associative array with the names of fields to be renamed as indexes
312      *                                  of the array. The value of each entry of the array should be set to
313      *                                  another associative array with the entry named name with the new
314      *                                  field name and the entry named Declaration that is expected to contain
315      *                                  the portion of the field declaration already in DBMS specific SQL code
316      *                                  as it is used in the CREATE TABLE statement.
317      *
318      *                             change
319      *
320      *                                 Associative array with the names of the fields to be changed as indexes
321      *                                  of the array. Keep in mind that if it is intended to change either the
322      *                                  name of a field and any other properties, the change array entries
323      *                                  should have the new names of the fields as array indexes.
324      *
325      *                                 The value of each entry of the array should be set to another associative
326      *                                  array with the properties of the fields to that are meant to be changed as
327      *                                  array entries. These entries should be assigned to the new values of the
328      *                                  respective properties. The properties of the fields should be the same
329      *                                  as defined by the Metabase parser.
330      *
331      *                             Example
332      *                                 array(
333      *                                     'name' => 'userlist',
334      *                                     'add' => array(
335      *                                         'quota' => array(
336      *                                             'type' => 'integer',
337      *                                             'unsigned' => 1
338      *                                         )
339      *                                     ),
340      *                                     'remove' => array(
341      *                                         'file_limit' => array(),
342      *                                         'time_limit' => array()
343      *                                         ),
344      *                                     'change' => array(
345      *                                         'gender' => array(
346      *                                             'default' => 'M',
347      *                                         )
348      *                                     ),
349      *                                     'rename' => array(
350      *                                         'sex' => array(
351      *                                             'name' => 'gender',
352      *                                         )
353      *                                     )
354      *                                 )
355      * @param boolean $check indicates whether the function should just check if the DBMS driver
356      *                              can perform the requested table alterations if the value is true or
357      *                              actually perform them otherwise.
358      * @return mixed MDB2_OK on success, a MDB2 error on failure
359      * @access public
360      */
361     function alterTable($name, $changes, $check)
362     {
363         $db =& $this->getDBInstance();
364         if (PEAR::isError($db)) {
365             return $db;
366         }
367
368         foreach ($changes as $change_name => $change) {
369             switch ($change_name) {
370             case 'add':
371             case 'remove':
372             case 'rename':
373                 break;
374             case 'change':
375                 foreach ($changes['change'] as $field) {
376                     if (PEAR::isError($err = $this->checkSupportedChanges($field))) {
377                         return $err;
378                     }
379                 }
380                 break;
381             default:
382                 return $db->raiseError(MDB2_ERROR, null, null,
383                     'alterTable: change type ' . $change_name . ' not yet supported');
384             }
385         }
386         if ($check) {
387             return MDB2_OK;
388         }
389         $query = '';
390         if (array_key_exists('add', $changes)) {
391             foreach ($changes['add'] as $field_name => $field) {
392                 $type_declaration = $db->getDeclaration($field['type'], $field_name, $field, $name);
393                 if (PEAR::isError($type_declaration)) {
394                     return $err;
395                 }
396                 if (strlen($query)) {
397                     $query.= ', ';
398                 }
399                 $query.= 'ADD ' . $type_declaration;
400             }
401         }
402
403         if (array_key_exists('remove', $changes)) {
404             foreach ($changes['remove'] as $field_name => $field) {
405                 if (strlen($query)) {
406                     $query.= ', ';
407                 }
408                 $query.= 'DROP ' . $field_name;
409             }
410         }
411
412         if (array_key_exists('rename', $changes)) {
413             foreach ($changes['rename'] as $field_name => $field) {
414                 if (strlen($query)) {
415                     $query.= ', ';
416                 }
417                 $query.= 'ALTER ' . $field_name . ' TO ' . $field['name'];
418             }
419         }
420
421         if (array_key_exists('change', $changes)) {
422             // missing support to change DEFAULT and NULLability
423             foreach ($changes['change'] as $field_name => $field) {
424                 if (PEAR::isError($err = $this->checkSupportedChanges($field))) {
425                     return $err;
426                 }
427                 if (strlen($query)) {
428                     $query.= ', ';
429                 }
430                 $db->loadModule('Datatype');
431                 $query.= 'ALTER ' . $field_name.' TYPE ' . $db->datatype->getTypeDeclaration($field);
432             }
433         }
434
435         if (!strlen($query)) {
436             return MDB2_OK;
437         }
438
439         return $db->query("ALTER TABLE $name $query");
440     }
441
442     // }}}
443     // {{{ listTables()
444
445     /**
446      * list all tables in the current database
447      *
448      * @return mixed data array on success, a MDB2 error on failure
449      * @access public
450      */
451     function listTables()
452     {
453         $db =& $this->getDBInstance();
454         if (PEAR::isError($db)) {
455             return $db;
456         }
457         $query = 'SELECT DISTINCT R.RDB$RELATION_NAME FROM RDB$RELATION_FIELDS R WHERE R.RDB$SYSTEM_FLAG=0';
458         $tables = $db->queryCol($query);
459         if (PEAR::isError($tables)) {
460             return $tables;
461         }
462         if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
463             $tables = array_flip(array_change_key_case(array_flip($tables), $db->options['field_case']));
464         }
465         return $tables;
466     }
467
468     // }}}
469     // {{{ listTableFields()
470
471     /**
472      * list all fields in a tables in the current database
473      *
474      * @param string $table name of table that should be used in method
475      * @return mixed data array on success, a MDB2 error on failure
476      * @access public
477      */
478     function listTableFields($table)
479     {
480         $db =& $this->getDBInstance();
481         if (PEAR::isError($db)) {
482             return $db;
483         }
484         $table = strtoupper($table);
485         $query = "SELECT RDB\$FIELD_NAME FROM RDB\$RELATION_FIELDS WHERE RDB\$RELATION_NAME='$table'";
486         $columns = $db->queryCol($query);
487         if (PEAR::isError($columns)) {
488             return $columns;
489         }
490         if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
491             $columns = array_flip(array_change_key_case(array_flip($columns), $db->options['field_case']));
492         }
493         return $columns;
494     }
495
496     // }}}
497     // {{{ listViews()
498
499     /**
500      * list the views in the database
501      *
502      * @return mixed MDB2_OK on success, a MDB2 error on failure
503      * @access public
504      */
505     function listViews()
506     {
507         $db =& $this->getDBInstance();
508         if (PEAR::isError($db)) {
509             return $db;
510         }
511
512         return $db->queryCol('SELECT RDB$VIEW_NAME');
513     }
514
515     // }}}
516     // {{{ createIndex()
517
518     /**
519      * get the stucture of a field into an array
520      *
521      * @param string    $table         name of the table on which the index is to be created
522      * @param string    $name         name of the index to be created
523      * @param array     $definition        associative array that defines properties of the index to be created.
524      *                                 Currently, only one property named FIELDS is supported. This property
525      *                                 is also an associative with the names of the index fields as array
526      *                                 indexes. Each entry of this array is set to another type of associative
527      *                                 array that specifies properties of the index that are specific to
528      *                                 each field.
529      *
530      *                                Currently, only the sorting property is supported. It should be used
531      *                                 to define the sorting direction of the index. It may be set to either
532      *                                 ascending or descending.
533      *
534      *                                Not all DBMS support index sorting direction configuration. The DBMS
535      *                                 drivers of those that do not support it ignore this property. Use the
536      *                                 function support() to determine whether the DBMS driver can manage indexes.
537
538      *                                 Example
539      *                                    array(
540      *                                        'fields' => array(
541      *                                            'user_name' => array(
542      *                                                'sorting' => 'ascending'
543      *                                            ),
544      *                                            'last_login' => array()
545      *                                        )
546      *                                    )
547      * @return mixed MDB2_OK on success, a MDB2 error on failure
548      * @access public
549      */
550     function createIndex($table, $name, $definition)
551     {
552         $db =& $this->getDBInstance();
553         if (PEAR::isError($db)) {
554             return $db;
555         }
556         if (array_key_exists('primary', $definition) && $definition['primary']) {
557             $query = "ALTER TABLE $table ADD CONSTRAINT $name PRIMARY KEY (";
558         } else {
559             $query = 'CREATE';
560             if (array_key_exists('unique', $definition) && $definition['unique']) {
561                 $query.= ' UNIQUE';
562             }
563             $query_sort = '';
564             foreach ($definition['fields'] as $field) {
565                 if (!strcmp($query_sort, '') && isset($field['sorting'])) {
566                     switch ($field['sorting']) {
567                     case 'ascending':
568                         $query_sort = ' ASC';
569                         break;
570                     case 'descending':
571                         $query_sort = ' DESC';
572                         break;
573                     }
574                 }
575             }
576             $query .= $query_sort. " INDEX $name ON $table (";
577         }
578         $query .= implode(', ', array_keys($definition['fields'])) . ')';
579
580         return $db->query($query);
581     }
582
583     // }}}
584     // {{{ listTableIndexes()
585
586     /**
587      * list all indexes in a table
588      *
589      * @param string $table name of table that should be used in method
590      * @return mixed data array on success, a MDB2 error on failure
591      * @access public
592      */
593     function listTableIndexes($table)
594     {
595         $db =& $this->getDBInstance();
596         if (PEAR::isError($db)) {
597             return $db;
598         }
599         return $db->queryCol("SELECT RDB\$INDEX_NAME FROM RDB\$INDICES WHERE RDB\$RELATION_NAME='$table'");
600     }
601
602     // }}}
603     // {{{ createSequence()
604
605     /**
606      * create sequence
607      *
608      * @param string $seq_name name of the sequence to be created
609      * @param string $start start value of the sequence; default is 1
610      * @return mixed MDB2_OK on success, a MDB2 error on failure
611      * @access public
612      */
613     function createSequence($seq_name, $start = 1)
614     {
615         $db =& $this->getDBInstance();
616         if (PEAR::isError($db)) {
617             return $db;
618         }
619
620         $sequence_name = $db->getSequenceName($seq_name);
621         if (PEAR::isError($result = $db->query('CREATE GENERATOR '.strtoupper($sequence_name)))) {
622             return $result;
623         }
624         if (PEAR::isError($result = $db->query('SET GENERATOR '.strtoupper($sequence_name).' TO '.($start-1)))) {
625             if (PEAR::isError($err = $db->dropSequence($seq_name))) {
626                 return $db->raiseError(MDB2_ERROR, null, null,
627                     'createSequence: Could not setup sequence start value and then it was not possible to drop it: '.
628                     $err->getMessage().' - ' .$err->getUserInfo());
629             }
630         }
631         return $result;
632     }
633
634     // }}}
635     // {{{ dropSequence()
636
637     /**
638      * drop existing sequence
639      *
640      * @param string $seq_name name of the sequence to be dropped
641      * @return mixed MDB2_OK on success, a MDB2 error on failure
642      * @access public
643      */
644     function dropSequence($seq_name)
645     {
646         $db =& $this->getDBInstance();
647         if (PEAR::isError($db)) {
648             return $db;
649         }
650
651         $sequence_name = $db->getSequenceName($seq_name);
652         return $db->query('DELETE FROM RDB$GENERATORS WHERE RDB$GENERATOR_NAME=\''.strtoupper($sequence_name).'\'');
653     }
654
655     // }}}
656     // {{{ listSequences()
657
658     /**
659      * list all sequences in the current database
660      *
661      * @return mixed data array on success, a MDB2 error on failure
662      * @access public
663      */
664     function listSequences()
665     {
666         $db =& $this->getDBInstance();
667         if (PEAR::isError($db)) {
668             return $db;
669         }
670
671         $query = 'SELECT RDB$GENERATOR_NAME FROM RDB$GENERATORS';
672         $table_names = $db->queryCol($query);
673         if (PEAR::isError($table_names)) {
674             return $table_names;
675         }
676         $sequences = array();
677         for ($i = 0, $j = count($table_names); $i < $j; ++$i) {
678             if ($sqn = $this->_isSequenceName($table_names[$i]))
679                 $sequences[] = $sqn;
680         }
681         return $sequences;
682     }
683 }
684 ?>