/modules/database/classes/kohana/database/mysql.php
PHP | 443 lines | 307 code | 60 blank | 76 comment | 26 complexity | 19e70c8c6b4f95e28447491e9d3858cd MD5 | raw file
- <?php defined('SYSPATH') OR die('No direct script access.');
- /**
- * MySQL database connection.
- *
- * @package Kohana/Database
- * @category Drivers
- * @author Kohana Team
- * @copyright (c) 2008-2009 Kohana Team
- * @license http://kohanaphp.com/license
- */
- class Kohana_Database_MySQL extends Database {
- // Database in use by each connection
- protected static $_current_databases = array();
- // Use SET NAMES to set the character set
- protected static $_set_names;
- // Identifier for this connection within the PHP driver
- protected $_connection_id;
- // MySQL uses a backtick for identifiers
- protected $_identifier = '`';
- public function connect()
- {
- if ($this->_connection)
- return;
- if (Database_MySQL::$_set_names === NULL)
- {
- // Determine if we can use mysql_set_charset(), which is only
- // available on PHP 5.2.3+ when compiled against MySQL 5.0+
- Database_MySQL::$_set_names = ! function_exists('mysql_set_charset');
- }
- // Extract the connection parameters, adding required variabels
- extract($this->_config['connection'] + array(
- 'database' => '',
- 'hostname' => '',
- 'username' => '',
- 'password' => '',
- 'persistent' => FALSE,
- ));
- // Prevent this information from showing up in traces
- unset($this->_config['connection']['username'], $this->_config['connection']['password']);
- try
- {
- if ($persistent)
- {
- // Create a persistent connection
- $this->_connection = mysql_pconnect($hostname, $username, $password);
- }
- else
- {
- // Create a connection and force it to be a new link
- $this->_connection = mysql_connect($hostname, $username, $password, TRUE);
- }
- }
- catch (Exception $e)
- {
- // No connection exists
- $this->_connection = NULL;
- throw new Database_Exception(':error',
- array(':error' => $e->getMessage()),
- $e->getCode());
- }
- // \xFF is a better delimiter, but the PHP driver uses underscore
- $this->_connection_id = sha1($hostname.'_'.$username.'_'.$password);
- $this->_select_db($database);
- if ( ! empty($this->_config['charset']))
- {
- // Set the character set
- $this->set_charset($this->_config['charset']);
- }
- if ( ! empty($this->_config['connection']['variables']))
- {
- // Set session variables
- $variables = array();
- foreach ($this->_config['connection']['variables'] as $var => $val)
- {
- $variables[] = 'SESSION '.$var.' = '.$this->quote($val);
- }
- mysql_query('SET '.implode(', ', $variables), $this->_connection);
- }
- }
- /**
- * Select the database
- *
- * @param string $database Database
- * @return void
- */
- protected function _select_db($database)
- {
- if ( ! mysql_select_db($database, $this->_connection))
- {
- // Unable to select database
- throw new Database_Exception(':error',
- array(':error' => mysql_error($this->_connection)),
- mysql_errno($this->_connection));
- }
- Database_MySQL::$_current_databases[$this->_connection_id] = $database;
- }
- public function disconnect()
- {
- try
- {
- // Database is assumed disconnected
- $status = TRUE;
- if (is_resource($this->_connection))
- {
- if ($status = mysql_close($this->_connection))
- {
- // Clear the connection
- $this->_connection = NULL;
- // Clear the instance
- parent::disconnect();
- }
- }
- }
- catch (Exception $e)
- {
- // Database is probably not disconnected
- $status = ! is_resource($this->_connection);
- }
- return $status;
- }
- public function set_charset($charset)
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- if (Database_MySQL::$_set_names === TRUE)
- {
- // PHP is compiled against MySQL 4.x
- $status = (bool) mysql_query('SET NAMES '.$this->quote($charset), $this->_connection);
- }
- else
- {
- // PHP is compiled against MySQL 5.x
- $status = mysql_set_charset($charset, $this->_connection);
- }
- if ($status === FALSE)
- {
- throw new Database_Exception(':error',
- array(':error' => mysql_error($this->_connection)),
- mysql_errno($this->_connection));
- }
- }
- public function query($type, $sql, $as_object = FALSE, array $params = NULL)
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- if ( ! empty($this->_config['profiling']))
- {
- // Benchmark this query for the current instance
- $benchmark = Profiler::start("Database ({$this->_instance})", $sql);
- }
- if ( ! empty($this->_config['connection']['persistent']) AND $this->_config['connection']['database'] !== Database_MySQL::$_current_databases[$this->_connection_id])
- {
- // Select database on persistent connections
- $this->_select_db($this->_config['connection']['database']);
- }
- // Execute the query
- if (($result = mysql_query($sql, $this->_connection)) === FALSE)
- {
- if (isset($benchmark))
- {
- // This benchmark is worthless
- Profiler::delete($benchmark);
- }
- throw new Database_Exception(':error [ :query ]',
- array(':error' => mysql_error($this->_connection), ':query' => $sql),
- mysql_errno($this->_connection));
- }
- if (isset($benchmark))
- {
- Profiler::stop($benchmark);
- }
- // Set the last query
- $this->last_query = $sql;
- if ($type === Database::SELECT)
- {
- // Return an iterator of results
- return new Database_MySQL_Result($result, $sql, $as_object, $params);
- }
- elseif ($type === Database::INSERT)
- {
- // Return a list of insert id and rows created
- return array(
- mysql_insert_id($this->_connection),
- mysql_affected_rows($this->_connection),
- );
- }
- else
- {
- // Return the number of rows affected
- return mysql_affected_rows($this->_connection);
- }
- }
- public function datatype($type)
- {
- static $types = array
- (
- 'blob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '65535'),
- 'bool' => array('type' => 'bool'),
- 'bigint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '18446744073709551615'),
- 'datetime' => array('type' => 'string'),
- 'decimal unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
- 'double' => array('type' => 'float'),
- 'double precision unsigned' => array('type' => 'float', 'min' => '0'),
- 'double unsigned' => array('type' => 'float', 'min' => '0'),
- 'enum' => array('type' => 'string'),
- 'fixed' => array('type' => 'float', 'exact' => TRUE),
- 'fixed unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
- 'float unsigned' => array('type' => 'float', 'min' => '0'),
- 'int unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
- 'integer unsigned' => array('type' => 'int', 'min' => '0', 'max' => '4294967295'),
- 'longblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '4294967295'),
- 'longtext' => array('type' => 'string', 'character_maximum_length' => '4294967295'),
- 'mediumblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '16777215'),
- 'mediumint' => array('type' => 'int', 'min' => '-8388608', 'max' => '8388607'),
- 'mediumint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '16777215'),
- 'mediumtext' => array('type' => 'string', 'character_maximum_length' => '16777215'),
- 'national varchar' => array('type' => 'string'),
- 'numeric unsigned' => array('type' => 'float', 'exact' => TRUE, 'min' => '0'),
- 'nvarchar' => array('type' => 'string'),
- 'point' => array('type' => 'string', 'binary' => TRUE),
- 'real unsigned' => array('type' => 'float', 'min' => '0'),
- 'set' => array('type' => 'string'),
- 'smallint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '65535'),
- 'text' => array('type' => 'string', 'character_maximum_length' => '65535'),
- 'tinyblob' => array('type' => 'string', 'binary' => TRUE, 'character_maximum_length' => '255'),
- 'tinyint' => array('type' => 'int', 'min' => '-128', 'max' => '127'),
- 'tinyint unsigned' => array('type' => 'int', 'min' => '0', 'max' => '255'),
- 'tinytext' => array('type' => 'string', 'character_maximum_length' => '255'),
- 'year' => array('type' => 'string'),
- );
- $type = str_replace(' zerofill', '', $type);
- if (isset($types[$type]))
- return $types[$type];
- return parent::datatype($type);
- }
- /**
- * Start a SQL transaction
- *
- * @link http://dev.mysql.com/doc/refman/5.0/en/set-transaction.html
- *
- * @param string $mode Isolation level
- * @return boolean
- */
- public function begin($mode = NULL)
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- if ($mode AND ! mysql_query("SET TRANSACTION ISOLATION LEVEL $mode", $this->_connection))
- {
- throw new Database_Exception(':error',
- array(':error' => mysql_error($this->_connection)),
- mysql_errno($this->_connection));
- }
- return (bool) mysql_query('START TRANSACTION', $this->_connection);
- }
- /**
- * Commit a SQL transaction
- *
- * @return boolean
- */
- public function commit()
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- return (bool) mysql_query('COMMIT', $this->_connection);
- }
- /**
- * Rollback a SQL transaction
- *
- * @return boolean
- */
- public function rollback()
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- return (bool) mysql_query('ROLLBACK', $this->_connection);
- }
- public function list_tables($like = NULL)
- {
- if (is_string($like))
- {
- // Search for table names
- $result = $this->query(Database::SELECT, 'SHOW TABLES LIKE '.$this->quote($like), FALSE);
- }
- else
- {
- // Find all table names
- $result = $this->query(Database::SELECT, 'SHOW TABLES', FALSE);
- }
- $tables = array();
- foreach ($result as $row)
- {
- $tables[] = reset($row);
- }
- return $tables;
- }
- public function list_columns($table, $like = NULL, $add_prefix = TRUE)
- {
- // Quote the table name
- $table = ($add_prefix === TRUE) ? $this->quote_table($table) : $table;
- if (is_string($like))
- {
- // Search for column names
- $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table.' LIKE '.$this->quote($like), FALSE);
- }
- else
- {
- // Find all column names
- $result = $this->query(Database::SELECT, 'SHOW FULL COLUMNS FROM '.$table, FALSE);
- }
- $count = 0;
- $columns = array();
- foreach ($result as $row)
- {
- list($type, $length) = $this->_parse_type($row['Type']);
- $column = $this->datatype($type);
- $column['column_name'] = $row['Field'];
- $column['column_default'] = $row['Default'];
- $column['data_type'] = $type;
- $column['is_nullable'] = ($row['Null'] == 'YES');
- $column['ordinal_position'] = ++$count;
- switch ($column['type'])
- {
- case 'float':
- if (isset($length))
- {
- list($column['numeric_precision'], $column['numeric_scale']) = explode(',', $length);
- }
- break;
- case 'int':
- if (isset($length))
- {
- // MySQL attribute
- $column['display'] = $length;
- }
- break;
- case 'string':
- switch ($column['data_type'])
- {
- case 'binary':
- case 'varbinary':
- $column['character_maximum_length'] = $length;
- break;
- case 'char':
- case 'varchar':
- $column['character_maximum_length'] = $length;
- case 'text':
- case 'tinytext':
- case 'mediumtext':
- case 'longtext':
- $column['collation_name'] = $row['Collation'];
- break;
- case 'enum':
- case 'set':
- $column['collation_name'] = $row['Collation'];
- $column['options'] = explode('\',\'', substr($length, 1, -1));
- break;
- }
- break;
- }
- // MySQL attributes
- $column['comment'] = $row['Comment'];
- $column['extra'] = $row['Extra'];
- $column['key'] = $row['Key'];
- $column['privileges'] = $row['Privileges'];
- $columns[$row['Field']] = $column;
- }
- return $columns;
- }
- public function escape($value)
- {
- // Make sure the database is connected
- $this->_connection or $this->connect();
- if (($value = mysql_real_escape_string( (string) $value, $this->_connection)) === FALSE)
- {
- throw new Database_Exception(':error',
- array(':error' => mysql_error($this->_connection)),
- mysql_errno($this->_connection));
- }
- // SQL standard is to use single-quotes for all values
- return "'$value'";
- }
- } // End Database_MySQL