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