]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/pear/DB/common.php
Include (and use) latest PEAR DB code from the PEAR CVS repository.
[SourceForge/phpwiki.git] / lib / pear / DB / common.php
1 <?php
2 //
3 // +----------------------------------------------------------------------+
4 // | PHP Version 4                                                        |
5 // +----------------------------------------------------------------------+
6 // | Copyright (c) 1997-2002 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 // | Authors: Stig Bakken <ssb@fast.no>                                   |
17 // |                                                                      |
18 // +----------------------------------------------------------------------+
19 //
20 // Based on code from the PHP CVS repository.  The only modifications made
21 // have been modification of the include paths.
22 //
23 rcs_id('$Id: common.php,v 1.1 2002-01-28 04:01:57 dairiki Exp $');
24 rcs_id('From Pear CVS: Id: common.php,v 1.78 2002/01/19 07:46:24 cox Exp');
25 //
26 // Base class for DB implementations.
27 //
28
29 /**
30  * DB_common is a base class for DB implementations, and must be
31  * inherited by all such.
32  */
33
34 class DB_common extends PEAR
35 {
36     // {{{ properties
37     /**
38     * assoc of capabilities for this DB implementation
39     * $features['limit'] =>  'emulate' => emulate with fetch row by number
40     *                        'alter'   => alter the query
41     *                        false     => skip rows
42     * @var array
43     */
44     var $features;
45
46     /**
47     * assoc mapping native error codes to DB ones
48     * @var array
49     */
50     var $errorcode_map;
51
52     /**
53     * DB type (mysql, oci8, odbc etc.)
54     * @var string
55     */
56     var $type;
57
58     /**
59     * @var string
60     */
61     var $prepare_tokens;
62
63     /**
64     * @var string
65     */
66     var $prepare_types;
67
68     /**
69     * @var string
70     */
71     var $prepared_queries;
72
73     /**
74     * @var integer
75     */
76     var $prepare_maxstmt = 0;
77
78     /**
79     * @var string
80     */
81     var $last_query = '';
82
83     /**
84     * @var integer
85     */
86     var $fetchmode = DB_FETCHMODE_ORDERED;
87
88     /**
89     * @var string
90     */
91     var $fetchmode_object_class = 'stdClass';
92
93     /**
94     * $options["persistent"] -> boolean persistent connection true|false?
95     * $options["optimize"] -> string 'performance' or 'portability'
96     * $options["debug"] -> integer numeric debug level
97     * @var array
98     */
99     var $options = array(
100         'persistent' => false,
101         'optimize' => 'performance',
102         'debug' => 0
103     );
104
105     /**
106     * DB handle
107     * @var resource
108     */
109     var $dbh;
110
111     // }}}
112     // {{{ toString()
113     /**
114     * String conversation
115     *
116     * @return string
117     * @access private
118     */
119     function toString()
120     {
121         $info = get_class($this);
122         $info .=  ": (phptype=" . $this->phptype .
123                   ", dbsyntax=" . $this->dbsyntax .
124                   ")";
125
126         if ($this->connection) {
127             $info .= " [connected]";
128         }
129
130         return $info;
131     }
132
133     // }}}
134     // {{{ constructor
135     /**
136     * Constructor
137     */
138     function DB_common()
139     {
140         $this->PEAR('DB_Error');
141         $this->features = array();
142         $this->errorcode_map = array();
143         $this->fetchmode = DB_FETCHMODE_ORDERED;
144     }
145
146     // }}}
147     // {{{ quoteString()
148
149     /**
150      * Quotes a string so it can be safely used within string delimiters
151      * in a query (preserved for compatibility issues, quote() is preffered).
152      *
153      * @return string quoted string
154      * @access public
155      * @see quote()
156      */
157     function quoteString($string)
158     {
159         $string = $this->quote($string);
160         if ($string{0} == "'") {
161             return substr($string, 1, -1);
162         }
163         return $string;
164     }
165
166     /**
167      * Quotes a string so it can be safely used in a query. It will return
168      * the string with single quotes around. Other backend quote styles
169      * should override this method.
170      *
171      * @param string $string the input string to quote
172      *
173      * @return string The NULL string or the string quotes
174      *                in magic_quote_sybase style
175      */
176     function quote($string)
177     {
178         return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'";
179     }
180
181     // }}}
182     // {{{ provides()
183
184     /**
185      * Tell whether a DB implementation or its backend extension
186      * supports a given feature.
187      *
188      * @param array $feature name of the feature (see the DB class doc)
189      * @return bool whether this DB implementation supports $feature
190      * @access public
191      */
192
193     function provides($feature)
194     {
195         return $this->features[$feature];
196     }
197
198     // }}}
199     // {{{ errorCode()
200
201     /**
202      * Map native error codes to DB's portable ones.  Requires that
203      * the DB implementation's constructor fills in the $errorcode_map
204      * property.
205      *
206      * @param mixed $nativecode the native error code, as returned by the backend
207      * database extension (string or integer)
208      *
209      * @return int a portable DB error code, or FALSE if this DB
210      * implementation has no mapping for the given error code.
211      *
212      * @access public
213      */
214
215     function errorCode($nativecode)
216     {
217         if (isset($this->errorcode_map[$nativecode])) {
218             return $this->errorcode_map[$nativecode];
219         }
220
221         //php_error(E_WARNING, get_class($this)."::errorCode: no mapping for $nativecode");
222         // Fall back to DB_ERROR if there was no mapping.
223
224         return DB_ERROR;
225     }
226
227     // }}}
228     // {{{ errorMessage()
229
230     /**
231      * Map a DB error code to a textual message.  This is actually
232      * just a wrapper for DB::errorMessage().
233      *
234      * @param integer $dbcode the DB error code
235      *
236      * @return string the corresponding error message, of FALSE
237      * if the error code was unknown
238      *
239      * @access public
240      */
241
242     function errorMessage($dbcode)
243     {
244         return DB::errorMessage($this->errorcode_map[$dbcode]);
245     }
246
247     // }}}
248     // {{{ raiseError()
249
250     /**
251      * This method is used to communicate an error and invoke error
252      * callbacks etc.  Basically a wrapper for PEAR::raiseError
253      * without the message string.
254      *
255      * @param mixed    integer error code, or a PEAR error object (all
256      *                 other parameters are ignored if this parameter is
257      *                 an object
258      *
259      * @param int      error mode, see PEAR_Error docs
260      *
261      * @param mixed    If error mode is PEAR_ERROR_TRIGGER, this is the
262      *                 error level (E_USER_NOTICE etc).  If error mode is
263      *                 PEAR_ERROR_CALLBACK, this is the callback function,
264      *                 either as a function name, or as an array of an
265      *                 object and method name.  For other error modes this
266      *                 parameter is ignored.
267      *
268      * @param string   Extra debug information.  Defaults to the last
269      *                 query and native error code.
270      *
271      * @param mixed    Native error code, integer or string depending the
272      *                 backend.
273      *
274      * @return object  a PEAR error object
275      *
276      * @access public
277      * @see PEAR_Error
278      */
279     function &raiseError($code = DB_ERROR, $mode = null, $options = null,
280                          $userinfo = null, $nativecode = null)
281     {
282         // The error is yet a DB error object
283         if (is_object($code)) {
284             return PEAR::raiseError($code, null, null, null, null, null, true);
285         }
286
287         if ($userinfo === null) {
288             $userinfo = $this->last_query;
289         }
290
291         if ($nativecode) {
292             $userinfo .= " [nativecode=$nativecode]";
293         }
294
295         return PEAR::raiseError(null, $code, $mode, $options, $userinfo,
296                                   'DB_Error', true);
297     }
298
299     // }}}
300     // {{{ setFetchMode()
301
302     /**
303      * Sets which fetch mode should be used by default on queries
304      * on this connection.
305      *
306      * @param integer $fetchmode DB_FETCHMODE_ORDERED or
307      *        DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with
308      *        DB_FETCHMODE_FLIPPED.
309      *
310      * @param string $object_class The class of the object
311      *                      to be returned by the fetch methods when
312      *                      the DB_FETCHMODE_OBJECT mode is selected.
313      *                      If no class is specified by default a cast
314      *                      to object from the assoc array row will be done.
315      *                      There is also the posibility to use and extend the
316      *                      'DB_Row' class.
317      *
318      * @see DB_FETCHMODE_ORDERED
319      * @see DB_FETCHMODE_ASSOC
320      * @see DB_FETCHMODE_FLIPPED
321      * @see DB_FETCHMODE_OBJECT
322      * @see DB_Row::DB_Row()
323      * @access public
324      */
325
326     function setFetchMode($fetchmode, $object_class = null)
327     {
328         switch ($fetchmode) {
329             case DB_FETCHMODE_OBJECT:
330                 if ($object_class) {
331                     $this->fetchmode_object_class = $object_class;
332                 }
333             case DB_FETCHMODE_ORDERED:
334             case DB_FETCHMODE_ASSOC:
335                 $this->fetchmode = $fetchmode;
336                 break;
337             default:
338                 return $this->raiseError('invalid fetchmode mode');
339         }
340     }
341
342     // }}}
343     // {{{ setOption()
344     /**
345     * set the option for the db class
346     *
347     * @param string $option option name
348     * @param mixed  $value value for the option
349     *
350     * @return mixed DB_OK or DB_Error
351     */
352     function setOption($option, $value)
353     {
354         if (isset($this->options[$option])) {
355             $this->options[$option] = $value;
356             return DB_OK;
357         }
358         return $this->raiseError("unknown option $option");
359     }
360
361     // }}}
362     // {{{ getOption()
363     /**
364     * returns the value of an option
365     *
366     * @param string $option option name
367     *
368     * @return mixed the option value
369     */
370     function getOption($option)
371     {
372         if (isset($this->options[$option])) {
373             return $this->options[$option];
374         }
375         return $this->raiseError("unknown option $option");
376     }
377
378     // }}}
379     // {{{ prepare()
380
381     /**
382     * Prepares a query for multiple execution with execute().
383     * With some database backends, this is emulated.
384     * prepare() requires a generic query as string like
385     * "INSERT INTO numbers VALUES(?,?,?)". The ? are wildcards.
386     * Types of wildcards:
387     *   ? - a quoted scalar value, i.e. strings, integers
388     *   & - requires a file name, the content of the file
389     *       insert into the query (i.e. saving binary data
390     *       in a db)
391     *   ! - value is inserted 'as is'
392     *
393     * @param string the query to prepare
394     *
395     * @return resource handle for the query
396     *
397     * @access public
398     * @see execute
399     */
400
401     function prepare($query)
402     {
403         $tokens = split("[\&\?\!]", $query);
404         $token = 0;
405         $types = array();
406
407         for ($i = 0; $i < strlen($query); $i++) {
408             switch ($query[$i]) {
409                 case '?':
410                     $types[$token++] = DB_PARAM_SCALAR;
411                     break;
412                 case '&':
413                     $types[$token++] = DB_PARAM_OPAQUE;
414                     break;
415                 case '!':
416                     $types[$token++] = DB_PARAM_MISC;
417                     break;
418             }
419         }
420
421         $this->prepare_tokens[] = &$tokens;
422         end($this->prepare_tokens);
423
424         $k = key($this->prepare_tokens);
425         $this->prepare_types[$k] = $types;
426         $this->prepared_queries[$k] = &$query;
427
428         return $k;
429     }
430
431     // }}}
432     // {{{ execute()
433     /**
434     * Executes a prepared SQL query
435     * With execute() the generic query of prepare is
436     * assigned with the given data array. The values
437     * of the array inserted into the query in the same
438     * order like the array order
439     *
440     * @param resource $stmt query handle from prepare()
441     * @param array    $data numeric array containing the
442     *                       data to insert into the query
443     *
444     * @return mixed  a new DB_Result or a DB_Error when fail
445     *
446     * @access public
447     * @see prepare()
448     */
449     function execute($stmt, $data = false)
450     {
451         $realquery = $this->executeEmulateQuery($stmt, $data);
452         if (DB::isError($realquery)) {
453             return $realquery;
454         }
455         $result = $this->simpleQuery($realquery);
456         if (DB::isError($result) || $result === DB_OK) {
457             return $result;
458         } else {
459             return new DB_result($this, $result);
460         }
461     }
462
463     // }}}
464     // {{{ executeEmulateQuery()
465
466     /**
467     * Emulates the execute statement, when not supported
468     *
469     * @param resource $stmt query handle from prepare()
470     * @param array    $data numeric array containing the
471     *                       data to insert into the query
472     *
473     * @return mixed a string containing the real query run when emulating
474     * prepare/execute.  A DB error code is returned on failure.
475     *
476     * @access private
477     * @see execute()
478     */
479
480     function executeEmulateQuery($stmt, $data = false)
481     {
482         $p = &$this->prepare_tokens;
483
484         if (!isset($this->prepare_tokens[$stmt]) ||
485             !is_array($this->prepare_tokens[$stmt]) ||
486             !sizeof($this->prepare_tokens[$stmt]))
487         {
488             return $this->raiseError(DB_ERROR_INVALID);
489         }
490
491         $qq = &$this->prepare_tokens[$stmt];
492         $qp = sizeof($qq) - 1;
493
494         if ((!$data && $qp > 0) ||
495             (!is_array($data) && $qp > 1) ||
496             (is_array($data) && $qp > sizeof($data)))
497         {
498             $this->last_query = $this->prepared_queries[$stmt];
499             return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
500         }
501
502         $realquery = $qq[0];
503         for ($i = 0; $i < $qp; $i++) {
504             $type = $this->prepare_types[$stmt][$i];
505             if ($type == DB_PARAM_OPAQUE) {
506                 if (is_array($data)) {
507                     $fp = fopen($data[$i], 'r');
508                 } else {
509                     $fp = fopen($data, 'r');
510                 }
511
512                 $pdata = '';
513
514                 if ($fp) {
515                     while (($buf = fread($fp, 4096)) != false) {
516                         $pdata .= $buf;
517                     }
518                 }
519             } else {
520                 if (is_array($data)) {
521                     $pdata = &$data[$i];
522                 } else {
523                     $pdata = &$data;
524                 }
525             }
526
527             $realquery .= ($type != DB_PARAM_MISC) ? $this->quote($pdata) : $pdata;
528             $realquery .= $qq[$i + 1];
529         }
530
531         return $realquery;
532     }
533
534     // }}}
535     // {{{ executeMultiple()
536
537     /**
538     * This function does several execute() calls on the same
539     * statement handle.  $data must be an array indexed numerically
540     * from 0, one execute call is done for every "row" in the array.
541     *
542     * If an error occurs during execute(), executeMultiple() does not
543     * execute the unfinished rows, but rather returns that error.
544     *
545     * @param resource $stmt query handle from prepare()
546     * @param array    $data numeric array containing the
547     *                       data to insert into the query
548     *
549     * @return mixed DB_OK or DB_Error
550     *
551     * @access public
552     * @see prepare(), execute()
553     */
554
555     function executeMultiple( $stmt, &$data )
556     {
557         for($i = 0; $i < sizeof( $data ); $i++) {
558             $res = $this->execute($stmt, $data[$i]);
559             if (DB::isError($res)) {
560                 return $res;
561             }
562         }
563         return DB_OK;
564     }
565
566     // }}}
567     // {{{ modifyQuery()
568
569     /**
570      * This method is used by backends to alter queries for various
571      * reasons.  It is defined here to assure that all implementations
572      * have this method defined.
573      *
574      * @param string $query  query to modify
575      *
576      * @return the new (modified) query
577      *
578      * @access private
579      */
580     function modifyQuery($query) {
581         return $query;
582     }
583
584     // }}}
585     // {{{ modifyLimitQuery()
586     /**
587     * This method is used by backends to alter limited queries
588     *
589     * @param string  $query query to modify
590     * @param integer $from  the row to start to fetching
591     * @param integer $count the numbers of rows to fetch
592     *
593     * @return the new (modified) query
594     *
595     * @access private
596     */
597
598     function modifyLimitQuery($query, $from, $count)
599     {
600         return $query;
601     }
602
603     // }}}
604     // {{{ query()
605
606     /**
607      * Send a query to the database and return any results with a
608      * DB_result object.
609      *
610      * @access public
611      *
612      * @param string $query  the SQL query or the statement to prepare
613      * @param string $params the data to be added to the query
614      * @return mixed a DB_result object or DB_OK on success, a DB
615      *                error on failure
616      *
617      * @see DB::isError
618      * @see DB_common::prepare
619      * @see DB_common::execute
620      */
621     function &query($query, $params = array()) {
622         if (sizeof($params) > 0) {
623             $sth = $this->prepare($query);
624             if (DB::isError($sth)) {
625                 return $sth;
626             }
627             return $this->execute($sth, $params);
628         } else {
629             $result = $this->simpleQuery($query);
630             if (DB::isError($result) || $result === DB_OK) {
631                 return $result;
632             } else {
633                 return new DB_result($this, $result);
634             }
635         }
636     }
637
638     // }}}
639     // {{{ limitQuery()
640     /**
641     * Generates a limited query
642     * *EXPERIMENTAL*
643     *
644     * @param string  $query query
645     * @param integer $from  the row to start to fetching
646     * @param integer $count the numbers of rows to fetch
647     *
648     * @return mixed a DB_Result object or a DB_Error
649     *
650     * @access public
651     */
652     function limitQuery($query, $from, $count)
653     {
654         $query  = $this->modifyLimitQuery($query, $from, $count);
655         $result = $this->simpleQuery($query);
656         if (DB::isError($result) || $result === DB_OK) {
657             return $result;
658         } else {
659             $res_obj =& new DB_result($this, $result);
660             $res_obj->limit_from  = $from;
661             $res_obj->limit_count = $count;
662             return $res_obj;
663         }
664     }
665
666     // }}}
667     // {{{ getOne()
668
669     /**
670      * Fetch the first column of the first row of data returned from
671      * a query.  Takes care of doing the query and freeing the results
672      * when finished.
673      *
674      * @param string $query the SQL query
675      * @param array $params if supplied, prepare/execute will be used
676      *        with this array as execute parameters
677      *
678      * @return mixed DB_Error or the returned value of the query
679      *
680      * @access public
681      */
682
683     function &getOne($query, $params = array())
684     {
685         settype($params, "array");
686         if (sizeof($params) > 0) {
687             $sth = $this->prepare($query);
688             if (DB::isError($sth)) {
689                 return $sth;
690             }
691             $res = $this->execute($sth, $params);
692         } else {
693             $res = $this->query($query);
694         }
695
696         if (DB::isError($res)) {
697             return $res;
698         }
699
700         $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
701         if ($err !== DB_OK) {
702             return $err;
703         }
704
705         $res->free();
706
707         if (isset($sth)) {
708             $this->freeResult($sth);
709         }
710
711         return $row[0];
712     }
713
714     // }}}
715     // {{{ getRow()
716
717     /**
718      * Fetch the first row of data returned from a query.  Takes care
719      * of doing the query and freeing the results when finished.
720      *
721      * @param string $query the SQL query
722      * @param integer $fetchmode the fetch mode to use
723      * @param array $params array if supplied, prepare/execute will be used
724      *        with this array as execute parameters
725      * @access public
726      * @return array the first row of results as an array indexed from
727      * 0, or a DB error code.
728      */
729
730     function &getRow($query,
731                      $params = null,
732                      $fetchmode = DB_FETCHMODE_DEFAULT)
733     {
734         // compat check, the params and fetchmode parameters used to
735         // have the opposite order
736         if (!is_array($params)) {
737             if (is_array($fetchmode)) {
738                 $tmp = $params;
739                 $params = $fetchmode;
740                 $fetchmode = $tmp;
741             } elseif ($params !== null) {
742                 $fetchmode = $params;
743                 $params = null;
744             }
745         }
746         $params = (empty($params)) ? array() : $params;
747         $fetchmode = (empty($fetchmode)) ? DB_FETCHMODE_DEFAULT : $fetchmode;
748         settype($params, 'array');
749         if (sizeof($params) > 0) {
750             $sth = $this->prepare($query);
751             if (DB::isError($sth)) {
752                 return $sth;
753             }
754             $res = $this->execute($sth, $params);
755         } else {
756             $res = $this->query($query);
757         }
758
759         if (DB::isError($res)) {
760             return $res;
761         }
762
763         $err = $res->fetchInto($row, $fetchmode);
764
765         if ($err !== DB_OK) {
766             return $err;
767         }
768         $res->free();
769
770         if (isset($sth)) {
771             $this->freeResult($sth);
772         }
773
774         return $row;
775     }
776
777     // }}}
778     // {{{ getCol()
779
780     /**
781      * Fetch a single column from a result set and return it as an
782      * indexed array.
783      *
784      * @param string $query the SQL query
785      *
786      * @param mixed $col which column to return (integer [column number,
787      * starting at 0] or string [column name])
788      *
789      * @param array $params array if supplied, prepare/execute will be used
790      *        with this array as execute parameters
791      * @access public
792      *
793      * @return array an indexed array with the data from the first
794      * row at index 0, or a DB error code.
795      */
796
797     function &getCol($query, $col = 0, $params = array())
798     {
799         settype($params, "array");
800         if (sizeof($params) > 0) {
801             $sth = $this->prepare($query);
802
803             if (DB::isError($sth)) {
804                 return $sth;
805             }
806
807             $res = $this->execute($sth, $params);
808         } else {
809             $res = $this->query($query);
810         }
811
812         if (DB::isError($res)) {
813             return $res;
814         }
815
816         $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
817         $ret = array();
818
819         while (is_array($row = $res->fetchRow($fetchmode))) {
820             $ret[] = $row[$col];
821         }
822         if (DB::isError($row)) {
823             $ret = $row;
824         }
825         $res->free();
826
827         if (isset($sth)) {
828             $this->freeResult($sth);
829         }
830
831         return $ret;
832     }
833
834     // }}}
835     // {{{ getAssoc()
836
837     /**
838      * Fetch the entire result set of a query and return it as an
839      * associative array using the first column as the key.
840      *
841      * If the result set contains more than two columns, the value
842      * will be an array of the values from column 2-n.  If the result
843      * set contains only two columns, the returned value will be a
844      * scalar with the value of the second column (unless forced to an
845      * array with the $force_array parameter).  A DB error code is
846      * returned on errors.  If the result set contains fewer than two
847      * columns, a DB_ERROR_TRUNCATED error is returned.
848      *
849      * For example, if the table "mytable" contains:
850      *
851      *  ID      TEXT       DATE
852      * --------------------------------
853      *  1       'one'      944679408
854      *  2       'two'      944679408
855      *  3       'three'    944679408
856      *
857      * Then the call getAssoc('SELECT id,text FROM mytable') returns:
858      *   array(
859      *     '1' => 'one',
860      *     '2' => 'two',
861      *     '3' => 'three',
862      *   )
863      *
864      * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
865      *   array(
866      *     '1' => array('one', '944679408'),
867      *     '2' => array('two', '944679408'),
868      *     '3' => array('three', '944679408')
869      *   )
870      *
871      * If the more than one row occurs with the same value in the
872      * first column, the last row overwrites all previous ones by
873      * default.  Use the $group parameter if you don't want to
874      * overwrite like this.  Example:
875      *
876      * getAssoc('SELECT category,id,name FROM mytable', false, null,
877                 DB_FETCHMODE_ASSOC, true) returns:
878      *   array(
879      *     '1' => array(array('id' => '4', 'name' => 'number four'),
880      *                  array('id' => '6', 'name' => 'number six')
881      *            ),
882      *     '9' => array(array('id' => '4', 'name' => 'number four'),
883      *                  array('id' => '6', 'name' => 'number six')
884      *            )
885      *   )
886      *
887      * Keep in mind that database functions in PHP usually return string
888      * values for results regardless of the database's internal type.
889      *
890      * @param string $query the SQL query
891      *
892      * @param boolean $force_array used only when the query returns
893      * exactly two columns.  If true, the values of the returned array
894      * will be one-element arrays instead of scalars.
895      *
896      * @param array $params array if supplied, prepare/execute will be used
897      *        with this array as execute parameters
898      *
899      * @param boolean $group if true, the values of the returned array
900      *                       is wrapped in another array.  If the same
901      *                       key value (in the first column) repeats
902      *                       itself, the values will be appended to
903      *                       this array instead of overwriting the
904      *                       existing values.
905      *
906      * @access public
907      *
908      * @return array associative array with results from the query.
909      */
910
911     function &getAssoc($query, $force_array = false, $params = array(),
912                        $fetchmode = DB_FETCHMODE_ORDERED, $group = false)
913     {
914         settype($params, "array");
915         if (sizeof($params) > 0) {
916             $sth = $this->prepare($query);
917
918             if (DB::isError($sth)) {
919                 return $sth;
920             }
921
922             $res = $this->execute($sth, $params);
923         } else {
924             $res = $this->query($query);
925         }
926
927         if (DB::isError($res)) {
928             return $res;
929         }
930
931         $cols = $res->numCols();
932
933         if ($cols < 2) {
934             return $this->raiseError(DB_ERROR_TRUNCATED);
935         }
936
937         $results = array();
938
939         if ($cols > 2 || $force_array) {
940             // return array values
941             // XXX this part can be optimized
942             if ($fetchmode == DB_FETCHMODE_ASSOC) {
943                 while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
944                     reset($row);
945                     $key = current($row);
946                     unset($row[key($row)]);
947                     if ($group) {
948                         $results[$key][] = $row;
949                     } else {
950                         $results[$key] = $row;
951                     }
952                 }
953             } else {
954                 while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
955                     // we shift away the first element to get
956                     // indices running from 0 again
957                     $key = array_shift($row);
958                     if ($group) {
959                         $results[$key][] = $row;
960                     } else {
961                         $results[$key] = $row;
962                     }
963                 }
964             }
965             if (DB::isError($row)) {
966                 $results = $row;
967             }
968         } else {
969             // return scalar values
970             while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
971                 if ($group) {
972                     $results[$row[0]][] = $row[1];
973                 } else {
974                     $results[$row[0]] = $row[1];
975                 }
976             }
977             if (DB::isError($row)) {
978                 $results = $row;
979             }
980         }
981
982         $res->free();
983
984         if (isset($sth)) {
985             $this->freeResult($sth);
986         }
987
988         return $results;
989     }
990
991     // }}}
992     // {{{ getAll()
993
994     /**
995      * Fetch all the rows returned from a query.
996      *
997      * @param string $query the SQL query
998      *
999      * @param array $params array if supplied, prepare/execute will be used
1000      *        with this array as execute parameters
1001      * @param integer $fetchmode the fetch mode to use
1002      *
1003      * @access public
1004      * @return array an nested array, or a DB error
1005      */
1006
1007     function &getAll($query,
1008                      $params = null,
1009                      $fetchmode = DB_FETCHMODE_DEFAULT)
1010     {
1011         // compat check, the params and fetchmode parameters used to
1012         // have the opposite order
1013         if (!is_array($params)) {
1014             if (is_array($fetchmode)) {
1015                 $tmp = $params;
1016                 $params = $fetchmode;
1017                 $fetchmode = $tmp;
1018             } elseif ($params !== null) {
1019                 $fetchmode = $params;
1020                 $params = null;
1021             }
1022         }
1023         $params = (empty($params)) ? array() : $params;
1024         $fetchmode = (empty($fetchmode)) ? DB_FETCHMODE_DEFAULT : $fetchmode;
1025         settype($params, "array");
1026         if (sizeof($params) > 0) {
1027             $sth = $this->prepare($query);
1028
1029             if (DB::isError($sth)) {
1030                 return $sth;
1031             }
1032
1033             $res = $this->execute($sth, $params);
1034         } else {
1035             $res = $this->query($query);
1036         }
1037
1038         if (DB::isError($res)) {
1039             return $res;
1040         }
1041
1042         $results = array();
1043         $this->pushErrorHandling(PEAR_ERROR_RETURN);
1044         while (DB_OK === $res->fetchInto($row, $fetchmode)) {
1045             if ($fetchmode & DB_FETCHMODE_FLIPPED) {
1046                 foreach ($row as $key => $val) {
1047                     $results[$key][] = $val;
1048                 }
1049             } else {
1050                 $results[] = $row;
1051             }
1052         }
1053         $this->popErrorHandling();
1054
1055         $res->free();
1056
1057         if (isset($sth)) {
1058             $this->freeResult($sth);
1059         }
1060         if (DB::isError($row)) {
1061             return $this->raiseError($row);
1062         }
1063         return $results;
1064     }
1065
1066     // }}}
1067     // {{{ autoCommit()
1068     /**
1069     * enable automatic Commit
1070     *
1071     * @param boolean $onoff
1072     * @return mixed DB_Error
1073     *
1074     * @access public
1075     */
1076     function autoCommit($onoff=false)
1077     {
1078         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1079     }
1080
1081     // }}}
1082     // {{{ commit()
1083     /**
1084     * starts a Commit
1085     *
1086     * @return mixed DB_Error
1087     *
1088     * @access public
1089     */
1090     function commit()
1091     {
1092         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1093     }
1094
1095     // }}}
1096     // {{{ rollback()
1097     /**
1098     * starts a rollback
1099     *
1100     * @return mixed DB_Error
1101     *
1102     * @access public
1103     */
1104     function rollback()
1105     {
1106         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1107     }
1108
1109     // }}}
1110     // {{{ numRows()
1111     /**
1112     * returns the number of rows in a result object
1113     *
1114     * @param object DB_Result the result object to check
1115     *
1116     * @return mixed DB_Error or the number of rows
1117     *
1118     * @access public
1119     */
1120     function numRows($result)
1121     {
1122         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1123     }
1124
1125     // }}}
1126     // {{{ affectedRows()
1127     /**
1128     * returns the affected rows of a query
1129     *
1130     * @return mixed DB_Error or number of rows
1131     *
1132     * @access public
1133     */
1134     function affectedRows()
1135     {
1136         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1137     }
1138
1139     // }}}
1140     // {{{ errorNative()
1141     /**
1142     * returns an errormessage, provides by the database
1143     *
1144     * @return mixed DB_Error or message
1145     *
1146     * @access public
1147     */
1148     function errorNative()
1149     {
1150         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1151     }
1152
1153     // }}}
1154     // {{{ nextId()
1155     /**
1156     * returns the next free id of a sequence
1157     *
1158     * @param string  $seq_name name of the sequence
1159     * @param boolean $ondemand when true the seqence is
1160     *                          automatic created, if it
1161     *                          not exists
1162     *
1163     * @return mixed DB_Error or id
1164     */
1165     function nextId($seq_name, $ondemand = true)
1166     {
1167         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1168     }
1169
1170     // }}}
1171     // {{{ createSequence()
1172     /**
1173     * creates a new sequence
1174     *
1175     * @param string $seq_name name of the new sequence
1176     *
1177     * @return mixed DB_Error
1178     *
1179     * @access public
1180     */
1181     function createSequence($seq_name)
1182     {
1183         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1184     }
1185
1186     // }}}
1187     // {{{ dropSequence()
1188     /**
1189     * deletes a sequence
1190     *
1191     * @param string $seq_name name of the sequence
1192     *
1193     * @return mixed DB_Error
1194     *
1195     * @access public
1196     */
1197     function dropSequence($seq_name)
1198     {
1199         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1200     }
1201
1202     // }}}
1203     // {{{ tableInfo()
1204     /**
1205     * returns meta data about the result set
1206     *
1207     * @param object DB_Result $result the result object to analyse
1208     * @param mixed $mode depends on implementation
1209     *
1210     * @return mixed DB_Error
1211     *
1212     * @access public
1213     */
1214     function tableInfo($result, $mode = null)
1215     {
1216         return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1217     }
1218
1219     // }}}
1220     // {{{ getTables()
1221     /**
1222     * @deprecated
1223     */
1224     function getTables()
1225     {
1226         return $this->getListOf('tables');
1227     }
1228
1229     // }}}
1230     // {{{ getListOf()
1231     /**
1232     * list internal DB info
1233     * valid values for $type are db dependent,
1234     * often: databases, users, view, functions
1235     *
1236     * @param string $type type of requested info
1237     *
1238     * @return mixed DB_Error or the requested data
1239     *
1240     * @access public
1241     */
1242     function getListOf($type)
1243     {
1244         $sql = $this->getSpecialQuery($type);
1245         if ($sql === null) {                                // No support
1246             return $this->raiseError(DB_ERROR_UNSUPPORTED);
1247         } elseif (is_int($sql) || DB::isError($sql)) {      // Previous error
1248             return $this->raiseError($sql);
1249         } elseif (is_array($sql)) {                         // Already the result
1250             return $sql;
1251         }
1252         return $this->getCol($sql);                         // Launch this query
1253     }
1254     // }}}
1255 }
1256
1257 ?>