From b076a460e5418ae8f0db0b4b392d91853fd2a21b Mon Sep 17 00:00:00 2001 From: thomascube Date: Wed, 26 Oct 2005 22:12:36 +0000 Subject: Finished message sorting and fixed some skin issues --- program/lib/MDB2/Driver/Datatype/mysql.php | 543 +++++++++++++++++ program/lib/MDB2/Driver/Manager/mysql.php | 749 ++++++++++++++++++++++++ program/lib/MDB2/Driver/Native/mysql.php | 58 ++ program/lib/MDB2/Driver/Reverse/mysql.php | 298 ++++++++++ program/lib/MDB2/Driver/mysql.php | 911 +++++++++++++++++++++++++++++ 5 files changed, 2559 insertions(+) create mode 100644 program/lib/MDB2/Driver/Datatype/mysql.php create mode 100644 program/lib/MDB2/Driver/Manager/mysql.php create mode 100644 program/lib/MDB2/Driver/Native/mysql.php create mode 100644 program/lib/MDB2/Driver/Reverse/mysql.php create mode 100644 program/lib/MDB2/Driver/mysql.php (limited to 'program/lib/MDB2') diff --git a/program/lib/MDB2/Driver/Datatype/mysql.php b/program/lib/MDB2/Driver/Datatype/mysql.php new file mode 100644 index 000000000..f57867025 --- /dev/null +++ b/program/lib/MDB2/Driver/Datatype/mysql.php @@ -0,0 +1,543 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +require_once 'MDB2/Driver/Datatype/Common.php'; + +/** + * MDB2 MySQL driver + * + * @package MDB2 + * @category Database + * @author Lukas Smith + */ +class MDB2_Driver_Datatype_mysql extends MDB2_Driver_Datatype_Common +{ + // }}} + // {{{ _getIntegerDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an integer type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * unsigned + * Boolean flag that indicates whether the field + * should be declared as unsigned integer if + * possible. + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getIntegerDeclaration($name, $field) + { + if (array_key_exists('autoincrement', $field) && $field['autoincrement']) { + $autoinc = ' AUTO_INCREMENT PRIMARY KEY'; + $default = ''; + } else { + $autoinc = ''; + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'integer') : ''; + } + + $unsigned = (array_key_exists('unsigned', $field) && $field['unsigned']) ? ' UNSIGNED' : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' INT'.$unsigned.$default.$notnull.$autoinc; + } + + // }}} + // {{{ _getCLOBDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an character + * large object type field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the + * properties of the field being declared as array + * indexes. Currently, the types of supported field + * properties are as follows: + * + * length + * Integer value that determines the maximum length + * of the large object field. If this argument is + * missing the field should be declared to have the + * longest length allowed by the DBMS. + * + * notnull + * Boolean flag that indicates whether this field + * is constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getCLOBDeclaration($name, $field) + { + if (array_key_exists('length', $field)) { + $length = $field['length']; + if ($length <= 255) { + $type = 'TINYTEXT'; + } else { + if ($length <= 65535) { + $type = 'TEXT'; + } else { + if ($length <= 16777215) { + $type = 'MEDIUMTEXT'; + } else { + $type = 'LONGTEXT'; + } + } + } + } else { + $type = 'LONGTEXT'; + } + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' '.$type.$notnull; + } + + // }}} + // {{{ _getBLOBDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an binary large + * object type field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * length + * Integer value that determines the maximum length + * of the large object field. If this argument is + * missing the field should be declared to have the + * longest length allowed by the DBMS. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getBLOBDeclaration($name, $field) + { + if (array_key_exists('length', $field)) { + $length = $field['length']; + if ($length <= 255) { + $type = 'TINYBLOB'; + } else { + if ($length <= 65535) { + $type = 'BLOB'; + } else { + if ($length <= 16777215) { + $type = 'MEDIUMBLOB'; + } else { + $type = 'LONGBLOB'; + } + } + } + } else { + $type = 'LONGBLOB'; + } + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' '.$type.$notnull; + } + + // }}} + // {{{ _getDateDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an date type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field properties + * are as follows: + * + * default + * Date value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getDateDeclaration($name, $field) + { + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'date') : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' DATE'.$default.$notnull; + } + + // }}} + // {{{ _getTimestampDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an timestamp + * type field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * default + * Time stamp value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getTimestampDeclaration($name, $field) + { + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'timestamp') : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' DATETIME'.$default.$notnull; + } + + // }}} + // {{{ _getTimeDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an time type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * default + * Time value to be used as default for this field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getTimeDeclaration($name, $field) + { + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'time') : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' TIME'.$default.$notnull; + } + + // }}} + // {{{ _getFloatDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an float type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getFloatDeclaration($name, $field) + { + $type = 'DOUBLE'; + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'float') : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' '.$type.$default.$notnull; + } + + // }}} + // {{{ _getDecimalDeclaration() + + /** + * Obtain DBMS specific SQL code portion needed to declare an decimal type + * field to be used in statements like CREATE TABLE. + * + * @param string $name name the field to be declared. + * @param string $field associative array with the name of the properties + * of the field being declared as array indexes. + * Currently, the types of supported field + * properties are as follows: + * + * default + * Integer value to be used as default for this + * field. + * + * notnull + * Boolean flag that indicates whether this field is + * constrained to not be set to null. + * @return string DBMS specific SQL code portion that should be used to + * declare the specified field. + * @access protected + */ + function _getDecimalDeclaration($name, $field) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $type = 'DECIMAL(18,'.$db->options['decimal_places'].')'; + $default = array_key_exists('default', $field) ? ' DEFAULT '. + $this->quote($field['default'], 'decimal') : ''; + $notnull = (array_key_exists('notnull', $field) && $field['notnull']) ? ' NOT NULL' : ''; + return $name.' '.$type.$default.$notnull; + } + + // }}} + // {{{ _quoteBLOB() + + /** + * Convert a text value into a DBMS specific format that is suitable to + * compose query statements. + * + * @param $value + * @return string text string that represents the given argument value in + * a DBMS specific format. + * @access protected + */ + function _quoteBLOB($value) + { + $value = $this->_readFile($value); + return "'".addslashes($value)."'"; + } + + // }}} + // {{{ _quoteFloat() + + /** + * Convert a text value into a DBMS specific format that is suitable to + * compose query statements. + * + * @param string $value text string value that is intended to be converted. + * @return string text string that represents the given argument value in + * a DBMS specific format. + * @access protected + */ + function _quoteFloat($value) + { + return (float)$value; + } + + // }}} + // {{{ _quoteDecimal() + + /** + * Convert a text value into a DBMS specific format that is suitable to + * compose query statements. + * + * @param string $value text string value that is intended to be converted. + * @return string text string that represents the given argument value in + * a DBMS specific format. + * @access protected + */ + function _quoteDecimal($value) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + return $db->escape($value); + } + + // }}} + // {{{ mapNativeDatatype() + + /** + * Maps a native array description of a field to a MDB2 datatype and length + * + * @param array $field native field description + * @return array containing the various possible types and the length + * @access public + */ + function mapNativeDatatype($field) + { + $db_type = strtolower($field['type']); + $db_type = strtok($db_type, '(), '); + if ($db_type == 'national') { + $db_type = strtok('(), '); + } + $length = strtok('(), '); + $decimal = strtok('(), '); + $type = array(); + switch ($db_type) { + case 'tinyint': + case 'smallint': + case 'mediumint': + case 'int': + case 'integer': + case 'bigint': + $type[] = 'integer'; + if ($length == '1') { + $type[] = 'boolean'; + if (preg_match('/^[is|has]/', $field['field'])) { + $type = array_reverse($type); + } + } + break; + case 'char': + case 'varchar': + $type[] = 'text'; + if ($length == '1') { + $type[] = 'boolean'; + if (preg_match('/[is|has]/', $field['field'])) { + $type = array_reverse($type); + } + } + break; + case 'enum': + preg_match_all('/\'.+\'/U', $field['type'], $matches); + $length = 0; + if (is_array($matches)) { + foreach ($matches[0] as $value) { + $length = max($length, strlen($value)-2); + } + } + case 'set': + $type[] = 'text'; + $type[] = 'integer'; + break; + case 'date': + $type[] = 'date'; + $length = null; + break; + case 'datetime': + case 'timestamp': + $type[] = 'timestamp'; + $length = null; + break; + case 'time': + $type[] = 'time'; + $length = null; + break; + case 'float': + case 'double': + case 'real': + $type[] = 'float'; + break; + case 'decimal': + case 'numeric': + $type[] = 'decimal'; + break; + case 'tinytext': + case 'mediumtext': + case 'longtext': + case 'text': + if ($decimal == 'binary') { + $type[] = 'blob'; + } + $type[] = 'clob'; + $type[] = 'text'; + break; + case 'tinyblob': + case 'mediumblob': + case 'longblob': + case 'blob': + $type[] = 'blob'; + $type[] = 'text'; + $length = null; + break; + case 'year': + $type[] = 'integer'; + $type[] = 'date'; + $length = null; + break; + default: + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + return $db->raiseError(MDB2_ERROR, null, null, + 'getTableFieldDefinition: unknown database attribute type'); + } + + return array($type, $length); + } +} + +?> diff --git a/program/lib/MDB2/Driver/Manager/mysql.php b/program/lib/MDB2/Driver/Manager/mysql.php new file mode 100644 index 000000000..120467c77 --- /dev/null +++ b/program/lib/MDB2/Driver/Manager/mysql.php @@ -0,0 +1,749 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +require_once 'MDB2/Driver/Manager/Common.php'; + +/** + * MDB2 MySQL driver for the management modules + * + * @package MDB2 + * @category Database + * @author Lukas Smith + */ +class MDB2_Driver_Manager_mysql extends MDB2_Driver_Manager_Common +{ + // {{{ properties + var $verified_table_types = array();# + // }}} + + // }}} + // {{{ _verifyTableType() + + /** + * verify that chosen transactional table hanlder is available in the database + * + * @param string $table_type name of the table handler + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access protected + */ + function _verifyTableType($table_type) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + switch (strtoupper($table_type)) { + case 'BERKELEYDB': + case 'BDB': + $check = array('have_bdb'); + break; + case 'INNODB': + $check = array('have_innobase', 'have_innodb'); + break; + case 'GEMINI': + $check = array('have_gemini'); + break; + case 'HEAP': + case 'ISAM': + case 'MERGE': + case 'MRG_MYISAM': + case 'MYISAM': + case '': + return MDB2_OK; + default: + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $table_type.' is not a supported table type'); + } + if (isset($this->verified_table_types[$table_type]) + && $this->verified_table_types[$table_type] == $db->connection + ) { + return MDB2_OK; + } + $not_supported = false; + for ($i = 0, $j = count($check); $i < $j; ++$i) { + $query = 'SHOW VARIABLES LIKE '.$db->quote($check[$i], 'text'); + $has = $db->queryRow($query, null, MDB2_FETCHMODE_ORDERED); + if (PEAR::isError($has)) { + return $has; + } + if (is_array($has)) { + $not_supported = true; + if ($has[1] == 'YES') { + $this->verified_table_types[$table_type] = $db->connection; + return MDB2_OK; + } + } + } + if ($not_supported) { + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + $table_type.' is not a supported table type by this MySQL database server'); + } + return $db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'could not tell if '.$table_type.' is a supported table type'); + } + + // }}} + // {{{ createDatabase() + + /** + * create a new database + * + * @param string $name name of the database that should be created + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function createDatabase($name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $query = 'CREATE DATABASE '.$name; + $result = $db->query($query); + if (PEAR::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ dropDatabase() + + /** + * drop an existing database + * + * @param string $name name of the database that should be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function dropDatabase($name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $query = 'DROP DATABASE '.$name; + $result = $db->query($query); + if (PEAR::isError($result)) { + return $result; + } + return MDB2_OK; + } + + // }}} + // {{{ createTable() + + /** + * create a new table + * + * @param string $name Name of the database that should be created + * @param array $fields Associative array that contains the definition of each field of the new table + * The indexes of the array entries are the names of the fields of the table an + * the array entry values are associative arrays like those that are meant to be + * passed with the field definitions to get[Type]Declaration() functions. + * + * Example + * array( + * + * 'id' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * 'notnull' => 1 + * 'default' => 0 + * ), + * 'name' => array( + * 'type' => 'text', + * 'length' => 12 + * ), + * 'password' => array( + * 'type' => 'text', + * 'length' => 12 + * ) + * ); + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function createTable($name, $fields) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + if (!$name) { + return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, + 'createTable: no valid table name specified'); + } + if (empty($fields)) { + return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, + 'createTable: no fields specified for table "'.$name.'"'); + } + $verify = $this->_verifyTableType($db->options['default_table_type']); + if (PEAR::isError($verify)) { + return $verify; + } + $query_fields = $this->getFieldDeclarationList($fields); + if (PEAR::isError($query_fields)) { + return $db->raiseError(MDB2_ERROR_CANNOT_CREATE, null, null, + 'createTable: '.$this->getUserinfo()); + } + $query = "CREATE TABLE $name ($query_fields)".(strlen($db->options['default_table_type']) + ? ' TYPE='.$db->options['default_table_type'] : ''); + + return $db->query($query); + } + + // }}} + // {{{ alterTable() + + /** + * alter an existing table + * + * @param string $name name of the table that is intended to be changed. + * @param array $changes associative array that contains the details of each type + * of change that is intended to be performed. The types of + * changes that are currently supported are defined as follows: + * + * name + * + * New name for the table. + * + * add + * + * Associative array with the names of fields to be added as + * indexes of the array. The value of each entry of the array + * should be set to another associative array with the properties + * of the fields to be added. The properties of the fields should + * be the same as defined by the Metabase parser. + * + * + * remove + * + * Associative array with the names of fields to be removed as indexes + * of the array. Currently the values assigned to each entry are ignored. + * An empty array should be used for future compatibility. + * + * rename + * + * Associative array with the names of fields to be renamed as indexes + * of the array. The value of each entry of the array should be set to + * another associative array with the entry named name with the new + * field name and the entry named Declaration that is expected to contain + * the portion of the field declaration already in DBMS specific SQL code + * as it is used in the CREATE TABLE statement. + * + * change + * + * Associative array with the names of the fields to be changed as indexes + * of the array. Keep in mind that if it is intended to change either the + * name of a field and any other properties, the change array entries + * should have the new names of the fields as array indexes. + * + * The value of each entry of the array should be set to another associative + * array with the properties of the fields to that are meant to be changed as + * array entries. These entries should be assigned to the new values of the + * respective properties. The properties of the fields should be the same + * as defined by the Metabase parser. + * + * Example + * array( + * 'name' => 'userlist', + * 'add' => array( + * 'quota' => array( + * 'type' => 'integer', + * 'unsigned' => 1 + * ) + * ), + * 'remove' => array( + * 'file_limit' => array(), + * 'time_limit' => array() + * ), + * 'change' => array( + * 'gender' => array( + * 'default' => 'M', + * ) + * ), + * 'rename' => array( + * 'sex' => array( + * 'name' => 'gender', + * ) + * ) + * ) + * + * @param boolean $check indicates whether the function should just check if the DBMS driver + * can perform the requested table alterations if the value is true or + * actually perform them otherwise. + * @access public + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + */ + function alterTable($name, $changes, $check) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + foreach ($changes as $change_name => $change) { + switch ($change_name) { + case 'add': + case 'remove': + case 'change': + case 'rename': + case 'name': + break; + default: + return $db->raiseError(MDB2_ERROR_CANNOT_ALTER, null, null, + 'alterTable: change type "'.$change_name.'" not yet supported'); + } + } + + if ($check) { + return MDB2_OK; + } + + $query = (array_key_exists('name', $changes) ? 'RENAME AS '.$changes['name'] : ''); + + if (array_key_exists('add', $changes)) { + foreach ($changes['add'] as $field_name => $field) { + $type_declaration = $db->getDeclaration($field['type'], $field_name, $field); + if (PEAR::isError($type_declaration)) { + return $err; + } + if ($query) { + $query.= ', '; + } + $query.= 'ADD ' . $type_declaration; + } + } + + if (array_key_exists('remove', $changes)) { + foreach ($changes['remove'] as $field_name => $field) { + if ($query) { + $query.= ', '; + } + $query.= 'DROP ' . $field_name; + } + } + + $rename = array(); + if (array_key_exists('rename', $changes)) { + foreach ($changes['rename'] as $field_name => $field) { + $rename[$field['name']] = $field_name; + } + } + + if (array_key_exists('change', $changes)) { + foreach ($changes['change'] as $field_name => $field) { + if ($query) { + $query.= ', '; + } + if (isset($rename[$field_name])) { + $old_field_name = $rename[$field_name]; + unset($rename[$field_name]); + } else { + $old_field_name = $field_name; + } + $query.= "CHANGE $field_name " . $db->getDeclaration($field['type'], $old_field_name, $field); + } + } + + if (!empty($rename)) { + foreach ($rename as $rename_name => $renamed_field) { + if ($query) { + $query.= ', '; + } + $old_field_name = $renamed_field; + $field = $changes['rename'][$old_field_name]; + $query.= 'CHANGE ' . $db->getDeclaration($field['type'], $old_field_name, $field); + } + } + + if (!$query) { + return MDB2_OK; + } + + return $db->query("ALTER TABLE $name $query"); + } + + // }}} + // {{{ listDatabases() + + /** + * list all databases + * + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listDatabases() + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $databases = $db->queryCol('SHOW DATABASES'); + return $databases; + } + + // }}} + // {{{ listUsers() + + /** + * list all users + * + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listUsers() + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $users = $db->queryCol('SELECT DISTINCT USER FROM USER'); + return $users; + } + + // }}} + // {{{ listTables() + + /** + * list all tables in the current database + * + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listTables() + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $table_names = $db->queryCol('SHOW TABLES'); + if (PEAR::isError($table_names)) { + return $table_names; + } + + $tables = array(); + for ($i = 0, $j = count($table_names); $i < $j; ++$i) { + if (!$this->_isSequenceName($table_names[$i])) + $tables[] = $table_names[$i]; + } + + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + $tables = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $tables); + } + + return $tables; + } + + // }}} + // {{{ listTableFields() + + /** + * list all fields in a tables in the current database + * + * @param string $table name of table that should be used in method + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listTableFields($table) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $fields = $db->queryCol("SHOW COLUMNS FROM $table"); + if (PEAR::isError($fields)) { + return $fields; + } + + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + $fields = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $fields); + } + + return $fields; + } + + // }}} + // {{{ createIndex() + + /** + * get the stucture of a field into an array + * + * @param string $table name of the table on which the index is to be created + * @param string $name name of the index to be created + * @param array $definition associative array that defines properties of the index to be created. + * Currently, only one property named FIELDS is supported. This property + * is also an associative with the names of the index fields as array + * indexes. Each entry of this array is set to another type of associative + * array that specifies properties of the index that are specific to + * each field. + * + * Currently, only the sorting property is supported. It should be used + * to define the sorting direction of the index. It may be set to either + * ascending or descending. + * + * Not all DBMS support index sorting direction configuration. The DBMS + * drivers of those that do not support it ignore this property. Use the + * function supports() to determine whether the DBMS driver can manage indexes. + + * Example + * array( + * 'fields' => array( + * 'user_name' => array( + * 'sorting' => 'ascending' + * ), + * 'last_login' => array() + * ) + * ) + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function createIndex($table, $name, $definition) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + if (array_key_exists('primary', $definition) && $definition['primary']) { + $type = 'PRIMARY'; + $name = 'KEY'; + } elseif (array_key_exists('unique', $definition) && $definition['unique']) { + $type = 'UNIQUE'; + } else { + $type = 'INDEX'; + } + + $query = "ALTER TABLE $table ADD $type $name ("; + $query.= implode(', ', array_keys($definition['fields'])); + $query.= ')'; + + return $db->query($query); + } + + // }}} + // {{{ dropIndex() + + /** + * drop existing index + * + * @param string $table name of table that should be used in method + * @param string $name name of the index to be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function dropIndex($table, $name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + return $db->query("ALTER TABLE $table DROP INDEX $name"); + } + + // }}} + // {{{ listTableIndexes() + + /** + * list all indexes in a table + * + * @param string $table name of table that should be used in method + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listTableIndexes($table) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $key_name = 'Key_name'; + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $key_name = strtolower($key_name); + } else { + $key_name = strtoupper($key_name); + } + } + + $query = "SHOW INDEX FROM $table"; + $indexes_all = $db->queryCol($query, 'text', $key_name); + if (PEAR::isError($indexes_all)) { + return $indexes_all; + } + + $indexes = array_unique($indexes_all); + + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + $indexes = array_map(($db->options['field_case'] == CASE_LOWER ? 'strtolower' : 'strtoupper'), $indexes); + } + + return $indexes; + } + + // }}} + // {{{ createSequence() + + /** + * create sequence + * + * @param string $seq_name name of the sequence to be created + * @param string $start start value of the sequence; default is 1 + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function createSequence($seq_name, $start = 1) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $sequence_name = $db->getSequenceName($seq_name); + $seqcol_name = $db->options['seqcol_name']; + $result = $this->_verifyTableType($db->options['default_table_type']); + if (PEAR::isError($result)) { + return $result; + } + + $res = $db->query("CREATE TABLE $sequence_name". + "($seqcol_name INT NOT NULL AUTO_INCREMENT, PRIMARY KEY ($seqcol_name))". + (strlen($db->options['default_table_type']) ? ' TYPE='.$db->options['default_table_type'] : '') + ); + + if (PEAR::isError($res)) { + return $res; + } + + if ($start == 1) { + return MDB2_OK; + } + + $res = $db->query("INSERT INTO $sequence_name ($seqcol_name) VALUES (".($start-1).')'); + if (!PEAR::isError($res)) { + return MDB2_OK; + } + + // Handle error + $result = $db->query("DROP TABLE $sequence_name"); + if (PEAR::isError($result)) { + return $db->raiseError(MDB2_ERROR, null, null, + 'createSequence: could not drop inconsistent sequence table ('. + $result->getMessage().' ('.$result->getUserinfo().'))'); + } + + return $db->raiseError(MDB2_ERROR, null, null, + 'createSequence: could not create sequence table ('. + $res->getMessage().' ('.$res->getUserinfo().'))'); + } + + // }}} + // {{{ dropSequence() + + /** + * drop existing sequence + * + * @param string $seq_name name of the sequence to be dropped + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function dropSequence($seq_name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $sequence_name = $db->getSequenceName($seq_name); + return $db->query("DROP TABLE $sequence_name"); + } + + // }}} + // {{{ listSequences() + + /** + * list all sequences in the current database + * + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function listSequences() + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $table_names = $db->queryCol('SHOW TABLES'); + if (PEAR::isError($table_names)) { + return $table_names; + } + + $sequences = array(); + for ($i = 0, $j = count($table_names); $i < $j; ++$i) { + if ($sqn = $this->_isSequenceName($table_names[$i])) { + $sequences[] = $sqn; + } + } + + return $sequences; + } + + // }}} +} +?> \ No newline at end of file diff --git a/program/lib/MDB2/Driver/Native/mysql.php b/program/lib/MDB2/Driver/Native/mysql.php new file mode 100644 index 000000000..53970af1c --- /dev/null +++ b/program/lib/MDB2/Driver/Native/mysql.php @@ -0,0 +1,58 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +/** + * MDB2 MySQL driver for the native module + * + * @package MDB2 + * @category Database + * @author Lukas Smith + */ +class MDB2_Driver_Native_mysql extends MDB2_Module_Common +{ +} +?> \ No newline at end of file diff --git a/program/lib/MDB2/Driver/Reverse/mysql.php b/program/lib/MDB2/Driver/Reverse/mysql.php new file mode 100644 index 000000000..c17e4f6d4 --- /dev/null +++ b/program/lib/MDB2/Driver/Reverse/mysql.php @@ -0,0 +1,298 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +require_once 'MDB2/Driver/Reverse/Common.php'; + +/** + * MDB2 MySQL driver for the schema reverse engineering module + * + * @package MDB2 + * @category Database + * @author Lukas Smith + */ +class MDB2_Driver_Reverse_mysql extends MDB2_Driver_Reverse_Common +{ + // {{{ getTableFieldDefinition() + + /** + * get the stucture of a field into an array + * + * @param string $table name of table that should be used in method + * @param string $field_name name of field that should be used in method + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function getTableFieldDefinition($table, $field_name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $result = $db->loadModule('Datatype'); + if (PEAR::isError($result)) { + return $result; + } + $columns = $db->queryAll("SHOW COLUMNS FROM $table", null, MDB2_FETCHMODE_ASSOC); + if (PEAR::isError($columns)) { + return $columns; + } + foreach ($columns as $column) { + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $column['field'] = strtolower($column['field']); + } else { + $column['field'] = strtoupper($column['field']); + } + } else { + $column = array_change_key_case($column, $db->options['field_case']); + } + if ($field_name == $column['field']) { + list($types, $length) = $db->datatype->mapNativeDatatype($column); + $notnull = false; + if (array_key_exists('null', $column) && $column['null'] != 'YES') { + $notnull = true; + } + $default = false; + if (array_key_exists('default', $column)) { + $default = $column['default']; + if (is_null($default) && $notnull) { + $default = ''; + } + } + $autoincrement = false; + if (array_key_exists('extra', $column) && $column['extra'] == 'auto_increment') { + $autoincrement = true; + } + $definition = array(); + foreach ($types as $key => $type) { + $definition[$key] = array( + 'type' => $type, + 'notnull' => $notnull, + ); + if ($length > 0) { + $definition[$key]['length'] = $length; + } + if ($default !== false) { + $definition[$key]['default'] = $default; + } + if ($autoincrement !== false) { + $definition[$key]['autoincrement'] = $autoincrement; + } + } + return $definition; + } + } + + return $db->raiseError(MDB2_ERROR, null, null, + 'getTableFieldDefinition: it was not specified an existing table column'); + } + + // }}} + // {{{ getTableIndexDefinition() + + /** + * get the stucture of an index into an array + * + * @param string $table name of table that should be used in method + * @param string $index_name name of index that should be used in method + * @return mixed data array on success, a MDB2 error on failure + * @access public + */ + function getTableIndexDefinition($table, $index_name) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + $result = $db->query("SHOW INDEX FROM $table"); + if (PEAR::isError($result)) { + return $result; + } + $definition = array(); + while (is_array($row = $result->fetchRow(MDB2_FETCHMODE_ASSOC))) { + if (!($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) + || $db->options['field_case'] != CASE_LOWER + ) { + $row = array_change_key_case($row, CASE_LOWER); + } + $key_name = $row['key_name']; + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $key_name = strtolower($key_name); + } else { + $key_name = strtoupper($key_name); + } + } + if ($index_name == $key_name) { + if ($row['key_name'] == 'PRIMARY') { + $definition['primary'] = true; + } elseif (!$row['non_unique']) { + $definition['unique'] = true; + } + $column_name = $row['column_name']; + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $column_name = strtolower($column_name); + } else { + $column_name = strtoupper($column_name); + } + } + $definition['fields'][$column_name] = array(); + if (array_key_exists('collation', $row)) { + $definition['fields'][$column_name]['sorting'] = ($row['collation'] == 'A' + ? 'ascending' : 'descending'); + } + } + } + $result->free(); + if (!array_key_exists('fields', $definition)) { + return $db->raiseError(MDB2_ERROR, null, null, + 'getTableIndexDefinition: it was not specified an existing table index'); + } + return $definition; + } + + // }}} + // {{{ tableInfo() + + /** + * Returns information about a table or a result set + * + * @param object|string $result MDB2_result object from a query or a + * string containing the name of a table. + * While this also accepts a query result + * resource identifier, this behavior is + * deprecated. + * @param int $mode a valid tableInfo mode + * + * @return array an associative array with the information requested. + * A MDB2_Error object on failure. + * + * @see MDB2_Driver_Common::setOption() + */ + function tableInfo($result, $mode = null) + { + $db =& $this->getDBInstance(); + if (PEAR::isError($db)) { + return $db; + } + + if (is_string($result)) { + /* + * Probably received a table name. + * Create a result resource identifier. + */ + $connected = $db->connect(); + if (PEAR::isError($connected)) { + return $connected; + } + $id = @mysql_list_fields($db->database_name, $result, $db->connection); + $got_string = true; + } elseif (MDB2::isResultCommon($result)) { + /* + * Probably received a result object. + * Extract the result resource identifier. + */ + $id = $result->getResource(); + $got_string = false; + } else { + /* + * Probably received a result resource identifier. + * Copy it. + * Deprecated. Here for compatibility only. + */ + $id = $result; + $got_string = false; + } + + if (!is_resource($id)) { + return $db->raiseError(MDB2_ERROR_NEED_MORE_DATA); + } + + if ($db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + if ($db->options['field_case'] == CASE_LOWER) { + $case_func = 'strtolower'; + } else { + $case_func = 'strtoupper'; + } + } else { + $case_func = 'strval'; + } + + $count = @mysql_num_fields($id); + $res = array(); + + if ($mode) { + $res['num_fields'] = $count; + } + + for ($i = 0; $i < $count; $i++) { + $res[$i] = array( + 'table' => $case_func(@mysql_field_table($id, $i)), + 'name' => $case_func(@mysql_field_name($id, $i)), + 'type' => @mysql_field_type($id, $i), + 'len' => @mysql_field_len($id, $i), + 'flags' => @mysql_field_flags($id, $i), + ); + if ($mode & MDB2_TABLEINFO_ORDER) { + $res['order'][$res[$i]['name']] = $i; + } + if ($mode & MDB2_TABLEINFO_ORDERTABLE) { + $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; + } + } + + // free the result only if we were called on a table + if ($got_string) { + @mysql_free_result($id); + } + return $res; + } +} +?> \ No newline at end of file diff --git a/program/lib/MDB2/Driver/mysql.php b/program/lib/MDB2/Driver/mysql.php new file mode 100644 index 000000000..387512c8c --- /dev/null +++ b/program/lib/MDB2/Driver/mysql.php @@ -0,0 +1,911 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id$ +// + +/** + * MDB2 MySQL driver + * + * @package MDB2 + * @category Database + * @author Lukas Smith + */ +class MDB2_Driver_mysql extends MDB2_Driver_Common +{ + // {{{ properties + var $escape_quotes = "\\"; + + // }}} + // {{{ constructor + + /** + * Constructor + */ + function __construct() + { + parent::__construct(); + + $this->phptype = 'mysql'; + $this->dbsyntax = 'mysql'; + + $this->supported['sequences'] = 'emulated'; + $this->supported['indexes'] = true; + $this->supported['affected_rows'] = true; + $this->supported['transactions'] = false; + $this->supported['summary_functions'] = true; + $this->supported['order_by_text'] = true; + $this->supported['current_id'] = 'emulated'; + $this->supported['limit_queries'] = true; + $this->supported['LOBs'] = true; + $this->supported['replace'] = true; + $this->supported['sub_selects'] = 'emulated'; + $this->supported['auto_increment'] = true; + $this->supported['primary_key'] = true; + + $this->options['default_table_type'] = null; + } + + // }}} + // {{{ errorInfo() + + /** + * This method is used to collect information about an error + * + * @param integer $error + * @return array + * @access public + */ + function errorInfo($error = null) + { + if ($this->connection) { + $native_code = @mysql_errno($this->connection); + $native_msg = @mysql_error($this->connection); + } else { + $native_code = @mysql_errno(); + $native_msg = @mysql_error(); + } + if (is_null($error)) { + static $ecode_map; + if (empty($ecode_map)) { + $ecode_map = array( + 1004 => MDB2_ERROR_CANNOT_CREATE, + 1005 => MDB2_ERROR_CANNOT_CREATE, + 1006 => MDB2_ERROR_CANNOT_CREATE, + 1007 => MDB2_ERROR_ALREADY_EXISTS, + 1008 => MDB2_ERROR_CANNOT_DROP, + 1022 => MDB2_ERROR_ALREADY_EXISTS, + 1044 => MDB2_ERROR_ACCESS_VIOLATION, + 1046 => MDB2_ERROR_NODBSELECTED, + 1048 => MDB2_ERROR_CONSTRAINT, + 1049 => MDB2_ERROR_NOSUCHDB, + 1050 => MDB2_ERROR_ALREADY_EXISTS, + 1051 => MDB2_ERROR_NOSUCHTABLE, + 1054 => MDB2_ERROR_NOSUCHFIELD, + 1061 => MDB2_ERROR_ALREADY_EXISTS, + 1062 => MDB2_ERROR_ALREADY_EXISTS, + 1064 => MDB2_ERROR_SYNTAX, + 1091 => MDB2_ERROR_NOT_FOUND, + 1100 => MDB2_ERROR_NOT_LOCKED, + 1136 => MDB2_ERROR_VALUE_COUNT_ON_ROW, + 1142 => MDB2_ERROR_ACCESS_VIOLATION, + 1146 => MDB2_ERROR_NOSUCHTABLE, + 1216 => MDB2_ERROR_CONSTRAINT, + 1217 => MDB2_ERROR_CONSTRAINT, + ); + } + if ($this->options['portability'] & MDB2_PORTABILITY_ERRORS) { + $ecode_map[1022] = MDB2_ERROR_CONSTRAINT; + $ecode_map[1048] = MDB2_ERROR_CONSTRAINT_NOT_NULL; + $ecode_map[1062] = MDB2_ERROR_CONSTRAINT; + } else { + // Doing this in case mode changes during runtime. + $ecode_map[1022] = MDB2_ERROR_ALREADY_EXISTS; + $ecode_map[1048] = MDB2_ERROR_CONSTRAINT; + $ecode_map[1062] = MDB2_ERROR_ALREADY_EXISTS; + } + if (isset($ecode_map[$native_code])) { + $error = $ecode_map[$native_code]; + } + } + return array($error, $native_code, $native_msg); + } + + // }}} + // {{{ escape() + + /** + * Quotes a string so it can be safely used in a query. It will quote + * the text so it can safely be used within a query. + * + * @param string $text the input string to quote + * @return string quoted string + * @access public + */ + function escape($text) + { + return @mysql_real_escape_string($text); + } + + // }}} + // {{{ quoteIdentifier() + + /** + * Quote a string so it can be safely used as a table or column name + * + * Quoting style depends on which database driver is being used. + * + * MySQL can't handle the backtick character (`) in + * table or column names. + * + * @param string $str identifier name to be quoted + * + * @return string quoted identifier string + * + * @access public + * @internal + */ + function quoteIdentifier($str) + { + return '`' . $str . '`'; + } + + // }}} + // {{{ beginTransaction() + + /** + * Start a transaction. + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function beginTransaction() + { + $this->debug('starting transaction', 'beginTransaction'); + if (!$this->supports('transactions')) { + return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'beginTransaction: transactions are not in use'); + } + if ($this->in_transaction) { + return MDB2_OK; //nothing to do + } + if (!$this->destructor_registered && $this->opened_persistent) { + $this->destructor_registered = true; + register_shutdown_function('MDB2_closeOpenTransactions'); + } + $result = $this->_doQuery('SET AUTOCOMMIT = 0', true); + if (PEAR::isError($result)) { + return $result; + } + $this->in_transaction = true; + return MDB2_OK; + } + + // }}} + // {{{ commit() + + /** + * Commit the database changes done during a transaction that is in + * progress. + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function commit() + { + $this->debug('commit transaction', 'commit'); + if (!$this->supports('transactions')) { + return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'commit: transactions are not in use'); + } + if (!$this->in_transaction) { + return $this->raiseError(MDB2_ERROR, null, null, + 'commit: transaction changes are being auto committed'); + } + $result = $this->_doQuery('COMMIT', true); + if (PEAR::isError($result)) { + return $result; + } + $result = $this->_doQuery('SET AUTOCOMMIT = 1', true); + if (PEAR::isError($result)) { + return $result; + } + $this->in_transaction = false; + return MDB2_OK; + } + + // }}} + // {{{ rollback() + + /** + * Cancel any database changes done during a transaction that is in + * progress. + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function rollback() + { + $this->debug('rolling back transaction', 'rollback'); + if (!$this->supports('transactions')) { + return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null, + 'rollback: transactions are not in use'); + } + if (!$this->in_transaction) { + return $this->raiseError(MDB2_ERROR, null, null, + 'rollback: transactions can not be rolled back when changes are auto committed'); + } + $result = $this->_doQuery('ROLLBACK', true); + if (PEAR::isError($result)) { + return $result; + } + $result = $this->_doQuery('SET AUTOCOMMIT = 1', true); + if (PEAR::isError($result)) { + return $result; + } + $this->in_transaction = false; + return MDB2_OK; + + } + + // }}} + // {{{ connect() + + /** + * Connect to the database + * + * @return true on success, MDB2 Error Object on failure + */ + function connect() + { + if (is_resource($this->connection)) { + if (count(array_diff($this->connected_dsn, $this->dsn)) == 0 + && $this->opened_persistent == $this->options['persistent'] + ) { + return MDB2_OK; + } + $this->disconnect(false); + } + + if (!PEAR::loadExtension($this->phptype)) { + return $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null, + 'connect: extension '.$this->phptype.' is not compiled into PHP'); + } + + $params = array(); + if ($this->dsn['protocol'] && $this->dsn['protocol'] == 'unix') { + $params[0] = ':' . $this->dsn['socket']; + } else { + $params[0] = $this->dsn['hostspec'] ? $this->dsn['hostspec'] + : 'localhost'; + if ($this->dsn['port']) { + $params[0].= ':' . $this->dsn['port']; + } + } + $params[] = $this->dsn['username'] ? $this->dsn['username'] : null; + $params[] = $this->dsn['password'] ? $this->dsn['password'] : null; + if (!$this->options['persistent']) { + if (isset($this->dsn['new_link']) + && ($this->dsn['new_link'] == 'true' || $this->dsn['new_link'] === true) + ) { + $params[] = true; + } else { + $params[] = false; + } + } + if (version_compare(phpversion(), '4.3.0', '>=')) { + $params[] = isset($this->dsn['client_flags']) + ? $this->dsn['client_flags'] : null; + } + + $connect_function = $this->options['persistent'] ? 'mysql_pconnect' : 'mysql_connect'; + + @ini_set('track_errors', true); + $php_errormsg = ''; + $connection = @call_user_func_array($connect_function, $params); + @ini_restore('track_errors'); + if (!$connection) { + if (($err = @mysql_error()) != '') { + return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, $err); + } else { + return $this->raiseError(MDB2_ERROR_CONNECT_FAILED, null, null, $php_errormsg); + } + } + + $this->connection = $connection; + $this->connected_dsn = $this->dsn; + $this->connected_database_name = ''; + $this->opened_persistent = $this->options['persistent']; + $this->dbsyntax = $this->dsn['dbsyntax'] ? $this->dsn['dbsyntax'] : $this->phptype; + + $this->supported['transactions'] = false; + if ($this->options['default_table_type']) { + switch (strtoupper($this->options['default_table_type'])) { + case 'BERKELEYDB': + $this->options['default_table_type'] = 'BDB'; + case 'BDB': + case 'INNODB': + case 'GEMINI': + $this->supported['transactions'] = true; + break; + case 'HEAP': + case 'ISAM': + case 'MERGE': + case 'MRG_MYISAM': + case 'MYISAM': + break; + default: + $this->warnings[] = $default_table_type. + ' is not a supported default table type'; + } + } + + if ($this->options['use_transactions'] && !$this->supports('transactions')) { + $this->warnings[] = $this->options['default_table_type']. + ' is not a transaction-safe default table type; switched to INNODB'; + $this->options['default_table_type'] = 'INNODB'; + $this->supported['transactions'] = true; + } + + return MDB2_OK; + } + + // }}} + // {{{ disconnect() + + /** + * Log out and disconnect from the database. + * + * @return mixed true on success, false if not connected and error + * object on error + * @access public + */ + function disconnect($force = true) + { + if (is_resource($this->connection)) { + if (!$this->opened_persistent || $force) { + @mysql_close($this->connection); + } + $this->connection = 0; + } + return MDB2_OK; + } + + // }}} + // {{{ _doQuery() + + /** + * Execute a query + * @param string $query query + * @param boolean $isManip if the query is a manipulation query + * @param resource $connection + * @param string $database_name + * @return result or error object + * @access protected + */ + function _doQuery($query, $isManip = false, $connection = null, $database_name = null) + { + $this->last_query = $query; + $this->debug($query, 'query'); + if ($this->options['disable_query']) { + if ($isManip) { + return 0; + } + return null; + } + + if (is_null($connection)) { + $err = $this->connect(); + if (PEAR::isError($err)) { + return $err; + } + $connection = $this->connection; + } + if (is_null($database_name)) { + $database_name = $this->database_name; + } + + if ($database_name) { + if ($database_name != $this->connected_database_name) { + if (!@mysql_select_db($database_name, $connection)) { + return $this->raiseError(); + } + $this->connected_database_name = $database_name; + } + } + + $function = $this->options['result_buffering'] + ? 'mysql_query' : 'mysql_unbuffered_query'; + $result = @$function($query, $connection); + if (!$result) { + return $this->raiseError(); + } + + if ($isManip) { + return @mysql_affected_rows($connection); + } + return $result; + } + + // }}} + // {{{ _modifyQuery() + + /** + * Changes a query string for various DBMS specific reasons + * + * @param string $query query to modify + * @return the new (modified) query + * @access protected + */ + function _modifyQuery($query, $isManip, $limit, $offset) + { + if ($this->options['portability'] & MDB2_PORTABILITY_DELETE_COUNT) { + // "DELETE FROM table" gives 0 affected rows in MySQL. + // This little hack lets you know how many rows were deleted. + if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) { + $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/', + 'DELETE FROM \1 WHERE 1=1', $query); + } + } + if ($limit > 0 + && !preg_match('/LIMIT\s*\d(\s*(,|OFFSET)\s*\d+)?/i', $query) + ) { + $query = rtrim($query); + if (substr($query, -1) == ';') { + $query = substr($query, 0, -1); + } + if ($isManip) { + return $query . " LIMIT $limit"; + } else { + return $query . " LIMIT $offset, $limit"; + } + } + return $query; + } + + // }}} + // {{{ replace() + + /** + * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT + * query, except that if there is already a row in the table with the same + * key field values, the REPLACE query just updates its values instead of + * inserting a new row. + * + * The REPLACE type of query does not make part of the SQL standards. Since + * practically only MySQL implements it natively, this type of query is + * emulated through this method for other DBMS using standard types of + * queries inside a transaction to assure the atomicity of the operation. + * + * @access public + * + * @param string $table name of the table on which the REPLACE query will + * be executed. + * @param array $fields associative array that describes the fields and the + * values that will be inserted or updated in the specified table. The + * indexes of the array are the names of all the fields of the table. The + * values of the array are also associative arrays that describe the + * values and other properties of the table fields. + * + * Here follows a list of field properties that need to be specified: + * + * value: + * Value to be assigned to the specified field. This value may be + * of specified in database independent type format as this + * function can perform the necessary datatype conversions. + * + * Default: + * this property is required unless the Null property + * is set to 1. + * + * type + * Name of the type of the field. Currently, all types Metabase + * are supported except for clob and blob. + * + * Default: no type conversion + * + * null + * Boolean property that indicates that the value for this field + * should be set to null. + * + * The default value for fields missing in INSERT queries may be + * specified the definition of a table. Often, the default value + * is already null, but since the REPLACE may be emulated using + * an UPDATE query, make sure that all fields of the table are + * listed in this function argument array. + * + * Default: 0 + * + * key + * Boolean property that indicates that this field should be + * handled as a primary key or at least as part of the compound + * unique index of the table that will determine the row that will + * updated if it exists or inserted a new row otherwise. + * + * This function will fail if no key field is specified or if the + * value of a key field is set to null because fields that are + * part of unique index they may not be null. + * + * Default: 0 + * + * @return mixed MDB2_OK on success, a MDB2 error on failure + */ + function replace($table, $fields) + { + $count = count($fields); + $query = $values = ''; + $keys = $colnum = 0; + for (reset($fields); $colnum < $count; next($fields), $colnum++) { + $name = key($fields); + if ($colnum > 0) { + $query .= ','; + $values.= ','; + } + $query.= $name; + if (isset($fields[$name]['null']) && $fields[$name]['null']) { + $value = 'NULL'; + } else { + $value = $this->quote($fields[$name]['value'], $fields[$name]['type']); + } + $values.= $value; + if (isset($fields[$name]['key']) && $fields[$name]['key']) { + if ($value === 'NULL') { + return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, + 'replace: key value '.$name.' may not be NULL'); + } + $keys++; + } + } + if ($keys == 0) { + return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null, + 'replace: not specified which fields are keys'); + } + $query = "REPLACE INTO $table ($query) VALUES ($values)"; + $this->last_query = $query; + $this->debug($query, 'query'); + return $this->_doQuery($query, true); + } + + // }}} + // {{{ nextID() + + /** + * returns the next free id of a sequence + * + * @param string $seq_name name of the sequence + * @param boolean $ondemand when true the seqence is + * automatic created, if it + * not exists + * + * @return mixed MDB2 Error Object or id + * @access public + */ + function nextID($seq_name, $ondemand = true) + { + $sequence_name = $this->getSequenceName($seq_name); + $query = "INSERT INTO $sequence_name (".$this->options['seqcol_name'].") VALUES (NULL)"; + $this->expectError(MDB2_ERROR_NOSUCHTABLE); + $result = $this->_doQuery($query, true); + $this->popExpect(); + if (PEAR::isError($result)) { + if ($ondemand && $result->getCode() == MDB2_ERROR_NOSUCHTABLE) { + $this->loadModule('Manager'); + // Since we are creating the sequence on demand + // we know the first id = 1 so initialize the + // sequence at 2 + $result = $this->manager->createSequence($seq_name, 2); + if (PEAR::isError($result)) { + return $this->raiseError(MDB2_ERROR, null, null, + 'nextID: on demand sequence '.$seq_name.' could not be created'); + } else { + // First ID of a newly created sequence is 1 + return 1; + } + } + return $result; + } + $value = $this->queryOne('SELECT LAST_INSERT_ID()', 'integer'); + if (is_numeric($value)) { + $query = "DELETE FROM $sequence_name WHERE ".$this->options['seqcol_name']." < $value"; + $result = $this->_doQuery($query, true); + if (PEAR::isError($result)) { + $this->warnings[] = 'nextID: could not delete previous sequence table values from '.$seq_name; + } + } + return $value; + } + + // }}} + // {{{ lastInsertID() + + /** + * returns the autoincrement ID if supported or $id + * + * @param mixed $id value as returned by getBeforeId() + * @param string $table name of the table into which a new row was inserted + * @return mixed MDB2 Error Object or id + * @access public + */ + function lastInsertID($table = null, $field = null) + { + return $this->queryOne('SELECT LAST_INSERT_ID()', 'integer'); + } + + // }}} + // {{{ currID() + + /** + * returns the current id of a sequence + * + * @param string $seq_name name of the sequence + * @return mixed MDB2 Error Object or id + * @access public + */ + function currID($seq_name) + { + $sequence_name = $this->getSequenceName($seq_name); + $query = "SELECT MAX(".$this->options['seqcol_name'].") FROM $sequence_name"; + return $this->queryOne($query, 'integer'); + } +} + +class MDB2_Result_mysql extends MDB2_Result_Common +{ + // }}} + // {{{ fetchRow() + + /** + * Fetch a row and insert the data into an existing array. + * + * @param int $fetchmode how the array data should be indexed + * @param int $rownum number of the row where the data can be found + * @return int data array on success, a MDB2 error on failure + * @access public + */ + function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null) + { + if (!is_null($rownum)) { + $seek = $this->seek($rownum); + if (PEAR::isError($seek)) { + return $seek; + } + } + if ($fetchmode == MDB2_FETCHMODE_DEFAULT) { + $fetchmode = $this->db->fetchmode; + } + if ($fetchmode & MDB2_FETCHMODE_ASSOC) { + $row = @mysql_fetch_assoc($this->result); + if (is_array($row) + && $this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE + ) { + $row = array_change_key_case($row, $this->db->options['field_case']); + } + } else { + $row = @mysql_fetch_row($this->result); + } + + if (!$row) { + if (is_null($this->result)) { + $err =& $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, + 'fetchRow: resultset has already been freed'); + return $err; + } + $null = null; + return $null; + } + if ($this->db->options['portability'] & MDB2_PORTABILITY_EMPTY_TO_NULL) { + $this->db->_fixResultArrayValues($row, MDB2_PORTABILITY_EMPTY_TO_NULL); + } + if (!empty($this->values)) { + $this->_assignBindColumns($row); + } + if (!empty($this->types)) { + $row = $this->db->datatype->convertResultRow($this->types, $row); + } + if ($fetchmode === MDB2_FETCHMODE_OBJECT) { + $object_class = $this->db->options['fetch_class']; + if ($object_class == 'stdClass') { + $row = (object) $row; + } else { + $row = &new $object_class($row); + } + } + ++$this->rownum; + return $row; + } + + // }}} + // {{{ _getColumnNames() + + /** + * Retrieve the names of columns returned by the DBMS in a query result. + * + * @return mixed an associative array variable + * that will hold the names of columns. The + * indexes of the array are the column names + * mapped to lower case and the values are the + * respective numbers of the columns starting + * from 0. Some DBMS may not return any + * columns when the result set does not + * contain any rows. + * + * a MDB2 error on failure + * @access private + */ + function _getColumnNames() + { + $columns = array(); + $numcols = $this->numCols(); + if (PEAR::isError($numcols)) { + return $numcols; + } + for ($column = 0; $column < $numcols; $column++) { + $column_name = @mysql_field_name($this->result, $column); + $columns[$column_name] = $column; + } + if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) { + $columns = array_change_key_case($columns, $this->db->options['field_case']); + } + return $columns; + } + + // }}} + // {{{ numCols() + + /** + * Count the number of columns returned by the DBMS in a query result. + * + * @return mixed integer value with the number of columns, a MDB2 error + * on failure + * @access public + */ + function numCols() + { + $cols = @mysql_num_fields($this->result); + if (is_null($cols)) { + if (is_null($this->result)) { + return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, + 'numCols: resultset has already been freed'); + } + return $this->db->raiseError(); + } + return $cols; + } + + // }}} + // {{{ free() + + /** + * Free the internal resources associated with result. + * + * @return boolean true on success, false if result is invalid + * @access public + */ + function free() + { + $free = @mysql_free_result($this->result); + if (!$free) { + if (is_null($this->result)) { + return MDB2_OK; + } + return $this->db->raiseError(); + } + $this->result = null; + return MDB2_OK; + } +} + +class MDB2_BufferedResult_mysql extends MDB2_Result_mysql +{ + // }}} + // {{{ seek() + + /** + * seek to a specific row in a result set + * + * @param int $rownum number of the row where the data can be found + * @return mixed MDB2_OK on success, a MDB2 error on failure + * @access public + */ + function seek($rownum = 0) + { + if ($this->rownum != ($rownum - 1) && !@mysql_data_seek($this->result, $rownum)) { + if (is_null($this->result)) { + return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, + 'seek: resultset has already been freed'); + } + return $this->db->raiseError(MDB2_ERROR_INVALID, null, null, + 'seek: tried to seek to an invalid row number ('.$rownum.')'); + } + $this->rownum = $rownum - 1; + return MDB2_OK; + } + + // }}} + // {{{ valid() + + /** + * check if the end of the result set has been reached + * + * @return mixed true or false on sucess, a MDB2 error on failure + * @access public + */ + function valid() + { + $numrows = $this->numRows(); + if (PEAR::isError($numrows)) { + return $numrows; + } + return $this->rownum < ($numrows - 1); + } + + // }}} + // {{{ numRows() + + /** + * returns the number of rows in a result object + * + * @return mixed MDB2 Error Object or the number of rows + * @access public + */ + function numRows() + { + $rows = @mysql_num_rows($this->result); + if (is_null($rows)) { + if (is_null($this->result)) { + return $this->db->raiseError(MDB2_ERROR_NEED_MORE_DATA, null, null, + 'numRows: resultset has already been freed'); + } + return $this->raiseError(); + } + return $rows; + } +} + +class MDB2_Statement_mysql extends MDB2_Statement_Common +{ + +} +?> \ No newline at end of file -- cgit v1.2.3