3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * Creates, checks or alters tables from DB_Table definitions.
8 * DB_Table_Manager provides database automated table creation
11 * PHP versions 4 and 5
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.
20 * Redistribution and use in source and binary forms, with or without
21 * modification, are permitted provided that the following conditions
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.
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.
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
54 require_once 'DB/Table.php';
58 * Valid types for the different data types in the different DBMS.
60 $GLOBALS['_DB_TABLE']['valid_type'] = array(
61 'fbsql' => array( // currently not supported
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',
89 'timestamp' => 'timestamp'
91 'mssql' => array( // currently not supported
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'),
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')
122 'boolean' => array('char', 'decimal', 'tinyint'),
123 'char' => array('char', 'varchar'),
124 'varchar' => array('char', 'varchar'),
125 'smallint' => array('smallint', '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')
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'),
147 'date' => array('char', 'date'),
148 'time' => array('char', 'date'),
149 'timestamp' => array('char', 'date')
152 'boolean' => array('bool', 'numeric'),
153 'char' => array('bpchar', 'varchar'),
154 'varchar' => 'varchar',
155 'smallint' => array('int2', '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')
167 'boolean' => 'boolean',
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'),
179 'timestamp' => array('datetime', 'timestamp')
184 * Mapping between DB_Table and MDB2 data types.
186 $GLOBALS['_DB_TABLE']['mdb2_type'] = array(
187 'boolean' => 'boolean',
190 'smallint' => 'integer',
191 'integer' => 'integer',
192 'bigint' => 'integer',
193 'decimal' => 'decimal',
199 'timestamp' => 'timestamp'
203 * Creates, checks or alters tables from DB_Table definitions.
205 * DB_Table_Manager provides database automated table creation
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
216 class DB_Table_Manager
{
221 * Create the table based on DB_Table column and index arrays.
227 * @param object &$db A PEAR DB/MDB2 object.
229 * @param string $table The table name to connect to in the database.
231 * @param mixed $column_set A DB_Table $this->col array.
233 * @param mixed $index_set A DB_Table $this->idx array.
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.
241 function create(&$db, $table, $column_set, $index_set)
243 if (is_subclass_of($db, 'db_common')) {
245 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
247 $db->loadModule('Manager');
249 $phptype = $db->phptype
;
251 // columns to be created
254 // max. value for scope (only used with MDB2 as backend)
257 // indexes to be created
260 // check the table name
261 $name_check = DB_Table_Manager
::_validateTableName($table);
262 if (PEAR
::isError($name_check)) {
267 // -------------------------------------------------------------
269 // validate each column mapping and build the individual
270 // definitions, and note column indexes as we go.
273 if (is_null($column_set)) {
274 $column_set = array();
277 foreach ($column_set as $colname => $val) {
279 $colname = trim($colname);
281 // check the column name
282 $name_check = DB_Table_Manager
::_validateColumnName($colname);
283 if (PEAR
::isError($name_check)) {
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;
295 if ($backend == 'mdb2') {
297 // get the declaration string
298 $result = DB_Table_Manager
::getDeclareMDB2($type,
299 $size, $scope, $require, $default, $max_scope);
302 if (PEAR
::isError($result)) {
303 $result->userinfo
.= " ('$colname')";
307 // add the declaration to the array of all columns
308 $column[$colname] = $result;
312 // get the declaration string
313 $result = DB_Table_Manager
::getDeclare($phptype, $type,
314 $size, $scope, $require, $default);
317 if (PEAR
::isError($result)) {
318 $result->userinfo
.= " ('$colname')";
322 // add the declaration to the array of all columns
323 $column[] = "$colname $result";
330 // -------------------------------------------------------------
332 // validate the indexes.
335 if (is_null($index_set)) {
336 $index_set = array();
339 $count_primary_keys = 0;
341 foreach ($index_set as $idxname => $val) {
343 list($type, $cols) = DB_Table_Manager
::_getIndexTypeAndColumns($val, $idxname);
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)) {
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
);
360 $count_primary_keys++
;
362 if ($count_primary_keys > 1) {
363 return DB_Table
::throwError(DB_TABLE_ERR_DECLARE_PRIMARY
);
366 // create index entry
367 if ($backend == 'mdb2') {
369 // array with column names as keys
371 foreach ($cols as $col) {
372 $idx_cols[$col] = array();
377 $indexes['primary'][$newIdxName] =
378 array('fields' => $idx_cols,
382 $indexes['unique'][$newIdxName] =
383 array('fields' => $idx_cols,
387 $indexes['normal'][$newIdxName] =
388 array('fields' => $idx_cols);
394 $indexes[] = DB_Table_Manager
::getDeclareForIndex($phptype,
395 $type, $newIdxName, $table, $cols);
402 // -------------------------------------------------------------
404 // now for the real action: create the table and indexes!
406 if ($backend == 'mdb2') {
408 // save user defined 'decimal_places' option
409 $decimal_places = $db->getOption('decimal_places');
410 $db->setOption('decimal_places', $max_scope);
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)) {
422 // build the CREATE TABLE command
423 $cmd = "CREATE TABLE $table (\n\t";
424 $cmd .= implode(",\n\t", $column);
427 // attempt to create the table
428 $result = $db->query($cmd);
429 if (PEAR
::isError($result)) {
435 $result = DB_Table_Manager
::_createIndexesAndContraints($db, $backend,
437 if (PEAR
::isError($result)) {
448 * Verify whether the table and columns exist, whether the columns
449 * have the right type and whether the indexes exist.
455 * @param object &$db A PEAR DB/MDB2 object.
457 * @param string $table The table name to connect to in the database.
459 * @param mixed $column_set A DB_Table $this->col array.
461 * @param mixed $index_set A DB_Table $this->idx array.
463 * @return mixed Boolean true if the verification was successful, and a
464 * PEAR_Error if verification failed.
468 function verify(&$db, $table, $column_set, $index_set)
470 if (is_subclass_of($db, 'db_common')) {
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')) {
477 $reverse =& $this->db
->loadModule('Reverse');
478 $table_info_mode = MDB2_TABLEINFO_FULL
;
479 $table_info_error = MDB2_ERROR_NEED_MORE_DATA
;
481 $phptype = $db->phptype
;
483 // check #1: does the table exist?
485 // check the table name
486 $name_check = DB_Table_Manager
::_validateTableName($table);
487 if (PEAR
::isError($name_check)) {
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
,
502 $tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER
);
504 if (is_null($column_set)) {
505 $column_set = array();
508 foreach ($column_set as $colname => $val) {
509 $colname = strtolower(trim($colname));
511 // check the column name
512 $name_check = DB_Table_Manager
::_validateColumnName($colname);
513 if (PEAR
::isError($name_check)) {
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;
524 // check #3: do all columns have the right type?
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)) {
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)) {
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;
547 if (is_null($index_set)) {
548 $index_set = array();
551 foreach ($index_set as $idxname => $val) {
553 list($type, $cols) = DB_Table_Manager
::_getIndexTypeAndColumns($val, $idxname);
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)) {
564 // check whether the index has the right type and has all
566 $index_check = DB_Table_Manager
::_checkIndex($idxname, $newIdxName,
567 $type, $cols, $table_indexes, 'verify');
568 if (PEAR
::isError($index_check)) {
580 * Alter columns and indexes of a table based on DB_Table column and index
587 * @param object &$db A PEAR DB/MDB2 object.
589 * @param string $table The table name to connect to in the database.
591 * @param mixed $column_set A DB_Table $this->col array.
593 * @param mixed $index_set A DB_Table $this->idx array.
595 * @return bool|object True if altering was successful or a PEAR_Error on
600 function alter(&$db, $table, $column_set, $index_set)
602 $phptype = $db->phptype
;
604 if (is_subclass_of($db, 'db_common')) {
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
614 $table_info_mode = DB_TABLEINFO_FULL
;
616 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
618 $db->loadModule('Reverse');
619 $manager =& $db->manager
;
620 $reverse =& $db->reverse
;
621 $table_info_mode = MDB2_TABLEINFO_FULL
;
626 $tableInfo = $reverse->tableInfo($table, $table_info_mode);
627 if (PEAR
::isError($tableInfo)) {
630 $tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER
);
632 // emulate MDB2 Reverse extension for PEAR::DB as backend
633 if (is_subclass_of($db, 'db_common')) {
637 // check (and alter) columns
638 if (is_null($column_set)) {
639 $column_set = array();
642 foreach ($column_set as $colname => $val) {
643 $colname = strtolower(trim($colname));
645 // check the column name
646 $name_check = DB_Table_Manager
::_validateColumnName($colname);
647 if (PEAR
::isError($name_check)) {
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;
657 if ($column_exists === false) { // add the column
658 $definition = DB_Table_Manager
::_getColumnDefinition($backend,
660 if (PEAR
::isError($definition)) {
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";
669 $result = $manager->alterTable($table, $changes, false);
670 if (PEAR
::isError($result)) {
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)) {
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)) {
688 if ($type_check === false) { // change the column type
689 $definition = DB_Table_Manager
::_getColumnDefinition($backend,
691 if (PEAR
::isError($definition)) {
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";
702 $result = $manager->alterTable($table, $changes, false);
703 if (PEAR
::isError($result)) {
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;
717 // check (and alter) indexes / constraints
718 if (is_null($index_set)) {
719 $index_set = array();
722 foreach ($index_set as $idxname => $val) {
724 list($type, $cols) = DB_Table_Manager
::_getIndexTypeAndColumns($val, $idxname);
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)) {
735 // check whether the index has the right type and has all
737 $index_check = DB_Table_Manager
::_checkIndex($idxname, $newIdxName,
738 $type, $cols, $table_indexes, 'alter');
739 if (PEAR
::isError($index_check)) {
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');
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";
756 if ($idx_type == 'normal') {
757 $result = $manager->dropIndex($table, $newIdxName);
759 $result = $manager->dropConstraint($table, $newIdxName);
761 if (PEAR
::isError($result)) {
762 if ($backend == 'mdb2') {
763 // restore user defined 'idxname_format' option
764 $db->setOption('idxname_format', $idxname_format);
772 // prepare index/constraint definition
774 if ($backend == 'mdb2') {
776 // array with column names as keys
778 foreach ($cols as $col) {
779 $idx_cols[$col] = array();
784 $indexes['primary'][$newIdxName] =
785 array('fields' => $idx_cols,
789 $indexes['unique'][$newIdxName] =
790 array('fields' => $idx_cols,
794 $indexes['normal'][$newIdxName] =
795 array('fields' => $idx_cols);
801 $indexes[] = DB_Table_Manager
::getDeclareForIndex($phptype,
802 $type, $newIdxName, $table, $cols);
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";
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);
818 if (PEAR
::isError($result)) {
833 * Check whether a table exists.
839 * @param object &$db A PEAR DB/MDB2 object.
841 * @param string $table The table name that should be checked.
843 * @return bool|object True if the table exists, false if not, or a
844 * PEAR_Error on failure.
848 function tableExists(&$db, $table)
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();
856 if (PEAR
::isError($list)) {
859 array_walk($list, create_function('&$value,$key',
860 '$value = trim(strtolower($value));'));
861 return in_array(strtolower($table), $list);
867 * Get the column declaration string for a DB_Table column.
873 * @param string $phptype The DB/MDB2 phptype key.
875 * @param string $coltype The DB_Table column type.
877 * @param int $size The size for the column (needed for string and
880 * @param int $scope The scope for the column (needed for decimal).
882 * @param bool $require True if the column should be NOT NULL, false
883 * allowed to be NULL.
885 * @param string $default The SQL calculation for a default value.
887 * @return string|object A declaration string on success, or a
888 * PEAR_Error on failure.
892 function getDeclare($phptype, $coltype, $size = null, $scope = null,
893 $require = null, $default = null)
895 // validate char/varchar/decimal type declaration
896 $validation = DB_Table_Manager
::_validateTypeDeclaration($coltype, $size,
898 if (PEAR
::isError($validation)) {
902 // map of column types and declarations for this RDBMS
903 $map = $GLOBALS['_DB_TABLE']['type'][$phptype];
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
,
919 $declare = $map[$coltype] . "($size)";
923 $declare = $map[$coltype] . "($size,$scope)";
927 $declare = $map[$coltype];
932 // set the "NULL"/"NOT NULL" portion
934 if ($phptype == 'ibase') { // Firebird does not like 'NULL'
935 $null = ''; // in CREATE TABLE
937 if ($phptype == 'pgsql') { // PostgreSQL does not like 'NULL'
938 $null = ''; // in ALTER TABLE
940 $declare .= ($require) ?
' NOT NULL' : $null;
942 // set the "DEFAULT" portion
948 $declare .= " DEFAULT '$default'";
952 $declare .= " DEFAULT $default";
964 * Get the column declaration string for a DB_Table column.
970 * @param string $coltype The DB_Table column type.
972 * @param int $size The size for the column (needed for string and
975 * @param int $scope The scope for the column (needed for decimal).
977 * @param bool $require True if the column should be NOT NULL, false
978 * allowed to be NULL.
980 * @param string $default The SQL calculation for a default value.
982 * @param int $max_scope The maximal scope for all table column
983 * (pass-by-reference).
985 * @return string|object A MDB2 column definition array on success, or a
986 * PEAR_Error on failure.
990 function getDeclareMDB2($coltype, $size = null, $scope = null,
991 $require = null, $default = null, &$max_scope)
993 // validate char/varchar/decimal type declaration
994 $validation = DB_Table_Manager
::_validateTypeDeclaration($coltype, $size,
996 if (PEAR
::isError($validation)) {
1000 // map of MDB2 column types
1001 $map = $GLOBALS['_DB_TABLE']['mdb2_type'];
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
,
1012 // build declaration array
1013 $new_column = array(
1014 'type' => $map[$coltype],
1015 'notnull' => $require
1019 $new_column['length'] = $size;
1022 // determine integer length to be used in MDB2
1023 if (in_array($coltype, array('smallint', 'integer', 'bigint'))) {
1026 $new_column['length'] = 2;
1029 $new_column['length'] = 4;
1032 $new_column['length'] = 5;
1038 $max_scope = max($max_scope, $scope);
1042 $new_column['default'] = $default;
1051 * Get the index declaration string for a DB_Table index.
1057 * @param string $phptype The DB phptype key.
1059 * @param string $type The index type.
1061 * @param string $idxname The index name.
1063 * @param string $table The table name.
1065 * @param mixed $cols Array with the column names for the index.
1067 * @return string A declaration string.
1071 function getDeclareForIndex($phptype, $type, $idxname, $table, $cols)
1073 // string of column names
1074 $colstring = implode(', ', $cols);
1083 $declare = "ALTER TABLE $table ADD";
1084 $declare .= " CONSTRAINT $idxname";
1085 $declare .= " PRIMARY KEY ($colstring)";
1089 $declare = "ALTER TABLE $table ADD PRIMARY KEY";
1090 $declare .= " ($colstring)";
1093 // currently not possible
1099 $declare = "CREATE UNIQUE INDEX $idxname ON $table ($colstring)";
1103 $declare = "CREATE INDEX $idxname ON $table ($colstring)";
1114 * Return the definition array for a column.
1118 * @param string $backend The name of the backend ('db' or 'mdb2').
1120 * @param string $phptype The DB/MDB2 phptype key.
1122 * @param mixed $column A single DB_Table column definition array.
1124 * @return mixed|object Declaration string (DB), declaration array (MDB2) or a
1125 * PEAR_Error with a description about the invalidity, otherwise.
1129 function _getColumnDefinition($backend, $phptype, $column)
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;
1140 if ($backend == 'db') {
1141 return DB_Table_Manager
::getDeclare($phptype, $type,
1142 $size, $scope, $require, $default);
1144 return DB_Table_Manager
::getDeclareMDB2($type,
1145 $size, $scope, $require, $default, $max_scope);
1152 * Check char/varchar/decimal type declarations for validity.
1156 * @param string $coltype The DB_Table column type.
1158 * @param int $size The size for the column (needed for string and
1161 * @param int $scope The scope for the column (needed for decimal).
1163 * @return bool|object Boolean true if the type declaration is valid or a
1164 * PEAR_Error with a description about the invalidity, otherwise.
1168 function _validateTypeDeclaration($coltype, $size, $scope)
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
,
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')"
1194 * Check a table name for validity.
1198 * @param string $tablename The table name.
1200 * @return bool|object Boolean true if the table name is valid or a
1201 * PEAR_Error with a description about the invalidity, otherwise.
1205 function _validateTableName($tablename)
1207 // is the table name too long?
1208 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1209 && strlen($tablename) > 30
1211 return DB_Table
::throwError(
1212 DB_TABLE_ERR_TABLE_STRLEN
,
1223 * Check a column name for validity.
1227 * @param string $colname The column name.
1229 * @return bool|object Boolean true if the column name is valid or a
1230 * PEAR_Error with a description about the invalidity, otherwise.
1234 function _validateColumnName($colname)
1236 // column name cannot be a reserved keyword
1237 $reserved = in_array(
1238 strtoupper($colname),
1239 $GLOBALS['_DB_TABLE']['reserved']
1243 return DB_Table
::throwError(
1244 DB_TABLE_ERR_DECLARE_COLNAME
,
1249 // column name must be no longer than 30 chars
1250 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1251 && strlen($colname) > 30
1253 return DB_Table
::throwError(
1254 DB_TABLE_ERR_DECLARE_STRLEN
,
1265 * Check whether a column exists.
1269 * @param string $colname The column name.
1271 * @param mixed $tableInfoOrder Array with columns in the table (result
1272 * from tableInfo(), shortened to key 'order').
1274 * @param string $mode The name of the calling function, this can be either
1275 * 'verify' or 'alter'.
1277 * @return bool|object Boolean true if the column exists.
1278 * Otherwise, either boolean false (case 'alter') or a PEAR_Error
1283 function _columnExists($colname, $tableInfoOrder, $mode)
1285 if (array_key_exists($colname, $tableInfoOrder)) {
1295 return DB_Table
::throwError(
1296 DB_TABLE_ERR_VER_COLUMN_MISSING
,
1297 "(column='$colname')"
1306 * Check whether a column type is a known type.
1310 * @param string $phptype The DB/MDB2 phptype key.
1312 * @param string $type The column type.
1314 * @return bool|object Boolean true if the column type is a known type
1315 * or a PEAR_Error, otherwise.
1319 function _validateColumnType($phptype, $type)
1321 // map of valid types for the current RDBMS
1322 $map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
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
,
1339 * Check whether a column has the right type.
1343 * @param string $phptype The DB/MDB2 phptype key.
1345 * @param string $colname The column name.
1347 * @param string $coltype The column type.
1349 * @param mixed $tableInfoOrder Array with columns in the table (result
1350 * from tableInfo(), shortened to key 'order').
1352 * @param mixed $tableInfo Array with information about the table (result
1353 * from tableInfo()).
1355 * @param string $mode The name of the calling function, this can be either
1356 * 'verify' or 'alter'.
1358 * @return bool|object Boolean true if the column has the right type.
1359 * Otherwise, either boolean false (case 'alter') or a PEAR_Error
1364 function _checkColumnType($phptype, $colname, $coltype, $tableInfoOrder,
1367 // map of valid types for the current RDBMS
1368 $map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
1370 // get the column type from tableInfo()
1371 $colindex = $tableInfoOrder[$colname];
1372 $type = strtolower($tableInfo[$colindex]['type']);
1374 // workaround for possibly wrong detected column type (taken from MDB2)
1375 if ($type == 'unknown' && ($phptype == 'mysql' ||
$phptype == 'mysqli')) {
1379 // strip size information (e.g. NUMERIC(9,2) => NUMERIC) if given
1380 if (($pos = strpos($type, '(')) !== false) {
1381 $type = substr($type, 0, $pos);
1384 // is the type valid for the given DB_Table column type?
1385 if (in_array($type, (array)$map[$coltype])) {
1395 return DB_Table
::throwError(
1396 DB_TABLE_ERR_VER_COLUMN_TYPE
,
1397 "(column='$colname', type='$type')"
1406 * Return the index type and the columns belonging to this index.
1410 * @param mixed $idx_def The index definition.
1412 * @return mixed Array with the index type and the columns belonging to
1417 function _getIndexTypeAndColumns($idx_def, $idxname)
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;
1431 return array($type, $cols);
1437 * Check an index name for validity.
1441 * @param string $idxname The index name.
1443 * @param string $table The table name.
1445 * @param string $phptype The DB/MDB2 phptype key.
1447 * @param string $type The index type.
1449 * @param mixed $cols The column names for the index. Will become an array
1450 * if it is not an array.
1452 * @param mixed $column_set A DB_Table $this->col array.
1454 * @param string $newIdxName The new index name (prefixed with the table
1455 * name, suffixed with '_idx').
1457 * @return bool|object Boolean true if the index name is valid or a
1458 * PEAR_Error with a description about the invalidity, otherwise.
1462 function _validateIndexName($idxname, $table, $phptype, $type, &$cols,
1463 $column_set, &$newIdxName)
1465 // index name cannot be a reserved keyword
1466 $reserved = in_array(
1467 strtoupper($idxname),
1468 $GLOBALS['_DB_TABLE']['reserved']
1471 if ($reserved && !($type == 'primary' && $idxname == 'PRIMARY')) {
1472 return DB_Table
::throwError(
1473 DB_TABLE_ERR_DECLARE_IDXNAME
,
1478 // are there any columns for the index?
1480 return DB_Table
::throwError(
1481 DB_TABLE_ERR_IDX_NO_COLS
,
1486 // are there any CLOB columns, or any columns that are not
1488 settype($cols, 'array');
1489 $valid_cols = array_keys($column_set);
1490 foreach ($cols as $colname) {
1492 if (! in_array($colname, $valid_cols)) {
1493 return DB_Table
::throwError(
1494 DB_TABLE_ERR_IDX_COL_UNDEF
,
1495 "'$idxname' ('$colname')"
1499 if ($column_set[$colname]['type'] == 'clob') {
1500 return DB_Table
::throwError(
1501 DB_TABLE_ERR_IDX_COL_CLOB
,
1502 "'$idxname' ('$colname')"
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';
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';
1521 // now check the length; must be under 30 chars to
1523 if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
1524 && strlen($newIdxName) > 30
1526 return DB_Table
::throwError(
1527 DB_TABLE_ERR_IDX_STRLEN
,
1528 "'$idxname' ('$newIdxName')"
1533 if ($type != 'primary' && $type != 'unique' && $type != 'normal') {
1534 return DB_Table
::throwError(
1535 DB_TABLE_ERR_IDX_TYPE
,
1536 "'$idxname' ('$type')"
1546 * Return all indexes for a table.
1550 * @param object &$db A PEAR DB/MDB2 object.
1552 * @param string $table The table name.
1554 * @return mixed Array with all indexes or a PEAR_Error when an error
1559 function getIndexes(&$db, $table)
1561 if (is_subclass_of($db, 'db_common')) {
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
1571 } elseif (is_subclass_of($db, 'mdb2_driver_common')) {
1573 $manager =& $db->manager
;
1574 $reverse =& $db->reverse
;
1577 $indexes = array('normal' => array(),
1578 'primary' => array(),
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');
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);
1595 return $table_indexes_tmp;
1598 // get fields of table constraints
1599 foreach ($table_indexes_tmp as $table_idx_tmp) {
1600 $index_fields = $reverse->getTableConstraintDefinition($table,
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);
1607 return $index_fields;
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) {
1615 $indexes[$index_type][$table_idx_tmp] = array_keys($index_fields['fields']);
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);
1625 return $table_indexes_tmp;
1628 // get fields of table indexes
1629 foreach ($table_indexes_tmp as $table_idx_tmp) {
1630 $index_fields = $reverse->getTableIndexDefinition($table,
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);
1637 return $index_fields;
1639 $indexes['normal'][$table_idx_tmp] = array_keys($index_fields['fields']);
1642 // restore user defined 'idxname_format' option (MDB2 only)
1643 if ($backend == 'mdb2') {
1644 $db->setOption('idxname_format', $idxname_format);
1653 * Check whether an index has the right type and has all specified columns.
1657 * @param string $idxname The index name.
1659 * @param string $newIdxName The prefixed and suffixed index name.
1661 * @param string $type The index type.
1663 * @param mixed $cols The column names for the index.
1665 * @param mixed $table_indexes Array with all indexes of the table.
1667 * @param string $mode The name of the calling function, this can be either
1668 * 'verify' or 'alter'.
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').
1676 function _checkIndex($idxname, $newIdxName, $type, $cols, &$table_indexes, $mode)
1678 $index_found = false;
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) {
1696 if (!$index_found) {
1697 return ($mode == 'alter') ?
false : DB_Table
::throwError(
1698 DB_TABLE_ERR_VER_IDX_MISSING
,
1699 "'$idxname' ('$newIdxName')"
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)"
1718 * Create indexes and contraints.
1722 * @param object &$db A PEAR DB/MDB2 object.
1724 * @param string $backend The name of the backend ('db' or 'mdb2').
1726 * @param string $table The table name.
1728 * @param mixed $indexes An array with index and constraint definitions.
1730 * @return bool|object Boolean true on success or a PEAR_Error with a
1731 * description about the invalidity, otherwise.
1735 function _createIndexesAndContraints($db, $backend, $table, $indexes)
1737 if ($backend == 'mdb2') {
1739 // save user defined 'idxname_format' option
1740 $idxname_format = $db->getOption('idxname_format');
1741 $db->setOption('idxname_format', '%s');
1743 // attempt to create the primary key
1744 if (!array_key_exists('primary', $indexes)) {
1745 $indexes['primary'] = array();
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);
1756 // attempt to create the unique indexes / constraints
1757 if (!array_key_exists('unique', $indexes)) {
1758 $indexes['unique'] = array();
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);
1769 // attempt to create the normal indexes
1770 if (!array_key_exists('normal', $indexes)) {
1771 $indexes['normal'] = array();
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);
1782 // restore user defined 'idxname_format' option
1783 $db->setOption('idxname_format', $idxname_format);
1787 // attempt to create the indexes
1788 foreach ($indexes as $cmd) {
1789 $result = $db->query($cmd);
1790 if (PEAR
::isError($result)) {
1805 * List of all reserved words for all supported databases. Yes, this is a
1806 * monster of a list.
1808 if (! isset($GLOBALS['_DB_TABLE']['reserved'])) {
1809 $GLOBALS['_DB_TABLE']['reserved'] = array(
1903 'CURRENT_TIMESTAMP',
2082 'MINUTE_MICROSECOND',
2099 'NO_WRITE_TO_BINLOG',
2211 'SECOND_MICROSECOND',
2236 'SQL_CALC_FOUND_ROWS',
2239 'SQL_TSI_FRAC_SECOND',
2251 'SQLITE_TEMP_MASTER',