]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/ifx.php
Remove svn:keywords
[SourceForge/phpwiki.git] / lib / pear / DB / ifx.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2004 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Author: Tomas V.V.Cox <cox@idecnet.com>                              |
17 // | Maintainer: Daniel Convissor <danielc@php.net>                       |
18 // +----------------------------------------------------------------------+
19
20 // Legend:
21 // For more info on Informix errors see:
22 // http://www.informix.com/answers/english/ierrors.htm
23 //
24 // TODO:
25 //  - set needed env Informix vars on connect
26 //  - implement native prepare/execute
27
28 require_once 'DB/common.php';
29
30 /**
31  * Database independent query interface definition for PHP's Informix
32  * extension.
33  *
34  * @package  DB
35  * @version  
36  * @category Database
37  * @author   Tomas V.V.Cox <cox@idecnet.com>
38  */
39 class DB_ifx extends DB_common
40 {
41     // {{{ properties
42
43     var $connection;
44     var $affected = 0;
45     var $dsn = array();
46     var $transaction_opcount = 0;
47     var $autocommit = true;
48     var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
49
50     // }}}
51     // {{{ constructor
52
53     function DB_ifx()
54     {
55         $this->phptype = 'ifx';
56         $this->dbsyntax = 'ifx';
57         $this->features = array(
58             'prepare' => false,
59             'pconnect' => true,
60             'transactions' => true,
61             'limit' => 'emulate'
62         );
63         $this->errorcode_map = array(
64             '-201'    => DB_ERROR_SYNTAX,
65             '-206'    => DB_ERROR_NOSUCHTABLE,
66             '-217'    => DB_ERROR_NOSUCHFIELD,
67             '-239'    => DB_ERROR_CONSTRAINT,
68             '-253'    => DB_ERROR_SYNTAX,
69             '-292'    => DB_ERROR_CONSTRAINT_NOT_NULL,
70             '-310'    => DB_ERROR_ALREADY_EXISTS,
71             '-329'    => DB_ERROR_NODBSELECTED,
72             '-346'    => DB_ERROR_CONSTRAINT,
73             '-386'    => DB_ERROR_CONSTRAINT_NOT_NULL,
74             '-391'    => DB_ERROR_CONSTRAINT_NOT_NULL,
75             '-554'    => DB_ERROR_SYNTAX,
76             '-691'    => DB_ERROR_CONSTRAINT,
77             '-703'    => DB_ERROR_CONSTRAINT_NOT_NULL,
78             '-1204'   => DB_ERROR_INVALID_DATE,
79             '-1205'   => DB_ERROR_INVALID_DATE,
80             '-1206'   => DB_ERROR_INVALID_DATE,
81             '-1209'   => DB_ERROR_INVALID_DATE,
82             '-1210'   => DB_ERROR_INVALID_DATE,
83             '-1212'   => DB_ERROR_INVALID_DATE,
84             '-1213'   => DB_ERROR_INVALID_NUMBER,
85         );
86     }
87
88     // }}}
89     // {{{ connect()
90
91     /**
92      * Connect to a database and log in as the specified user.
93      *
94      * @param $dsn the data source name (see DB::parseDSN for syntax)
95      * @param $persistent (optional) whether the connection should
96      *        be persistent
97      *
98      * @return int DB_OK on success, a DB error code on failure
99      */
100     function connect($dsninfo, $persistent = false)
101     {
102         if (!DB::assertExtension('informix') &&
103             !DB::assertExtension('Informix'))
104         {
105             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
106         }
107         $this->dsn = $dsninfo;
108         $dbhost = $dsninfo['hostspec'] ? '@' . $dsninfo['hostspec'] : '';
109         $dbname = $dsninfo['database'] ? $dsninfo['database'] . $dbhost : '';
110         $user = $dsninfo['username'] ? $dsninfo['username'] : '';
111         $pw = $dsninfo['password'] ? $dsninfo['password'] : '';
112
113         $connect_function = $persistent ? 'ifx_pconnect' : 'ifx_connect';
114
115         $this->connection = @$connect_function($dbname, $user, $pw);
116         if (!is_resource($this->connection)) {
117             return $this->ifxraiseError(DB_ERROR_CONNECT_FAILED);
118         }
119         return DB_OK;
120     }
121
122     // }}}
123     // {{{ disconnect()
124
125     /**
126      * Log out and disconnect from the database.
127      *
128      * @return bool true on success, false if not connected.
129      */
130     function disconnect()
131     {
132         $ret = @ifx_close($this->connection);
133         $this->connection = null;
134         return $ret;
135     }
136
137     // }}}
138     // {{{ simpleQuery()
139
140     /**
141      * Send a query to Informix and return the results as a
142      * Informix resource identifier.
143      *
144      * @param $query the SQL query
145      *
146      * @return int returns a valid Informix result for successful SELECT
147      * queries, DB_OK for other successful queries.  A DB error code
148      * is returned on failure.
149      */
150     function simpleQuery($query)
151     {
152         $ismanip = DB::isManip($query);
153         $this->last_query = $query;
154         $this->affected   = null;
155         if (preg_match('/(SELECT)/i', $query)) {    //TESTME: Use !DB::isManip()?
156             // the scroll is needed for fetching absolute row numbers
157             // in a select query result
158             $result = @ifx_query($query, $this->connection, IFX_SCROLL);
159         } else {
160             if (!$this->autocommit && $ismanip) {
161                 if ($this->transaction_opcount == 0) {
162                     $result = @ifx_query('BEGIN WORK', $this->connection);
163                     if (!$result) {
164                         return $this->ifxraiseError();
165                     }
166                 }
167                 $this->transaction_opcount++;
168             }
169             $result = @ifx_query($query, $this->connection);
170         }
171         if (!$result) {
172             return $this->ifxraiseError();
173         }
174         $this->affected = @ifx_affected_rows($result);
175         // Determine which queries should return data, and which
176         // should return an error code only.
177         if (preg_match('/(SELECT)/i', $query)) {
178             return $result;
179         }
180         // XXX Testme: free results inside a transaction
181         // may cause to stop it and commit the work?
182
183         // Result has to be freed even with a insert or update
184         @ifx_free_result($result);
185
186         return DB_OK;
187     }
188
189     // }}}
190     // {{{ nextResult()
191
192     /**
193      * Move the internal ifx result pointer to the next available result
194      *
195      * @param a valid fbsql result resource
196      *
197      * @access public
198      *
199      * @return true if a result is available otherwise return false
200      */
201     function nextResult($result)
202     {
203         return false;
204     }
205
206     // }}}
207     // {{{ affectedRows()
208
209     /**
210      * Gets the number of rows affected by the last query.
211      * if the last query was a select, returns 0.
212      *
213      * @return number of rows affected by the last query
214      */
215     function affectedRows()
216     {
217         if (DB::isManip($this->last_query)) {
218             return $this->affected;
219         } else {
220             return 0;
221         }
222
223     }
224
225     // }}}
226     // {{{ fetchInto()
227
228     /**
229      * Fetch a row and insert the data into an existing array.
230      *
231      * Formating of the array and the data therein are configurable.
232      * See DB_result::fetchInto() for more information.
233      *
234      * @param resource $result query result identifier
235      * @param array    $arr    (reference) array where data from the row
236      *                            should be placed
237      * @param int $fetchmode how the resulting array should be indexed
238      * @param int $rownum    the row number to fetch
239      *
240      * @return mixed DB_OK on success, null when end of result set is
241      *               reached or on failure
242      *
243      * @see DB_result::fetchInto()
244      * @access private
245      */
246     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
247     {
248         if (($rownum !== null) && ($rownum < 0)) {
249             return null;
250         }
251         if ($rownum === null) {
252             /*
253              * Even though fetch_row() should return the next row  if
254              * $rownum is null, it doesn't in all cases.  Bug 598.
255              */
256             $rownum = 'NEXT';
257         } else {
258             // Index starts at row 1, unlike most DBMS's starting at 0.
259             $rownum++;
260         }
261         if (!$arr = @ifx_fetch_row($result, $rownum)) {
262             return null;
263         }
264         if ($fetchmode !== DB_FETCHMODE_ASSOC) {
265             $i=0;
266             $order = array();
267             foreach ($arr as $val) {
268                 $order[$i++] = $val;
269             }
270             $arr = $order;
271         } elseif ($fetchmode == DB_FETCHMODE_ASSOC &&
272                   $this->options['portability'] & DB_PORTABILITY_LOWERCASE)
273         {
274             $arr = array_change_key_case($arr, CASE_LOWER);
275         }
276         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
277             $this->_rtrimArrayValues($arr);
278         }
279         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
280             $this->_convertNullArrayValuesToEmpty($arr);
281         }
282         return DB_OK;
283     }
284
285     // }}}
286     // {{{ numRows()
287
288     function numRows($result)
289     {
290         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
291     }
292
293     // }}}
294     // {{{ numCols()
295
296     /**
297      * Get the number of columns in a result set.
298      *
299      * @param $result Informix result identifier
300      *
301      * @return int the number of columns per row in $result
302      */
303     function numCols($result)
304     {
305         if (!$cols = @ifx_num_fields($result)) {
306             return $this->ifxraiseError();
307         }
308         return $cols;
309     }
310
311     // }}}
312     // {{{ freeResult()
313
314     /**
315      * Free the internal resources associated with $result.
316      *
317      * @param $result Informix result identifier
318      *
319      * @return bool true on success, false if $result is invalid
320      */
321     function freeResult($result)
322     {
323         return @ifx_free_result($result);
324     }
325
326     // }}}
327     // {{{ autoCommit()
328
329     /**
330      * Enable/disable automatic commits
331      */
332     function autoCommit($onoff = true)
333     {
334         // XXX if $this->transaction_opcount > 0, we should probably
335         // issue a warning here.
336         $this->autocommit = $onoff ? true : false;
337         return DB_OK;
338     }
339
340     // }}}
341     // {{{ commit()
342
343     /**
344      * Commit the current transaction.
345      */
346     function commit()
347     {
348         if ($this->transaction_opcount > 0) {
349             $result = @ifx_query('COMMIT WORK', $this->connection);
350             $this->transaction_opcount = 0;
351             if (!$result) {
352                 return $this->ifxRaiseError();
353             }
354         }
355         return DB_OK;
356     }
357
358     // }}}
359     // {{{ rollback()
360
361     /**
362      * Roll back (undo) the current transaction.
363      */
364     function rollback()
365     {
366         if ($this->transaction_opcount > 0) {
367             $result = @ifx_query('ROLLBACK WORK', $this->connection);
368             $this->transaction_opcount = 0;
369             if (!$result) {
370                 return $this->ifxRaiseError();
371             }
372         }
373         return DB_OK;
374     }
375
376     // }}}
377     // {{{ ifxraiseError()
378
379     /**
380      * Gather information about an error, then use that info to create a
381      * DB error object and finally return that object.
382      *
383      * @param integer $errno PEAR error number (usually a DB constant) if
384      *                          manually raising an error
385      * @return object DB error object
386      * @see errorNative()
387      * @see errorCode()
388      * @see DB_common::raiseError()
389      */
390     function ifxraiseError($errno = null)
391     {
392         if ($errno === null) {
393             $errno = $this->errorCode(ifx_error());
394         }
395
396         return $this->raiseError($errno, null, null, null,
397                             $this->errorNative());
398     }
399
400     // }}}
401     // {{{ errorCode()
402
403     /**
404      * Map native error codes to DB's portable ones.
405      *
406      * Requires that the DB implementation's constructor fills
407      * in the <var>$errorcode_map</var> property.
408      *
409      * @param  string $nativecode error code returned by the database
410      * @return int    a portable DB error code, or DB_ERROR if this DB
411      * implementation has no mapping for the given error code.
412      */
413     function errorCode($nativecode)
414     {
415         if (ereg('SQLCODE=(.*)]', $nativecode, $match)) {
416             $code = $match[1];
417             if (isset($this->errorcode_map[$code])) {
418                 return $this->errorcode_map[$code];
419             }
420         }
421         return DB_ERROR;
422     }
423
424     // }}}
425     // {{{ errorNative()
426
427     /**
428      * Get the native error message of the last error (if any) that
429      * occured on the current connection.
430      *
431      * @return int native Informix error code
432      */
433     function errorNative()
434     {
435         return @ifx_error() . ' ' . @ifx_errormsg();
436     }
437
438     // }}}
439     // {{{ getSpecialQuery()
440
441     /**
442      * Returns the query needed to get some backend info
443      * @param  string $type What kind of info you want to retrieve
444      * @return string The SQL query string
445      */
446     function getSpecialQuery($type)
447     {
448         switch ($type) {
449             case 'tables':
450                 return 'select tabname from systables where tabid >= 100';
451             default:
452                 return null;
453         }
454     }
455
456     // }}}
457     // {{{ tableInfo()
458
459     /**
460      * Returns information about a table or a result set.
461      *
462      * NOTE: only supports 'table' if <var>$result</var> is a table name.
463      *
464      * If analyzing a query result and the result has duplicate field names,
465      * an error will be raised saying
466      * <samp>can't distinguish duplicate field names</samp>.
467      *
468      * @param object|string $result DB_result object from a query or a
469      *                                string containing the name of a table
470      * @param  int   $mode a valid tableInfo mode
471      * @return array an associative array with the information requested
472      *                or an error object if something is wrong
473      * @access public
474      * @internal
475      * @since 1.6.0
476      * @see DB_common::tableInfo()
477      */
478     function tableInfo($result, $mode = null)
479     {
480         if (isset($result->result)) {
481             /*
482              * Probably received a result object.
483              * Extract the result resource identifier.
484              */
485             $id = $result->result;
486             $got_string = false;
487         } elseif (is_string($result)) {
488             /*
489              * Probably received a table name.
490              * Create a result resource identifier.
491              */
492             $id = @ifx_query("SELECT * FROM $result WHERE 1=0",
493                              $this->connection);
494             $got_string = true;
495         } else {
496             /*
497              * Probably received a result resource identifier.
498              * Copy it.
499              */
500             $id = $result;
501             $got_string = false;
502         }
503
504         if (!is_resource($id)) {
505             return $this->ifxRaiseError(DB_ERROR_NEED_MORE_DATA);
506         }
507
508         $flds = @ifx_fieldproperties($id);
509         $count = @ifx_num_fields($id);
510
511         if (count($flds) != $count) {
512             return $this->raiseError("can't distinguish duplicate field names");
513         }
514
515         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
516             $case_func = 'strtolower';
517         } else {
518             $case_func = 'strval';
519         }
520
521         $i = 0;
522         // made this IF due to performance (one if is faster than $count if's)
523         if (!$mode) {
524             foreach ($flds as $key => $value) {
525                 $props = explode(';', $value);
526
527                 $res[$i]['table'] = $got_string ? $case_func($result) : '';
528                 $res[$i]['name']  = $case_func($key);
529                 $res[$i]['type']  = $props[0];
530                 $res[$i]['len']   = $props[1];
531                 $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : '';
532                 $i++;
533             }
534
535         } else { // full
536             $res['num_fields'] = $count;
537
538             foreach ($flds as $key => $value) {
539                 $props = explode(';', $value);
540
541                 $res[$i]['table'] = $got_string ? $case_func($result) : '';
542                 $res[$i]['name']  = $case_func($key);
543                 $res[$i]['type']  = $props[0];
544                 $res[$i]['len']   = $props[1];
545                 $res[$i]['flags'] = $props[4] == 'N' ? 'not_null' : '';
546
547                 if ($mode & DB_TABLEINFO_ORDER) {
548                     $res['order'][$res[$i]['name']] = $i;
549                 }
550                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
551                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
552                 }
553                 $i++;
554             }
555         }
556
557         // free the result only if we were called on a table
558         if ($got_string) {
559             @ifx_free_result($id);
560         }
561         return $res;
562     }
563
564     // }}}
565
566 }
567
568 /*
569  * Local variables:
570  * tab-width: 4
571  * c-basic-offset: 4
572  * End:
573  */
574
575 ?>