]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - extlib/DB/odbc.php
Revert "* [Cc]an't -> [Cc]annot"
[quix0rs-gnu-social.git] / extlib / DB / odbc.php
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  *
9  * PHP versions 4 and 5
10  *
11  * LICENSE: This source file is subject to version 3.0 of the PHP license
12  * that is available through the world-wide-web at the following URI:
13  * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
14  * the PHP License and are unable to obtain it through the web, please
15  * send a note to license@php.net so we can mail you a copy immediately.
16  *
17  * @category   Database
18  * @package    DB
19  * @author     Stig Bakken <ssb@php.net>
20  * @author     Daniel Convissor <danielc@php.net>
21  * @copyright  1997-2007 The PHP Group
22  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23  * @version    CVS: $Id: odbc.php,v 1.81 2007/07/06 05:19:21 aharvey Exp $
24  * @link       http://pear.php.net/package/DB
25  */
26
27 /**
28  * Obtain the DB_common class so it can be extended from
29  */
30 require_once 'DB/common.php';
31
32 /**
33  * The methods PEAR DB uses to interact with PHP's 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>
45  * @copyright  1997-2007 The PHP Group
46  * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
47  * @version    Release: 1.7.14RC1
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     /**
156      * This constructor calls <kbd>$this->DB_common()</kbd>
157      *
158      * @return void
159      */
160     function DB_odbc()
161     {
162         $this->DB_common();
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.
269         if ($this->_checkManip($query)) {
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     {
370         return is_resource($result) ? odbc_free_result($result) : false;
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     // {{{ quote()
485
486     /**
487      * @deprecated  Deprecated in release 1.6.0
488      * @internal
489      */
490     function quote($str)
491     {
492         return $this->quoteSmart($str);
493     }
494
495     // }}}
496     // {{{ nextId()
497
498     /**
499      * Returns the next free id in a sequence
500      *
501      * @param string  $seq_name  name of the sequence
502      * @param boolean $ondemand  when true, the seqence is automatically
503      *                            created if it does not exist
504      *
505      * @return int  the next id number in the sequence.
506      *               A DB_Error object on failure.
507      *
508      * @see DB_common::nextID(), DB_common::getSequenceName(),
509      *      DB_odbc::createSequence(), DB_odbc::dropSequence()
510      */
511     function nextId($seq_name, $ondemand = true)
512     {
513         $seqname = $this->getSequenceName($seq_name);
514         $repeat = 0;
515         do {
516             $this->pushErrorHandling(PEAR_ERROR_RETURN);
517             $result = $this->query("update ${seqname} set id = id + 1");
518             $this->popErrorHandling();
519             if ($ondemand && DB::isError($result) &&
520                 $result->getCode() == DB_ERROR_NOSUCHTABLE) {
521                 $repeat = 1;
522                 $this->pushErrorHandling(PEAR_ERROR_RETURN);
523                 $result = $this->createSequence($seq_name);
524                 $this->popErrorHandling();
525                 if (DB::isError($result)) {
526                     return $this->raiseError($result);
527                 }
528                 $result = $this->query("insert into ${seqname} (id) values(0)");
529             } else {
530                 $repeat = 0;
531             }
532         } while ($repeat);
533
534         if (DB::isError($result)) {
535             return $this->raiseError($result);
536         }
537
538         $result = $this->query("select id from ${seqname}");
539         if (DB::isError($result)) {
540             return $result;
541         }
542
543         $row = $result->fetchRow(DB_FETCHMODE_ORDERED);
544         if (DB::isError($row || !$row)) {
545             return $row;
546         }
547
548         return $row[0];
549     }
550
551     /**
552      * Creates a new sequence
553      *
554      * @param string $seq_name  name of the new sequence
555      *
556      * @return int  DB_OK on success.  A DB_Error object on failure.
557      *
558      * @see DB_common::createSequence(), DB_common::getSequenceName(),
559      *      DB_odbc::nextID(), DB_odbc::dropSequence()
560      */
561     function createSequence($seq_name)
562     {
563         return $this->query('CREATE TABLE '
564                             . $this->getSequenceName($seq_name)
565                             . ' (id integer NOT NULL,'
566                             . ' PRIMARY KEY(id))');
567     }
568
569     // }}}
570     // {{{ dropSequence()
571
572     /**
573      * Deletes a sequence
574      *
575      * @param string $seq_name  name of the sequence to be deleted
576      *
577      * @return int  DB_OK on success.  A DB_Error object on failure.
578      *
579      * @see DB_common::dropSequence(), DB_common::getSequenceName(),
580      *      DB_odbc::nextID(), DB_odbc::createSequence()
581      */
582     function dropSequence($seq_name)
583     {
584         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
585     }
586
587     // }}}
588     // {{{ autoCommit()
589
590     /**
591      * Enables or disables automatic commits
592      *
593      * @param bool $onoff  true turns it on, false turns it off
594      *
595      * @return int  DB_OK on success.  A DB_Error object if the driver
596      *               doesn't support auto-committing transactions.
597      */
598     function autoCommit($onoff = false)
599     {
600         if (!@odbc_autocommit($this->connection, $onoff)) {
601             return $this->odbcRaiseError();
602         }
603         return DB_OK;
604     }
605
606     // }}}
607     // {{{ commit()
608
609     /**
610      * Commits the current transaction
611      *
612      * @return int  DB_OK on success.  A DB_Error object on failure.
613      */
614     function commit()
615     {
616         if (!@odbc_commit($this->connection)) {
617             return $this->odbcRaiseError();
618         }
619         return DB_OK;
620     }
621
622     // }}}
623     // {{{ rollback()
624
625     /**
626      * Reverts the current transaction
627      *
628      * @return int  DB_OK on success.  A DB_Error object on failure.
629      */
630     function rollback()
631     {
632         if (!@odbc_rollback($this->connection)) {
633             return $this->odbcRaiseError();
634         }
635         return DB_OK;
636     }
637
638     // }}}
639     // {{{ odbcRaiseError()
640
641     /**
642      * Produces a DB_Error object regarding the current problem
643      *
644      * @param int $errno  if the error is being manually raised pass a
645      *                     DB_ERROR* constant here.  If this isn't passed
646      *                     the error information gathered from the DBMS.
647      *
648      * @return object  the DB_Error object
649      *
650      * @see DB_common::raiseError(),
651      *      DB_odbc::errorNative(), DB_common::errorCode()
652      */
653     function odbcRaiseError($errno = null)
654     {
655         if ($errno === null) {
656             switch ($this->dbsyntax) {
657                 case 'access':
658                     if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
659                         $this->errorcode_map['07001'] = DB_ERROR_NOSUCHFIELD;
660                     } else {
661                         // Doing this in case mode changes during runtime.
662                         $this->errorcode_map['07001'] = DB_ERROR_MISMATCH;
663                     }
664
665                     $native_code = odbc_error($this->connection);
666
667                     // S1000 is for "General Error."  Let's be more specific.
668                     if ($native_code == 'S1000') {
669                         $errormsg = odbc_errormsg($this->connection);
670                         static $error_regexps;
671                         if (!isset($error_regexps)) {
672                             $error_regexps = array(
673                                 '/includes related records.$/i'  => DB_ERROR_CONSTRAINT,
674                                 '/cannot contain a Null value/i' => DB_ERROR_CONSTRAINT_NOT_NULL,
675                             );
676                         }
677                         foreach ($error_regexps as $regexp => $code) {
678                             if (preg_match($regexp, $errormsg)) {
679                                 return $this->raiseError($code,
680                                         null, null, null,
681                                         $native_code . ' ' . $errormsg);
682                             }
683                         }
684                         $errno = DB_ERROR;
685                     } else {
686                         $errno = $this->errorCode($native_code);
687                     }
688                     break;
689                 default:
690                     $errno = $this->errorCode(odbc_error($this->connection));
691             }
692         }
693         return $this->raiseError($errno, null, null, null,
694                                  $this->errorNative());
695     }
696
697     // }}}
698     // {{{ errorNative()
699
700     /**
701      * Gets the DBMS' native error code and message produced by the last query
702      *
703      * @return string  the DBMS' error code and message
704      */
705     function errorNative()
706     {
707         if (!is_resource($this->connection)) {
708             return @odbc_error() . ' ' . @odbc_errormsg();
709         }
710         return @odbc_error($this->connection) . ' ' . @odbc_errormsg($this->connection);
711     }
712
713     // }}}
714     // {{{ tableInfo()
715
716     /**
717      * Returns information about a table or a result set
718      *
719      * @param object|string  $result  DB_result object from a query or a
720      *                                 string containing the name of a table.
721      *                                 While this also accepts a query result
722      *                                 resource identifier, this behavior is
723      *                                 deprecated.
724      * @param int            $mode    a valid tableInfo mode
725      *
726      * @return array  an associative array with the information requested.
727      *                 A DB_Error object on failure.
728      *
729      * @see DB_common::tableInfo()
730      * @since Method available since Release 1.7.0
731      */
732     function tableInfo($result, $mode = null)
733     {
734         if (is_string($result)) {
735             /*
736              * Probably received a table name.
737              * Create a result resource identifier.
738              */
739             $id = @odbc_exec($this->connection, "SELECT * FROM $result");
740             if (!$id) {
741                 return $this->odbcRaiseError();
742             }
743             $got_string = true;
744         } elseif (isset($result->result)) {
745             /*
746              * Probably received a result object.
747              * Extract the result resource identifier.
748              */
749             $id = $result->result;
750             $got_string = false;
751         } else {
752             /*
753              * Probably received a result resource identifier.
754              * Copy it.
755              * Deprecated.  Here for compatibility only.
756              */
757             $id = $result;
758             $got_string = false;
759         }
760
761         if (!is_resource($id)) {
762             return $this->odbcRaiseError(DB_ERROR_NEED_MORE_DATA);
763         }
764
765         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
766             $case_func = 'strtolower';
767         } else {
768             $case_func = 'strval';
769         }
770
771         $count = @odbc_num_fields($id);
772         $res   = array();
773
774         if ($mode) {
775             $res['num_fields'] = $count;
776         }
777
778         for ($i = 0; $i < $count; $i++) {
779             $col = $i + 1;
780             $res[$i] = array(
781                 'table' => $got_string ? $case_func($result) : '',
782                 'name'  => $case_func(@odbc_field_name($id, $col)),
783                 'type'  => @odbc_field_type($id, $col),
784                 'len'   => @odbc_field_len($id, $col),
785                 'flags' => '',
786             );
787             if ($mode & DB_TABLEINFO_ORDER) {
788                 $res['order'][$res[$i]['name']] = $i;
789             }
790             if ($mode & DB_TABLEINFO_ORDERTABLE) {
791                 $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
792             }
793         }
794
795         // free the result only if we were called on a table
796         if ($got_string) {
797             @odbc_free_result($id);
798         }
799         return $res;
800     }
801
802     // }}}
803     // {{{ getSpecialQuery()
804
805     /**
806      * Obtains the query string needed for listing a given type of objects
807      *
808      * Thanks to symbol1@gmail.com and Philippe.Jausions@11abacus.com.
809      *
810      * @param string $type  the kind of objects you want to retrieve
811      *
812      * @return string  the list of objects requested
813      *
814      * @access protected
815      * @see DB_common::getListOf()
816      * @since Method available since Release 1.7.0
817      */
818     function getSpecialQuery($type)
819     {
820         switch ($type) {
821             case 'databases':
822                 if (!function_exists('odbc_data_source')) {
823                     return null;
824                 }
825                 $res = @odbc_data_source($this->connection, SQL_FETCH_FIRST);
826                 if (is_array($res)) {
827                     $out = array($res['server']);
828                     while($res = @odbc_data_source($this->connection,
829                                                    SQL_FETCH_NEXT))
830                     {
831                         $out[] = $res['server'];
832                     }
833                     return $out;
834                 } else {
835                     return $this->odbcRaiseError();
836                 }
837                 break;
838             case 'tables':
839             case 'schema.tables':
840                 $keep = 'TABLE';
841                 break;
842             case 'views':
843                 $keep = 'VIEW';
844                 break;
845             default:
846                 return null;
847         }
848
849         /*
850          * Removing non-conforming items in the while loop rather than
851          * in the odbc_tables() call because some backends choke on this:
852          *     odbc_tables($this->connection, '', '', '', 'TABLE')
853          */
854         $res  = @odbc_tables($this->connection);
855         if (!$res) {
856             return $this->odbcRaiseError();
857         }
858         $out = array();
859         while ($row = odbc_fetch_array($res)) {
860             if ($row['TABLE_TYPE'] != $keep) {
861                 continue;
862             }
863             if ($type == 'schema.tables') {
864                 $out[] = $row['TABLE_SCHEM'] . '.' . $row['TABLE_NAME'];
865             } else {
866                 $out[] = $row['TABLE_NAME'];
867             }
868         }
869         return $out;
870     }
871
872     // }}}
873
874 }
875
876 /*
877  * Local variables:
878  * tab-width: 4
879  * c-basic-offset: 4
880  * End:
881  */
882
883 ?>