dbo_firebird.php

Go to the documentation of this file.
00001 <?php
00002 /* SVN FILE: $Id: dbo__firebird_8php-source.html 580 2008-07-01 14:45:49Z gwoo $ */
00003 /**
00004  * Firebird/Interbase 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.dbo
00023  * @since           CakePHP(tm) v 1.2.0.5152
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  * Short description for class.
00031  *
00032  * Long description for class
00033  *
00034  * @package     cake
00035  * @subpackage  cake.cake.libs.model.dbo
00036  */
00037 class DboFirebird extends DboSource {
00038 /**
00039  * Enter description here...
00040  *
00041  * @var unknown_type
00042  */
00043     var $description = "Firebird/Interbase DBO Driver";
00044 /**
00045  * Saves the original table name
00046  *
00047  * @var unknown_type
00048  */
00049     var $modeltmp = array();
00050 /**
00051  * Enter description here...
00052  *
00053  * @var unknown_type
00054  */
00055     var $startQuote = "\'";
00056 /**
00057  * Enter description here...
00058  *
00059  * @var unknown_type
00060  */
00061     var $endQuote = "\'";
00062 /**
00063  * Enter description here...
00064  *
00065  * @var unknown_type
00066  */
00067     var $alias = ' ';
00068 /**
00069  * Enter description here...
00070  *
00071  * @var unknown_type
00072  */
00073     var $goofyLimit = true;
00074 /**
00075  * Creates a map between field aliases and numeric indexes.
00076  *
00077  * @var array
00078  */
00079     var $__fieldMappings = array();
00080 /**
00081  * Base configuration settings for Firebird driver
00082  *
00083  * @var array
00084  */
00085     var $_baseConfig = array(
00086         'persistent' => true,
00087         'host' => 'localhost',
00088         'login' => 'SYSDBA',
00089         'password' => 'masterkey',
00090         'database' => 'c:\\CAKE.FDB',
00091         'port' => '3050',
00092         'connect' => 'ibase_connect'
00093     );
00094 /**
00095  * Firebird column definition
00096  *
00097  * @var array
00098  */
00099     var $columns = array(
00100         'primary_key' => array('name' => 'IDENTITY (1, 1) NOT NULL'),
00101         'string'    => array('name'  => 'varchar', 'limit' => '255'),
00102         'text'      => array('name' => 'BLOB SUB_TYPE 1 SEGMENT SIZE 100 CHARACTER SET NONE'),
00103         'integer'   => array('name' => 'integer'),
00104         'float'     => array('name' => 'float', 'formatter' => 'floatval'),
00105         'datetime'  => array('name' => 'timestamp', 'format'    => 'd.m.Y H:i:s', 'formatter' => 'date'),
00106         'timestamp' => array('name' => 'timestamp', 'format'     => 'd.m.Y H:i:s', 'formatter' => 'date'),
00107         'time'      => array('name' => 'time', 'format'    => 'H:i:s', 'formatter' => 'date'),
00108         'date'      => array('name' => 'date', 'format'    => 'd.m.Y', 'formatter' => 'date'),
00109         'binary'    => array('name' => 'blob'),
00110         'boolean'   => array('name' => 'smallint')
00111     );
00112 /**
00113  * Connects to the database using options in the given configuration array.
00114  *
00115  * @return boolean True if the database could be connected, else false
00116  */
00117     function connect() {
00118         $config = $this->config;
00119         $connect = $config['connect'];
00120 
00121         $this->connected = false;
00122         $this->connection = $connect($config['host'] . ':' . $config['database'], $config['login'], $config['password']);
00123         $this->connected = true;
00124     }
00125 /**
00126  * Disconnects from database.
00127  *
00128  * @return boolean True if the database could be disconnected, else false
00129  */
00130     function disconnect() {
00131         $this->connected = false;
00132         return @ibase_close($this->connection);
00133     }
00134 /**
00135  * Executes given SQL statement.
00136  *
00137  * @param string $sql SQL statement
00138  * @return resource Result resource identifier
00139  * @access protected
00140  */
00141     function _execute($sql) {
00142         if (strpos(strtolower($sql),"update") > 0) {
00143             break;
00144         }
00145         return @ibase_query($this->connection,  $sql);
00146     }
00147 /**
00148  * Returns a row from given resultset as an array .
00149  *
00150  * @return array The fetched row as an array
00151  */
00152     function fetchRow() {
00153         if ($this->hasResult()) {
00154             $this->resultSet($this->_result);
00155             $resultRow = $this->fetchResult();
00156             return $resultRow;
00157         } else {
00158             return null;
00159         }
00160     }
00161 /**
00162  * Returns an array of sources (tables) in the database.
00163  *
00164  * @return array Array of tablenames in the database
00165  */
00166     function listSources() {
00167         $cache = parent::listSources();
00168 
00169         if ($cache != null) {
00170             return $cache;
00171         }
00172         $sql = "select RDB" . "$" . "RELATION_NAME as name
00173                 FROM RDB" ."$" . "RELATIONS
00174                 Where RDB" . "$" . "SYSTEM_FLAG =0";
00175 
00176         $result = @ibase_query($this->connection,$sql);
00177         $tables = array();
00178         while ($row = ibase_fetch_row ($result)) {
00179             $tables[] = strtolower(trim($row[0]));
00180         }
00181         parent::listSources($tables);
00182         return $tables;
00183     }
00184 /**
00185  * Returns an array of the fields in given table name.
00186  *
00187  * @param Model $model Model object to describe
00188  * @return array Fields in table. Keys are name and type
00189  */
00190     function describe(&$model) {
00191         $this->modeltmp[$model->table] = $model->alias;
00192         $cache = parent::describe($model);
00193 
00194         if ($cache != null) {
00195             return $cache;
00196         }
00197         $fields = false;
00198         $sql = "SELECT * FROM " . $this->fullTableName($model, false);
00199         $rs = ibase_query($sql);
00200         $coln = ibase_num_fields($rs);
00201         $fields = false;
00202 
00203         for ($i = 0; $i < $coln; $i++) {
00204             $col_info = ibase_field_info($rs, $i);
00205             $fields[strtolower($col_info['name'])] = array(
00206                     'type' => $this->column($col_info['type']),
00207                     'null' => '',
00208                     'length' => $col_info['length']
00209                 );
00210         }
00211         $this->__cacheDescription($this->fullTableName($model, false), $fields);
00212         return $fields;
00213     }
00214 /**
00215  * Returns a quoted name of $data for use in an SQL statement.
00216  *
00217  * @param string $data Name (table.field) to be prepared for use in an SQL statement
00218  * @return string Quoted for Firebird
00219  */
00220     function name($data) {
00221         if ($data == '*') {
00222                 return '*';
00223         }
00224         $pos = strpos($data, '"');
00225 
00226         if ($pos === false) {
00227             if (!strpos($data, ".")) {
00228                 $data = '"' . strtoupper($data) . '"';
00229             } else {
00230                 $build = explode('.', $data);
00231                 $data = '"' . strtoupper($build[0]) . '"."' . strtoupper($build[1]) . '"';
00232             }
00233         }
00234         return $data;
00235     }
00236 /**
00237  * Returns a quoted and escaped string of $data for use in an SQL statement.
00238  *
00239  * @param string $data String to be prepared for use in an SQL statement
00240  * @param string $column The column into which this data will be inserted
00241  * @param boolean $safe Whether or not numeric data should be handled automagically if no column data is provided
00242  * @return string Quoted and escaped data
00243  */
00244     function value($data, $column = null, $safe = false) {
00245         $parent = parent::value($data, $column, $safe);
00246 
00247         if ($parent != null) {
00248             return $parent;
00249         }
00250         if ($data === null) {
00251             return 'NULL';
00252         }
00253         if ($data === '') {
00254             return "''";
00255         }
00256 
00257         switch($column) {
00258             case 'boolean':
00259                 $data = $this->boolean((bool)$data);
00260             break;
00261             default:
00262                 if (get_magic_quotes_gpc()) {
00263                     $data = stripslashes(str_replace("'", "''", $data));
00264                 } else {
00265                     $data = str_replace("'", "''", $data);
00266                 }
00267             break;
00268         }
00269         return "'" . $data . "'";
00270     }
00271 /**
00272  * Removes Identity (primary key) column from update data before returning to parent
00273  *
00274  * @param Model $model
00275  * @param array $fields
00276  * @param array $values
00277  * @return array
00278  */
00279     function update(&$model, $fields = array(), $values = array()) {
00280         foreach ($fields as $i => $field) {
00281             if ($field == $model->primaryKey) {
00282                 unset ($fields[$i]);
00283                 unset ($values[$i]);
00284                 break;
00285             }
00286         }
00287         return parent::update($model, $fields, $values);
00288     }
00289 /**
00290  * Returns a formatted error message from previous database operation.
00291  *
00292  * @return string Error message with error number
00293  */
00294     function lastError() {
00295         $error = ibase_errmsg();
00296 
00297         if ($error !== false) {
00298             return $error;
00299         }
00300         return null;
00301     }
00302 /**
00303  * Returns number of affected rows in previous database operation. If no previous operation exists,
00304  * this returns false.
00305  *
00306  * @return integer Number of affected rows
00307  */
00308     function lastAffected() {
00309         if ($this->_result) {
00310             return ibase_affected_rows($this->connection);
00311         }
00312         return null;
00313     }
00314 /**
00315  * Returns number of rows in previous resultset. If no previous resultset exists,
00316  * this returns false.
00317  *
00318  * @return integer Number of rows in resultset
00319  */
00320     function lastNumRows() {
00321         return $this->_result? /*ibase_affected_rows($this->_result)*/ 1: false;
00322     }
00323 /**
00324  * Returns the ID generated from the previous INSERT operation.
00325  *
00326  * @param unknown_type $source
00327  * @return in
00328  */
00329     function lastInsertId($source = null, $field = 'id') {
00330         $query = "SELECT RDB\$TRIGGER_SOURCE
00331         FROM RDB\$TRIGGERS WHERE RDB\$RELATION_NAME = '".  strtoupper($source) .  "' AND
00332         RDB\$SYSTEM_FLAG IS NULL AND  RDB\$TRIGGER_TYPE = 1 ";
00333 
00334         $result = @ibase_query($this->connection,$query);
00335         $generator = "";
00336 
00337         while ($row = ibase_fetch_row($result, IBASE_TEXT)) {
00338             if (strpos($row[0], "NEW." . strtoupper($field))) {
00339                 $pos = strpos($row[0], "GEN_ID(");
00340 
00341                 if ($pos > 0) {
00342                     $pos2 = strpos($row[0],",",$pos + 7);
00343 
00344                     if ($pos2 > 0) {
00345                         $generator = substr($row[0], $pos +7, $pos2 - $pos- 7);
00346                     }
00347                 }
00348                 break;
00349             }
00350         }
00351 
00352         if (!empty($generator)) {
00353             $sql = "SELECT GEN_ID(". $generator  . ",0) AS maxi FROM RDB" . "$" . "DATABASE";
00354             $res = $this->rawQuery($sql);
00355             $data = $this->fetchRow($res);
00356             return $data['maxi'];
00357         } else {
00358             return false;
00359         }
00360     }
00361 /**
00362  * Returns a limit statement in the correct format for the particular database.
00363  *
00364  * @param integer $limit Limit of results returned
00365  * @param integer $offset Offset from which to start results
00366  * @return string SQL limit/offset statement
00367  */
00368     function limit($limit, $offset = null) {
00369         if ($limit) {
00370             $rt = '';
00371 
00372             if (!strpos(strtolower($limit), 'top') || strpos(strtolower($limit), 'top') === 0) {
00373                 $rt = ' FIRST';
00374             }
00375             $rt .= ' ' . $limit;
00376 
00377             if (is_int($offset) && $offset > 0) {
00378                 $rt .= ' SKIP ' . $offset;
00379             }
00380             return $rt;
00381         }
00382         return null;
00383     }
00384 /**
00385  * Converts database-layer column types to basic types
00386  *
00387  * @param string $real Real database-layer column type (i.e. "varchar(255)")
00388  * @return string Abstract column type (i.e. "string")
00389  */
00390     function column($real) {
00391         if (is_array($real)) {
00392             $col = $real['name'];
00393 
00394             if (isset($real['limit'])) {
00395                 $col .= '(' . $real['limit'] . ')';
00396             }
00397             return $col;
00398         }
00399 
00400         $col = str_replace(')', '', $real);
00401         $limit = null;
00402         if (strpos($col, '(') !== false) {
00403             list($col, $limit) = explode('(', $col);
00404         }
00405 
00406         if (in_array($col, array('DATE', 'TIME'))) {
00407             return strtolower($col);
00408         }
00409         if ($col == 'TIMESTAMP') {
00410             return 'datetime';
00411         }
00412         if ($col == 'SMALLINT') {
00413             return 'boolean';
00414         }
00415         if (strpos($col, 'int') !== false || $col == 'numeric' || $col == 'INTEGER') {
00416             return 'integer';
00417         }
00418         if (strpos($col, 'char') !== false) {
00419             return 'string';
00420         }
00421         if (strpos($col, 'text') !== false) {
00422             return 'text';
00423         }
00424         if (strpos($col, 'VARCHAR') !== false) {
00425             return 'string';
00426         }
00427         if (strpos($col, 'BLOB') !== false) {
00428             return 'text';
00429         }
00430         if (in_array($col, array('FLOAT', 'NUMERIC', 'DECIMAL'))) {
00431             return 'float';
00432         }
00433         return 'text';
00434     }
00435 /**
00436  * Enter description here...
00437  *
00438  * @param unknown_type $results
00439  */
00440     function resultSet(&$results) {
00441         $this->results =& $results;
00442         $this->map = array();
00443         $num_fields = ibase_num_fields($results);
00444         $index = 0;
00445         $j = 0;
00446 
00447         while ($j < $num_fields) {
00448             $column = ibase_field_info($results, $j);
00449             if (!empty($column[2])) {
00450                 $this->map[$index++] = array(ucfirst(strtolower($this->modeltmp[strtolower($column[2])])), strtolower($column[1]));
00451             } else {
00452                 $this->map[$index++] = array(0, strtolower($column[1]));
00453             }
00454             $j++;
00455         }
00456     }
00457 /**
00458  * Builds final SQL statement
00459  *
00460  * @param string $type Query type
00461  * @param array $data Query data
00462  * @return string
00463  */
00464     function renderStatement($type, $data) {
00465         extract($data);
00466 
00467         if (strtolower($type) == 'select') {
00468             if (preg_match('/offset\s+([0-9]+)/i', $limit, $offset)) {
00469                 $limit = preg_replace('/\s*offset.*$/i', '', $limit);
00470                 preg_match('/top\s+([0-9]+)/i', $limit, $limitVal);
00471                 $offset = intval($offset[1]) + intval($limitVal[1]);
00472                 $rOrder = $this->__switchSort($order);
00473                 list($order2, $rOrder) = array($this->__mapFields($order), $this->__mapFields($rOrder));
00474                 return "SELECT * FROM (SELECT {$limit} * FROM (SELECT TOP {$offset} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}) AS Set1 {$rOrder}) AS Set2 {$order2}";
00475             } else {
00476                 return "SELECT {$limit} {$fields} FROM {$table} {$alias} {$joins} {$conditions} {$order}";
00477             }
00478         } else {
00479             return parent::renderStatement($type, $data);
00480         }
00481     }
00482 /**
00483  * Fetches the next row from the current result set
00484  *
00485  * @return unknown
00486  */
00487     function fetchResult() {
00488         if ($row = ibase_fetch_row($this->results, IBASE_TEXT)) {
00489             $resultRow = array();
00490             $i = 0;
00491 
00492             foreach ($row as $index => $field) {
00493                 list($table, $column) = $this->map[$index];
00494 
00495                 if (trim($table) == "") {
00496                     $resultRow[0][$column] = $row[$index];
00497                 } else {
00498                     $resultRow[$table][$column] = $row[$index];
00499                     $i++;
00500                 }
00501             }
00502             return $resultRow;
00503         } else {
00504             return false;
00505         }
00506     }
00507 }
00508 ?>