1<?php 2// +----------------------------------------------------------------------+ 3// | PHP Version 4 | 4// +----------------------------------------------------------------------+ 5// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox, | 6// | Stig. S. Bakken, Lukas Smith | 7// | All rights reserved. | 8// +----------------------------------------------------------------------+ 9// | MDB is a merge of PEAR DB and Metabases that provides a unified DB | 10// | API as well as database abstraction for PHP applications. | 11// | This LICENSE is in the BSD license style. | 12// | | 13// | Redistribution and use in source and binary forms, with or without | 14// | modification, are permitted provided that the following conditions | 15// | are met: | 16// | | 17// | Redistributions of source code must retain the above copyright | 18// | notice, this list of conditions and the following disclaimer. | 19// | | 20// | Redistributions in binary form must reproduce the above copyright | 21// | notice, this list of conditions and the following disclaimer in the | 22// | documentation and/or other materials provided with the distribution. | 23// | | 24// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken, | 25// | Lukas Smith nor the names of his contributors may be used to endorse | 26// | or promote products derived from this software without specific prior| 27// | written permission. | 28// | | 29// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 30// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 31// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | 32// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | 33// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | 34// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | 35// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS| 36// | OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | 37// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | 38// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY| 39// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 40// | POSSIBILITY OF SUCH DAMAGE. | 41// +----------------------------------------------------------------------+ 42// | Author: Lorenzo Alberton <l.alberton@quipo.it> | 43// +----------------------------------------------------------------------+ 44// 45// $Id: ibase.php,v 1.9.4.29 2004/03/31 20:00:55 quipo Exp $ 46 47require_once 'MDB/Common.php'; 48 49/** 50 * MDB FireBird/InterBase driver 51 * 52 * Notes: 53 * - when fetching in associative mode all keys are lowercased. 54 * 55 * - Currently, the driver relies on the Interbase server to use SQL dialect 3 56 * that was introduced with Interbase 6. Some versions of Interbase server, 57 * like the Super Server, do not seem to work by default with dialect 3. 58 * This may lead to errors when trying to create tables using Interbase SQL 59 * data types that are only available when using this dialect version. 60 * 61 * - Interbase does not support per field index sorting support. Indexes are 62 * either ascending, descending or both even when they are defined from more 63 * than one field. Currently Metabase Interbase driver uses the index sorting 64 * type given by the first field of the index for which it is specified the 65 * sorting type. 66 * 67 * - The numRows method is emulated by fetching all the rows into memory. 68 * Avoid using it if for queries with large result sets. 69 * 70 * - Interbase does not provide direct support for returning result sets 71 restrictedto a given range. Such support is emulated in the MDB ibase driver. 72 * 73 * - Current Interbase versions do not support altering table field DEFAULT 74 * values and NOT NULL constraint. Text fields' length may only be raised in 75 * increments defined by Interbase, so the Metabase Interbase does not support 76 * altering text field length yet. 77 * 78 * - createDatabase and dropDatabase are not supported 79 * 80 * - MDB creates Interbase blobs before executing a prepared queries to insert 81 * or update large object fields. If such queries fail to execute, MDB 82 * Interbase driver class is not able to reclaim the database space allocated 83 * for the large object values because there is currently no PHP function to do so. 84 * 85 * @package MDB 86 * @category Database 87 * @author Lorenzo Alberton <l.alberton@quipo.it> 88 */ 89 90class MDB_ibase extends MDB_Common 91{ 92 var $connection = 0; 93 var $connected_host; 94 var $connected_port; 95 var $selected_database = ''; 96 var $selected_database_file = ''; 97 var $opened_persistent = ''; 98 var $transaction_id = 0; 99 100 var $escape_quotes = "'"; 101 var $decimal_factor = 1.0; 102 103 var $results = array(); 104 var $current_row = array(); 105 var $columns = array(); 106 var $rows = array(); 107 var $limits = array(); 108 var $row_buffer = array(); 109 var $highest_fetched_row = array(); 110 var $query_parameters = array(); 111 var $query_parameter_values = array(); 112 113 // }}} 114 // {{{ constructor 115 116 /** 117 * Constructor 118 */ 119 function MDB_ibase() 120 { 121 $this->MDB_Common(); 122 $this->phptype = 'ibase'; 123 $this->dbsyntax = 'ibase'; 124 125 $this->supported['Sequences'] = 1; 126 $this->supported['Indexes'] = 1; 127 $this->supported['SummaryFunctions'] = 1; 128 $this->supported['OrderByText'] = 1; 129 $this->supported['Transactions'] = 1; 130 $this->supported['CurrId'] = 0; 131 $this->supported['AffectedRows'] = 0; 132 $this->supported['SelectRowRanges'] = 1; 133 $this->supported['LOBs'] = 1; 134 $this->supported['Replace'] = 1; 135 $this->supported['SubSelects'] = 1; 136 137 $this->decimal_factor = pow(10.0, $this->decimal_places); 138 139 $this->options['DatabasePath'] = ''; 140 $this->options['DatabaseExtension'] = '.gdb'; 141 $this->options['DBAUser'] = FALSE; 142 $this->options['DBAPassword'] = FALSE; 143 144 $this->errorcode_map = array( 145 -104 => MDB_ERROR_SYNTAX, 146 -150 => MDB_ERROR_ACCESS_VIOLATION, 147 -151 => MDB_ERROR_ACCESS_VIOLATION, 148 -155 => MDB_ERROR_NOSUCHTABLE, 149 88 => MDB_ERROR_NOSUCHTABLE, 150 -157 => MDB_ERROR_NOSUCHFIELD, 151 -158 => MDB_ERROR_VALUE_COUNT_ON_ROW, 152 -170 => MDB_ERROR_MISMATCH, 153 -171 => MDB_ERROR_MISMATCH, 154 -172 => MDB_ERROR_INVALID, 155 -204 => MDB_ERROR_INVALID, 156 -205 => MDB_ERROR_NOSUCHFIELD, 157 -206 => MDB_ERROR_NOSUCHFIELD, 158 -208 => MDB_ERROR_INVALID, 159 -219 => MDB_ERROR_NOSUCHTABLE, 160 -297 => MDB_ERROR_CONSTRAINT, 161 -530 => MDB_ERROR_CONSTRAINT, 162 -551 => MDB_ERROR_ACCESS_VIOLATION, 163 -552 => MDB_ERROR_ACCESS_VIOLATION, 164 -607 => MDB_ERROR_NOSUCHTABLE, 165 -803 => MDB_ERROR_CONSTRAINT, 166 -913 => MDB_ERROR_DEADLOCK, 167 -922 => MDB_ERROR_NOSUCHDB, 168 -923 => MDB_ERROR_CONNECT_FAILED, 169 -924 => MDB_ERROR_CONNECT_FAILED 170 ); 171 172 } 173 174 // }}} 175 // {{{ errorCode() 176 177 /** 178 * Map native error codes to DB's portable ones. Requires that 179 * the DB implementation's constructor fills in the $errorcode_map 180 * property. 181 * 182 * @param $nativecode the native error code, as returned by the backend 183 * database extension (string or integer) 184 * @return int a portable MDB error code, or FALSE if this DB 185 * implementation has no mapping for the given error code. 186 */ 187 function errorCode($errormsg) 188 { 189 // memo for the interbase php module hackers: we need something similar 190 // to mysql_errno() to retrieve error codes instead of this ugly hack 191 if (preg_match('/^([^0-9\-]+)([0-9\-]+)\s+(.*)$/', $errormsg, $match)) { 192 $errno = (int)$match[2]; 193 } else { 194 $errno = NULL; 195 } 196 switch ($errno) { 197 case -204: 198 if (is_int(strpos($match[3], 'Table unknown'))) { 199 return MDB_ERROR_NOSUCHTABLE; 200 } 201 break; 202 default: 203 if (isset($this->errorcode_map[$errno])) { 204 return($this->errorcode_map[$errno]); 205 } 206 static $error_regexps; 207 if (empty($error_regexps)) { 208 $error_regexps = array( 209 '/[tT]able not found/' => MDB_ERROR_NOSUCHTABLE, 210 '/[tT]able unknown/' => MDB_ERROR_NOSUCHTABLE, 211 '/[tT]able .* already exists/' => MDB_ERROR_ALREADY_EXISTS, 212 '/validation error for column .* value "\*\*\* null/' => MDB_ERROR_CONSTRAINT_NOT_NULL, 213 '/violation of [\w ]+ constraint/' => MDB_ERROR_CONSTRAINT, 214 '/conversion error from string/' => MDB_ERROR_INVALID_NUMBER, 215 '/no permission for/' => MDB_ERROR_ACCESS_VIOLATION, 216 '/arithmetic exception, numeric overflow, or string truncation/' => MDB_ERROR_DIVZERO, 217 '/deadlock/' => MDB_ERROR_DEADLOCK, 218 '/attempt to store duplicate value/' => MDB_ERROR_CONSTRAINT 219 ); 220 } 221 foreach ($error_regexps as $regexp => $code) { 222 if (preg_match($regexp, $errormsg)) { 223 return $code; 224 } 225 } 226 } 227 // Fall back to MDB_ERROR if there was no mapping. 228 return MDB_ERROR; 229 } 230 231 // }}} 232 // {{{ ibaseRaiseError() 233 234 /** 235 * This method is used to communicate an error and invoke error 236 * callbacks etc. Basically a wrapper for MDB::raiseError 237 * that checks for native error msgs. 238 * 239 * @param integer $errno error code 240 * @param string $message userinfo message 241 * @return object a PEAR error object 242 * @access public 243 * @see PEAR_Error 244 */ 245 function ibaseRaiseError($errno = NULL, $message = NULL) 246 { 247 $error = $this->errorNative(); 248 return($this->raiseError($this->errorCode($error), NULL, NULL, 249 $message, $error)); 250 } 251 252 // }}} 253 // {{{ errorNative() 254 255 /** 256 * Get the native error code of the last error (if any) that 257 * occured on the current connection. 258 * 259 * @access public 260 * @return int native ibase error code 261 */ 262 function errorNative() 263 { 264 return @ibase_errmsg(); 265 } 266 267 // }}} 268 // {{{ autoCommit() 269 270 /** 271 * Define whether database changes done on the database be automatically 272 * committed. This function may also implicitly start or end a transaction. 273 * 274 * @param boolean $auto_commit flag that indicates whether the database 275 * changes should be committed right after executing every query 276 * statement. If this argument is 0 a transaction implicitly started. 277 * Otherwise, if a transaction is in progress it is ended by committing 278 * any database changes that were pending. 279 * @return mixed MDB_OK on success, a MDB error on failure 280 * @access public 281 */ 282 function autoCommit($auto_commit) 283 { 284 $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off')); 285 if ((!$this->auto_commit) == (!$auto_commit)) { 286 return MDB_OK; 287 } 288 if ($this->connection && $auto_commit && MDB::isError($commit = $this->commit())) { 289 return($commit); 290 } 291 $this->auto_commit = $auto_commit; 292 $this->in_transaction = !$auto_commit; 293 return MDB_OK; 294 } 295 296 // }}} 297 // {{{ commit() 298 299 /** 300 * Commit the database changes done during a transaction that is in 301 * progress. This function may only be called when auto-committing is 302 * disabled, otherwise it will fail. Therefore, a new transaction is 303 * implicitly started after committing the pending changes. 304 * 305 * @return mixed MDB_OK on success, a MDB error on failure 306 * @access public 307 */ 308 function commit() 309 { 310 $this->debug('Commit Transaction'); 311 if ($this->auto_commit) { 312 return($this->raiseError(MDB_ERROR, NULL, NULL, 313 'Commit: transaction changes are being auto commited')); 314 } 315 return @ibase_commit($this->connection); 316 } 317 318 // }}} 319 // {{{ rollback() 320 321 /** 322 * Cancel any database changes done during a transaction that is in 323 * progress. This function may only be called when auto-committing is 324 * disabled, otherwise it will fail. Therefore, a new transaction is 325 * implicitly started after canceling the pending changes. 326 * 327 * @return mixed MDB_OK on success, a MDB error on failure 328 * @access public 329 */ 330 function rollback() 331 { 332 $this->debug('Rollback Transaction'); 333 if ($this->auto_commit) { 334 return($this->raiseError(MDB_ERROR, NULL, NULL, 335 'Rollback: transactions can not be rolled back when changes are auto commited')); 336 } 337 338 //return ibase_rollback($this->connection); 339 340 if ($this->transaction_id && !@ibase_rollback($this->connection)) { 341 return($this->raiseError(MDB_ERROR, NULL, NULL, 342 'Rollback: Could not rollback a pending transaction: '.@ibase_errmsg())); 343 } 344 if (!$this->transaction_id = @ibase_trans(IBASE_COMMITTED, $this->connection)) { 345 return($this->raiseError(MDB_ERROR, NULL, NULL, 346 'Rollback: Could not start a new transaction: '.@ibase_errmsg())); 347 } 348 return MDB_OK; 349 } 350 351 // }}} 352 // {{{ getDatabaseFile() 353 354 function getDatabaseFile($database_name) 355 { 356 if (isset($this->options['DatabasePath'])) { 357 $this->database_path = $this->options['DatabasePath']; 358 } 359 if (isset($this->options['DatabaseExtension'])) { 360 $this->database_extension = $this->options['DatabaseExtension']; 361 } 362 //$this->database_path = (isset($this->options['DatabasePath']) ? $this->options['DatabasePath'] : ''); 363 //$this->database_extension = (isset($this->options['DatabaseExtension']) ? $this->options['DatabaseExtension'] : '.gdb'); 364 365 //$database_path = (isset($this->options['DatabasePath']) ? $this->options['DatabasePath'] : ''); 366 //$database_extension = (isset($this->options['DatabaseExtension']) ? $this->options['DatabaseExtension'] : '.gdb'); 367 return $this->database_path.$database_name.$this->database_extension; 368 } 369 370 // }}} 371 // {{{ _doConnect() 372 373 /** 374 * Does the grunt work of connecting to the database 375 * 376 * @return mixed connection resource on success, MDB_Error on failure 377 * @access private 378 **/ 379 function _doConnect($database_name, $persistent) 380 { 381 $function = ($persistent ? 'ibase_pconnect' : 'ibase_connect'); 382 if (!function_exists($function)) { 383 return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL, 384 'doConnect: FireBird/InterBase support is not available in this PHP configuration')); 385 } 386 387 $dbhost = $this->host ? 388 ($this->host . ':' . $database_name) : 389 $database_name; 390 391 $params = array(); 392 $params[] = $dbhost; 393 $params[] = !empty($this->user) ? $this->user : NULL; 394 $params[] = !empty($this->password) ? $this->password : NULL; 395 396 $connection = @call_user_func_array($function, $params); 397 if ($connection > 0) { 398 @ibase_timefmt("%Y-%m-%d %H:%M:%S", IBASE_TIMESTAMP); 399 @ibase_timefmt("%Y-%m-%d", IBASE_DATE); 400 return $connection; 401 } 402 if (isset($php_errormsg)) { 403 $error_msg = $php_errormsg; 404 } else { 405 $error_msg = 'Could not connect to FireBird/InterBase server'; 406 } 407 return($this->raiseError(MDB_ERROR_CONNECT_FAILED, NULL, NULL, 408 'doConnect: '.$error_msg)); 409 } 410 411 // }}} 412 // {{{ connect() 413 414 /** 415 * Connect to the database 416 * 417 * @return TRUE on success, MDB_Error on failure 418 * @access public 419 **/ 420 function connect() 421 { 422 $port = (isset($this->options['port']) ? $this->options['port'] : ''); 423 424 $database_file = $this->getDatabaseFile($this->database_name); 425 426 if ($this->connection != 0) { 427 if (!strcmp($this->connected_host, $this->host) 428 && !strcmp($this->connected_port, $port) 429 && !strcmp($this->selected_database_file, $database_file) 430 && ($this->opened_persistent == $this->options['persistent'])) 431 { 432 return MDB_OK; 433 } 434 @ibase_close($this->connection); 435 $this->affected_rows = -1; 436 $this->connection = 0; 437 } 438 $connection = $this->_doConnect($database_file, $this->options['persistent']); 439 if (MDB::isError($connection)) { 440 return $connection; 441 } 442 $this->connection = $connection; 443 444 //the if below was added after PEAR::DB. Review me!! 445 if ($this->dbsyntax == 'fbird') { 446 $this->supported['limit'] = 'alter'; 447 } 448 449 if (!$this->auto_commit && MDB::isError($trans_result = $this->_doQuery('BEGIN'))) { 450 @ibase_close($this->connection); 451 $this->connection = 0; 452 $this->affected_rows = -1; 453 return $trans_result; 454 } 455 $this->connected_host = $this->host; 456 $this->connected_port = $port; 457 $this->selected_database_file = $database_file; 458 $this->opened_persistent = $this->options['persistent']; 459 return MDB_OK; 460 } 461 462 // }}} 463 // {{{ _close() 464 /** 465 * Close the database connection 466 * 467 * @return boolean 468 * @access private 469 **/ 470 function _close() 471 { 472 if ($this->connection != 0) { 473 if (!$this->auto_commit) { 474 $this->_doQuery('END'); 475 } 476 @ibase_close($this->connection); 477 $this->connection = 0; 478 $this->affected_rows = -1; 479 480 unset($GLOBALS['_MDB_databases'][$this->database]); 481 return true; 482 } 483 return false; 484 } 485 486 // }}} 487 // {{{ _doQuery() 488 489 /** 490 * Execute a query 491 * @param string $query the SQL query 492 * @return mixed result identifier if query executed, else MDB_error 493 * @access private 494 **/ 495 function _doQuery($query, $first=0, $limit=0, $prepared_query=0) // function _doQuery($query) 496 { 497 $connection = ($this->auto_commit ? $this->connection : $this->transaction_id); 498 if ($prepared_query 499 && isset($this->query_parameters[$prepared_query]) 500 && count($this->query_parameters[$prepared_query]) > 2) 501 { 502 503 $this->query_parameters[$prepared_query][0] = $connection; 504 $this->query_parameters[$prepared_query][1] = $query; 505 $result = @call_user_func_array("ibase_query", $this->query_parameters[$prepared_query]); 506 } else { 507 //Not Prepared Query 508 $result = @ibase_query($connection, $query); 509 while (@ibase_errmsg() == 'Query argument missed') { //ibase_errcode() only available in PHP5 510 //connection lost, try again... 511 $this->connect(); 512 //rollback the failed transaction to prevent deadlock and execute the query again 513 if ($this->transaction_id) { 514 $this->rollback(); 515 } 516 $result = @ibase_query($this->connection, $query); 517 } 518 } 519 if ($result) { 520 if (!MDB::isManip($query)) { 521 $result_value = intval($result); 522 $this->current_row[$result_value] = -1; 523 if ($limit > 0) { 524 $this->limits[$result_value] = array($first, $limit, 0); 525 } 526 $this->highest_fetched_row[$result_value] = -1; 527 } else { 528 $this->affected_rows = -1; 529 } 530 } else { 531 return ($this->raiseError(MDB_ERROR, NULL, NULL, 532 '_doQuery: Could not execute query ("'.$query.'"): ' . @ibase_errmsg())); 533 } 534 return $result; 535 } 536 537 // }}} 538 // {{{ query() 539 540 /** 541 * Send a query to the database and return any results 542 * 543 * @param string $query the SQL query 544 * @param array $types array that contains the types of the columns in 545 * the result set 546 * @return mixed result identifier if query executed, else MDB_error 547 * @access public 548 **/ 549 function query($query, $types = NULL) 550 { 551 $this->debug('Query: '.$query); 552 $this->last_query = $query; 553 $first = $this->first_selected_row; 554 $limit = $this->selected_row_limit; 555 $this->first_selected_row = $this->selected_row_limit = 0; 556 $connected = $this->connect(); 557 if (MDB::isError($connected)) { 558 return $connected; 559 } 560 561 if (!MDB::isError($result = $this->_doQuery($query, $first, $limit, 0))) { 562 if ($types != NULL) { 563 if (!is_array($types)) { 564 $types = array($types); 565 } 566 if (MDB::isError($err = $this->setResultTypes($result, $types))) { 567 $this->freeResult($result); 568 return $err; 569 } 570 } 571 return $result; 572 } 573 return $this->ibaseRaiseError(); 574 575 } 576 577 // }}} 578 // {{{ _executePreparedQuery() 579 580 /** 581 * Execute a prepared query statement. 582 * 583 * @param int $prepared_query argument is a handle that was returned by 584 * the function prepareQuery() 585 * @param string $query query to be executed 586 * @param array $types array that contains the types of the columns in 587 * the result set 588 * @return mixed a result handle or MDB_OK on success, a MDB error on failure 589 * @access private 590 */ 591 function _executePreparedQuery($prepared_query, $query) 592 { 593 $first = $this->first_selected_row; 594 $limit = $this->selected_row_limit; 595 $this->first_selected_row = $this->selected_row_limit = 0; 596 if (MDB::isError($connect = $this->connect())) { 597 return $connect; 598 } 599 return($this->_doQuery($query, $first, $limit, $prepared_query)); 600 } 601 602 // }}} 603 // {{{ _skipLimitOffset() 604 605 /** 606 * Skip the first row of a result set. 607 * 608 * @param resource $result 609 * @return mixed a result handle or MDB_OK on success, a MDB error on failure 610 * @access private 611 */ 612 function _skipLimitOffset($result) 613 { 614 $result_value = intval($result); 615 $first = $this->limits[$result_value][0]; 616 for (; $this->limits[$result_value][2] < $first; $this->limits[$result_value][2]++) { 617 if (!is_array(@ibase_fetch_row($result))) { 618 $this->limits[$result_value][2] = $first; 619 return($this->raiseError(MDB_ERROR, NULL, NULL, 620 'Skip first rows: could not skip a query result row')); 621 } 622 } 623 return MDB_OK; 624 } 625 626 // }}} 627 // {{{ getColumnNames() 628 629 /** 630 * Retrieve the names of columns returned by the DBMS in a query result. 631 * 632 * @param resource $result result identifier 633 * @return mixed an associative array variable 634 * that will hold the names of columns.The 635 * indexes of the array are the column names 636 * mapped to lower case and the values are the 637 * respective numbers of the columns starting 638 * from 0. Some DBMS may not return any 639 * columns when the result set does not 640 * contain any rows. 641 * 642 * a MDB error on failure 643 * @access public 644 */ 645 function getColumnNames($result) 646 { 647 $result_value = intval($result); 648 if (!isset($this->highest_fetched_row[$result_value])) { 649 return($this->raiseError(MDB_ERROR, NULL, NULL, 650 'Get column names: it was specified an inexisting result set')); 651 } 652 if (!isset($this->columns[$result_value])) { 653 $this->columns[$result_value] = array(); 654 $columns = @ibase_num_fields($result); 655 for ($column=0; $column < $columns; $column++) { 656 $column_info = @ibase_field_info($result, $column); 657 $field_name = $column_info['name']; 658 if ($this->options['optimize'] == 'portability') { 659 $field_name = strtolower($field_name); 660 } 661 $this->columns[$result_value][$field_name] = $column; 662 } 663 } 664 return $this->columns[$result_value]; 665 } 666 667 // }}} 668 // {{{ numCols() 669 670 /** 671 * Count the number of columns returned by the DBMS in a query result. 672 * 673 * @param resource $result result identifier 674 * @return mixed integer value with the number of columns, a MDB error 675 * on failure 676 * @access public 677 */ 678 function numCols($result) 679 { 680 if (!isset($this->highest_fetched_row[intval($result)])) { 681 return($this->raiseError(MDB_ERROR, NULL, NULL, 682 'Number of columns: it was specified an inexisting result set')); 683 } 684 return @ibase_num_fields($result); 685 } 686 687 // }}} 688 // {{{ endOfResult() 689 690 /** 691 * check if the end of the result set has been reached 692 * 693 * @param resource $result result identifier 694 * @return mixed TRUE or FALSE on sucess, a MDB error on failure 695 * @access public 696 */ 697 function endOfResult($result) 698 { 699 $result_value = intval($result); 700 if (!isset($this->current_row[$result_value])) { 701 return($this->raiseError(MDB_ERROR, NULL, NULL, 702 'End of result: attempted to check the end of an unknown result')); 703 } 704 if (isset($this->results[$result_value]) && end($this->results[$result_value]) === false) { 705 return(($this->highest_fetched_row[$result_value]-1) <= $this->current_row[$result_value]); 706 } 707 if (isset($this->row_buffer[$result_value])) { 708 return(!$this->row_buffer[$result_value]); 709 } 710 if (isset($this->limits[$result_value])) { 711 if (MDB::isError($this->_skipLimitOffset($result)) 712 || ($this->current_row[$result_value]) > $this->limits[$result_value][1] 713 ) { 714 return true; 715 } 716 } 717 if (is_array($this->row_buffer[$result_value] = @ibase_fetch_row($result))) { 718 return false; 719 } 720 $this->row_buffer[$result_value] = false; 721 return true; 722 } 723 724 // }}} 725 // {{{ fetch() 726 727 /** 728 * fetch value from a result set 729 * 730 * @param resource $result result identifier 731 * @param int $rownum number of the row where the data can be found 732 * @param int $field field number where the data can be found 733 * @return mixed string on success, a MDB error on failure 734 * @access public 735 */ 736 function fetch($result, $rownum, $field) 737 { 738 $fetchmode = is_numeric($field) ? MDB_FETCHMODE_ORDERED : MDB_FETCHMODE_ASSOC; 739 $row = $this->fetchInto($result, $fetchmode, $rownum); 740 if (MDB::isError($row)) { 741 return $row; 742 } 743 if (!array_key_exists($field, $row)) { 744 return null; 745 } 746 return $row[$field]; 747 } 748 749 // }}} 750 // {{{ fetchInto() 751 752 /** 753 * Fetch a row and return data in an array. 754 * 755 * @param resource $result result identifier 756 * @param int $fetchmode how the array data should be indexed 757 * @param int $rownum the row number to fetch 758 * @return mixed data array or NULL on success, a MDB error on failure 759 * @access public 760 */ 761 function fetchInto($result, $fetchmode=MDB_FETCHMODE_DEFAULT, $rownum=null) 762 { 763 $result_value = intval($result); 764 if (!isset($this->current_row[$result_value])) { 765 return($this->raiseError(MDB_ERROR, NULL, NULL, 766 'fetchInto: attemped to fetch on an unknown query result')); 767 } 768 if ($fetchmode == MDB_FETCHMODE_DEFAULT) { 769 $fetchmode = $this->fetchmode; 770 } 771 if (is_null($rownum)) { 772 $rownum = $this->current_row[$result_value] + 1; 773 } 774 if (!isset($this->results[$result_value][$rownum]) 775 && (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]]) 776 || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== false) 777 ) { 778 if (isset($this->limits[$result_value])) { 779 //upper limit 780 if ($rownum > $this->limits[$result_value][1]) { 781 // are all previous rows fetched so that we can set the end 782 // of the result set and not have any "holes" in between? 783 if ($rownum == 0 784 || (isset($this->results[$result_value]) 785 && count($this->results[$result_value]) == $rownum) 786 ) { 787 $this->highest_fetched_row[$result_value] = $rownum; 788 $this->current_row[$result_value] = $rownum; 789 $this->results[$result_value][$rownum] = false; 790 } 791 if ($this->options['autofree']) { 792 $this->freeResult($result); 793 } 794 return null; 795 } 796 // offset skipping 797 if (MDB::isError($this->_skipLimitOffset($result))) { 798 $this->current_row[$result_value] = 0; 799 $this->results[$result_value] = array(false); 800 if ($this->options['autofree']) { 801 $this->freeResult($result); 802 } 803 return null; 804 } 805 } 806 if (isset($this->row_buffer[$result_value])) { 807 ++$this->current_row[$result_value]; 808 $this->results[$result_value][$this->current_row[$result_value]] = 809 $this->row_buffer[$result_value]; 810 unset($this->row_buffer[$result_value]); 811 } 812 if (!isset($this->results[$result_value][$rownum]) 813 && (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]]) 814 || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== false) 815 ) { 816 while ($this->current_row[$result_value] < $rownum 817 && is_array($buffer = @ibase_fetch_row($result)) 818 ) { 819 ++$this->current_row[$result_value]; 820 $this->results[$result_value][$this->current_row[$result_value]] = $buffer; 821 } 822 // end of result set reached 823 if ($this->current_row[$result_value] < $rownum) { 824 ++$this->current_row[$result_value]; 825 $this->results[$result_value][$this->current_row[$result_value]] = false; 826 } 827 } 828 $this->highest_fetched_row[$result_value] = 829 max($this->highest_fetched_row[$result_value], 830 $this->current_row[$result_value]); 831 } else { 832 ++$this->current_row[$result_value]; 833 } 834 if (isset($this->results[$result_value][$rownum]) 835 && $this->results[$result_value][$rownum] 836 ) { 837 $row = $this->results[$result_value][$rownum]; 838 } else { 839 if ($this->options['autofree']) { 840 $this->freeResult($result); 841 } 842 return null; 843 } 844 foreach ($row as $key => $value_with_space) { 845 if (!is_null($value_with_space)) { 846 $row[$key] = rtrim($value_with_space, ' '); 847 } 848 } 849 if ($fetchmode & MDB_FETCHMODE_ASSOC) { 850 $column_names = $this->getColumnNames($result); 851 foreach ($column_names as $name => $i) { 852 $column_names[$name] = $row[$i]; 853 } 854 $row = $column_names; 855 } 856 if (isset($this->result_types[$result_value])) { 857 $row = $this->convertResultRow($result, $row); 858 } 859 return $row; 860 } 861 862 // }}} 863 // {{{ _retrieveLob() 864 865 /** 866 * fetch a lob value from a result set 867 * 868 * @param int $lob handle to a lob created by the createLob() function 869 * @return mixed MDB_OK on success, a MDB error on failure 870 * @access private 871 */ 872 function _retrieveLob($lob) 873 { 874 if (!isset($this->lobs[$lob])) { 875 return($this->raiseError(MDB_ERROR, NULL, NULL, 876 'Retrieve LOB: it was not specified a valid lob')); 877 } 878 879 if (!isset($this->lobs[$lob]['Value'])) { 880 $this->lobs[$lob]['Value'] = $this->fetch($this->lobs[$lob]['Result'], 881 $this->lobs[$lob]['Row'], 882 $this->lobs[$lob]['Field']); 883 884 if (!$this->lobs[$lob]['Handle'] = @ibase_blob_open($this->lobs[$lob]['Value'])) { 885 unset($this->lobs[$lob]['Value']); 886 return($this->raiseError(MDB_ERROR, NULL, NULL, 887 'Retrieve LOB: Could not open fetched large object field' . @ibase_errmsg())); 888 } 889 } 890 return MDB_OK; 891 } 892 893 // }}} 894 // {{{ endOfResultLob() 895 896 /** 897 * Determine whether it was reached the end of the large object and 898 * therefore there is no more data to be read for the its input stream. 899 * 900 * @param int $lob handle to a lob created by the createLob() function 901 * @return mixed TRUE or FALSE on success, a MDB error on failure 902 * @access public 903 */ 904 function endOfResultLob($lob) 905 { 906 if (MDB::isError($lobresult = $this->_retrieveLob($lob))) { 907 return($lobresult); 908 } 909 return(isset($this->lobs[$lob]['EndOfLOB'])); 910 } 911 912 // }}} 913 // {{{ _readResultLob() 914 915 /** 916 * Read data from large object input stream. 917 * 918 * @param int $lob handle to a lob created by the createLob() function 919 * @param blob $data reference to a variable that will hold data to be 920 * read from the large object input stream 921 * @param int $length integer value that indicates the largest ammount of 922 * data to be read from the large object input stream. 923 * @return mixed length on success, a MDB error on failure 924 * @access private 925 */ 926 function _readResultLob($lob, &$data, $length) 927 { 928 if (MDB::isError($lobresult = $this->_retrieveLob($lob))) { 929 return $lobresult; 930 } 931 $data = @ibase_blob_get($this->lobs[$lob]['Handle'], $length); 932 if (!is_string($data)) { 933 $this->raiseError(MDB_ERROR, NULL, NULL, 934 'Read Result LOB: ' . @ibase_errmsg()); 935 } 936 if (($length = strlen($data)) == 0) { 937 $this->lobs[$lob]['EndOfLOB'] = 1; 938 } 939 return $length; 940 } 941 942 // }}} 943 // {{{ _destroyResultLob() 944 945 /** 946 * Free any resources allocated during the lifetime of the large object 947 * handler object. 948 * 949 * @param int $lob handle to a lob created by the createLob() function 950 * @access private 951 */ 952 function _destroyResultLob($lob) 953 { 954 if (isset($this->lobs[$lob])) { 955 if (isset($this->lobs[$lob]['Value'])) { 956 @ibase_blob_close($this->lobs[$lob]['Handle']); 957 } 958 $this->lobs[$lob] = ''; 959 } 960 } 961 962 // }}} 963 // {{{ fetchClob() 964 965 /** 966 * fetch a clob value from a result set 967 * 968 * @param resource $result result identifier 969 * @param int $row number of the row where the data can be found 970 * @param int $field field number where the data can be found 971 * @return mixed content of the specified data cell, a MDB error on failure, 972 * a MDB error on failure 973 * @access public 974 */ 975 function fetchClob($result, $row, $field) 976 { 977 return $this->fetchLob($result, $row, $field); 978 } 979 980 // }}} 981 // {{{ fetchBlob() 982 983 /** 984 * fetch a blob value from a result set 985 * 986 * @param resource $result result identifier 987 * @param int $row number of the row where the data can be found 988 * @param int $field field number where the data can be found 989 * @return mixed content of the specified data cell, a MDB error on failure 990 * @access public 991 */ 992 function fetchBlob($result, $row, $field) 993 { 994 return $this->fetchLob($result, $row, $field); 995 } 996 997 // }}} 998 // {{{ convertResult() 999 1000 /** 1001 * convert a value to a RDBMS indepdenant MDB type 1002 * 1003 * @param mixed $value value to be converted 1004 * @param int $type constant that specifies which type to convert to 1005 * @return mixed converted value or a MDB error on failure 1006 * @access public 1007 */ 1008 function convertResult($value, $type) 1009 { 1010 switch ($type) { 1011 case MDB_TYPE_DECIMAL: 1012 return sprintf('%.'.$this->decimal_places.'f', doubleval($value)/$this->decimal_factor); 1013 case MDB_TYPE_TIMESTAMP: 1014 return substr($value, 0, strlen('YYYY-MM-DD HH:MM:SS')); 1015 default: 1016 return $this->_baseConvertResult($value, $type); 1017 } 1018 } 1019 1020 // }}} 1021 // {{{ resultIsNull() 1022 1023 /** 1024 * Determine whether the value of a query result located in given row and 1025 * field is a NULL. 1026 * 1027 * @param resource $result result identifier 1028 * @param int $rownum number of the row where the data can be found 1029 * @param int $field field number where the data can be found 1030 * @return mixed TRUE or FALSE on success, a MDB error on failure 1031 * @access public 1032 */ 1033 function resultIsNull($result, $rownum, $field) 1034 { 1035 $value = $this->fetch($result, $rownum, $field); 1036 if (MDB::isError($value)) { 1037 return $value; 1038 } 1039 return(!isset($value)); 1040 } 1041 1042 // }}} 1043 // {{{ numRows() 1044 1045 /** 1046 * returns the number of rows in a result object 1047 * 1048 * @param ressource $result a valid result ressouce pointer 1049 * @return mixed MDB_Error or the number of rows 1050 * @access public 1051 */ 1052 function numRows($result) 1053 { 1054 $result_value = intval($result); 1055 if (!isset($this->current_row[$result_value])) { 1056 return($this->raiseError(MDB_ERROR, NULL, NULL, 1057 'Number of rows: attemped to obtain the number of rows contained in an unknown query result')); 1058 } 1059 if (!isset($this->rows[$result_value][$this->highest_fetched_row[$result_value]]) 1060 || $this->rows[$result_value][$this->highest_fetched_row[$result_value]] !== false 1061 ) { 1062 if (isset($this->limits[$result_value])) { 1063 if (MDB::isError($skipfirstrow = $this->_skipLimitOffset($result))) { 1064 //$this->rows[$result_value] = 0; 1065 return $skipfirstrow; 1066 } 1067 } 1068 if (isset($this->row_buffer[$result_value])) { 1069 ++$this->highest_fetched_row[$result_value]; 1070 $this->results[$result_value][$this->highest_fetched_row[$result_value]] 1071 = $this->row_buffer[$result_value]; 1072 unset($this->row_buffer[$result_value]); 1073 } 1074 if (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]]) 1075 || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== false 1076 ) { 1077 while((!isset($this->limits[$result_value]) 1078 || ($this->highest_fetched_row[$result_value]+1) < $this->limits[$result_value][1] 1079 ) 1080 && (is_array($buffer = @ibase_fetch_row($result))) 1081 ) { 1082 ++$this->highest_fetched_row[$result_value]; 1083 $this->results[$result_value][$this->highest_fetched_row[$result_value]] = $buffer; 1084 } 1085 ++$this->highest_fetched_row[$result_value]; 1086 $this->results[$result_value][$this->highest_fetched_row[$result_value]] = false; 1087 } 1088 } 1089 return(max(0, $this->highest_fetched_row[$result_value])); 1090 } 1091 1092 // }}} 1093 // {{{ freeResult() 1094 1095 /** 1096 * Free the internal resources associated with $result. 1097 * 1098 * @param $result result identifier 1099 * @return boolean TRUE on success, FALSE if $result is invalid 1100 * @access public 1101 */ 1102 function freeResult($result) 1103 { 1104 $result_value = intval($result); 1105 if (!isset($this->current_row[$result_value])) { 1106 return($this->raiseError(MDB_ERROR, NULL, NULL, 1107 'Free result: attemped to free an unknown query result')); 1108 } 1109 if (isset($this->highest_fetched_row[$result_value])) { 1110 unset($this->highest_fetched_row[$result_value]); 1111 } 1112 if (isset($this->row_buffer[$result_value])) { 1113 unset($this->row_buffer[$result_value]); 1114 } 1115 if (isset($this->limits[$result_value])) { 1116 unset($this->limits[$result_value]); 1117 } 1118 if (isset($this->current_row[$result_value])) { 1119 unset($this->current_row[$result_value]); 1120 } 1121 if (isset($this->results[$result_value])) { 1122 unset($this->results[$result_value]); 1123 } 1124 if (isset($this->columns[$result_value])) { 1125 unset($this->columns[$result_value]); 1126 } 1127 if (isset($this->rows[$result_value])) { 1128 unset($this->rows[$result_value]); 1129 } 1130 if (isset($this->result_types[$result_value])) { 1131 unset($this->result_types[$result_value]); 1132 } 1133 if (is_resource($result)) { 1134 return @ibase_free_result($result); 1135 } 1136 return true; 1137 } 1138 // }}} 1139 // {{{ getTypeDeclaration() 1140 1141 /** 1142 * Obtain DBMS specific SQL code portion needed to declare an text type 1143 * field to be used in statements like CREATE TABLE. 1144 * 1145 * @param string $field associative array with the name of the properties 1146 * of the field being declared as array indexes. Currently, the types 1147 * of supported field properties are as follows: 1148 * 1149 * length 1150 * Integer value that determines the maximum length of the text 1151 * field. If this argument is missing the field should be 1152 * declared to have the longest length allowed by the DBMS. 1153 * 1154 * default 1155 * Text value to be used as default for this field. 1156 * 1157 * notnull 1158 * Boolean flag that indicates whether this field is constrained 1159 * to not be set to null. 1160 * @return string DBMS specific SQL code portion that should be used to 1161 * declare the specified field. 1162 * @access public 1163 */ 1164 function getTypeDeclaration($field) 1165 { 1166 switch($field['type']) 1167 { 1168 case 'text': 1169 return('VARCHAR ('.(isset($field['length']) ? $field['length'] : (isset($this->options['DefaultTextFieldLength']) ? $this->options['DefaultTextFieldLength'] : 4000)).')'); 1170 case 'clob': 1171 return 'BLOB SUB_TYPE 1'; 1172 case 'blob': 1173 return 'BLOB SUB_TYPE 0'; 1174 case 'integer': 1175 return 'INTEGER'; 1176 case 'boolean': 1177 return 'CHAR (1)'; 1178 case 'date': 1179 return 'DATE'; 1180 case 'time': 1181 return 'TIME'; 1182 case 'timestamp': 1183 return 'TIMESTAMP'; 1184 case 'float': 1185 return 'DOUBLE PRECISION'; 1186 case 'decimal': 1187 return 'DECIMAL(18,'.$this->decimal_places.')'; 1188 } 1189 return ''; 1190 } 1191 1192 // }}} 1193 // {{{ getTextDeclaration() 1194 1195 /** 1196 * Obtain DBMS specific SQL code portion needed to declare an text type 1197 * field to be used in statements like CREATE TABLE. 1198 * 1199 * @param string $name name the field to be declared. 1200 * @param string $field associative array with the name of the properties 1201 * of the field being declared as array indexes. Currently, the types 1202 * of supported field properties are as follows: 1203 * 1204 * length 1205 * Integer value that determines the maximum length of the text 1206 * field. If this argument is missing the field should be 1207 * declared to have the longest length allowed by the DBMS. 1208 * 1209 * default 1210 * Text value to be used as default for this field. 1211 * 1212 * notnull 1213 * Boolean flag that indicates whether this field is constrained 1214 * to not be set to NULL. 1215 * @return string DBMS specific SQL code portion that should be used to 1216 * declare the specified field. 1217 * @access public 1218 */ 1219 function getTextDeclaration($name, $field) 1220 { 1221 return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getTextValue($field['default']) : '').(IsSet($field['notnull']) ? ' NOT NULL' : '')); 1222 } 1223 1224 // }}} 1225 // {{{ getClobDeclaration() 1226 1227 /** 1228 * Obtain DBMS specific SQL code portion needed to declare an character 1229 * large object type field to be used in statements like CREATE TABLE. 1230 * 1231 * @param string $name name the field to be declared. 1232 * @param string $field associative array with the name of the properties 1233 * of the field being declared as array indexes. Currently, the types 1234 * of supported field properties are as follows: 1235 * 1236 * length 1237 * Integer value that determines the maximum length of the large 1238 * object field. If this argument is missing the field should be 1239 * declared to have the longest length allowed by the DBMS. 1240 * 1241 * notnull 1242 * Boolean flag that indicates whether this field is constrained 1243 * to not be set to NULL. 1244 * @return string DBMS specific SQL code portion that should be used to 1245 * declare the specified field. 1246 * @access public 1247 */ 1248 function getClobDeclaration($name, $field) 1249 { 1250 return($name.' '.$this->getTypeDeclaration($field).(isset($field['notnull']) ? ' NOT NULL' : '')); 1251 } 1252 1253 // }}} 1254 // {{{ getBlobDeclaration() 1255 1256 /** 1257 * Obtain DBMS specific SQL code portion needed to declare an binary large 1258 * object type field to be used in statements like CREATE TABLE. 1259 * 1260 * @param string $name name the field to be declared. 1261 * @param string $field associative array with the name of the properties 1262 * of the field being declared as array indexes. Currently, the types 1263 * of supported field properties are as follows: 1264 * 1265 * length 1266 * Integer value that determines the maximum length of the large 1267 * object field. If this argument is missing the field should be 1268 * declared to have the longest length allowed by the DBMS. 1269 * 1270 * notnull 1271 * Boolean flag that indicates whether this field is constrained 1272 * to not be set to NULL. 1273 * @return string DBMS specific SQL code portion that should be used to 1274 * declare the specified field. 1275 * @access public 1276 */ 1277 function getBlobDeclaration($name, $field) 1278 { 1279 return($name.' '.$this->getTypeDeclaration($field).(isset($field['notnull']) ? ' NOT NULL' : '')); 1280 } 1281 1282 // }}} 1283 // {{{ getDateDeclaration() 1284 1285 /** 1286 * Obtain DBMS specific SQL code portion needed to declare a date type 1287 * field to be used in statements like CREATE TABLE. 1288 * 1289 * @param string $name name the field to be declared. 1290 * @param string $field associative array with the name of the properties 1291 * of the field being declared as array indexes. Currently, the types 1292 * of supported field properties are as follows: 1293 * 1294 * default 1295 * Date value to be used as default for this field. 1296 * 1297 * notnull 1298 * Boolean flag that indicates whether this field is constrained 1299 * to not be set to NULL. 1300 * @return string DBMS specific SQL code portion that should be used to 1301 * declare the specified field. 1302 * @access public 1303 */ 1304 function getDateDeclaration($name, $field) 1305 { 1306 return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT "'.$field['default'].'"' : '').(isset($field['notnull']) ? ' NOT NULL' : '')); 1307 } 1308 1309 // }}} 1310 // {{{ getTimeDeclaration() 1311 1312 /** 1313 * Obtain DBMS specific SQL code portion needed to declare a time 1314 * field to be used in statements like CREATE TABLE. 1315 * 1316 * @param string $name name the field to be declared. 1317 * @param string $field associative array with the name of the properties 1318 * of the field being declared as array indexes. Currently, the types 1319 * of supported field properties are as follows: 1320 * 1321 * default 1322 * Time value to be used as default for this field. 1323 * 1324 * notnull 1325 * Boolean flag that indicates whether this field is constrained 1326 * to not be set to NULL. 1327 * @return string DBMS specific SQL code portion that should be used to 1328 * declare the specified field. 1329 * @access public 1330 */ 1331 function getTimeDeclaration($name, $field) 1332 { 1333 return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT "'.$field['default'].'"' : '').(isset($field['notnull']) ? ' NOT NULL' : '')); 1334 } 1335 1336 // }}} 1337 // {{{ getFloatDeclaration() 1338 1339 /** 1340 * Obtain DBMS specific SQL code portion needed to declare a float type 1341 * field to be used in statements like CREATE TABLE. 1342 * 1343 * @param string $name name the field to be declared. 1344 * @param string $field associative array with the name of the properties 1345 * of the field being declared as array indexes. Currently, the types 1346 * of supported field properties are as follows: 1347 * 1348 * default 1349 * Float value to be used as default for this field. 1350 * 1351 * notnull 1352 * Boolean flag that indicates whether this field is constrained 1353 * to not be set to NULL. 1354 * @return string DBMS specific SQL code portion that should be used to 1355 * declare the specified field. 1356 * @access public 1357 */ 1358 function getFloatDeclaration($name, $field) 1359 { 1360 return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getFloatValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : '')); 1361 } 1362 1363 // }}} 1364 // {{{ getDecimalDeclaration() 1365 1366 /** 1367 * Obtain DBMS specific SQL code portion needed to declare a decimal type 1368 * field to be used in statements like CREATE TABLE. 1369 * 1370 * @param string $name name the field to be declared. 1371 * @param string $field associative array with the name of the properties 1372 * of the field being declared as array indexes. Currently, the types 1373 * of supported field properties are as follows: 1374 * 1375 * default 1376 * Decimal value to be used as default for this field. 1377 * 1378 * notnull 1379 * Boolean flag that indicates whether this field is constrained 1380 * to not be set to NULL. 1381 * @return string DBMS specific SQL code portion that should be used to 1382 * declare the specified field. 1383 * @access public 1384 */ 1385 function getDecimalDeclaration($name, $field) 1386 { 1387 return($name.' '.$this->getTypeDeclaration($field).(isset($field['default']) ? ' DEFAULT '.$this->getDecimalValue($field['default']) : '').(isset($field['notnull']) ? ' NOT NULL' : '')); 1388 } 1389 1390 // }}} 1391 // {{{ _getLobValue() 1392 1393 /** 1394 * Convert a text value into a DBMS specific format that is suitable to 1395 * compose query statements. 1396 * 1397 * @param resource $prepared_query query handle from prepare() 1398 * @param $parameter 1399 * @param $lob 1400 * @return string text string that represents the given argument value in 1401 * a DBMS specific format. 1402 * @access private 1403 */ 1404 function _getLobValue($prepared_query, $parameter, $lob) 1405 { 1406 if (MDB::isError($connect = $this->connect())) { 1407 return $connect; 1408 } 1409 $value = ''; // DEAL WITH ME 1410 if (!$this->transaction_id = @ibase_trans(IBASE_COMMITTED, $this->connection)) { 1411 return($this->raiseError(MDB_ERROR, NULL, NULL, '_getLobValue: Could not start a new transaction: '.@ibase_errmsg())); 1412 } 1413 1414 if (($lo = @ibase_blob_create($this->auto_commit ? $this->connection : $this->transaction_id))) { 1415 while (!$this->endOfLob($lob)) { 1416 if (MDB::isError($result = $this->readLob($lob, $data, $this->options['lob_buffer_length']))) { 1417 break; 1418 } 1419 if (@ibase_blob_add($lo, $data) === false) { 1420 $result = $this->raiseError(MDB_ERROR, NULL, NULL, 1421 '_getLobValue - Could not add data to a large object: ' . @ibase_errmsg()); 1422 break; 1423 } 1424 } 1425 if (MDB::isError($result)) { 1426 @ibase_blob_cancel($lo); 1427 } else { 1428 $value = @ibase_blob_close($lo); 1429 } 1430 } else { 1431 $result = $this->raiseError(MDB_ERROR, NULL, NULL, 1432 'Get LOB field value' . @ibase_errmsg()); 1433 } 1434 if (!isset($this->query_parameters[$prepared_query])) { 1435 $this->query_parameters[$prepared_query] = array(0, ''); 1436 $this->query_parameter_values[$prepared_query] = array(); 1437 } 1438 $query_parameter = count($this->query_parameters[$prepared_query]); 1439 $this->query_parameter_values[$prepared_query][$parameter] = $query_parameter; 1440 $this->query_parameters[$prepared_query][$query_parameter] = $value; 1441 $value = '?'; 1442 1443 if (!$this->auto_commit) { 1444 $this->commit(); 1445 } 1446 return $value; 1447 } 1448 1449 // }}} 1450 // {{{ getClobValue() 1451 1452 /** 1453 * Convert a text value into a DBMS specific format that is suitable to 1454 * compose query statements. 1455 * 1456 * @param resource $prepared_query query handle from prepare() 1457 * @param $parameter 1458 * @param $clob 1459 * @return string text string that represents the given argument value in 1460 * a DBMS specific format. 1461 * @access public 1462 */ 1463 function getClobValue($prepared_query, $parameter, $clob) 1464 { 1465 return $this->_getLobValue($prepared_query, $parameter, $clob); 1466 } 1467 1468 1469 // }}} 1470 // {{{ freeLobValue() 1471 1472 /** 1473 * free a large object 1474 * 1475 * @param resource $prepared_query query handle from prepare() 1476 * @param string $lob 1477 * @param string $value 1478 * @return MDB_OK 1479 * @access public 1480 */ 1481 function freeLobValue($prepared_query, $lob, &$value) 1482 { 1483 $query_parameter=$this->query_parameter_values[$prepared_query][$lob]; 1484 1485 unset($this->query_parameters[$prepared_query][$query_parameter]); 1486 unset($this->query_parameter_values[$prepared_query][$lob]); 1487 if (count($this->query_parameter_values[$prepared_query]) == 0) { 1488 unset($this->query_parameters[$prepared_query]); 1489 unset($this->query_parameter_values[$prepared_query]); 1490 } 1491 unset($value); 1492 } 1493 1494 // }}} 1495 // {{{ freeClobValue() 1496 1497 /** 1498 * free a character large object 1499 * 1500 * @param resource $prepared_query query handle from prepare() 1501 * @param string $clob 1502 * @param string $value 1503 * @return MDB_OK 1504 * @access public 1505 */ 1506 function freeClobValue($prepared_query, $clob, &$value) 1507 { 1508 $this->freeLobValue($prepared_query, $clob, $value); 1509 } 1510 1511 // }}} 1512 // {{{ getBlobValue() 1513 1514 /** 1515 * Convert a text value into a DBMS specific format that is suitable to 1516 * compose query statements. 1517 * 1518 * @param resource $prepared_query query handle from prepare() 1519 * @param $parameter 1520 * @param $blob 1521 * @return string text string that represents the given argument value in 1522 * a DBMS specific format. 1523 * @access public 1524 */ 1525 function getBlobValue($prepared_query, $parameter, $blob) 1526 { 1527 return $this->_getLobValue($prepared_query, $parameter, $blob); 1528 } 1529 1530 // }}} 1531 // {{{ freeBlobValue() 1532 1533 /** 1534 * free a binary large object 1535 * 1536 * @param resource $prepared_query query handle from prepare() 1537 * @param string $blob 1538 * @param string $value 1539 * @return MDB_OK 1540 * @access public 1541 */ 1542 function freeBlobValue($prepared_query, $blob, &$value) 1543 { 1544 $this->freeLobValue($prepared_query, $blob, $value); 1545 } 1546 1547 // }}} 1548 // {{{ getFloatValue() 1549 1550 /** 1551 * Convert a text value into a DBMS specific format that is suitable to 1552 * compose query statements. 1553 * 1554 * @param string $value text string value that is intended to be converted. 1555 * @return string text string that represents the given argument value in 1556 * a DBMS specific format. 1557 * @access public 1558 */ 1559 function getFloatValue($value) 1560 { 1561 return (($value === null) ? 'NULL' : $value); 1562 } 1563 1564 // }}} 1565 // {{{ getDecimalValue() 1566 1567 /** 1568 * Convert a text value into a DBMS specific format that is suitable to 1569 * compose query statements. 1570 * 1571 * @param string $value text string value that is intended to be converted. 1572 * @return string text string that represents the given argument value in 1573 * a DBMS specific format. 1574 * @access public 1575 */ 1576 function getDecimalValue($value) 1577 { 1578 return (($value === null) ? 'NULL' : strval(round($value*$this->decimal_factor))); 1579 } 1580 1581 // }}} 1582 // {{{ affectedRows() 1583 1584 /** 1585 * returns the affected rows of a query 1586 * 1587 * @return mixed MDB Error Object or number of rows 1588 * @access public 1589 */ 1590 function affectedRows() 1591 { 1592 if (function_exists('ibase_affected_rows')) { //PHP5 only 1593 $affected_rows = @ibase_affected_rows($this->connection); 1594 if ($affected_rows === false) { 1595 return $this->raiseError(MDB_ERROR_NEED_MORE_DATA); 1596 } 1597 return $affected_rows; 1598 } 1599 return parent::affectedRows(); 1600 } 1601 1602 // }}} 1603 // {{{ nextId() 1604 1605 /** 1606 * returns the next free id of a sequence 1607 * 1608 * @param string $seq_name name of the sequence 1609 * @param boolean $ondemand when TRUE the seqence is 1610 * automatic created, if it 1611 * not exists 1612 * @return mixed MDB_Error or id 1613 * @access public 1614 */ 1615 function nextId($seq_name, $ondemand = true) 1616 { 1617 if (MDB::isError($connect = $this->connect())) { 1618 return $connect; 1619 } 1620 //$sequence_name = $this->getSequenceName($seq_name); 1621 $sequence_name = strtoupper($this->getSequenceName($seq_name)); 1622 $this->expectError(MDB_ERROR_NOSUCHTABLE); 1623 $query = "SELECT GEN_ID($sequence_name, 1) as the_value FROM RDB\$DATABASE"; 1624 $result = $this->_doQuery($query); 1625 $this->popExpect(); 1626 if ($ondemand && MDB::isError($result)) { 1627 $result = $this->createSequence($seq_name, 1); 1628 if (MDB::isError($result)) { 1629 return $result; 1630 } 1631 return $this->nextId($seq_name, false); 1632 } 1633 return $this->fetchOne($result); 1634 } 1635 1636 // }}} 1637 // {{{ currId() 1638 1639 /** 1640 * returns the current id of a sequence 1641 * 1642 * @param string $seq_name name of the sequence 1643 * @return mixed MDB_Error or id 1644 * @access public 1645 */ 1646 function currId($seq_name) 1647 { 1648 $sequence_name = strtoupper($this->getSequenceName($seq_name)); 1649 //$sequence_name = $this->getSequenceName($seq_name); 1650 $query = "SELECT RDB\$GENERATOR_ID FROM RDB\$GENERATORS WHERE RDB\$GENERATOR_NAME='$sequence_name'"; 1651 if (MDB::isError($result = $this->queryOne($query))) { 1652 return($this->raiseError(MDB_ERROR, NULL, NULL, 1653 'currId: Unable to select from ' . $seqname) ); 1654 } 1655 if (!is_numeric($result)) { 1656 //var_dump($result); ==> null 1657 return($this->raiseError(MDB_ERROR, NULL, NULL, 1658 'currId: could not find value in sequence table')); 1659 } 1660 return $result; 1661 } 1662 1663 // }}} 1664 // {{{ nextResult() 1665 1666 /** 1667 * Move the internal ibase result pointer to the next available result 1668 * 1669 * @param $result a valid ibase result resource 1670 * @return TRUE if a result is available otherwise return FALSE 1671 * @access public 1672 */ 1673 function nextResult($result) 1674 { 1675 return false; 1676 } 1677 1678 // }}} 1679 // {{{ tableInfo() 1680 1681 /** 1682 * returns meta data about the result set 1683 * 1684 * @param mixed $resource FireBird/InterBase result identifier or table name 1685 * @param mixed $mode depends on implementation 1686 * @return array an nested array, or a MDB error 1687 * @access public 1688 */ 1689 function tableInfo($result, $mode = NULL) 1690 { 1691 $count = 0; 1692 $id = 0; 1693 $res = array(); 1694 1695 /** 1696 * depending on $mode, metadata returns the following values: 1697 * 1698 * - mode is FALSE (default): 1699 * $result[]: 1700 * [0]['table'] table name 1701 * [0]['name'] field name 1702 * [0]['type'] field type 1703 * [0]['len'] field length 1704 * [0]['flags'] field flags 1705 * 1706 * - mode is MDB_TABLEINFO_ORDER 1707 * $result[]: 1708 * ['num_fields'] number of metadata records 1709 * [0]['table'] table name 1710 * [0]['name'] field name 1711 * [0]['type'] field type 1712 * [0]['len'] field length 1713 * [0]['flags'] field flags 1714 * ['order'][field name] index of field named 'field name' 1715 * The last one is used, if you have a field name, but no index. 1716 * Test: if (isset($result['meta']['myfield'])) { ... 1717 * 1718 * - mode is MDB_TABLEINFO_ORDERTABLE 1719 * the same as above. but additionally 1720 * ['ordertable'][table name][field name] index of field 1721 * named 'field name' 1722 * 1723 * this is, because if you have fields from different 1724 * tables with the same field name * they override each 1725 * other with MDB_TABLEINFO_ORDER 1726 * 1727 * you can combine MDB_TABLEINFO_ORDER and 1728 * MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER | 1729 * MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL 1730 **/ 1731 1732 // if $result is a string, then we want information about a 1733 // table without a resultset 1734 if (is_string($result)) { 1735 $id = @ibase_query($this->connection,"SELECT * FROM $result"); 1736 if (empty($id)) { 1737 return $this->ibaseRaiseError(); 1738 } 1739 } else { // else we want information about a resultset 1740 $id = $result; 1741 if (empty($id)) { 1742 return $this->ibaseRaiseError(); 1743 } 1744 } 1745 1746 $count = @ibase_num_fields($id); 1747 1748 // made this IF due to performance (one if is faster than $count if's) 1749 if (empty($mode)) { 1750 for ($i=0; $i<$count; $i++) { 1751 $info = @ibase_field_info($id, $i); 1752 //$res[$i]['table'] = (is_string($result)) ? $result : ''; 1753 $res[$i]['table'] = (is_string($result)) ? $result : $info['relation']; 1754 $res[$i]['name'] = $info['name']; 1755 $res[$i]['type'] = $info['type']; 1756 $res[$i]['len'] = $info['length']; 1757 //$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; 1758 $res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($id, $i, $result) : ''; 1759 } 1760 } else { // full 1761 $res['num_fields'] = $count; 1762 1763 for ($i=0; $i<$count; $i++) { 1764 $info = @ibase_field_info($id, $i); 1765 //$res[$i]['table'] = (is_string($result)) ? $result : ''; 1766 $res[$i]['table'] = (is_string($result)) ? $result : $info['relation']; 1767 $res[$i]['name'] = $info['name']; 1768 $res[$i]['type'] = $info['type']; 1769 $res[$i]['len'] = $info['length']; 1770 //$res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($info['name'], $result) : ''; 1771 $res[$i]['flags'] = (is_string($result)) ? $this->_ibaseFieldFlags($id, $i, $result) : ''; 1772 if ($mode & MDB_TABLEINFO_ORDER) { 1773 $res['order'][$res[$i]['name']] = $i; 1774 } 1775 if ($mode & MDB_TABLEINFO_ORDERTABLE) { 1776 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i; 1777 } 1778 } 1779 } 1780 1781 // free the result only if we were called on a table 1782 if (is_string($result) && is_resource($id)) { 1783 @ibase_free_result($id); 1784 } 1785 return $res; 1786 } 1787 1788 // }}} 1789 // {{{ _ibaseFieldFlags() 1790 1791 /** 1792 * get the Flags of a Field 1793 * 1794 * @param int $resource FireBird/InterBase result identifier 1795 * @param int $num_field the field number 1796 * @return string The flags of the field ('not_null', 'default_xx', 'primary_key', 1797 * 'unique' and 'multiple_key' are supported) 1798 * @access private 1799 **/ 1800 function _ibaseFieldFlags($resource, $num_field, $table_name) 1801 { 1802 $field_name = @ibase_field_info($resource, $num_field); 1803 $field_name = @$field_name['name']; 1804 $sql = 'SELECT R.RDB$CONSTRAINT_TYPE CTYPE' 1805 .' FROM RDB$INDEX_SEGMENTS I' 1806 .' JOIN RDB$RELATION_CONSTRAINTS R ON I.RDB$INDEX_NAME=R.RDB$INDEX_NAME' 1807 .' WHERE I.RDB$FIELD_NAME=\''.$field_name.'\'' 1808 .' AND UPPER(R.RDB$RELATION_NAME)=\''.strtoupper($table_name).'\''; 1809 $result = @ibase_query($this->connection, $sql); 1810 if (empty($result)) { 1811 return $this->ibaseRaiseError(); 1812 } 1813 $flags = ''; 1814 if ($obj = @ibase_fetch_object($result)) { 1815 @ibase_free_result($result); 1816 if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'PRIMARY KEY') { 1817 $flags = 'primary_key '; 1818 } 1819 if (isset($obj->CTYPE) && trim($obj->CTYPE) == 'UNIQUE') { 1820 $flags .= 'unique_key '; 1821 } 1822 } 1823 1824 $sql = 'SELECT R.RDB$NULL_FLAG AS NFLAG,' 1825 .' R.RDB$DEFAULT_SOURCE AS DSOURCE,' 1826 .' F.RDB$FIELD_TYPE AS FTYPE,' 1827 .' F.RDB$COMPUTED_SOURCE AS CSOURCE' 1828 .' FROM RDB$RELATION_FIELDS R ' 1829 .' JOIN RDB$FIELDS F ON R.RDB$FIELD_SOURCE=F.RDB$FIELD_NAME' 1830 .' WHERE UPPER(R.RDB$RELATION_NAME)=\''.strtoupper($table_name).'\'' 1831 .' AND R.RDB$FIELD_NAME=\''.$field_name.'\''; 1832 $result = @ibase_query($this->connection, $sql); 1833 if (empty($result)) { 1834 return $this->ibaseRaiseError(); 1835 } 1836 if ($obj = @ibase_fetch_object($result)) { 1837 @ibase_free_result($result); 1838 if (isset($obj->NFLAG)) { 1839 $flags .= 'not_null '; 1840 } 1841 if (isset($obj->DSOURCE)) { 1842 $flags .= 'default '; 1843 } 1844 if (isset($obj->CSOURCE)) { 1845 $flags .= 'computed '; 1846 } 1847 if (isset($obj->FTYPE) && $obj->FTYPE == 261) { 1848 $flags .= 'blob '; 1849 } 1850 } 1851 1852 return trim($flags); 1853 } 1854} 1855 1856?>