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