]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/mysql.php
Harmonize file footer
[SourceForge/phpwiki.git] / lib / pear / DB / mysql.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2004 The PHP Group                                |
7 // +----------------------------------------------------------------------+
8 // | This source file is subject to version 2.02 of the PHP license,      |
9 // | that is bundled with this package in the file LICENSE, and is        |
10 // | available at through the world-wide-web at                           |
11 // | http://www.php.net/license/2_02.txt.                                 |
12 // | If you did not receive a copy of the PHP license and are unable to   |
13 // | obtain it through the world-wide-web, please send a note to          |
14 // | license@php.net so we can mail you a copy immediately.               |
15 // +----------------------------------------------------------------------+
16 // | Author: Stig Bakken <ssb@php.net>                                    |
17 // | Maintainer: Daniel Convissor <danielc@php.net>                       |
18 // +----------------------------------------------------------------------+
19 //
20 // $Id$
21
22
23 // XXX legend:
24 //
25 // XXX ERRORMSG: The error message from the mysql function should
26 //               be registered here.
27 //
28 // TODO/wishlist:
29 // longReadlen
30 // binmode
31
32
33 require_once 'DB/common.php';
34
35 /**
36  * Database independent query interface definition for PHP's MySQL
37  * extension.
38  *
39  * This is for MySQL versions 4.0 and below.
40  *
41  * @package  DB
42  * @version  $Id$
43  * @category Database
44  * @author   Stig Bakken <ssb@php.net>
45  */
46 class DB_mysql extends DB_common
47 {
48     // {{{ properties
49
50     var $connection;
51     var $phptype, $dbsyntax;
52     var $prepare_tokens = array();
53     var $prepare_types = array();
54     var $num_rows = array();
55     var $transaction_opcount = 0;
56     var $autocommit = true;
57     var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
58     var $_db = false;
59
60     // }}}
61     // {{{ constructor
62
63     /**
64      * DB_mysql constructor.
65      *
66      * @access public
67      */
68     function DB_mysql()
69     {
70         $this->DB_common();
71         $this->phptype = 'mysql';
72         $this->dbsyntax = 'mysql';
73         $this->features = array(
74             'prepare' => false,
75             'pconnect' => true,
76             'transactions' => true,
77             'limit' => 'alter'
78         );
79         $this->errorcode_map = array(
80             1004 => DB_ERROR_CANNOT_CREATE,
81             1005 => DB_ERROR_CANNOT_CREATE,
82             1006 => DB_ERROR_CANNOT_CREATE,
83             1007 => DB_ERROR_ALREADY_EXISTS,
84             1008 => DB_ERROR_CANNOT_DROP,
85             1022 => DB_ERROR_ALREADY_EXISTS,
86             1046 => DB_ERROR_NODBSELECTED,
87             1050 => DB_ERROR_ALREADY_EXISTS,
88             1051 => DB_ERROR_NOSUCHTABLE,
89             1054 => DB_ERROR_NOSUCHFIELD,
90             1062 => DB_ERROR_ALREADY_EXISTS,
91             1064 => DB_ERROR_SYNTAX,
92             1100 => DB_ERROR_NOT_LOCKED,
93             1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
94             1146 => DB_ERROR_NOSUCHTABLE,
95             1048 => DB_ERROR_CONSTRAINT,
96             1216 => DB_ERROR_CONSTRAINT
97         );
98     }
99
100     // }}}
101     // {{{ connect()
102
103     /**
104      * Connect to a database and log in as the specified user.
105      *
106      * @param $dsn the data source name (see DB::parseDSN for syntax)
107      * @param $persistent (optional) whether the connection should
108      *        be persistent
109      * @access public
110      * @return int DB_OK on success, a DB error on failure
111      */
112     function connect($dsninfo, $persistent = false)
113     {
114         if (!DB::assertExtension('mysql')) {
115             return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
116         }
117         $this->dsn = $dsninfo;
118         if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') {
119             $dbhost = ':' . $dsninfo['socket'];
120         } else {
121             $dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
122             if ($dsninfo['port']) {
123                 $dbhost .= ':' . $dsninfo['port'];
124             }
125         }
126
127         $connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
128
129         if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) {
130             $conn = @$connect_function($dbhost, $dsninfo['username'],
131                                        $dsninfo['password']);
132         } elseif ($dbhost && $dsninfo['username']) {
133             $conn = @$connect_function($dbhost, $dsninfo['username']);
134         } elseif ($dbhost) {
135             $conn = @$connect_function($dbhost);
136         } else {
137             $conn = false;
138         }
139         if (!$conn) {
140             if (($err = @mysql_error()) != '') {
141                 return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
142                                          null, $err);
143             } elseif (empty($php_errormsg)) {
144                 return $this->raiseError(DB_ERROR_CONNECT_FAILED);
145             } else {
146                 return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
147                                          null, $php_errormsg);
148             }
149         }
150
151         if ($dsninfo['database']) {
152             if (!@mysql_select_db($dsninfo['database'], $conn)) {
153                switch(mysql_errno($conn)) {
154                         case 1049:
155                             return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
156                                                      null, @mysql_error($conn));
157                         case 1044:
158                              return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
159                                                       null, @mysql_error($conn));
160                         default:
161                             return $this->raiseError(DB_ERROR, null, null,
162                                                      null, @mysql_error($conn));
163                     }
164             }
165             // fix to allow calls to different databases in the same script
166             $this->_db = $dsninfo['database'];
167         }
168
169         $this->connection = $conn;
170         return DB_OK;
171     }
172
173     // }}}
174     // {{{ disconnect()
175
176     /**
177      * Log out and disconnect from the database.
178      *
179      * @access public
180      *
181      * @return bool true on success, false if not connected.
182      */
183     function disconnect()
184     {
185         $ret = @mysql_close($this->connection);
186         $this->connection = null;
187         return $ret;
188     }
189
190     // }}}
191     // {{{ simpleQuery()
192
193     /**
194      * Send a query to MySQL and return the results as a MySQL resource
195      * identifier.
196      *
197      * @param the SQL query
198      *
199      * @access public
200      *
201      * @return mixed returns a valid MySQL result for successful SELECT
202      * queries, DB_OK for other successful queries.  A DB error is
203      * returned on failure.
204      */
205     function simpleQuery($query)
206     {
207         $ismanip = DB::isManip($query);
208         $this->last_query = $query;
209         $query = $this->modifyQuery($query);
210         if ($this->_db) {
211             if (!@mysql_select_db($this->_db, $this->connection)) {
212                 return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
213             }
214         }
215         if (!$this->autocommit && $ismanip) {
216             if ($this->transaction_opcount == 0) {
217                 $result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
218                 $result = @mysql_query('BEGIN', $this->connection);
219                 if (!$result) {
220                     return $this->mysqlRaiseError();
221                 }
222             }
223             $this->transaction_opcount++;
224         }
225         $result = @mysql_query($query, $this->connection);
226         if (!$result) {
227             return $this->mysqlRaiseError();
228         }
229         if (is_resource($result)) {
230             $numrows = $this->numrows($result);
231             if (is_object($numrows)) {
232                 return $numrows;
233             }
234             $this->num_rows[(int)$result] = $numrows;
235             return $result;
236         }
237         return DB_OK;
238     }
239
240     // }}}
241     // {{{ nextResult()
242
243     /**
244      * Move the internal mysql result pointer to the next available result
245      *
246      * This method has not been implemented yet.
247      *
248      * @param a valid sql result resource
249      *
250      * @access public
251      *
252      * @return false
253      */
254     function nextResult($result)
255     {
256         return false;
257     }
258
259     // }}}
260     // {{{ fetchInto()
261
262     /**
263      * Fetch a row and insert the data into an existing array.
264      *
265      * Formating of the array and the data therein are configurable.
266      * See DB_result::fetchInto() for more information.
267      *
268      * @param resource $result    query result identifier
269      * @param array    $arr       (reference) array where data from the row
270      *                            should be placed
271      * @param int      $fetchmode how the resulting array should be indexed
272      * @param int      $rownum    the row number to fetch
273      *
274      * @return mixed DB_OK on success, null when end of result set is
275      *               reached or on failure
276      *
277      * @see DB_result::fetchInto()
278      * @access private
279      */
280     function fetchInto($result, &$arr, $fetchmode, $rownum=null)
281     {
282         if ($rownum !== null) {
283             if (!@mysql_data_seek($result, $rownum)) {
284                 return null;
285             }
286         }
287         if ($fetchmode & DB_FETCHMODE_ASSOC) {
288             $arr = @mysql_fetch_array($result, MYSQL_ASSOC);
289             if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
290                 $arr = array_change_key_case($arr, CASE_LOWER);
291             }
292         } else {
293             $arr = @mysql_fetch_row($result);
294         }
295         if (!$arr) {
296             // See: http://bugs.php.net/bug.php?id=22328
297             // for why we can't check errors on fetching
298             return null;
299             /*
300             $errno = @mysql_errno($this->connection);
301             if (!$errno) {
302                 return null;
303             }
304             return $this->mysqlRaiseError($errno);
305             */
306         }
307         if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
308             /*
309              * Even though this DBMS already trims output, we do this because
310              * a field might have intentional whitespace at the end that
311              * gets removed by DB_PORTABILITY_RTRIM under another driver.
312              */
313             $this->_rtrimArrayValues($arr);
314         }
315         if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
316             $this->_convertNullArrayValuesToEmpty($arr);
317         }
318         return DB_OK;
319     }
320
321     // }}}
322     // {{{ freeResult()
323
324     /**
325      * Free the internal resources associated with $result.
326      *
327      * @param $result MySQL result identifier
328      *
329      * @access public
330      *
331      * @return bool true on success, false if $result is invalid
332      */
333     function freeResult($result)
334     {
335         unset($this->num_rows[(int)$result]);
336         return @mysql_free_result($result);
337     }
338
339     // }}}
340     // {{{ numCols()
341
342     /**
343      * Get the number of columns in a result set.
344      *
345      * @param $result MySQL result identifier
346      *
347      * @access public
348      *
349      * @return int the number of columns per row in $result
350      */
351     function numCols($result)
352     {
353         $cols = @mysql_num_fields($result);
354
355         if (!$cols) {
356             return $this->mysqlRaiseError();
357         }
358
359         return $cols;
360     }
361
362     // }}}
363     // {{{ numRows()
364
365     /**
366      * Get the number of rows in a result set.
367      *
368      * @param $result MySQL result identifier
369      *
370      * @access public
371      *
372      * @return int the number of rows in $result
373      */
374     function numRows($result)
375     {
376         $rows = @mysql_num_rows($result);
377         if ($rows === null) {
378             return $this->mysqlRaiseError();
379         }
380         return $rows;
381     }
382
383     // }}}
384     // {{{ autoCommit()
385
386     /**
387      * Enable/disable automatic commits
388      */
389     function autoCommit($onoff = false)
390     {
391         // XXX if $this->transaction_opcount > 0, we should probably
392         // issue a warning here.
393         $this->autocommit = $onoff ? true : false;
394         return DB_OK;
395     }
396
397     // }}}
398     // {{{ commit()
399
400     /**
401      * Commit the current transaction.
402      */
403     function commit()
404     {
405         if ($this->transaction_opcount > 0) {
406             if ($this->_db) {
407                 if (!@mysql_select_db($this->_db, $this->connection)) {
408                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
409                 }
410             }
411             $result = @mysql_query('COMMIT', $this->connection);
412             $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
413             $this->transaction_opcount = 0;
414             if (!$result) {
415                 return $this->mysqlRaiseError();
416             }
417         }
418         return DB_OK;
419     }
420
421     // }}}
422     // {{{ rollback()
423
424     /**
425      * Roll back (undo) the current transaction.
426      */
427     function rollback()
428     {
429         if ($this->transaction_opcount > 0) {
430             if ($this->_db) {
431                 if (!@mysql_select_db($this->_db, $this->connection)) {
432                     return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
433                 }
434             }
435             $result = @mysql_query('ROLLBACK', $this->connection);
436             $result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
437             $this->transaction_opcount = 0;
438             if (!$result) {
439                 return $this->mysqlRaiseError();
440             }
441         }
442         return DB_OK;
443     }
444
445     // }}}
446     // {{{ affectedRows()
447
448     /**
449      * Gets the number of rows affected by the data manipulation
450      * query.  For other queries, this function returns 0.
451      *
452      * @return number of rows affected by the last query
453      */
454     function affectedRows()
455     {
456         if (DB::isManip($this->last_query)) {
457             return @mysql_affected_rows($this->connection);
458         } else {
459             return 0;
460         }
461      }
462
463     // }}}
464     // {{{ errorNative()
465
466     /**
467      * Get the native error code of the last error (if any) that
468      * occured on the current connection.
469      *
470      * @access public
471      *
472      * @return int native MySQL error code
473      */
474     function errorNative()
475     {
476         return @mysql_errno($this->connection);
477     }
478
479     // }}}
480     // {{{ nextId()
481
482     /**
483      * Returns the next free id in a sequence
484      *
485      * @param string  $seq_name  name of the sequence
486      * @param boolean $ondemand  when true, the seqence is automatically
487      *                           created if it does not exist
488      *
489      * @return int  the next id number in the sequence.  DB_Error if problem.
490      *
491      * @internal
492      * @see DB_common::nextID()
493      * @access public
494      */
495     function nextId($seq_name, $ondemand = true)
496     {
497         $seqname = $this->getSequenceName($seq_name);
498         do {
499             $repeat = 0;
500             $this->pushErrorHandling(PEAR_ERROR_RETURN);
501             $result = $this->query("UPDATE ${seqname} ".
502                                    'SET id=LAST_INSERT_ID(id+1)');
503             $this->popErrorHandling();
504             if ($result == DB_OK) {
505                 /** COMMON CASE **/
506                 $id = @mysql_insert_id($this->connection);
507                 if ($id != 0) {
508                     return $id;
509                 }
510                 /** EMPTY SEQ TABLE **/
511                 // Sequence table must be empty for some reason, so fill it and return 1
512                 // Obtain a user-level lock
513                 $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
514                 if (DB::isError($result)) {
515                     return $this->raiseError($result);
516                 }
517                 if ($result == 0) {
518                     // Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
519                     return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
520                 }
521
522                 // add the default value
523                 $result = $this->query("REPLACE INTO ${seqname} VALUES (0)");
524                 if (DB::isError($result)) {
525                     return $this->raiseError($result);
526                 }
527
528                 // Release the lock
529                 $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
530                 if (DB::isError($result)) {
531                     return $this->raiseError($result);
532                 }
533                 // We know what the result will be, so no need to try again
534                 return 1;
535
536             /** ONDEMAND TABLE CREATION **/
537             } elseif ($ondemand && DB::isError($result) &&
538                 $result->getCode() == DB_ERROR_NOSUCHTABLE)
539             {
540                 $result = $this->createSequence($seq_name);
541                 if (DB::isError($result)) {
542                     return $this->raiseError($result);
543                 } else {
544                     $repeat = 1;
545                 }
546
547             /** BACKWARDS COMPAT **/
548             } elseif (DB::isError($result) &&
549                       $result->getCode() == DB_ERROR_ALREADY_EXISTS)
550             {
551                 // see _BCsequence() comment
552                 $result = $this->_BCsequence($seqname);
553                 if (DB::isError($result)) {
554                     return $this->raiseError($result);
555                 }
556                 $repeat = 1;
557             }
558         } while ($repeat);
559
560         return $this->raiseError($result);
561     }
562
563     // }}}
564     // {{{ createSequence()
565
566     /**
567      * Creates a new sequence
568      *
569      * @param string $seq_name  name of the new sequence
570      *
571      * @return int  DB_OK on success.  A DB_Error object is returned if
572      *              problems arise.
573      *
574      * @internal
575      * @see DB_common::createSequence()
576      * @access public
577      */
578     function createSequence($seq_name)
579     {
580         $seqname = $this->getSequenceName($seq_name);
581         $res = $this->query("CREATE TABLE ${seqname} ".
582                             '(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
583                             ' PRIMARY KEY(id))');
584         if (DB::isError($res)) {
585             return $res;
586         }
587         // insert yields value 1, nextId call will generate ID 2
588         $res = $this->query("INSERT INTO ${seqname} VALUES(0)");
589         if (DB::isError($res)) {
590             return $res;
591         }
592         // so reset to zero
593         return $this->query("UPDATE ${seqname} SET id = 0;");
594     }
595
596     // }}}
597     // {{{ dropSequence()
598
599     /**
600      * Deletes a sequence
601      *
602      * @param string $seq_name  name of the sequence to be deleted
603      *
604      * @return int  DB_OK on success.  DB_Error if problems.
605      *
606      * @internal
607      * @see DB_common::dropSequence()
608      * @access public
609      */
610     function dropSequence($seq_name)
611     {
612         return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
613     }
614
615     // }}}
616     // {{{ _BCsequence()
617
618     /**
619      * Backwards compatibility with old sequence emulation implementation
620      * (clean up the dupes)
621      *
622      * @param string $seqname The sequence name to clean up
623      * @return mixed DB_Error or true
624      */
625     function _BCsequence($seqname)
626     {
627         // Obtain a user-level lock... this will release any previous
628         // application locks, but unlike LOCK TABLES, it does not abort
629         // the current transaction and is much less frequently used.
630         $result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
631         if (DB::isError($result)) {
632             return $result;
633         }
634         if ($result == 0) {
635             // Failed to get the lock, can't do the conversion, bail
636             // with a DB_ERROR_NOT_LOCKED error
637             return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
638         }
639
640         $highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
641         if (DB::isError($highest_id)) {
642             return $highest_id;
643         }
644         // This should kill all rows except the highest
645         // We should probably do something if $highest_id isn't
646         // numeric, but I'm at a loss as how to handle that...
647         $result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
648         if (DB::isError($result)) {
649             return $result;
650         }
651
652         // If another thread has been waiting for this lock,
653         // it will go thru the above procedure, but will have no
654         // real effect
655         $result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
656         if (DB::isError($result)) {
657             return $result;
658         }
659         return true;
660     }
661
662     // }}}
663     // {{{ quoteIdentifier()
664
665     /**
666      * Quote a string so it can be safely used as a table or column name
667      *
668      * Quoting style depends on which database driver is being used.
669      *
670      * MySQL can't handle the backtick character (<kbd>`</kbd>) in
671      * table or column names.
672      *
673      * @param string $str  identifier name to be quoted
674      *
675      * @return string  quoted identifier string
676      *
677      * @since 1.6.0
678      * @access public
679      * @internal
680      */
681     function quoteIdentifier($str)
682     {
683         return '`' . $str . '`';
684     }
685
686     // }}}
687     // {{{ quote()
688
689     /**
690      * @deprecated  Deprecated in release 1.6.0
691      * @internal
692      */
693     function quote($str) {
694         return $this->quoteSmart($str);
695     }
696
697     // }}}
698     // {{{ escapeSimple()
699
700     /**
701      * Escape a string according to the current DBMS's standards
702      *
703      * @param string $str  the string to be escaped
704      *
705      * @return string  the escaped string
706      *
707      * @internal
708      */
709     function escapeSimple($str) {
710         if (function_exists('mysql_real_escape_string')) {
711             return @mysql_real_escape_string($str, $this->connection);
712         } else {
713             return @mysql_escape_string($str);
714         }
715     }
716
717     // }}}
718     // {{{ modifyQuery()
719
720     function modifyQuery($query)
721     {
722         if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
723             // "DELETE FROM table" gives 0 affected rows in MySQL.
724             // This little hack lets you know how many rows were deleted.
725             if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
726                 $query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
727                                       'DELETE FROM \1 WHERE 1=1', $query);
728             }
729         }
730         return $query;
731     }
732
733     // }}}
734     // {{{ modifyLimitQuery()
735
736     function modifyLimitQuery($query, $from, $count)
737     {
738         if (DB::isManip($query)) {
739             return $query . " LIMIT $count";
740         } else {
741             return $query . " LIMIT $from, $count";
742         }
743     }
744
745     // }}}
746     // {{{ mysqlRaiseError()
747
748     /**
749      * Gather information about an error, then use that info to create a
750      * DB error object and finally return that object.
751      *
752      * @param  integer  $errno  PEAR error number (usually a DB constant) if
753      *                          manually raising an error
754      * @return object  DB error object
755      * @see DB_common::errorCode()
756      * @see DB_common::raiseError()
757      */
758     function mysqlRaiseError($errno = null)
759     {
760         if ($errno === null) {
761             if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
762                 $this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
763                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
764                 $this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
765             } else {
766                 // Doing this in case mode changes during runtime.
767                 $this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
768                 $this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
769                 $this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
770             }
771             $errno = $this->errorCode(mysql_errno($this->connection));
772         }
773         return $this->raiseError($errno, null, null, null,
774                                  @mysql_errno($this->connection) . ' ** ' .
775                                  @mysql_error($this->connection));
776     }
777
778     // }}}
779     // {{{ tableInfo()
780
781     /**
782      * Returns information about a table or a result set.
783      *
784      * @param object|string  $result  DB_result object from a query or a
785      *                                string containing the name of a table
786      * @param int            $mode    a valid tableInfo mode
787      * @return array  an associative array with the information requested
788      *                or an error object if something is wrong
789      * @access public
790      * @internal
791      * @see DB_common::tableInfo()
792      */
793     function tableInfo($result, $mode = null) {
794         if (isset($result->result)) {
795             /*
796              * Probably received a result object.
797              * Extract the result resource identifier.
798              */
799             $id = $result->result;
800             $got_string = false;
801         } elseif (is_string($result)) {
802             /*
803              * Probably received a table name.
804              * Create a result resource identifier.
805              */
806             $id = @mysql_list_fields($this->dsn['database'],
807                                      $result, $this->connection);
808             $got_string = true;
809         } else {
810             /*
811              * Probably received a result resource identifier.
812              * Copy it.
813              * Deprecated.  Here for compatibility only.
814              */
815             $id = $result;
816             $got_string = false;
817         }
818
819         if (!is_resource($id)) {
820             return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
821         }
822
823         if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
824             $case_func = 'strtolower';
825         } else {
826             $case_func = 'strval';
827         }
828
829         $count = @mysql_num_fields($id);
830
831         // made this IF due to performance (one if is faster than $count if's)
832         if (!$mode) {
833             for ($i=0; $i<$count; $i++) {
834                 $res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
835                 $res[$i]['name']  = $case_func(@mysql_field_name($id, $i));
836                 $res[$i]['type']  = @mysql_field_type($id, $i);
837                 $res[$i]['len']   = @mysql_field_len($id, $i);
838                 $res[$i]['flags'] = @mysql_field_flags($id, $i);
839             }
840         } else { // full
841             $res['num_fields']= $count;
842
843             for ($i=0; $i<$count; $i++) {
844                 $res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
845                 $res[$i]['name']  = $case_func(@mysql_field_name($id, $i));
846                 $res[$i]['type']  = @mysql_field_type($id, $i);
847                 $res[$i]['len']   = @mysql_field_len($id, $i);
848                 $res[$i]['flags'] = @mysql_field_flags($id, $i);
849
850                 if ($mode & DB_TABLEINFO_ORDER) {
851                     $res['order'][$res[$i]['name']] = $i;
852                 }
853                 if ($mode & DB_TABLEINFO_ORDERTABLE) {
854                     $res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
855                 }
856             }
857         }
858
859         // free the result only if we were called on a table
860         if ($got_string) {
861             @mysql_free_result($id);
862         }
863         return $res;
864     }
865
866     // }}}
867     // {{{ getSpecialQuery()
868
869     /**
870      * Returns the query needed to get some backend info
871      * @param string $type What kind of info you want to retrieve
872      * @return string The SQL query string
873      */
874     function getSpecialQuery($type)
875     {
876         switch ($type) {
877             case 'tables':
878                 return 'SHOW TABLES';
879             case 'views':
880                 return DB_ERROR_NOT_CAPABLE;
881             case 'users':
882                 $sql = 'select distinct User from user';
883                 if ($this->dsn['database'] != 'mysql') {
884                     $dsn = $this->dsn;
885                     $dsn['database'] = 'mysql';
886                     if (DB::isError($db = DB::connect($dsn))) {
887                         return $db;
888                     }
889                     $sql = $db->getCol($sql);
890                     $db->disconnect();
891                     // XXX Fixme the mysql driver should take care of this
892                     if (!@mysql_select_db($this->dsn['database'], $this->connection)) {
893                         return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
894                     }
895                 }
896                 return $sql;
897             case 'databases':
898                 return 'SHOW DATABASES';
899             default:
900                 return null;
901         }
902     }
903
904     // }}}
905
906 }
907
908 /*
909  * Local variables:
910  * tab-width: 8
911  * c-basic-offset: 4
912  * End:
913  */
914
915 ?>