source: temp/trunk/data/module/DB/common.php @ 6114

Revision 6114, 67.9 KB checked in by naka, 20 years ago (diff)

* empty log message *

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1<?php
2
3/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
5/**
6 * Contains the DB_common base class
7 *
8 * PHP versions 4 and 5
9 *
10 * LICENSE: This source file is subject to version 3.0 of the PHP license
11 * that is available through the world-wide-web at the following URI:
12 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
13 * the PHP License and are unable to obtain it through the web, please
14 * send a note to [email protected] so we can mail you a copy immediately.
15 *
16 * @category   Database
17 * @package    DB
18 * @author     Stig Bakken <[email protected]>
19 * @author     Tomas V.V. Cox <[email protected]>
20 * @author     Daniel Convissor <[email protected]>
21 * @copyright  1997-2005 The PHP Group
22 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
23 * @version    CVS: $Id$
24 * @link       http://pear.php.net/package/DB
25 */
26
27/**
28 * Obtain the PEAR class so it can be extended from
29 */
30require_once DB_PHP_DIR . '/PEAR.php';
31
32/**
33 * DB_common is the base class from which each database driver class extends
34 *
35 * All common methods are declared here.  If a given DBMS driver contains
36 * a particular method, that method will overload the one here.
37 *
38 * @category   Database
39 * @package    DB
40 * @author     Stig Bakken <[email protected]>
41 * @author     Tomas V.V. Cox <[email protected]>
42 * @author     Daniel Convissor <[email protected]>
43 * @copyright  1997-2005 The PHP Group
44 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
45 * @version    Release: @package_version@
46 * @link       http://pear.php.net/package/DB
47 */
48class DB_common extends PEAR
49{
50    // {{{ properties
51
52    /**
53     * The current default fetch mode
54     * @var integer
55     */
56    var $fetchmode = DB_FETCHMODE_ORDERED;
57
58    /**
59     * The name of the class into which results should be fetched when
60     * DB_FETCHMODE_OBJECT is in effect
61     *
62     * @var string
63     */
64    var $fetchmode_object_class = 'stdClass';
65
66    /**
67     * Was a connection present when the object was serialized()?
68     * @var bool
69     * @see DB_common::__sleep(), DB_common::__wake()
70     */
71    var $was_connected = null;
72
73    /**
74     * The most recently executed query
75     * @var string
76     */
77    var $last_query = '';
78
79    /**
80     * Run-time configuration options
81     *
82     * The 'optimize' option has been deprecated.  Use the 'portability'
83     * option instead.
84     *
85     * @var array
86     * @see DB_common::setOption()
87     */
88    var $options = array(
89        'result_buffering' => 500,
90        'persistent' => false,
91        'ssl' => false,
92        'debug' => 0,
93        'seqname_format' => '%s_seq',
94        'autofree' => false,
95        'portability' => DB_PORTABILITY_NONE,
96        'optimize' => 'performance',  // Deprecated.  Use 'portability'.
97    );
98
99    /**
100     * The parameters from the most recently executed query
101     * @var array
102     * @since Property available since Release 1.7.0
103     */
104    var $last_parameters = array();
105
106    /**
107     * The elements from each prepared statement
108     * @var array
109     */
110    var $prepare_tokens = array();
111
112    /**
113     * The data types of the various elements in each prepared statement
114     * @var array
115     */
116    var $prepare_types = array();
117
118    /**
119     * The prepared queries
120     * @var array
121     */
122    var $prepared_queries = array();
123
124
125    // }}}
126    // {{{ DB_common
127
128    /**
129     * This constructor calls <kbd>$this->PEAR('DB_Error')</kbd>
130     *
131     * @return void
132     */
133    function DB_common()
134    {
135        $this->PEAR('DB_Error');
136    }
137
138    // }}}
139    // {{{ __sleep()
140
141    /**
142     * Automatically indicates which properties should be saved
143     * when PHP's serialize() function is called
144     *
145     * @return array  the array of properties names that should be saved
146     */
147    function __sleep()
148    {
149        if ($this->connection) {
150            // Don't disconnect(), people use serialize() for many reasons
151            $this->was_connected = true;
152        } else {
153            $this->was_connected = false;
154        }
155        if (isset($this->autocommit)) {
156            return array('autocommit',
157                         'dbsyntax',
158                         'dsn',
159                         'features',
160                         'fetchmode',
161                         'fetchmode_object_class',
162                         'options',
163                         'was_connected',
164                   );
165        } else {
166            return array('dbsyntax',
167                         'dsn',
168                         'features',
169                         'fetchmode',
170                         'fetchmode_object_class',
171                         'options',
172                         'was_connected',
173                   );
174        }
175    }
176
177    // }}}
178    // {{{ __wakeup()
179
180    /**
181     * Automatically reconnects to the database when PHP's unserialize()
182     * function is called
183     *
184     * The reconnection attempt is only performed if the object was connected
185     * at the time PHP's serialize() function was run.
186     *
187     * @return void
188     */
189    function __wakeup()
190    {
191        if ($this->was_connected) {
192            $this->connect($this->dsn, $this->options);
193        }
194    }
195
196    // }}}
197    // {{{ __toString()
198
199    /**
200     * Automatic string conversion for PHP 5
201     *
202     * @return string  a string describing the current PEAR DB object
203     *
204     * @since Method available since Release 1.7.0
205     */
206    function __toString()
207    {
208        $info = strtolower(get_class($this));
209        $info .=  ': (phptype=' . $this->phptype .
210                  ', dbsyntax=' . $this->dbsyntax .
211                  ')';
212        if ($this->connection) {
213            $info .= ' [connected]';
214        }
215        return $info;
216    }
217
218    // }}}
219    // {{{ toString()
220
221    /**
222     * DEPRECATED:  String conversion method
223     *
224     * @return string  a string describing the current PEAR DB object
225     *
226     * @deprecated Method deprecated in Release 1.7.0
227     */
228    function toString()
229    {
230        return $this->__toString();
231    }
232
233    // }}}
234    // {{{ quoteString()
235
236    /**
237     * DEPRECATED: Quotes a string so it can be safely used within string
238     * delimiters in a query
239     *
240     * @param string $string  the string to be quoted
241     *
242     * @return string  the quoted string
243     *
244     * @see DB_common::quoteSmart(), DB_common::escapeSimple()
245     * @deprecated Method deprecated some time before Release 1.2
246     */
247    function quoteString($string)
248    {
249        $string = $this->quote($string);
250        if ($string{0} == "'") {
251            return substr($string, 1, -1);
252        }
253        return $string;
254    }
255
256    // }}}
257    // {{{ quote()
258
259    /**
260     * DEPRECATED: Quotes a string so it can be safely used in a query
261     *
262     * @param string $string  the string to quote
263     *
264     * @return string  the quoted string or the string <samp>NULL</samp>
265     *                  if the value submitted is <kbd>null</kbd>.
266     *
267     * @see DB_common::quoteSmart(), DB_common::escapeSimple()
268     * @deprecated Deprecated in release 1.6.0
269     */
270    function quote($string = null)
271    {
272        return ($string === null) ? 'NULL'
273                                  : "'" . str_replace("'", "''", $string) . "'";
274    }
275
276    // }}}
277    // {{{ quoteIdentifier()
278
279    /**
280     * Quotes a string so it can be safely used as a table or column name
281     *
282     * Delimiting style depends on which database driver is being used.
283     *
284     * NOTE: just because you CAN use delimited identifiers doesn't mean
285     * you SHOULD use them.  In general, they end up causing way more
286     * problems than they solve.
287     *
288     * Portability is broken by using the following characters inside
289     * delimited identifiers:
290     *   + backtick (<kbd>`</kbd>) -- due to MySQL
291     *   + double quote (<kbd>"</kbd>) -- due to Oracle
292     *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
293     *
294     * Delimited identifiers are known to generally work correctly under
295     * the following drivers:
296     *   + mssql
297     *   + mysql
298     *   + mysqli
299     *   + oci8
300     *   + odbc(access)
301     *   + odbc(db2)
302     *   + pgsql
303     *   + sqlite
304     *   + sybase (must execute <kbd>set quoted_identifier on</kbd> sometime
305     *     prior to use)
306     *
307     * InterBase doesn't seem to be able to use delimited identifiers
308     * via PHP 4.  They work fine under PHP 5.
309     *
310     * @param string $str  the identifier name to be quoted
311     *
312     * @return string  the quoted identifier
313     *
314     * @since Method available since Release 1.6.0
315     */
316    function quoteIdentifier($str)
317    {
318        return '"' . str_replace('"', '""', $str) . '"';
319    }
320
321    // }}}
322    // {{{ quoteSmart()
323
324    /**
325     * Formats input so it can be safely used in a query
326     *
327     * The output depends on the PHP data type of input and the database
328     * type being used.
329     *
330     * @param mixed $in  the data to be formatted
331     *
332     * @return mixed  the formatted data.  The format depends on the input's
333     *                 PHP type:
334     * <ul>
335     *  <li>
336     *    <kbd>input</kbd> -> <samp>returns</samp>
337     *  </li>
338     *  <li>
339     *    <kbd>null</kbd> -> the string <samp>NULL</samp>
340     *  </li>
341     *  <li>
342     *    <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
343     *  </li>
344     *  <li>
345     *    <kbd>bool</kbd> -> output depends on the driver in use
346     *    Most drivers return integers: <samp>1</samp> if
347     *    <kbd>true</kbd> or <samp>0</samp> if
348     *    <kbd>false</kbd>.
349     *    Some return strings: <samp>TRUE</samp> if
350     *    <kbd>true</kbd> or <samp>FALSE</samp> if
351     *    <kbd>false</kbd>.
352     *    Finally one returns strings: <samp>T</samp> if
353     *    <kbd>true</kbd> or <samp>F</samp> if
354     *    <kbd>false</kbd>. Here is a list of each DBMS,
355     *    the values returned and the suggested column type:
356     *    <ul>
357     *      <li>
358     *        <kbd>dbase</kbd> -> <samp>T/F</samp>
359     *        (<kbd>Logical</kbd>)
360     *      </li>
361     *      <li>
362     *        <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
363     *        (<kbd>BOOLEAN</kbd>)
364     *      </li>
365     *      <li>
366     *        <kbd>ibase</kbd> -> <samp>1/0</samp>
367     *        (<kbd>SMALLINT</kbd>) [1]
368     *      </li>
369     *      <li>
370     *        <kbd>ifx</kbd> -> <samp>1/0</samp>
371     *        (<kbd>SMALLINT</kbd>) [1]
372     *      </li>
373     *      <li>
374     *        <kbd>msql</kbd> -> <samp>1/0</samp>
375     *        (<kbd>INTEGER</kbd>)
376     *      </li>
377     *      <li>
378     *        <kbd>mssql</kbd> -> <samp>1/0</samp>
379     *        (<kbd>BIT</kbd>)
380     *      </li>
381     *      <li>
382     *        <kbd>mysql</kbd> -> <samp>1/0</samp>
383     *        (<kbd>TINYINT(1)</kbd>)
384     *      </li>
385     *      <li>
386     *        <kbd>mysqli</kbd> -> <samp>1/0</samp>
387     *        (<kbd>TINYINT(1)</kbd>)
388     *      </li>
389     *      <li>
390     *        <kbd>oci8</kbd> -> <samp>1/0</samp>
391     *        (<kbd>NUMBER(1)</kbd>)
392     *      </li>
393     *      <li>
394     *        <kbd>odbc</kbd> -> <samp>1/0</samp>
395     *        (<kbd>SMALLINT</kbd>) [1]
396     *      </li>
397     *      <li>
398     *        <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
399     *        (<kbd>BOOLEAN</kbd>)
400     *      </li>
401     *      <li>
402     *        <kbd>sqlite</kbd> -> <samp>1/0</samp>
403     *        (<kbd>INTEGER</kbd>)
404     *      </li>
405     *      <li>
406     *        <kbd>sybase</kbd> -> <samp>1/0</samp>
407     *        (<kbd>TINYINT(1)</kbd>)
408     *      </li>
409     *    </ul>
410     *    [1] Accommodate the lowest common denominator because not all
411     *    versions of have <kbd>BOOLEAN</kbd>.
412     *  </li>
413     *  <li>
414     *    other (including strings and numeric strings) ->
415     *    the data with single quotes escaped by preceeding
416     *    single quotes, backslashes are escaped by preceeding
417     *    backslashes, then the whole string is encapsulated
418     *    between single quotes
419     *  </li>
420     * </ul>
421     *
422     * @see DB_common::escapeSimple()
423     * @since Method available since Release 1.6.0
424     */
425    function quoteSmart($in)
426    {
427        if (is_int($in) || is_double($in)) {
428            return $in;
429        } elseif (is_bool($in)) {
430            return $in ? 1 : 0;
431        } elseif (is_null($in)) {
432            return 'NULL';
433        } else {
434            return "'" . $this->escapeSimple($in) . "'";
435        }
436    }
437
438    // }}}
439    // {{{ escapeSimple()
440
441    /**
442     * Escapes a string according to the current DBMS's standards
443     *
444     * In SQLite, this makes things safe for inserts/updates, but may
445     * cause problems when performing text comparisons against columns
446     * containing binary data. See the
447     * {@link http://php.net/sqlite_escape_string PHP manual} for more info.
448     *
449     * @param string $str  the string to be escaped
450     *
451     * @return string  the escaped string
452     *
453     * @see DB_common::quoteSmart()
454     * @since Method available since Release 1.6.0
455     */
456    function escapeSimple($str)
457    {
458        return str_replace("'", "''", $str);
459    }
460
461    // }}}
462    // {{{ provides()
463
464    /**
465     * Tells whether the present driver supports a given feature
466     *
467     * @param string $feature  the feature you're curious about
468     *
469     * @return bool  whether this driver supports $feature
470     */
471    function provides($feature)
472    {
473        return $this->features[$feature];
474    }
475
476    // }}}
477    // {{{ setFetchMode()
478
479    /**
480     * Sets the fetch mode that should be used by default for query results
481     *
482     * @param integer $fetchmode    DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC
483     *                               or DB_FETCHMODE_OBJECT
484     * @param string $object_class  the class name of the object to be returned
485     *                               by the fetch methods when the
486     *                               DB_FETCHMODE_OBJECT mode is selected.
487     *                               If no class is specified by default a cast
488     *                               to object from the assoc array row will be
489     *                               done.  There is also the posibility to use
490     *                               and extend the 'DB_row' class.
491     *
492     * @see DB_FETCHMODE_ORDERED, DB_FETCHMODE_ASSOC, DB_FETCHMODE_OBJECT
493     */
494    function setFetchMode($fetchmode, $object_class = 'stdClass')
495    {
496        switch ($fetchmode) {
497            case DB_FETCHMODE_OBJECT:
498                $this->fetchmode_object_class = $object_class;
499            case DB_FETCHMODE_ORDERED:
500            case DB_FETCHMODE_ASSOC:
501                $this->fetchmode = $fetchmode;
502                break;
503            default:
504                return $this->raiseError('invalid fetchmode mode');
505        }
506    }
507
508    // }}}
509    // {{{ setOption()
510
511    /**
512     * Sets run-time configuration options for PEAR DB
513     *
514     * Options, their data types, default values and description:
515     * <ul>
516     * <li>
517     * <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
518     *      <br />should results be freed automatically when there are no
519     *            more rows?
520     * </li><li>
521     * <var>result_buffering</var> <kbd>integer</kbd> = <samp>500</samp>
522     *      <br />how many rows of the result set should be buffered?
523     *      <br />In mysql: mysql_unbuffered_query() is used instead of
524     *            mysql_query() if this value is 0.  (Release 1.7.0)
525     *      <br />In oci8: this value is passed to ocisetprefetch().
526     *            (Release 1.7.0)
527     * </li><li>
528     * <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
529     *      <br />debug level
530     * </li><li>
531     * <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
532     *      <br />should the connection be persistent?
533     * </li><li>
534     * <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
535     *      <br />portability mode constant (see below)
536     * </li><li>
537     * <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
538     *      <br />the sprintf() format string used on sequence names.  This
539     *            format is applied to sequence names passed to
540     *            createSequence(), nextID() and dropSequence().
541     * </li><li>
542     * <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
543     *      <br />use ssl to connect?
544     * </li>
545     * </ul>
546     *
547     * -----------------------------------------
548     *
549     * PORTABILITY MODES
550     *
551     * These modes are bitwised, so they can be combined using <kbd>|</kbd>
552     * and removed using <kbd>^</kbd>.  See the examples section below on how
553     * to do this.
554     *
555     * <samp>DB_PORTABILITY_NONE</samp>
556     * turn off all portability features
557     *
558     * This mode gets automatically turned on if the deprecated
559     * <var>optimize</var> option gets set to <samp>performance</samp>.
560     *
561     *
562     * <samp>DB_PORTABILITY_LOWERCASE</samp>
563     * convert names of tables and fields to lower case when using
564     * <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
565     *
566     * This mode gets automatically turned on in the following databases
567     * if the deprecated option <var>optimize</var> gets set to
568     * <samp>portability</samp>:
569     * + oci8
570     *
571     *
572     * <samp>DB_PORTABILITY_RTRIM</samp>
573     * right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
574     *
575     *
576     * <samp>DB_PORTABILITY_DELETE_COUNT</samp>
577     * force reporting the number of rows deleted
578     *
579     * Some DBMS's don't count the number of rows deleted when performing
580     * simple <kbd>DELETE FROM tablename</kbd> queries.  This portability
581     * mode tricks such DBMS's into telling the count by adding
582     * <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
583     *
584     * This mode gets automatically turned on in the following databases
585     * if the deprecated option <var>optimize</var> gets set to
586     * <samp>portability</samp>:
587     * + fbsql
588     * + mysql
589     * + mysqli
590     * + sqlite
591     *
592     *
593     * <samp>DB_PORTABILITY_NUMROWS</samp>
594     * enable hack that makes <kbd>numRows()</kbd> work in Oracle
595     *
596     * This mode gets automatically turned on in the following databases
597     * if the deprecated option <var>optimize</var> gets set to
598     * <samp>portability</samp>:
599     * + oci8
600     *
601     *
602     * <samp>DB_PORTABILITY_ERRORS</samp>
603     * makes certain error messages in certain drivers compatible
604     * with those from other DBMS's
605     *
606     * + mysql, mysqli:  change unique/primary key constraints
607     *   DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
608     *
609     * + odbc(access):  MS's ODBC driver reports 'no such field' as code
610     *   07001, which means 'too few parameters.'  When this option is on
611     *   that code gets mapped to DB_ERROR_NOSUCHFIELD.
612     *   DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
613     *
614     * <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
615     * convert null values to empty strings in data output by get*() and
616     * fetch*().  Needed because Oracle considers empty strings to be null,
617     * while most other DBMS's know the difference between empty and null.
618     *
619     *
620     * <samp>DB_PORTABILITY_ALL</samp>
621     * turn on all portability features
622     *
623     * -----------------------------------------
624     *
625     * Example 1. Simple setOption() example
626     * <code>
627     * $db->setOption('autofree', true);
628     * </code>
629     *
630     * Example 2. Portability for lowercasing and trimming
631     * <code>
632     * $db->setOption('portability',
633     *                 DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
634     * </code>
635     *
636     * Example 3. All portability options except trimming
637     * <code>
638     * $db->setOption('portability',
639     *                 DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
640     * </code>
641     *
642     * @param string $option option name
643     * @param mixed  $value value for the option
644     *
645     * @return int  DB_OK on success.  A DB_Error object on failure.
646     *
647     * @see DB_common::$options
648     */
649    function setOption($option, $value)
650    {
651        if (isset($this->options[$option])) {
652            $this->options[$option] = $value;
653
654            /*
655             * Backwards compatibility check for the deprecated 'optimize'
656             * option.  Done here in case settings change after connecting.
657             */
658            if ($option == 'optimize') {
659                if ($value == 'portability') {
660                    switch ($this->phptype) {
661                        case 'oci8':
662                            $this->options['portability'] =
663                                    DB_PORTABILITY_LOWERCASE |
664                                    DB_PORTABILITY_NUMROWS;
665                            break;
666                        case 'fbsql':
667                        case 'mysql':
668                        case 'mysqli':
669                        case 'sqlite':
670                            $this->options['portability'] =
671                                    DB_PORTABILITY_DELETE_COUNT;
672                            break;
673                    }
674                } else {
675                    $this->options['portability'] = DB_PORTABILITY_NONE;
676                }
677            }
678
679            return DB_OK;
680        }
681        return $this->raiseError("unknown option $option");
682    }
683
684    // }}}
685    // {{{ getOption()
686
687    /**
688     * Returns the value of an option
689     *
690     * @param string $option  the option name you're curious about
691     *
692     * @return mixed  the option's value
693     */
694    function getOption($option)
695    {
696        if (isset($this->options[$option])) {
697            return $this->options[$option];
698        }
699        return $this->raiseError("unknown option $option");
700    }
701
702    // }}}
703    // {{{ prepare()
704
705    /**
706     * Prepares a query for multiple execution with execute()
707     *
708     * Creates a query that can be run multiple times.  Each time it is run,
709     * the placeholders, if any, will be replaced by the contents of
710     * execute()'s $data argument.
711     *
712     * Three types of placeholders can be used:
713     *   + <kbd>?</kbd>  scalar value (i.e. strings, integers).  The system
714     *                   will automatically quote and escape the data.
715     *   + <kbd>!</kbd>  value is inserted 'as is'
716     *   + <kbd>&</kbd>  requires a file name.  The file's contents get
717     *                   inserted into the query (i.e. saving binary
718     *                   data in a db)
719     *
720     * Example 1.
721     * <code>
722     * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
723     * $data = array(
724     *     "John's text",
725     *     "'it''s good'",
726     *     'filename.txt'
727     * );
728     * $res = $db->execute($sth, $data);
729     * </code>
730     *
731     * Use backslashes to escape placeholder characters if you don't want
732     * them to be interpreted as placeholders:
733     * <pre>
734     *    "UPDATE foo SET col=? WHERE col='over \& under'"
735     * </pre>
736     *
737     * With some database backends, this is emulated.
738     *
739     * {@internal ibase and oci8 have their own prepare() methods.}}
740     *
741     * @param string $query  the query to be prepared
742     *
743     * @return mixed  DB statement resource on success. A DB_Error object
744     *                 on failure.
745     *
746     * @see DB_common::execute()
747     */
748    function prepare($query)
749    {
750        $tokens   = preg_split('/((?<!\\\)[&?!])/', $query, -1,
751                               PREG_SPLIT_DELIM_CAPTURE);
752        $token     = 0;
753        $types     = array();
754        $newtokens = array();
755
756        foreach ($tokens as $val) {
757            switch ($val) {
758                case '?':
759                    $types[$token++] = DB_PARAM_SCALAR;
760                    break;
761                case '&':
762                    $types[$token++] = DB_PARAM_OPAQUE;
763                    break;
764                case '!':
765                    $types[$token++] = DB_PARAM_MISC;
766                    break;
767                default:
768                    $newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
769            }
770        }
771
772        $this->prepare_tokens[] = &$newtokens;
773        end($this->prepare_tokens);
774
775        $k = key($this->prepare_tokens);
776        $this->prepare_types[$k] = $types;
777        $this->prepared_queries[$k] = implode(' ', $newtokens);
778
779        return $k;
780    }
781
782    // }}}
783    // {{{ autoPrepare()
784
785    /**
786     * Automaticaly generates an insert or update query and pass it to prepare()
787     *
788     * @param string $table         the table name
789     * @param array  $table_fields  the array of field names
790     * @param int    $mode          a type of query to make:
791     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
792     * @param string $where         for update queries: the WHERE clause to
793     *                               append to the SQL statement.  Don't
794     *                               include the "WHERE" keyword.
795     *
796     * @return resource  the query handle
797     *
798     * @uses DB_common::prepare(), DB_common::buildManipSQL()
799     */
800    function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT,
801                         $where = false)
802    {
803        $query = $this->buildManipSQL($table, $table_fields, $mode, $where);
804        if (DB::isError($query)) {
805            return $query;
806        }
807        return $this->prepare($query);
808    }
809
810    // }}}
811    // {{{ autoExecute()
812
813    /**
814     * Automaticaly generates an insert or update query and call prepare()
815     * and execute() with it
816     *
817     * @param string $table         the table name
818     * @param array  $fields_values the associative array where $key is a
819     *                               field name and $value its value
820     * @param int    $mode          a type of query to make:
821     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
822     * @param string $where         for update queries: the WHERE clause to
823     *                               append to the SQL statement.  Don't
824     *                               include the "WHERE" keyword.
825     *
826     * @return mixed  a new DB_result object for successful SELECT queries
827     *                 or DB_OK for successul data manipulation queries.
828     *                 A DB_Error object on failure.
829     *
830     * @uses DB_common::autoPrepare(), DB_common::execute()
831     */
832    function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT,
833                         $where = false)
834    {
835        $sth = $this->autoPrepare($table, array_keys($fields_values), $mode,
836                                  $where);
837        if (DB::isError($sth)) {
838            return $sth;
839        }
840        $ret =& $this->execute($sth, array_values($fields_values));
841        $this->freePrepared($sth);
842        return $ret;
843
844    }
845
846    // }}}
847    // {{{ buildManipSQL()
848
849    /**
850     * Produces an SQL query string for autoPrepare()
851     *
852     * Example:
853     * <pre>
854     * buildManipSQL('table_sql', array('field1', 'field2', 'field3'),
855     *               DB_AUTOQUERY_INSERT);
856     * </pre>
857     *
858     * That returns
859     * <samp>
860     * INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
861     * </samp>
862     *
863     * NOTES:
864     *   - This belongs more to a SQL Builder class, but this is a simple
865     *     facility.
866     *   - Be carefull! If you don't give a $where param with an UPDATE
867     *     query, all the records of the table will be updated!
868     *
869     * @param string $table         the table name
870     * @param array  $table_fields  the array of field names
871     * @param int    $mode          a type of query to make:
872     *                               DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE
873     * @param string $where         for update queries: the WHERE clause to
874     *                               append to the SQL statement.  Don't
875     *                               include the "WHERE" keyword.
876     *
877     * @return string  the sql query for autoPrepare()
878     */
879    function buildManipSQL($table, $table_fields, $mode, $where = false)
880    {
881        if (count($table_fields) == 0) {
882            return $this->raiseError(DB_ERROR_NEED_MORE_DATA);
883        }
884        $first = true;
885        switch ($mode) {
886            case DB_AUTOQUERY_INSERT:
887                $values = '';
888                $names = '';
889                foreach ($table_fields as $value) {
890                    if ($first) {
891                        $first = false;
892                    } else {
893                        $names .= ',';
894                        $values .= ',';
895                    }
896                    $names .= $value;
897                    $values .= '?';
898                }
899                return "INSERT INTO $table ($names) VALUES ($values)";
900            case DB_AUTOQUERY_UPDATE:
901                $set = '';
902                foreach ($table_fields as $value) {
903                    if ($first) {
904                        $first = false;
905                    } else {
906                        $set .= ',';
907                    }
908                    $set .= "$value = ?";
909                }
910                $sql = "UPDATE $table SET $set";
911                if ($where) {
912                    $sql .= " WHERE $where";
913                }
914                return $sql;
915            default:
916                return $this->raiseError(DB_ERROR_SYNTAX);
917        }
918    }
919
920    // }}}
921    // {{{ execute()
922
923    /**
924     * Executes a DB statement prepared with prepare()
925     *
926     * Example 1.
927     * <code>
928     * $sth = $db->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
929     * $data = array(
930     *     "John's text",
931     *     "'it''s good'",
932     *     'filename.txt'
933     * );
934     * $res =& $db->execute($sth, $data);
935     * </code>
936     *
937     * @param resource $stmt  a DB statement resource returned from prepare()
938     * @param mixed    $data  array, string or numeric data to be used in
939     *                         execution of the statement.  Quantity of items
940     *                         passed must match quantity of placeholders in
941     *                         query:  meaning 1 placeholder for non-array
942     *                         parameters or 1 placeholder per array element.
943     *
944     * @return mixed  a new DB_result object for successful SELECT queries
945     *                 or DB_OK for successul data manipulation queries.
946     *                 A DB_Error object on failure.
947     *
948     * {@internal ibase and oci8 have their own execute() methods.}}
949     *
950     * @see DB_common::prepare()
951     */
952    function &execute($stmt, $data = array())
953    {
954        $realquery = $this->executeEmulateQuery($stmt, $data);
955        if (DB::isError($realquery)) {
956            return $realquery;
957        }
958        $result = $this->simpleQuery($realquery);
959
960        if ($result === DB_OK || DB::isError($result)) {
961            return $result;
962        } else {
963            $tmp =& new DB_result($this, $result);
964            return $tmp;
965        }
966    }
967
968    // }}}
969    // {{{ executeEmulateQuery()
970
971    /**
972     * Emulates executing prepared statements if the DBMS not support them
973     *
974     * @param resource $stmt  a DB statement resource returned from execute()
975     * @param mixed    $data  array, string or numeric data to be used in
976     *                         execution of the statement.  Quantity of items
977     *                         passed must match quantity of placeholders in
978     *                         query:  meaning 1 placeholder for non-array
979     *                         parameters or 1 placeholder per array element.
980     *
981     * @return mixed  a string containing the real query run when emulating
982     *                 prepare/execute.  A DB_Error object on failure.
983     *
984     * @access protected
985     * @see DB_common::execute()
986     */
987    function executeEmulateQuery($stmt, $data = array())
988    {
989        $stmt = (int)$stmt;
990        $data = (array)$data;
991        $this->last_parameters = $data;
992
993        if (count($this->prepare_types[$stmt]) != count($data)) {
994            $this->last_query = $this->prepared_queries[$stmt];
995            return $this->raiseError(DB_ERROR_MISMATCH);
996        }
997
998        $realquery = $this->prepare_tokens[$stmt][0];
999
1000        $i = 0;
1001        foreach ($data as $value) {
1002            if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
1003                if ($value != "") {
1004                    $realquery .= $this->quoteSmart($value);
1005                }else{
1006                    $realquery .= 'NULL';
1007                }
1008            } elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
1009                $fp = @fopen($value, 'rb');
1010                if (!$fp) {
1011                    return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
1012                }
1013                $realquery .= $this->quoteSmart(fread($fp, filesize($value)));
1014                fclose($fp);
1015            } else {
1016                $realquery .= $value;
1017            }
1018
1019            $realquery .= $this->prepare_tokens[$stmt][++$i];
1020        }
1021
1022        return $realquery;
1023    }
1024
1025    // }}}
1026    // {{{ executeMultiple()
1027
1028    /**
1029     * Performs several execute() calls on the same statement handle
1030     *
1031     * $data must be an array indexed numerically
1032     * from 0, one execute call is done for every "row" in the array.
1033     *
1034     * If an error occurs during execute(), executeMultiple() does not
1035     * execute the unfinished rows, but rather returns that error.
1036     *
1037     * @param resource $stmt  query handle from prepare()
1038     * @param array    $data  numeric array containing the
1039     *                         data to insert into the query
1040     *
1041     * @return int  DB_OK on success.  A DB_Error object on failure.
1042     *
1043     * @see DB_common::prepare(), DB_common::execute()
1044     */
1045    function executeMultiple($stmt, $data)
1046    {
1047        foreach ($data as $value) {
1048            $res =& $this->execute($stmt, $value);
1049            if (DB::isError($res)) {
1050                return $res;
1051            }
1052        }
1053        return DB_OK;
1054    }
1055
1056    // }}}
1057    // {{{ freePrepared()
1058
1059    /**
1060     * Frees the internal resources associated with a prepared query
1061     *
1062     * @param resource $stmt           the prepared statement's PHP resource
1063     * @param bool     $free_resource  should the PHP resource be freed too?
1064     *                                  Use false if you need to get data
1065     *                                  from the result set later.
1066     *
1067     * @return bool  TRUE on success, FALSE if $result is invalid
1068     *
1069     * @see DB_common::prepare()
1070     */
1071    function freePrepared($stmt, $free_resource = true)
1072    {
1073        $stmt = (int)$stmt;
1074        if (isset($this->prepare_tokens[$stmt])) {
1075            unset($this->prepare_tokens[$stmt]);
1076            unset($this->prepare_types[$stmt]);
1077            unset($this->prepared_queries[$stmt]);
1078            return true;
1079        }
1080        return false;
1081    }
1082
1083    // }}}
1084    // {{{ modifyQuery()
1085
1086    /**
1087     * Changes a query string for various DBMS specific reasons
1088     *
1089     * It is defined here to ensure all drivers have this method available.
1090     *
1091     * @param string $query  the query string to modify
1092     *
1093     * @return string  the modified query string
1094     *
1095     * @access protected
1096     * @see DB_mysql::modifyQuery(), DB_oci8::modifyQuery(),
1097     *      DB_sqlite::modifyQuery()
1098     */
1099    function modifyQuery($query)
1100    {
1101        return $query;
1102    }
1103
1104    // }}}
1105    // {{{ modifyLimitQuery()
1106
1107    /**
1108     * Adds LIMIT clauses to a query string according to current DBMS standards
1109     *
1110     * It is defined here to assure that all implementations
1111     * have this method defined.
1112     *
1113     * @param string $query   the query to modify
1114     * @param int    $from    the row to start to fetching (0 = the first row)
1115     * @param int    $count   the numbers of rows to fetch
1116     * @param mixed  $params  array, string or numeric data to be used in
1117     *                         execution of the statement.  Quantity of items
1118     *                         passed must match quantity of placeholders in
1119     *                         query:  meaning 1 placeholder for non-array
1120     *                         parameters or 1 placeholder per array element.
1121     *
1122     * @return string  the query string with LIMIT clauses added
1123     *
1124     * @access protected
1125     */
1126    function modifyLimitQuery($query, $from, $count, $params = array())
1127    {
1128        return $query;
1129    }
1130
1131    // }}}
1132    // {{{ query()
1133
1134    /**
1135     * Sends a query to the database server
1136     *
1137     * The query string can be either a normal statement to be sent directly
1138     * to the server OR if <var>$params</var> are passed the query can have
1139     * placeholders and it will be passed through prepare() and execute().
1140     *
1141     * @param string $query   the SQL query or the statement to prepare
1142     * @param mixed  $params  array, string or numeric data to be used in
1143     *                         execution of the statement.  Quantity of items
1144     *                         passed must match quantity of placeholders in
1145     *                         query:  meaning 1 placeholder for non-array
1146     *                         parameters or 1 placeholder per array element.
1147     *
1148     * @return mixed  a new DB_result object for successful SELECT queries
1149     *                 or DB_OK for successul data manipulation queries.
1150     *                 A DB_Error object on failure.
1151     *
1152     * @see DB_result, DB_common::prepare(), DB_common::execute()
1153     */
1154    function &query($query, $params = array())
1155    {
1156        if (sizeof($params) > 0) {
1157            $sth = $this->prepare($query);
1158            if (DB::isError($sth)) {
1159                return $sth;
1160            }
1161            $ret =& $this->execute($sth, $params);
1162            $this->freePrepared($sth, false);
1163            return $ret;
1164        } else {
1165            $this->last_parameters = array();
1166            $result = $this->simpleQuery($query);
1167            if ($result === DB_OK || DB::isError($result)) {
1168                return $result;
1169            } else {
1170                $tmp =& new DB_result($this, $result);
1171                return $tmp;
1172            }
1173        }
1174    }
1175
1176    // }}}
1177    // {{{ limitQuery()
1178
1179    /**
1180     * Generates and executes a LIMIT query
1181     *
1182     * @param string $query   the query
1183     * @param intr   $from    the row to start to fetching (0 = the first row)
1184     * @param int    $count   the numbers of rows to fetch
1185     * @param mixed  $params  array, string or numeric data to be used in
1186     *                         execution of the statement.  Quantity of items
1187     *                         passed must match quantity of placeholders in
1188     *                         query:  meaning 1 placeholder for non-array
1189     *                         parameters or 1 placeholder per array element.
1190     *
1191     * @return mixed  a new DB_result object for successful SELECT queries
1192     *                 or DB_OK for successul data manipulation queries.
1193     *                 A DB_Error object on failure.
1194     */
1195    function &limitQuery($query, $from, $count, $params = array())
1196    {
1197        $query = $this->modifyLimitQuery($query, $from, $count, $params);
1198        if (DB::isError($query)){
1199            return $query;
1200        }
1201        $result =& $this->query($query, $params);
1202        if (is_a($result, 'DB_result')) {
1203            $result->setOption('limit_from', $from);
1204            $result->setOption('limit_count', $count);
1205        }
1206        return $result;
1207    }
1208
1209    // }}}
1210    // {{{ getOne()
1211
1212    /**
1213     * Fetches the first column of the first row from a query result
1214     *
1215     * Takes care of doing the query and freeing the results when finished.
1216     *
1217     * @param string $query   the SQL query
1218     * @param mixed  $params  array, string or numeric data to be used in
1219     *                         execution of the statement.  Quantity of items
1220     *                         passed must match quantity of placeholders in
1221     *                         query:  meaning 1 placeholder for non-array
1222     *                         parameters or 1 placeholder per array element.
1223     *
1224     * @return mixed  the returned value of the query.
1225     *                 A DB_Error object on failure.
1226     */
1227    function &getOne($query, $params = array())
1228    {
1229        $params = (array)$params;
1230        // modifyLimitQuery() would be nice here, but it causes BC issues
1231        if (sizeof($params) > 0) {
1232            $sth = $this->prepare($query);
1233            if (DB::isError($sth)) {
1234                return $sth;
1235            }
1236            $res =& $this->execute($sth, $params);
1237            $this->freePrepared($sth);
1238        } else {
1239            $res =& $this->query($query);
1240        }
1241
1242        if (DB::isError($res)) {
1243            return $res;
1244        }
1245
1246        $err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
1247        $res->free();
1248
1249        if ($err !== DB_OK) {
1250            return $err;
1251        }
1252
1253        return $row[0];
1254    }
1255
1256    // }}}
1257    // {{{ getRow()
1258
1259    /**
1260     * Fetches the first row of data returned from a query result
1261     *
1262     * Takes care of doing the query and freeing the results when finished.
1263     *
1264     * @param string $query   the SQL query
1265     * @param mixed  $params  array, string or numeric data to be used in
1266     *                         execution of the statement.  Quantity of items
1267     *                         passed must match quantity of placeholders in
1268     *                         query:  meaning 1 placeholder for non-array
1269     *                         parameters or 1 placeholder per array element.
1270     * @param int $fetchmode  the fetch mode to use
1271     *
1272     * @return array  the first row of results as an array.
1273     *                 A DB_Error object on failure.
1274     */
1275    function &getRow($query, $params = array(),
1276                     $fetchmode = DB_FETCHMODE_DEFAULT)
1277    {
1278        // compat check, the params and fetchmode parameters used to
1279        // have the opposite order
1280        if (!is_array($params)) {
1281            if (is_array($fetchmode)) {
1282                if ($params === null) {
1283                    $tmp = DB_FETCHMODE_DEFAULT;
1284                } else {
1285                    $tmp = $params;
1286                }
1287                $params = $fetchmode;
1288                $fetchmode = $tmp;
1289            } elseif ($params !== null) {
1290                $fetchmode = $params;
1291                $params = array();
1292            }
1293        }
1294        // modifyLimitQuery() would be nice here, but it causes BC issues
1295        if (sizeof($params) > 0) {
1296            $sth = $this->prepare($query);
1297            if (DB::isError($sth)) {
1298                return $sth;
1299            }
1300            $res =& $this->execute($sth, $params);
1301            $this->freePrepared($sth);
1302        } else {
1303            $res =& $this->query($query);
1304        }
1305
1306        if (DB::isError($res)) {
1307            return $res;
1308        }
1309
1310        $err = $res->fetchInto($row, $fetchmode);
1311
1312        $res->free();
1313
1314        if ($err !== DB_OK) {
1315            return $err;
1316        }
1317
1318        return $row;
1319    }
1320
1321    // }}}
1322    // {{{ getCol()
1323
1324    /**
1325     * Fetches a single column from a query result and returns it as an
1326     * indexed array
1327     *
1328     * @param string $query   the SQL query
1329     * @param mixed  $col     which column to return (integer [column number,
1330     *                         starting at 0] or string [column name])
1331     * @param mixed  $params  array, string or numeric data to be used in
1332     *                         execution of the statement.  Quantity of items
1333     *                         passed must match quantity of placeholders in
1334     *                         query:  meaning 1 placeholder for non-array
1335     *                         parameters or 1 placeholder per array element.
1336     *
1337     * @return array  the results as an array.  A DB_Error object on failure.
1338     *
1339     * @see DB_common::query()
1340     */
1341    function &getCol($query, $col = 0, $params = array())
1342    {
1343        $params = (array)$params;
1344        if (sizeof($params) > 0) {
1345            $sth = $this->prepare($query);
1346
1347            if (DB::isError($sth)) {
1348                return $sth;
1349            }
1350
1351            $res =& $this->execute($sth, $params);
1352            $this->freePrepared($sth);
1353        } else {
1354            $res =& $this->query($query);
1355        }
1356
1357        if (DB::isError($res)) {
1358            return $res;
1359        }
1360
1361        $fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
1362
1363        if (!is_array($row = $res->fetchRow($fetchmode))) {
1364            $ret = array();
1365        } else {
1366            if (!array_key_exists($col, $row)) {
1367                $ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
1368            } else {
1369                $ret = array($row[$col]);
1370                while (is_array($row = $res->fetchRow($fetchmode))) {
1371                    $ret[] = $row[$col];
1372                }
1373            }
1374        }
1375
1376        $res->free();
1377
1378        if (DB::isError($row)) {
1379            $ret = $row;
1380        }
1381
1382        return $ret;
1383    }
1384
1385    // }}}
1386    // {{{ getAssoc()
1387
1388    /**
1389     * Fetches an entire query result and returns it as an
1390     * associative array using the first column as the key
1391     *
1392     * If the result set contains more than two columns, the value
1393     * will be an array of the values from column 2-n.  If the result
1394     * set contains only two columns, the returned value will be a
1395     * scalar with the value of the second column (unless forced to an
1396     * array with the $force_array parameter).  A DB error code is
1397     * returned on errors.  If the result set contains fewer than two
1398     * columns, a DB_ERROR_TRUNCATED error is returned.
1399     *
1400     * For example, if the table "mytable" contains:
1401     *
1402     * <pre>
1403     *  ID      TEXT       DATE
1404     * --------------------------------
1405     *  1       'one'      944679408
1406     *  2       'two'      944679408
1407     *  3       'three'    944679408
1408     * </pre>
1409     *
1410     * Then the call getAssoc('SELECT id,text FROM mytable') returns:
1411     * <pre>
1412     *   array(
1413     *     '1' => 'one',
1414     *     '2' => 'two',
1415     *     '3' => 'three',
1416     *   )
1417     * </pre>
1418     *
1419     * ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
1420     * <pre>
1421     *   array(
1422     *     '1' => array('one', '944679408'),
1423     *     '2' => array('two', '944679408'),
1424     *     '3' => array('three', '944679408')
1425     *   )
1426     * </pre>
1427     *
1428     * If the more than one row occurs with the same value in the
1429     * first column, the last row overwrites all previous ones by
1430     * default.  Use the $group parameter if you don't want to
1431     * overwrite like this.  Example:
1432     *
1433     * <pre>
1434     * getAssoc('SELECT category,id,name FROM mytable', false, null,
1435     *          DB_FETCHMODE_ASSOC, true) returns:
1436     *
1437     *   array(
1438     *     '1' => array(array('id' => '4', 'name' => 'number four'),
1439     *                  array('id' => '6', 'name' => 'number six')
1440     *            ),
1441     *     '9' => array(array('id' => '4', 'name' => 'number four'),
1442     *                  array('id' => '6', 'name' => 'number six')
1443     *            )
1444     *   )
1445     * </pre>
1446     *
1447     * Keep in mind that database functions in PHP usually return string
1448     * values for results regardless of the database's internal type.
1449     *
1450     * @param string $query        the SQL query
1451     * @param bool   $force_array  used only when the query returns
1452     *                              exactly two columns.  If true, the values
1453     *                              of the returned array will be one-element
1454     *                              arrays instead of scalars.
1455     * @param mixed  $params       array, string or numeric data to be used in
1456     *                              execution of the statement.  Quantity of
1457     *                              items passed must match quantity of
1458     *                              placeholders in query:  meaning 1
1459     *                              placeholder for non-array parameters or
1460     *                              1 placeholder per array element.
1461     * @param int   $fetchmode     the fetch mode to use
1462     * @param bool  $group         if true, the values of the returned array
1463     *                              is wrapped in another array.  If the same
1464     *                              key value (in the first column) repeats
1465     *                              itself, the values will be appended to
1466     *                              this array instead of overwriting the
1467     *                              existing values.
1468     *
1469     * @return array  the associative array containing the query results.
1470     *                A DB_Error object on failure.
1471     */
1472    function &getAssoc($query, $force_array = false, $params = array(),
1473                       $fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
1474    {
1475        $params = (array)$params;
1476        if (sizeof($params) > 0) {
1477            $sth = $this->prepare($query);
1478
1479            if (DB::isError($sth)) {
1480                return $sth;
1481            }
1482
1483            $res =& $this->execute($sth, $params);
1484            $this->freePrepared($sth);
1485        } else {
1486            $res =& $this->query($query);
1487        }
1488
1489        if (DB::isError($res)) {
1490            return $res;
1491        }
1492        if ($fetchmode == DB_FETCHMODE_DEFAULT) {
1493            $fetchmode = $this->fetchmode;
1494        }
1495        $cols = $res->numCols();
1496
1497        if ($cols < 2) {
1498            $tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
1499            return $tmp;
1500        }
1501
1502        $results = array();
1503
1504        if ($cols > 2 || $force_array) {
1505            // return array values
1506            // XXX this part can be optimized
1507            if ($fetchmode == DB_FETCHMODE_ASSOC) {
1508                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
1509                    reset($row);
1510                    $key = current($row);
1511                    unset($row[key($row)]);
1512                    if ($group) {
1513                        $results[$key][] = $row;
1514                    } else {
1515                        $results[$key] = $row;
1516                    }
1517                }
1518            } elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
1519                while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
1520                    $arr = get_object_vars($row);
1521                    $key = current($arr);
1522                    if ($group) {
1523                        $results[$key][] = $row;
1524                    } else {
1525                        $results[$key] = $row;
1526                    }
1527                }
1528            } else {
1529                while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1530                    // we shift away the first element to get
1531                    // indices running from 0 again
1532                    $key = array_shift($row);
1533                    if ($group) {
1534                        $results[$key][] = $row;
1535                    } else {
1536                        $results[$key] = $row;
1537                    }
1538                }
1539            }
1540            if (DB::isError($row)) {
1541                $results = $row;
1542            }
1543        } else {
1544            // return scalar values
1545            while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
1546                if ($group) {
1547                    $results[$row[0]][] = $row[1];
1548                } else {
1549                    $results[$row[0]] = $row[1];
1550                }
1551            }
1552            if (DB::isError($row)) {
1553                $results = $row;
1554            }
1555        }
1556
1557        $res->free();
1558
1559        return $results;
1560    }
1561
1562    // }}}
1563    // {{{ getAll()
1564
1565    /**
1566     * Fetches all of the rows from a query result
1567     *
1568     * @param string $query      the SQL query
1569     * @param mixed  $params     array, string or numeric data to be used in
1570     *                            execution of the statement.  Quantity of
1571     *                            items passed must match quantity of
1572     *                            placeholders in query:  meaning 1
1573     *                            placeholder for non-array parameters or
1574     *                            1 placeholder per array element.
1575     * @param int    $fetchmode  the fetch mode to use:
1576     *                            + DB_FETCHMODE_ORDERED
1577     *                            + DB_FETCHMODE_ASSOC
1578     *                            + DB_FETCHMODE_ORDERED | DB_FETCHMODE_FLIPPED
1579     *                            + DB_FETCHMODE_ASSOC | DB_FETCHMODE_FLIPPED
1580     *
1581     * @return array  the nested array.  A DB_Error object on failure.
1582     */
1583    function &getAll($query, $params = array(),
1584                     $fetchmode = DB_FETCHMODE_DEFAULT)
1585    {
1586        // compat check, the params and fetchmode parameters used to
1587        // have the opposite order
1588        if (!is_array($params)) {
1589            if (is_array($fetchmode)) {
1590                if ($params === null) {
1591                    $tmp = DB_FETCHMODE_DEFAULT;
1592                } else {
1593                    $tmp = $params;
1594                }
1595                $params = $fetchmode;
1596                $fetchmode = $tmp;
1597            } elseif ($params !== null) {
1598                $fetchmode = $params;
1599                $params = array();
1600            }
1601        }
1602
1603        if (sizeof($params) > 0) {
1604            $sth = $this->prepare($query);
1605
1606            if (DB::isError($sth)) {
1607                return $sth;
1608            }
1609
1610            $res =& $this->execute($sth, $params);
1611            $this->freePrepared($sth);
1612        } else {
1613            $res =& $this->query($query);
1614        }
1615
1616        if ($res === DB_OK || DB::isError($res)) {
1617            return $res;
1618        }
1619
1620        $results = array();
1621        while (DB_OK === $res->fetchInto($row, $fetchmode)) {
1622            if ($fetchmode & DB_FETCHMODE_FLIPPED) {
1623                foreach ($row as $key => $val) {
1624                    $results[$key][] = $val;
1625                }
1626            } else {
1627                $results[] = $row;
1628            }
1629        }
1630
1631        $res->free();
1632
1633        if (DB::isError($row)) {
1634            $tmp =& $this->raiseError($row);
1635            return $tmp;
1636        }
1637        return $results;
1638    }
1639
1640    // }}}
1641    // {{{ autoCommit()
1642
1643    /**
1644     * Enables or disables automatic commits
1645     *
1646     * @param bool $onoff  true turns it on, false turns it off
1647     *
1648     * @return int  DB_OK on success.  A DB_Error object if the driver
1649     *               doesn't support auto-committing transactions.
1650     */
1651    function autoCommit($onoff = false)
1652    {
1653        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1654    }
1655
1656    // }}}
1657    // {{{ commit()
1658
1659    /**
1660     * Commits the current transaction
1661     *
1662     * @return int  DB_OK on success.  A DB_Error object on failure.
1663     */
1664    function commit()
1665    {
1666        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1667    }
1668
1669    // }}}
1670    // {{{ rollback()
1671
1672    /**
1673     * Reverts the current transaction
1674     *
1675     * @return int  DB_OK on success.  A DB_Error object on failure.
1676     */
1677    function rollback()
1678    {
1679        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1680    }
1681
1682    // }}}
1683    // {{{ numRows()
1684
1685    /**
1686     * Determines the number of rows in a query result
1687     *
1688     * @param resource $result  the query result idenifier produced by PHP
1689     *
1690     * @return int  the number of rows.  A DB_Error object on failure.
1691     */
1692    function numRows($result)
1693    {
1694        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1695    }
1696
1697    // }}}
1698    // {{{ affectedRows()
1699
1700    /**
1701     * Determines the number of rows affected by a data maniuplation query
1702     *
1703     * 0 is returned for queries that don't manipulate data.
1704     *
1705     * @return int  the number of rows.  A DB_Error object on failure.
1706     */
1707    function affectedRows()
1708    {
1709        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1710    }
1711
1712    // }}}
1713    // {{{ getSequenceName()
1714
1715    /**
1716     * Generates the name used inside the database for a sequence
1717     *
1718     * The createSequence() docblock contains notes about storing sequence
1719     * names.
1720     *
1721     * @param string $sqn  the sequence's public name
1722     *
1723     * @return string  the sequence's name in the backend
1724     *
1725     * @access protected
1726     * @see DB_common::createSequence(), DB_common::dropSequence(),
1727     *      DB_common::nextID(), DB_common::setOption()
1728     */
1729    function getSequenceName($sqn)
1730    {
1731        return sprintf($this->getOption('seqname_format'),
1732                       preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
1733    }
1734
1735    // }}}
1736    // {{{ nextId()
1737
1738    /**
1739     * Returns the next free id in a sequence
1740     *
1741     * @param string  $seq_name  name of the sequence
1742     * @param boolean $ondemand  when true, the seqence is automatically
1743     *                            created if it does not exist
1744     *
1745     * @return int  the next id number in the sequence.
1746     *               A DB_Error object on failure.
1747     *
1748     * @see DB_common::createSequence(), DB_common::dropSequence(),
1749     *      DB_common::getSequenceName()
1750     */
1751    function nextId($seq_name, $ondemand = true)
1752    {
1753        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1754    }
1755
1756    // }}}
1757    // {{{ createSequence()
1758
1759    /**
1760     * Creates a new sequence
1761     *
1762     * The name of a given sequence is determined by passing the string
1763     * provided in the <var>$seq_name</var> argument through PHP's sprintf()
1764     * function using the value from the <var>seqname_format</var> option as
1765     * the sprintf()'s format argument.
1766     *
1767     * <var>seqname_format</var> is set via setOption().
1768     *
1769     * @param string $seq_name  name of the new sequence
1770     *
1771     * @return int  DB_OK on success.  A DB_Error object on failure.
1772     *
1773     * @see DB_common::dropSequence(), DB_common::getSequenceName(),
1774     *      DB_common::nextID()
1775     */
1776    function createSequence($seq_name)
1777    {
1778        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1779    }
1780
1781    // }}}
1782    // {{{ dropSequence()
1783
1784    /**
1785     * Deletes a sequence
1786     *
1787     * @param string $seq_name  name of the sequence to be deleted
1788     *
1789     * @return int  DB_OK on success.  A DB_Error object on failure.
1790     *
1791     * @see DB_common::createSequence(), DB_common::getSequenceName(),
1792     *      DB_common::nextID()
1793     */
1794    function dropSequence($seq_name)
1795    {
1796        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1797    }
1798
1799    // }}}
1800    // {{{ raiseError()
1801
1802    /**
1803     * Communicates an error and invoke error callbacks, etc
1804     *
1805     * Basically a wrapper for PEAR::raiseError without the message string.
1806     *
1807     * @param mixed   integer error code, or a PEAR error object (all
1808     *                 other parameters are ignored if this parameter is
1809     *                 an object
1810     * @param int     error mode, see PEAR_Error docs
1811     * @param mixed   if error mode is PEAR_ERROR_TRIGGER, this is the
1812     *                 error level (E_USER_NOTICE etc).  If error mode is
1813     *                 PEAR_ERROR_CALLBACK, this is the callback function,
1814     *                 either as a function name, or as an array of an
1815     *                 object and method name.  For other error modes this
1816     *                 parameter is ignored.
1817     * @param string  extra debug information.  Defaults to the last
1818     *                 query and native error code.
1819     * @param mixed   native error code, integer or string depending the
1820     *                 backend
1821     *
1822     * @return object  the PEAR_Error object
1823     *
1824     * @see PEAR_Error
1825     */
1826    function &raiseError($code = DB_ERROR, $mode = null, $options = null,
1827                         $userinfo = null, $nativecode = null)
1828    {
1829        // The error is yet a DB error object
1830        if (is_object($code)) {
1831            // because we the static PEAR::raiseError, our global
1832            // handler should be used if it is set
1833            if ($mode === null && !empty($this->_default_error_mode)) {
1834                $mode    = $this->_default_error_mode;
1835                $options = $this->_default_error_options;
1836            }
1837            $tmp = PEAR::raiseError($code, null, $mode, $options,
1838                                    null, null, true);
1839            return $tmp;
1840        }
1841
1842        if ($userinfo === null) {
1843            $userinfo = $this->last_query;
1844        }
1845
1846        if ($nativecode) {
1847            $userinfo .= ' [nativecode=' . trim($nativecode) . ']';
1848        } else {
1849            $userinfo .= ' [DB Error: ' . DB::errorMessage($code) . ']';
1850        }
1851
1852        $tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
1853                                'DB_Error', true);
1854        return $tmp;
1855    }
1856
1857    // }}}
1858    // {{{ errorNative()
1859
1860    /**
1861     * Gets the DBMS' native error code produced by the last query
1862     *
1863     * @return mixed  the DBMS' error code.  A DB_Error object on failure.
1864     */
1865    function errorNative()
1866    {
1867        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
1868    }
1869
1870    // }}}
1871    // {{{ errorCode()
1872
1873    /**
1874     * Maps native error codes to DB's portable ones
1875     *
1876     * Uses the <var>$errorcode_map</var> property defined in each driver.
1877     *
1878     * @param string|int $nativecode  the error code returned by the DBMS
1879     *
1880     * @return int  the portable DB error code.  Return DB_ERROR if the
1881     *               current driver doesn't have a mapping for the
1882     *               $nativecode submitted.
1883     */
1884    function errorCode($nativecode)
1885    {
1886        if (isset($this->errorcode_map[$nativecode])) {
1887            return $this->errorcode_map[$nativecode];
1888        }
1889        // Fall back to DB_ERROR if there was no mapping.
1890        return DB_ERROR;
1891    }
1892
1893    // }}}
1894    // {{{ errorMessage()
1895
1896    /**
1897     * Maps a DB error code to a textual message
1898     *
1899     * @param integer $dbcode  the DB error code
1900     *
1901     * @return string  the error message corresponding to the error code
1902     *                  submitted.  FALSE if the error code is unknown.
1903     *
1904     * @see DB::errorMessage()
1905     */
1906    function errorMessage($dbcode)
1907    {
1908        return DB::errorMessage($this->errorcode_map[$dbcode]);
1909    }
1910
1911    // }}}
1912    // {{{ tableInfo()
1913
1914    /**
1915     * Returns information about a table or a result set
1916     *
1917     * The format of the resulting array depends on which <var>$mode</var>
1918     * you select.  The sample output below is based on this query:
1919     * <pre>
1920     *    SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
1921     *    FROM tblFoo
1922     *    JOIN tblBar ON tblFoo.fldId = tblBar.fldId
1923     * </pre>
1924     *
1925     * <ul>
1926     * <li>
1927     *
1928     * <kbd>null</kbd> (default)
1929     *   <pre>
1930     *   [0] => Array (
1931     *       [table] => tblFoo
1932     *       [name] => fldId
1933     *       [type] => int
1934     *       [len] => 11
1935     *       [flags] => primary_key not_null
1936     *   )
1937     *   [1] => Array (
1938     *       [table] => tblFoo
1939     *       [name] => fldPhone
1940     *       [type] => string
1941     *       [len] => 20
1942     *       [flags] =>
1943     *   )
1944     *   [2] => Array (
1945     *       [table] => tblBar
1946     *       [name] => fldId
1947     *       [type] => int
1948     *       [len] => 11
1949     *       [flags] => primary_key not_null
1950     *   )
1951     *   </pre>
1952     *
1953     * </li><li>
1954     *
1955     * <kbd>DB_TABLEINFO_ORDER</kbd>
1956     *
1957     *   <p>In addition to the information found in the default output,
1958     *   a notation of the number of columns is provided by the
1959     *   <samp>num_fields</samp> element while the <samp>order</samp>
1960     *   element provides an array with the column names as the keys and
1961     *   their location index number (corresponding to the keys in the
1962     *   the default output) as the values.</p>
1963     *
1964     *   <p>If a result set has identical field names, the last one is
1965     *   used.</p>
1966     *
1967     *   <pre>
1968     *   [num_fields] => 3
1969     *   [order] => Array (
1970     *       [fldId] => 2
1971     *       [fldTrans] => 1
1972     *   )
1973     *   </pre>
1974     *
1975     * </li><li>
1976     *
1977     * <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
1978     *
1979     *   <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
1980     *   dimensions to the array in which the table names are keys and
1981     *   the field names are sub-keys.  This is helpful for queries that
1982     *   join tables which have identical field names.</p>
1983     *
1984     *   <pre>
1985     *   [num_fields] => 3
1986     *   [ordertable] => Array (
1987     *       [tblFoo] => Array (
1988     *           [fldId] => 0
1989     *           [fldPhone] => 1
1990     *       )
1991     *       [tblBar] => Array (
1992     *           [fldId] => 2
1993     *       )
1994     *   )
1995     *   </pre>
1996     *
1997     * </li>
1998     * </ul>
1999     *
2000     * The <samp>flags</samp> element contains a space separated list
2001     * of extra information about the field.  This data is inconsistent
2002     * between DBMS's due to the way each DBMS works.
2003     *   + <samp>primary_key</samp>
2004     *   + <samp>unique_key</samp>
2005     *   + <samp>multiple_key</samp>
2006     *   + <samp>not_null</samp>
2007     *
2008     * Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
2009     * elements if <var>$result</var> is a table name.  The following DBMS's
2010     * provide full information from queries:
2011     *   + fbsql
2012     *   + mysql
2013     *
2014     * If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
2015     * turned on, the names of tables and fields will be lowercased.
2016     *
2017     * @param object|string  $result  DB_result object from a query or a
2018     *                                string containing the name of a table.
2019     *                                While this also accepts a query result
2020     *                                resource identifier, this behavior is
2021     *                                deprecated.
2022     * @param int  $mode   either unused or one of the tableInfo modes:
2023     *                     <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
2024     *                     <kbd>DB_TABLEINFO_ORDER</kbd> or
2025     *                     <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
2026     *                     These are bitwise, so the first two can be
2027     *                     combined using <kbd>|</kbd>.
2028     *
2029     * @return array  an associative array with the information requested.
2030     *                 A DB_Error object on failure.
2031     *
2032     * @see DB_common::setOption()
2033     */
2034    function tableInfo($result, $mode = null)
2035    {
2036        /*
2037         * If the DB_<driver> class has a tableInfo() method, that one
2038         * overrides this one.  But, if the driver doesn't have one,
2039         * this method runs and tells users about that fact.
2040         */
2041        return $this->raiseError(DB_ERROR_NOT_CAPABLE);
2042    }
2043
2044    // }}}
2045    // {{{ getTables()
2046
2047    /**
2048     * Lists the tables in the current database
2049     *
2050     * @return array  the list of tables.  A DB_Error object on failure.
2051     *
2052     * @deprecated Method deprecated some time before Release 1.2
2053     */
2054    function getTables()
2055    {
2056        return $this->getListOf('tables');
2057    }
2058
2059    // }}}
2060    // {{{ getListOf()
2061
2062    /**
2063     * Lists internal database information
2064     *
2065     * @param string $type  type of information being sought.
2066     *                       Common items being sought are:
2067     *                       tables, databases, users, views, functions
2068     *                       Each DBMS's has its own capabilities.
2069     *
2070     * @return array  an array listing the items sought.
2071     *                 A DB DB_Error object on failure.
2072     */
2073    function getListOf($type)
2074    {
2075        $sql = $this->getSpecialQuery($type);
2076        if ($sql === null) {
2077            $this->last_query = '';
2078            return $this->raiseError(DB_ERROR_UNSUPPORTED);
2079        } elseif (is_int($sql) || DB::isError($sql)) {
2080            // Previous error
2081            return $this->raiseError($sql);
2082        } elseif (is_array($sql)) {
2083            // Already the result
2084            return $sql;
2085        }
2086        // Launch this query
2087        return $this->getCol($sql);
2088    }
2089
2090    // }}}
2091    // {{{ getSpecialQuery()
2092
2093    /**
2094     * Obtains the query string needed for listing a given type of objects
2095     *
2096     * @param string $type  the kind of objects you want to retrieve
2097     *
2098     * @return string  the SQL query string or null if the driver doesn't
2099     *                  support the object type requested
2100     *
2101     * @access protected
2102     * @see DB_common::getListOf()
2103     */
2104    function getSpecialQuery($type)
2105    {
2106        return $this->raiseError(DB_ERROR_UNSUPPORTED);
2107    }
2108
2109    // }}}
2110    // {{{ _rtrimArrayValues()
2111
2112    /**
2113     * Right-trims all strings in an array
2114     *
2115     * @param array $array  the array to be trimmed (passed by reference)
2116     *
2117     * @return void
2118     *
2119     * @access protected
2120     */
2121    function _rtrimArrayValues(&$array)
2122    {
2123        foreach ($array as $key => $value) {
2124            if (is_string($value)) {
2125                $array[$key] = rtrim($value);
2126            }
2127        }
2128    }
2129
2130    // }}}
2131    // {{{ _convertNullArrayValuesToEmpty()
2132
2133    /**
2134     * Converts all null values in an array to empty strings
2135     *
2136     * @param array  $array  the array to be de-nullified (passed by reference)
2137     *
2138     * @return void
2139     *
2140     * @access protected
2141     */
2142    function _convertNullArrayValuesToEmpty(&$array)
2143    {
2144        foreach ($array as $key => $value) {
2145            if (is_null($value)) {
2146                $array[$key] = '';
2147            }
2148        }
2149    }
2150
2151    // }}}
2152}
2153
2154/*
2155 * Local variables:
2156 * tab-width: 4
2157 * c-basic-offset: 4
2158 * End:
2159 */
2160
2161?>
Note: See TracBrowser for help on using the repository browser.