dbo_mysql.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dbo__mysql_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * MySQL layer for DBO
00005  *
00006  * Long description for file
00007  *
00008  * PHP versions 4 and 5
00009  *
00010  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00011  * Copyright 2005-2008, Cake Software Foundation, Inc.
00012  *                              1785 E. Sahara Avenue, Suite 490-204
00013  *                              Las Vegas, Nevada 89104
00014  *
00015  * Licensed under The MIT License
00016  * Redistributions of files must retain the above copyright notice.
00017  *
00018  * @filesource
00019  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00020  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00021  * @package         cake
00022  * @subpackage      cake.cake.libs.model.datasources.dbo
00023  * @since           CakePHP(tm) v 0.10.5.1790
00024  * @version         $Revision: 580 $
00025  * @modifiedby      $LastChangedBy: gwoo $
00026  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00027  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00028  */
00029 
00030 /**
00031  * Short description for class.
00032  *
00033  * Long description for class
00034  *
00035  * @package     cake
00036  * @subpackage  cake.cake.libs.model.datasources.dbo
00037  */
00038 class DboMysql extends DboSource {
00039 /**
00040  * Enter description here...
00041  *
00042  * @var unknown_type
00043  */
00044     var $description = "MySQL DBO Driver";
00045 /**
00046  * Enter description here...
00047  *
00048  * @var unknown_type
00049  */
00050     var $startQuote = "`";
00051 /**
00052  * Enter description here...
00053  *
00054  * @var unknown_type
00055  */
00056     var $endQuote = "`";
00057 /**
00058  * Index of basic SQL commands
00059  *
00060  * @var array
00061  * @access protected
00062  */
00063     var $_commands = array(
00064         'begin'    => 'START TRANSACTION',
00065         'commit'   => 'COMMIT',
00066         'rollback' => 'ROLLBACK'
00067     );
00068 /**
00069  * Base configuration settings for MySQL driver
00070  *
00071  * @var array
00072  */
00073     var $_baseConfig = array(
00074         'persistent' => true,
00075         'host' => 'localhost',
00076         'login' => 'root',
00077         'password' => '',
00078         'database' => 'cake',
00079         'port' => '3306',
00080         'connect' => 'mysql_pconnect'
00081     );
00082 /**
00083  * MySQL column definition
00084  *
00085  * @var array
00086  */
00087     var $columns = array(
00088         'primary_key' => array('name' => 'NOT NULL AUTO_INCREMENT'),
00089         'string' => array('name' => 'varchar', 'limit' => '255'),
00090         'text' => array('name' => 'text'),
00091         'integer' => array('name' => 'int', 'limit' => '11', 'formatter' => 'intval'),
00092         'float' => array('name' => 'float', 'formatter' => 'floatval'),
00093         'datetime' => array('name' => 'datetime', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00094         'timestamp' => array('name' => 'timestamp', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00095         'time' => array('name' => 'time', 'format' => 'H:i:s', 'formatter' => 'date'),
00096         'date' => array('name' => 'date', 'format' => 'Y-m-d', 'formatter' => 'date'),
00097         'binary' => array('name' => 'blob'),
00098         'boolean' => array('name' => 'tinyint', 'limit' => '1')
00099     );
00100 /**
00101  * use alias for update and delete. Set to true if version >= 4.1
00102  *
00103  * @var boolean
00104  */
00105     var $__useAlias = true;
00106 /**
00107  * Connects to the database using options in the given configuration array.
00108  *
00109  * @return boolean True if the database could be connected, else false
00110  */
00111     function connect() {
00112         $config = $this->config;
00113         $connect = $config['connect'];
00114         $this->connected = false;
00115 
00116         if (!$config['persistent']) {
00117             $this->connection = mysql_connect($config['host'] . ':' . $config['port'], $config['login'], $config['password'], true);
00118         } else {
00119             $this->connection = $connect($config['host'] . ':' . $config['port'], $config['login'], $config['password']);
00120         }
00121 
00122         if (mysql_select_db($config['database'], $this->connection)) {
00123             $this->connected = true;
00124         }
00125 
00126         if (isset($config['encoding']) && !empty($config['encoding'])) {
00127             $this->setEncoding($config['encoding']);
00128         }
00129 
00130         $this->__useAlias = (bool)version_compare(mysql_get_server_info($this->connection), "4.1", ">=");
00131 
00132         return $this->connected;
00133     }
00134 /**
00135  * Disconnects from database.
00136  *
00137  * @return boolean True if the database could be disconnected, else false
00138  */
00139     function disconnect() {
00140         @mysql_free_result($this->results);
00141         $this->connected = !@mysql_close($this->connection);
00142         return !$this->connected;
00143     }
00144 /**
00145  * Executes given SQL statement.
00146  *
00147  * @param string $sql SQL statement
00148  * @return resource Result resource identifier
00149  * @access protected
00150  */
00151     function _execute($sql) {
00152         return mysql_query($sql, $this->connection);
00153     }
00154 /**
00155  * Returns an array of sources (tables) in the database.
00156  *
00157  * @return array Array of tablenames in the database
00158  */
00159     function listSources() {
00160         $cache = parent::listSources();
00161         if ($cache != null) {
00162             return $cache;
00163         }
00164         $result = $this->_execute('SHOW TABLES FROM ' . $this->name($this->config['database']) . ';');
00165 
00166         if (!$result) {
00167             return array();
00168         } else {
00169             $tables = array();
00170 
00171             while ($line = mysql_fetch_array($result)) {
00172                 $tables[] = $line[0];
00173             }
00174             parent::listSources($tables);
00175             return $tables;
00176         }
00177     }
00178 /**
00179  * Returns an array of the fields in given table name.
00180  *
00181  * @param string $tableName Name of database table to inspect
00182  * @return array Fields in table. Keys are name and type
00183  */
00184     function describe(&$model) {
00185         $cache = parent::describe($model);
00186         if ($cache != null) {
00187             return $cache;
00188         }
00189         $fields = false;
00190         $cols = $this->query('DESCRIBE ' . $this->fullTableName($model));
00191 
00192         foreach ($cols as $column) {
00193             $colKey = array_keys($column);
00194             if (isset($column[$colKey[0]]) && !isset($column[0])) {
00195                 $column[0] = $column[$colKey[0]];
00196             }
00197             if (isset($column[0])) {
00198                 $fields[$column[0]['Field']] = array(
00199                     'type'      => $this->column($column[0]['Type']),
00200                     'null'      => ($column[0]['Null'] == 'YES' ? true : false),
00201                     'default'   => $column[0]['Default'],
00202                     'length'    => $this->length($column[0]['Type']),
00203                 );
00204                 if(!empty($column[0]['Key']) && isset($this->index[$column[0]['Key']])) {
00205                     $fields[$column[0]['Field']]['key'] = $this->index[$column[0]['Key']];
00206                 }
00207             }
00208         }
00209         $this->__cacheDescription($this->fullTableName($model, false), $fields);
00210         return $fields;
00211     }
00212 /**
00213  * Returns a quoted and escaped string of $data for use in an SQL statement.
00214  *
00215  * @param string $data String to be prepared for use in an SQL statement
00216  * @param string $column The column into which this data will be inserted
00217  * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
00218  * @return string Quoted and escaped data
00219  */
00220     function value($data, $column = null, $safe = false) {
00221         $parent = parent::value($data, $column, $safe);
00222 
00223         if ($parent != null) {
00224             return $parent;
00225         } elseif ($data === null || (is_array($data) && empty($data))) {
00226             return 'NULL';
00227         } elseif ($data === '') {
00228             return  "''";
00229         }
00230         if (empty($column)) {
00231             $column = $this->introspectType($data);
00232         }
00233 
00234         switch ($column) {
00235             case 'boolean':
00236                 return $this->boolean((bool)$data);
00237             break;
00238             case 'integer':
00239             case 'float':
00240                 if ((is_int($data) || is_float($data)) || (
00241                     is_numeric($data) && strpos($data, ',') === false &&
00242                     $data[0] != '0' && strpos($data, 'e') === false
00243                 )) {
00244                     return $data;
00245                 }
00246             default:
00247                 $data = "'" . mysql_real_escape_string($data, $this->connection) . "'";
00248             break;
00249         }
00250         return $data;
00251     }
00252 /**
00253  * Generates and executes an SQL UPDATE statement for given model, fields, and values.
00254  *
00255  * @param Model $model
00256  * @param array $fields
00257  * @param array $values
00258  * @param mixed $conditions
00259  * @return array
00260  */
00261     function update(&$model, $fields = array(), $values = null, $conditions = null) {
00262         if (!$this->__useAlias) {
00263             return parent::update($model, $fields, $values, $conditions);
00264         }
00265 
00266         if ($values == null) {
00267             $combined = $fields;
00268         } else {
00269             $combined = array_combine($fields, $values);
00270         }
00271 
00272         $fields = $this->_prepareUpdateFields($model, $combined, empty($conditions), !empty($conditions));
00273         $fields = join(', ', $fields);
00274         $table = $this->fullTableName($model);
00275         $alias = $this->name($model->alias);
00276         $joins = implode(' ', $this->_getJoins($model));
00277 
00278         if (empty($conditions)) {
00279             $alias = $joins = false;
00280         }
00281         $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
00282 
00283         if ($conditions === false) {
00284             return false;
00285         }
00286 
00287         if (!$this->execute($this->renderStatement('update', compact('table', 'alias', 'joins', 'fields', 'conditions')))) {
00288             $model->onError();
00289             return false;
00290         }
00291         return true;
00292     }
00293 /**
00294  * Generates and executes an SQL DELETE statement for given id/conditions on given model.
00295  *
00296  * @param Model $model
00297  * @param mixed $conditions
00298  * @return boolean Success
00299  */
00300     function delete(&$model, $conditions = null) {
00301         if (!$this->__useAlias) {
00302             return parent::delete($model, $conditions);
00303         }
00304         $alias = $this->name($model->alias);
00305         $table = $this->fullTableName($model);
00306         $joins = implode(' ', $this->_getJoins($model));
00307 
00308         if (empty($conditions)) {
00309             $alias = $joins = false;
00310         }
00311         $conditions = $this->conditions($this->defaultConditions($model, $conditions, $alias), true, true, $model);
00312 
00313         if ($conditions === false) {
00314             return false;
00315         }
00316 
00317         if ($this->execute($this->renderStatement('delete', compact('alias', 'table', 'joins', 'conditions'))) === false) {
00318             $model->onError();
00319             return false;
00320         }
00321         return true;
00322     }
00323 /**
00324  * Returns a formatted error message from previous database operation.
00325  *
00326  * @return string Error message with error number
00327  */
00328     function lastError() {
00329         if (mysql_errno($this->connection)) {
00330             return mysql_errno($this->connection).': '.mysql_error($this->connection);
00331         }
00332         return null;
00333     }
00334 /**
00335  * Returns number of affected rows in previous database operation. If no previous operation exists,
00336  * this returns false.
00337  *
00338  * @return integer Number of affected rows
00339  */
00340     function lastAffected() {
00341         if ($this->_result) {
00342             return mysql_affected_rows($this->connection);
00343         }
00344         return null;
00345     }
00346 /**
00347  * Returns number of rows in previous resultset. If no previous resultset exists,
00348  * this returns false.
00349  *
00350  * @return integer Number of rows in resultset
00351  */
00352     function lastNumRows() {
00353         if ($this->_result) {
00354             return @mysql_num_rows($this->_result);
00355         }
00356         return null;
00357     }
00358 /**
00359  * Returns the ID generated from the previous INSERT operation.
00360  *
00361  * @param unknown_type $source
00362  * @return in
00363  */
00364     function lastInsertId($source = null) {
00365         $id = $this->fetchRow('SELECT LAST_INSERT_ID() AS insertID', false);
00366         if ($id !== false && !empty($id) && !empty($id[0]) && isset($id[0]['insertID'])) {
00367             return $id[0]['insertID'];
00368         }
00369 
00370         return null;
00371     }
00372 /**
00373  * Converts database-layer column types to basic types
00374  *
00375  * @param string $real Real database-layer column type (i.e. "varchar(255)")
00376  * @return string Abstract column type (i.e. "string")
00377  */
00378     function column($real) {
00379         if (is_array($real)) {
00380             $col = $real['name'];
00381             if (isset($real['limit'])) {
00382                 $col .= '('.$real['limit'].')';
00383             }
00384             return $col;
00385         }
00386 
00387         $col = str_replace(')', '', $real);
00388         $limit = $this->length($real);
00389         if (strpos($col, '(') !== false) {
00390             list($col, $vals) = explode('(', $col);
00391         }
00392 
00393         if (in_array($col, array('date', 'time', 'datetime', 'timestamp'))) {
00394             return $col;
00395         }
00396         if ($col == 'tinyint' && $limit == 1) {
00397             return 'boolean';
00398         }
00399         if (strpos($col, 'int') !== false) {
00400             return 'integer';
00401         }
00402         if (strpos($col, 'char') !== false || $col == 'tinytext') {
00403             return 'string';
00404         }
00405         if (strpos($col, 'text') !== false) {
00406             return 'text';
00407         }
00408         if (strpos($col, 'blob') !== false || $col == 'binary') {
00409             return 'binary';
00410         }
00411         if (in_array($col, array('float', 'double', 'decimal'))) {
00412             return 'float';
00413         }
00414         if (strpos($col, 'enum') !== false) {
00415             return "enum($vals)";
00416         }
00417         if ($col == 'boolean') {
00418             return $col;
00419         }
00420         return 'text';
00421     }
00422 /**
00423  * Enter description here...
00424  *
00425  * @param unknown_type $results
00426  */
00427     function resultSet(&$results) {
00428         $this->results =& $results;
00429         $this->map = array();
00430         $num_fields = mysql_num_fields($results);
00431         $index = 0;
00432         $j = 0;
00433 
00434         while ($j < $num_fields) {
00435 
00436             $column = mysql_fetch_field($results,$j);
00437             if (!empty($column->table)) {
00438                 $this->map[$index++] = array($column->table, $column->name);
00439             } else {
00440                 $this->map[$index++] = array(0, $column->name);
00441             }
00442             $j++;
00443         }
00444     }
00445 /**
00446  * Fetches the next row from the current result set
00447  *
00448  * @return unknown
00449  */
00450     function fetchResult() {
00451         if ($row = mysql_fetch_row($this->results)) {
00452             $resultRow = array();
00453             $i = 0;
00454             foreach ($row as $index => $field) {
00455                 list($table, $column) = $this->map[$index];
00456                 $resultRow[$table][$column] = $row[$index];
00457                 $i++;
00458             }
00459             return $resultRow;
00460         } else {
00461             return false;
00462         }
00463     }
00464 /**
00465  * Sets the database encoding
00466  *
00467  * @param string $enc Database encoding
00468  */
00469     function setEncoding($enc) {
00470         return $this->_execute('SET NAMES ' . $enc) != false;
00471     }
00472 /**
00473  * Gets the database encoding
00474  *
00475  * @return string The database encoding
00476  */
00477     function getEncoding() {
00478         return mysql_client_encoding($this->connection);
00479     }
00480 /**
00481  * Inserts multiple values into a table
00482  *
00483  * @param string $table
00484  * @param string $fields
00485  * @param array $values
00486  */
00487     function insertMulti($table, $fields, $values) {
00488         $table = $this->fullTableName($table);
00489         if (is_array($fields)) {
00490             $fields = join(', ', array_map(array(&$this, 'name'), $fields));
00491         }
00492         $values = implode(', ', $values);
00493         $this->query("INSERT INTO {$table} ({$fields}) VALUES {$values}");
00494     }
00495 /**
00496  * Returns an array of the indexes in given table name.
00497  *
00498  * @param string $model Name of model to inspect
00499  * @return array Fields in table. Keys are column and unique
00500  */
00501     function index($model) {
00502         $index = array();
00503         $table = $this->fullTableName($model);
00504         if($table) {
00505             $indexes = $this->query('SHOW INDEX FROM ' . $table);
00506             $keys = Set::extract($indexes, '{n}.STATISTICS');
00507             foreach ($keys as $i => $key) {
00508                 if(!isset($index[$key['Key_name']])) {
00509                     $index[$key['Key_name']]['column'] = $key['Column_name'];
00510                     $index[$key['Key_name']]['unique'] = ife($key['Non_unique'] == 0, 1, 0);
00511                 } else {
00512                     if(!is_array($index[$key['Key_name']]['column'])) {
00513                         $col[] = $index[$key['Key_name']]['column'];
00514                     }
00515                     $col[] = $key['Column_name'];
00516                     $index[$key['Key_name']]['column'] = $col;
00517                 }
00518             }
00519         }
00520         return $index;
00521     }
00522 /**
00523  * Generate a MySQL Alter Table syntax for the given Schema comparison
00524  *
00525  * @param unknown_type $schema
00526  * @return unknown
00527  */
00528     function alterSchema($compare, $table = null) {
00529         if(!is_array($compare)) {
00530             return false;
00531         }
00532         $out = '';
00533         $colList = array();
00534         foreach($compare as $curTable => $types) {
00535             if (!$table || $table == $curTable) {
00536                 $out .= 'ALTER TABLE ' . $this->fullTableName($curTable) . " \n";
00537                 foreach($types as $type => $column) {
00538                     switch($type) {
00539                         case 'add':
00540                             foreach($column as $field => $col) {
00541                                 $col['name'] = $field;
00542                                 $alter = 'ADD '.$this->buildColumn($col);
00543                                 if(isset($col['after'])) {
00544                                     $alter .= ' AFTER '. $this->name($col['after']);
00545                                 }
00546                                 $colList[] = $alter;
00547                             }
00548                         break;
00549                         case 'drop':
00550                             foreach($column as $field => $col) {
00551                                 $col['name'] = $field;
00552                                 $colList[] = 'DROP '.$this->name($field);
00553                             }
00554                         break;
00555                         case 'change':
00556                             foreach($column as $field => $col) {
00557                                 if(!isset($col['name'])) {
00558                                     $col['name'] = $field;
00559                                 }
00560                                 $colList[] = 'CHANGE '. $this->name($field).' '.$this->buildColumn($col);
00561                             }
00562                         break;
00563                     }
00564                 }
00565                 $out .= "\t" . join(",\n\t", $colList) . ";\n\n";
00566             }
00567         }
00568         return $out;
00569     }
00570 /**
00571  * Generate a MySQL "drop table" statement for the given Schema object
00572  *
00573  * @param object $schema An instance of a subclass of CakeSchema
00574  * @param string $table Optional.  If specified only the table name given will be generated.
00575  *                      Otherwise, all tables defined in the schema are generated.
00576  * @return string
00577  */
00578     function dropSchema($schema, $table = null) {
00579         if (!is_a($schema, 'CakeSchema')) {
00580             trigger_error(__('Invalid schema object', true), E_USER_WARNING);
00581             return null;
00582         }
00583         $out = '';
00584         foreach ($schema->tables as $curTable => $columns) {
00585             if (!$table || $table == $curTable) {
00586                 $out .= 'DROP TABLE IF EXISTS ' . $this->fullTableName($curTable) . ";\n";
00587             }
00588         }
00589         return $out;
00590     }
00591 }
00592 ?>