drupal-civicrm/sites/all/modules/civicrm/packages/DB/Table/Manager.php
2018-01-14 13:10:16 +00:00

2350 lines
68 KiB
PHP

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
/**
* Creates, checks or alters tables from DB_Table definitions.
*
* DB_Table_Manager provides database automated table creation
* facilities.
*
* PHP versions 4 and 5
*
* LICENSE:
*
* Copyright (c) 1997-2007, Paul M. Jones <pmjones@php.net>
* David C. Morse <morse@php.net>
* Mark Wiesemann <wiesemann@php.net>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * The names of the authors may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @category Database
* @package DB_Table
* @author Paul M. Jones <pmjones@php.net>
* @author David C. Morse <morse@php.net>
* @author Mark Wiesemann <wiesemann@php.net>
* @license http://opensource.org/licenses/bsd-license.php New BSD License
* @version CVS: $Id: Manager.php,v 1.40 2008/12/25 19:56:35 wiesemann Exp $
* @link http://pear.php.net/package/DB_Table
*/
require_once 'DB/Table.php';
/**
* Valid types for the different data types in the different DBMS.
*/
$GLOBALS['_DB_TABLE']['valid_type'] = array(
'fbsql' => array( // currently not supported
'boolean' => '',
'char' => '',
'varchar' => '',
'smallint' => '',
'integer' => '',
'bigint' => '',
'decimal' => '',
'single' => '',
'double' => '',
'clob' => '',
'date' => '',
'time' => '',
'timestamp' => ''
),
'ibase' => array(
'boolean' => array('char', 'integer', 'real', 'smallint'),
'char' => array('char', 'varchar'),
'varchar' => 'varchar',
'smallint' => array('integer', 'smallint'),
'integer' => 'integer',
'bigint' => array('bigint', 'integer'),
'decimal' => 'numeric',
'single' => array('double precision', 'float'),
'double' => 'double precision',
'clob' => 'blob',
'date' => 'date',
'time' => 'time',
'timestamp' => 'timestamp'
),
'mssql' => array( // currently not supported
'boolean' => '',
'char' => '',
'varchar' => '',
'smallint' => '',
'integer' => '',
'bigint' => '',
'decimal' => '',
'single' => '',
'double' => '',
'clob' => '',
'date' => '',
'time' => '',
'timestamp' => ''
),
'mysql' => array(
'boolean' => array('char', 'decimal', 'int', 'real', 'tinyint'),
'char' => array('char', 'string', 'varchar'),
'varchar' => array('char', 'string', 'varchar'),
'smallint' => array('smallint', 'int'),
'integer' => 'int',
'bigint' => array('int', 'bigint'),
'decimal' => array('decimal', 'real'),
'single' => array('double', 'real'),
'double' => array('double', 'real'),
'clob' => array('blob', 'longtext', 'tinytext', 'text', 'mediumtext'),
'date' => array('char', 'date', 'string'),
'time' => array('char', 'string', 'time'),
'timestamp' => array('char', 'datetime', 'string')
),
'mysqli' => array(
'boolean' => array('char', 'decimal', 'tinyint'),
'char' => array('char', 'varchar'),
'varchar' => array('char', 'varchar'),
'smallint' => array('smallint', 'int'),
'integer' => 'int',
'bigint' => array('int', 'bigint'),
'decimal' => 'decimal',
'single' => array('double', 'float'),
'double' => 'double',
'clob' => array('blob', 'longtext', 'tinytext', 'text', 'mediumtext'),
'date' => array('char', 'date', 'varchar'),
'time' => array('char', 'time', 'varchar'),
'timestamp' => array('char', 'datetime', 'varchar')
),
'oci8' => array(
'boolean' => 'number',
'char' => array('char', 'varchar2'),
'varchar' => 'varchar2',
'smallint' => 'number',
'integer' => 'number',
'bigint' => 'number',
'decimal' => 'number',
'single' => array('float', 'number'),
'double' => array('float', 'number'),
'clob' => 'clob',
'date' => array('char', 'date'),
'time' => array('char', 'date'),
'timestamp' => array('char', 'date')
),
'pgsql' => array(
'boolean' => array('bool', 'numeric'),
'char' => array('bpchar', 'varchar'),
'varchar' => 'varchar',
'smallint' => array('int2', 'int4'),
'integer' => 'int4',
'bigint' => array('int4', 'int8'),
'decimal' => 'numeric',
'single' => array('float4', 'float8'),
'double' => 'float8',
'clob' => array('oid', 'text'),
'date' => array('bpchar', 'date'),
'time' => array('bpchar', 'time'),
'timestamp' => array('bpchar', 'timestamp')
),
'sqlite' => array(
'boolean' => 'boolean',
'char' => 'char',
'varchar' => array('char', 'varchar'),
'smallint' => array('int', 'smallint'),
'integer' => array('int', 'integer'),
'bigint' => array('int', 'bigint'),
'decimal' => array('decimal', 'numeric'),
'single' => array('double', 'float'),
'double' => 'double',
'clob' => array('clob', 'longtext'),
'date' => 'date',
'time' => 'time',
'timestamp' => array('datetime', 'timestamp')
),
);
/**
* Mapping between DB_Table and MDB2 data types.
*/
$GLOBALS['_DB_TABLE']['mdb2_type'] = array(
'boolean' => 'boolean',
'char' => 'text',
'varchar' => 'text',
'smallint' => 'integer',
'integer' => 'integer',
'bigint' => 'integer',
'decimal' => 'decimal',
'single' => 'float',
'double' => 'float',
'clob' => 'clob',
'date' => 'date',
'time' => 'time',
'timestamp' => 'timestamp'
);
/**
* Creates, checks or alters tables from DB_Table definitions.
*
* DB_Table_Manager provides database automated table creation
* facilities.
*
* @category Database
* @package DB_Table
* @author Paul M. Jones <pmjones@php.net>
* @author David C. Morse <morse@php.net>
* @author Mark Wiesemann <wiesemann@php.net>
* @version Release: 1.5.6
* @link http://pear.php.net/package/DB_Table
*/
class DB_Table_Manager {
/**
*
* Create the table based on DB_Table column and index arrays.
*
* @static
*
* @access public
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $table The table name to connect to in the database.
*
* @param mixed $column_set A DB_Table $this->col array.
*
* @param mixed $index_set A DB_Table $this->idx array.
*
* @return mixed Boolean false if there was no attempt to create the
* table, boolean true if the attempt succeeded, and a PEAR_Error if
* the attempt failed.
*
*/
function create(&$db, $table, $column_set, $index_set)
{
if (is_subclass_of($db, 'db_common')) {
$backend = 'db';
} elseif (is_subclass_of($db, 'mdb2_driver_common')) {
$backend = 'mdb2';
$db->loadModule('Manager');
}
$phptype = $db->phptype;
// columns to be created
$column = array();
// max. value for scope (only used with MDB2 as backend)
$max_scope = 0;
// indexes to be created
$indexes = array();
// check the table name
$name_check = DB_Table_Manager::_validateTableName($table);
if (PEAR::isError($name_check)) {
return $name_check;
}
// -------------------------------------------------------------
//
// validate each column mapping and build the individual
// definitions, and note column indexes as we go.
//
if (is_null($column_set)) {
$column_set = array();
}
foreach ($column_set as $colname => $val) {
$colname = trim($colname);
// check the column name
$name_check = DB_Table_Manager::_validateColumnName($colname);
if (PEAR::isError($name_check)) {
return $name_check;
}
// prepare variables
$type = (isset($val['type'])) ? $val['type'] : null;
$size = (isset($val['size'])) ? $val['size'] : null;
$scope = (isset($val['scope'])) ? $val['scope'] : null;
$require = (isset($val['require'])) ? $val['require'] : null;
$default = (isset($val['default'])) ? $val['default'] : null;
if ($backend == 'mdb2') {
// get the declaration string
$result = DB_Table_Manager::getDeclareMDB2($type,
$size, $scope, $require, $default, $max_scope);
// did it work?
if (PEAR::isError($result)) {
$result->userinfo .= " ('$colname')";
return $result;
}
// add the declaration to the array of all columns
$column[$colname] = $result;
} else {
// get the declaration string
$result = DB_Table_Manager::getDeclare($phptype, $type,
$size, $scope, $require, $default);
// did it work?
if (PEAR::isError($result)) {
$result->userinfo .= " ('$colname')";
return $result;
}
// add the declaration to the array of all columns
$column[] = "$colname $result";
}
}
// -------------------------------------------------------------
//
// validate the indexes.
//
if (is_null($index_set)) {
$index_set = array();
}
$count_primary_keys = 0;
foreach ($index_set as $idxname => $val) {
list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
$newIdxName = '';
// check the index definition
$index_check = DB_Table_Manager::_validateIndexName($idxname,
$table, $phptype, $type, $cols, $column_set, $newIdxName);
if (PEAR::isError($index_check)) {
return $index_check;
}
// check number of primary keys (only one is allowed)
if ($type == 'primary') {
// SQLite does not support primary keys
if ($phptype == 'sqlite') {
return DB_Table::throwError(DB_TABLE_ERR_DECLARE_PRIM_SQLITE);
}
$count_primary_keys++;
}
if ($count_primary_keys > 1) {
return DB_Table::throwError(DB_TABLE_ERR_DECLARE_PRIMARY);
}
// create index entry
if ($backend == 'mdb2') {
// array with column names as keys
$idx_cols = array();
foreach ($cols as $col) {
$idx_cols[$col] = array();
}
switch ($type) {
case 'primary':
$indexes['primary'][$newIdxName] =
array('fields' => $idx_cols,
'primary' => true);
break;
case 'unique':
$indexes['unique'][$newIdxName] =
array('fields' => $idx_cols,
'unique' => true);
break;
case 'normal':
$indexes['normal'][$newIdxName] =
array('fields' => $idx_cols);
break;
}
} else {
$indexes[] = DB_Table_Manager::getDeclareForIndex($phptype,
$type, $newIdxName, $table, $cols);
}
}
// -------------------------------------------------------------
//
// now for the real action: create the table and indexes!
//
if ($backend == 'mdb2') {
// save user defined 'decimal_places' option
$decimal_places = $db->getOption('decimal_places');
$db->setOption('decimal_places', $max_scope);
// attempt to create the table
$result = $db->manager->createTable($table, $column);
// restore user defined 'decimal_places' option
$db->setOption('decimal_places', $decimal_places);
if (PEAR::isError($result)) {
return $result;
}
} else {
// build the CREATE TABLE command
$cmd = "CREATE TABLE $table (\n\t";
$cmd .= implode(",\n\t", $column);
$cmd .= "\n)";
// attempt to create the table
$result = $db->query($cmd);
if (PEAR::isError($result)) {
return $result;
}
}
$result = DB_Table_Manager::_createIndexesAndContraints($db, $backend,
$table, $indexes);
if (PEAR::isError($result)) {
return $result;
}
// we're done!
return true;
}
/**
*
* Verify whether the table and columns exist, whether the columns
* have the right type and whether the indexes exist.
*
* @static
*
* @access public
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $table The table name to connect to in the database.
*
* @param mixed $column_set A DB_Table $this->col array.
*
* @param mixed $index_set A DB_Table $this->idx array.
*
* @return mixed Boolean true if the verification was successful, and a
* PEAR_Error if verification failed.
*
*/
function verify(&$db, $table, $column_set, $index_set)
{
if (is_subclass_of($db, 'db_common')) {
$backend = 'db';
$reverse =& $db;
$table_info_mode = DB_TABLEINFO_FULL;
$table_info_error = DB_ERROR_NEED_MORE_DATA;
} elseif (is_subclass_of($db, 'mdb2_driver_common')) {
$backend = 'mdb2';
$reverse =& $this->db->loadModule('Reverse');
$table_info_mode = MDB2_TABLEINFO_FULL;
$table_info_error = MDB2_ERROR_NEED_MORE_DATA;
}
$phptype = $db->phptype;
// check #1: does the table exist?
// check the table name
$name_check = DB_Table_Manager::_validateTableName($table);
if (PEAR::isError($name_check)) {
return $name_check;
}
// get table info
$tableInfo = $reverse->tableInfo($table, $table_info_mode);
if (PEAR::isError($tableInfo)) {
if ($tableInfo->getCode() == $table_info_error) {
return DB_Table::throwError(
DB_TABLE_ERR_VER_TABLE_MISSING,
"(table='$table')"
);
}
return $tableInfo;
}
$tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER);
if (is_null($column_set)) {
$column_set = array();
}
foreach ($column_set as $colname => $val) {
$colname = strtolower(trim($colname));
// check the column name
$name_check = DB_Table_Manager::_validateColumnName($colname);
if (PEAR::isError($name_check)) {
return $name_check;
}
// check #2: do all columns exist?
$column_exists = DB_Table_Manager::_columnExists($colname,
$tableInfoOrder, 'verify');
if (PEAR::isError($column_exists)) {
return $column_exists;
}
// check #3: do all columns have the right type?
// check whether the column type is a known type
$type_check = DB_Table_Manager::_validateColumnType($phptype, $val['type']);
if (PEAR::isError($type_check)) {
return $type_check;
}
// check whether the column has the right type
$type_check = DB_Table_Manager::_checkColumnType($phptype,
$colname, $val['type'], $tableInfoOrder, $tableInfo, 'verify');
if (PEAR::isError($type_check)) {
return $type_check;
}
}
// check #4: do all indexes exist?
$table_indexes = DB_Table_Manager::getIndexes($db, $table);
if (PEAR::isError($table_indexes)) {
return $table_indexes;
}
if (is_null($index_set)) {
$index_set = array();
}
foreach ($index_set as $idxname => $val) {
list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
$newIdxName = '';
// check the index definition
$index_check = DB_Table_Manager::_validateIndexName($idxname,
$table, $phptype, $type, $cols, $column_set, $newIdxName);
if (PEAR::isError($index_check)) {
return $index_check;
}
// check whether the index has the right type and has all
// specified columns
$index_check = DB_Table_Manager::_checkIndex($idxname, $newIdxName,
$type, $cols, $table_indexes, 'verify');
if (PEAR::isError($index_check)) {
return $index_check;
}
}
return true;
}
/**
*
* Alter columns and indexes of a table based on DB_Table column and index
* arrays.
*
* @static
*
* @access public
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $table The table name to connect to in the database.
*
* @param mixed $column_set A DB_Table $this->col array.
*
* @param mixed $index_set A DB_Table $this->idx array.
*
* @return bool|object True if altering was successful or a PEAR_Error on
* failure.
*
*/
function alter(&$db, $table, $column_set, $index_set)
{
$phptype = $db->phptype;
if (is_subclass_of($db, 'db_common')) {
$backend = 'db';
$reverse =& $db;
// workaround for missing index and constraint information methods
// in PEAR::DB ==> use adopted code from MDB2's driver classes
require_once 'DB/Table/Manager/' . $phptype . '.php';
$classname = 'DB_Table_Manager_' . $phptype;
$dbtm = new $classname();
$dbtm->_db =& $db; // pass database instance to the 'workaround' class
$manager =& $dbtm;
$table_info_mode = DB_TABLEINFO_FULL;
$ok_const = DB_OK;
} elseif (is_subclass_of($db, 'mdb2_driver_common')) {
$backend = 'mdb2';
$db->loadModule('Reverse');
$manager =& $db->manager;
$reverse =& $db->reverse;
$table_info_mode = MDB2_TABLEINFO_FULL;
$ok_const = MDB2_OK;
}
// get table info
$tableInfo = $reverse->tableInfo($table, $table_info_mode);
if (PEAR::isError($tableInfo)) {
return $tableInfo;
}
$tableInfoOrder = array_change_key_case($tableInfo['order'], CASE_LOWER);
// emulate MDB2 Reverse extension for PEAR::DB as backend
if (is_subclass_of($db, 'db_common')) {
$reverse =& $dbtm;
}
// check (and alter) columns
if (is_null($column_set)) {
$column_set = array();
}
foreach ($column_set as $colname => $val) {
$colname = strtolower(trim($colname));
// check the column name
$name_check = DB_Table_Manager::_validateColumnName($colname);
if (PEAR::isError($name_check)) {
return $name_check;
}
// check the column's existence
$column_exists = DB_Table_Manager::_columnExists($colname,
$tableInfoOrder, 'alter');
if (PEAR::isError($column_exists)) {
return $column_exists;
}
if ($column_exists === false) { // add the column
$definition = DB_Table_Manager::_getColumnDefinition($backend,
$phptype, $val);
if (PEAR::isError($definition)) {
return $definition;
}
$changes = array('add' => array($colname => $definition));
if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
echo "(alter) New table field will be added ($colname):\n";
var_dump($changes);
echo "\n";
}
$result = $manager->alterTable($table, $changes, false);
if (PEAR::isError($result)) {
return $result;
}
continue;
}
// check whether the column type is a known type
$type_check = DB_Table_Manager::_validateColumnType($phptype, $val['type']);
if (PEAR::isError($type_check)) {
return $type_check;
}
// check whether the column has the right type
$type_check = DB_Table_Manager::_checkColumnType($phptype,
$colname, $val['type'], $tableInfoOrder, $tableInfo, 'alter');
if (PEAR::isError($type_check)) {
return $type_check;
}
if ($type_check === false) { // change the column type
$definition = DB_Table_Manager::_getColumnDefinition($backend,
$phptype, $val);
if (PEAR::isError($definition)) {
return $definition;
}
$changes = array('change' =>
array($colname => array('type' => null,
'definition' => $definition)));
if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
echo "(alter) Table field's type will be changed ($colname):\n";
var_dump($changes);
echo "\n";
}
$result = $manager->alterTable($table, $changes, false);
if (PEAR::isError($result)) {
return $result;
}
continue;
}
}
// get information about indexes / constraints
$table_indexes = DB_Table_Manager::getIndexes($db, $table);
if (PEAR::isError($table_indexes)) {
return $table_indexes;
}
// check (and alter) indexes / constraints
if (is_null($index_set)) {
$index_set = array();
}
foreach ($index_set as $idxname => $val) {
list($type, $cols) = DB_Table_Manager::_getIndexTypeAndColumns($val, $idxname);
$newIdxName = '';
// check the index definition
$index_check = DB_Table_Manager::_validateIndexName($idxname,
$table, $phptype, $type, $cols, $column_set, $newIdxName);
if (PEAR::isError($index_check)) {
return $index_check;
}
// check whether the index has the right type and has all
// specified columns
$index_check = DB_Table_Manager::_checkIndex($idxname, $newIdxName,
$type, $cols, $table_indexes, 'alter');
if (PEAR::isError($index_check)) {
return $index_check;
}
if ($index_check === false) { // (1) drop wrong index/constraint
// (2) add right index/constraint
if ($backend == 'mdb2') {
// save user defined 'idxname_format' option
$idxname_format = $db->getOption('idxname_format');
$db->setOption('idxname_format', '%s');
}
// drop index/constraint only if it exists
foreach (array('normal', 'unique', 'primary') as $idx_type) {
if (array_key_exists(strtolower($newIdxName),
$table_indexes[$idx_type])) {
if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
echo "(alter) Index/constraint will be deleted (name: '$newIdxName', type: '$idx_type').\n";
}
if ($idx_type == 'normal') {
$result = $manager->dropIndex($table, $newIdxName);
} else {
$result = $manager->dropConstraint($table, $newIdxName);
}
if (PEAR::isError($result)) {
if ($backend == 'mdb2') {
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
}
return $result;
}
break;
}
}
// prepare index/constraint definition
$indexes = array();
if ($backend == 'mdb2') {
// array with column names as keys
$idx_cols = array();
foreach ($cols as $col) {
$idx_cols[$col] = array();
}
switch ($type) {
case 'primary':
$indexes['primary'][$newIdxName] =
array('fields' => $idx_cols,
'primary' => true);
break;
case 'unique':
$indexes['unique'][$newIdxName] =
array('fields' => $idx_cols,
'unique' => true);
break;
case 'normal':
$indexes['normal'][$newIdxName] =
array('fields' => $idx_cols);
break;
}
} else {
$indexes[] = DB_Table_Manager::getDeclareForIndex($phptype,
$type, $newIdxName, $table, $cols);
}
// create index/constraint
if (array_key_exists('debug', $GLOBALS['_DB_TABLE'])) {
echo "(alter) New index/constraint will be created (name: '$newIdxName', type: '$type'):\n";
var_dump($indexes);
echo "\n";
}
$result = DB_Table_Manager::_createIndexesAndContraints(
$db, $backend, $table, $indexes);
if ($backend == 'mdb2') {
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
}
if (PEAR::isError($result)) {
return $result;
}
continue;
}
}
return true;
}
/**
*
* Check whether a table exists.
*
* @static
*
* @access public
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $table The table name that should be checked.
*
* @return bool|object True if the table exists, false if not, or a
* PEAR_Error on failure.
*
*/
function tableExists(&$db, $table)
{
if (is_subclass_of($db, 'db_common')) {
$list = $db->getListOf('tables');
} elseif (is_subclass_of($db, 'mdb2_driver_common')) {
$db->loadModule('Manager');
$list = $db->manager->listTables();
}
if (PEAR::isError($list)) {
return $list;
}
array_walk($list, create_function('&$value,$key',
'$value = trim(strtolower($value));'));
return in_array(strtolower($table), $list);
}
/**
*
* Get the column declaration string for a DB_Table column.
*
* @static
*
* @access public
*
* @param string $phptype The DB/MDB2 phptype key.
*
* @param string $coltype The DB_Table column type.
*
* @param int $size The size for the column (needed for string and
* decimal).
*
* @param int $scope The scope for the column (needed for decimal).
*
* @param bool $require True if the column should be NOT NULL, false
* allowed to be NULL.
*
* @param string $default The SQL calculation for a default value.
*
* @return string|object A declaration string on success, or a
* PEAR_Error on failure.
*
*/
function getDeclare($phptype, $coltype, $size = null, $scope = null,
$require = null, $default = null)
{
// validate char/varchar/decimal type declaration
$validation = DB_Table_Manager::_validateTypeDeclaration($coltype, $size,
$scope);
if (PEAR::isError($validation)) {
return $validation;
}
// map of column types and declarations for this RDBMS
$map = $GLOBALS['_DB_TABLE']['type'][$phptype];
// is it a recognized column type?
$types = array_keys($map);
if (! in_array($coltype, $types)) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_TYPE,
"('$coltype')"
);
}
// basic declaration
switch ($coltype) {
case 'char':
case 'varchar':
$declare = $map[$coltype] . "($size)";
break;
case 'decimal':
$declare = $map[$coltype] . "($size,$scope)";
break;
default:
$declare = $map[$coltype];
break;
}
// set the "NULL"/"NOT NULL" portion
$null = ' NULL';
if ($phptype == 'ibase') { // Firebird does not like 'NULL'
$null = ''; // in CREATE TABLE
}
if ($phptype == 'pgsql') { // PostgreSQL does not like 'NULL'
$null = ''; // in ALTER TABLE
}
$declare .= ($require) ? ' NOT NULL' : $null;
// set the "DEFAULT" portion
if ($default) {
switch ($coltype) {
case 'char':
case 'varchar':
case 'clob':
$declare .= " DEFAULT '$default'";
break;
default:
$declare .= " DEFAULT $default";
break;
}
}
// done
return $declare;
}
/**
*
* Get the column declaration string for a DB_Table column.
*
* @static
*
* @access public
*
* @param string $coltype The DB_Table column type.
*
* @param int $size The size for the column (needed for string and
* decimal).
*
* @param int $scope The scope for the column (needed for decimal).
*
* @param bool $require True if the column should be NOT NULL, false
* allowed to be NULL.
*
* @param string $default The SQL calculation for a default value.
*
* @param int $max_scope The maximal scope for all table column
* (pass-by-reference).
*
* @return string|object A MDB2 column definition array on success, or a
* PEAR_Error on failure.
*
*/
function getDeclareMDB2($coltype, $size = null, $scope = null,
$require = null, $default = null, &$max_scope)
{
// validate char/varchar/decimal type declaration
$validation = DB_Table_Manager::_validateTypeDeclaration($coltype, $size,
$scope);
if (PEAR::isError($validation)) {
return $validation;
}
// map of MDB2 column types
$map = $GLOBALS['_DB_TABLE']['mdb2_type'];
// is it a recognized column type?
$types = array_keys($map);
if (! in_array($coltype, $types)) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_TYPE,
"('$coltype')"
);
}
// build declaration array
$new_column = array(
'type' => $map[$coltype],
'notnull' => $require
);
if ($size) {
$new_column['length'] = $size;
}
// determine integer length to be used in MDB2
if (in_array($coltype, array('smallint', 'integer', 'bigint'))) {
switch ($coltype) {
case 'smallint':
$new_column['length'] = 2;
break;
case 'integer':
$new_column['length'] = 4;
break;
case 'bigint':
$new_column['length'] = 5;
break;
}
}
if ($scope) {
$max_scope = max($max_scope, $scope);
}
if ($default) {
$new_column['default'] = $default;
}
return $new_column;
}
/**
*
* Get the index declaration string for a DB_Table index.
*
* @static
*
* @access public
*
* @param string $phptype The DB phptype key.
*
* @param string $type The index type.
*
* @param string $idxname The index name.
*
* @param string $table The table name.
*
* @param mixed $cols Array with the column names for the index.
*
* @return string A declaration string.
*
*/
function getDeclareForIndex($phptype, $type, $idxname, $table, $cols)
{
// string of column names
$colstring = implode(', ', $cols);
switch ($type) {
case 'primary':
switch ($phptype) {
case 'ibase':
case 'oci8':
case 'pgsql':
$declare = "ALTER TABLE $table ADD";
$declare .= " CONSTRAINT $idxname";
$declare .= " PRIMARY KEY ($colstring)";
break;
case 'mysql':
case 'mysqli':
$declare = "ALTER TABLE $table ADD PRIMARY KEY";
$declare .= " ($colstring)";
break;
case 'sqlite':
// currently not possible
break;
}
break;
case 'unique':
$declare = "CREATE UNIQUE INDEX $idxname ON $table ($colstring)";
break;
case 'normal':
$declare = "CREATE INDEX $idxname ON $table ($colstring)";
break;
}
return $declare;
}
/**
*
* Return the definition array for a column.
*
* @access private
*
* @param string $backend The name of the backend ('db' or 'mdb2').
*
* @param string $phptype The DB/MDB2 phptype key.
*
* @param mixed $column A single DB_Table column definition array.
*
* @return mixed|object Declaration string (DB), declaration array (MDB2) or a
* PEAR_Error with a description about the invalidity, otherwise.
*
*/
function _getColumnDefinition($backend, $phptype, $column)
{
static $max_scope;
// prepare variables
$type = (isset($column['type'])) ? $column['type'] : null;
$size = (isset($column['size'])) ? $column['size'] : null;
$scope = (isset($column['scope'])) ? $column['scope'] : null;
$require = (isset($column['require'])) ? $column['require'] : null;
$default = (isset($column['default'])) ? $column['default'] : null;
if ($backend == 'db') {
return DB_Table_Manager::getDeclare($phptype, $type,
$size, $scope, $require, $default);
} else {
return DB_Table_Manager::getDeclareMDB2($type,
$size, $scope, $require, $default, $max_scope);
}
}
/**
*
* Check char/varchar/decimal type declarations for validity.
*
* @access private
*
* @param string $coltype The DB_Table column type.
*
* @param int $size The size for the column (needed for string and
* decimal).
*
* @param int $scope The scope for the column (needed for decimal).
*
* @return bool|object Boolean true if the type declaration is valid or a
* PEAR_Error with a description about the invalidity, otherwise.
*
*/
function _validateTypeDeclaration($coltype, $size, $scope)
{
// validate char and varchar: does it have a size?
if (($coltype == 'char' || $coltype == 'varchar') &&
($size < 1 || $size > 255) ) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_STRING,
"(size='$size')"
);
}
// validate decimal: does it have a size and scope?
if ($coltype == 'decimal' &&
($size < 1 || $size > 255 || $scope < 0 || $scope > $size)) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_DECIMAL,
"(size='$size' scope='$scope')"
);
}
return true;
}
/**
*
* Check a table name for validity.
*
* @access private
*
* @param string $tablename The table name.
*
* @return bool|object Boolean true if the table name is valid or a
* PEAR_Error with a description about the invalidity, otherwise.
*
*/
function _validateTableName($tablename)
{
// is the table name too long?
if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
&& strlen($tablename) > 30
) {
return DB_Table::throwError(
DB_TABLE_ERR_TABLE_STRLEN,
" ('$tablename')"
);
}
return true;
}
/**
*
* Check a column name for validity.
*
* @access private
*
* @param string $colname The column name.
*
* @return bool|object Boolean true if the column name is valid or a
* PEAR_Error with a description about the invalidity, otherwise.
*
*/
function _validateColumnName($colname)
{
// column name cannot be a reserved keyword
$reserved = in_array(
strtoupper($colname),
$GLOBALS['_DB_TABLE']['reserved']
);
if ($reserved) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_COLNAME,
" ('$colname')"
);
}
// column name must be no longer than 30 chars
if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
&& strlen($colname) > 30
) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_STRLEN,
"('$colname')"
);
}
return true;
}
/**
*
* Check whether a column exists.
*
* @access private
*
* @param string $colname The column name.
*
* @param mixed $tableInfoOrder Array with columns in the table (result
* from tableInfo(), shortened to key 'order').
*
* @param string $mode The name of the calling function, this can be either
* 'verify' or 'alter'.
*
* @return bool|object Boolean true if the column exists.
* Otherwise, either boolean false (case 'alter') or a PEAR_Error
* (case 'verify').
*
*/
function _columnExists($colname, $tableInfoOrder, $mode)
{
if (array_key_exists($colname, $tableInfoOrder)) {
return true;
}
switch ($mode) {
case 'alter':
return false;
case 'verify':
return DB_Table::throwError(
DB_TABLE_ERR_VER_COLUMN_MISSING,
"(column='$colname')"
);
}
}
/**
*
* Check whether a column type is a known type.
*
* @access private
*
* @param string $phptype The DB/MDB2 phptype key.
*
* @param string $type The column type.
*
* @return bool|object Boolean true if the column type is a known type
* or a PEAR_Error, otherwise.
*
*/
function _validateColumnType($phptype, $type)
{
// map of valid types for the current RDBMS
$map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
// is it a recognized column type?
$types = array_keys($map);
if (!in_array($type, $types)) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_TYPE,
"('" . $type . "')"
);
}
return true;
}
/**
*
* Check whether a column has the right type.
*
* @access private
*
* @param string $phptype The DB/MDB2 phptype key.
*
* @param string $colname The column name.
*
* @param string $coltype The column type.
*
* @param mixed $tableInfoOrder Array with columns in the table (result
* from tableInfo(), shortened to key 'order').
*
* @param mixed $tableInfo Array with information about the table (result
* from tableInfo()).
*
* @param string $mode The name of the calling function, this can be either
* 'verify' or 'alter'.
*
* @return bool|object Boolean true if the column has the right type.
* Otherwise, either boolean false (case 'alter') or a PEAR_Error
* (case 'verify').
*
*/
function _checkColumnType($phptype, $colname, $coltype, $tableInfoOrder,
$tableInfo, $mode)
{
// map of valid types for the current RDBMS
$map = $GLOBALS['_DB_TABLE']['valid_type'][$phptype];
// get the column type from tableInfo()
$colindex = $tableInfoOrder[$colname];
$type = strtolower($tableInfo[$colindex]['type']);
// workaround for possibly wrong detected column type (taken from MDB2)
if ($type == 'unknown' && ($phptype == 'mysql' || $phptype == 'mysqli')) {
$type = 'decimal';
}
// strip size information (e.g. NUMERIC(9,2) => NUMERIC) if given
if (($pos = strpos($type, '(')) !== false) {
$type = substr($type, 0, $pos);
}
// is the type valid for the given DB_Table column type?
if (in_array($type, (array)$map[$coltype])) {
return true;
}
switch ($mode) {
case 'alter':
return false;
case 'verify':
return DB_Table::throwError(
DB_TABLE_ERR_VER_COLUMN_TYPE,
"(column='$colname', type='$type')"
);
}
}
/**
*
* Return the index type and the columns belonging to this index.
*
* @access private
*
* @param mixed $idx_def The index definition.
*
* @return mixed Array with the index type and the columns belonging to
* this index.
*
*/
function _getIndexTypeAndColumns($idx_def, $idxname)
{
$type = '';
$cols = '';
if (is_string($idx_def)) {
// shorthand for index names: colname => index_type
$type = trim($idx_def);
$cols = trim($idxname);
} elseif (is_array($idx_def)) {
// normal: index_name => array('type' => ..., 'cols' => ...)
$type = (isset($idx_def['type'])) ? $idx_def['type'] : 'normal';
$cols = (isset($idx_def['cols'])) ? $idx_def['cols'] : null;
}
return array($type, $cols);
}
/**
*
* Check an index name for validity.
*
* @access private
*
* @param string $idxname The index name.
*
* @param string $table The table name.
*
* @param string $phptype The DB/MDB2 phptype key.
*
* @param string $type The index type.
*
* @param mixed $cols The column names for the index. Will become an array
* if it is not an array.
*
* @param mixed $column_set A DB_Table $this->col array.
*
* @param string $newIdxName The new index name (prefixed with the table
* name, suffixed with '_idx').
*
* @return bool|object Boolean true if the index name is valid or a
* PEAR_Error with a description about the invalidity, otherwise.
*
*/
function _validateIndexName($idxname, $table, $phptype, $type, &$cols,
$column_set, &$newIdxName)
{
// index name cannot be a reserved keyword
$reserved = in_array(
strtoupper($idxname),
$GLOBALS['_DB_TABLE']['reserved']
);
if ($reserved && !($type == 'primary' && $idxname == 'PRIMARY')) {
return DB_Table::throwError(
DB_TABLE_ERR_DECLARE_IDXNAME,
"('$idxname')"
);
}
// are there any columns for the index?
if (! $cols) {
return DB_Table::throwError(
DB_TABLE_ERR_IDX_NO_COLS,
"('$idxname')"
);
}
// are there any CLOB columns, or any columns that are not
// in the schema?
settype($cols, 'array');
$valid_cols = array_keys($column_set);
foreach ($cols as $colname) {
if (! in_array($colname, $valid_cols)) {
return DB_Table::throwError(
DB_TABLE_ERR_IDX_COL_UNDEF,
"'$idxname' ('$colname')"
);
}
if ($column_set[$colname]['type'] == 'clob') {
return DB_Table::throwError(
DB_TABLE_ERR_IDX_COL_CLOB,
"'$idxname' ('$colname')"
);
}
}
// we prefix all index names with the table name,
// and suffix all index names with '_idx'. this
// is to soothe PostgreSQL, which demands that index
// names not collide, even when they indexes are on
// different tables.
$newIdxName = $table . '_' . $idxname . '_idx';
// MySQL requires the primary key to be named 'primary', therefore let's
// ignore the user defined name
if (($phptype == 'mysql' || $phptype == 'mysqli') && $type == 'primary') {
$newIdxName = 'primary';
}
// now check the length; must be under 30 chars to
// soothe Oracle.
if ( $GLOBALS['_DB_TABLE']['disable_length_check'] === false
&& strlen($newIdxName) > 30
) {
return DB_Table::throwError(
DB_TABLE_ERR_IDX_STRLEN,
"'$idxname' ('$newIdxName')"
);
}
// check index type
if ($type != 'primary' && $type != 'unique' && $type != 'normal') {
return DB_Table::throwError(
DB_TABLE_ERR_IDX_TYPE,
"'$idxname' ('$type')"
);
}
return true;
}
/**
*
* Return all indexes for a table.
*
* @access public
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $table The table name.
*
* @return mixed Array with all indexes or a PEAR_Error when an error
* occured.
*
*/
function getIndexes(&$db, $table)
{
if (is_subclass_of($db, 'db_common')) {
$backend = 'db';
// workaround for missing index and constraint information methods
// in PEAR::DB ==> use adopted code from MDB2's driver classes
require_once 'DB/Table/Manager/' . $db->phptype . '.php';
$classname = 'DB_Table_Manager_' . $db->phptype;
$dbtm = new $classname();
$dbtm->_db =& $db; // pass database instance to the 'workaround' class
$manager =& $dbtm;
$reverse =& $dbtm;
} elseif (is_subclass_of($db, 'mdb2_driver_common')) {
$backend = 'mdb2';
$manager =& $db->manager;
$reverse =& $db->reverse;
}
$indexes = array('normal' => array(),
'primary' => array(),
'unique' => array()
);
// save user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$idxname_format = $db->getOption('idxname_format');
$db->setOption('idxname_format', '%s');
}
// get table constraints
$table_indexes_tmp = $manager->listTableConstraints($table);
if (PEAR::isError($table_indexes_tmp)) {
// restore user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$db->setOption('idxname_format', $idxname_format);
}
return $table_indexes_tmp;
}
// get fields of table constraints
foreach ($table_indexes_tmp as $table_idx_tmp) {
$index_fields = $reverse->getTableConstraintDefinition($table,
$table_idx_tmp);
if (PEAR::isError($index_fields)) {
// restore user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$db->setOption('idxname_format', $idxname_format);
}
return $index_fields;
}
// get the first key of $index_fields that has boolean true value
foreach ($index_fields as $index_type => $value) {
if ($value === true) {
break;
}
}
$indexes[$index_type][$table_idx_tmp] = array_keys($index_fields['fields']);
}
// get table indexes
$table_indexes_tmp = $manager->listTableIndexes($table);
if (PEAR::isError($table_indexes_tmp)) {
// restore user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$db->setOption('idxname_format', $idxname_format);
}
return $table_indexes_tmp;
}
// get fields of table indexes
foreach ($table_indexes_tmp as $table_idx_tmp) {
$index_fields = $reverse->getTableIndexDefinition($table,
$table_idx_tmp);
if (PEAR::isError($index_fields)) {
// restore user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$db->setOption('idxname_format', $idxname_format);
}
return $index_fields;
}
$indexes['normal'][$table_idx_tmp] = array_keys($index_fields['fields']);
}
// restore user defined 'idxname_format' option (MDB2 only)
if ($backend == 'mdb2') {
$db->setOption('idxname_format', $idxname_format);
}
return $indexes;
}
/**
*
* Check whether an index has the right type and has all specified columns.
*
* @access private
*
* @param string $idxname The index name.
*
* @param string $newIdxName The prefixed and suffixed index name.
*
* @param string $type The index type.
*
* @param mixed $cols The column names for the index.
*
* @param mixed $table_indexes Array with all indexes of the table.
*
* @param string $mode The name of the calling function, this can be either
* 'verify' or 'alter'.
*
* @return bool|object Boolean true if the index has the right type and all
* specified columns. Otherwise, either boolean false (case 'alter') or a
* PEAR_Error (case 'verify').
*
*/
function _checkIndex($idxname, $newIdxName, $type, $cols, &$table_indexes, $mode)
{
$index_found = false;
foreach ($table_indexes[$type] as $index_name => $index_fields) {
if (strtolower($index_name) == strtolower($newIdxName)) {
$index_found = true;
array_walk($cols, create_function('&$value,$key',
'$value = trim(strtolower($value));'));
array_walk($index_fields, create_function('&$value,$key',
'$value = trim(strtolower($value));'));
foreach ($index_fields as $index_field) {
if (($key = array_search($index_field, $cols)) !== false) {
unset($cols[$key]);
}
}
break;
}
}
if (!$index_found) {
return ($mode == 'alter') ? false : DB_Table::throwError(
DB_TABLE_ERR_VER_IDX_MISSING,
"'$idxname' ('$newIdxName')"
);
}
if (count($cols) > 0) {
// string of column names
$colstring = implode(', ', $cols);
return ($mode == 'alter') ? false : DB_Table::throwError(
DB_TABLE_ERR_VER_IDX_COL_MISSING,
"'$idxname' ($colstring)"
);
}
return true;
}
/**
*
* Create indexes and contraints.
*
* @access private
*
* @param object &$db A PEAR DB/MDB2 object.
*
* @param string $backend The name of the backend ('db' or 'mdb2').
*
* @param string $table The table name.
*
* @param mixed $indexes An array with index and constraint definitions.
*
* @return bool|object Boolean true on success or a PEAR_Error with a
* description about the invalidity, otherwise.
*
*/
function _createIndexesAndContraints($db, $backend, $table, $indexes)
{
if ($backend == 'mdb2') {
// save user defined 'idxname_format' option
$idxname_format = $db->getOption('idxname_format');
$db->setOption('idxname_format', '%s');
// attempt to create the primary key
if (!array_key_exists('primary', $indexes)) {
$indexes['primary'] = array();
}
foreach ($indexes['primary'] as $name => $definition) {
$result = $db->manager->createConstraint($table, $name, $definition);
if (PEAR::isError($result)) {
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
return $result;
}
}
// attempt to create the unique indexes / constraints
if (!array_key_exists('unique', $indexes)) {
$indexes['unique'] = array();
}
foreach ($indexes['unique'] as $name => $definition) {
$result = $db->manager->createConstraint($table, $name, $definition);
if (PEAR::isError($result)) {
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
return $result;
}
}
// attempt to create the normal indexes
if (!array_key_exists('normal', $indexes)) {
$indexes['normal'] = array();
}
foreach ($indexes['normal'] as $name => $definition) {
$result = $db->manager->createIndex($table, $name, $definition);
if (PEAR::isError($result)) {
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
return $result;
}
}
// restore user defined 'idxname_format' option
$db->setOption('idxname_format', $idxname_format);
} else {
// attempt to create the indexes
foreach ($indexes as $cmd) {
$result = $db->query($cmd);
if (PEAR::isError($result)) {
return $result;
}
}
}
return true;
}
}
/**
* List of all reserved words for all supported databases. Yes, this is a
* monster of a list.
*/
if (! isset($GLOBALS['_DB_TABLE']['reserved'])) {
$GLOBALS['_DB_TABLE']['reserved'] = array(
'_ROWID_',
'ABSOLUTE',
'ACCESS',
'ACTION',
'ADD',
'ADMIN',
'AFTER',
'AGGREGATE',
'ALIAS',
'ALL',
'ALLOCATE',
'ALTER',
'ANALYSE',
'ANALYZE',
'AND',
'ANY',
'ARE',
'ARRAY',
'AS',
'ASC',
'ASENSITIVE',
'ASSERTION',
'AT',
'AUDIT',
'AUTHORIZATION',
'AUTO_INCREMENT',
'AVG',
'BACKUP',
'BDB',
'BEFORE',
'BEGIN',
'BERKELEYDB',
'BETWEEN',
'BIGINT',
'BINARY',
'BIT',
'BIT_LENGTH',
'BLOB',
'BOOLEAN',
'BOTH',
'BREADTH',
'BREAK',
'BROWSE',
'BULK',
'BY',
'CALL',
'CASCADE',
'CASCADED',
'CASE',
'CAST',
'CATALOG',
'CHANGE',
'CHAR',
'CHAR_LENGTH',
'CHARACTER',
'CHARACTER_LENGTH',
'CHECK',
'CHECKPOINT',
'CLASS',
'CLOB',
'CLOSE',
'CLUSTER',
'CLUSTERED',
'COALESCE',
'COLLATE',
'COLLATION',
'COLUMN',
'COLUMNS',
'COMMENT',
'COMMIT',
'COMPLETION',
'COMPRESS',
'COMPUTE',
'CONDITION',
'CONNECT',
'CONNECTION',
'CONSTRAINT',
'CONSTRAINTS',
'CONSTRUCTOR',
'CONTAINS',
'CONTAINSTABLE',
'CONTINUE',
'CONVERT',
'CORRESPONDING',
'COUNT',
'CREATE',
'CROSS',
'CUBE',
'CURRENT',
'CURRENT_DATE',
'CURRENT_PATH',
'CURRENT_ROLE',
'CURRENT_TIME',
'CURRENT_TIMESTAMP',
'CURRENT_USER',
'CURSOR',
'CYCLE',
'DATA',
'DATABASE',
'DATABASES',
'DATE',
'DAY',
'DAY_HOUR',
'DAY_MICROSECOND',
'DAY_MINUTE',
'DAY_SECOND',
'DBCC',
'DEALLOCATE',
'DEC',
'DECIMAL',
'DECLARE',
'DEFAULT',
'DEFERRABLE',
'DEFERRED',
'DELAYED',
'DELETE',
'DENY',
'DEPTH',
'DEREF',
'DESC',
'DESCRIBE',
'DESCRIPTOR',
'DESTROY',
'DESTRUCTOR',
'DETERMINISTIC',
'DIAGNOSTICS',
'DICTIONARY',
'DISCONNECT',
'DISK',
'DISTINCT',
'DISTINCTROW',
'DISTRIBUTED',
'DIV',
'DO',
'DOMAIN',
'DOUBLE',
'DROP',
'DUMMY',
'DUMP',
'DYNAMIC',
'EACH',
'ELSE',
'ELSEIF',
'ENCLOSED',
'END',
'END-EXEC',
'EQUALS',
'ERRLVL',
'ESCAPE',
'ESCAPED',
'EVERY',
'EXCEPT',
'EXCEPTION',
'EXCLUSIVE',
'EXEC',
'EXECUTE',
'EXISTS',
'EXIT',
'EXPLAIN',
'EXTERNAL',
'EXTRACT',
'FALSE',
'FETCH',
'FIELDS',
'FILE',
'FILLFACTOR',
'FIRST',
'FLOAT',
'FOR',
'FORCE',
'FOREIGN',
'FOUND',
'FRAC_SECOND',
'FREE',
'FREETEXT',
'FREETEXTTABLE',
'FREEZE',
'FROM',
'FULL',
'FULLTEXT',
'FUNCTION',
'GENERAL',
'GET',
'GLOB',
'GLOBAL',
'GO',
'GOTO',
'GRANT',
'GROUP',
'GROUPING',
'HAVING',
'HIGH_PRIORITY',
'HOLDLOCK',
'HOST',
'HOUR',
'HOUR_MICROSECOND',
'HOUR_MINUTE',
'HOUR_SECOND',
'IDENTIFIED',
'IDENTITY',
'IDENTITY_INSERT',
'IDENTITYCOL',
'IF',
'IGNORE',
'ILIKE',
'IMMEDIATE',
'IN',
'INCREMENT',
'INDEX',
'INDICATOR',
'INFILE',
'INITIAL',
'INITIALIZE',
'INITIALLY',
'INNER',
'INNODB',
'INOUT',
'INPUT',
'INSENSITIVE',
'INSERT',
'INT',
'INTEGER',
'INTERSECT',
'INTERVAL',
'INTO',
'IO_THREAD',
'IS',
'ISNULL',
'ISOLATION',
'ITERATE',
'JOIN',
'KEY',
'KEYS',
'KILL',
'LANGUAGE',
'LARGE',
'LAST',
'LATERAL',
'LEADING',
'LEAVE',
'LEFT',
'LESS',
'LEVEL',
'LIKE',
'LIMIT',
'LINENO',
'LINES',
'LOAD',
'LOCAL',
'LOCALTIME',
'LOCALTIMESTAMP',
'LOCATOR',
'LOCK',
'LONG',
'LONGBLOB',
'LONGTEXT',
'LOOP',
'LOW_PRIORITY',
'LOWER',
'MAIN',
'MAP',
'MASTER_SERVER_ID',
'MATCH',
'MAX',
'MAXEXTENTS',
'MEDIUMBLOB',
'MEDIUMINT',
'MEDIUMTEXT',
'MIDDLEINT',
'MIN',
'MINUS',
'MINUTE',
'MINUTE_MICROSECOND',
'MINUTE_SECOND',
'MLSLABEL',
'MOD',
'MODE',
'MODIFIES',
'MODIFY',
'MODULE',
'MONTH',
'NAMES',
'NATIONAL',
'NATURAL',
'NCHAR',
'NCLOB',
'NEW',
'NEXT',
'NO',
'NO_WRITE_TO_BINLOG',
'NOAUDIT',
'NOCHECK',
'NOCOMPRESS',
'NONCLUSTERED',
'NONE',
'NOT',
'NOTNULL',
'NOWAIT',
'NULL',
'NULLIF',
'NUMBER',
'NUMERIC',
'OBJECT',
'OCTET_LENGTH',
'OF',
'OFF',
'OFFLINE',
'OFFSET',
'OFFSETS',
'OID',
'OLD',
'ON',
'ONLINE',
'ONLY',
'OPEN',
'OPENDATASOURCE',
'OPENQUERY',
'OPENROWSET',
'OPENXML',
'OPERATION',
'OPTIMIZE',
'OPTION',
'OPTIONALLY',
'OR',
'ORDER',
'ORDINALITY',
'OUT',
'OUTER',
'OUTFILE',
'OUTPUT',
'OVER',
'OVERLAPS',
'PAD',
'PARAMETER',
'PARAMETERS',
'PARTIAL',
'PATH',
'PCTFREE',
'PERCENT',
'PLACING',
'PLAN',
'POSITION',
'POSTFIX',
'PRECISION',
'PREFIX',
'PREORDER',
'PREPARE',
'PRESERVE',
'PRIMARY',
'PRINT',
'PRIOR',
'PRIVILEGES',
'PROC',
'PROCEDURE',
'PUBLIC',
'PURGE',
'RAISERROR',
'RAW',
'READ',
'READS',
'READTEXT',
'REAL',
'RECONFIGURE',
'RECURSIVE',
'REF',
'REFERENCES',
'REFERENCING',
'REGEXP',
'RELATIVE',
'RENAME',
'REPEAT',
'REPLACE',
'REPLICATION',
'REQUIRE',
'RESOURCE',
'RESTORE',
'RESTRICT',
'RESULT',
'RETURN',
'RETURNS',
'REVOKE',
'RIGHT',
'RLIKE',
'ROLE',
'ROLLBACK',
'ROLLUP',
'ROUTINE',
'ROW',
'ROWCOUNT',
'ROWGUIDCOL',
'ROWID',
'ROWNUM',
'ROWS',
'RULE',
'SAVE',
'SAVEPOINT',
'SCHEMA',
'SCOPE',
'SCROLL',
'SEARCH',
'SECOND',
'SECOND_MICROSECOND',
'SECTION',
'SELECT',
'SENSITIVE',
'SEPARATOR',
'SEQUENCE',
'SESSION',
'SESSION_USER',
'SET',
'SETS',
'SETUSER',
'SHARE',
'SHOW',
'SHUTDOWN',
'SIMILAR',
'SIZE',
'SMALLINT',
'SOME',
'SONAME',
'SPACE',
'SPATIAL',
'SPECIFIC',
'SPECIFICTYPE',
'SQL',
'SQL_BIG_RESULT',
'SQL_CALC_FOUND_ROWS',
'SQL_SMALL_RESULT',
'SQL_TSI_DAY',
'SQL_TSI_FRAC_SECOND',
'SQL_TSI_HOUR',
'SQL_TSI_MINUTE',
'SQL_TSI_MONTH',
'SQL_TSI_QUARTER',
'SQL_TSI_SECOND',
'SQL_TSI_WEEK',
'SQL_TSI_YEAR',
'SQLCODE',
'SQLERROR',
'SQLEXCEPTION',
'SQLITE_MASTER',
'SQLITE_TEMP_MASTER',
'SQLSTATE',
'SQLWARNING',
'SSL',
'START',
'STARTING',
'STATE',
'STATEMENT',
'STATIC',
'STATISTICS',
'STRAIGHT_JOIN',
'STRIPED',
'STRUCTURE',
'SUBSTRING',
'SUCCESSFUL',
'SUM',
'SYNONYM',
'SYSDATE',
'SYSTEM_USER',
'TABLE',
'TABLES',
'TEMPORARY',
'TERMINATE',
'TERMINATED',
'TEXTSIZE',
'THAN',
'THEN',
'TIME',
'TIMESTAMP',
'TIMESTAMPADD',
'TIMESTAMPDIFF',
'TIMEZONE_HOUR',
'TIMEZONE_MINUTE',
'TINYBLOB',
'TINYINT',
'TINYTEXT',
'TO',
'TOP',
'TRAILING',
'TRAN',
'TRANSACTION',
'TRANSLATE',
'TRANSLATION',
'TREAT',
'TRIGGER',
'TRIM',
'TRUE',
'TRUNCATE',
'TSEQUAL',
'UID',
'UNDER',
'UNDO',
'UNION',
'UNIQUE',
'UNKNOWN',
'UNLOCK',
'UNNEST',
'UNSIGNED',
'UPDATE',
'UPDATETEXT',
'UPPER',
'USAGE',
'USE',
'USER',
'USER_RESOURCES',
'USING',
'UTC_DATE',
'UTC_TIME',
'UTC_TIMESTAMP',
'VALIDATE',
'VALUE',
'VALUES',
'VARBINARY',
'VARCHAR',
'VARCHAR2',
'VARCHARACTER',
'VARIABLE',
'VARYING',
'VERBOSE',
'VIEW',
'WAITFOR',
'WHEN',
'WHENEVER',
'WHERE',
'WHILE',
'WITH',
'WITHOUT',
'WORK',
'WRITE',
'WRITETEXT',
'XOR',
'YEAR',
'YEAR_MONTH',
'ZEROFILL',
'ZONE',
);
}
?>