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-2007 The PHP Group
22 * @license http://www.php.net/license/3_0.txt PHP License 3.0
23 * @version CVS: $Id: ifx.php,v 1.75 2007/07/06 05:19:21 aharvey Exp $
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-2007 The PHP Group
50 * @license http://www.php.net/license/3_0.txt PHP License 3.0
51 * @version Release: 1.7.14RC1
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 '-268' => DB_ERROR_CONSTRAINT,
105 '-292' => DB_ERROR_CONSTRAINT_NOT_NULL,
106 '-310' => DB_ERROR_ALREADY_EXISTS,
107 '-316' => DB_ERROR_ALREADY_EXISTS,
108 '-319' => DB_ERROR_NOT_FOUND,
109 '-329' => DB_ERROR_NODBSELECTED,
110 '-346' => DB_ERROR_CONSTRAINT,
111 '-386' => DB_ERROR_CONSTRAINT_NOT_NULL,
112 '-391' => DB_ERROR_CONSTRAINT_NOT_NULL,
113 '-554' => DB_ERROR_SYNTAX,
114 '-691' => DB_ERROR_CONSTRAINT,
115 '-692' => DB_ERROR_CONSTRAINT,
116 '-703' => DB_ERROR_CONSTRAINT_NOT_NULL,
117 '-1202' => DB_ERROR_DIVZERO,
118 '-1204' => DB_ERROR_INVALID_DATE,
119 '-1205' => DB_ERROR_INVALID_DATE,
120 '-1206' => DB_ERROR_INVALID_DATE,
121 '-1209' => DB_ERROR_INVALID_DATE,
122 '-1210' => DB_ERROR_INVALID_DATE,
123 '-1212' => DB_ERROR_INVALID_DATE,
124 '-1213' => DB_ERROR_INVALID_NUMBER,
128 * The raw database connection created by PHP
134 * The DSN information for connecting to a database
141 * Should data manipulation queries be committed automatically?
145 var $autocommit = true;
148 * The quantity of transactions begun
150 * {@internal While this is private, it can't actually be designated
151 * private in PHP 5 because it is directly accessed in the test suite.}}
156 var $transaction_opcount = 0;
159 * The number of rows affected by a data manipulation query
170 * This constructor calls <kbd>$this->DB_common()</kbd>
183 * Connect to the database server, log in and open the database
185 * Don't call this method directly. Use DB::connect() instead.
187 * @param array $dsn the data source name
188 * @param bool $persistent should the connection be persistent?
190 * @return int DB_OK on success. A DB_Error object on failure.
192 function connect($dsn, $persistent = false)
194 if (!PEAR::loadExtension('informix') &&
195 !PEAR::loadExtension('Informix'))
197 return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
201 if ($dsn['dbsyntax']) {
202 $this->dbsyntax = $dsn['dbsyntax'];
205 $dbhost = $dsn['hostspec'] ? '@' . $dsn['hostspec'] : '';
206 $dbname = $dsn['database'] ? $dsn['database'] . $dbhost : '';
207 $user = $dsn['username'] ? $dsn['username'] : '';
208 $pw = $dsn['password'] ? $dsn['password'] : '';
210 $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
212 $this->connection = @$connect_function($dbname, $user, $pw);
213 if (!is_resource($this->connection)) {
214 return $this->ifxRaiseError(DB_ERROR_CONNECT_FAILED);
223 * Disconnects from the database server
225 * @return bool TRUE on success, FALSE on failure
227 function disconnect()
229 $ret = @ifx_close($this->connection);
230 $this->connection = null;
238 * Sends a query to the database server
240 * @param string the SQL query string
242 * @return mixed + a PHP result resrouce for successful SELECT queries
243 * + the DB_OK constant for other successful queries
244 * + a DB_Error object on failure
246 function simpleQuery($query)
248 $ismanip = $this->_checkManip($query);
249 $this->last_query = $query;
250 $this->affected = null;
251 if (preg_match('/(SELECT|EXECUTE)/i', $query)) { //TESTME: Use !DB::isManip()?
252 // the scroll is needed for fetching absolute row numbers
253 // in a select query result
254 $result = @ifx_query($query, $this->connection, IFX_SCROLL);
256 if (!$this->autocommit && $ismanip) {
257 if ($this->transaction_opcount == 0) {
258 $result = @ifx_query('BEGIN WORK', $this->connection);
260 return $this->ifxRaiseError();
263 $this->transaction_opcount++;
265 $result = @ifx_query($query, $this->connection);
268 return $this->ifxRaiseError();
270 $this->affected = @ifx_affected_rows($result);
271 // Determine which queries should return data, and which
272 // should return an error code only.
273 if (preg_match('/(SELECT|EXECUTE)/i', $query)) {
276 // XXX Testme: free results inside a transaction
277 // may cause to stop it and commit the work?
279 // Result has to be freed even with a insert or update
280 @ifx_free_result($result);
289 * Move the internal ifx result pointer to the next available result
291 * @param a valid fbsql result resource
295 * @return true if a result is available otherwise return false
297 function nextResult($result)
303 // {{{ affectedRows()
306 * Determines the number of rows affected by a data maniuplation query
308 * 0 is returned for queries that don't manipulate data.
310 * @return int the number of rows. A DB_Error object on failure.
312 function affectedRows()
314 if ($this->_last_query_manip) {
315 return $this->affected;
325 * Places a row from the result set into the given array
327 * Formating of the array and the data therein are configurable.
328 * See DB_result::fetchInto() for more information.
330 * This method is not meant to be called directly. Use
331 * DB_result::fetchInto() instead. It can't be declared "protected"
332 * because DB_result is a separate object.
334 * @param resource $result the query result resource
335 * @param array $arr the referenced array to put the data in
336 * @param int $fetchmode how the resulting array should be indexed
337 * @param int $rownum the row number to fetch (0 = first row)
339 * @return mixed DB_OK on success, NULL when the end of a result set is
340 * reached or on failure
342 * @see DB_result::fetchInto()
344 function fetchInto($result, &$arr, $fetchmode, $rownum = null)
346 if (($rownum !== null) && ($rownum < 0)) {
349 if ($rownum === null) {
351 * Even though fetch_row() should return the next row if
352 * $rownum is null, it doesn't in all cases. Bug 598.
356 // Index starts at row 1, unlike most DBMS's starting at 0.
359 if (!$arr = @ifx_fetch_row($result, $rownum)) {
362 if ($fetchmode !== DB_FETCHMODE_ASSOC) {
365 foreach ($arr as $val) {
369 } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
370 $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
372 $arr = array_change_key_case($arr, CASE_LOWER);
374 if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
375 $this->_rtrimArrayValues($arr);
377 if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
378 $this->_convertNullArrayValuesToEmpty($arr);
387 * Gets the number of columns in a result set
389 * This method is not meant to be called directly. Use
390 * DB_result::numCols() instead. It can't be declared "protected"
391 * because DB_result is a separate object.
393 * @param resource $result PHP's query result resource
395 * @return int the number of columns. A DB_Error object on failure.
397 * @see DB_result::numCols()
399 function numCols($result)
401 if (!$cols = @ifx_num_fields($result)) {
402 return $this->ifxRaiseError();
411 * Deletes the result set and frees the memory occupied by the result set
413 * This method is not meant to be called directly. Use
414 * DB_result::free() instead. It can't be declared "protected"
415 * because DB_result is a separate object.
417 * @param resource $result PHP's query result resource
419 * @return bool TRUE on success, FALSE if $result is invalid
421 * @see DB_result::free()
423 function freeResult($result)
425 return is_resource($result) ? ifx_free_result($result) : false;
432 * Enables or disables automatic commits
434 * @param bool $onoff true turns it on, false turns it off
436 * @return int DB_OK on success. A DB_Error object if the driver
437 * doesn't support auto-committing transactions.
439 function autoCommit($onoff = true)
441 // XXX if $this->transaction_opcount > 0, we should probably
442 // issue a warning here.
443 $this->autocommit = $onoff ? true : false;
451 * Commits the current transaction
453 * @return int DB_OK on success. A DB_Error object on failure.
457 if ($this->transaction_opcount > 0) {
458 $result = @ifx_query('COMMIT WORK', $this->connection);
459 $this->transaction_opcount = 0;
461 return $this->ifxRaiseError();
471 * Reverts the current transaction
473 * @return int DB_OK on success. A DB_Error object on failure.
477 if ($this->transaction_opcount > 0) {
478 $result = @ifx_query('ROLLBACK WORK', $this->connection);
479 $this->transaction_opcount = 0;
481 return $this->ifxRaiseError();
488 // {{{ ifxRaiseError()
491 * Produces a DB_Error object regarding the current problem
493 * @param int $errno if the error is being manually raised pass a
494 * DB_ERROR* constant here. If this isn't passed
495 * the error information gathered from the DBMS.
497 * @return object the DB_Error object
499 * @see DB_common::raiseError(),
500 * DB_ifx::errorNative(), DB_ifx::errorCode()
502 function ifxRaiseError($errno = null)
504 if ($errno === null) {
505 $errno = $this->errorCode(ifx_error());
507 return $this->raiseError($errno, null, null, null,
508 $this->errorNative());
515 * Gets the DBMS' native error code and message produced by the last query
517 * @return string the DBMS' error code and message
519 function errorNative()
521 return @ifx_error() . ' ' . @ifx_errormsg();
528 * Maps native error codes to DB's portable ones.
530 * Requires that the DB implementation's constructor fills
531 * in the <var>$errorcode_map</var> property.
533 * @param string $nativecode error code returned by the database
534 * @return int a portable DB error code, or DB_ERROR if this DB
535 * implementation has no mapping for the given error code.
537 function errorCode($nativecode)
539 if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
541 if (isset($this->errorcode_map[$code])) {
542 return $this->errorcode_map[$code];
552 * Returns information about a table or a result set
554 * NOTE: only supports 'table' if <var>$result</var> is a table name.
556 * If analyzing a query result and the result has duplicate field names,
557 * an error will be raised saying
558 * <samp>can't distinguish duplicate field names</samp>.
560 * @param object|string $result DB_result object from a query or a
561 * string containing the name of a table.
562 * While this also accepts a query result
563 * resource identifier, this behavior is
565 * @param int $mode a valid tableInfo mode
567 * @return array an associative array with the information requested.
568 * A DB_Error object on failure.
570 * @see DB_common::tableInfo()
571 * @since Method available since Release 1.6.0
573 function tableInfo($result, $mode = null)
575 if (is_string($result)) {
577 * Probably received a table name.
578 * Create a result resource identifier.
580 $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
583 } elseif (isset($result->result)) {
585 * Probably received a result object.
586 * Extract the result resource identifier.
588 $id = $result->result;
592 * Probably received a result resource identifier.
599 if (!is_resource($id)) {
600 return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
603 $flds = @ifx_fieldproperties($id);
604 $count = @ifx_num_fields($id);
606 if (count($flds) != $count) {
607 return $this->raiseError("can't distinguish duplicate field names");
610 if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
611 $case_func = 'strtolower';
613 $case_func = 'strval';
620 $res['num_fields'] = $count;
623 foreach ($flds as $key => $value) {
624 $props = explode(';', $value);
626 'table' => $got_string ? $case_func($result) : '',
627 'name' => $case_func($key),
630 'flags' => $props[4] == 'N' ? 'not_null' : '',
632 if ($mode & DB_TABLEINFO_ORDER) {
633 $res['order'][$res[$i]['name']] = $i;
635 if ($mode & DB_TABLEINFO_ORDERTABLE) {
636 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
641 // free the result only if we were called on a table
643 @ifx_free_result($id);
649 // {{{ getSpecialQuery()
652 * Obtains the query string needed for listing a given type of objects
654 * @param string $type the kind of objects you want to retrieve
656 * @return string the SQL query string or null if the driver doesn't
657 * support the object type requested
660 * @see DB_common::getListOf()
662 function getSpecialQuery($type)
666 return 'SELECT tabname FROM systables WHERE tabid >= 100';