]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/oci8.php
Activated Id substitution for Subversion
[SourceForge/phpwiki.git] / lib / pear / DB / oci8.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: James L. Pine <jlp@valinux.com>                              |
17 // | Maintainer: Daniel Convissor <danielc@php.net>                       |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id$
21
22
23 // be aware...  OCIError() only appears to return anything when given a
24 // statement, so functions return the generic DB_ERROR instead of more
25 // useful errors that have to do with feedback from the database.
26
27
28 require_once 'DB/common.php';
29
30 /**
31  * Database independent query interface definition for PHP's Oracle 8
32  * call-interface extension.
33  *
34  * Definitely works with versions 8 and 9 of Oracle.
35  *
36  * @package  DB
37  * @version  $Id$
38  * @category Database
39  * @author   James L. Pine <jlp@valinux.com>
40  */
41 class DB_oci8 extends DB_common
42 {
43     // {{{ properties
44
45     var $connection;
46     var $phptype, $dbsyntax;
47     var $manip_query = array();
48     var $prepare_types = array();
49     var $autoCommit = 1;
50     var $last_stmt = false;
51
52     /**
53      * stores the $data passed to execute() in the oci8 driver
54      *
55      * Gets reset to array() when simpleQuery() is run.
56      *
57      * Needed in case user wants to call numRows() after prepare/execute
58      * was used.
59      *
60      * @var array
61      * @access private
62      */
63     var $_data = array();
64
65     // }}}
66     // {{{ constructor
67
68     function DB_oci8()
69     {
70         $this->DB_common();
71         $this->phptype = 'oci8';
72         $this->dbsyntax = 'oci8';
73         $this->features = array(
74             'prepare' => false,
75             'pconnect' => true,
76             'transactions' => true,
77             'limit' => 'alter'
78         );
79         $this->errorcode_map = array(
80             1 => DB_ERROR_CONSTRAINT,
81             900 => DB_ERROR_SYNTAX,
82             904 => DB_ERROR_NOSUCHFIELD,
83             921 => DB_ERROR_SYNTAX,
84             923 => DB_ERROR_SYNTAX,
85             942 => DB_ERROR_NOSUCHTABLE,
86             955 => DB_ERROR_ALREADY_EXISTS,
87             1400 => DB_ERROR_CONSTRAINT_NOT_NULL,
88             1407 => DB_ERROR_CONSTRAINT_NOT_NULL,
89             1476 => DB_ERROR_DIVZERO,
90             1722 => DB_ERROR_INVALID_NUMBER,
91             2289 => DB_ERROR_NOSUCHTABLE,
92             2291 => DB_ERROR_CONSTRAINT,
93             2449 => DB_ERROR_CONSTRAINT,
94         );
95     }
96
97     // }}}
98     // {{{ connect()
99
100     /**
101      * Connect to a database and log in as the specified user.
102      *
103      * @param $dsn the data source name (see DB::parseDSN for syntax)
104      * @param $persistent (optional) whether the connection should
105      *        be persistent
106      *
107      * @return int DB_OK on success, a DB error code on failure
108      */
109     function connect($dsninfo, $persistent = false)
110     {
111         if (!DB::assertExtension('oci8')) {
112             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
113         }
114         $this->dsn = $dsninfo;
115
116         $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
117
118         if ($dsninfo['hostspec']) {
119             $conn = @$connect_function($dsninfo['username'],
120                                        $dsninfo['password'],
121                                        $dsninfo['hostspec']);
122         } elseif ($dsninfo['username'] || $dsninfo['password']) {
123             $conn = @$connect_function($dsninfo['username'],
124                                        $dsninfo['password']);
125         } else {
126             $conn = false;
127         }
128         if ($conn == false) {
129             $error = OCIError();
130             $error = (is_array($error)) ? $error['message'] : null;
131             return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
132                                      null, $error);
133         }
134         $this->connection = $conn;
135         return DB_OK;
136     }
137
138     // }}}
139     // {{{ disconnect()
140
141     /**
142      * Log out and disconnect from the database.
143      *
144      * @return bool true on success, false if not connected.
145      */
146     function disconnect()
147     {
148         $ret = @OCILogOff($this->connection);
149         $this->connection = null;
150         return $ret;
151     }
152
153     // }}}
154     // {{{ simpleQuery()
155
156     /**
157      * Send a query to oracle and return the results as an oci8 resource
158      * identifier.
159      *
160      * @param $query the SQL query
161      *
162      * @return int returns a valid oci8 result for successful SELECT
163      * queries, DB_OK for other successful queries.  A DB error code
164      * is returned on failure.
165      */
166     function simpleQuery($query)
167     {
168         $this->_data = array();
169         $this->last_query = $query;
170         $query = $this->modifyQuery($query);
171         $result = @OCIParse($this->connection, $query);
172         if (!$result) {
173             return $this->oci8RaiseError();
174         }
175         if ($this->autoCommit) {
176             $success = @OCIExecute($result,OCI_COMMIT_ON_SUCCESS);
177         } else {
178             $success = @OCIExecute($result,OCI_DEFAULT);
179         }
180         if (!$success) {
181             return $this->oci8RaiseError($result);
182         }
183         $this->last_stmt=$result;
184         // Determine which queries that should return data, and which
185         // should return an error code only.
186         return DB::isManip($query) ? DB_OK : $result;
187     }
188
189     // }}}
190     // {{{ nextResult()
191
192     /**
193      * Move the internal oracle result pointer to the next available result
194      *
195      * @param a valid oci8 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     // {{{ fetchInto()
208
209     /**
210      * Fetch a row and insert the data into an existing array.
211      *
212      * Formating of the array and the data therein are configurable.
213      * See DB_result::fetchInto() for more information.
214      *
215      * @param resource $result    query result identifier
216      * @param array    $arr       (reference) array where data from the row
217      *                            should be placed
218      * @param int      $fetchmode how the resulting array should be indexed
219      * @param int      $rownum    the row number to fetch
220      *
221      * @return mixed DB_OK on success, null when end of result set is
222      *               reached or on failure
223      *
224      * @see DB_result::fetchInto()
225      * @access private
226      */
227     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
228     {
229         if ($rownum !== null) {
230             return $this->raiseError(DB_ERROR_NOT_CAPABLE);
231         }
232         if ($fetchmode & DB_FETCHMODE_ASSOC) {
233             $moredata = @OCIFetchInto($result,$arr,OCI_ASSOC+OCI_RETURN_NULLS+OCI_RETURN_LOBS);
234             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
235                 $moredata)
236             {
237                 $arr = array_change_key_case($arr, CASE_LOWER);
238             }
239         } else {
240             $moredata = OCIFetchInto($result,$arr,OCI_RETURN_NULLS+OCI_RETURN_LOBS);
241         }
242         if (!$moredata) {
243             return null;
244         }
245         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
246             $this->_rtrimArrayValues($arr);
247         }
248         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
249             $this->_convertNullArrayValuesToEmpty($arr);
250         }
251         return DB_OK;
252     }
253
254     // }}}
255     // {{{ freeResult()
256
257     /**
258      * Free the internal resources associated with $result.
259      *
260      * @param $result oci8 result identifier
261      *
262      * @return bool true on success, false if $result is invalid
263      */
264     function freeResult($result)
265     {
266         return @OCIFreeStatement($result);
267     }
268
269     /**
270      * Free the internal resources associated with a prepared query.
271      *
272      * @param $stmt oci8 statement identifier
273      *
274      * @return bool true on success, false if $result is invalid
275      */
276     function freePrepared($stmt)
277     {
278         if (isset($this->prepare_types[(int)$stmt])) {
279             unset($this->prepare_types[(int)$stmt]);
280             unset($this->manip_query[(int)$stmt]);
281         } else {
282             return false;
283         }
284         return true;
285     }
286
287     // }}}
288     // {{{ numRows()
289
290     function numRows($result)
291     {
292         // emulate numRows for Oracle.  yuck.
293         if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
294             $result === $this->last_stmt)
295         {
296             $countquery = 'SELECT COUNT(*) FROM ('.$this->last_query.')';
297             $save_query = $this->last_query;
298             $save_stmt = $this->last_stmt;
299
300             if (count($this->_data)) {
301                 $smt = $this->prepare('SELECT COUNT(*) FROM ('.$this->last_query.')');
302                 $count = $this->execute($smt, $this->_data);
303             } else {
304                 $count =& $this->query($countquery);
305             }
306
307             if (DB::isError($count) ||
308                 DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED)))
309             {
310                 $this->last_query = $save_query;
311                 $this->last_stmt = $save_stmt;
312                 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
313             }
314             return $row[0];
315         }
316         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
317     }
318
319     // }}}
320     // {{{ numCols()
321
322     /**
323      * Get the number of columns in a result set.
324      *
325      * @param $result oci8 result identifier
326      *
327      * @return int the number of columns per row in $result
328      */
329     function numCols($result)
330     {
331         $cols = @OCINumCols($result);
332         if (!$cols) {
333             return $this->oci8RaiseError($result);
334         }
335         return $cols;
336     }
337
338     // }}}
339     // {{{ errorNative()
340
341     /**
342      * Get the native error code of the last error (if any) that occured
343      * on the current connection.  This does not work, as OCIError does
344      * not work unless given a statement.  If OCIError does return
345      * something, so will this.
346      *
347      * @return int native oci8 error code
348      */
349     function errorNative()
350     {
351         if (is_resource($this->last_stmt)) {
352             $error = @OCIError($this->last_stmt);
353         } else {
354             $error = @OCIError($this->connection);
355         }
356         if (is_array($error)) {
357             return $error['code'];
358         }
359         return false;
360     }
361
362     // }}}
363     // {{{ prepare()
364
365     /**
366      * Prepares a query for multiple execution with execute().
367      *
368      * With oci8, this is emulated.
369      *
370      * prepare() requires a generic query as string like <code>
371      *    INSERT INTO numbers VALUES (?, ?, ?)
372      * </code>.  The <kbd>?</kbd> characters are placeholders.
373      *
374      * Three types of placeholders can be used:
375      *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
376      *   + <kbd>!</kbd>  value is inserted 'as is'
377      *   + <kbd>&</kbd>  requires a file name.  The file's contents get
378      *                     inserted into the query (i.e. saving binary
379      *                     data in a db)
380      *
381      * Use backslashes to escape placeholder characters if you don't want
382      * them to be interpreted as placeholders.  Example: <code>
383      *    "UPDATE foo SET col=? WHERE col='over \& under'"
384      * </code>
385      *
386      * @param string $query query to be prepared
387      * @return mixed DB statement resource on success. DB_Error on failure.
388      */
389     function prepare($query)
390     {
391         $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
392                                PREG_SPLIT_DELIM_CAPTURE);
393         $binds    = count($tokens) - 1;
394         $token    = 0;
395         $types    = array();
396         $newquery = '';
397
398         foreach ($tokens as $key => $val) {
399             switch ($val) {
400                 case '?':
401                     $types[$token++] = DB_PARAM_SCALAR;
402                     unset($tokens[$key]);
403                     break;
404                 case '&':
405                     $types[$token++] = DB_PARAM_OPAQUE;
406                     unset($tokens[$key]);
407                     break;
408                 case '!':
409                     $types[$token++] = DB_PARAM_MISC;
410                     unset($tokens[$key]);
411                     break;
412                 default:
413                     $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
414                     if ($key != $binds) {
415                         $newquery .= $tokens[$key] . ':bind' . $token;
416                     } else {
417                         $newquery .= $tokens[$key];
418                     }
419             }
420         }
421
422         $this->last_query = $query;
423         $newquery = $this->modifyQuery($newquery);
424         if (!$stmt = @OCIParse($this->connection, $newquery)) {
425             return $this->oci8RaiseError();
426         }
427         $this->prepare_types[$stmt] = $types;
428         $this->manip_query[(int)$stmt] = DB::isManip($query);
429         return $stmt;
430     }
431
432     // }}}
433     // {{{ execute()
434
435     /**
436      * Executes a DB statement prepared with prepare().
437      *
438      * @param resource  $stmt  a DB statement resource returned from prepare()
439      * @param mixed  $data  array, string or numeric data to be used in
440      *                      execution of the statement.  Quantity of items
441      *                      passed must match quantity of placeholders in
442      *                      query:  meaning 1 for non-array items or the
443      *                      quantity of elements in the array.
444      * @return int returns an oci8 result resource for successful
445      * SELECT queries, DB_OK for other successful queries.  A DB error
446      * code is returned on failure.
447      * @see DB_oci::prepare()
448      */
449     function &execute($stmt, $data = array())
450     {
451         if (!is_array($data)) {
452             $data = array($data);
453         }
454
455         $this->_data = $data;
456
457         $types =& $this->prepare_types[$stmt];
458         if (count($types) != count($data)) {
459             $tmp =& $this->raiseError(DB_ERROR_MISMATCH);
460             return $tmp;
461         }
462
463         $i = 0;
464         foreach ($data as $key => $value) {
465             if ($types[$i] == DB_PARAM_MISC) {
466                 /*
467                  * Oracle doesn't seem to have the ability to pass a
468                  * parameter along unchanged, so strip off quotes from start
469                  * and end, plus turn two single quotes to one single quote,
470                  * in order to avoid the quotes getting escaped by
471                  * Oracle and ending up in the database.
472                  */
473                 $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
474                 $data[$key] = str_replace("''", "'", $data[$key]);
475             } elseif ($types[$i] == DB_PARAM_OPAQUE) {
476                 $fp = @fopen($data[$key], 'rb');
477                 if (!$fp) {
478                     $tmp =& $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
479                     return $tmp;
480                 }
481                 $data[$key] = fread($fp, filesize($data[$key]));
482                 fclose($fp);
483             }
484             if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
485                 $tmp = $this->oci8RaiseError($stmt);
486                 return $tmp;
487             }
488             $i++;
489         }
490         if ($this->autoCommit) {
491             $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
492         } else {
493             $success = @OCIExecute($stmt, OCI_DEFAULT);
494         }
495         if (!$success) {
496             $tmp = $this->oci8RaiseError($stmt);
497             return $tmp;
498         }
499         $this->last_stmt = $stmt;
500         if ($this->manip_query[(int)$stmt]) {
501             $tmp = DB_OK;
502         } else {
503             $tmp =& new DB_result($this, $stmt);
504         }
505         return $tmp;
506     }
507
508     // }}}
509     // {{{ autoCommit()
510
511     /**
512      * Enable/disable automatic commits
513      *
514      * @param $onoff true/false whether to autocommit
515      */
516     function autoCommit($onoff = false)
517     {
518         $this->autoCommit = (bool)$onoff;;
519         return DB_OK;
520     }
521
522     // }}}
523     // {{{ commit()
524
525     /**
526      * Commit transactions on the current connection
527      *
528      * @return DB_ERROR or DB_OK
529      */
530     function commit()
531     {
532         $result = @OCICommit($this->connection);
533         if (!$result) {
534             return $this->oci8RaiseError();
535         }
536         return DB_OK;
537     }
538
539     // }}}
540     // {{{ rollback()
541
542     /**
543      * Roll back all uncommitted transactions on the current connection.
544      *
545      * @return DB_ERROR or DB_OK
546      */
547     function rollback()
548     {
549         $result = @OCIRollback($this->connection);
550         if (!$result) {
551             return $this->oci8RaiseError();
552         }
553         return DB_OK;
554     }
555
556     // }}}
557     // {{{ affectedRows()
558
559     /**
560      * Gets the number of rows affected by the last query.
561      * if the last query was a select, returns 0.
562      *
563      * @return number of rows affected by the last query or DB_ERROR
564      */
565     function affectedRows()
566     {
567         if ($this->last_stmt === false) {
568             return $this->oci8RaiseError();
569         }
570         $result = @OCIRowCount($this->last_stmt);
571         if ($result === false) {
572             return $this->oci8RaiseError($this->last_stmt);
573         }
574         return $result;
575     }
576
577     // }}}
578     // {{{ modifyQuery()
579
580     function modifyQuery($query)
581     {
582         // "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle
583         if (preg_match('/^\s*SELECT/i', $query) &&
584             !preg_match('/\sFROM\s/i', $query)) {
585             $query .= ' FROM dual';
586         }
587         return $query;
588     }
589
590     // }}}
591     // {{{ modifyLimitQuery()
592
593     /**
594      * Emulate the row limit support altering the query
595      *
596      * @param string $query The query to treat
597      * @param int    $from  The row to start to fetch from
598      * @param int    $count The offset
599      * @return string The modified query
600      *
601      * @author Tomas V.V.Cox <cox@idecnet.com>
602      */
603     function modifyLimitQuery($query, $from, $count)
604     {
605         // Let Oracle return the name of the columns instead of
606         // coding a "home" SQL parser
607         $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
608         if (!$result = @OCIParse($this->connection, $q_fields)) {
609             $this->last_query = $q_fields;
610             return $this->oci8RaiseError();
611         }
612         if (!@OCIExecute($result, OCI_DEFAULT)) {
613             $this->last_query = $q_fields;
614             return $this->oci8RaiseError($result);
615         }
616         $ncols = OCINumCols($result);
617         $cols  = array();
618         for ( $i = 1; $i <= $ncols; $i++ ) {
619             $cols[] = '"' . OCIColumnName($result, $i) . '"';
620         }
621         $fields = implode(', ', $cols);
622         // XXX Test that (tip by John Lim)
623         //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
624         //    // Introduce the FIRST_ROWS Oracle query optimizer
625         //    $query = substr($query, strlen($match[0]), strlen($query));
626         //    $query = "SELECT /* +FIRST_ROWS */ " . $query;
627         //}
628
629         // Construct the query
630         // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
631         // Perhaps this could be optimized with the use of Unions
632         $query = "SELECT $fields FROM".
633                  "  (SELECT rownum as linenum, $fields FROM".
634                  "      ($query)".
635                  '  WHERE rownum <= '. ($from + $count) .
636                  ') WHERE linenum >= ' . ++$from;
637         return $query;
638     }
639
640     // }}}
641     // {{{ nextId()
642
643     /**
644      * Returns the next free id in a sequence
645      *
646      * @param string  $seq_name  name of the sequence
647      * @param boolean $ondemand  when true, the seqence is automatically
648      *                           created if it does not exist
649      *
650      * @return int  the next id number in the sequence.  DB_Error if problem.
651      *
652      * @internal
653      * @see DB_common::nextID()
654      * @access public
655      */
656     function nextId($seq_name, $ondemand = true)
657     {
658         $seqname = $this->getSequenceName($seq_name);
659         $repeat = 0;
660         do {
661             $this->expectError(DB_ERROR_NOSUCHTABLE);
662             $result =& $this->query("SELECT ${seqname}.nextval FROM dual");
663             $this->popExpect();
664             if ($ondemand && DB::isError($result) &&
665                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
666                 $repeat = 1;
667                 $result = $this->createSequence($seq_name);
668                 if (DB::isError($result)) {
669                     return $this->raiseError($result);
670                 }
671             } else {
672                 $repeat = 0;
673             }
674         } while ($repeat);
675         if (DB::isError($result)) {
676             return $this->raiseError($result);
677         }
678         $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
679         return $arr[0];
680     }
681
682     /**
683      * Creates a new sequence
684      *
685      * @param string $seq_name  name of the new sequence
686      *
687      * @return int  DB_OK on success.  A DB_Error object is returned if
688      *              problems arise.
689      *
690      * @internal
691      * @see DB_common::createSequence()
692      * @access public
693      */
694     function createSequence($seq_name)
695     {
696         $seqname = $this->getSequenceName($seq_name);
697         return $this->query("CREATE SEQUENCE ${seqname}");
698     }
699
700     // }}}
701     // {{{ dropSequence()
702
703     /**
704      * Deletes a sequence
705      *
706      * @param string $seq_name  name of the sequence to be deleted
707      *
708      * @return int  DB_OK on success.  DB_Error if problems.
709      *
710      * @internal
711      * @see DB_common::dropSequence()
712      * @access public
713      */
714     function dropSequence($seq_name)
715     {
716         $seqname = $this->getSequenceName($seq_name);
717         return $this->query("DROP SEQUENCE ${seqname}");
718     }
719
720     // }}}
721     // {{{ oci8RaiseError()
722
723     /**
724      * Gather information about an error, then use that info to create a
725      * DB error object and finally return that object.
726      *
727      * @param  integer  $errno  PEAR error number (usually a DB constant) if
728      *                          manually raising an error
729      * @return object  DB error object
730      * @see DB_common::errorCode()
731      * @see DB_common::raiseError()
732      */
733     function oci8RaiseError($errno = null)
734     {
735         if ($errno === null) {
736             $error = @OCIError($this->connection);
737             return $this->raiseError($this->errorCode($error['code']),
738                                      null, null, null, $error['message']);
739         } elseif (is_resource($errno)) {
740             $error = @OCIError($errno);
741             return $this->raiseError($this->errorCode($error['code']),
742                                      null, null, null, $error['message']);
743         }
744         return $this->raiseError($this->errorCode($errno));
745     }
746
747     // }}}
748     // {{{ getSpecialQuery()
749
750     /**
751      * Returns the query needed to get some backend info
752      * @param string $type What kind of info you want to retrieve
753      * @return string The SQL query string
754      */
755     function getSpecialQuery($type)
756     {
757         switch ($type) {
758             case 'tables':
759                 return 'SELECT table_name FROM user_tables';
760             default:
761                 return null;
762         }
763     }
764
765     // }}}
766     // {{{ tableInfo()
767
768     /**
769      * Returns information about a table or a result set.
770      *
771      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
772      * is a table name.
773      *
774      * NOTE: flags won't contain index information.
775      *
776      * @param object|string  $result  DB_result object from a query or a
777      *                                string containing the name of a table
778      * @param int            $mode    a valid tableInfo mode
779      * @return array  an associative array with the information requested
780      *                or an error object if something is wrong
781      * @access public
782      * @internal
783      * @see DB_common::tableInfo()
784      */
785     function tableInfo($result, $mode = null)
786     {
787         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
788             $case_func = 'strtolower';
789         } else {
790             $case_func = 'strval';
791         }
792
793         if (is_string($result)) {
794             /*
795              * Probably received a table name.
796              * Create a result resource identifier.
797              */
798             $result = strtoupper($result);
799             $q_fields = 'SELECT column_name, data_type, data_length, '
800                         . 'nullable '
801                         . 'FROM user_tab_columns '
802                         . "WHERE table_name='$result' ORDER BY column_id";
803
804             $this->last_query = $q_fields;
805
806             if (!$stmt = @OCIParse($this->connection, $q_fields)) {
807                 return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
808             }
809             if (!@OCIExecute($stmt, OCI_DEFAULT)) {
810                 return $this->oci8RaiseError($stmt);
811             }
812
813             $i = 0;
814             while (@OCIFetch($stmt)) {
815                 $res[$i]['table'] = $case_func($result);
816                 $res[$i]['name']  = $case_func(@OCIResult($stmt, 1));
817                 $res[$i]['type']  = @OCIResult($stmt, 2);
818                 $res[$i]['len']   = @OCIResult($stmt, 3);
819                 $res[$i]['flags'] = (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '';
820
821                 if ($mode & DB_TABLEINFO_ORDER) {
822                     $res['order'][$res[$i]['name']] = $i;
823                 }
824                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
825                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
826                 }
827                 $i++;
828             }
829
830             if ($mode) {
831                 $res['num_fields'] = $i;
832             }
833             @OCIFreeStatement($stmt);
834
835         } else {
836             if (isset($result->result)) {
837                 /*
838                  * Probably received a result object.
839                  * Extract the result resource identifier.
840                  */
841                 $result = $result->result;
842             } else {
843                 /*
844                  * ELSE, probably received a result resource identifier.
845                  * Depricated.  Here for compatibility only.
846                  */
847             }
848
849             if ($result === $this->last_stmt) {
850                 $count = @OCINumCols($result);
851
852                 for ($i=0; $i<$count; $i++) {
853                     $res[$i]['table'] = '';
854                     $res[$i]['name']  = $case_func(@OCIColumnName($result, $i+1));
855                     $res[$i]['type']  = @OCIColumnType($result, $i+1);
856                     $res[$i]['len']   = @OCIColumnSize($result, $i+1);
857                     $res[$i]['flags'] = '';
858
859                     if ($mode & DB_TABLEINFO_ORDER) {
860                         $res['order'][$res[$i]['name']] = $i;
861                     }
862                     if ($mode & DB_TABLEINFO_ORDERTABLE) {
863                         $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
864                     }
865                 }
866
867                 if ($mode) {
868                     $res['num_fields'] = $count;
869                 }
870
871             } else {
872                 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
873             }
874         }
875         return $res;
876     }
877
878     // }}}
879
880 }
881
882 /*
883  * Local variables:
884  * tab-width: 4
885  * c-basic-offset: 4
886  * End:
887  */
888
889 ?>