dbo_adodb.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dbo__adodb_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 
00004 /**
00005  * AdoDB layer for DBO.
00006  *
00007  * Long description for file
00008  *
00009  * PHP versions 4 and 5
00010  *
00011  * CakePHP(tm) :  Rapid Development Framework <http://www.cakephp.org/>
00012  * Copyright 2005-2008, Cake Software Foundation, Inc.
00013  *                              1785 E. Sahara Avenue, Suite 490-204
00014  *                              Las Vegas, Nevada 89104
00015  *
00016  * Licensed under The MIT License
00017  * Redistributions of files must retain the above copyright notice.
00018  *
00019  * @filesource
00020  * @copyright       Copyright 2005-2008, Cake Software Foundation, Inc.
00021  * @link                http://www.cakefoundation.org/projects/info/cakephp CakePHP(tm) Project
00022  * @package         cake
00023  * @subpackage      cake.cake.libs.model.datasources.dbo
00024  * @since           CakePHP(tm) v 0.2.9
00025  * @version         $Revision: 580 $
00026  * @modifiedby      $LastChangedBy: gwoo $
00027  * @lastmodified    $Date: 2008-07-01 09:45:49 -0500 (Tue, 01 Jul 2008) $
00028  * @license         http://www.opensource.org/licenses/mit-license.php The MIT License
00029  */
00030 
00031 /**
00032  * Include AdoDB files.
00033  */
00034 App::import('Vendor', 'NewADOConnection', array('file' => 'adodb' . DS . 'adodb.inc.php'));
00035 
00036 /**
00037  * AdoDB DBO implementation.
00038  *
00039  * Database abstraction implementation for the AdoDB library.
00040  *
00041  * @package     cake
00042  * @subpackage  cake.cake.libs.model.datasources.dbo
00043  */
00044 class DboAdodb extends DboSource {
00045 /**
00046  * Enter description here...
00047  *
00048  * @var string
00049  */
00050     var $description = "ADOdb DBO Driver";
00051 /**
00052  * ADOConnection object with which we connect.
00053  *
00054  * @var ADOConnection The connection object.
00055  * @access private
00056  */
00057     var $_adodb = null;
00058 /**
00059  * Array translating ADOdb column MetaTypes to cake-supported metatypes
00060  *
00061  * @var array
00062  * @access private
00063  */
00064     var $_adodbColumnTypes = array(
00065         'string' => 'C',
00066         'text' => 'X',
00067         'date' => 'D',
00068         'timestamp' => 'T',
00069         'time' => 'T',
00070         'datetime' => 'T',
00071         'boolean' => 'L',
00072         'float' => 'N',
00073         'integer' => 'I',
00074         'binary' => 'R',
00075     );
00076 /**
00077  * ADOdb column definition
00078  *
00079  * @var array
00080  */
00081     var $columns = array(
00082         'primary_key' => array('name' => 'R', 'limit' => 11),
00083         'string' => array('name' => 'C', 'limit' => '255'),
00084         'text' => array('name' => 'X'),
00085         'integer' => array('name' => 'I', 'limit' => '11', 'formatter' => 'intval'),
00086         'float' => array('name' => 'N', 'formatter' => 'floatval'),
00087         'timestamp' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00088         'time' => array('name' => 'T',  'format' => 'H:i:s', 'formatter' => 'date'),
00089         'datetime' => array('name' => 'T', 'format' => 'Y-m-d H:i:s', 'formatter' => 'date'),
00090         'date' => array('name' => 'D', 'format' => 'Y-m-d', 'formatter' => 'date'),
00091         'binary' => array('name' => 'B'),
00092         'boolean' => array('name' => 'L', 'limit' => '1')
00093     );
00094 /**
00095  * Connects to the database using options in the given configuration array.
00096  *
00097  * @param array $config Configuration array for connecting
00098  */
00099     function connect() {
00100         $config = $this->config;
00101         $persistent = strrpos($config['connect'], '|p');
00102 
00103         if ($persistent === false) {
00104             $adodb_driver = $config['connect'];
00105             $connect = 'Connect';
00106         } else {
00107             $adodb_driver = substr($config['connect'], 0, $persistent);
00108             $connect = 'PConnect';
00109         }
00110 
00111         $this->_adodb = NewADOConnection($adodb_driver);
00112 
00113         $this->_adodbDataDict = NewDataDictionary($this->_adodb, $adodb_driver);
00114 
00115         $this->startQuote = $this->_adodb->nameQuote;
00116         $this->endQuote = $this->_adodb->nameQuote;
00117 
00118         $this->connected = $this->_adodb->$connect($config['host'], $config['login'], $config['password'], $config['database']);
00119         $this->_adodbMetatyper = &$this->_adodb->execute('Select 1');
00120         return $this->connected;
00121     }
00122 /**
00123  * Disconnects from database.
00124  *
00125  * @return boolean True if the database could be disconnected, else false
00126  */
00127     function disconnect() {
00128         return $this->_adodb->Close();
00129     }
00130 /**
00131  * Executes given SQL statement.
00132  *
00133  * @param string $sql SQL statement
00134  * @return resource Result resource identifier
00135  */
00136     function _execute($sql) {
00137         global $ADODB_FETCH_MODE;
00138         $ADODB_FETCH_MODE = ADODB_FETCH_ASSOC;
00139         return $this->_adodb->execute($sql);
00140     }
00141 /**
00142  * Returns a row from given resultset as an array .
00143  *
00144  * @return array The fetched row as an array
00145  */
00146 /**
00147  * Returns a row from current resultset as an array .
00148  *
00149  * @return array The fetched row as an array
00150  */
00151     function fetchRow($sql = null) {
00152 
00153         if (!empty($sql) && is_string($sql) && strlen($sql) > 5) {
00154             if (!$this->execute($sql)) {
00155                 return null;
00156             }
00157         }
00158 
00159         if (!$this->hasResult()) {
00160             return null;
00161         } else {
00162             $resultRow = $this->_result->FetchRow();
00163             $this->resultSet($resultRow);
00164             return $this->fetchResult();
00165         }
00166     }
00167 /**
00168  * Begin a transaction
00169  *
00170  * @param unknown_type $model
00171  * @return boolean True on success, false on fail
00172  * (i.e. if the database/model does not support transactions).
00173  */
00174     function begin(&$model) {
00175         if (parent::begin($model)) {
00176             if ($this->_adodb->BeginTrans()) {
00177                 $this->_transactionStarted = true;
00178                 return true;
00179             }
00180         }
00181         return false;
00182     }
00183 /**
00184  * Commit a transaction
00185  *
00186  * @param unknown_type $model
00187  * @return boolean True on success, false on fail
00188  * (i.e. if the database/model does not support transactions,
00189  * or a transaction has not started).
00190  */
00191     function commit(&$model) {
00192         if (parent::commit($model)) {
00193             $this->_transactionStarted = false;
00194             return $this->_adodb->CommitTrans();
00195         }
00196         return false;
00197     }
00198 /**
00199  * Rollback a transaction
00200  *
00201  * @param unknown_type $model
00202  * @return boolean True on success, false on fail
00203  * (i.e. if the database/model does not support transactions,
00204  * or a transaction has not started).
00205  */
00206     function rollback(&$model) {
00207         if (parent::rollback($model)) {
00208             return $this->_adodb->RollbackTrans();
00209         }
00210         return false;
00211     }
00212 /**
00213  * Returns an array of tables in the database. If there are no tables, an error is raised and the application exits.
00214  *
00215  * @return array Array of tablenames in the database
00216  */
00217     function listSources() {
00218         $tables = $this->_adodb->MetaTables('TABLES');
00219 
00220         if (!sizeof($tables) > 0) {
00221             trigger_error(ERROR_NO_TABLE_LIST, E_USER_NOTICE);
00222             exit;
00223         }
00224         return $tables;
00225     }
00226 /**
00227  * Returns an array of the fields in the table used by the given model.
00228  *
00229  * @param AppModel $model Model object
00230  * @return array Fields in table. Keys are name and type
00231  */
00232     function describe(&$model) {
00233         $cache = parent::describe($model);
00234         if ($cache != null) {
00235             return $cache;
00236         }
00237 
00238         $fields = false;
00239         $cols = $this->_adodb->MetaColumns($this->fullTableName($model, false));
00240 
00241         foreach ($cols as $column) {
00242             $fields[$column->name] = array(
00243                                         'type' => $this->column($column->type),
00244                                         'null' => !$column->not_null,
00245                                         'length' => $column->max_length,
00246                                     );
00247             if ($column->has_default) {
00248                 $fields[$column->name]['default'] = $column->default_value;
00249             }
00250             if ($column->primary_key == 1) {
00251                 $fields[$column->name]['key'] = 'primary';
00252             }
00253         }
00254 
00255         $this->__cacheDescription($this->fullTableName($model, false), $fields);
00256         return $fields;
00257     }
00258 /**
00259  * Returns a formatted error message from previous database operation.
00260  *
00261  * @return string Error message
00262  */
00263     function lastError() {
00264         return $this->_adodb->ErrorMsg();
00265     }
00266 /**
00267  * Returns number of affected rows in previous database operation, or false if no previous operation exists.
00268  *
00269  * @return integer Number of affected rows
00270  */
00271     function lastAffected() {
00272         return $this->_adodb->Affected_Rows();
00273     }
00274 /**
00275  * Returns number of rows in previous resultset, or false if no previous resultset exists.
00276  *
00277  * @return integer Number of rows in resultset
00278  */
00279     function lastNumRows() {
00280         return $this->_result ? $this->_result->RecordCount() : false;
00281     }
00282 /**
00283  * Returns the ID generated from the previous INSERT operation.
00284  *
00285  * @return int
00286  *
00287  * @Returns the last autonumbering ID inserted. Returns false if function not supported.
00288  */
00289     function lastInsertId() {
00290         return $this->_adodb->Insert_ID();
00291     }
00292 /**
00293  * Returns a LIMIT statement in the correct format for the particular database.
00294  *
00295  * @param integer $limit Limit of results returned
00296  * @param integer $offset Offset from which to start results
00297  * @return string SQL limit/offset statement
00298  * @todo Please change output string to whatever select your database accepts. adodb doesn't allow us to get the correct limit string out of it.
00299  */
00300     function limit($limit, $offset = null) {
00301         if ($limit) {
00302             $rt = '';
00303             if (!strpos(strtolower($limit), 'limit') || strpos(strtolower($limit), 'limit') === 0) {
00304                 $rt = ' LIMIT';
00305             }
00306 
00307             if ($offset) {
00308                 $rt .= ' ' . $offset . ',';
00309             }
00310 
00311             $rt .= ' ' . $limit;
00312             return $rt;
00313         }
00314         return null;
00315         // please change to whatever select your database accepts
00316         // adodb doesn't allow us to get the correct limit string out of it
00317     }
00318 /**
00319  * Converts database-layer column types to basic types
00320  *
00321  * @param string $real Real database-layer column type (i.e. "varchar(255)")
00322  * @return string Abstract column type (i.e. "string")
00323  */
00324     function column($real) {
00325         $metaTypes = array_flip($this->_adodbColumnTypes);
00326 
00327         $interpreted_type = $this->_adodbMetatyper->MetaType($real);
00328 
00329         if (!isset($metaTypes[$interpreted_type])) {
00330             return 'text';
00331         }
00332         return $metaTypes[$interpreted_type];
00333     }
00334 /**
00335  * Returns a quoted and escaped string of $data for use in an SQL statement.
00336  *
00337  * @param string $data String to be prepared for use in an SQL statement
00338  * @param string $column_type The type of the column into which this data will be inserted
00339  * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
00340  * @return string Quoted and escaped data
00341  */
00342     function value($data, $column = null, $safe = false) {
00343         $parent = parent::value($data, $column, $safe);
00344         if ($parent != null) {
00345             return $parent;
00346         }
00347 
00348         if ($data === null) {
00349             return 'NULL';
00350         }
00351 
00352         if ($data === '') {
00353             return "''";
00354         }
00355         return $this->_adodb->qstr($data);
00356     }
00357 
00358 /**
00359  * Generates the fields list of an SQL query.
00360  *
00361  * @param Model $model
00362  * @param string $alias Alias tablename
00363  * @param mixed $fields
00364  * @return array
00365  */
00366     function fields(&$model, $alias = null, $fields = array(), $quote = true) {
00367         if (empty($alias)) {
00368             $alias = $model->alias;
00369         }
00370         $fields = parent::fields($model, $alias, $fields, false);
00371 
00372         if (!$quote) {
00373             return $fields;
00374         }
00375         $count = count($fields);
00376 
00377         if ($count >= 1 && $fields[0] != '*' && strpos($fields[0], 'COUNT(*)') === false) {
00378             for ($i = 0; $i < $count; $i++) {
00379                 if (!preg_match('/^.+\\(.*\\)/', $fields[$i]) && !preg_match('/\s+AS\s+/', $fields[$i])) {
00380                     $prepend = '';
00381                     if (strpos($fields[$i], 'DISTINCT') !== false) {
00382                         $prepend = 'DISTINCT ';
00383                         $fields[$i] = trim(str_replace('DISTINCT', '', $fields[$i]));
00384                     }
00385 
00386                     if (strrpos($fields[$i], '.') === false) {
00387                         $fields[$i] = $prepend . $this->name($alias) . '.' . $this->name($fields[$i]) . ' AS ' . $this->name($alias . '__' . $fields[$i]);
00388                     } else {
00389                         $build = explode('.', $fields[$i]);
00390                         $fields[$i] = $prepend . $this->name($build[0]) . '.' . $this->name($build[1]) . ' AS ' . $this->name($build[0] . '__' . $build[1]);
00391                     }
00392                 }
00393             }
00394         }
00395         return $fields;
00396     }
00397 /**
00398  * Build ResultSets and map data
00399  *
00400  * @param array $results
00401  */
00402     function resultSet(&$results) {
00403         $num_fields = count($results);
00404         $fields = array_keys($results);
00405         $this->results =& $results;
00406         $this->map = array();
00407         $index = 0;
00408         $j = 0;
00409 
00410         while ($j < $num_fields) {
00411             $columnName = $fields[$j];
00412 
00413             if (strpos($columnName, '__')) {
00414                 $parts = explode('__', $columnName);
00415                 $this->map[$index++] = array($parts[0], $parts[1]);
00416             } else {
00417                 $this->map[$index++] = array(0, $columnName);
00418             }
00419             $j++;
00420         }
00421     }
00422 /**
00423  * Fetches the next row from the current result set
00424  *
00425  * @return unknown
00426  */
00427     function fetchResult() {
00428         if (!empty($this->results) && $row = $this->results) {
00429             $resultRow = array();
00430             $fields = array_keys($row);
00431             $count = count($fields);
00432             $i = 0;
00433             for ($i = 0; $i < $count; $i++) { //$row as $index => $field) {
00434                 list($table, $column) = $this->map[$i];
00435                 $resultRow[$table][$column] = $row[$fields[$i]];
00436             }
00437             return $resultRow;
00438         } else {
00439             return false;
00440         }
00441     }
00442 
00443 /**
00444  * Generate a database-native column schema string
00445  *
00446  * @param array $column An array structured like the following: array('name'=>'value', 'type'=>'value'[, options]),
00447  *                      where options can be 'default', 'length', or 'key'.
00448  * @return string
00449  */
00450     function buildColumn($column) {
00451         $name = $type = null;
00452         extract(array_merge(array('null' => true), $column));
00453 
00454         if (empty($name) || empty($type)) {
00455             trigger_error('Column name or type not defined in schema', E_USER_WARNING);
00456             return null;
00457         }
00458 
00459         //$metaTypes = array_flip($this->_adodbColumnTypes);
00460         if (!isset($this->_adodbColumnTypes[$type])) {
00461             trigger_error("Column type {$type} does not exist", E_USER_WARNING);
00462             return null;
00463         }
00464         $metaType = $this->_adodbColumnTypes[$type];
00465         $concreteType = $this->_adodbDataDict->ActualType($metaType);
00466         $real = $this->columns[$type];
00467 
00468         //UUIDs are broken so fix them.
00469         if ($type == 'string' && isset($real['length']) && $real['length'] == 36) {
00470             $concreteType = 'CHAR';
00471         }
00472 
00473         $out = $this->name($name) . ' ' . $concreteType;
00474 
00475         if (isset($real['limit']) || isset($real['length']) || isset($column['limit']) || isset($column['length'])) {
00476             if (isset($column['length'])) {
00477                 $length = $column['length'];
00478             } elseif (isset($column['limit'])) {
00479                 $length = $column['limit'];
00480             } elseif (isset($real['length'])) {
00481                 $length = $real['length'];
00482             } else {
00483                 $length = $real['limit'];
00484             }
00485             $out .= '(' . $length . ')';
00486         }
00487         $_notNull = $_default = $_autoInc = $_constraint = $_unsigned = false;
00488 
00489         if (isset($column['key']) && $column['key'] == 'primary' && $type == 'integer') {
00490             $_constraint = '';
00491             $_autoInc = true;
00492         } elseif (isset($column['key']) && $column['key'] == 'primary') {
00493             $_notNull = '';
00494         } elseif (isset($column['default']) && isset($column['null']) && $column['null'] == false) {
00495             $_notNull = true;
00496             $_default = $column['default'];
00497         } elseif ( isset($column['null']) && $column['null'] == true) {
00498             $_notNull = false;
00499             $_default = 'NULL';
00500         }
00501         if (isset($column['default']) && $_default == false) {
00502             $_default = $this->value($column['default']);
00503         }
00504         if (isset($column['null']) && $column['null'] == false) {
00505             $_notNull = true;
00506         }
00507         //use concrete instance of DataDict to make the suffixes for us.
00508         $out .= $this->_adodbDataDict->_CreateSuffix($out, $metaType, $_notNull, $_default, $_autoInc, $_constraint, $_unsigned);
00509         return $out;
00510 
00511     }
00512 /**
00513  * Checks if the result is valid
00514  *
00515  * @return boolean True if the result is valid, else false
00516  */
00517     function hasResult() {
00518         return is_object($this->_result) && !$this->_result->EOF;
00519     }
00520 }
00521 ?>