]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/oci8.php
[ROUTES] Allow accept-header specification during router creation
[quix0rs-gnu-social.git] / extlib / DB / oci8.php
1 <?php
2
3 /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5 /**
6  * The PEAR DB driver for PHP's oci8 extension
7  * for interacting with Oracle databases
8  *
9  * PHP version 5
10  *
11  * LICENSE: This source file is subject to version 3.0 of the PHP license
12  * that is available through the world-wide-web at the following URI:
13  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14  * the PHP License and are unable to obtain it through the web, please
15  * send a note to license@php.net so we can mail you a copy immediately.
16  *
17  * @category   Database
18  * @package    DB
19  * @author     James L. Pine <jlp@valinux.com>
20  * @author     Daniel Convissor <danielc@php.net>
21  * @copyright  1997-2007 The PHP Group
22  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23  * @version    CVS: $Id$
24  * @link       http://pear.php.net/package/DB
25  */
26
27 /**
28  * Obtain the DB_common class so it can be extended from
29  */
30 //require_once 'DB/common.php';
31 require_once 'common.php';
32
33 /**
34  * The methods PEAR DB uses to interact with PHP's oci8 extension
35  * for interacting with Oracle databases
36  *
37  * Definitely works with versions 8 and 9 of Oracle.
38  *
39  * These methods overload the ones declared in DB_common.
40  *
41  * Be aware...  OCIError() only appears to return anything when given a
42  * statement, so functions return the generic DB_ERROR instead of more
43  * useful errors that have to do with feedback from the database.
44  *
45  * @category   Database
46  * @package    DB
47  * @author     James L. Pine <jlp@valinux.com>
48  * @author     Daniel Convissor <danielc@php.net>
49  * @copyright  1997-2007 The PHP Group
50  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
51  * @version    Release: 1.9.2
52  * @link       http://pear.php.net/package/DB
53  */
54 class DB_oci8 extends DB_common
55 {
56     // {{{ properties
57
58     /**
59      * The DB driver type (mysql, oci8, odbc, etc.)
60      * @var string
61      */
62     public $phptype = 'oci8';
63
64     /**
65      * The database syntax variant to be used (db2, access, etc.), if any
66      * @var string
67      */
68     public $dbsyntax = 'oci8';
69
70     /**
71      * The capabilities of this DB implementation
72      *
73      * The 'new_link' element contains the PHP version that first provided
74      * new_link support for this DBMS.  Contains false if it's unsupported.
75      *
76      * Meaning of the 'limit' element:
77      *   + 'emulate' = emulate with fetch row by number
78      *   + 'alter'   = alter the query
79      *   + false     = skip rows
80      *
81      * @var array
82      */
83     public $features = array(
84         'limit' => 'alter',
85         'new_link' => '5.0.0',
86         'numrows' => 'subquery',
87         'pconnect' => true,
88         'prepare' => true,
89         'ssl' => false,
90         'transactions' => true,
91     );
92
93     /**
94      * A mapping of native error codes to DB error codes
95      * @var array
96      */
97     public $errorcode_map = array(
98         1 => DB_ERROR_CONSTRAINT,
99         900 => DB_ERROR_SYNTAX,
100         904 => DB_ERROR_NOSUCHFIELD,
101         913 => DB_ERROR_VALUE_COUNT_ON_ROW,
102         921 => DB_ERROR_SYNTAX,
103         923 => DB_ERROR_SYNTAX,
104         942 => DB_ERROR_NOSUCHTABLE,
105         955 => DB_ERROR_ALREADY_EXISTS,
106         1400 => DB_ERROR_CONSTRAINT_NOT_NULL,
107         1401 => DB_ERROR_INVALID,
108         1407 => DB_ERROR_CONSTRAINT_NOT_NULL,
109         1418 => DB_ERROR_NOT_FOUND,
110         1476 => DB_ERROR_DIVZERO,
111         1722 => DB_ERROR_INVALID_NUMBER,
112         2289 => DB_ERROR_NOSUCHTABLE,
113         2291 => DB_ERROR_CONSTRAINT,
114         2292 => DB_ERROR_CONSTRAINT,
115         2449 => DB_ERROR_CONSTRAINT,
116         12899 => DB_ERROR_INVALID,
117     );
118
119     /**
120      * The raw database connection created by PHP
121      * @var resource
122      */
123     public $connection;
124
125     /**
126      * The DSN information for connecting to a database
127      * @var array
128      */
129     public $dsn = array();
130
131
132     /**
133      * Should data manipulation queries be committed automatically?
134      * @var bool
135      * @access private
136      */
137     public $autocommit = true;
138
139     /**
140      * Stores the $data passed to execute() in the oci8 driver
141      *
142      * Gets reset to array() when simpleQuery() is run.
143      *
144      * Needed in case user wants to call numRows() after prepare/execute
145      * was used.
146      *
147      * @var array
148      * @access private
149      */
150     public $_data = array();
151
152     /**
153      * The result or statement handle from the most recently executed query
154      * @var resource
155      */
156     public $last_stmt;
157
158     /**
159      * Is the given prepared statement a data manipulation query?
160      * @var array
161      * @access private
162      */
163     public $manip_query = array();
164
165     /**
166      * Store of prepared SQL queries.
167      * @var array
168      * @access private
169      */
170     public $_prepared_queries = array();
171
172
173     // }}}
174     // {{{ constructor
175
176     /**
177      * This constructor calls <kbd>parent::__construct()</kbd>
178      *
179      * @return void
180      */
181     public function __construct()
182     {
183         parent::__construct();
184     }
185
186     // }}}
187     // {{{ connect()
188
189     /**
190      * Connect to the database server, log in and open the database
191      *
192      * Don't call this method directly.  Use DB::connect() instead.
193      *
194      * If PHP is at version 5.0.0 or greater:
195      *   + Generally, oci_connect() or oci_pconnect() are used.
196      *   + But if the new_link DSN option is set to true, oci_new_connect()
197      *     is used.
198      *
199      * When using PHP version 4.x, OCILogon() or OCIPLogon() are used.
200      *
201      * PEAR DB's oci8 driver supports the following extra DSN options:
202      *   + charset       The character set to be used on the connection.
203      *                    Only used if PHP is at version 5.0.0 or greater
204      *                    and the Oracle server is at 9.2 or greater.
205      *                    Available since PEAR DB 1.7.0.
206      *   + new_link      If set to true, causes subsequent calls to
207      *                    connect() to return a new connection link
208      *                    instead of the existing one.  WARNING: this is
209      *                    not portable to other DBMS's.
210      *                    Available since PEAR DB 1.7.0.
211      *
212      * @param array $dsn the data source name
213      * @param bool $persistent should the connection be persistent?
214      *
215      * @return int|object
216      */
217     public function connect($dsn, $persistent = false)
218     {
219         if (!PEAR::loadExtension('oci8')) {
220             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
221         }
222
223         $this->dsn = $dsn;
224         if ($dsn['dbsyntax']) {
225             $this->dbsyntax = $dsn['dbsyntax'];
226         }
227
228         // Backwards compatibility with DB < 1.7.0
229         if (empty($dsn['database']) && !empty($dsn['hostspec'])) {
230             $db = $dsn['hostspec'];
231         } else {
232             $db = $dsn['database'];
233         }
234
235         if (function_exists('oci_connect')) {
236             if (isset($dsn['new_link'])
237                 && ($dsn['new_link'] == 'true' || $dsn['new_link'] === true)) {
238                 $connect_function = 'oci_new_connect';
239             } else {
240                 $connect_function = $persistent ? 'oci_pconnect'
241                     : 'oci_connect';
242             }
243             if (isset($this->dsn['port']) && $this->dsn['port']) {
244                 $db = '//' . $db . ':' . $this->dsn['port'];
245             }
246
247             $char = empty($dsn['charset']) ? null : $dsn['charset'];
248             $this->connection = @$connect_function(
249                 $dsn['username'],
250                 $dsn['password'],
251                 $db,
252                 $char
253             );
254             $error = OCIError();
255             if (!empty($error) && $error['code'] == 12541) {
256                 // Couldn't find TNS listener.  Try direct connection.
257                 $this->connection = @$connect_function(
258                     $dsn['username'],
259                     $dsn['password'],
260                     null,
261                     $char
262                 );
263             }
264         } else {
265             $connect_function = $persistent ? 'OCIPLogon' : 'OCILogon';
266             if ($db) {
267                 $this->connection = @$connect_function(
268                     $dsn['username'],
269                     $dsn['password'],
270                     $db
271                 );
272             } elseif ($dsn['username'] || $dsn['password']) {
273                 $this->connection = @$connect_function(
274                     $dsn['username'],
275                     $dsn['password']
276                 );
277             }
278         }
279
280         if (!$this->connection) {
281             $error = OCIError();
282             $error = (is_array($error)) ? $error['message'] : null;
283             return $this->raiseError(
284                 DB_ERROR_CONNECT_FAILED,
285                 null,
286                 null,
287                 null,
288                 $error
289             );
290         }
291         return DB_OK;
292     }
293
294     // }}}
295     // {{{ disconnect()
296
297     /**
298      * Disconnects from the database server
299      *
300      * @return bool  TRUE on success, FALSE on failure
301      */
302     public function disconnect()
303     {
304         if (function_exists('oci_close')) {
305             $ret = @oci_close($this->connection);
306         } else {
307             $ret = @OCILogOff($this->connection);
308         }
309         $this->connection = null;
310         return $ret;
311     }
312
313     // }}}
314     // {{{ simpleQuery()
315
316     /**
317      * Sends a query to the database server
318      *
319      * To determine how many rows of a result set get buffered using
320      * ocisetprefetch(), see the "result_buffering" option in setOptions().
321      * This option was added in Release 1.7.0.
322      *
323      * @param string  the SQL query string
324      *
325      * @return mixed  + a PHP result resrouce for successful SELECT queries
326      *                + the DB_OK constant for other successful queries
327      *                + a DB_Error object on failure
328      */
329     public function simpleQuery($query)
330     {
331         $this->_data = array();
332         $this->last_parameters = array();
333         $this->last_query = $query;
334         $query = $this->modifyQuery($query);
335         $result = @OCIParse($this->connection, $query);
336         if (!$result) {
337             return $this->oci8RaiseError();
338         }
339         if ($this->autocommit) {
340             $success = @OCIExecute($result, OCI_COMMIT_ON_SUCCESS);
341         } else {
342             $success = @OCIExecute($result, OCI_DEFAULT);
343         }
344         if (!$success) {
345             return $this->oci8RaiseError($result);
346         }
347         $this->last_stmt = $result;
348         if ($this->_checkManip($query)) {
349             return DB_OK;
350         } else {
351             @ocisetprefetch($result, $this->options['result_buffering']);
352             return $result;
353         }
354     }
355
356     // }}}
357     // {{{ nextResult()
358
359     /**
360      * Changes a query string for various DBMS specific reasons
361      *
362      * "SELECT 2+2" must be "SELECT 2+2 FROM dual" in Oracle.
363      *
364      * @param string $query the query string to modify
365      *
366      * @return string  the modified query string
367      *
368      * @access protected
369      */
370     public function modifyQuery($query)
371     {
372         if (preg_match('/^\s*SELECT/i', $query) &&
373             !preg_match('/\sFROM\s/i', $query)) {
374             $query .= ' FROM dual';
375         }
376         return $query;
377     }
378
379     // }}}
380     // {{{ fetchInto()
381
382     /**
383      * Produces a DB_Error object regarding the current problem
384      *
385      * @param int $errno if the error is being manually raised pass a
386      *                     DB_ERROR* constant here.  If this isn't passed
387      *                     the error information gathered from the DBMS.
388      *
389      * @return object  the DB_Error object
390      *
391      * @see DB_common::raiseError(),
392      *      DB_oci8::errorNative(), DB_oci8::errorCode()
393      */
394     public function oci8RaiseError($errno = null)
395     {
396         if ($errno === null) {
397             $error = @OCIError($this->connection);
398             return $this->raiseError(
399                 $this->errorCode($error['code']),
400                 null,
401                 null,
402                 null,
403                 $error['message']
404             );
405         } elseif (is_resource($errno)) {
406             $error = @OCIError($errno);
407             return $this->raiseError(
408                 $this->errorCode($error['code']),
409                 null,
410                 null,
411                 null,
412                 $error['message']
413             );
414         }
415         return $this->raiseError($this->errorCode($errno));
416     }
417
418     // }}}
419     // {{{ freeResult()
420
421     /**
422      * Move the internal oracle result pointer to the next available result
423      *
424      * @param a valid oci8 result resource
425      *
426      * @access public
427      *
428      * @return true if a result is available otherwise return false
429      */
430     public function nextResult($result)
431     {
432         return false;
433     }
434
435     /**
436      * Places a row from the result set into the given array
437      *
438      * Formating of the array and the data therein are configurable.
439      * See DB_result::fetchInto() for more information.
440      *
441      * This method is not meant to be called directly.  Use
442      * DB_result::fetchInto() instead.  It can't be declared "protected"
443      * because DB_result is a separate object.
444      *
445      * @param resource $result the query result resource
446      * @param array $arr the referenced array to put the data in
447      * @param int $fetchmode how the resulting array should be indexed
448      * @param int $rownum the row number to fetch (0 = first row)
449      *
450      * @return mixed  DB_OK on success, NULL when the end of a result set is
451      *                 reached or on failure
452      *
453      * @see DB_result::fetchInto()
454      */
455     public function fetchInto($result, &$arr, $fetchmode, $rownum = null)
456     {
457         if ($rownum !== null) {
458             return $this->raiseError(DB_ERROR_NOT_CAPABLE);
459         }
460         if ($fetchmode & DB_FETCHMODE_ASSOC) {
461             $moredata = @OCIFetchInto($result, $arr, OCI_ASSOC + OCI_RETURN_NULLS + OCI_RETURN_LOBS);
462             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE &&
463                 $moredata) {
464                 $arr = array_change_key_case($arr, CASE_LOWER);
465             }
466         } else {
467             $moredata = OCIFetchInto($result, $arr, OCI_RETURN_NULLS + OCI_RETURN_LOBS);
468         }
469         if (!$moredata) {
470             return null;
471         }
472         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
473             $this->_rtrimArrayValues($arr);
474         }
475         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
476             $this->_convertNullArrayValuesToEmpty($arr);
477         }
478         return DB_OK;
479     }
480
481     // }}}
482     // {{{ numRows()
483
484     /**
485      * Deletes the result set and frees the memory occupied by the result set
486      *
487      * This method is not meant to be called directly.  Use
488      * DB_result::free() instead.  It can't be declared "protected"
489      * because DB_result is a separate object.
490      *
491      * @param resource $result PHP's query result resource
492      *
493      * @return bool  TRUE on success, FALSE if $result is invalid
494      *
495      * @see DB_result::free()
496      */
497     public function freeResult($result)
498     {
499         return is_resource($result) ? OCIFreeStatement($result) : false;
500     }
501
502     // }}}
503     // {{{ numCols()
504
505     /**
506      * Frees the internal resources associated with a prepared query
507      *
508      * @param resource $stmt the prepared statement's resource
509      * @param bool $free_resource should the PHP resource be freed too?
510      *                                  Use false if you need to get data
511      *                                  from the result set later.
512      *
513      * @return bool  TRUE on success, FALSE if $result is invalid
514      *
515      * @see DB_oci8::prepare()
516      */
517     public function freePrepared($stmt, $free_resource = true)
518     {
519         if (!is_resource($stmt)) {
520             return false;
521         }
522         if ($free_resource) {
523             @ocifreestatement($stmt);
524         }
525         if (isset($this->prepare_types[(int)$stmt])) {
526             unset($this->prepare_types[(int)$stmt]);
527             unset($this->manip_query[(int)$stmt]);
528             unset($this->_prepared_queries[(int)$stmt]);
529         } else {
530             return false;
531         }
532         return true;
533     }
534
535     // }}}
536     // {{{ prepare()
537
538     /**
539      * Gets the number of rows in a result set
540      *
541      * Only works if the DB_PORTABILITY_NUMROWS portability option
542      * is turned on.
543      *
544      * This method is not meant to be called directly.  Use
545      * DB_result::numRows() instead.  It can't be declared "protected"
546      * because DB_result is a separate object.
547      *
548      * @param resource $result PHP's query result resource
549      *
550      * @return int|object
551      *
552      * @see DB_result::numRows(), DB_common::setOption()
553      */
554     public function numRows($result)
555     {
556         // emulate numRows for Oracle.  yuck.
557         if ($this->options['portability'] & DB_PORTABILITY_NUMROWS &&
558             $result === $this->last_stmt) {
559             $countquery = 'SELECT COUNT(*) FROM (' . $this->last_query . ')';
560             $save_query = $this->last_query;
561             $save_stmt = $this->last_stmt;
562
563             $count = $this->query($countquery);
564
565             // Restore the last query and statement.
566             $this->last_query = $save_query;
567             $this->last_stmt = $save_stmt;
568
569             if (DB::isError($count) ||
570                 DB::isError($row = $count->fetchRow(DB_FETCHMODE_ORDERED))) {
571                 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
572             }
573
574             return $row[0];
575         }
576         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
577     }
578
579     // }}}
580     // {{{ execute()
581
582     /**
583      * Gets the number of columns in a result set
584      *
585      * This method is not meant to be called directly.  Use
586      * DB_result::numCols() instead.  It can't be declared "protected"
587      * because DB_result is a separate object.
588      *
589      * @param resource $result PHP's query result resource
590      *
591      * @return int|object
592      *
593      * @see DB_result::numCols()
594      */
595     public function numCols($result)
596     {
597         $cols = @OCINumCols($result);
598         if (!$cols) {
599             return $this->oci8RaiseError($result);
600         }
601         return $cols;
602     }
603
604     // }}}
605     // {{{ autoCommit()
606
607     /**
608      * Enables or disables automatic commits
609      *
610      * @param bool $onoff true turns it on, false turns it off
611      *
612      * @return int  DB_OK on success.  A DB_Error object if the driver
613      *               doesn't support auto-committing transactions.
614      */
615     public function autoCommit($onoff = false)
616     {
617         $this->autocommit = (bool)$onoff;;
618         return DB_OK;
619     }
620
621     // }}}
622     // {{{ commit()
623
624     /**
625      * Commits the current transaction
626      *
627      * @return int|object
628      */
629     public function commit()
630     {
631         $result = @OCICommit($this->connection);
632         if (!$result) {
633             return $this->oci8RaiseError();
634         }
635         return DB_OK;
636     }
637
638     // }}}
639     // {{{ rollback()
640
641     /**
642      * Reverts the current transaction
643      *
644      * @return int|object
645      */
646     public function rollback()
647     {
648         $result = @OCIRollback($this->connection);
649         if (!$result) {
650             return $this->oci8RaiseError();
651         }
652         return DB_OK;
653     }
654
655     // }}}
656     // {{{ affectedRows()
657
658     /**
659      * Determines the number of rows affected by a data maniuplation query
660      *
661      * 0 is returned for queries that don't manipulate data.
662      *
663      * @return int|object
664      */
665     public function affectedRows()
666     {
667         if ($this->last_stmt === false) {
668             return $this->oci8RaiseError();
669         }
670         $result = @OCIRowCount($this->last_stmt);
671         if ($result === false) {
672             return $this->oci8RaiseError($this->last_stmt);
673         }
674         return $result;
675     }
676
677     // }}}
678     // {{{ modifyQuery()
679
680     /**
681      * Adds LIMIT clauses to a query string according to current DBMS standards
682      *
683      * @param string $query the query to modify
684      * @param int $from the row to start to fetching (0 = the first row)
685      * @param int $count the numbers of rows to fetch
686      * @param mixed $params array, string or numeric data to be used in
687      *                         execution of the statement.  Quantity of items
688      *                         passed must match quantity of placeholders in
689      *                         query:  meaning 1 placeholder for non-array
690      *                         parameters or 1 placeholder per array element.
691      *
692      * @return string  the query string with LIMIT clauses added
693      *
694      * @access protected
695      */
696     public function modifyLimitQuery($query, $from, $count, $params = array())
697     {
698         // Let Oracle return the name of the columns instead of
699         // coding a "home" SQL parser
700
701         if (count($params)) {
702             $result = $this->prepare("SELECT * FROM ($query) "
703                 . 'WHERE NULL = NULL');
704             $tmp = $this->execute($result, $params);
705         } else {
706             $q_fields = "SELECT * FROM ($query) WHERE NULL = NULL";
707
708             if (!$result = @OCIParse($this->connection, $q_fields)) {
709                 $this->last_query = $q_fields;
710                 return $this->oci8RaiseError();
711             }
712             if (!@OCIExecute($result, OCI_DEFAULT)) {
713                 $this->last_query = $q_fields;
714                 return $this->oci8RaiseError($result);
715             }
716         }
717
718         $ncols = OCINumCols($result);
719         $cols = array();
720         for ($i = 1; $i <= $ncols; $i++) {
721             $cols[] = '"' . OCIColumnName($result, $i) . '"';
722         }
723         $fields = implode(', ', $cols);
724         // XXX Test that (tip by John Lim)
725         //if (preg_match('/^\s*SELECT\s+/is', $query, $match)) {
726         //    // Introduce the FIRST_ROWS Oracle query optimizer
727         //    $query = substr($query, strlen($match[0]), strlen($query));
728         //    $query = "SELECT /* +FIRST_ROWS */ " . $query;
729         //}
730
731         // Construct the query
732         // more at: http://marc.theaimsgroup.com/?l=php-db&m=99831958101212&w=2
733         // Perhaps this could be optimized with the use of Unions
734         $query = "SELECT $fields FROM" .
735             "  (SELECT rownum as linenum, $fields FROM" .
736             "      ($query)" .
737             '  WHERE rownum <= ' . ($from + $count) .
738             ') WHERE linenum >= ' . ++$from;
739         return $query;
740     }
741
742     // }}}
743     // {{{ modifyLimitQuery()
744
745     /**
746      * Prepares a query for multiple execution with execute().
747      *
748      * With oci8, this is emulated.
749      *
750      * prepare() requires a generic query as string like <code>
751      *    INSERT INTO numbers VALUES (?, ?, ?)
752      * </code>.  The <kbd>?</kbd> characters are placeholders.
753      *
754      * Three types of placeholders can be used:
755      *   + <kbd>?</kbd>  a quoted scalar value, i.e. strings, integers
756      *   + <kbd>!</kbd>  value is inserted 'as is'
757      *   + <kbd>&</kbd>  requires a file name.  The file's contents get
758      *                     inserted into the query (i.e. saving binary
759      *                     data in a db)
760      *
761      * Use backslashes to escape placeholder characters if you don't want
762      * them to be interpreted as placeholders.  Example: <code>
763      *    "UPDATE foo SET col=? WHERE col='over \& under'"
764      * </code>
765      *
766      * @param string $query the query to be prepared
767      *
768      * @return mixed  DB statement resource on success. DB_Error on failure.
769      *
770      * @see DB_oci8::execute()
771      */
772     public function prepare($query)
773     {
774         $tokens = preg_split(
775             '/((?<!\\\)[&?!])/',
776             $query,
777             -1,
778             PREG_SPLIT_DELIM_CAPTURE
779         );
780         $binds = count($tokens) - 1;
781         $token = 0;
782         $types = array();
783         $newquery = '';
784
785         foreach ($tokens as $key => $val) {
786             switch ($val) {
787                 case '?':
788                     $types[$token++] = DB_PARAM_SCALAR;
789                     unset($tokens[$key]);
790                     break;
791                 case '&':
792                     $types[$token++] = DB_PARAM_OPAQUE;
793                     unset($tokens[$key]);
794                     break;
795                 case '!':
796                     $types[$token++] = DB_PARAM_MISC;
797                     unset($tokens[$key]);
798                     break;
799                 default:
800                     $tokens[$key] = preg_replace('/\\\([&?!])/', "\\1", $val);
801                     if ($key != $binds) {
802                         $newquery .= $tokens[$key] . ':bind' . $token;
803                     } else {
804                         $newquery .= $tokens[$key];
805                     }
806             }
807         }
808
809         $this->last_query = $query;
810         $newquery = $this->modifyQuery($newquery);
811         if (!$stmt = @OCIParse($this->connection, $newquery)) {
812             return $this->oci8RaiseError();
813         }
814         $this->prepare_types[(int)$stmt] = $types;
815         $this->manip_query[(int)$stmt] = DB::isManip($query);
816         $this->_prepared_queries[(int)$stmt] = $newquery;
817         return $stmt;
818     }
819
820     // }}}
821     // {{{ nextId()
822
823     /**
824      * Executes a DB statement prepared with prepare().
825      *
826      * To determine how many rows of a result set get buffered using
827      * ocisetprefetch(), see the "result_buffering" option in setOptions().
828      * This option was added in Release 1.7.0.
829      *
830      * @param resource $stmt a DB statement resource returned from prepare()
831      * @param mixed $data array, string or numeric data to be used in
832      *                      execution of the statement.  Quantity of items
833      *                      passed must match quantity of placeholders in
834      *                      query:  meaning 1 for non-array items or the
835      *                      quantity of elements in the array.
836      *
837      * @return mixed  returns an oic8 result resource for successful SELECT
838      *                queries, DB_OK for other successful queries.
839      *                A DB error object is returned on failure.
840      *
841      * @see DB_oci8::prepare()
842      */
843     public function &execute($stmt, $data = array())
844     {
845         $data = (array)$data;
846         $this->last_parameters = $data;
847         $this->last_query = $this->_prepared_queries[(int)$stmt];
848         $this->_data = $data;
849
850         $types = $this->prepare_types[(int)$stmt];
851         if (count($types) != count($data)) {
852             $tmp = $this->raiseError(DB_ERROR_MISMATCH);
853             return $tmp;
854         }
855
856         $i = 0;
857         foreach ($data as $key => $value) {
858             if ($types[$i] == DB_PARAM_MISC) {
859                 /*
860                  * Oracle doesn't seem to have the ability to pass a
861                  * parameter along unchanged, so strip off quotes from start
862                  * and end, plus turn two single quotes to one single quote,
863                  * in order to avoid the quotes getting escaped by
864                  * Oracle and ending up in the database.
865                  */
866                 $data[$key] = preg_replace("/^'(.*)'$/", "\\1", $data[$key]);
867                 $data[$key] = str_replace("''", "'", $data[$key]);
868             } elseif ($types[$i] == DB_PARAM_OPAQUE) {
869                 $fp = @fopen($data[$key], 'rb');
870                 if (!$fp) {
871                     $tmp = $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
872                     return $tmp;
873                 }
874                 $data[$key] = fread($fp, filesize($data[$key]));
875                 fclose($fp);
876             } elseif ($types[$i] == DB_PARAM_SCALAR) {
877                 // Floats have to be converted to a locale-neutral
878                 // representation.
879                 if (is_float($data[$key])) {
880                     $data[$key] = $this->quoteFloat($data[$key]);
881                 }
882             }
883             if (!@OCIBindByName($stmt, ':bind' . $i, $data[$key], -1)) {
884                 $tmp = $this->oci8RaiseError($stmt);
885                 return $tmp;
886             }
887             $this->last_query = preg_replace(
888                 "/:bind$i(?!\d)/",
889                 $this->quoteSmart($data[$key]),
890                 $this->last_query,
891                 1
892             );
893             $i++;
894         }
895         if ($this->autocommit) {
896             $success = @OCIExecute($stmt, OCI_COMMIT_ON_SUCCESS);
897         } else {
898             $success = @OCIExecute($stmt, OCI_DEFAULT);
899         }
900         if (!$success) {
901             $tmp = $this->oci8RaiseError($stmt);
902             return $tmp;
903         }
904         $this->last_stmt = $stmt;
905         if ($this->manip_query[(int)$stmt] || $this->_next_query_manip) {
906             $this->_last_query_manip = true;
907             $this->_next_query_manip = false;
908             $tmp = DB_OK;
909         } else {
910             $this->_last_query_manip = false;
911             @ocisetprefetch($stmt, $this->options['result_buffering']);
912             $tmp = new DB_result($this, $stmt);
913         }
914         return $tmp;
915     }
916
917     /**
918      * Formats a float value for use within a query in a locale-independent
919      * manner.
920      *
921      * @param float the float value to be quoted.
922      * @return string the quoted string.
923      * @see DB_common::quoteSmart()
924      * @since Method available since release 1.7.8.
925      */
926     public function quoteFloat($float)
927     {
928         return $this->escapeSimple(str_replace(',', '.', strval(floatval($float))));
929     }
930
931     // }}}
932     // {{{ dropSequence()
933
934     /**
935      * Returns the next free id in a sequence
936      *
937      * @param string $seq_name name of the sequence
938      * @param boolean $ondemand when true, the seqence is automatically
939      *                            created if it does not exist
940      *
941      * @return int|object
942      *               A DB_Error object on failure.
943      *
944      * @see DB_common::nextID(), DB_common::getSequenceName(),
945      *      DB_oci8::createSequence(), DB_oci8::dropSequence()
946      */
947     public function nextId($seq_name, $ondemand = true)
948     {
949         $seqname = $this->getSequenceName($seq_name);
950         $repeat = 0;
951         do {
952             $this->expectError(DB_ERROR_NOSUCHTABLE);
953             $result = $this->query("SELECT ${seqname}.nextval FROM dual");
954             $this->popExpect();
955             if ($ondemand && DB::isError($result) &&
956                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
957                 $repeat = 1;
958                 $result = $this->createSequence($seq_name);
959                 if (DB::isError($result)) {
960                     return $this->raiseError($result);
961                 }
962             } else {
963                 $repeat = 0;
964             }
965         } while ($repeat);
966         if (DB::isError($result)) {
967             return $this->raiseError($result);
968         }
969         $arr = $result->fetchRow(DB_FETCHMODE_ORDERED);
970         return $arr[0];
971     }
972
973     // }}}
974     // {{{ oci8RaiseError()
975
976     /**
977      * Creates a new sequence
978      *
979      * @param string $seq_name name of the new sequence
980      *
981      * @return int  DB_OK on success.  A DB_Error object on failure.
982      *
983      * @see DB_common::createSequence(), DB_common::getSequenceName(),
984      *      DB_oci8::nextID(), DB_oci8::dropSequence()
985      */
986     public function createSequence($seq_name)
987     {
988         return $this->query('CREATE SEQUENCE '
989             . $this->getSequenceName($seq_name));
990     }
991
992     // }}}
993     // {{{ errorNative()
994
995     /**
996      * Deletes a sequence
997      *
998      * @param string $seq_name name of the sequence to be deleted
999      *
1000      * @return int  DB_OK on success.  A DB_Error object on failure.
1001      *
1002      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
1003      *      DB_oci8::nextID(), DB_oci8::createSequence()
1004      */
1005     public function dropSequence($seq_name)
1006     {
1007         return $this->query('DROP SEQUENCE '
1008             . $this->getSequenceName($seq_name));
1009     }
1010
1011     // }}}
1012     // {{{ tableInfo()
1013
1014     /**
1015      * Gets the DBMS' native error code produced by the last query
1016      *
1017      * @return int  the DBMS' error code.  FALSE if the code could not be
1018      *               determined
1019      */
1020     public function errorNative()
1021     {
1022         if (is_resource($this->last_stmt)) {
1023             $error = @OCIError($this->last_stmt);
1024         } else {
1025             $error = @OCIError($this->connection);
1026         }
1027         if (is_array($error)) {
1028             return $error['code'];
1029         }
1030         return false;
1031     }
1032
1033     // }}}
1034     // {{{ getSpecialQuery()
1035
1036     /**
1037      * Returns information about a table or a result set
1038      *
1039      * NOTE: only supports 'table' and 'flags' if <var>$result</var>
1040      * is a table name.
1041      *
1042      * NOTE: flags won't contain index information.
1043      *
1044      * @param object|string $result DB_result object from a query or a
1045      *                                 string containing the name of a table.
1046      *                                 While this also accepts a query result
1047      *                                 resource identifier, this behavior is
1048      *                                 deprecated.
1049      * @param int $mode a valid tableInfo mode
1050      *
1051      * @return array|object
1052      *                 A DB_Error object on failure.
1053      *
1054      * @see DB_common::tableInfo()
1055      */
1056     public function tableInfo($result, $mode = null)
1057     {
1058         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
1059             $case_func = 'strtolower';
1060         } else {
1061             $case_func = 'strval';
1062         }
1063
1064         $res = array();
1065
1066         if (is_string($result)) {
1067             /*
1068              * Probably received a table name.
1069              * Create a result resource identifier.
1070              */
1071             $result = strtoupper($result);
1072             $q_fields = 'SELECT column_name, data_type, data_length, '
1073                 . 'nullable '
1074                 . 'FROM user_tab_columns '
1075                 . "WHERE table_name='$result' ORDER BY column_id";
1076
1077             $this->last_query = $q_fields;
1078
1079             if (!$stmt = @OCIParse($this->connection, $q_fields)) {
1080                 return $this->oci8RaiseError(DB_ERROR_NEED_MORE_DATA);
1081             }
1082             if (!@OCIExecute($stmt, OCI_DEFAULT)) {
1083                 return $this->oci8RaiseError($stmt);
1084             }
1085
1086             $i = 0;
1087             while (@OCIFetch($stmt)) {
1088                 $res[$i] = array(
1089                     'table' => $case_func($result),
1090                     'name' => $case_func(@OCIResult($stmt, 1)),
1091                     'type' => @OCIResult($stmt, 2),
1092                     'len' => @OCIResult($stmt, 3),
1093                     'flags' => (@OCIResult($stmt, 4) == 'N') ? 'not_null' : '',
1094                 );
1095                 if ($mode & DB_TABLEINFO_ORDER) {
1096                     $res['order'][$res[$i]['name']] = $i;
1097                 }
1098                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
1099                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1100                 }
1101                 $i++;
1102             }
1103
1104             if ($mode) {
1105                 $res['num_fields'] = $i;
1106             }
1107             @OCIFreeStatement($stmt);
1108         } else {
1109             if (isset($result->result)) {
1110                 /*
1111                  * Probably received a result object.
1112                  * Extract the result resource identifier.
1113                  */
1114                 $result = $result->result;
1115             }
1116
1117             $res = array();
1118
1119             if ($result === $this->last_stmt) {
1120                 $count = @OCINumCols($result);
1121                 if ($mode) {
1122                     $res['num_fields'] = $count;
1123                 }
1124                 for ($i = 0; $i < $count; $i++) {
1125                     $res[$i] = array(
1126                         'table' => '',
1127                         'name' => $case_func(@OCIColumnName($result, $i + 1)),
1128                         'type' => @OCIColumnType($result, $i + 1),
1129                         'len' => @OCIColumnSize($result, $i + 1),
1130                         'flags' => '',
1131                     );
1132                     if ($mode & DB_TABLEINFO_ORDER) {
1133                         $res['order'][$res[$i]['name']] = $i;
1134                     }
1135                     if ($mode & DB_TABLEINFO_ORDERTABLE) {
1136                         $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1137                     }
1138                 }
1139             } else {
1140                 return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1141             }
1142         }
1143         return $res;
1144     }
1145
1146     // }}}
1147     // {{{ quoteFloat()
1148
1149     /**
1150      * Obtains the query string needed for listing a given type of objects
1151      *
1152      * @param string $type the kind of objects you want to retrieve
1153      *
1154      * @return string  the SQL query string or null if the driver doesn't
1155      *                  support the object type requested
1156      *
1157      * @access protected
1158      * @see DB_common::getListOf()
1159      */
1160     public function getSpecialQuery($type)
1161     {
1162         switch ($type) {
1163             case 'tables':
1164                 return 'SELECT table_name FROM user_tables';
1165             case 'synonyms':
1166                 return 'SELECT synonym_name FROM user_synonyms';
1167             case 'views':
1168                 return 'SELECT view_name FROM user_views';
1169             default:
1170                 return null;
1171         }
1172     }
1173
1174     // }}}
1175 }
1176
1177 /*
1178  * Local variables:
1179  * tab-width: 4
1180  * c-basic-offset: 4
1181  * End:
1182  */