Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
 
3
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
 
5
/**
6
 * The PEAR DB driver for PHP's odbc extension
7
 * for interacting with databases via ODBC connections
8
 *
187 mathias 9
 * PHP version 5
94 jpm 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     Stig Bakken <ssb@php.net>
20
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 21
 * @copyright  1997-2007 The PHP Group
94 jpm 22
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 23
 * @version    CVS: $Id$
94 jpm 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 odbc extension
34
 * for interacting with databases via ODBC connections
35
 *
36
 * These methods overload the ones declared in DB_common.
37
 *
38
 * More info on ODBC errors could be found here:
39
 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/trblsql/tr_err_odbc_5stz.asp
40
 *
41
 * @category   Database
42
 * @package    DB
43
 * @author     Stig Bakken <ssb@php.net>
44
 * @author     Daniel Convissor <danielc@php.net>
187 mathias 45
 * @copyright  1997-2007 The PHP Group
94 jpm 46
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
187 mathias 47
 * @version    Release: 1.9.2
94 jpm 48
 * @link       http://pear.php.net/package/DB
49
 */
50
class DB_odbc extends DB_common
51
{
52
    // {{{ properties
53
 
54
    /**
55
     * The DB driver type (mysql, oci8, odbc, etc.)
56
     * @var string
57
     */
58
    var $phptype = 'odbc';
59
 
60
    /**
61
     * The database syntax variant to be used (db2, access, etc.), if any
62
     * @var string
63
     */
64
    var $dbsyntax = 'sql92';
65
 
66
    /**
67
     * The capabilities of this DB implementation
68
     *
69
     * The 'new_link' element contains the PHP version that first provided
70
     * new_link support for this DBMS.  Contains false if it's unsupported.
71
     *
72
     * Meaning of the 'limit' element:
73
     *   + 'emulate' = emulate with fetch row by number
74
     *   + 'alter'   = alter the query
75
     *   + false     = skip rows
76
     *
77
     * NOTE: The feature set of the following drivers are different than
78
     * the default:
79
     *   + solid: 'transactions' = true
80
     *   + navision: 'limit' = false
81
     *
82
     * @var array
83
     */
84
    var $features = array(
85
        'limit'         => 'emulate',
86
        'new_link'      => false,
87
        'numrows'       => true,
88
        'pconnect'      => true,
89
        'prepare'       => false,
90
        'ssl'           => false,
91
        'transactions'  => false,
92
    );
93
 
94
    /**
95
     * A mapping of native error codes to DB error codes
96
     * @var array
97
     */
98
    var $errorcode_map = array(
99
        '01004' => DB_ERROR_TRUNCATED,
100
        '07001' => DB_ERROR_MISMATCH,
101
        '21S01' => DB_ERROR_VALUE_COUNT_ON_ROW,
102
        '21S02' => DB_ERROR_MISMATCH,
103
        '22001' => DB_ERROR_INVALID,
104
        '22003' => DB_ERROR_INVALID_NUMBER,
105
        '22005' => DB_ERROR_INVALID_NUMBER,
106
        '22008' => DB_ERROR_INVALID_DATE,
107
        '22012' => DB_ERROR_DIVZERO,
108
        '23000' => DB_ERROR_CONSTRAINT,
109
        '23502' => DB_ERROR_CONSTRAINT_NOT_NULL,
110
        '23503' => DB_ERROR_CONSTRAINT,
111
        '23504' => DB_ERROR_CONSTRAINT,
112
        '23505' => DB_ERROR_CONSTRAINT,
113
        '24000' => DB_ERROR_INVALID,
114
        '34000' => DB_ERROR_INVALID,
115
        '37000' => DB_ERROR_SYNTAX,
116
        '42000' => DB_ERROR_SYNTAX,
117
        '42601' => DB_ERROR_SYNTAX,
118
        'IM001' => DB_ERROR_UNSUPPORTED,
119
        'S0000' => DB_ERROR_NOSUCHTABLE,
120
        'S0001' => DB_ERROR_ALREADY_EXISTS,
121
        'S0002' => DB_ERROR_NOSUCHTABLE,
122
        'S0011' => DB_ERROR_ALREADY_EXISTS,
123
        'S0012' => DB_ERROR_NOT_FOUND,
124
        'S0021' => DB_ERROR_ALREADY_EXISTS,
125
        'S0022' => DB_ERROR_NOSUCHFIELD,
126
        'S1009' => DB_ERROR_INVALID,
127
        'S1090' => DB_ERROR_INVALID,
128
        'S1C00' => DB_ERROR_NOT_CAPABLE,
129
    );
130
 
131
    /**
132
     * The raw database connection created by PHP
133
     * @var resource
134
     */
135
    var $connection;
136
 
137
    /**
138
     * The DSN information for connecting to a database
139
     * @var array
140
     */
141
    var $dsn = array();
142
 
143
 
144
    /**
145
     * The number of rows affected by a data manipulation query
146
     * @var integer
147
     * @access private
148
     */
149
    var $affected = 0;
150
 
151
 
152
    // }}}
153
    // {{{ constructor
154
 
155
    /**
187 mathias 156
     * This constructor calls <kbd>parent::__construct()</kbd>
94 jpm 157
     *
158
     * @return void
159
     */
187 mathias 160
    function __construct()
94 jpm 161
    {
187 mathias 162
        parent::__construct();
94 jpm 163
    }
164
 
165
    // }}}
166
    // {{{ connect()
167
 
168
    /**
169
     * Connect to the database server, log in and open the database
170
     *
171
     * Don't call this method directly.  Use DB::connect() instead.
172
     *
173
     * PEAR DB's odbc driver supports the following extra DSN options:
174
     *   + cursor  The type of cursor to be used for this connection.
175
     *
176
     * @param array $dsn         the data source name
177
     * @param bool  $persistent  should the connection be persistent?
178
     *
179
     * @return int  DB_OK on success. A DB_Error object on failure.
180
     */
181
    function connect($dsn, $persistent = false)
182
    {
183
        if (!PEAR::loadExtension('odbc')) {
184
            return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
185
        }
186
 
187
        $this->dsn = $dsn;
188
        if ($dsn['dbsyntax']) {
189
            $this->dbsyntax = $dsn['dbsyntax'];
190
        }
191
        switch ($this->dbsyntax) {
192
            case 'access':
193
            case 'db2':
194
            case 'solid':
195
                $this->features['transactions'] = true;
196
                break;
197
            case 'navision':
198
                $this->features['limit'] = false;
199
        }
200
 
201
        /*
202
         * This is hear for backwards compatibility. Should have been using
203
         * 'database' all along, but prior to 1.6.0RC3 'hostspec' was used.
204
         */
205
        if ($dsn['database']) {
206
            $odbcdsn = $dsn['database'];
207
        } elseif ($dsn['hostspec']) {
208
            $odbcdsn = $dsn['hostspec'];
209
        } else {
210
            $odbcdsn = 'localhost';
211
        }
212
 
213
        $connect_function = $persistent ? 'odbc_pconnect' : 'odbc_connect';
214
 
215
        if (empty($dsn['cursor'])) {
216
            $this->connection = @$connect_function($odbcdsn, $dsn['username'],
217
                                                   $dsn['password']);
218
        } else {
219
            $this->connection = @$connect_function($odbcdsn, $dsn['username'],
220
                                                   $dsn['password'],
221
                                                   $dsn['cursor']);
222
        }
223
 
224
        if (!is_resource($this->connection)) {
225
            return $this->raiseError(DB_ERROR_CONNECT_FAILED,
226
                                     null, null, null,
227
                                     $this->errorNative());
228
        }
229
        return DB_OK;
230
    }
231
 
232
    // }}}
233
    // {{{ disconnect()
234
 
235
    /**
236
     * Disconnects from the database server
237
     *
238
     * @return bool  TRUE on success, FALSE on failure
239
     */
240
    function disconnect()
241
    {
242
        $err = @odbc_close($this->connection);
243
        $this->connection = null;
244
        return $err;
245
    }
246
 
247
    // }}}
248
    // {{{ simpleQuery()
249
 
250
    /**
251
     * Sends a query to the database server
252
     *
253
     * @param string  the SQL query string
254
     *
255
     * @return mixed  + a PHP result resrouce for successful SELECT queries
256
     *                + the DB_OK constant for other successful queries
257
     *                + a DB_Error object on failure
258
     */
259
    function simpleQuery($query)
260
    {
261
        $this->last_query = $query;
262
        $query = $this->modifyQuery($query);
263
        $result = @odbc_exec($this->connection, $query);
264
        if (!$result) {
265
            return $this->odbcRaiseError(); // XXX ERRORMSG
266
        }
267
        // Determine which queries that should return data, and which
268
        // should return an error code only.
187 mathias 269
        if ($this->_checkManip($query)) {
94 jpm 270
            $this->affected = $result; // For affectedRows()
271
            return DB_OK;
272
        }
273
        $this->affected = 0;
274
        return $result;
275
    }
276
 
277
    // }}}
278
    // {{{ nextResult()
279
 
280
    /**
281
     * Move the internal odbc result pointer to the next available result
282
     *
283
     * @param a valid fbsql result resource
284
     *
285
     * @access public
286
     *
287
     * @return true if a result is available otherwise return false
288
     */
289
    function nextResult($result)
290
    {
291
        return @odbc_next_result($result);
292
    }
293
 
294
    // }}}
295
    // {{{ fetchInto()
296
 
297
    /**
298
     * Places a row from the result set into the given array
299
     *
300
     * Formating of the array and the data therein are configurable.
301
     * See DB_result::fetchInto() for more information.
302
     *
303
     * This method is not meant to be called directly.  Use
304
     * DB_result::fetchInto() instead.  It can't be declared "protected"
305
     * because DB_result is a separate object.
306
     *
307
     * @param resource $result    the query result resource
308
     * @param array    $arr       the referenced array to put the data in
309
     * @param int      $fetchmode how the resulting array should be indexed
310
     * @param int      $rownum    the row number to fetch (0 = first row)
311
     *
312
     * @return mixed  DB_OK on success, NULL when the end of a result set is
313
     *                 reached or on failure
314
     *
315
     * @see DB_result::fetchInto()
316
     */
317
    function fetchInto($result, &$arr, $fetchmode, $rownum = null)
318
    {
319
        $arr = array();
320
        if ($rownum !== null) {
321
            $rownum++; // ODBC first row is 1
322
            if (version_compare(phpversion(), '4.2.0', 'ge')) {
323
                $cols = @odbc_fetch_into($result, $arr, $rownum);
324
            } else {
325
                $cols = @odbc_fetch_into($result, $rownum, $arr);
326
            }
327
        } else {
328
            $cols = @odbc_fetch_into($result, $arr);
329
        }
330
        if (!$cols) {
331
            return null;
332
        }
333
        if ($fetchmode !== DB_FETCHMODE_ORDERED) {
334
            for ($i = 0; $i < count($arr); $i++) {
335
                $colName = @odbc_field_name($result, $i+1);
336
                $a[$colName] = $arr[$i];
337
            }
338
            if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
339
                $a = array_change_key_case($a, CASE_LOWER);
340
            }
341
            $arr = $a;
342
        }
343
        if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
344
            $this->_rtrimArrayValues($arr);
345
        }
346
        if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
347
            $this->_convertNullArrayValuesToEmpty($arr);
348
        }
349
        return DB_OK;
350
    }
351
 
352
    // }}}
353
    // {{{ freeResult()
354
 
355
    /**
356
     * Deletes the result set and frees the memory occupied by the result set
357
     *
358
     * This method is not meant to be called directly.  Use
359
     * DB_result::free() instead.  It can't be declared "protected"
360
     * because DB_result is a separate object.
361
     *
362
     * @param resource $result  PHP's query result resource
363
     *
364
     * @return bool  TRUE on success, FALSE if $result is invalid
365
     *
366
     * @see DB_result::free()
367
     */
368
    function freeResult($result)
369
    {
187 mathias 370
        return is_resource($result) ? odbc_free_result($result) : false;
94 jpm 371
    }
372
 
373
    // }}}
374
    // {{{ numCols()
375
 
376
    /**
377
     * Gets the number of columns in a result set
378
     *
379
     * This method is not meant to be called directly.  Use
380
     * DB_result::numCols() instead.  It can't be declared "protected"
381
     * because DB_result is a separate object.
382
     *
383
     * @param resource $result  PHP's query result resource
384
     *
385
     * @return int  the number of columns.  A DB_Error object on failure.
386
     *
387
     * @see DB_result::numCols()
388
     */
389
    function numCols($result)
390
    {
391
        $cols = @odbc_num_fields($result);
392
        if (!$cols) {
393
            return $this->odbcRaiseError();
394
        }
395
        return $cols;
396
    }
397
 
398
    // }}}
399
    // {{{ affectedRows()
400
 
401
    /**
402
     * Determines the number of rows affected by a data maniuplation query
403
     *
404
     * 0 is returned for queries that don't manipulate data.
405
     *
406
     * @return int  the number of rows.  A DB_Error object on failure.
407
     */
408
    function affectedRows()
409
    {
410
        if (empty($this->affected)) {  // In case of SELECT stms
411
            return 0;
412
        }
413
        $nrows = @odbc_num_rows($this->affected);
414
        if ($nrows == -1) {
415
            return $this->odbcRaiseError();
416
        }
417
        return $nrows;
418
    }
419
 
420
    // }}}
421
    // {{{ numRows()
422
 
423
    /**
424
     * Gets the number of rows in a result set
425
     *
426
     * Not all ODBC drivers support this functionality.  If they don't
427
     * a DB_Error object for DB_ERROR_UNSUPPORTED is returned.
428
     *
429
     * This method is not meant to be called directly.  Use
430
     * DB_result::numRows() instead.  It can't be declared "protected"
431
     * because DB_result is a separate object.
432
     *
433
     * @param resource $result  PHP's query result resource
434
     *
435
     * @return int  the number of rows.  A DB_Error object on failure.
436
     *
437
     * @see DB_result::numRows()
438
     */
439
    function numRows($result)
440
    {
441
        $nrows = @odbc_num_rows($result);
442
        if ($nrows == -1) {
443
            return $this->odbcRaiseError(DB_ERROR_UNSUPPORTED);
444
        }
445
        if ($nrows === false) {
446
            return $this->odbcRaiseError();
447
        }
448
        return $nrows;
449
    }
450
 
451
    // }}}
452
    // {{{ quoteIdentifier()
453
 
454
    /**
455
     * Quotes a string so it can be safely used as a table or column name
456
     *
457
     * Use 'mssql' as the dbsyntax in the DB DSN only if you've unchecked
458
     * "Use ANSI quoted identifiers" when setting up the ODBC data source.
459
     *
460
     * @param string $str  identifier name to be quoted
461
     *
462
     * @return string  quoted identifier string
463
     *
464
     * @see DB_common::quoteIdentifier()
465
     * @since Method available since Release 1.6.0
466
     */
467
    function quoteIdentifier($str)
468
    {
469
        switch ($this->dsn['dbsyntax']) {
470
            case 'access':
471
                return '[' . $str . ']';
472
            case 'mssql':
473
            case 'sybase':
474
                return '[' . str_replace(']', ']]', $str) . ']';
475
            case 'mysql':
476
            case 'mysqli':
477
                return '`' . $str . '`';
478
            default:
479
                return '"' . str_replace('"', '""', $str) . '"';
480
        }
481
    }
482
 
483
    // }}}
484
    // {{{ nextId()
485
 
486
    /**
487
     * Returns the next free id in a sequence
488
     *
489
     * @param string  $seq_name  name of the sequence
490
     * @param boolean $ondemand  when true, the seqence is automatically
491
     *                            created if it does not exist
492
     *
493
     * @return int  the next id number in the sequence.
494
     *               A DB_Error object on failure.
495
     *
496
     * @see DB_common::nextID(), DB_common::getSequenceName(),
497
     *      DB_odbc::createSequence(), DB_odbc::dropSequence()
498
     */
499
    function nextId($seq_name, $ondemand = true)
500
    {
501
        $seqname = $this->getSequenceName($seq_name);
502
        $repeat = 0;
503
        do {
504
            $this->pushErrorHandling(PEAR_ERROR_RETURN);
505
            $result = $this->query("update ${seqname} set id = id + 1");
506
            $this->popErrorHandling();
507
            if ($ondemand && DB::isError($result) &&
508
                $result->getCode() == DB_ERROR_NOSUCHTABLE) {
509
                $repeat = 1;
510
                $this->pushErrorHandling(PEAR_ERROR_RETURN);
511
                $result = $this->createSequence($seq_name);
512
                $this->popErrorHandling();
513
                if (DB::isError($result)) {
514
                    return $this->raiseError($result);
515
                }
516
                $result = $this->query("insert into ${seqname} (id) values(0)");
517
            } else {
518
                $repeat = 0;
519
            }
520
        } while ($repeat);
521
 
522
        if (DB::isError($result)) {
523
            return $this->raiseError($result);
524
        }
525
 
526
        $result = $this->query("select id from ${seqname}");
527
        if (DB::isError($result)) {
528
            return $result;
529
        }
530
 
531
        $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
532
        if (DB::isError($row || !$row)) {
533
            return $row;
534
        }
535
 
536
        return $row[0];
537
    }
538
 
539
    /**
540
     * Creates a new sequence
541
     *
542
     * @param string $seq_name  name of the new sequence
543
     *
544
     * @return int  DB_OK on success.  A DB_Error object on failure.
545
     *
546
     * @see DB_common::createSequence(), DB_common::getSequenceName(),
547
     *      DB_odbc::nextID(), DB_odbc::dropSequence()
548
     */
549
    function createSequence($seq_name)
550
    {
551
        return $this->query('CREATE TABLE '
552
                            . $this->getSequenceName($seq_name)
553
                            . ' (id integer NOT NULL,'
554
                            . ' PRIMARY KEY(id))');
555
    }
556
 
557
    // }}}
558
    // {{{ dropSequence()
559
 
560
    /**
561
     * Deletes a sequence
562
     *
563
     * @param string $seq_name  name of the sequence to be deleted
564
     *
565
     * @return int  DB_OK on success.  A DB_Error object on failure.
566
     *
567
     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
568
     *      DB_odbc::nextID(), DB_odbc::createSequence()
569
     */
570
    function dropSequence($seq_name)
571
    {
572
        return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
573
    }
574
 
575
    // }}}
576
    // {{{ autoCommit()
577
 
578
    /**
579
     * Enables or disables automatic commits
580
     *
581
     * @param bool $onoff  true turns it on, false turns it off
582
     *
583
     * @return int  DB_OK on success.  A DB_Error object if the driver
584
     *               doesn't support auto-committing transactions.
585
     */
586
    function autoCommit($onoff = false)
587
    {
588
        if (!@odbc_autocommit($this->connection, $onoff)) {
589
            return $this->odbcRaiseError();
590
        }
591
        return DB_OK;
592
    }
593
 
594
    // }}}
595
    // {{{ commit()
596
 
597
    /**
598
     * Commits the current transaction
599
     *
600
     * @return int  DB_OK on success.  A DB_Error object on failure.
601
     */
602
    function commit()
603
    {
604
        if (!@odbc_commit($this->connection)) {
605
            return $this->odbcRaiseError();
606
        }
607
        return DB_OK;
608
    }
609
 
610
    // }}}
611
    // {{{ rollback()
612
 
613
    /**
614
     * Reverts the current transaction
615
     *
616
     * @return int  DB_OK on success.  A DB_Error object on failure.
617
     */
618
    function rollback()
619
    {
620
        if (!@odbc_rollback($this->connection)) {
621
            return $this->odbcRaiseError();
622
        }
623
        return DB_OK;
624
    }
625
 
626
    // }}}
627
    // {{{ odbcRaiseError()
628
 
629
    /**
630
     * Produces a DB_Error object regarding the current problem
631
     *
632
     * @param int $errno  if the error is being manually raised pass a
633
     *                     DB_ERROR* constant here.  If this isn't passed
634
     *                     the error information gathered from the DBMS.
635
     *
636
     * @return object  the DB_Error object
637
     *
638
     * @see DB_common::raiseError(),
639
     *      DB_odbc::errorNative(), DB_common::errorCode()
640
     */
641
    function odbcRaiseError($errno = null)
642
    {
643
        if ($errno === null) {
644
            switch ($this->dbsyntax) {
645
                case 'access':
646
                    if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
647
                        $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
648
                    } else {
649
                        // Doing this in case mode changes during runtime.
650
                        $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
651
                    }
652
 
653
                    $native_code = odbc_error($this->connection);
654
 
655
                    // S1000 is for "General Error."  Let's be more specific.
656
                    if ($native_code == 'S1000') {
657
                        $errormsg = odbc_errormsg($this->connection);
658
                        static $error_regexps;
659
                        if (!isset($error_regexps)) {
660
                            $error_regexps = array(
661
                                '/includes related records.$/i'  => DB_ERROR_CONSTRAINT,
662
                                '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
663
                            );
664
                        }
665
                        foreach ($error_regexps as $regexp => $code) {
666
                            if (preg_match($regexp, $errormsg)) {
667
                                return $this->raiseError($code,
668
                                        null, null, null,
669
                                        $native_code . ' ' . $errormsg);
670
                            }
671
                        }
672
                        $errno = DB_ERROR;
673
                    } else {
674
                        $errno = $this->errorCode($native_code);
675
                    }
676
                    break;
677
                default:
678
                    $errno = $this->errorCode(odbc_error($this->connection));
679
            }
680
        }
681
        return $this->raiseError($errno, null, null, null,
682
                                 $this->errorNative());
683
    }
684
 
685
    // }}}
686
    // {{{ errorNative()
687
 
688
    /**
689
     * Gets the DBMS' native error code and message produced by the last query
690
     *
691
     * @return string  the DBMS' error code and message
692
     */
693
    function errorNative()
694
    {
695
        if (!is_resource($this->connection)) {
696
            return @odbc_error() . ' ' . @odbc_errormsg();
697
        }
698
        return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
699
    }
700
 
701
    // }}}
702
    // {{{ tableInfo()
703
 
704
    /**
705
     * Returns information about a table or a result set
706
     *
707
     * @param object|string  $result  DB_result object from a query or a
708
     *                                 string containing the name of a table.
709
     *                                 While this also accepts a query result
710
     *                                 resource identifier, this behavior is
711
     *                                 deprecated.
712
     * @param int            $mode    a valid tableInfo mode
713
     *
714
     * @return array  an associative array with the information requested.
715
     *                 A DB_Error object on failure.
716
     *
717
     * @see DB_common::tableInfo()
718
     * @since Method available since Release 1.7.0
719
     */
720
    function tableInfo($result, $mode = null)
721
    {
722
        if (is_string($result)) {
723
            /*
724
             * Probably received a table name.
725
             * Create a result resource identifier.
726
             */
727
            $id = @odbc_exec($this->connection, "SELECT * FROM $result");
728
            if (!$id) {
729
                return $this->odbcRaiseError();
730
            }
731
            $got_string = true;
732
        } elseif (isset($result->result)) {
733
            /*
734
             * Probably received a result object.
735
             * Extract the result resource identifier.
736
             */
737
            $id = $result->result;
738
            $got_string = false;
739
        } else {
740
            /*
741
             * Probably received a result resource identifier.
742
             * Copy it.
743
             * Deprecated.  Here for compatibility only.
744
             */
745
            $id = $result;
746
            $got_string = false;
747
        }
748
 
749
        if (!is_resource($id)) {
750
            return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
751
        }
752
 
753
        if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
754
            $case_func = 'strtolower';
755
        } else {
756
            $case_func = 'strval';
757
        }
758
 
759
        $count = @odbc_num_fields($id);
760
        $res   = array();
761
 
762
        if ($mode) {
763
            $res['num_fields'] = $count;
764
        }
765
 
766
        for ($i = 0; $i < $count; $i++) {
767
            $col = $i + 1;
768
            $res[$i] = array(
769
                'table' => $got_string ? $case_func($result) : '',
770
                'name'  => $case_func(@odbc_field_name($id, $col)),
771
                'type'  => @odbc_field_type($id, $col),
772
                'len'   => @odbc_field_len($id, $col),
773
                'flags' => '',
774
            );
775
            if ($mode & DB_TABLEINFO_ORDER) {
776
                $res['order'][$res[$i]['name']] = $i;
777
            }
778
            if ($mode & DB_TABLEINFO_ORDERTABLE) {
779
                $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
780
            }
781
        }
782
 
783
        // free the result only if we were called on a table
784
        if ($got_string) {
785
            @odbc_free_result($id);
786
        }
787
        return $res;
788
    }
789
 
790
    // }}}
791
    // {{{ getSpecialQuery()
792
 
793
    /**
794
     * Obtains the query string needed for listing a given type of objects
795
     *
796
     * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
797
     *
798
     * @param string $type  the kind of objects you want to retrieve
799
     *
800
     * @return string  the list of objects requested
801
     *
802
     * @access protected
803
     * @see DB_common::getListOf()
804
     * @since Method available since Release 1.7.0
805
     */
806
    function getSpecialQuery($type)
807
    {
808
        switch ($type) {
809
            case 'databases':
810
                if (!function_exists('odbc_data_source')) {
811
                    return null;
812
                }
813
                $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
814
                if (is_array($res)) {
815
                    $out = array($res['server']);
816
                    while($res = @odbc_data_source($this->connection,
817
                                                   SQL_FETCH_NEXT))
818
                    {
819
                        $out[] = $res['server'];
820
                    }
821
                    return $out;
822
                } else {
823
                    return $this->odbcRaiseError();
824
                }
825
                break;
826
            case 'tables':
827
            case 'schema.tables':
828
                $keep = 'TABLE';
829
                break;
830
            case 'views':
831
                $keep = 'VIEW';
832
                break;
833
            default:
834
                return null;
835
        }
836
 
837
        /*
838
         * Removing non-conforming items in the while loop rather than
839
         * in the odbc_tables() call because some backends choke on this:
840
         *     odbc_tables($this->connection, '', '', '', 'TABLE')
841
         */
842
        $res  = @odbc_tables($this->connection);
843
        if (!$res) {
844
            return $this->odbcRaiseError();
845
        }
846
        $out = array();
847
        while ($row = odbc_fetch_array($res)) {
848
            if ($row['TABLE_TYPE'] != $keep) {
849
                continue;
850
            }
851
            if ($type == 'schema.tables') {
852
                $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
853
            } else {
854
                $out[] = $row['TABLE_NAME'];
855
            }
856
        }
857
        return $out;
858
    }
859
 
860
    // }}}
861
 
862
}
863
 
864
/*
865
 * Local variables:
866
 * tab-width: 4
867
 * c-basic-offset: 4
868
 * End:
869
 */
870
 
871
?>