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