Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
// +----------------------------------------------------------------------+
3
// | PHP Version 4                                                        |
4
// +----------------------------------------------------------------------+
5
// | Copyright (c) 1998-2004 Manuel Lemos, Tomas V.V.Cox,                 |
6
// | Stig. S. Bakken, Lukas Smith                                         |
7
// | All rights reserved.                                                 |
8
// +----------------------------------------------------------------------+
9
// | MDB is a merge of PEAR DB and Metabases that provides a unified DB   |
10
// | API as well as database abstraction for PHP applications.            |
11
// | This LICENSE is in the BSD license style.                            |
12
// |                                                                      |
13
// | Redistribution and use in source and binary forms, with or without   |
14
// | modification, are permitted provided that the following conditions   |
15
// | are met:                                                             |
16
// |                                                                      |
17
// | Redistributions of source code must retain the above copyright       |
18
// | notice, this list of conditions and the following disclaimer.        |
19
// |                                                                      |
20
// | Redistributions in binary form must reproduce the above copyright    |
21
// | notice, this list of conditions and the following disclaimer in the  |
22
// | documentation and/or other materials provided with the distribution. |
23
// |                                                                      |
24
// | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
25
// | Lukas Smith nor the names of his contributors may be used to endorse |
26
// | or promote products derived from this software without specific prior|
27
// | written permission.                                                  |
28
// |                                                                      |
29
// | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
30
// | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
31
// | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
32
// | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
33
// | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
34
// | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
35
// | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
36
// |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
37
// | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
38
// | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
39
// | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
40
// | POSSIBILITY OF SUCH DAMAGE.                                          |
41
// +----------------------------------------------------------------------+
42
// | Author: Lukas Smith <smith@backendmedia.com>                         |
43
// +----------------------------------------------------------------------+
44
 
45
// $Id: oci8.php,v 1.21.4.24 2004/03/31 16:02:40 lsmith Exp $
46
 
47
require_once('MDB/Common.php');
48
 
49
/**
50
 * MDB OCI8 driver
51
 *
52
 * Notes:
53
 * - when fetching in associative mode all keys are uppercased which is not the
54
 *   intenteded behavior. Due to BC issues this will not be changed in MDB 1.x
55
 *   however.
56
 *
57
 * - createDatabase and dropDatabase are not supported
58
 *
59
 * - Text fields with unspecified length limit are created as VARCHAR with an
60
 *   optional limit that may not exceed 4000 characters.
61
 *
62
 * - date fields are emulated with date fields with time set to 00:00:00.
63
     time fields are emulated with date fields with the day set to 0001-01-01.
64
 *
65
 * - The numRows method is emulated by fetching all the rows into memory.
66
 *   Avoid using it if for queries with large result sets.
67
 *
68
 * - Oracle does not provide direct support for returning result sets restricted
69
     to a given range. Such support is emulated in the MDB oci8 driver.
70
 *
71
 * - Storing data in large object fields has to be done in two phases: first the
72
     fields are initialized using a INSERT or UPDATE query that sets the fields
73
     to an empty value, then the data values are uploaded to the large objects
74
     returned by reference from the executed queries.
75
 *   Besides the fact that only INSERT and UPDATE queries are supported to
76
     upload large object data values, only UPDATE queries that affect only one
77
     row will set the large object fields correctly.
78
 *
79
 * - The driver alterTable method does not implement table or column renaming.
80
 *
81
 * @package MDB
82
 * @category Database
83
 * @author Lukas Smith <smith@backendmedia.com>
84
 */
85
class MDB_oci8 extends MDB_Common {
86
    var $connection = 0;
87
    var $connected_user;
88
    var $connected_password;
89
 
90
    var $escape_quotes = "'";
91
 
92
    var $uncommitedqueries = 0;
93
 
94
    var $results = array();
95
    var $current_row = array();
96
    var $columns = array();
97
    var $rows = array();
98
    var $limits = array();
99
    var $row_buffer = array();
100
    var $highest_fetched_row = array();
101
 
102
    // {{{ constructor
103
 
104
    /**
105
     * Constructor
106
     */
107
    function MDB_oci8()
108
    {
109
        $this->MDB_Common();
110
        $this->phptype = 'oci8';
111
        $this->dbsyntax = 'oci8';
112
 
113
        $this->supported['Sequences'] = 1;
114
        $this->supported['Indexes'] = 1;
115
        $this->supported['SummaryFunctions'] = 1;
116
        $this->supported['OrderByText'] = 1;
117
        $this->supported['CurrId'] = 1;
118
        $this->supported["AffectedRows"]= 1;
119
        $this->supported['Transactions'] = 1;
120
        $this->supported['SelectRowRanges'] = 1;
121
        $this->supported['LOBs'] = 1;
122
        $this->supported['Replace'] = 1;
123
        $this->supported['SubSelects'] = 1;
124
 
125
        $this->options['DBAUser'] = FALSE;
126
        $this->options['DBAPassword'] = FALSE;
127
 
128
        $this->errorcode_map = array(
129
            1 => MDB_ERROR_CONSTRAINT,
130
            900 => MDB_ERROR_SYNTAX,
131
            904 => MDB_ERROR_NOSUCHFIELD,
132
            921 => MDB_ERROR_SYNTAX,
133
            923 => MDB_ERROR_SYNTAX,
134
            942 => MDB_ERROR_NOSUCHTABLE,
135
            955 => MDB_ERROR_ALREADY_EXISTS,
136
            1400 => MDB_ERROR_CONSTRAINT_NOT_NULL,
137
            1407 => MDB_ERROR_CONSTRAINT_NOT_NULL,
138
            1476 => MDB_ERROR_DIVZERO,
139
            1722 => MDB_ERROR_INVALID_NUMBER,
140
            2289 => MDB_ERROR_NOSUCHTABLE,
141
            2291 => MDB_ERROR_CONSTRAINT,
142
            2449 => MDB_ERROR_CONSTRAINT,
143
        );
144
    }
145
 
146
    // }}}
147
    // {{{ errorNative()
148
 
149
    /**
150
     * Get the native error code of the last error (if any) that
151
     * occured on the current connection.
152
     *
153
     * @access public
154
     * @return int native oci8 error code
155
     */
156
    function errorNative($statement = NULL)
157
    {
158
        if (is_resource($statement)) {
159
            $error = @OCIError($statement);
160
        } else {
161
            $error = @OCIError($this->connection);
162
        }
163
        if (is_array($error)) {
164
            return($error['code']);
165
        }
166
        return(FALSE);
167
    }
168
 
169
    // }}}
170
    // {{{ oci8RaiseError()
171
 
172
    /**
173
     * This method is used to communicate an error and invoke error
174
     * callbacks etc.  Basically a wrapper for MDB::raiseError
175
     * that checks for native error msgs.
176
     *
177
     * @param integer $errno error code
178
     * @param string  $message userinfo message
179
     * @return object a PEAR error object
180
     * @access public
181
     * @see PEAR_Error
182
     */
183
    function oci8RaiseError($errno = NULL, $message = NULL)
184
    {
185
        if ($errno === NULL) {
186
            if ($this->connection) {
187
                $error = @OCIError($this->connection);
188
            } else {
189
                $error = @OCIError();
190
            }
191
            return($this->raiseError($this->errorCode($error['code']),
192
                NULL, NULL, $message, $error['message']));
193
        } elseif (is_resource($errno)) {
194
            $error = @OCIError($errno);
195
            return($this->raiseError($this->errorCode($error['code']),
196
                NULL, NULL, $message, $error['message']));
197
        }
198
        return($this->raiseError($this->errorCode($errno), NULL, NULL, $message));
199
    }
200
 
201
    // }}}
202
    // {{{ autoCommit()
203
 
204
    /**
205
     * Define whether database changes done on the database be automatically
206
     * committed. This function may also implicitly start or end a transaction.
207
     *
208
     * @param boolean $auto_commit flag that indicates whether the database
209
     *                                 changes should be committed right after
210
     *                                 executing every query statement. If this
211
     *                                 argument is 0 a transaction implicitly
212
     *                                 started. Otherwise, if a transaction is
213
     *                                 in progress it is ended by committing any
214
     *                                 database changes that were pending.
215
     * @access public
216
     * @return mixed MDB_OK on success, a MDB error on failure
217
     */
218
    function autoCommit($auto_commit)
219
    {
220
        $this->debug('AutoCommit: '.($auto_commit ? 'On' : 'Off'));
221
        if ($this->auto_commit == $auto_commit) {
222
            return(MDB_OK);
223
        }
224
        if ($this->connection && $auto_commit && MDB::isError($commit = $this->commit())) {
225
            return($commit);
226
        }
227
        $this->auto_commit = $auto_commit;
228
        $this->in_transaction = !$auto_commit;
229
        return(MDB_OK);
230
    }
231
 
232
    // }}}
233
    // {{{ commit()
234
 
235
    /**
236
     * Commit the database changes done during a transaction that is in
237
     * progress. This function may only be called when auto-committing is
238
     * disabled, otherwise it will fail. Therefore, a new transaction is
239
     * implicitly started after committing the pending changes.
240
     *
241
     * @access public
242
     * @return mixed MDB_OK on success, a MDB error on failure
243
     */
244
    function commit()
245
    {
246
        $this->debug('Commit Transaction');
247
        if (!isset($this->supported['Transactions'])) {
248
            return($this->raiseError(MDB_ERROR_UNSUPPORTED, NULL, NULL,
249
                'Commit transactions: transactions are not in use'));
250
        }
251
        if ($this->auto_commit) {
252
            return($this->raiseError(MDB_ERROR, NULL, NULL,
253
            'Commit transactions: transaction changes are being auto commited'));
254
        }
255
        if ($this->uncommitedqueries) {
256
            if (!@OCICommit($this->connection)) {
257
                return($this->oci8RaiseError(NULL,
258
                    'Commit transactions: Could not commit pending transaction: '."$message. Error: ".$error['code'].' ('.$error['message'].')'));
259
            }
260
            $this->uncommitedqueries = 0;
261
        }
262
        return(MDB_OK);
263
    }
264
 
265
    // }}}
266
    // {{{ rollback()
267
 
268
    /**
269
     * Cancel any database changes done during a transaction that is in
270
     * progress. This function may only be called when auto-committing is
271
     * disabled, otherwise it will fail. Therefore, a new transaction is
272
     * implicitly started after canceling the pending changes.
273
     *
274
     * @access public
275
     * @return mixed MDB_OK on success, a MDB error on failure
276
     */
277
    function rollback()
278
    {
279
        $this->debug('Rollback Transaction');
280
        if ($this->auto_commit) {
281
            return($this->raiseError(MDB_ERROR, NULL, NULL,
282
                'Rollback transactions: transactions can not be rolled back when changes are auto commited'));
283
        }
284
        if ($this->uncommitedqueries) {
285
            if (!@OCIRollback($this->connection)) {
286
                return($this->oci8RaiseError(NULL,
287
                    'Rollback transaction: Could not rollback pending transaction'));
288
            }
289
            $this->uncommitedqueries = 0;
290
        }
291
        return(MDB_OK);
292
    }
293
 
294
    // }}}
295
    // {{{ connect()
296
 
297
    /**
298
     * Connect to the database
299
     *
300
     * @return TRUE on success, MDB_Error on failure
301
     */
302
    function connect($user = NULL , $password = NULL, $persistent = NULL)
303
    {
304
        if($user === NULL) {
305
            $user = $this->user;
306
        }
307
        if($password === NULL) {
308
            $password = $this->password;
309
        }
310
        if($persistent === NULL) {
311
            $persistent = $this->getOption('persistent');
312
        }
313
        if (isset($this->host)) {
314
            $sid = $this->host;
315
        } else {
316
            $sid = getenv('ORACLE_SID');
317
        }
318
        if(!strcmp($sid, '')) {
319
            return($this->raiseError(MDB_ERROR, NULL, NULL,
320
                'Connect: it was not specified a valid Oracle Service IDentifier (SID)'));
321
        }
322
        if($this->connection != 0) {
323
            if (!strcmp($this->connected_user, $user)
324
                && !strcmp($this->connected_password, $password)
325
                && $this->opened_persistent == $persistent)
326
            {
327
                return(MDB_OK);
328
            }
329
            $this->_close();
330
        }
331
 
332
        if(!PEAR::loadExtension($this->phptype)) {
333
            return(PEAR::raiseError(NULL, MDB_ERROR_NOT_FOUND,
334
                NULL, NULL, 'extension '.$this->phptype.' is not compiled into PHP',
335
                'MDB_Error', TRUE));
336
        }
337
 
338
        if (isset($this->options['HOME'])) {
339
            putenv('ORACLE_HOME='.$this->options['HOME']);
340
        }
341
        putenv('ORACLE_SID='.$sid);
342
        $function = ($persistent ? 'OCIPLogon' : 'OCINLogon');
343
        if (!function_exists($function)) {
344
            return($this->raiseError(MDB_ERROR, NULL, NULL,
345
                'Connect: Oracle OCI API support is not available in this PHP configuration'));
346
        }
347
        if (!($this->connection = @$function($user, $password, $sid))) {
348
            return($this->oci8RaiseError(NULL,
349
                'Connect: Could not connect to Oracle server'));
350
        }
351
        if (MDB::isError($doquery = $this->_doQuery("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'"))) {
352
            $this->_close();
353
            return($doquery);
354
        }
355
        if (MDB::isError($doquery = $this->_doQuery("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='. '"))) {
356
            $this->_close();
357
            return($doquery);
358
        }
359
 
360
        $this->connected_user = $user;
361
        $this->connected_password = $password;
362
        $this->opened_persistent = $persistent;
363
        return(MDB_OK);
364
    }
365
 
366
    // }}}
367
    // {{{ _close()
368
 
369
    /**
370
     * all the RDBMS specific things needed close a DB connection
371
     *
372
     * @access private
373
     */
374
    function _close()
375
    {
376
        if ($this->connection != 0) {
377
            @OCILogOff($this->connection);
378
            $this->connection = 0;
379
            $this->affected_rows = -1;
380
            $this->uncommitedqueries = 0;
381
        }
382
    }
383
 
384
    // }}}
385
    // {{{ _doQuery()
386
 
387
    /**
388
     * all the RDBMS specific things needed close a DB connection
389
     *
390
     * @access private
391
     */
392
    function _doQuery($query, $first = 0, $limit = 0, $prepared_query = 0)
393
    {
394
        $lobs = 0;
395
        $success = MDB_OK;
396
        $result = 0;
397
        $descriptors = array();
398
        if ($prepared_query) {
399
            $columns = '';
400
            $variables = '';
401
            for(reset($this->clobs[$prepared_query]), $clob = 0;
402
                $clob < count($this->clobs[$prepared_query]);
403
                $clob++, next($this->clobs[$prepared_query]))
404
            {
405
                $position = key($this->clobs[$prepared_query]);
406
                if (gettype($descriptors[$position] = @OCINewDescriptor($this->connection, OCI_D_LOB)) != 'object') {
407
                    $success = $this->raiseError(MDB_ERROR, NULL, NULL,
408
                        'Do query: Could not create descriptor for clob parameter');
409
                    break;
410
                }
411
                $columns.= ($lobs == 0 ? ' RETURNING ' : ',').$this->prepared_queries[$prepared_query-1]['Fields'][$position-1];
412
                $variables.= ($lobs == 0 ? ' INTO ' : ',').':clob'.$position;
413
                $lobs++;
414
            }
415
            if (!MDB::isError($success)) {
416
                for(reset($this->blobs[$prepared_query]), $blob = 0;$blob < count($this->blobs[$prepared_query]);$blob++, next($this->blobs[$prepared_query])) {
417
                    $position = key($this->blobs[$prepared_query]);
418
                    if (gettype($descriptors[$position] = @OCINewDescriptor($this->connection, OCI_D_LOB)) != 'object') {
419
                        $success = $this->raiseError(MDB_ERROR, NULL, NULL,
420
                            'Do query: Could not create descriptor for blob parameter');
421
                        break;
422
                    }
423
                    $columns.= ($lobs == 0 ? ' RETURNING ' : ',').$this->prepared_queries[$prepared_query-1]['Fields'][$position-1];
424
                    $variables.= ($lobs == 0 ? ' INTO ' : ',').':blob'.$position;
425
                    $lobs++;
426
                }
427
                $query.= $columns.$variables;
428
            }
429
        }
430
        if (!MDB::isError($success)) {
431
            if (($statement = @OCIParse($this->connection, $query))) {
432
                if ($lobs) {
433
                    for(reset($this->clobs[$prepared_query]), $clob = 0;$clob < count($this->clobs[$prepared_query]);$clob++, next($this->clobs[$prepared_query])) {
434
                        $position = key($this->clobs[$prepared_query]);
435
                        if (!@OCIBindByName($statement, ':clob'.$position, $descriptors[$position], -1, OCI_B_CLOB)) {
436
                            $success = $this->oci8RaiseError(NULL,
437
                                'Do query: Could not bind clob upload descriptor');
438
                            break;
439
                        }
440
                    }
441
                    if (!MDB::isError($success)) {
442
                        for(reset($this->blobs[$prepared_query]), $blob = 0;
443
                            $blob < count($this->blobs[$prepared_query]);
444
                            $blob++, next($this->blobs[$prepared_query]))
445
                        {
446
                            $position = key($this->blobs[$prepared_query]);
447
                            if (!@OCIBindByName($statement, ':blob'.$position, $descriptors[$position], -1, OCI_B_BLOB)) {
448
                                $success = $this->oci8RaiseError(NULL,
449
                                    'Do query: Could not bind blob upload descriptor');
450
                                break;
451
                            }
452
                        }
453
                    }
454
                }
455
                if (!MDB::isError($success)) {
456
                    if (($result = @OCIExecute($statement, ($lobs == 0 && $this->auto_commit) ? OCI_COMMIT_ON_SUCCESS : OCI_DEFAULT))) {
457
                        if ($lobs) {
458
                            for(reset($this->clobs[$prepared_query]), $clob = 0;
459
                                $clob < count($this->clobs[$prepared_query]);
460
                                $clob++, next($this->clobs[$prepared_query]))
461
                            {
462
                                $position = key($this->clobs[$prepared_query]);
463
                                $clob_stream = $this->prepared_queries[$prepared_query-1]['Values'][$position-1];
464
                                for($value = '';!$this->endOfLOB($clob_stream);) {
465
                                    if ($this->readLOB($clob_stream, $data, $this->getOption('lob_buffer_length')) < 0) {
466
                                        $success = $this->raiseError();
467
                                        break;
468
                                    }
469
                                    $value.= $data;
470
                                }
471
                                if (!MDB::isError($success) && !$descriptors[$position]->save($value)) {
472
                                    $success = $this->oci8RaiseError(NULL,
473
                                        'Do query: Could not upload clob data');
474
                                }
475
                            }
476
                            if (!MDB::isError($success)) {
477
                                for(reset($this->blobs[$prepared_query]), $blob = 0;$blob < count($this->blobs[$prepared_query]);$blob++, next($this->blobs[$prepared_query])) {
478
                                    $position = key($this->blobs[$prepared_query]);
479
                                    $blob_stream = $this->prepared_queries[$prepared_query-1]['Values'][$position-1];
480
                                    for($value = '';!$this->endOfLOB($blob_stream);) {
481
                                        if ($this->readLOB($blob_stream, $data, $this->getOption('lob_buffer_length')) < 0) {
482
                                            $success = $this->raiseError();
483
                                            break;
484
                                        }
485
                                        $value.= $data;
486
                                    }
487
                                    if (!MDB::isError($success) && !$descriptors[$position]->save($value)) {
488
                                        $success = $this->oci8RaiseError(NULL,
489
                                                'Do query: Could not upload blob data');
490
                                    }
491
                                }
492
                            }
493
                        }
494
                        if ($this->auto_commit) {
495
                            if ($lobs) {
496
                                if (MDB::isError($success)) {
497
                                    if (!@OCIRollback($this->connection)) {
498
                                        $success = $this->oci8RaiseError(NULL,
499
                                            'Do query: '.$success->getUserinfo().' and then could not rollback LOB updating transaction');
500
                                    }
501
                                } else {
502
                                    if (!@OCICommit($this->connection)) {
503
                                        $success = $this->oci8RaiseError(NULL,
504
                                            'Do query: Could not commit pending LOB updating transaction');
505
                                    }
506
                                }
507
                            }
508
                        } else {
509
                            $this->uncommitedqueries++;
510
                        }
511
                        if (!MDB::isError($success)) {
512
                            switch (@OCIStatementType($statement)) {
513
                                case 'SELECT':
514
                                    $result_value = intval($statement);
515
                                    $this->current_row[$result_value] = -1;
516
                                    if ($limit > 0) {
517
                                        $this->limits[$result_value] = array($first, $limit, 0);
518
                                    }
519
                                    $this->highest_fetched_row[$result_value] = -1;
520
                                    break;
521
                                default:
522
                                    $this->affected_rows = @OCIRowCount($statement);
523
                                    @OCIFreeCursor($statement);
524
                                    break;
525
                            }
526
                            $result = $statement;
527
                        }
528
                    } else {
529
                        return($this->oci8RaiseError($statement, 'Do query: Could not execute query'));
530
                    }
531
                }
532
            } else {
533
                return($this->oci8RaiseError(NULL, 'Do query: Could not parse query'));
534
            }
535
        }
536
        for(reset($descriptors), $descriptor = 0;
537
            $descriptor < count($descriptors);
538
            $descriptor++, next($descriptors))
539
        {
540
            @OCIFreeDesc($descriptors[key($descriptors)]);
541
        }
542
        return($result);
543
    }
544
 
545
    // }}}
546
    // {{{ query()
547
 
548
   /**
549
     * Send a query to the database and return any results
550
     *
551
     * @access public
552
     * @param string $query the SQL query
553
     * @param array $types array that contains the types of the columns in
554
     *                         the result set
555
     * @return mixed a result handle or MDB_OK on success, a MDB error on failure
556
     */
557
    function query($query, $types = NULL)
558
    {
559
        $this->debug("Query: $query");
560
        $this->last_query = $query;
561
        $first = $this->first_selected_row;
562
        $limit = $this->selected_row_limit;
563
        $this->first_selected_row = $this->selected_row_limit = 0;
564
        if (MDB::isError($connect = $this->connect())) {
565
            return($connect);
566
        }
567
        if(!MDB::isError($result = $this->_doQuery($query, $first, $limit))) {
568
            if ($types != NULL) {
569
                if (!is_array($types)) {
570
                    $types = array($types);
571
                }
572
                if (MDB::isError($err = $this->setResultTypes($result, $types))) {
573
                    $this->freeResult($result);
574
                    return($err);
575
                }
576
            }
577
            return($result);
578
        }
579
        return($this->oci8RaiseError());
580
    }
581
 
582
    // }}}
583
    // {{{ _executePreparedQuery()
584
 
585
    /**
586
     * Execute a prepared query statement.
587
     *
588
     * @param int $prepared_query argument is a handle that was returned by
589
     *       the function prepareQuery()
590
     * @param string $query query to be executed
591
     * @param array $types array that contains the types of the columns in
592
     *       the result set
593
     * @return mixed a result handle or MDB_OK on success, a MDB error on failure
594
     * @access private
595
     */
596
    function _executePreparedQuery($prepared_query, $query)
597
    {
598
        $first = $this->first_selected_row;
599
        $limit = $this->selected_row_limit;
600
        $this->first_selected_row = $this->selected_row_limit = 0;
601
        if (MDB::isError($connect = $this->connect())) {
602
            return($connect);
603
        }
604
        return($this->_doQuery($query, $first, $limit, $prepared_query));
605
    }
606
 
607
    // }}}
608
    // {{{ _skipLimitOffset()
609
 
610
    /**
611
     * Skip the first row of a result set.
612
     *
613
     * @param resource $result
614
     * @return mixed a result handle or MDB_OK on success, a MDB error on failure
615
     * @access private
616
     */
617
    function _skipLimitOffset($result)
618
    {
619
        $result_value = intval($result);
620
        $first = $this->limits[$result_value][0];
621
        for(;$this->limits[$result_value][2] < $first;$this->limits[$result_value][2]++) {
622
            if (!@OCIFetch($result)) {
623
                $this->limits[$result_value][2] = $first;
624
                return($this->raiseError(MDB_ERROR, NULL, NULL,
625
                    'Skip first rows: could not skip a query result row'));
626
            }
627
        }
628
        return(MDB_OK);
629
    }
630
 
631
    // }}}
632
    // {{{ getColumnNames()
633
 
634
    /**
635
     * Retrieve the names of columns returned by the DBMS in a query result.
636
     *
637
     * @param resource $result result identifier
638
     * @return mixed an associative array variable
639
     *                               that will hold the names of columns.The
640
     *                               indexes of the array are the column names
641
     *                               mapped to lower case and the values are the
642
     *                               respective numbers of the columns starting
643
     *                               from 0. Some DBMS may not return any
644
     *                               columns when the result set does not
645
     *                               contain any rows.
646
     *
647
     *                               a MDB error on failure
648
     * @access public
649
     */
650
    function getColumnNames($result)
651
    {
652
        $result_value = intval($result);
653
        if (!isset($this->highest_fetched_row[$result_value])) {
654
            return($this->raiseError(MDB_ERROR, NULL, NULL,
655
                'Get column names: it was specified an inexisting result set'));
656
        }
657
        if (!isset($this->columns[$result_value])) {
658
            $this->columns[$result_value] = array();
659
            $columns = @OCINumCols($result);
660
            for($column = 0; $column < $columns; $column++) {
661
                $field_name = @OCIColumnName($result, $column + 1);
662
                if ($this->options['optimize'] == 'portability') {
663
                    $field_name = strtolower($field_name);
664
                }
665
                $this->columns[$result_value][$field_name] = $column;
666
            }
667
        }
668
        return($this->columns[$result_value]);
669
    }
670
 
671
    // }}}
672
    // {{{ numCols()
673
 
674
    /**
675
     * Count the number of columns returned by the DBMS in a query result.
676
     *
677
     * @param resource $result result identifier
678
     * @return mixed integer value with the number of columns, a MDB error
679
     *      on failure
680
     * @access public
681
     */
682
    function numCols($result)
683
    {
684
        $result_value = intval($result);
685
        if (!isset($this->highest_fetched_row[$result_value])) {
686
            return($this->raiseError(MDB_ERROR, NULL, NULL,
687
                'Number of columns: it was specified an inexisting result set'));
688
        }
689
        return(@OCINumCols($result));
690
    }
691
 
692
    // }}}
693
    // {{{ endOfResult()
694
 
695
    /**
696
     * check if the end of the result set has been reached
697
     *
698
     * @param resource $result result identifier
699
     * @return mixed TRUE or FALSE on sucess, a MDB error on failure
700
     * @access public
701
     */
702
    function endOfResult($result)
703
    {
704
        $result_value = intval($result);
705
        if (!isset($this->current_row[$result_value])) {
706
            return($this->raiseError(MDB_ERROR, NULL, NULL,
707
                'End of result: attempted to check the end of an unknown result'));
708
        }
709
        if (isset($this->results[$result_value]) && end($this->results[$result_value]) === FALSE) {
710
            return(($this->highest_fetched_row[$result_value]-1) <= $this->current_row[$result_value]);
711
        }
712
        if (isset($this->row_buffer[$result_value])) {
713
            return(!$this->row_buffer[$result_value]);
714
        }
715
        if (isset($this->limits[$result_value])) {
716
            if (MDB::isError($this->_skipLimitOffset($result))
717
                || ($this->current_row[$result_value]) > $this->limits[$result_value][1]
718
            ) {
719
                return(TRUE);
720
            }
721
        }
722
        if (@OCIFetchInto($result, $this->row_buffer[$result_value], OCI_RETURN_NULLS)) {
723
            return(FALSE);
724
        }
725
        $this->row_buffer[$result_value] = FALSE;
726
        return(TRUE);
727
    }
728
 
729
    // }}}
730
    // {{{ _retrieveLob()
731
 
732
    /**
733
     * fetch a lob value from a result set
734
     *
735
     * @param int $lob handle to a lob created by the createLob() function
736
     * @return mixed MDB_OK on success, a MDB error on failure
737
     * @access private
738
     */
739
    function _retrieveLob($lob)
740
    {
741
        if (!isset($this->lobs[$lob])) {
742
            return($this->raiseError(MDB_ERROR, NULL, NULL,
743
                'Retrieve LOB: it was not specified a valid lob'));
744
        }
745
        if (!isset($this->lobs[$lob]['Value'])) {
746
            unset($lob_object);
747
            $result = $this->lobs[$lob]['Result'];
748
            $row = $this->lobs[$lob]['Row'];
749
            $field = $this->lobs[$lob]['Field'];
750
            $lob_object = $this->fetch($result, $row, $field);
751
            if (MDB::isError($lob_object)) {
752
                return $lob_object;
753
            }
754
            if (gettype($lob_object) != 'object') {
755
                return($this->raiseError(MDB_ERROR, NULL, NULL,
756
                    'Retrieve LOB: attemped to retrieve LOB from non existing or NULL column'));
757
            }
758
            $this->lobs[$lob]['Value'] = $lob_object->load();
759
        }
760
        return(MDB_OK);
761
    }
762
 
763
    // }}}
764
    // {{{ fetch()
765
 
766
    /**
767
     * fetch value from a result set
768
     *
769
     * @param resource $result result identifier
770
     * @param int $rownum number of the row where the data can be found
771
     * @param int $colnum field number where the data can be found
772
     * @return mixed string on success, a MDB error on failure
773
     * @access public
774
     */
775
    function fetch($result, $rownum, $colnum)
776
    {
777
        $fetchmode = is_numeric($colnum) ? MDB_FETCHMODE_ORDERED : MDB_FETCHMODE_ASSOC;
778
        $row = $this->fetchInto($result, $fetchmode, $rownum);
779
        if (MDB::isError($row)) {
780
            return($row);
781
        }
782
        if (!array_key_exists($colnum, $row)) {
783
            return(NULL);
784
        }
785
        return($row[$colnum]);
786
    }
787
 
788
    // }}}
789
    // {{{ fetchClob()
790
 
791
    /**
792
     * fetch a clob value from a result set
793
     *
794
     * @param resource $result result identifier
795
     * @param int $row number of the row where the data can be found
796
     * @param int $field field number where the data can be found
797
     * @return mixed content of the specified data cell, a MDB error on failure,
798
     *                a MDB error on failure
799
     * @access public
800
     */
801
    function fetchClob($result, $row, $field)
802
    {
803
        return($this->fetchLOB($result, $row, $field));
804
    }
805
 
806
    // }}}
807
    // {{{ fetchBlob()
808
    /**
809
     * fetch a blob value from a result set
810
     *
811
     * @param resource $result result identifier
812
     * @param int $row number of the row where the data can be found
813
     * @param int $field field number where the data can be found
814
     * @return mixed content of the specified data cell, a MDB error on failure
815
     * @access public
816
     */
817
    function fetchBlob($result, $row, $field)
818
    {
819
        return($this->fetchLOB($result, $row, $field));
820
    }
821
 
822
    // }}}
823
    // {{{ resultIsNull()
824
 
825
    /**
826
     * Determine whether the value of a query result located in given row and
827
     *    field is a NULL.
828
     *
829
     * @param resource $result result identifier
830
     * @param int $rownum number of the row where the data can be found
831
     * @param int $field field number where the data can be found
832
     * @return mixed TRUE or FALSE on success, a MDB error on failure
833
     * @access public
834
     */
835
    function resultIsNull($result, $rownum, $field)
836
    {
837
        $value = $this->fetch($result, $rownum, $field);
838
        if (MDB::isError($value)) {
839
            return($value);
840
        }
841
        return(!isset($value));
842
    }
843
 
844
    // }}}
845
    // {{{ convertResult()
846
 
847
    /**
848
     * convert a value to a RDBMS indepdenant MDB type
849
     *
850
     * @param mixed $value value to be converted
851
     * @param int $type constant that specifies which type to convert to
852
     * @return mixed converted value
853
     * @access public
854
     */
855
    function convertResult($value, $type)
856
    {
857
        switch ($type) {
858
            case MDB_TYPE_DATE:
859
                return(substr($value, 0, strlen('YYYY-MM-DD')));
860
            case MDB_TYPE_TIME:
861
                return(substr($value, strlen('YYYY-MM-DD '), strlen('HH:MI:SS')));
862
            default:
863
                return($this->_baseConvertResult($value, $type));
864
        }
865
    }
866
 
867
    // }}}
868
    // {{{ numRows()
869
 
870
    /**
871
     * returns the number of rows in a result object
872
     *
873
     * @param ressource $result a valid result ressouce pointer
874
     * @return mixed MDB_Error or the number of rows
875
     * @access public
876
     */
877
    function numRows($result)
878
    {
879
        $result_value = intval($result);
880
        if (!isset($this->current_row[$result_value])) {
881
            return($this->raiseError(MDB_ERROR, NULL, NULL,
882
                'Number of rows: attemped to obtain the number of rows contained in an unknown query result'));
883
        }
884
        if (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]])
885
            || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== FALSE
886
        ) {
887
            if (isset($this->limits[$result_value])) {
888
                if (MDB::isError($skipfirstrow = $this->_skipLimitOffset($result))) {
889
                     return($skipfirstrow);
890
                }
891
            }
892
            if (isset($this->row_buffer[$result_value])) {
893
                ++$this->highest_fetched_row[$result_value];
894
                $this->results[$result_value][$this->highest_fetched_row[$result_value]]
895
                    = $this->row_buffer[$result_value];
896
                unset($this->row_buffer[$result_value]);
897
            }
898
            if (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]])
899
                || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== FALSE
900
            ) {
901
                while((!isset($this->limits[$result_value])
902
                        || ($this->highest_fetched_row[$result_value]+1) < $this->limits[$result_value][1]
903
                    )
904
                    && @OCIFetchInto($result, $buffer, OCI_RETURN_NULLS)
905
                ) {
906
                    ++$this->highest_fetched_row[$result_value];
907
                    $this->results[$result_value][$this->highest_fetched_row[$result_value]] = $buffer;
908
                }
909
                ++$this->highest_fetched_row[$result_value];
910
                $this->results[$result_value][$this->highest_fetched_row[$result_value]] = FALSE;
911
            }
912
        }
913
        return(max(0, $this->highest_fetched_row[$result_value]));
914
    }
915
 
916
    // }}}
917
    // {{{ freeResult()
918
 
919
    /**
920
     * Free the internal resources associated with $result.
921
     *
922
     * @param  $result result identifier
923
     * @return bool TRUE on success, FALSE if $result is invalid
924
     * @access public
925
     */
926
    function freeResult($result)
927
    {
928
        $result_value = intval($result);
929
        if (!isset($this->current_row[$result_value])) {
930
           return($this->raiseError(MDB_ERROR, NULL, NULL,
931
               'Free result: attemped to free an unknown query result'));
932
        }
933
        if(isset($this->highest_fetched_row[$result_value])) {
934
            unset($this->highest_fetched_row[$result_value]);
935
        }
936
        if(isset($this->row_buffer[$result_value])) {
937
            unset($this->row_buffer[$result_value]);
938
        }
939
        if(isset($this->limits[$result_value])) {
940
            unset($this->limits[$result_value]);
941
        }
942
        if(isset($this->current_row[$result_value])) {
943
            unset($this->current_row[$result_value]);
944
        }
945
        if(isset($this->results[$result_value])) {
946
            unset($this->results[$result_value]);
947
        }
948
        if(isset($this->columns[$result_value])) {
949
            unset($this->columns[$result_value]);
950
        }
951
        if(isset($this->result_types[$result_value])) {
952
            unset($this->result_types[$result_value]);
953
        }
954
        return(@OCIFreeCursor($result));
955
    }
956
 
957
    // }}}
958
    // {{{ getTypeDeclaration()
959
 
960
    /**
961
     * Obtain DBMS specific native datatype as a string
962
     *
963
     * @param string $field associative array with the name of the properties
964
     *        of the field being declared as array indexes. Currently, the types
965
     *        of supported field properties are as follows:
966
     *
967
     * @return string with the correct RDBMS native type
968
     * @access public
969
     */
970
    function getTypeDeclaration($field)
971
    {
972
        switch ($field['type']) {
973
            case 'integer':
974
                return('INT');
975
            case 'text':
976
                return('VARCHAR ('.(isset($field['length'])
977
                    ? $field['length'] : (isset($this->options['DefaultTextFieldLength'])
978
                        ? $this->options['DefaultTextFieldLength'] : 4000)).')');
979
            case 'boolean':
980
                return('CHAR (1)');
981
            case 'date':
982
            case 'time':
983
            case 'timestamp':
984
                return('DATE');
985
            case 'float':
986
                return('NUMBER');
987
            case 'decimal':
988
                return('NUMBER(*,'.$this->decimal_places.')');
989
        }
990
        return('');
991
    }
992
 
993
    // }}}
994
    // {{{ getIntegerDeclaration()
995
 
996
    /**
997
     * Obtain DBMS specific SQL code portion needed to declare an integer type
998
     * field to be used in statements like CREATE TABLE.
999
     *
1000
     * @param string $name name the field to be declared.
1001
     * @param string $field associative array with the name of the properties
1002
     *        of the field being declared as array indexes. Id
1003
     * ently, the types
1004
     *        of supported field properties are as follows:
1005
     *
1006
     *        unsigned
1007
     *            Boolean flag that indicates whether the field should be
1008
     *            declared as unsigned integer if possible.
1009
     *
1010
     *        default
1011
     *            Integer value to be used as default for this field.
1012
     *
1013
     *        notnull
1014
     *            Boolean flag that indicates whether this field is constrained
1015
     *            to not be set to NULL.
1016
     * @return string DBMS specific SQL code portion that should be used to
1017
     *        declare the specified field.
1018
     * @access public
1019
     */
1020
    function getIntegerDeclaration($name, $field)
1021
    {
1022
        if (isset($field['unsigned']))
1023
            $this->warning = "unsigned integer field \"$name\" is being declared as signed integer";
1024
        return("$name ".$this->getTypeDeclaration($field)
1025
            .(isset($field['default']) ? ' DEFAULT '.$field['default'] : '')
1026
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1027
    }
1028
 
1029
    // }}}
1030
    // {{{ getTextDeclaration()
1031
 
1032
    /**
1033
     * Obtain DBMS specific SQL code portion needed to declare an text type
1034
     * field to be used in statements like CREATE TABLE.
1035
     *
1036
     * @param string $name name the field to be declared.
1037
     * @param string $field associative array with the name of the properties
1038
     *        of the field being declared as array indexes. Currently, the types
1039
     *        of supported field properties are as follows:
1040
     *
1041
     *        length
1042
     *            Integer value that determines the maximum length of the text
1043
     *            field. If this argument is missing the field should be
1044
     *            declared to have the longest length allowed by the DBMS.
1045
     *
1046
     *        default
1047
     *            Text value to be used as default for this field.
1048
     *
1049
     *        notnull
1050
     *            Boolean flag that indicates whether this field is constrained
1051
     *            to not be set to NULL.
1052
     * @return string DBMS specific SQL code portion that should be used to
1053
     *        declare the specified field.
1054
     * @access public
1055
     */
1056
    function getTextDeclaration($name, $field)
1057
    {
1058
        return("$name ".$this->getTypeDeclaration($field)
1059
            .(isset($field['default']) ? ' DEFAULT '.$this->getTextValue($field['default']) : '')
1060
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1061
    }
1062
 
1063
    // }}}
1064
    // {{{ getClobDeclaration()
1065
 
1066
    /**
1067
     * Obtain DBMS specific SQL code portion needed to declare an character
1068
     * large object type field to be used in statements like CREATE TABLE.
1069
     *
1070
     * @param string $name name the field to be declared.
1071
     * @param string $field associative array with the name of the properties
1072
     *        of the field being declared as array indexes. Currently, the types
1073
     *        of supported field properties are as follows:
1074
     *
1075
     *        length
1076
     *            Integer value that determines the maximum length of the large
1077
     *            object field. If this argument is missing the field should be
1078
     *            declared to have the longest length allowed by the DBMS.
1079
     *
1080
     *        notnull
1081
     *            Boolean flag that indicates whether this field is constrained
1082
     *            to not be set to NULL.
1083
     * @return string DBMS specific SQL code portion that should be used to
1084
     *        declare the specified field.
1085
     * @access public
1086
     */
1087
    function getClobDeclaration($name, $field)
1088
    {
1089
        return("$name CLOB".(isset($field['notnull']) ? ' NOT NULL' : ''));
1090
    }
1091
 
1092
    // }}}
1093
    // {{{ getBlobDeclaration()
1094
 
1095
    /**
1096
     * Obtain DBMS specific SQL code portion needed to declare an binary large
1097
     * object type field to be used in statements like CREATE TABLE.
1098
     *
1099
     * @param string $name name the field to be declared.
1100
     * @param string $field associative array with the name of the properties
1101
     *        of the field being declared as array indexes. Currently, the types
1102
     *        of supported field properties are as follows:
1103
     *
1104
     *        length
1105
     *            Integer value that determines the maximum length of the large
1106
     *            object field. If this argument is missing the field should be
1107
     *            declared to have the longest length allowed by the DBMS.
1108
     *
1109
     *        notnull
1110
     *            Boolean flag that indicates whether this field is constrained
1111
     *            to not be set to NULL.
1112
     * @return string DBMS specific SQL code portion that should be used to
1113
     *        declare the specified field.
1114
     * @access public
1115
     */
1116
    function getBlobDeclaration($name, $field)
1117
    {
1118
        return("$name BLOB".(isset($field['notnull']) ? ' NOT NULL' : ''));
1119
    }
1120
 
1121
    // }}}
1122
    // {{{ getBooleanDeclaration()
1123
 
1124
    /**
1125
     * Obtain DBMS specific SQL code portion needed to declare a boolean type
1126
     * field to be used in statements like CREATE TABLE.
1127
     *
1128
     * @param string $name name the field to be declared.
1129
     * @param string $field associative array with the name of the properties
1130
     *        of the field being declared as array indexes. Currently, the types
1131
     *        of supported field properties are as follows:
1132
     *
1133
     *        default
1134
     *            Boolean value to be used as default for this field.
1135
     *
1136
     *        notnullL
1137
     *            Boolean flag that indicates whether this field is constrained
1138
     *            to not be set to NULL.
1139
     * @return string DBMS specific SQL code portion that should be used to
1140
     *        declare the specified field.
1141
     * @access public
1142
     */
1143
    function getBooleanDeclaration($name, $field)
1144
    {
1145
        return("$name ".$this->getTypeDeclaration($field)
1146
            .(isset($field['default']) ? ' DEFAULT '
1147
            .$this->getBooleanValue($field['default']) : '')
1148
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1149
    }
1150
 
1151
    // }}}
1152
    // {{{ getDateDeclaration()
1153
 
1154
    /**
1155
     * Obtain DBMS specific SQL code portion needed to declare a date type
1156
     * field to be used in statements like CREATE TABLE.
1157
     *
1158
     * @param string $name name the field to be declared.
1159
     * @param string $field associative array with the name of the properties
1160
     *        of the field being declared as array indexes. Currently, the types
1161
     *        of supported field properties are as follows:
1162
     *
1163
     *        default
1164
     *            Date value to be used as default for this field.
1165
     *
1166
     *        notnull
1167
     *            Boolean flag that indicates whether this field is constrained
1168
     *            to not be set to NULL.
1169
     * @return string DBMS specific SQL code portion that should be used to
1170
     *        declare the specified field.
1171
     * @access public
1172
     */
1173
    function getDateDeclaration($name, $field)
1174
    {
1175
        return("$name ".$this->getTypeDeclaration($field)
1176
            .(isset($field['default']) ? ' DEFAULT '
1177
            .$this->getDateValue($field['default']) : '')
1178
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1179
    }
1180
 
1181
    // }}}
1182
    // {{{ getTimestampDeclaration()
1183
 
1184
    /**
1185
     * Obtain DBMS specific SQL code portion needed to declare a timestamp
1186
     * field to be used in statements like CREATE TABLE.
1187
     *
1188
     * @param string $name name the field to be declared.
1189
     * @param string $field associative array with the name of the properties
1190
     *        of the field being declared as array indexes. Currently, the types
1191
     *        of supported field properties are as follows:
1192
     *
1193
     *        default
1194
     *            Timestamp value to be used as default for this field.
1195
     *
1196
     *        notnull
1197
     *            Boolean flag that indicates whether this field is constrained
1198
     *            to not be set to NULL.
1199
     * @return string DBMS specific SQL code portion that should be used to
1200
     *        declare the specified field.
1201
     * @access public
1202
     */
1203
    function getTimestampDeclaration($name, $field)
1204
    {
1205
        return("$name ".$this->getTypeDeclaration($field)
1206
            .(isset($field['default']) ? ' DEFAULT '
1207
            .$this->getTimestampValue($field['default']) : '')
1208
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1209
    }
1210
 
1211
    // }}}
1212
    // {{{ getTimeDeclaration()
1213
 
1214
    /**
1215
     * Obtain DBMS specific SQL code portion needed to declare a time
1216
     * field to be used in statements like CREATE TABLE.
1217
     *
1218
     * @param string $name name the field to be declared.
1219
     * @param string $field associative array with the name of the properties
1220
     *        of the field being declared as array indexes. Currently, the types
1221
     *        of supported field properties are as follows:
1222
     *
1223
     *        default
1224
     *            Time value to be used as default for this field.
1225
     *
1226
     *        notnull
1227
     *            Boolean flag that indicates whether this field is constrained
1228
     *            to not be set to NULL.
1229
     * @return string DBMS specific SQL code portion that should be used to
1230
     *        declare the specified field.
1231
     * @access public
1232
     */
1233
    function getTimeDeclaration($name, $field)
1234
    {
1235
        return("$name ".$this->getTypeDeclaration($field)
1236
            .(isset($field['default']) ? ' DEFAULT '
1237
            .$this->getTimeValue($field['default']) : '')
1238
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1239
    }
1240
 
1241
    // }}}
1242
    // {{{ getFloatDeclaration()
1243
 
1244
    /**
1245
     * Obtain DBMS specific SQL code portion needed to declare a float type
1246
     * field to be used in statements like CREATE TABLE.
1247
     *
1248
     * @param string $name name the field to be declared.
1249
     * @param string $field associative array with the name of the properties
1250
     *        of the field being declared as array indexes. Currently, the types
1251
     *        of supported field properties are as follows:
1252
     *
1253
     *        default
1254
     *            Float value to be used as default for this field.
1255
     *
1256
     *        notnull
1257
     *            Boolean flag that indicates whether this field is constrained
1258
     *            to not be set to NULL.
1259
     * @return string DBMS specific SQL code portion that should be used to
1260
     *        declare the specified field.
1261
     * @access public
1262
     */
1263
    function getFloatDeclaration($name, $field)
1264
    {
1265
        return("$name ".$this->getTypeDeclaration($field)
1266
            .(isset($field['default']) ? ' DEFAULT '
1267
            .$this->getFloatValue($field['default']) : '')
1268
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1269
    }
1270
 
1271
    // }}}
1272
    // {{{ getDecimalDeclaration()
1273
 
1274
    /**
1275
     * Obtain DBMS specific SQL code portion needed to declare a decimal type
1276
     * field to be used in statements like CREATE TABLE.
1277
     *
1278
     * @param string $name name the field to be declared.
1279
     * @param string $field associative array with the name of the properties
1280
     *        of the field being declared as array indexes. Currently, the types
1281
     *        of supported field properties are as follows:
1282
     *
1283
     *        default
1284
     *            Decimal value to be used as default for this field.
1285
     *
1286
     *        notnull
1287
     *            Boolean flag that indicates whether this field is constrained
1288
     *            to not be set to NULL.
1289
     * @return string DBMS specific SQL code portion that should be used to
1290
     *        declare the specified field.
1291
     * @access public
1292
     */
1293
    function getDecimalDeclaration($name, $field)
1294
    {
1295
        return("$name ".$this->getTypeDeclaration($field)
1296
            .(isset($field['default']) ? ' DEFAULT '
1297
            .$this->getDecimalValue($field['default']) : '')
1298
            .(isset($field['notnull']) ? ' NOT NULL' : ''));
1299
    }
1300
 
1301
    // }}}
1302
    // {{{ getClobValue()
1303
 
1304
    /**
1305
     * Convert a text value into a DBMS specific format that is suitable to
1306
     * compose query statements.
1307
     *
1308
     * @param resource $prepared_query query handle from prepare()
1309
     * @param  $parameter
1310
     * @param  $clob
1311
     * @return string text string that represents the given argument value in
1312
     *        a DBMS specific format.
1313
     * @access public
1314
     */
1315
    function getClobValue($prepared_query, $parameter, $clob)
1316
    {
1317
        return('EMPTY_CLOB()');
1318
    }
1319
 
1320
    // }}}
1321
    // {{{ freeClobValue()
1322
 
1323
    /**
1324
     * free a character large object
1325
     *
1326
     * @param resource $prepared_query query handle from prepare()
1327
     * @param string $blob
1328
     * @param string $value
1329
     * @access public
1330
     */
1331
    function freeClobValue($prepared_query, $clob, &$value)
1332
    {
1333
        unset($value);
1334
    }
1335
 
1336
    // }}}
1337
    // {{{ getBlobValue()
1338
 
1339
    /**
1340
     * Convert a text value into a DBMS specific format that is suitable to
1341
     * compose query statements.
1342
     *
1343
     * @param resource $prepared_query query handle from prepare()
1344
     * @param  $parameter
1345
     * @param  $blob
1346
     * @return string text string that represents the given argument value in
1347
     *        a DBMS specific format.
1348
     * @access public
1349
     */
1350
    function getBlobValue($prepared_query, $parameter, $blob)
1351
    {
1352
        return('EMPTY_BLOB()');
1353
    }
1354
 
1355
    // }}}
1356
    // {{{ freeBlobValue()
1357
 
1358
    /**
1359
     * free a binary large object
1360
     *
1361
     * @param resource $prepared_query query handle from prepare()
1362
     * @param string $blob
1363
     * @param string $value
1364
     * @access public
1365
     */
1366
    function freeBlobValue($prepared_query, $blob, &$value)
1367
    {
1368
        unset($value);
1369
    }
1370
 
1371
    // }}}
1372
    // {{{ getDateValue()
1373
 
1374
    /**
1375
     * Convert a text value into a DBMS specific format that is suitable to
1376
     * compose query statements.
1377
     *
1378
     * @param string $value text string value that is intended to be converted.
1379
     * @return string text string that represents the given argument value in
1380
     *        a DBMS specific format.
1381
     * @access public
1382
     */
1383
    function getDateValue($value)
1384
    {
1385
        return(($value === NULL) ? 'NULL' : "TO_DATE('$value','YYYY-MM-DD')");
1386
    }
1387
 
1388
    // }}}
1389
    // {{{ getTimestampValue()
1390
 
1391
    /**
1392
     * Convert a text value into a DBMS specific format that is suitable to
1393
     * compose query statements.
1394
     *
1395
     * @param string $value text string value that is intended to be converted.
1396
     * @return string text string that represents the given argument value in
1397
     *        a DBMS specific format.
1398
     * @access public
1399
     */
1400
    function getTimestampValue($value)
1401
    {
1402
        return(($value === NULL) ? 'NULL' : "TO_DATE('$value','YYYY-MM-DD HH24:MI:SS')");
1403
    }
1404
 
1405
    // }}}
1406
    // {{{ getTimeValue()
1407
 
1408
    /**
1409
     * Convert a text value into a DBMS specific format that is suitable to
1410
     *        compose query statements.
1411
     *
1412
     * @param string $value text string value that is intended to be converted.
1413
     * @return string text string that represents the given argument value in
1414
     *        a DBMS specific format.
1415
     * @access public
1416
     */
1417
    function getTimeValue($value)
1418
    {
1419
        return(($value === NULL) ? 'NULL' : "TO_DATE('0001-01-01 $value','YYYY-MM-DD HH24:MI:SS')");
1420
    }
1421
 
1422
    // }}}
1423
    // {{{ getFloatValue()
1424
 
1425
    /**
1426
     * Convert a text value into a DBMS specific format that is suitable to
1427
     * compose query statements.
1428
     *
1429
     * @param string $value text string value that is intended to be converted.
1430
     * @return string text string that represents the given argument value in
1431
     *        a DBMS specific format.
1432
     * @access public
1433
     */
1434
    function getFloatValue($value)
1435
    {
1436
        return(($value === NULL) ? 'NULL' : (float)$value);
1437
    }
1438
 
1439
    // }}}
1440
    // {{{ getDecimalValue()
1441
 
1442
    /**
1443
     * Convert a text value into a DBMS specific format that is suitable to
1444
     * compose query statements.
1445
     *
1446
     * @param string $value text string value that is intended to be converted.
1447
     * @return string text string that represents the given argument value in
1448
     *        a DBMS specific format.
1449
     * @access public
1450
     */
1451
    function getDecimalValue($value)
1452
    {
1453
        return(($value === NULL) ? 'NULL' : $value);
1454
    }
1455
 
1456
    // }}}
1457
    // {{{ nextId()
1458
 
1459
    /**
1460
     * returns the next free id of a sequence
1461
     *
1462
     * @param string $seq_name name of the sequence
1463
     * @param boolean $ondemand when TRUE the seqence is
1464
     *                           automatic created, if it
1465
     *                           not exists
1466
     * @return mixed MDB_Error or id
1467
     * @access public
1468
     */
1469
    function nextId($seq_name, $ondemand = TRUE)
1470
    {
1471
        if (MDB::isError($connect = $this->connect())) {
1472
            return($connect);
1473
        }
1474
        $sequence_name = $this->getSequenceName($seq_name);
1475
        $this->expectError(MDB_ERROR_NOSUCHTABLE);
1476
        $result = $this->_doQuery("SELECT $sequence_name.nextval FROM DUAL");
1477
        $this->popExpect();
1478
        if ($ondemand && MDB::isError($result)
1479
            && $result->getCode() == MDB_ERROR_NOSUCHTABLE)
1480
        {
1481
            $result = $this->createSequence($seq_name, 1);
1482
            if (MDB::isError($result)) {
1483
                return $result;
1484
            }
1485
            return $this->nextId($seq_name, false);
1486
        }
1487
        return($this->fetchOne($result));
1488
    }
1489
 
1490
    // }}}
1491
    // {{{ currId()
1492
 
1493
    /**
1494
     * returns the current id of a sequence
1495
     *
1496
     * @param string $seq_name name of the sequence
1497
     * @return mixed MDB_Error or id
1498
     * @access public
1499
     */
1500
    function currId($seq_name)
1501
    {
1502
        $sequence_name = $this->getSequenceName($seq_name);
1503
        $result = $this->_doQuery("SELECT $sequence_name.currval FROM DUAL");
1504
        if (MDB::isError($result)) {
1505
            return($this->raiseError(MDB_ERROR, NULL, NULL,
1506
                'currId: unable to select from ' . $seq_name) );
1507
        }
1508
        $result = $this->fetchOne($result);
1509
        if (!is_numeric($result)) {
1510
            return($this->raiseError(MDB_ERROR, NULL, NULL,
1511
                'currId: could not find value in sequence table'));
1512
        }
1513
        return($result);
1514
    }
1515
 
1516
    // }}}
1517
    // {{{ fetchInto()
1518
 
1519
    /**
1520
     * Fetch a row and return data in an array.
1521
     *
1522
     * @param resource $result result identifier
1523
     * @param int $fetchmode how the array data should be indexed
1524
     * @param int $rownum the row number to fetch
1525
     * @return mixed data array or NULL on success, a MDB error on failure
1526
     * @access public
1527
     */
1528
    function fetchInto($result, $fetchmode = MDB_FETCHMODE_DEFAULT, $rownum = NULL)
1529
    {
1530
        $result_value = intval($result);
1531
        if (!isset($this->current_row[$result_value])) {
1532
            return($this->raiseError(MDB_ERROR, NULL, NULL,
1533
                'fetchInto: attemped to fetch on an unknown query result'));
1534
        }
1535
        if ($fetchmode == MDB_FETCHMODE_DEFAULT) {
1536
            $fetchmode = $this->fetchmode;
1537
        }
1538
        if (is_null($rownum)) {
1539
            $rownum = $this->current_row[$result_value] + 1;
1540
        }
1541
        if (!isset($this->results[$result_value][$rownum])
1542
            && (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]])
1543
                || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== FALSE)
1544
        ) {
1545
            if (isset($this->limits[$result_value])) {
1546
                // upper limit
1547
                if ($rownum > $this->limits[$result_value][1]) {
1548
                    // are all previous rows fetched so that we can set the end
1549
                    // of the result set and not have any "holes" in between?
1550
                    if ($rownum == 0
1551
                        || (isset($this->results[$result_value])
1552
                            && count($this->results[$result_value]) == $rownum
1553
                        )
1554
                    ) {
1555
                        $this->highest_fetched_row[$result_value] = $rownum;
1556
                        $this->current_row[$result_value] = $rownum;
1557
                        $this->results[$result_value][$rownum] = FALSE;
1558
                    }
1559
                    if($this->options['autofree']) {
1560
                        $this->freeResult($result);
1561
                    }
1562
                    return(NULL);
1563
                }
1564
                // offset skipping
1565
                if (MDB::isError($this->_skipLimitOffset($result))) {
1566
                    $this->current_row[$result_value] = 0;
1567
                    $this->results[$result_value] = array(FALSE);
1568
                    if($this->options['autofree']) {
1569
                        $this->freeResult($result);
1570
                    }
1571
                    return(NULL);
1572
                }
1573
            }
1574
            if (isset($this->row_buffer[$result_value])) {
1575
                ++$this->current_row[$result_value];
1576
                $this->results[$result_value][$this->current_row[$result_value]] =
1577
                    $this->row_buffer[$result_value];
1578
                unset($this->row_buffer[$result_value]);
1579
            }
1580
            if (!isset($this->results[$result_value][$rownum])
1581
                && (!isset($this->results[$result_value][$this->highest_fetched_row[$result_value]])
1582
                    || $this->results[$result_value][$this->highest_fetched_row[$result_value]] !== FALSE)
1583
            ) {
1584
                while($this->current_row[$result_value] < $rownum
1585
                    && @OCIFetchInto($result, $buffer, OCI_RETURN_NULLS)
1586
                ) {
1587
                    ++$this->current_row[$result_value];
1588
                    $this->results[$result_value][$this->current_row[$result_value]] = $buffer;
1589
                }
1590
                // end of result set reached
1591
                if ($this->current_row[$result_value] < $rownum) {
1592
                    ++$this->current_row[$result_value];
1593
                    $this->results[$result_value][$this->current_row[$result_value]] = FALSE;
1594
                }
1595
            }
1596
            $this->highest_fetched_row[$result_value] =
1597
                max($this->highest_fetched_row[$result_value],
1598
                    $this->current_row[$result_value]);
1599
        } else {
1600
            ++$this->current_row[$result_value];
1601
        }
1602
        if (isset($this->results[$result_value][$rownum])
1603
            && $this->results[$result_value][$rownum]
1604
        ) {
1605
            $row = $this->results[$result_value][$rownum];
1606
        } else {
1607
            if($this->options['autofree']) {
1608
                $this->freeResult($result);
1609
            }
1610
            return(NULL);
1611
        }
1612
        if ($fetchmode & MDB_FETCHMODE_ASSOC) {
1613
            $column_names = $this->getColumnNames($result);
1614
            foreach($column_names as $name => $i) {
1615
                $column_names[$name] = $row[$i];
1616
            }
1617
            $row = $column_names;
1618
        }
1619
        if (isset($this->result_types[$result_value])) {
1620
            $row = $this->convertResultRow($result, $row);
1621
        }
1622
        return($row);
1623
    }
1624
 
1625
    // }}}
1626
    // {{{ nextResult()
1627
 
1628
    /**
1629
     * Move the internal oracle result pointer to the next available result
1630
     * Currently not supported
1631
     *
1632
     * @param $result a oracle valid result resource
1633
     * @return TRUE if a result is available otherwise return FALSE
1634
     * @access public
1635
     */
1636
    function nextResult($result)
1637
    {
1638
        return(FALSE);
1639
    }
1640
 
1641
    // }}}
1642
    // {{{ tableInfo()
1643
 
1644
    /**
1645
     * returns meta data about the result set
1646
     *
1647
     * @param resource $result result identifier
1648
     * @param mixed $mode depends on implementation
1649
     * @return array an nested array, or a MDB error
1650
     * @access public
1651
     */
1652
    function tableInfo($result, $mode = NULL)
1653
    {
1654
        $count = 0;
1655
        $res = array();
1656
        /**
1657
         * depending on $mode, metadata returns the following values:
1658
         *
1659
         * - mode is FALSE (default):
1660
         * $res[]:
1661
         *    [0]['table']       table name
1662
         *    [0]['name']        field name
1663
         *    [0]['type']        field type
1664
         *    [0]['len']         field length
1665
         *    [0]['nullable']    field can be NULL (boolean)
1666
         *    [0]['format']      field precision if NUMBER
1667
         *    [0]['default']     field default value
1668
         *
1669
         * - mode is MDB_TABLEINFO_ORDER
1670
         * $res[]:
1671
         *    ['num_fields']     number of fields
1672
         *    [0]['table']       table name
1673
         *    [0]['name']        field name
1674
         *    [0]['type']        field type
1675
         *    [0]['len']         field length
1676
         *    [0]['nullable']    field can be NULL (boolean)
1677
         *    [0]['format']      field precision if NUMBER
1678
         *    [0]['default']     field default value
1679
         *    ['order'][field name] index of field named 'field name'
1680
         *    The last one is used, if you have a field name, but no index.
1681
         *    Test:  if (isset($result['order']['myfield'])) { ...
1682
         *
1683
         * - mode is MDB_TABLEINFO_ORDERTABLE
1684
         *     the same as above. but additionally
1685
         *    ['ordertable'][table name][field name] index of field
1686
         *       named 'field name'
1687
         *
1688
         *       this is, because if you have fields from different
1689
         *       tables with the same field name * they override each
1690
         *       other with MDB_TABLEINFO_ORDER
1691
         *
1692
         *       you can combine DB_TABLEINFO_ORDER and
1693
         *       MDB_TABLEINFO_ORDERTABLE with MDB_TABLEINFO_ORDER |
1694
         *       MDB_TABLEINFO_ORDERTABLE * or with MDB_TABLEINFO_FULL
1695
         */
1696
        // if $result is a string, we collect info for a table only
1697
        if (is_string($result)) {
1698
            if (MDB::isError($connect = $this->connect())) {
1699
                return($connect);
1700
            }
1701
            $result = strtoupper($result);
1702
            $q_fields = "select column_name, data_type, data_length, data_precision,
1703
                     nullable, data_default from user_tab_columns
1704
                     where table_name='$result' order by column_id";
1705
            if (!$stmt = @OCIParse($this->connection, $q_fields)) {
1706
                return($this->oci8RaiseError());
1707
            }
1708
            if (!@OCIExecute($stmt, OCI_DEFAULT)) {
1709
                return($this->oci8RaiseError($stmt));
1710
            } while (@OCIFetch($stmt)) {
1711
                $res[$count]['table'] = strtolower($result);
1712
                $res[$count]['name'] = strtolower(@OCIResult($stmt, 1));
1713
                $res[$count]['type'] = strtolower(@OCIResult($stmt, 2));
1714
                $res[$count]['len'] = @OCIResult($stmt, 3);
1715
                $res[$count]['format'] = @OCIResult($stmt, 4);
1716
                $res[$count]['nullable'] = (@OCIResult($stmt, 5) == 'Y') ? TRUE : FALSE;
1717
                $res[$count]['default'] = @OCIResult($stmt, 6);
1718
                if ($mode & MDB_TABLEINFO_ORDER) {
1719
                    $res['order'][$res[$count]['name']] = $count;
1720
                }
1721
                if ($mode & MDB_TABLEINFO_ORDERTABLE) {
1722
                    $res['ordertable'][$res[$count]['table']][$res[$count]['name']] = $count;
1723
                }
1724
                $count++;
1725
            }
1726
            if ($mode) {
1727
                $res['num_fields'] = $count;
1728
            }
1729
            @OCIFreeStatement($stmt);
1730
        } else { // else we want information about a resultset
1731
            #if ($result === $this->last_stmt) {
1732
                $count = @OCINumCols($result);
1733
                for ($i = 0; $i < $count; $i++) {
1734
                    $res[$i]['name'] = strtolower(@OCIColumnName($result, $i + 1));
1735
                    $res[$i]['type'] = strtolower(@OCIColumnType($result, $i + 1));
1736
                    $res[$i]['len'] = @OCIColumnSize($result, $i + 1);
1737
 
1738
                    $q_fields = "SELECT table_name, data_precision, nullable, data_default from user_tab_columns WHERE column_name='".$res[$i]['name']."'";
1739
                    if (!$stmt = @OCIParse($this->connection, $q_fields)) {
1740
                        return($this->oci8RaiseError());
1741
                    }
1742
                    if (!@OCIExecute($stmt, OCI_DEFAULT)) {
1743
                        return($this->oci8RaiseError($stmt));
1744
                    }
1745
                    @OCIFetch($stmt);
1746
                    $res[$i]['table'] = strtolower(@OCIResult($stmt, 1));
1747
                    $res[$i]['format'] = @OCIResult($stmt, 2);
1748
                    $res[$i]['nullable'] = (@OCIResult($stmt, 3) == 'Y') ? TRUE : FALSE;
1749
                    $res[$i]['default'] = @OCIResult($stmt, 4);
1750
                    @OCIFreeStatement($stmt);
1751
 
1752
                    if ($mode & MDB_TABLEINFO_ORDER) {
1753
                        $res['order'][$res[$i]['name']] = $i;
1754
                    }
1755
                    if ($mode & MDB_TABLEINFO_ORDERTABLE) {
1756
                        $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
1757
                    }
1758
                }
1759
                if ($mode) {
1760
                    $res['num_fields'] = $count;
1761
                }
1762
            #} else {
1763
            #    return($this->raiseError(MDB_ERROR_NOT_CAPABLE));
1764
            #}
1765
        }
1766
        return($res);
1767
    }
1768
}
1769
?>