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