commiting uncommited changes on live site
[weblabels.fsf.org.git] / crm.fsf.org / 20131203 / files / sites / all / modules-new / civicrm / packages / DB / Table / Manager.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6 * Creates, checks or alters tables from DB_Table definitions.
7 *
8 * DB_Table_Manager provides database automated table creation
9 * facilities.
10 *
11 * PHP versions 4 and 5
12 *
13 * LICENSE:
14 *
15 * Copyright (c) 1997-2007, Paul M. Jones <pmjones@php.net>
16 * David C. Morse <morse@php.net>
17 * Mark Wiesemann <wiesemann@php.net>
18 * All rights reserved.
19 *
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
22 * are met:
23 *
24 * * Redistributions of source code must retain the above copyright
25 * notice, this list of conditions and the following disclaimer.
26 * * Redistributions in binary form must reproduce the above copyright
27 * notice, this list of conditions and the following disclaimer in the
28 * documentation and/or other materials provided with the distribution.
29 * * The names of the authors may not be used to endorse or promote products
30 * derived from this software without specific prior written permission.
31 *
32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
36 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
40 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43 *
44 * @category Database
45 * @package DB_Table
46 * @author Paul M. Jones <pmjones@php.net>
47 * @author David C. Morse <morse@php.net>
48 * @author Mark Wiesemann <wiesemann@php.net>
49 * @license http://opensource.org/licenses/bsd-license.php New BSD License
50 * @version CVS: $Id: Manager.php,v 1.40 2008/12/25 19:56:35 wiesemann Exp $
51 * @link http://pear.php.net/package/DB_Table
52 */
53
54 require_once 'DB/Table.php';
55
56
57 /**
58 * Valid types for the different data types in the different DBMS.
59 */
60 $GLOBALS['_DB_TABLE']['valid_type'] = array(
61 'fbsql' => array( // currently not supported
62 'boolean' => '',
63 'char' => '',
64 'varchar' => '',
65 'smallint' => '',
66 'integer' => '',
67 'bigint' => '',
68 'decimal' => '',
69 'single' => '',
70 'double' => '',
71 'clob' => '',
72 'date' => '',
73 'time' => '',
74 'timestamp' => ''
75 ),
76 'ibase' => array(
77 'boolean' => array('char', 'integer', 'real', 'smallint'),
78 'char' => array('char', 'varchar'),
79 'varchar' => 'varchar',
80 'smallint' => array('integer', 'smallint'),
81 'integer' => 'integer',
82 'bigint' => array('bigint', 'integer'),
83 'decimal' => 'numeric',
84 'single' => array('double precision', 'float'),
85 'double' => 'double precision',
86 'clob' => 'blob',
87 'date' => 'date',
88 'time' => 'time',
89 'timestamp' => 'timestamp'
90 ),
91 'mssql' => array( // currently not supported
92 'boolean' => '',
93 'char' => '',
94 'varchar' => '',
95 'smallint' => '',
96 'integer' => '',
97 'bigint' => '',
98 'decimal' => '',
99 'single' => '',
100 'double' => '',
101 'clob' => '',
102 'date' => '',
103 'time' => '',
104 'timestamp' => ''
105 ),
106 'mysql' => array(
107 'boolean' => array('char', 'decimal', 'int', 'real', 'tinyint'),
108 'char' => array('char', 'string', 'varchar'),
109 'varchar' => array('char', 'string', 'varchar'),
110 'smallint' => array('smallint', 'int'),
111 'integer' => 'int',
112 'bigint' => array('int', 'bigint'),
113 'decimal' => array('decimal', 'real'),
114 'single' => array('double', 'real'),
115 'double' => array('double', 'real'),
116 'clob' => array('blob', 'longtext', 'tinytext', 'text', 'mediumtext'),
117 'date' => array('char', 'date', 'string'),
118 'time' => array('char', 'string', 'time'),
119 'timestamp' => array('char', 'datetime', 'string')
120 ),
121 'mysqli' => array(
122 'boolean' => array('char', 'decimal', 'tinyint'),
123 'char' => array('char', 'varchar'),
124 'varchar' => array('char', 'varchar'),
125 'smallint' => array('smallint', 'int'),
126 'integer' => 'int',
127 'bigint' => array('int', 'bigint'),
128 'decimal' => 'decimal',
129 'single' => array('double', 'float'),
130 'double' => 'double',
131 'clob' => array('blob', 'longtext', 'tinytext', 'text', 'mediumtext'),
132 'date' => array('char', 'date', 'varchar'),
133 'time' => array('char', 'time', 'varchar'),
134 'timestamp' => array('char', 'datetime', 'varchar')
135 ),
136 'oci8' => array(
137 'boolean' => 'number',
138 'char' => array('char', 'varchar2'),
139 'varchar' => 'varchar2',
140 'smallint' => 'number',
141 'integer' => 'number',
142 'bigint' => 'number',
143 'decimal' => 'number',
144 'single' => array('float', 'number'),
145 'double' => array('float', 'number'),
146 'clob' => 'clob',
147 'date' => array('char', 'date'),
148 'time' => array('char', 'date'),
149 'timestamp' => array('char', 'date')
150 ),
151 'pgsql' => array(
152 'boolean' => array('bool', 'numeric'),
153 'char' => array('bpchar', 'varchar'),
154 'varchar' => 'varchar',
155 'smallint' => array('int2', 'int4'),
156 'integer' => 'int4',
157 'bigint' => array('int4', 'int8'),
158 'decimal' => 'numeric',
159 'single' => array('float4', 'float8'),
160 'double' => 'float8',
161 'clob' => array('oid', 'text'),
162 'date' => array('bpchar', 'date'),
163 'time' => array('bpchar', 'time'),
164 'timestamp' => array('bpchar', 'timestamp')
165 ),
166 'sqlite' => array(
167 'boolean' => 'boolean',
168 'char' => 'char',
169 'varchar' => array('char', 'varchar'),
170 'smallint' => array('int', 'smallint'),
171 'integer' => array('int', 'integer'),
172 'bigint' => array('int', 'bigint'),
173 'decimal' => array('decimal', 'numeric'),
174 'single' => array('double', 'float'),
175 'double' => 'double',
176 'clob' => array('clob', 'longtext'),
177 'date' => 'date',
178 'time' => 'time',
179 'timestamp' => array('datetime', 'timestamp')
180 ),
181 );
182
183 /**
184 * Mapping between DB_Table and MDB2 data types.
185 */
186 $GLOBALS['_DB_TABLE']['mdb2_type'] = array(
187 'boolean' => 'boolean',
188 'char' => 'text',
189 'varchar' => 'text',
190 'smallint' => 'integer',
191 'integer' => 'integer',
192 'bigint' => 'integer',
193 'decimal' => 'decimal',
194 'single' => 'float',
195 'double' => 'float',
196 'clob' => 'clob',
197 'date' => 'date',
198 'time' => 'time',
199 'timestamp' => 'timestamp'
200 );
201
202 /**
203 * Creates, checks or alters tables from DB_Table definitions.
204 *
205 * DB_Table_Manager provides database automated table creation
206 * facilities.
207 *
208 * @category Database
209 * @package DB_Table
210 * @author Paul M. Jones <pmjones@php.net>
211 * @author David C. Morse <morse@php.net>
212 * @author Mark Wiesemann <wiesemann@php.net>
213 * @version Release: 1.5.6
214 * @link http://pear.php.net/package/DB_Table
215 */
216 class DB_Table_Manager {
217
218
219 /**
220 *
221 * Create the table based on DB_Table column and index arrays.
222 *
223 * @static
224 *
225 * @access public
226 *
227 * @param object &$db A PEAR DB/MDB2 object.
228 *
229 * @param string $table The table name to connect to in the database.
230 *
231 * @param mixed $column_set A DB_Table $this->col array.
232 *
233 * @param mixed $index_set A DB_Table $this->idx array.
234 *
235 * @return mixed Boolean false if there was no attempt to create the
236 * table, boolean true if the attempt succeeded, and a PEAR_Error if
237 * the attempt failed.
238 *
239 */
240
241 function create(&$db, $table, $column_set, $index_set)
242 {
243 if (is_subclass_of($db, 'db_common')) {
244 $backend = 'db';
245 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
246 $backend = 'mdb2';
247 $db->loadModule('Manager');
248 }
249 $phptype = $db->phptype;
250
251 // columns to be created
252 $column = array();
253
254 // max. value for scope (only used with MDB2 as backend)
255 $max_scope = 0;
256
257 // indexes to be created
258 $indexes = array();
259
260 // check the table name
261 $name_check = DB_Table_Manager::_validateTableName($table);
262 if (PEAR::isError($name_check)) {
263 return $name_check;
264 }
265
266
267 // -------------------------------------------------------------
268 //
269 // validate each column mapping and build the individual
270 // definitions, and note column indexes as we go.
271 //
272
273 if (is_null($column_set)) {
274 $column_set = array();
275 }
276
277 foreach ($column_set as $colname => $val) {
278
279 $colname = trim($colname);
280
281 // check the column name
282 $name_check = DB_Table_Manager::_validateColumnName($colname);
283 if (PEAR::isError($name_check)) {
284 return $name_check;
285 }
286
287
288 // prepare variables
289 $type = (isset($val['type'])) ? $val['type'] : null;
290 $size = (isset($val['size'])) ? $val['size'] : null;
291 $scope = (isset($val['scope'])) ? $val['scope'] : null;
292 $require = (isset($val['require'])) ? $val['require'] : null;
293 $default = (isset($val['default'])) ? $val['default'] : null;
294
295 if ($backend == 'mdb2') {
296
297 // get the declaration string
298 $result = DB_Table_Manager::getDeclareMDB2($type,
299 $size, $scope, $require, $default, $max_scope);
300
301 // did it work?
302 if (PEAR::isError($result)) {
303 $result->userinfo .= " ('$colname')";
304 return $result;
305 }
306
307 // add the declaration to the array of all columns
308 $column[$colname] = $result;
309
310 } else {
311
312 // get the declaration string
313 $result = DB_Table_Manager::getDeclare($phptype, $type,
314 $size, $scope, $require, $default);
315
316 // did it work?
317 if (PEAR::isError($result)) {
318 $result->userinfo .= " ('$colname')";
319 return $result;
320 }
321
322 // add the declaration to the array of all columns
323 $column[] = "$colname $result";
324
325 }
326
327 }
328
329
330 // -------------------------------------------------------------
331 //
332 // validate the indexes.
333 //
334
335 if (is_null($index_set)) {
336 $index_set = array();
337 }
338
339 $count_primary_keys = 0;
340
341 foreach ($index_set as $idxname => $val) {
342
343 list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
344
345 $newIdxName = '';
346
347 // check the index definition
348 $index_check = DB_Table_Manager::_validateIndexName($idxname,
349 $table, $phptype, $type, $cols, $column_set, $newIdxName);
350 if (PEAR::isError($index_check)) {
351 return $index_check;
352 }
353
354 // check number of primary keys (only one is allowed)
355 if ($type == 'primary') {
356 // SQLite does not support primary keys
357 if ($phptype == 'sqlite') {
358 return DB_Table::throwError(DB_TABLE_ERR_DECLARE_PRIM_SQLITE);
359 }
360 $count_primary_keys++;
361 }
362 if ($count_primary_keys > 1) {
363 return DB_Table::throwError(DB_TABLE_ERR_DECLARE_PRIMARY);
364 }
365
366 // create index entry
367 if ($backend == 'mdb2') {
368
369 // array with column names as keys
370 $idx_cols = array();
371 foreach ($cols as $col) {
372 $idx_cols[$col] = array();
373 }
374
375 switch ($type) {
376 case 'primary':
377 $indexes['primary'][$newIdxName] =
378 array('fields' => $idx_cols,
379 'primary' => true);
380 break;
381 case 'unique':
382 $indexes['unique'][$newIdxName] =
383 array('fields' => $idx_cols,
384 'unique' => true);
385 break;
386 case 'normal':
387 $indexes['normal'][$newIdxName] =
388 array('fields' => $idx_cols);
389 break;
390 }
391
392 } else {
393
394 $indexes[] = DB_Table_Manager::getDeclareForIndex($phptype,
395 $type, $newIdxName, $table, $cols);
396
397 }
398
399 }
400
401
402 // -------------------------------------------------------------
403 //
404 // now for the real action: create the table and indexes!
405 //
406 if ($backend == 'mdb2') {
407
408 // save user defined 'decimal_places' option
409 $decimal_places = $db->getOption('decimal_places');
410 $db->setOption('decimal_places', $max_scope);
411
412 // attempt to create the table
413 $result = $db->manager->createTable($table, $column);
414 // restore user defined 'decimal_places' option
415 $db->setOption('decimal_places', $decimal_places);
416 if (PEAR::isError($result)) {
417 return $result;
418 }
419
420 } else {
421
422 // build the CREATE TABLE command
423 $cmd = "CREATE TABLE $table (\n\t";
424 $cmd .= implode(",\n\t", $column);
425 $cmd .= "\n)";
426
427 // attempt to create the table
428 $result = $db->query($cmd);
429 if (PEAR::isError($result)) {
430 return $result;
431 }
432
433 }
434
435 $result = DB_Table_Manager::_createIndexesAndContraints($db, $backend,
436 $table, $indexes);
437 if (PEAR::isError($result)) {
438 return $result;
439 }
440
441 // we're done!
442 return true;
443 }
444
445
446 /**
447 *
448 * Verify whether the table and columns exist, whether the columns
449 * have the right type and whether the indexes exist.
450 *
451 * @static
452 *
453 * @access public
454 *
455 * @param object &$db A PEAR DB/MDB2 object.
456 *
457 * @param string $table The table name to connect to in the database.
458 *
459 * @param mixed $column_set A DB_Table $this->col array.
460 *
461 * @param mixed $index_set A DB_Table $this->idx array.
462 *
463 * @return mixed Boolean true if the verification was successful, and a
464 * PEAR_Error if verification failed.
465 *
466 */
467
468 function verify(&$db, $table, $column_set, $index_set)
469 {
470 if (is_subclass_of($db, 'db_common')) {
471 $backend = 'db';
472 $reverse =& $db;
473 $table_info_mode = DB_TABLEINFO_FULL;
474 $table_info_error = DB_ERROR_NEED_MORE_DATA;
475 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
476 $backend = 'mdb2';
477 $reverse =& $this->db->loadModule('Reverse');
478 $table_info_mode = MDB2_TABLEINFO_FULL;
479 $table_info_error = MDB2_ERROR_NEED_MORE_DATA;
480 }
481 $phptype = $db->phptype;
482
483 // check #1: does the table exist?
484
485 // check the table name
486 $name_check = DB_Table_Manager::_validateTableName($table);
487 if (PEAR::isError($name_check)) {
488 return $name_check;
489 }
490
491 // get table info
492 $tableInfo = $reverse->tableInfo($table, $table_info_mode);
493 if (PEAR::isError($tableInfo)) {
494 if ($tableInfo->getCode() == $table_info_error) {
495 return DB_Table::throwError(
496 DB_TABLE_ERR_VER_TABLE_MISSING,
497 "(table='$table')"
498 );
499 }
500 return $tableInfo;
501 }
502 $tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER);
503
504 if (is_null($column_set)) {
505 $column_set = array();
506 }
507
508 foreach ($column_set as $colname => $val) {
509 $colname = strtolower(trim($colname));
510
511 // check the column name
512 $name_check = DB_Table_Manager::_validateColumnName($colname);
513 if (PEAR::isError($name_check)) {
514 return $name_check;
515 }
516
517 // check #2: do all columns exist?
518 $column_exists = DB_Table_Manager::_columnExists($colname,
519 $tableInfoOrder, 'verify');
520 if (PEAR::isError($column_exists)) {
521 return $column_exists;
522 }
523
524 // check #3: do all columns have the right type?
525
526 // check whether the column type is a known type
527 $type_check = DB_Table_Manager::_validateColumnType($phptype, $val['type']);
528 if (PEAR::isError($type_check)) {
529 return $type_check;
530 }
531
532 // check whether the column has the right type
533 $type_check = DB_Table_Manager::_checkColumnType($phptype,
534 $colname, $val['type'], $tableInfoOrder, $tableInfo, 'verify');
535 if (PEAR::isError($type_check)) {
536 return $type_check;
537 }
538
539 }
540
541 // check #4: do all indexes exist?
542 $table_indexes = DB_Table_Manager::getIndexes($db, $table);
543 if (PEAR::isError($table_indexes)) {
544 return $table_indexes;
545 }
546
547 if (is_null($index_set)) {
548 $index_set = array();
549 }
550
551 foreach ($index_set as $idxname => $val) {
552
553 list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
554
555 $newIdxName = '';
556
557 // check the index definition
558 $index_check = DB_Table_Manager::_validateIndexName($idxname,
559 $table, $phptype, $type, $cols, $column_set, $newIdxName);
560 if (PEAR::isError($index_check)) {
561 return $index_check;
562 }
563
564 // check whether the index has the right type and has all
565 // specified columns
566 $index_check = DB_Table_Manager::_checkIndex($idxname, $newIdxName,
567 $type, $cols, $table_indexes, 'verify');
568 if (PEAR::isError($index_check)) {
569 return $index_check;
570 }
571
572 }
573
574 return true;
575 }
576
577
578 /**
579 *
580 * Alter columns and indexes of a table based on DB_Table column and index
581 * arrays.
582 *
583 * @static
584 *
585 * @access public
586 *
587 * @param object &$db A PEAR DB/MDB2 object.
588 *
589 * @param string $table The table name to connect to in the database.
590 *
591 * @param mixed $column_set A DB_Table $this->col array.
592 *
593 * @param mixed $index_set A DB_Table $this->idx array.
594 *
595 * @return bool|object True if altering was successful or a PEAR_Error on
596 * failure.
597 *
598 */
599
600 function alter(&$db, $table, $column_set, $index_set)
601 {
602 $phptype = $db->phptype;
603
604 if (is_subclass_of($db, 'db_common')) {
605 $backend = 'db';
606 $reverse =& $db;
607 // workaround for missing index and constraint information methods
608 // in PEAR::DB ==> use adopted code from MDB2's driver classes
609 require_once 'DB/Table/Manager/' . $phptype . '.php';
610 $classname = 'DB_Table_Manager_' . $phptype;
611 $dbtm = new $classname();
612 $dbtm->_db =& $db; // pass database instance to the 'workaround' class
613 $manager =& $dbtm;
614 $table_info_mode = DB_TABLEINFO_FULL;
615 $ok_const = DB_OK;
616 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
617 $backend = 'mdb2';
618 $db->loadModule('Reverse');
619 $manager =& $db->manager;
620 $reverse =& $db->reverse;
621 $table_info_mode = MDB2_TABLEINFO_FULL;
622 $ok_const = MDB2_OK;
623 }
624
625 // get table info
626 $tableInfo = $reverse->tableInfo($table, $table_info_mode);
627 if (PEAR::isError($tableInfo)) {
628 return $tableInfo;
629 }
630 $tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER);
631
632 // emulate MDB2 Reverse extension for PEAR::DB as backend
633 if (is_subclass_of($db, 'db_common')) {
634 $reverse =& $dbtm;
635 }
636
637 // check (and alter) columns
638 if (is_null($column_set)) {
639 $column_set = array();
640 }
641
642 foreach ($column_set as $colname => $val) {
643 $colname = strtolower(trim($colname));
644
645 // check the column name
646 $name_check = DB_Table_Manager::_validateColumnName($colname);
647 if (PEAR::isError($name_check)) {
648 return $name_check;
649 }
650
651 // check the column's existence
652 $column_exists = DB_Table_Manager::_columnExists($colname,
653 $tableInfoOrder, 'alter');
654 if (PEAR::isError($column_exists)) {
655 return $column_exists;
656 }
657 if ($column_exists === false) { // add the column
658 $definition = DB_Table_Manager::_getColumnDefinition($backend,
659 $phptype, $val);
660 if (PEAR::isError($definition)) {
661 return $definition;
662 }
663 $changes = array('add' => array($colname => $definition));
664 if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
665 echo "(alter) New table field will be added ($colname):\n";
666 var_dump($changes);
667 echo "\n";
668 }
669 $result = $manager->alterTable($table, $changes, false);
670 if (PEAR::isError($result)) {
671 return $result;
672 }
673 continue;
674 }
675
676 // check whether the column type is a known type
677 $type_check = DB_Table_Manager::_validateColumnType($phptype, $val['type']);
678 if (PEAR::isError($type_check)) {
679 return $type_check;
680 }
681
682 // check whether the column has the right type
683 $type_check = DB_Table_Manager::_checkColumnType($phptype,
684 $colname, $val['type'], $tableInfoOrder, $tableInfo, 'alter');
685 if (PEAR::isError($type_check)) {
686 return $type_check;
687 }
688 if ($type_check === false) { // change the column type
689 $definition = DB_Table_Manager::_getColumnDefinition($backend,
690 $phptype, $val);
691 if (PEAR::isError($definition)) {
692 return $definition;
693 }
694 $changes = array('change' =>
695 array($colname => array('type' => null,
696 'definition' => $definition)));
697 if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
698 echo "(alter) Table field's type will be changed ($colname):\n";
699 var_dump($changes);
700 echo "\n";
701 }
702 $result = $manager->alterTable($table, $changes, false);
703 if (PEAR::isError($result)) {
704 return $result;
705 }
706 continue;
707 }
708
709 }
710
711 // get information about indexes / constraints
712 $table_indexes = DB_Table_Manager::getIndexes($db, $table);
713 if (PEAR::isError($table_indexes)) {
714 return $table_indexes;
715 }
716
717 // check (and alter) indexes / constraints
718 if (is_null($index_set)) {
719 $index_set = array();
720 }
721
722 foreach ($index_set as $idxname => $val) {
723
724 list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
725
726 $newIdxName = '';
727
728 // check the index definition
729 $index_check = DB_Table_Manager::_validateIndexName($idxname,
730 $table, $phptype, $type, $cols, $column_set, $newIdxName);
731 if (PEAR::isError($index_check)) {
732 return $index_check;
733 }
734
735 // check whether the index has the right type and has all
736 // specified columns
737 $index_check = DB_Table_Manager::_checkIndex($idxname, $newIdxName,
738 $type, $cols, $table_indexes, 'alter');
739 if (PEAR::isError($index_check)) {
740 return $index_check;
741 }
742 if ($index_check === false) { // (1) drop wrong index/constraint
743 // (2) add right index/constraint
744 if ($backend == 'mdb2') {
745 // save user defined 'idxname_format' option
746 $idxname_format = $db->getOption('idxname_format');
747 $db->setOption('idxname_format', '%s');
748 }
749 // drop index/constraint only if it exists
750 foreach (array('normal', 'unique', 'primary') as $idx_type) {
751 if (array_key_exists(strtolower($newIdxName),
752 $table_indexes[$idx_type])) {
753 if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
754 echo "(alter) Index/constraint will be deleted (name: '$newIdxName', type: '$idx_type').\n";
755 }
756 if ($idx_type == 'normal') {
757 $result = $manager->dropIndex($table, $newIdxName);
758 } else {
759 $result = $manager->dropConstraint($table, $newIdxName);
760 }
761 if (PEAR::isError($result)) {
762 if ($backend == 'mdb2') {
763 // restore user defined 'idxname_format' option
764 $db->setOption('idxname_format', $idxname_format);
765 }
766 return $result;
767 }
768 break;
769 }
770 }
771
772 // prepare index/constraint definition
773 $indexes = array();
774 if ($backend == 'mdb2') {
775
776 // array with column names as keys
777 $idx_cols = array();
778 foreach ($cols as $col) {
779 $idx_cols[$col] = array();
780 }
781
782 switch ($type) {
783 case 'primary':
784 $indexes['primary'][$newIdxName] =
785 array('fields' => $idx_cols,
786 'primary' => true);
787 break;
788 case 'unique':
789 $indexes['unique'][$newIdxName] =
790 array('fields' => $idx_cols,
791 'unique' => true);
792 break;
793 case 'normal':
794 $indexes['normal'][$newIdxName] =
795 array('fields' => $idx_cols);
796 break;
797 }
798
799 } else {
800
801 $indexes[] = DB_Table_Manager::getDeclareForIndex($phptype,
802 $type, $newIdxName, $table, $cols);
803
804 }
805
806 // create index/constraint
807 if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
808 echo "(alter) New index/constraint will be created (name: '$newIdxName', type: '$type'):\n";
809 var_dump($indexes);
810 echo "\n";
811 }
812 $result = DB_Table_Manager::_createIndexesAndContraints(
813 $db, $backend, $table, $indexes);
814 if ($backend == 'mdb2') {
815 // restore user defined 'idxname_format' option
816 $db->setOption('idxname_format', $idxname_format);
817 }
818 if (PEAR::isError($result)) {
819 return $result;
820 }
821
822 continue;
823 }
824
825 }
826
827 return true;
828 }
829
830
831 /**
832 *
833 * Check whether a table exists.
834 *
835 * @static
836 *
837 * @access public
838 *
839 * @param object &$db A PEAR DB/MDB2 object.
840 *
841 * @param string $table The table name that should be checked.
842 *
843 * @return bool|object True if the table exists, false if not, or a
844 * PEAR_Error on failure.
845 *
846 */
847
848 function tableExists(&$db, $table)
849 {
850 if (is_subclass_of($db, 'db_common')) {
851 $list = $db->getListOf('tables');
852 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
853 $db->loadModule('Manager');
854 $list = $db->manager->listTables();
855 }
856 if (PEAR::isError($list)) {
857 return $list;
858 }
859 array_walk($list, create_function('&$value,$key',
860 '$value = trim(strtolower($value));'));
861 return in_array(strtolower($table), $list);
862 }
863
864
865 /**
866 *
867 * Get the column declaration string for a DB_Table column.
868 *
869 * @static
870 *
871 * @access public
872 *
873 * @param string $phptype The DB/MDB2 phptype key.
874 *
875 * @param string $coltype The DB_Table column type.
876 *
877 * @param int $size The size for the column (needed for string and
878 * decimal).
879 *
880 * @param int $scope The scope for the column (needed for decimal).
881 *
882 * @param bool $require True if the column should be NOT NULL, false
883 * allowed to be NULL.
884 *
885 * @param string $default The SQL calculation for a default value.
886 *
887 * @return string|object A declaration string on success, or a
888 * PEAR_Error on failure.
889 *
890 */
891
892 function getDeclare($phptype, $coltype, $size = null, $scope = null,
893 $require = null, $default = null)
894 {
895 // validate char/varchar/decimal type declaration
896 $validation = DB_Table_Manager::_validateTypeDeclaration($coltype, $size,
897 $scope);
898 if (PEAR::isError($validation)) {
899 return $validation;
900 }
901
902 // map of column types and declarations for this RDBMS
903 $map = $GLOBALS['_DB_TABLE']['type'][$phptype];
904
905 // is it a recognized column type?
906 $types = array_keys($map);
907 if (! in_array($coltype, $types)) {
908 return DB_Table::throwError(
909 DB_TABLE_ERR_DECLARE_TYPE,
910 "('$coltype')"
911 );
912 }
913
914 // basic declaration
915 switch ($coltype) {
916
917 case 'char':
918 case 'varchar':
919 $declare = $map[$coltype] . "($size)";
920 break;
921
922 case 'decimal':
923 $declare = $map[$coltype] . "($size,$scope)";
924 break;
925
926 default:
927 $declare = $map[$coltype];
928 break;
929
930 }
931
932 // set the "NULL"/"NOT NULL" portion
933 $null = ' NULL';
934 if ($phptype == 'ibase') { // Firebird does not like 'NULL'
935 $null = ''; // in CREATE TABLE
936 }
937 if ($phptype == 'pgsql') { // PostgreSQL does not like 'NULL'
938 $null = ''; // in ALTER TABLE
939 }
940 $declare .= ($require) ? ' NOT NULL' : $null;
941
942 // set the "DEFAULT" portion
943 if ($default) {
944 switch ($coltype) {
945 case 'char':
946 case 'varchar':
947 case 'clob':
948 $declare .= " DEFAULT '$default'";
949 break;
950
951 default:
952 $declare .= " DEFAULT $default";
953 break;
954 }
955 }
956
957 // done
958 return $declare;
959 }
960
961
962 /**
963 *
964 * Get the column declaration string for a DB_Table column.
965 *
966 * @static
967 *
968 * @access public
969 *
970 * @param string $coltype The DB_Table column type.
971 *
972 * @param int $size The size for the column (needed for string and
973 * decimal).
974 *
975 * @param int $scope The scope for the column (needed for decimal).
976 *
977 * @param bool $require True if the column should be NOT NULL, false
978 * allowed to be NULL.
979 *
980 * @param string $default The SQL calculation for a default value.
981 *
982 * @param int $max_scope The maximal scope for all table column
983 * (pass-by-reference).
984 *
985 * @return string|object A MDB2 column definition array on success, or a
986 * PEAR_Error on failure.
987 *
988 */
989
990 function getDeclareMDB2($coltype, $size = null, $scope = null,
991 $require = null, $default = null, &$max_scope)
992 {
993 // validate char/varchar/decimal type declaration
994 $validation = DB_Table_Manager::_validateTypeDeclaration($coltype, $size,
995 $scope);
996 if (PEAR::isError($validation)) {
997 return $validation;
998 }
999
1000 // map of MDB2 column types
1001 $map = $GLOBALS['_DB_TABLE']['mdb2_type'];
1002
1003 // is it a recognized column type?
1004 $types = array_keys($map);
1005 if (! in_array($coltype, $types)) {
1006 return DB_Table::throwError(
1007 DB_TABLE_ERR_DECLARE_TYPE,
1008 "('$coltype')"
1009 );
1010 }
1011
1012 // build declaration array
1013 $new_column = array(
1014 'type' => $map[$coltype],
1015 'notnull' => $require
1016 );
1017
1018 if ($size) {
1019 $new_column['length'] = $size;
1020 }
1021
1022 // determine integer length to be used in MDB2
1023 if (in_array($coltype, array('smallint', 'integer', 'bigint'))) {
1024 switch ($coltype) {
1025 case 'smallint':
1026 $new_column['length'] = 2;
1027 break;
1028 case 'integer':
1029 $new_column['length'] = 4;
1030 break;
1031 case 'bigint':
1032 $new_column['length'] = 5;
1033 break;
1034 }
1035 }
1036
1037 if ($scope) {
1038 $max_scope = max($max_scope, $scope);
1039 }
1040
1041 if ($default) {
1042 $new_column['default'] = $default;
1043 }
1044
1045 return $new_column;
1046 }
1047
1048
1049 /**
1050 *
1051 * Get the index declaration string for a DB_Table index.
1052 *
1053 * @static
1054 *
1055 * @access public
1056 *
1057 * @param string $phptype The DB phptype key.
1058 *
1059 * @param string $type The index type.
1060 *
1061 * @param string $idxname The index name.
1062 *
1063 * @param string $table The table name.
1064 *
1065 * @param mixed $cols Array with the column names for the index.
1066 *
1067 * @return string A declaration string.
1068 *
1069 */
1070
1071 function getDeclareForIndex($phptype, $type, $idxname, $table, $cols)
1072 {
1073 // string of column names
1074 $colstring = implode(', ', $cols);
1075
1076 switch ($type) {
1077
1078 case 'primary':
1079 switch ($phptype) {
1080 case 'ibase':
1081 case 'oci8':
1082 case 'pgsql':
1083 $declare = "ALTER TABLE $table ADD";
1084 $declare .= " CONSTRAINT $idxname";
1085 $declare .= " PRIMARY KEY ($colstring)";
1086 break;
1087 case 'mysql':
1088 case 'mysqli':
1089 $declare = "ALTER TABLE $table ADD PRIMARY KEY";
1090 $declare .= " ($colstring)";
1091 break;
1092 case 'sqlite':
1093 // currently not possible
1094 break;
1095 }
1096 break;
1097
1098 case 'unique':
1099 $declare = "CREATE UNIQUE INDEX $idxname ON $table ($colstring)";
1100 break;
1101
1102 case 'normal':
1103 $declare = "CREATE INDEX $idxname ON $table ($colstring)";
1104 break;
1105
1106 }
1107
1108 return $declare;
1109 }
1110
1111
1112 /**
1113 *
1114 * Return the definition array for a column.
1115 *
1116 * @access private
1117 *
1118 * @param string $backend The name of the backend ('db' or 'mdb2').
1119 *
1120 * @param string $phptype The DB/MDB2 phptype key.
1121 *
1122 * @param mixed $column A single DB_Table column definition array.
1123 *
1124 * @return mixed|object Declaration string (DB), declaration array (MDB2) or a
1125 * PEAR_Error with a description about the invalidity, otherwise.
1126 *
1127 */
1128
1129 function _getColumnDefinition($backend, $phptype, $column)
1130 {
1131 static $max_scope;
1132
1133 // prepare variables
1134 $type = (isset($column['type'])) ? $column['type'] : null;
1135 $size = (isset($column['size'])) ? $column['size'] : null;
1136 $scope = (isset($column['scope'])) ? $column['scope'] : null;
1137 $require = (isset($column['require'])) ? $column['require'] : null;
1138 $default = (isset($column['default'])) ? $column['default'] : null;
1139
1140 if ($backend == 'db') {
1141 return DB_Table_Manager::getDeclare($phptype, $type,
1142 $size, $scope, $require, $default);
1143 } else {
1144 return DB_Table_Manager::getDeclareMDB2($type,
1145 $size, $scope, $require, $default, $max_scope);
1146 }
1147 }
1148
1149
1150 /**
1151 *
1152 * Check char/varchar/decimal type declarations for validity.
1153 *
1154 * @access private
1155 *
1156 * @param string $coltype The DB_Table column type.
1157 *
1158 * @param int $size The size for the column (needed for string and
1159 * decimal).
1160 *
1161 * @param int $scope The scope for the column (needed for decimal).
1162 *
1163 * @return bool|object Boolean true if the type declaration is valid or a
1164 * PEAR_Error with a description about the invalidity, otherwise.
1165 *
1166 */
1167
1168 function _validateTypeDeclaration($coltype, $size, $scope)
1169 {
1170 // validate char and varchar: does it have a size?
1171 if (($coltype == 'char' || $coltype == 'varchar') &&
1172 ($size < 1 || $size > 255) ) {
1173 return DB_Table::throwError(
1174 DB_TABLE_ERR_DECLARE_STRING,
1175 "(size='$size')"
1176 );
1177 }
1178
1179 // validate decimal: does it have a size and scope?
1180 if ($coltype == 'decimal' &&
1181 ($size < 1 || $size > 255 || $scope < 0 || $scope > $size)) {
1182 return DB_Table::throwError(
1183 DB_TABLE_ERR_DECLARE_DECIMAL,
1184 "(size='$size' scope='$scope')"
1185 );
1186 }
1187
1188 return true;
1189 }
1190
1191
1192 /**
1193 *
1194 * Check a table name for validity.
1195 *
1196 * @access private
1197 *
1198 * @param string $tablename The table name.
1199 *
1200 * @return bool|object Boolean true if the table name is valid or a
1201 * PEAR_Error with a description about the invalidity, otherwise.
1202 *
1203 */
1204
1205 function _validateTableName($tablename)
1206 {
1207 // is the table name too long?
1208 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1209 && strlen($tablename) > 30
1210 ) {
1211 return DB_Table::throwError(
1212 DB_TABLE_ERR_TABLE_STRLEN,
1213 " ('$tablename')"
1214 );
1215 }
1216
1217 return true;
1218 }
1219
1220
1221 /**
1222 *
1223 * Check a column name for validity.
1224 *
1225 * @access private
1226 *
1227 * @param string $colname The column name.
1228 *
1229 * @return bool|object Boolean true if the column name is valid or a
1230 * PEAR_Error with a description about the invalidity, otherwise.
1231 *
1232 */
1233
1234 function _validateColumnName($colname)
1235 {
1236 // column name cannot be a reserved keyword
1237 $reserved = in_array(
1238 strtoupper($colname),
1239 $GLOBALS['_DB_TABLE']['reserved']
1240 );
1241
1242 if ($reserved) {
1243 return DB_Table::throwError(
1244 DB_TABLE_ERR_DECLARE_COLNAME,
1245 " ('$colname')"
1246 );
1247 }
1248
1249 // column name must be no longer than 30 chars
1250 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1251 && strlen($colname) > 30
1252 ) {
1253 return DB_Table::throwError(
1254 DB_TABLE_ERR_DECLARE_STRLEN,
1255 "('$colname')"
1256 );
1257 }
1258
1259 return true;
1260 }
1261
1262
1263 /**
1264 *
1265 * Check whether a column exists.
1266 *
1267 * @access private
1268 *
1269 * @param string $colname The column name.
1270 *
1271 * @param mixed $tableInfoOrder Array with columns in the table (result
1272 * from tableInfo(), shortened to key 'order').
1273 *
1274 * @param string $mode The name of the calling function, this can be either
1275 * 'verify' or 'alter'.
1276 *
1277 * @return bool|object Boolean true if the column exists.
1278 * Otherwise, either boolean false (case 'alter') or a PEAR_Error
1279 * (case 'verify').
1280 *
1281 */
1282
1283 function _columnExists($colname, $tableInfoOrder, $mode)
1284 {
1285 if (array_key_exists($colname, $tableInfoOrder)) {
1286 return true;
1287 }
1288
1289 switch ($mode) {
1290
1291 case 'alter':
1292 return false;
1293
1294 case 'verify':
1295 return DB_Table::throwError(
1296 DB_TABLE_ERR_VER_COLUMN_MISSING,
1297 "(column='$colname')"
1298 );
1299
1300 }
1301 }
1302
1303
1304 /**
1305 *
1306 * Check whether a column type is a known type.
1307 *
1308 * @access private
1309 *
1310 * @param string $phptype The DB/MDB2 phptype key.
1311 *
1312 * @param string $type The column type.
1313 *
1314 * @return bool|object Boolean true if the column type is a known type
1315 * or a PEAR_Error, otherwise.
1316 *
1317 */
1318
1319 function _validateColumnType($phptype, $type)
1320 {
1321 // map of valid types for the current RDBMS
1322 $map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
1323
1324 // is it a recognized column type?
1325 $types = array_keys($map);
1326 if (!in_array($type, $types)) {
1327 return DB_Table::throwError(
1328 DB_TABLE_ERR_DECLARE_TYPE,
1329 "('" . $type . "')"
1330 );
1331 }
1332
1333 return true;
1334 }
1335
1336
1337 /**
1338 *
1339 * Check whether a column has the right type.
1340 *
1341 * @access private
1342 *
1343 * @param string $phptype The DB/MDB2 phptype key.
1344 *
1345 * @param string $colname The column name.
1346 *
1347 * @param string $coltype The column type.
1348 *
1349 * @param mixed $tableInfoOrder Array with columns in the table (result
1350 * from tableInfo(), shortened to key 'order').
1351 *
1352 * @param mixed $tableInfo Array with information about the table (result
1353 * from tableInfo()).
1354 *
1355 * @param string $mode The name of the calling function, this can be either
1356 * 'verify' or 'alter'.
1357 *
1358 * @return bool|object Boolean true if the column has the right type.
1359 * Otherwise, either boolean false (case 'alter') or a PEAR_Error
1360 * (case 'verify').
1361 *
1362 */
1363
1364 function _checkColumnType($phptype, $colname, $coltype, $tableInfoOrder,
1365 $tableInfo, $mode)
1366 {
1367 // map of valid types for the current RDBMS
1368 $map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
1369
1370 // get the column type from tableInfo()
1371 $colindex = $tableInfoOrder[$colname];
1372 $type = strtolower($tableInfo[$colindex]['type']);
1373
1374 // workaround for possibly wrong detected column type (taken from MDB2)
1375 if ($type == 'unknown' && ($phptype == 'mysql' || $phptype == 'mysqli')) {
1376 $type = 'decimal';
1377 }
1378
1379 // strip size information (e.g. NUMERIC(9,2) => NUMERIC) if given
1380 if (($pos = strpos($type, '(')) !== false) {
1381 $type = substr($type, 0, $pos);
1382 }
1383
1384 // is the type valid for the given DB_Table column type?
1385 if (in_array($type, (array)$map[$coltype])) {
1386 return true;
1387 }
1388
1389 switch ($mode) {
1390
1391 case 'alter':
1392 return false;
1393
1394 case 'verify':
1395 return DB_Table::throwError(
1396 DB_TABLE_ERR_VER_COLUMN_TYPE,
1397 "(column='$colname', type='$type')"
1398 );
1399
1400 }
1401 }
1402
1403
1404 /**
1405 *
1406 * Return the index type and the columns belonging to this index.
1407 *
1408 * @access private
1409 *
1410 * @param mixed $idx_def The index definition.
1411 *
1412 * @return mixed Array with the index type and the columns belonging to
1413 * this index.
1414 *
1415 */
1416
1417 function _getIndexTypeAndColumns($idx_def, $idxname)
1418 {
1419 $type = '';
1420 $cols = '';
1421 if (is_string($idx_def)) {
1422 // shorthand for index names: colname => index_type
1423 $type = trim($idx_def);
1424 $cols = trim($idxname);
1425 } elseif (is_array($idx_def)) {
1426 // normal: index_name => array('type' => ..., 'cols' => ...)
1427 $type = (isset($idx_def['type'])) ? $idx_def['type'] : 'normal';
1428 $cols = (isset($idx_def['cols'])) ? $idx_def['cols'] : null;
1429 }
1430
1431 return array($type, $cols);
1432 }
1433
1434
1435 /**
1436 *
1437 * Check an index name for validity.
1438 *
1439 * @access private
1440 *
1441 * @param string $idxname The index name.
1442 *
1443 * @param string $table The table name.
1444 *
1445 * @param string $phptype The DB/MDB2 phptype key.
1446 *
1447 * @param string $type The index type.
1448 *
1449 * @param mixed $cols The column names for the index. Will become an array
1450 * if it is not an array.
1451 *
1452 * @param mixed $column_set A DB_Table $this->col array.
1453 *
1454 * @param string $newIdxName The new index name (prefixed with the table
1455 * name, suffixed with '_idx').
1456 *
1457 * @return bool|object Boolean true if the index name is valid or a
1458 * PEAR_Error with a description about the invalidity, otherwise.
1459 *
1460 */
1461
1462 function _validateIndexName($idxname, $table, $phptype, $type, &$cols,
1463 $column_set, &$newIdxName)
1464 {
1465 // index name cannot be a reserved keyword
1466 $reserved = in_array(
1467 strtoupper($idxname),
1468 $GLOBALS['_DB_TABLE']['reserved']
1469 );
1470
1471 if ($reserved && !($type == 'primary' && $idxname == 'PRIMARY')) {
1472 return DB_Table::throwError(
1473 DB_TABLE_ERR_DECLARE_IDXNAME,
1474 "('$idxname')"
1475 );
1476 }
1477
1478 // are there any columns for the index?
1479 if (! $cols) {
1480 return DB_Table::throwError(
1481 DB_TABLE_ERR_IDX_NO_COLS,
1482 "('$idxname')"
1483 );
1484 }
1485
1486 // are there any CLOB columns, or any columns that are not
1487 // in the schema?
1488 settype($cols, 'array');
1489 $valid_cols = array_keys($column_set);
1490 foreach ($cols as $colname) {
1491
1492 if (! in_array($colname, $valid_cols)) {
1493 return DB_Table::throwError(
1494 DB_TABLE_ERR_IDX_COL_UNDEF,
1495 "'$idxname' ('$colname')"
1496 );
1497 }
1498
1499 if ($column_set[$colname]['type'] == 'clob') {
1500 return DB_Table::throwError(
1501 DB_TABLE_ERR_IDX_COL_CLOB,
1502 "'$idxname' ('$colname')"
1503 );
1504 }
1505
1506 }
1507
1508 // we prefix all index names with the table name,
1509 // and suffix all index names with '_idx'. this
1510 // is to soothe PostgreSQL, which demands that index
1511 // names not collide, even when they indexes are on
1512 // different tables.
1513 $newIdxName = $table . '_' . $idxname . '_idx';
1514
1515 // MySQL requires the primary key to be named 'primary', therefore let's
1516 // ignore the user defined name
1517 if (($phptype == 'mysql' || $phptype == 'mysqli') && $type == 'primary') {
1518 $newIdxName = 'primary';
1519 }
1520
1521 // now check the length; must be under 30 chars to
1522 // soothe Oracle.
1523 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1524 && strlen($newIdxName) > 30
1525 ) {
1526 return DB_Table::throwError(
1527 DB_TABLE_ERR_IDX_STRLEN,
1528 "'$idxname' ('$newIdxName')"
1529 );
1530 }
1531
1532 // check index type
1533 if ($type != 'primary' && $type != 'unique' && $type != 'normal') {
1534 return DB_Table::throwError(
1535 DB_TABLE_ERR_IDX_TYPE,
1536 "'$idxname' ('$type')"
1537 );
1538 }
1539
1540 return true;
1541 }
1542
1543
1544 /**
1545 *
1546 * Return all indexes for a table.
1547 *
1548 * @access public
1549 *
1550 * @param object &$db A PEAR DB/MDB2 object.
1551 *
1552 * @param string $table The table name.
1553 *
1554 * @return mixed Array with all indexes or a PEAR_Error when an error
1555 * occured.
1556 *
1557 */
1558
1559 function getIndexes(&$db, $table)
1560 {
1561 if (is_subclass_of($db, 'db_common')) {
1562 $backend = 'db';
1563 // workaround for missing index and constraint information methods
1564 // in PEAR::DB ==> use adopted code from MDB2's driver classes
1565 require_once 'DB/Table/Manager/' . $db->phptype . '.php';
1566 $classname = 'DB_Table_Manager_' . $db->phptype;
1567 $dbtm = new $classname();
1568 $dbtm->_db =& $db; // pass database instance to the 'workaround' class
1569 $manager =& $dbtm;
1570 $reverse =& $dbtm;
1571 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
1572 $backend = 'mdb2';
1573 $manager =& $db->manager;
1574 $reverse =& $db->reverse;
1575 }
1576
1577 $indexes = array('normal' => array(),
1578 'primary' => array(),
1579 'unique' => array()
1580 );
1581
1582 // save user defined 'idxname_format' option (MDB2 only)
1583 if ($backend == 'mdb2') {
1584 $idxname_format = $db->getOption('idxname_format');
1585 $db->setOption('idxname_format', '%s');
1586 }
1587
1588 // get table constraints
1589 $table_indexes_tmp = $manager->listTableConstraints($table);
1590 if (PEAR::isError($table_indexes_tmp)) {
1591 // restore user defined 'idxname_format' option (MDB2 only)
1592 if ($backend == 'mdb2') {
1593 $db->setOption('idxname_format', $idxname_format);
1594 }
1595 return $table_indexes_tmp;
1596 }
1597
1598 // get fields of table constraints
1599 foreach ($table_indexes_tmp as $table_idx_tmp) {
1600 $index_fields = $reverse->getTableConstraintDefinition($table,
1601 $table_idx_tmp);
1602 if (PEAR::isError($index_fields)) {
1603 // restore user defined 'idxname_format' option (MDB2 only)
1604 if ($backend == 'mdb2') {
1605 $db->setOption('idxname_format', $idxname_format);
1606 }
1607 return $index_fields;
1608 }
1609 // get the first key of $index_fields that has boolean true value
1610 foreach ($index_fields as $index_type => $value) {
1611 if ($value === true) {
1612 break;
1613 }
1614 }
1615 $indexes[$index_type][$table_idx_tmp] = array_keys($index_fields['fields']);
1616 }
1617
1618 // get table indexes
1619 $table_indexes_tmp = $manager->listTableIndexes($table);
1620 if (PEAR::isError($table_indexes_tmp)) {
1621 // restore user defined 'idxname_format' option (MDB2 only)
1622 if ($backend == 'mdb2') {
1623 $db->setOption('idxname_format', $idxname_format);
1624 }
1625 return $table_indexes_tmp;
1626 }
1627
1628 // get fields of table indexes
1629 foreach ($table_indexes_tmp as $table_idx_tmp) {
1630 $index_fields = $reverse->getTableIndexDefinition($table,
1631 $table_idx_tmp);
1632 if (PEAR::isError($index_fields)) {
1633 // restore user defined 'idxname_format' option (MDB2 only)
1634 if ($backend == 'mdb2') {
1635 $db->setOption('idxname_format', $idxname_format);
1636 }
1637 return $index_fields;
1638 }
1639 $indexes['normal'][$table_idx_tmp] = array_keys($index_fields['fields']);
1640 }
1641
1642 // restore user defined 'idxname_format' option (MDB2 only)
1643 if ($backend == 'mdb2') {
1644 $db->setOption('idxname_format', $idxname_format);
1645 }
1646
1647 return $indexes;
1648 }
1649
1650
1651 /**
1652 *
1653 * Check whether an index has the right type and has all specified columns.
1654 *
1655 * @access private
1656 *
1657 * @param string $idxname The index name.
1658 *
1659 * @param string $newIdxName The prefixed and suffixed index name.
1660 *
1661 * @param string $type The index type.
1662 *
1663 * @param mixed $cols The column names for the index.
1664 *
1665 * @param mixed $table_indexes Array with all indexes of the table.
1666 *
1667 * @param string $mode The name of the calling function, this can be either
1668 * 'verify' or 'alter'.
1669 *
1670 * @return bool|object Boolean true if the index has the right type and all
1671 * specified columns. Otherwise, either boolean false (case 'alter') or a
1672 * PEAR_Error (case 'verify').
1673 *
1674 */
1675
1676 function _checkIndex($idxname, $newIdxName, $type, $cols, &$table_indexes, $mode)
1677 {
1678 $index_found = false;
1679
1680 foreach ($table_indexes[$type] as $index_name => $index_fields) {
1681 if (strtolower($index_name) == strtolower($newIdxName)) {
1682 $index_found = true;
1683 array_walk($cols, create_function('&$value,$key',
1684 '$value = trim(strtolower($value));'));
1685 array_walk($index_fields, create_function('&$value,$key',
1686 '$value = trim(strtolower($value));'));
1687 foreach ($index_fields as $index_field) {
1688 if (($key = array_search($index_field, $cols)) !== false) {
1689 unset($cols[$key]);
1690 }
1691 }
1692 break;
1693 }
1694 }
1695
1696 if (!$index_found) {
1697 return ($mode == 'alter') ? false : DB_Table::throwError(
1698 DB_TABLE_ERR_VER_IDX_MISSING,
1699 "'$idxname' ('$newIdxName')"
1700 );
1701 }
1702
1703 if (count($cols) > 0) {
1704 // string of column names
1705 $colstring = implode(', ', $cols);
1706 return ($mode == 'alter') ? false : DB_Table::throwError(
1707 DB_TABLE_ERR_VER_IDX_COL_MISSING,
1708 "'$idxname' ($colstring)"
1709 );
1710 }
1711
1712 return true;
1713 }
1714
1715
1716 /**
1717 *
1718 * Create indexes and contraints.
1719 *
1720 * @access private
1721 *
1722 * @param object &$db A PEAR DB/MDB2 object.
1723 *
1724 * @param string $backend The name of the backend ('db' or 'mdb2').
1725 *
1726 * @param string $table The table name.
1727 *
1728 * @param mixed $indexes An array with index and constraint definitions.
1729 *
1730 * @return bool|object Boolean true on success or a PEAR_Error with a
1731 * description about the invalidity, otherwise.
1732 *
1733 */
1734
1735 function _createIndexesAndContraints($db, $backend, $table, $indexes)
1736 {
1737 if ($backend == 'mdb2') {
1738
1739 // save user defined 'idxname_format' option
1740 $idxname_format = $db->getOption('idxname_format');
1741 $db->setOption('idxname_format', '%s');
1742
1743 // attempt to create the primary key
1744 if (!array_key_exists('primary', $indexes)) {
1745 $indexes['primary'] = array();
1746 }
1747 foreach ($indexes['primary'] as $name => $definition) {
1748 $result = $db->manager->createConstraint($table, $name, $definition);
1749 if (PEAR::isError($result)) {
1750 // restore user defined 'idxname_format' option
1751 $db->setOption('idxname_format', $idxname_format);
1752 return $result;
1753 }
1754 }
1755
1756 // attempt to create the unique indexes / constraints
1757 if (!array_key_exists('unique', $indexes)) {
1758 $indexes['unique'] = array();
1759 }
1760 foreach ($indexes['unique'] as $name => $definition) {
1761 $result = $db->manager->createConstraint($table, $name, $definition);
1762 if (PEAR::isError($result)) {
1763 // restore user defined 'idxname_format' option
1764 $db->setOption('idxname_format', $idxname_format);
1765 return $result;
1766 }
1767 }
1768
1769 // attempt to create the normal indexes
1770 if (!array_key_exists('normal', $indexes)) {
1771 $indexes['normal'] = array();
1772 }
1773 foreach ($indexes['normal'] as $name => $definition) {
1774 $result = $db->manager->createIndex($table, $name, $definition);
1775 if (PEAR::isError($result)) {
1776 // restore user defined 'idxname_format' option
1777 $db->setOption('idxname_format', $idxname_format);
1778 return $result;
1779 }
1780 }
1781
1782 // restore user defined 'idxname_format' option
1783 $db->setOption('idxname_format', $idxname_format);
1784
1785 } else {
1786
1787 // attempt to create the indexes
1788 foreach ($indexes as $cmd) {
1789 $result = $db->query($cmd);
1790 if (PEAR::isError($result)) {
1791 return $result;
1792 }
1793 }
1794
1795 }
1796
1797 return true;
1798
1799 }
1800
1801 }
1802
1803
1804 /**
1805 * List of all reserved words for all supported databases. Yes, this is a
1806 * monster of a list.
1807 */
1808 if (! isset($GLOBALS['_DB_TABLE']['reserved'])) {
1809 $GLOBALS['_DB_TABLE']['reserved'] = array(
1810 '_ROWID_',
1811 'ABSOLUTE',
1812 'ACCESS',
1813 'ACTION',
1814 'ADD',
1815 'ADMIN',
1816 'AFTER',
1817 'AGGREGATE',
1818 'ALIAS',
1819 'ALL',
1820 'ALLOCATE',
1821 'ALTER',
1822 'ANALYSE',
1823 'ANALYZE',
1824 'AND',
1825 'ANY',
1826 'ARE',
1827 'ARRAY',
1828 'AS',
1829 'ASC',
1830 'ASENSITIVE',
1831 'ASSERTION',
1832 'AT',
1833 'AUDIT',
1834 'AUTHORIZATION',
1835 'AUTO_INCREMENT',
1836 'AVG',
1837 'BACKUP',
1838 'BDB',
1839 'BEFORE',
1840 'BEGIN',
1841 'BERKELEYDB',
1842 'BETWEEN',
1843 'BIGINT',
1844 'BINARY',
1845 'BIT',
1846 'BIT_LENGTH',
1847 'BLOB',
1848 'BOOLEAN',
1849 'BOTH',
1850 'BREADTH',
1851 'BREAK',
1852 'BROWSE',
1853 'BULK',
1854 'BY',
1855 'CALL',
1856 'CASCADE',
1857 'CASCADED',
1858 'CASE',
1859 'CAST',
1860 'CATALOG',
1861 'CHANGE',
1862 'CHAR',
1863 'CHAR_LENGTH',
1864 'CHARACTER',
1865 'CHARACTER_LENGTH',
1866 'CHECK',
1867 'CHECKPOINT',
1868 'CLASS',
1869 'CLOB',
1870 'CLOSE',
1871 'CLUSTER',
1872 'CLUSTERED',
1873 'COALESCE',
1874 'COLLATE',
1875 'COLLATION',
1876 'COLUMN',
1877 'COLUMNS',
1878 'COMMENT',
1879 'COMMIT',
1880 'COMPLETION',
1881 'COMPRESS',
1882 'COMPUTE',
1883 'CONDITION',
1884 'CONNECT',
1885 'CONNECTION',
1886 'CONSTRAINT',
1887 'CONSTRAINTS',
1888 'CONSTRUCTOR',
1889 'CONTAINS',
1890 'CONTAINSTABLE',
1891 'CONTINUE',
1892 'CONVERT',
1893 'CORRESPONDING',
1894 'COUNT',
1895 'CREATE',
1896 'CROSS',
1897 'CUBE',
1898 'CURRENT',
1899 'CURRENT_DATE',
1900 'CURRENT_PATH',
1901 'CURRENT_ROLE',
1902 'CURRENT_TIME',
1903 'CURRENT_TIMESTAMP',
1904 'CURRENT_USER',
1905 'CURSOR',
1906 'CYCLE',
1907 'DATA',
1908 'DATABASE',
1909 'DATABASES',
1910 'DATE',
1911 'DAY',
1912 'DAY_HOUR',
1913 'DAY_MICROSECOND',
1914 'DAY_MINUTE',
1915 'DAY_SECOND',
1916 'DBCC',
1917 'DEALLOCATE',
1918 'DEC',
1919 'DECIMAL',
1920 'DECLARE',
1921 'DEFAULT',
1922 'DEFERRABLE',
1923 'DEFERRED',
1924 'DELAYED',
1925 'DELETE',
1926 'DENY',
1927 'DEPTH',
1928 'DEREF',
1929 'DESC',
1930 'DESCRIBE',
1931 'DESCRIPTOR',
1932 'DESTROY',
1933 'DESTRUCTOR',
1934 'DETERMINISTIC',
1935 'DIAGNOSTICS',
1936 'DICTIONARY',
1937 'DISCONNECT',
1938 'DISK',
1939 'DISTINCT',
1940 'DISTINCTROW',
1941 'DISTRIBUTED',
1942 'DIV',
1943 'DO',
1944 'DOMAIN',
1945 'DOUBLE',
1946 'DROP',
1947 'DUMMY',
1948 'DUMP',
1949 'DYNAMIC',
1950 'EACH',
1951 'ELSE',
1952 'ELSEIF',
1953 'ENCLOSED',
1954 'END',
1955 'END-EXEC',
1956 'EQUALS',
1957 'ERRLVL',
1958 'ESCAPE',
1959 'ESCAPED',
1960 'EVERY',
1961 'EXCEPT',
1962 'EXCEPTION',
1963 'EXCLUSIVE',
1964 'EXEC',
1965 'EXECUTE',
1966 'EXISTS',
1967 'EXIT',
1968 'EXPLAIN',
1969 'EXTERNAL',
1970 'EXTRACT',
1971 'FALSE',
1972 'FETCH',
1973 'FIELDS',
1974 'FILE',
1975 'FILLFACTOR',
1976 'FIRST',
1977 'FLOAT',
1978 'FOR',
1979 'FORCE',
1980 'FOREIGN',
1981 'FOUND',
1982 'FRAC_SECOND',
1983 'FREE',
1984 'FREETEXT',
1985 'FREETEXTTABLE',
1986 'FREEZE',
1987 'FROM',
1988 'FULL',
1989 'FULLTEXT',
1990 'FUNCTION',
1991 'GENERAL',
1992 'GET',
1993 'GLOB',
1994 'GLOBAL',
1995 'GO',
1996 'GOTO',
1997 'GRANT',
1998 'GROUP',
1999 'GROUPING',
2000 'HAVING',
2001 'HIGH_PRIORITY',
2002 'HOLDLOCK',
2003 'HOST',
2004 'HOUR',
2005 'HOUR_MICROSECOND',
2006 'HOUR_MINUTE',
2007 'HOUR_SECOND',
2008 'IDENTIFIED',
2009 'IDENTITY',
2010 'IDENTITY_INSERT',
2011 'IDENTITYCOL',
2012 'IF',
2013 'IGNORE',
2014 'ILIKE',
2015 'IMMEDIATE',
2016 'IN',
2017 'INCREMENT',
2018 'INDEX',
2019 'INDICATOR',
2020 'INFILE',
2021 'INITIAL',
2022 'INITIALIZE',
2023 'INITIALLY',
2024 'INNER',
2025 'INNODB',
2026 'INOUT',
2027 'INPUT',
2028 'INSENSITIVE',
2029 'INSERT',
2030 'INT',
2031 'INTEGER',
2032 'INTERSECT',
2033 'INTERVAL',
2034 'INTO',
2035 'IO_THREAD',
2036 'IS',
2037 'ISNULL',
2038 'ISOLATION',
2039 'ITERATE',
2040 'JOIN',
2041 'KEY',
2042 'KEYS',
2043 'KILL',
2044 'LANGUAGE',
2045 'LARGE',
2046 'LAST',
2047 'LATERAL',
2048 'LEADING',
2049 'LEAVE',
2050 'LEFT',
2051 'LESS',
2052 'LEVEL',
2053 'LIKE',
2054 'LIMIT',
2055 'LINENO',
2056 'LINES',
2057 'LOAD',
2058 'LOCAL',
2059 'LOCALTIME',
2060 'LOCALTIMESTAMP',
2061 'LOCATOR',
2062 'LOCK',
2063 'LONG',
2064 'LONGBLOB',
2065 'LONGTEXT',
2066 'LOOP',
2067 'LOW_PRIORITY',
2068 'LOWER',
2069 'MAIN',
2070 'MAP',
2071 'MASTER_SERVER_ID',
2072 'MATCH',
2073 'MAX',
2074 'MAXEXTENTS',
2075 'MEDIUMBLOB',
2076 'MEDIUMINT',
2077 'MEDIUMTEXT',
2078 'MIDDLEINT',
2079 'MIN',
2080 'MINUS',
2081 'MINUTE',
2082 'MINUTE_MICROSECOND',
2083 'MINUTE_SECOND',
2084 'MLSLABEL',
2085 'MOD',
2086 'MODE',
2087 'MODIFIES',
2088 'MODIFY',
2089 'MODULE',
2090 'MONTH',
2091 'NAMES',
2092 'NATIONAL',
2093 'NATURAL',
2094 'NCHAR',
2095 'NCLOB',
2096 'NEW',
2097 'NEXT',
2098 'NO',
2099 'NO_WRITE_TO_BINLOG',
2100 'NOAUDIT',
2101 'NOCHECK',
2102 'NOCOMPRESS',
2103 'NONCLUSTERED',
2104 'NONE',
2105 'NOT',
2106 'NOTNULL',
2107 'NOWAIT',
2108 'NULL',
2109 'NULLIF',
2110 'NUMBER',
2111 'NUMERIC',
2112 'OBJECT',
2113 'OCTET_LENGTH',
2114 'OF',
2115 'OFF',
2116 'OFFLINE',
2117 'OFFSET',
2118 'OFFSETS',
2119 'OID',
2120 'OLD',
2121 'ON',
2122 'ONLINE',
2123 'ONLY',
2124 'OPEN',
2125 'OPENDATASOURCE',
2126 'OPENQUERY',
2127 'OPENROWSET',
2128 'OPENXML',
2129 'OPERATION',
2130 'OPTIMIZE',
2131 'OPTION',
2132 'OPTIONALLY',
2133 'OR',
2134 'ORDER',
2135 'ORDINALITY',
2136 'OUT',
2137 'OUTER',
2138 'OUTFILE',
2139 'OUTPUT',
2140 'OVER',
2141 'OVERLAPS',
2142 'PAD',
2143 'PARAMETER',
2144 'PARAMETERS',
2145 'PARTIAL',
2146 'PATH',
2147 'PCTFREE',
2148 'PERCENT',
2149 'PLACING',
2150 'PLAN',
2151 'POSITION',
2152 'POSTFIX',
2153 'PRECISION',
2154 'PREFIX',
2155 'PREORDER',
2156 'PREPARE',
2157 'PRESERVE',
2158 'PRIMARY',
2159 'PRINT',
2160 'PRIOR',
2161 'PRIVILEGES',
2162 'PROC',
2163 'PROCEDURE',
2164 'PUBLIC',
2165 'PURGE',
2166 'RAISERROR',
2167 'RAW',
2168 'READ',
2169 'READS',
2170 'READTEXT',
2171 'REAL',
2172 'RECONFIGURE',
2173 'RECURSIVE',
2174 'REF',
2175 'REFERENCES',
2176 'REFERENCING',
2177 'REGEXP',
2178 'RELATIVE',
2179 'RENAME',
2180 'REPEAT',
2181 'REPLACE',
2182 'REPLICATION',
2183 'REQUIRE',
2184 'RESOURCE',
2185 'RESTORE',
2186 'RESTRICT',
2187 'RESULT',
2188 'RETURN',
2189 'RETURNS',
2190 'REVOKE',
2191 'RIGHT',
2192 'RLIKE',
2193 'ROLE',
2194 'ROLLBACK',
2195 'ROLLUP',
2196 'ROUTINE',
2197 'ROW',
2198 'ROWCOUNT',
2199 'ROWGUIDCOL',
2200 'ROWID',
2201 'ROWNUM',
2202 'ROWS',
2203 'RULE',
2204 'SAVE',
2205 'SAVEPOINT',
2206 'SCHEMA',
2207 'SCOPE',
2208 'SCROLL',
2209 'SEARCH',
2210 'SECOND',
2211 'SECOND_MICROSECOND',
2212 'SECTION',
2213 'SELECT',
2214 'SENSITIVE',
2215 'SEPARATOR',
2216 'SEQUENCE',
2217 'SESSION',
2218 'SESSION_USER',
2219 'SET',
2220 'SETS',
2221 'SETUSER',
2222 'SHARE',
2223 'SHOW',
2224 'SHUTDOWN',
2225 'SIMILAR',
2226 'SIZE',
2227 'SMALLINT',
2228 'SOME',
2229 'SONAME',
2230 'SPACE',
2231 'SPATIAL',
2232 'SPECIFIC',
2233 'SPECIFICTYPE',
2234 'SQL',
2235 'SQL_BIG_RESULT',
2236 'SQL_CALC_FOUND_ROWS',
2237 'SQL_SMALL_RESULT',
2238 'SQL_TSI_DAY',
2239 'SQL_TSI_FRAC_SECOND',
2240 'SQL_TSI_HOUR',
2241 'SQL_TSI_MINUTE',
2242 'SQL_TSI_MONTH',
2243 'SQL_TSI_QUARTER',
2244 'SQL_TSI_SECOND',
2245 'SQL_TSI_WEEK',
2246 'SQL_TSI_YEAR',
2247 'SQLCODE',
2248 'SQLERROR',
2249 'SQLEXCEPTION',
2250 'SQLITE_MASTER',
2251 'SQLITE_TEMP_MASTER',
2252 'SQLSTATE',
2253 'SQLWARNING',
2254 'SSL',
2255 'START',
2256 'STARTING',
2257 'STATE',
2258 'STATEMENT',
2259 'STATIC',
2260 'STATISTICS',
2261 'STRAIGHT_JOIN',
2262 'STRIPED',
2263 'STRUCTURE',
2264 'SUBSTRING',
2265 'SUCCESSFUL',
2266 'SUM',
2267 'SYNONYM',
2268 'SYSDATE',
2269 'SYSTEM_USER',
2270 'TABLE',
2271 'TABLES',
2272 'TEMPORARY',
2273 'TERMINATE',
2274 'TERMINATED',
2275 'TEXTSIZE',
2276 'THAN',
2277 'THEN',
2278 'TIME',
2279 'TIMESTAMP',
2280 'TIMESTAMPADD',
2281 'TIMESTAMPDIFF',
2282 'TIMEZONE_HOUR',
2283 'TIMEZONE_MINUTE',
2284 'TINYBLOB',
2285 'TINYINT',
2286 'TINYTEXT',
2287 'TO',
2288 'TOP',
2289 'TRAILING',
2290 'TRAN',
2291 'TRANSACTION',
2292 'TRANSLATE',
2293 'TRANSLATION',
2294 'TREAT',
2295 'TRIGGER',
2296 'TRIM',
2297 'TRUE',
2298 'TRUNCATE',
2299 'TSEQUAL',
2300 'UID',
2301 'UNDER',
2302 'UNDO',
2303 'UNION',
2304 'UNIQUE',
2305 'UNKNOWN',
2306 'UNLOCK',
2307 'UNNEST',
2308 'UNSIGNED',
2309 'UPDATE',
2310 'UPDATETEXT',
2311 'UPPER',
2312 'USAGE',
2313 'USE',
2314 'USER',
2315 'USER_RESOURCES',
2316 'USING',
2317 'UTC_DATE',
2318 'UTC_TIME',
2319 'UTC_TIMESTAMP',
2320 'VALIDATE',
2321 'VALUE',
2322 'VALUES',
2323 'VARBINARY',
2324 'VARCHAR',
2325 'VARCHAR2',
2326 'VARCHARACTER',
2327 'VARIABLE',
2328 'VARYING',
2329 'VERBOSE',
2330 'VIEW',
2331 'WAITFOR',
2332 'WHEN',
2333 'WHENEVER',
2334 'WHERE',
2335 'WHILE',
2336 'WITH',
2337 'WITHOUT',
2338 'WORK',
2339 'WRITE',
2340 'WRITETEXT',
2341 'XOR',
2342 'YEAR',
2343 'YEAR_MONTH',
2344 'ZEROFILL',
2345 'ZONE',
2346 );
2347 }
2348
2349 ?>