dbo_adodb.php

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

Generated on Sun Nov 22 00:30:53 2009 for CakePHP 1.2.x.x (v1.2.4.8284) by doxygen 1.4.7