3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
6 * The PEAR DB driver for PHP's ifx extension
7 * for interacting with Informix databases
11 * LICENSE: This source file is subject to version 3.0 of the PHP license
12 * that is available through the world-wide-web at the following URI:
13 * http://www.php.net/license/3_0.txt. If you did not receive a copy of
14 * the PHP License and are unable to obtain it through the web, please
15 * send a note to license@php.net so we can mail you a copy immediately.
19 * @author Tomas V.V.Cox <cox@idecnet.com>
20 * @author Daniel Convissor <danielc@php.net>
21 * @copyright 1997-2005 The PHP Group
22 * @license http://www.php.net/license/3_0.txt PHP License 3.0
23 * @version CVS: $Id: ifx.php 12 2005-10-02 11:36:35Z sparc $
24 * @link http://pear.php.net/package/DB
28 * Obtain the DB_common class so it can be extended from
30 require_once 'DB/common.php';
33 * The methods PEAR DB uses to interact with PHP's ifx extension
34 * for interacting with Informix databases
36 * These methods overload the ones declared in DB_common.
38 * More info on Informix errors can be found at:
39 * http://www.informix.com/answers/english/ierrors.htm
42 * - set needed env Informix vars on connect
43 * - implement native prepare/execute
47 * @author Tomas V.V.Cox <cox@idecnet.com>
48 * @author Daniel Convissor <danielc@php.net>
49 * @copyright 1997-2005 The PHP Group
50 * @license http://www.php.net/license/3_0.txt PHP License 3.0
51 * @version Release: @package_version@
52 * @link http://pear.php.net/package/DB
54 class DB_ifx extends DB_common
59 * The DB driver type (mysql, oci8, odbc, etc.)
65 * The database syntax variant to be used (db2, access, etc.), if any
68 var $dbsyntax = 'ifx';
71 * The capabilities of this DB implementation
73 * The 'new_link' element contains the PHP version that first provided
74 * new_link support for this DBMS. Contains false if it's unsupported.
76 * Meaning of the 'limit' element:
77 * + 'emulate' = emulate with fetch row by number
78 * + 'alter' = alter the query
83 var $features = array(
86 'numrows' => 'emulate',
90 'transactions' => true,
94 * A mapping of native error codes to DB error codes
97 var $errorcode_map = array(
98 '-201' => DB_ERROR_SYNTAX,
99 '-206' => DB_ERROR_NOSUCHTABLE,
100 '-217' => DB_ERROR_NOSUCHFIELD,
101 '-236' => DB_ERROR_VALUE_COUNT_ON_ROW,
102 '-239' => DB_ERROR_CONSTRAINT,
103 '-253' => DB_ERROR_SYNTAX,
104 '-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
105 '-310' => DB_ERROR_ALREADY_EXISTS,
106 '-316' => DB_ERROR_ALREADY_EXISTS,
107 '-319' => DB_ERROR_NOT_FOUND,
108 '-329' => DB_ERROR_NODBSELECTED,
109 '-346' => DB_ERROR_CONSTRAINT,
110 '-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
111 '-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
112 '-554' => DB_ERROR_SYNTAX,
113 '-691' => DB_ERROR_CONSTRAINT,
114 '-692' => DB_ERROR_CONSTRAINT,
115 '-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
116 '-1204' => DB_ERROR_INVALID_DATE,
117 '-1205' => DB_ERROR_INVALID_DATE,
118 '-1206' => DB_ERROR_INVALID_DATE,
119 '-1209' => DB_ERROR_INVALID_DATE,
120 '-1210' => DB_ERROR_INVALID_DATE,
121 '-1212' => DB_ERROR_INVALID_DATE,
122 '-1213' => DB_ERROR_INVALID_NUMBER,
126 * The raw database connection created by PHP
132 * The DSN information for connecting to a database
139 * Should data manipulation queries be committed automatically?
143 var $autocommit = true;
146 * The quantity of transactions begun
148 * {@internal While this is private, it can't actually be designated
149 * private in PHP 5 because it is directly accessed in the test suite.}}
154 var $transaction_opcount = 0;
157 * The number of rows affected by a data manipulation query
168 * This constructor calls <kbd>$this->DB_common()</kbd>
181 * Connect to the database server, log in and open the database
183 * Don't call this method directly. Use DB::connect() instead.
185 * @param array $dsn the data source name
186 * @param bool $persistent should the connection be persistent?
188 * @return int DB_OK on success. A DB_Error object on failure.
190 function connect($dsn, $persistent = false)
192 if (!PEAR::loadExtension('informix') &&
193 !PEAR::loadExtension('Informix'))
195 return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
199 if ($dsn['dbsyntax']) {
200 $this->dbsyntax = $dsn['dbsyntax'];
203 $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
204 $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
205 $user = $dsn['username'] ? $dsn['username'] : '';
206 $pw = $dsn['password'] ? $dsn['password'] : '';
208 $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
210 $this->connection = @$connect_function($dbname, $user, $pw);
211 if (!is_resource($this->connection)) {
212 return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
221 * Disconnects from the database server
223 * @return bool TRUE on success, FALSE on failure
225 function disconnect()
227 $ret = @ifx_close($this->connection);
228 $this->connection = null;
236 * Sends a query to the database server
238 * @param string the SQL query string
240 * @return mixed + a PHP result resrouce for successful SELECT queries
241 * + the DB_OK constant for other successful queries
242 * + a DB_Error object on failure
244 function simpleQuery($query)
246 $ismanip = DB::isManip($query);
247 $this->last_query = $query;
248 $this->affected = null;
249 if (preg_match('/(SELECT)/i', $query)) { //TESTME: Use !DB::isManip()?
250 // the scroll is needed for fetching absolute row numbers
251 // in a select query result
252 $result = @ifx_query($query, $this->connection, IFX_SCROLL);
254 if (!$this->autocommit && $ismanip) {
255 if ($this->transaction_opcount == 0) {
256 $result = @ifx_query('BEGIN WORK', $this->connection);
258 return $this->ifxRaiseError();
261 $this->transaction_opcount++;
263 $result = @ifx_query($query, $this->connection);
266 return $this->ifxRaiseError();
268 $this->affected = @ifx_affected_rows($result);
269 // Determine which queries should return data, and which
270 // should return an error code only.
271 if (preg_match('/(SELECT)/i', $query)) {
274 // XXX Testme: free results inside a transaction
275 // may cause to stop it and commit the work?
277 // Result has to be freed even with a insert or update
278 @ifx_free_result($result);
287 * Move the internal ifx result pointer to the next available result
289 * @param a valid fbsql result resource
293 * @return true if a result is available otherwise return false
295 function nextResult($result)
301 // {{{ affectedRows()
304 * Determines the number of rows affected by a data maniuplation query
306 * 0 is returned for queries that don't manipulate data.
308 * @return int the number of rows. A DB_Error object on failure.
310 function affectedRows()
312 if (DB::isManip($this->last_query)) {
313 return $this->affected;
323 * Places a row from the result set into the given array
325 * Formating of the array and the data therein are configurable.
326 * See DB_result::fetchInto() for more information.
328 * This method is not meant to be called directly. Use
329 * DB_result::fetchInto() instead. It can't be declared "protected"
330 * because DB_result is a separate object.
332 * @param resource $result the query result resource
333 * @param array $arr the referenced array to put the data in
334 * @param int $fetchmode how the resulting array should be indexed
335 * @param int $rownum the row number to fetch (0 = first row)
337 * @return mixed DB_OK on success, NULL when the end of a result set is
338 * reached or on failure
340 * @see DB_result::fetchInto()
342 function fetchInto($result, &$arr, $fetchmode, $rownum = null)
344 if (($rownum !== null) && ($rownum < 0)) {
347 if ($rownum === null) {
349 * Even though fetch_row() should return the next row if
350 * $rownum is null, it doesn't in all cases. Bug 598.
354 // Index starts at row 1, unlike most DBMS's starting at 0.
357 if (!$arr = @ifx_fetch_row($result, $rownum)) {
360 if ($fetchmode !== DB_FETCHMODE_ASSOC) {
363 foreach ($arr as $val) {
367 } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
368 $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
370 $arr = array_change_key_case($arr, CASE_LOWER);
372 if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
373 $this->_rtrimArrayValues($arr);
375 if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
376 $this->_convertNullArrayValuesToEmpty($arr);
385 * Gets the number of columns in a result set
387 * This method is not meant to be called directly. Use
388 * DB_result::numCols() instead. It can't be declared "protected"
389 * because DB_result is a separate object.
391 * @param resource $result PHP's query result resource
393 * @return int the number of columns. A DB_Error object on failure.
395 * @see DB_result::numCols()
397 function numCols($result)
399 if (!$cols = @ifx_num_fields($result)) {
400 return $this->ifxRaiseError();
409 * Deletes the result set and frees the memory occupied by the result set
411 * This method is not meant to be called directly. Use
412 * DB_result::free() instead. It can't be declared "protected"
413 * because DB_result is a separate object.
415 * @param resource $result PHP's query result resource
417 * @return bool TRUE on success, FALSE if $result is invalid
419 * @see DB_result::free()
421 function freeResult($result)
423 return @ifx_free_result($result);
430 * Enables or disables automatic commits
432 * @param bool $onoff true turns it on, false turns it off
434 * @return int DB_OK on success. A DB_Error object if the driver
435 * doesn't support auto-committing transactions.
437 function autoCommit($onoff = true)
439 // XXX if $this->transaction_opcount > 0, we should probably
440 // issue a warning here.
441 $this->autocommit = $onoff ? true : false;
449 * Commits the current transaction
451 * @return int DB_OK on success. A DB_Error object on failure.
455 if ($this->transaction_opcount > 0) {
456 $result = @ifx_query('COMMIT WORK', $this->connection);
457 $this->transaction_opcount = 0;
459 return $this->ifxRaiseError();
469 * Reverts the current transaction
471 * @return int DB_OK on success. A DB_Error object on failure.
475 if ($this->transaction_opcount > 0) {
476 $result = @ifx_query('ROLLBACK WORK', $this->connection);
477 $this->transaction_opcount = 0;
479 return $this->ifxRaiseError();
486 // {{{ ifxRaiseError()
489 * Produces a DB_Error object regarding the current problem
491 * @param int $errno if the error is being manually raised pass a
492 * DB_ERROR* constant here. If this isn't passed
493 * the error information gathered from the DBMS.
495 * @return object the DB_Error object
497 * @see DB_common::raiseError(),
498 * DB_ifx::errorNative(), DB_ifx::errorCode()
500 function ifxRaiseError($errno = null)
502 if ($errno === null) {
503 $errno = $this->errorCode(ifx_error());
505 return $this->raiseError($errno, null, null, null,
506 $this->errorNative());
513 * Gets the DBMS' native error code and message produced by the last query
515 * @return string the DBMS' error code and message
517 function errorNative()
519 return @ifx_error() . ' ' . @ifx_errormsg();
526 * Maps native error codes to DB's portable ones.
528 * Requires that the DB implementation's constructor fills
529 * in the <var>$errorcode_map</var> property.
531 * @param string $nativecode error code returned by the database
532 * @return int a portable DB error code, or DB_ERROR if this DB
533 * implementation has no mapping for the given error code.
535 function errorCode($nativecode)
537 if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
539 if (isset($this->errorcode_map[$code])) {
540 return $this->errorcode_map[$code];
550 * Returns information about a table or a result set
552 * NOTE: only supports 'table' if <var>$result</var> is a table name.
554 * If analyzing a query result and the result has duplicate field names,
555 * an error will be raised saying
556 * <samp>can't distinguish duplicate field names</samp>.
558 * @param object|string $result DB_result object from a query or a
559 * string containing the name of a table.
560 * While this also accepts a query result
561 * resource identifier, this behavior is
563 * @param int $mode a valid tableInfo mode
565 * @return array an associative array with the information requested.
566 * A DB_Error object on failure.
568 * @see DB_common::tableInfo()
569 * @since Method available since Release 1.6.0
571 function tableInfo($result, $mode = null)
573 if (is_string($result)) {
575 * Probably received a table name.
576 * Create a result resource identifier.
578 $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
581 } elseif (isset($result->result)) {
583 * Probably received a result object.
584 * Extract the result resource identifier.
586 $id = $result->result;
590 * Probably received a result resource identifier.
597 if (!is_resource($id)) {
598 return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
601 $flds = @ifx_fieldproperties($id);
602 $count = @ifx_num_fields($id);
604 if (count($flds) != $count) {
605 return $this->raiseError("can't distinguish duplicate field names");
608 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
609 $case_func = 'strtolower';
611 $case_func = 'strval';
618 $res['num_fields'] = $count;
621 foreach ($flds as $key => $value) {
622 $props = explode(';', $value);
624 'table' => $got_string ? $case_func($result) : '',
625 'name' => $case_func($key),
628 'flags' => $props[4] == 'N' ? 'not_null' : '',
630 if ($mode & DB_TABLEINFO_ORDER) {
631 $res['order'][$res[$i]['name']] = $i;
633 if ($mode & DB_TABLEINFO_ORDERTABLE) {
634 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
639 // free the result only if we were called on a table
641 @ifx_free_result($id);
647 // {{{ getSpecialQuery()
650 * Obtains the query string needed for listing a given type of objects
652 * @param string $type the kind of objects you want to retrieve
654 * @return string the SQL query string or null if the driver doesn't
655 * support the object type requested
658 * @see DB_common::getListOf()
660 function getSpecialQuery($type)
664 return 'SELECT tabname FROM systables WHERE tabid >= 100';