diff options
author | Bharat Mediratta <bharat@menalto.com> | 2008-10-31 22:12:14 +0000 |
---|---|---|
committer | Bharat Mediratta <bharat@menalto.com> | 2008-10-31 22:12:14 +0000 |
commit | eba717f95f586d2538007bd18da6e9b32b076c30 (patch) | |
tree | 15fc596a270f9de0d163c66c96e3c65fca5ee100 /kohana/libraries/drivers/Database/Mysql.php | |
parent | fff10f8b70376ef25722bd867df26bc5aefced43 (diff) |
Merge over vendor code.
git-svn-id: http://gallery.svn.sourceforge.net/svnroot/gallery/trunk/eval/gx/gallery3/trunk@18408 57fcd75e-5312-0410-8df3-f5eb6fbb1595
Diffstat (limited to 'kohana/libraries/drivers/Database/Mysql.php')
-rw-r--r-- | kohana/libraries/drivers/Database/Mysql.php | 479 |
1 files changed, 479 insertions, 0 deletions
diff --git a/kohana/libraries/drivers/Database/Mysql.php b/kohana/libraries/drivers/Database/Mysql.php new file mode 100644 index 00000000..57459b4e --- /dev/null +++ b/kohana/libraries/drivers/Database/Mysql.php @@ -0,0 +1,479 @@ +<?php defined('SYSPATH') or die('No direct script access.'); +/** + * MySQL Database Driver + * + * $Id$ + * + * @package Core + * @author Kohana Team + * @copyright (c) 2007-2008 Kohana Team + * @license http://kohanaphp.com/license.html + */ +class Database_Mysql_Driver extends Database_Driver { + + /** + * Database connection link + */ + protected $link; + + /** + * Database configuration + */ + protected $db_config; + + /** + * Sets the config for the class. + * + * @param array database configuration + */ + public function __construct($config) + { + $this->db_config = $config; + + Kohana::log('debug', 'MySQL Database Driver Initialized'); + } + + /** + * Closes the database connection. + */ + public function __destruct() + { + is_resource($this->link) and mysql_close($this->link); + } + + public function connect() + { + // Check if link already exists + if (is_resource($this->link)) + return $this->link; + + // Import the connect variables + extract($this->db_config['connection']); + + // Persistent connections enabled? + $connect = ($this->db_config['persistent'] == TRUE) ? 'mysql_pconnect' : 'mysql_connect'; + + // Build the connection info + $host = isset($host) ? $host : $socket; + $port = isset($port) ? ':'.$port : ''; + + // Make the connection and select the database + if (($this->link = $connect($host.$port, $user, $pass, TRUE)) AND mysql_select_db($database, $this->link)) + { + if ($charset = $this->db_config['character_set']) + { + $this->set_charset($charset); + } + + // Clear password after successful connect + $this->config['connection']['pass'] = NULL; + + return $this->link; + } + + return FALSE; + } + + public function query($sql) + { + // Only cache if it's turned on, and only cache if it's not a write statement + if ($this->db_config['cache'] AND ! preg_match('#\b(?:INSERT|UPDATE|REPLACE|SET)\b#i', $sql)) + { + $hash = $this->query_hash($sql); + + if ( ! isset(self::$query_cache[$hash])) + { + // Set the cached object + self::$query_cache[$hash] = new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + + // Return the cached query + return self::$query_cache[$hash]; + } + + return new Mysql_Result(mysql_query($sql, $this->link), $this->link, $this->db_config['object'], $sql); + } + + public function set_charset($charset) + { + $this->query('SET NAMES '.$this->escape_str($charset)); + } + + public function escape_table($table) + { + if (!$this->db_config['escape']) + return $table; + + if (stripos($table, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $table = str_ireplace(' AS ', ' AS ', $table); + + // Runs escape_table on both sides of an AS statement + $table = array_map(array($this, __FUNCTION__), explode(' AS ', $table)); + + // Re-create the AS statement + return implode(' AS ', $table); + } + return '`'.str_replace('.', '`.`', $table).'`'; + } + + public function escape_column($column) + { + if (!$this->db_config['escape']) + return $column; + + if (strtolower($column) == 'count(*)' OR $column == '*') + return $column; + + // This matches any modifiers we support to SELECT. + if ( ! preg_match('/\b(?:rand|all|distinct(?:row)?|high_priority|sql_(?:small_result|b(?:ig_result|uffer_result)|no_cache|ca(?:che|lc_found_rows)))\s/i', $column)) + { + if (stripos($column, ' AS ') !== FALSE) + { + // Force 'AS' to uppercase + $column = str_ireplace(' AS ', ' AS ', $column); + + // Runs escape_column on both sides of an AS statement + $column = array_map(array($this, __FUNCTION__), explode(' AS ', $column)); + + // Re-create the AS statement + return implode(' AS ', $column); + } + + return preg_replace('/[^.*]+/', '`$0`', $column); + } + + $parts = explode(' ', $column); + $column = ''; + + for ($i = 0, $c = count($parts); $i < $c; $i++) + { + // The column is always last + if ($i == ($c - 1)) + { + $column .= preg_replace('/[^.*]+/', '`$0`', $parts[$i]); + } + else // otherwise, it's a modifier + { + $column .= $parts[$i].' '; + } + } + return $column; + } + + public function regex($field, $match = '', $type = 'AND ', $num_regexs) + { + $prefix = ($num_regexs == 0) ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' REGEXP \''.$this->escape_str($match).'\''; + } + + public function notregex($field, $match = '', $type = 'AND ', $num_regexs) + { + $prefix = $num_regexs == 0 ? '' : $type; + + return $prefix.' '.$this->escape_column($field).' NOT REGEXP \''.$this->escape_str($match) . '\''; + } + + public function merge($table, $keys, $values) + { + // Escape the column names + foreach ($keys as $key => $value) + { + $keys[$key] = $this->escape_column($value); + } + return 'REPLACE INTO '.$this->escape_table($table).' ('.implode(', ', $keys).') VALUES ('.implode(', ', $values).')'; + } + + public function limit($limit, $offset = 0) + { + return 'LIMIT '.$offset.', '.$limit; + } + + public function compile_select($database) + { + $sql = ($database['distinct'] == TRUE) ? 'SELECT DISTINCT ' : 'SELECT '; + $sql .= (count($database['select']) > 0) ? implode(', ', $database['select']) : '*'; + + if (count($database['from']) > 0) + { + // Escape the tables + $froms = array(); + foreach ($database['from'] as $from) + $froms[] = $this->escape_column($from); + $sql .= "\nFROM "; + $sql .= implode(', ', $froms); + } + + if (count($database['join']) > 0) + { + $sql .= ' '.$database['join']['type'].'JOIN ('.implode(', ', $database['join']['tables']).') ON '.implode(' AND ', $database['join']['conditions']); + } + + if (count($database['where']) > 0) + { + $sql .= "\nWHERE "; + } + + $sql .= implode("\n", $database['where']); + + if (count($database['groupby']) > 0) + { + $sql .= "\nGROUP BY "; + $sql .= implode(', ', $database['groupby']); + } + + if (count($database['having']) > 0) + { + $sql .= "\nHAVING "; + $sql .= implode("\n", $database['having']); + } + + if (count($database['orderby']) > 0) + { + $sql .= "\nORDER BY "; + $sql .= implode(', ', $database['orderby']); + } + + if (is_numeric($database['limit'])) + { + $sql .= "\n"; + $sql .= $this->limit($database['limit'], $database['offset']); + } + + return $sql; + } + + public function escape_str($str) + { + if (!$this->db_config['escape']) + return $str; + + is_resource($this->link) or $this->connect(); + + return mysql_real_escape_string($str, $this->link); + } + + public function list_tables() + { + $sql = 'SHOW TABLES FROM `'.$this->db_config['connection']['database'].'`'; + $result = $this->query($sql)->result(FALSE, MYSQL_ASSOC); + + $retval = array(); + foreach ($result as $row) + { + $retval[] = current($row); + } + + return $retval; + } + + public function show_error() + { + return mysql_error($this->link); + } + + public function list_fields($table) + { + static $tables; + + if (empty($tables[$table])) + { + foreach ($this->field_data($table) as $row) + { + // Make an associative array + $tables[$table][$row->Field] = $this->sql_type($row->Type); + + if ($row->Key === 'PRI' AND $row->Extra === 'auto_increment') + { + // For sequenced (AUTO_INCREMENT) tables + $tables[$table][$row->Field]['sequenced'] = TRUE; + } + + if ($row->Null === 'YES') + { + // Set NULL status + $tables[$table][$row->Field]['null'] = TRUE; + } + } + } + + if (!isset($tables[$table])) + throw new Kohana_Database_Exception('database.table_not_found', $table); + + return $tables[$table]; + } + + public function field_data($table) + { + $columns = array(); + + if ($query = mysql_query('SHOW COLUMNS FROM '.$this->escape_table($table), $this->link)) + { + if (mysql_num_rows($query) > 0) + { + while ($row = mysql_fetch_object($query)) + { + $columns[] = $row; + } + } + } + + return $columns; + } + +} // End Database_Mysql_Driver Class + +/** + * MySQL Result + */ +class Mysql_Result extends Database_Result { + + // Fetch function and return type + protected $fetch_type = 'mysql_fetch_object'; + protected $return_type = MYSQL_ASSOC; + + /** + * Sets up the result variables. + * + * @param resource query result + * @param resource database link + * @param boolean return objects or arrays + * @param string SQL query that was run + */ + public function __construct($result, $link, $object = TRUE, $sql) + { + $this->result = $result; + + // If the query is a resource, it was a SELECT, SHOW, DESCRIBE, EXPLAIN query + if (is_resource($result)) + { + $this->current_row = 0; + $this->total_rows = mysql_num_rows($this->result); + $this->fetch_type = ($object === TRUE) ? 'mysql_fetch_object' : 'mysql_fetch_array'; + } + elseif (is_bool($result)) + { + if ($result == FALSE) + { + // SQL error + throw new Kohana_Database_Exception('database.error', mysql_error($link).' - '.$sql); + } + else + { + // Its an DELETE, INSERT, REPLACE, or UPDATE query + $this->insert_id = mysql_insert_id($link); + $this->total_rows = mysql_affected_rows($link); + } + } + + // Set result type + $this->result($object); + + // Store the SQL + $this->sql = $sql; + } + + /** + * Destruct, the cleanup crew! + */ + public function __destruct() + { + if (is_resource($this->result)) + { + mysql_free_result($this->result); + } + } + + public function result($object = TRUE, $type = MYSQL_ASSOC) + { + $this->fetch_type = ((bool) $object) ? 'mysql_fetch_object' : 'mysql_fetch_array'; + + // This check has to be outside the previous statement, because we do not + // know the state of fetch_type when $object = NULL + // NOTE - The class set by $type must be defined before fetching the result, + // autoloading is disabled to save a lot of stupid overhead. + if ($this->fetch_type == 'mysql_fetch_object' AND $object === TRUE) + { + $this->return_type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $this->return_type = $type; + } + + return $this; + } + + public function as_array($object = NULL, $type = MYSQL_ASSOC) + { + return $this->result_array($object, $type); + } + + public function result_array($object = NULL, $type = MYSQL_ASSOC) + { + $rows = array(); + + if (is_string($object)) + { + $fetch = $object; + } + elseif (is_bool($object)) + { + if ($object === TRUE) + { + $fetch = 'mysql_fetch_object'; + + $type = (is_string($type) AND Kohana::auto_load($type)) ? $type : 'stdClass'; + } + else + { + $fetch = 'mysql_fetch_array'; + } + } + else + { + // Use the default config values + $fetch = $this->fetch_type; + + if ($fetch == 'mysql_fetch_object') + { + $type = (is_string($this->return_type) AND Kohana::auto_load($this->return_type)) ? $this->return_type : 'stdClass'; + } + } + + if (mysql_num_rows($this->result)) + { + // Reset the pointer location to make sure things work properly + mysql_data_seek($this->result, 0); + + while ($row = $fetch($this->result, $type)) + { + $rows[] = $row; + } + } + + return isset($rows) ? $rows : array(); + } + + public function list_fields() + { + $field_names = array(); + while ($field = mysql_fetch_field($this->result)) + { + $field_names[] = $field->name; + } + + return $field_names; + } + + public function seek($offset) + { + if ( ! $this->offsetExists($offset)) + return FALSE; + + return mysql_data_seek($this->result, $offset); + } + +} // End Mysql_Result Class
\ No newline at end of file |