Subversion Repositories Applications.papyrus

Rev

Rev 443 | Go to most recent revision | Details | Last modification | View Log | RSS feed

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