home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / MDB2.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  137.0 KB  |  4,271 lines

  1. <?php
  2. // vim: set et ts=4 sw=4 fdm=marker:
  3. // +----------------------------------------------------------------------+
  4. // | PHP versions 4 and 5                                                 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1998-2007 Manuel Lemos, Tomas V.V.Cox,                 |
  7. // | Stig. S. Bakken, Lukas Smith                                         |
  8. // | All rights reserved.                                                 |
  9. // +----------------------------------------------------------------------+
  10. // | MDB2 is a merge of PEAR DB and Metabases that provides a unified DB  |
  11. // | API as well as database abstraction for PHP applications.            |
  12. // | This LICENSE is in the BSD license style.                            |
  13. // |                                                                      |
  14. // | Redistribution and use in source and binary forms, with or without   |
  15. // | modification, are permitted provided that the following conditions   |
  16. // | are met:                                                             |
  17. // |                                                                      |
  18. // | Redistributions of source code must retain the above copyright       |
  19. // | notice, this list of conditions and the following disclaimer.        |
  20. // |                                                                      |
  21. // | Redistributions in binary form must reproduce the above copyright    |
  22. // | notice, this list of conditions and the following disclaimer in the  |
  23. // | documentation and/or other materials provided with the distribution. |
  24. // |                                                                      |
  25. // | Neither the name of Manuel Lemos, Tomas V.V.Cox, Stig. S. Bakken,    |
  26. // | Lukas Smith nor the names of his contributors may be used to endorse |
  27. // | or promote products derived from this software without specific prior|
  28. // | written permission.                                                  |
  29. // |                                                                      |
  30. // | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS  |
  31. // | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT    |
  32. // | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS    |
  33. // | FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE      |
  34. // | REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,          |
  35. // | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
  36. // | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS|
  37. // |  OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED  |
  38. // | AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT          |
  39. // | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY|
  40. // | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE          |
  41. // | POSSIBILITY OF SUCH DAMAGE.                                          |
  42. // +----------------------------------------------------------------------+
  43. // | Author: Lukas Smith <smith@pooteeweet.org>                           |
  44. // +----------------------------------------------------------------------+
  45. //
  46. // $Id: MDB2.php,v 1.292 2007/04/25 09:31:01 quipo Exp $
  47. //
  48.  
  49. /**
  50.  * @package     MDB2
  51.  * @category    Database
  52.  * @author      Lukas Smith <smith@pooteeweet.org>
  53.  */
  54.  
  55. require_once 'PEAR.php';
  56.  
  57. // {{{ Error constants
  58.  
  59. /**
  60.  * The method mapErrorCode in each MDB2_dbtype implementation maps
  61.  * native error codes to one of these.
  62.  *
  63.  * If you add an error code here, make sure you also add a textual
  64.  * version of it in MDB2::errorMessage().
  65.  */
  66.  
  67. define('MDB2_OK',                      true);
  68. define('MDB2_ERROR',                     -1);
  69. define('MDB2_ERROR_SYNTAX',              -2);
  70. define('MDB2_ERROR_CONSTRAINT',          -3);
  71. define('MDB2_ERROR_NOT_FOUND',           -4);
  72. define('MDB2_ERROR_ALREADY_EXISTS',      -5);
  73. define('MDB2_ERROR_UNSUPPORTED',         -6);
  74. define('MDB2_ERROR_MISMATCH',            -7);
  75. define('MDB2_ERROR_INVALID',             -8);
  76. define('MDB2_ERROR_NOT_CAPABLE',         -9);
  77. define('MDB2_ERROR_TRUNCATED',          -10);
  78. define('MDB2_ERROR_INVALID_NUMBER',     -11);
  79. define('MDB2_ERROR_INVALID_DATE',       -12);
  80. define('MDB2_ERROR_DIVZERO',            -13);
  81. define('MDB2_ERROR_NODBSELECTED',       -14);
  82. define('MDB2_ERROR_CANNOT_CREATE',      -15);
  83. define('MDB2_ERROR_CANNOT_DELETE',      -16);
  84. define('MDB2_ERROR_CANNOT_DROP',        -17);
  85. define('MDB2_ERROR_NOSUCHTABLE',        -18);
  86. define('MDB2_ERROR_NOSUCHFIELD',        -19);
  87. define('MDB2_ERROR_NEED_MORE_DATA',     -20);
  88. define('MDB2_ERROR_NOT_LOCKED',         -21);
  89. define('MDB2_ERROR_VALUE_COUNT_ON_ROW', -22);
  90. define('MDB2_ERROR_INVALID_DSN',        -23);
  91. define('MDB2_ERROR_CONNECT_FAILED',     -24);
  92. define('MDB2_ERROR_EXTENSION_NOT_FOUND',-25);
  93. define('MDB2_ERROR_NOSUCHDB',           -26);
  94. define('MDB2_ERROR_ACCESS_VIOLATION',   -27);
  95. define('MDB2_ERROR_CANNOT_REPLACE',     -28);
  96. define('MDB2_ERROR_CONSTRAINT_NOT_NULL',-29);
  97. define('MDB2_ERROR_DEADLOCK',           -30);
  98. define('MDB2_ERROR_CANNOT_ALTER',       -31);
  99. define('MDB2_ERROR_MANAGER',            -32);
  100. define('MDB2_ERROR_MANAGER_PARSE',      -33);
  101. define('MDB2_ERROR_LOADMODULE',         -34);
  102. define('MDB2_ERROR_INSUFFICIENT_DATA',  -35);
  103. // }}}
  104. // {{{ Verbose constants
  105. /**
  106.  * These are just helper constants to more verbosely express parameters to prepare()
  107.  */
  108.  
  109. define('MDB2_PREPARE_MANIP', false);
  110. define('MDB2_PREPARE_RESULT', null);
  111.  
  112. // }}}
  113. // {{{ Fetchmode constants
  114.  
  115. /**
  116.  * This is a special constant that tells MDB2 the user hasn't specified
  117.  * any particular get mode, so the default should be used.
  118.  */
  119. define('MDB2_FETCHMODE_DEFAULT', 0);
  120.  
  121. /**
  122.  * Column data indexed by numbers, ordered from 0 and up
  123.  */
  124. define('MDB2_FETCHMODE_ORDERED', 1);
  125.  
  126. /**
  127.  * Column data indexed by column names
  128.  */
  129. define('MDB2_FETCHMODE_ASSOC', 2);
  130.  
  131. /**
  132.  * Column data as object properties
  133.  */
  134. define('MDB2_FETCHMODE_OBJECT', 3);
  135.  
  136. /**
  137.  * For multi-dimensional results: normally the first level of arrays
  138.  * is the row number, and the second level indexed by column number or name.
  139.  * MDB2_FETCHMODE_FLIPPED switches this order, so the first level of arrays
  140.  * is the column name, and the second level the row number.
  141.  */
  142. define('MDB2_FETCHMODE_FLIPPED', 4);
  143.  
  144. // }}}
  145. // {{{ Portability mode constants
  146.  
  147. /**
  148.  * Portability: turn off all portability features.
  149.  * @see MDB2_Driver_Common::setOption()
  150.  */
  151. define('MDB2_PORTABILITY_NONE', 0);
  152.  
  153. /**
  154.  * Portability: convert names of tables and fields to case defined in the
  155.  * "field_case" option when using the query*(), fetch*() and tableInfo() methods.
  156.  * @see MDB2_Driver_Common::setOption()
  157.  */
  158. define('MDB2_PORTABILITY_FIX_CASE', 1);
  159.  
  160. /**
  161.  * Portability: right trim the data output by query*() and fetch*().
  162.  * @see MDB2_Driver_Common::setOption()
  163.  */
  164. define('MDB2_PORTABILITY_RTRIM', 2);
  165.  
  166. /**
  167.  * Portability: force reporting the number of rows deleted.
  168.  * @see MDB2_Driver_Common::setOption()
  169.  */
  170. define('MDB2_PORTABILITY_DELETE_COUNT', 4);
  171.  
  172. /**
  173.  * Portability: not needed in MDB2 (just left here for compatibility to DB)
  174.  * @see MDB2_Driver_Common::setOption()
  175.  */
  176. define('MDB2_PORTABILITY_NUMROWS', 8);
  177.  
  178. /**
  179.  * Portability: makes certain error messages in certain drivers compatible
  180.  * with those from other DBMS's.
  181.  *
  182.  * + mysql, mysqli:  change unique/primary key constraints
  183.  *   MDB2_ERROR_ALREADY_EXISTS -> MDB2_ERROR_CONSTRAINT
  184.  *
  185.  * + odbc(access):  MS's ODBC driver reports 'no such field' as code
  186.  *   07001, which means 'too few parameters.'  When this option is on
  187.  *   that code gets mapped to MDB2_ERROR_NOSUCHFIELD.
  188.  *
  189.  * @see MDB2_Driver_Common::setOption()
  190.  */
  191. define('MDB2_PORTABILITY_ERRORS', 16);
  192.  
  193. /**
  194.  * Portability: convert empty values to null strings in data output by
  195.  * query*() and fetch*().
  196.  * @see MDB2_Driver_Common::setOption()
  197.  */
  198. define('MDB2_PORTABILITY_EMPTY_TO_NULL', 32);
  199.  
  200. /**
  201.  * Portability: removes database/table qualifiers from associative indexes
  202.  * @see MDB2_Driver_Common::setOption()
  203.  */
  204. define('MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES', 64);
  205.  
  206. /**
  207.  * Portability: turn on all portability features.
  208.  * @see MDB2_Driver_Common::setOption()
  209.  */
  210. define('MDB2_PORTABILITY_ALL', 127);
  211.  
  212. // }}}
  213. // {{{ Globals for class instance tracking
  214.  
  215. /**
  216.  * These are global variables that are used to track the various class instances
  217.  */
  218.  
  219. $GLOBALS['_MDB2_databases'] = array();
  220. $GLOBALS['_MDB2_dsninfo_default'] = array(
  221.     'phptype'  => false,
  222.     'dbsyntax' => false,
  223.     'username' => false,
  224.     'password' => false,
  225.     'protocol' => false,
  226.     'hostspec' => false,
  227.     'port'     => false,
  228.     'socket'   => false,
  229.     'database' => false,
  230.     'mode'     => false,
  231. );
  232.  
  233. // }}}
  234. // {{{ class MDB2
  235.  
  236. /**
  237.  * The main 'MDB2' class is simply a container class with some static
  238.  * methods for creating DB objects as well as some utility functions
  239.  * common to all parts of DB.
  240.  *
  241.  * The object model of MDB2 is as follows (indentation means inheritance):
  242.  *
  243.  * MDB2          The main MDB2 class.  This is simply a utility class
  244.  *              with some 'static' methods for creating MDB2 objects as
  245.  *              well as common utility functions for other MDB2 classes.
  246.  *
  247.  * MDB2_Driver_Common   The base for each MDB2 implementation.  Provides default
  248.  * |            implementations (in OO lingo virtual methods) for
  249.  * |            the actual DB implementations as well as a bunch of
  250.  * |            query utility functions.
  251.  * |
  252.  * +-MDB2_Driver_mysql  The MDB2 implementation for MySQL. Inherits MDB2_Driver_Common.
  253.  *              When calling MDB2::factory or MDB2::connect for MySQL
  254.  *              connections, the object returned is an instance of this
  255.  *              class.
  256.  * +-MDB2_Driver_pgsql  The MDB2 implementation for PostGreSQL. Inherits MDB2_Driver_Common.
  257.  *              When calling MDB2::factory or MDB2::connect for PostGreSQL
  258.  *              connections, the object returned is an instance of this
  259.  *              class.
  260.  *
  261.  * @package     MDB2
  262.  * @category    Database
  263.  * @author      Lukas Smith <smith@pooteeweet.org>
  264.  */
  265. class MDB2
  266. {
  267.     // {{{ function setOptions(&$db, $options)
  268.  
  269.     /**
  270.      * set option array   in an exiting database object
  271.      *
  272.      * @param   MDB2_Driver_Common  MDB2 object
  273.      * @param   array   An associative array of option names and their values.
  274.      *
  275.      * @return mixed   MDB2_OK or a PEAR Error object
  276.      *
  277.      * @access  public
  278.      */
  279.     function setOptions(&$db, $options)
  280.     {
  281.         if (is_array($options)) {
  282.             foreach ($options as $option => $value) {
  283.                 $test = $db->setOption($option, $value);
  284.                 if (PEAR::isError($test)) {
  285.                     return $test;
  286.                 }
  287.             }
  288.         }
  289.         return MDB2_OK;
  290.     }
  291.  
  292.     // }}}
  293.     // {{{ function classExists($classname)
  294.  
  295.     /**
  296.      * Checks if a class exists without triggering __autoload
  297.      *
  298.      * @param   string  classname
  299.      *
  300.      * @return  bool    true success and false on error
  301.      * @static
  302.      * @access  public
  303.      */
  304.     function classExists($classname)
  305.     {
  306.         if (version_compare(phpversion(), "5.0", ">=")) {
  307.             return class_exists($classname, false);
  308.         }
  309.         return class_exists($classname);
  310.     }
  311.  
  312.     // }}}
  313.     // {{{ function loadClass($class_name, $debug)
  314.  
  315.     /**
  316.      * Loads a PEAR class.
  317.      *
  318.      * @param   string  classname to load
  319.      * @param   bool    if errors should be suppressed
  320.      *
  321.      * @return  mixed   true success or PEAR_Error on failure
  322.      *
  323.      * @access  public
  324.      */
  325.     function loadClass($class_name, $debug)
  326.     {
  327.         if (!MDB2::classExists($class_name)) {
  328.             $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  329.             if ($debug) {
  330.                 $include = include_once($file_name);
  331.             } else {
  332.                 $include = @include_once($file_name);
  333.             }
  334.             if (!$include) {
  335.                 if (!MDB2::fileExists($file_name)) {
  336.                     $msg = "unable to find package '$class_name' file '$file_name'";
  337.                 } else {
  338.                     $msg = "unable to load class '$class_name' from file '$file_name'";
  339.                 }
  340.                 $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null, $msg);
  341.                 return $err;
  342.             }
  343.         }
  344.         return MDB2_OK;
  345.     }
  346.  
  347.     // }}}
  348.     // {{{ function &factory($dsn, $options = false)
  349.  
  350.     /**
  351.      * Create a new MDB2 object for the specified database type
  352.      *
  353.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  354.      * you make sure that you work with a reference of the original
  355.      * object instead of a copy (this is a PHP4 quirk).
  356.      *
  357.      * For example:
  358.      *     $db =& MDB2::factory($dsn);
  359.      *          ^^
  360.      * And not:
  361.      *     $db = MDB2::factory($dsn);
  362.      *
  363.      * @param   mixed   'data source name', see the MDB2::parseDSN
  364.      *                      method for a description of the dsn format.
  365.      *                      Can also be specified as an array of the
  366.      *                      format returned by MDB2::parseDSN.
  367.      * @param   array   An associative array of option names and
  368.      *                            their values.
  369.      *
  370.      * @return  mixed   a newly created MDB2 object, or false on error
  371.      *
  372.      * @access  public
  373.      */
  374.     function &factory($dsn, $options = false)
  375.     {
  376.         $dsninfo = MDB2::parseDSN($dsn);
  377.         if (empty($dsninfo['phptype'])) {
  378.             $err =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND,
  379.                 null, null, 'no RDBMS driver specified');
  380.             return $err;
  381.         }
  382.         $class_name = 'MDB2_Driver_'.$dsninfo['phptype'];
  383.  
  384.         $debug = (!empty($options['debug']));
  385.         $err = MDB2::loadClass($class_name, $debug);
  386.         if (PEAR::isError($err)) {
  387.             return $err;
  388.         }
  389.  
  390.         $db =& new $class_name();
  391.         $db->setDSN($dsninfo);
  392.         $err = MDB2::setOptions($db, $options);
  393.         if (PEAR::isError($err)) {
  394.             return $err;
  395.         }
  396.  
  397.         return $db;
  398.     }
  399.  
  400.     // }}}
  401.     // {{{ function &connect($dsn, $options = false)
  402.  
  403.     /**
  404.      * Create a new MDB2 connection object and connect to the specified
  405.      * database
  406.      *
  407.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  408.      * you make sure that you work with a reference of the original
  409.      * object instead of a copy (this is a PHP4 quirk).
  410.      *
  411.      * For example:
  412.      *     $db =& MDB2::connect($dsn);
  413.      *          ^^
  414.      * And not:
  415.      *     $db = MDB2::connect($dsn);
  416.      *          ^^
  417.      *
  418.      * @param   mixed   'data source name', see the MDB2::parseDSN
  419.      *                            method for a description of the dsn format.
  420.      *                            Can also be specified as an array of the
  421.      *                            format returned by MDB2::parseDSN.
  422.      * @param   array   An associative array of option names and
  423.      *                            their values.
  424.      *
  425.      * @return  mixed   a newly created MDB2 connection object, or a MDB2
  426.      *                  error object on error
  427.      *
  428.      * @access  public
  429.      * @see     MDB2::parseDSN
  430.      */
  431.     function &connect($dsn, $options = false)
  432.     {
  433.         $db =& MDB2::factory($dsn, $options);
  434.         if (PEAR::isError($db)) {
  435.             return $db;
  436.         }
  437.  
  438.         $err = $db->connect();
  439.         if (PEAR::isError($err)) {
  440.             $dsn = $db->getDSN('string', 'xxx');
  441.             $db->disconnect();
  442.             $err->addUserInfo($dsn);
  443.             return $err;
  444.         }
  445.  
  446.         return $db;
  447.     }
  448.  
  449.     // }}}
  450.     // {{{ function &singleton($dsn = null, $options = false)
  451.  
  452.     /**
  453.      * Returns a MDB2 connection with the requested DSN.
  454.      * A new MDB2 connection object is only created if no object with the
  455.      * requested DSN exists yet.
  456.      *
  457.      * IMPORTANT: In order for MDB2 to work properly it is necessary that
  458.      * you make sure that you work with a reference of the original
  459.      * object instead of a copy (this is a PHP4 quirk).
  460.      *
  461.      * For example:
  462.      *     $db =& MDB2::singleton($dsn);
  463.      *          ^^
  464.      * And not:
  465.      *     $db = MDB2::singleton($dsn);
  466.      *          ^^
  467.      *
  468.      * @param   mixed   'data source name', see the MDB2::parseDSN
  469.      *                            method for a description of the dsn format.
  470.      *                            Can also be specified as an array of the
  471.      *                            format returned by MDB2::parseDSN.
  472.      * @param   array   An associative array of option names and
  473.      *                            their values.
  474.      *
  475.      * @return  mixed   a newly created MDB2 connection object, or a MDB2
  476.      *                  error object on error
  477.      *
  478.      * @access  public
  479.      * @see     MDB2::parseDSN
  480.      */
  481.     function &singleton($dsn = null, $options = false)
  482.     {
  483.         if ($dsn) {
  484.             $dsninfo = MDB2::parseDSN($dsn);
  485.             $dsninfo = array_merge($GLOBALS['_MDB2_dsninfo_default'], $dsninfo);
  486.             $keys = array_keys($GLOBALS['_MDB2_databases']);
  487.             for ($i=0, $j=count($keys); $i<$j; ++$i) {
  488.                 if (isset($GLOBALS['_MDB2_databases'][$keys[$i]])) {
  489.                     $tmp_dsn = $GLOBALS['_MDB2_databases'][$keys[$i]]->getDSN('array');
  490.                     if (count(array_diff_assoc($tmp_dsn, $dsninfo)) == 0) {
  491.                         MDB2::setOptions($GLOBALS['_MDB2_databases'][$keys[$i]], $options);
  492.                         return $GLOBALS['_MDB2_databases'][$keys[$i]];
  493.                     }
  494.                 }
  495.             }
  496.         } elseif (is_array($GLOBALS['_MDB2_databases']) && reset($GLOBALS['_MDB2_databases'])) {
  497.             $db =& $GLOBALS['_MDB2_databases'][key($GLOBALS['_MDB2_databases'])];
  498.             return $db;
  499.         }
  500.         $db =& MDB2::factory($dsn, $options);
  501.         return $db;
  502.     }
  503.  
  504.     // }}}
  505.     // {{{ function loadFile($file)
  506.  
  507.     /**
  508.      * load a file (like 'Date')
  509.      *
  510.      * @param   string  name of the file in the MDB2 directory (without '.php')
  511.      *
  512.      * @return  string  name of the file that was included
  513.      *
  514.      * @access  public
  515.      */
  516.     function loadFile($file)
  517.     {
  518.         $file_name = 'MDB2'.DIRECTORY_SEPARATOR.$file.'.php';
  519.         if (!MDB2::fileExists($file_name)) {
  520.             return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  521.                 'unable to find: '.$file_name);
  522.         }
  523.         if (!include_once($file_name)) {
  524.             return MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  525.                 'unable to load driver class: '.$file_name);
  526.         }
  527.         return $file_name;
  528.     }
  529.  
  530.     // }}}
  531.     // {{{ function apiVersion()
  532.  
  533.     /**
  534.      * Return the MDB2 API version
  535.      *
  536.      * @return  string  the MDB2 API version number
  537.      *
  538.      * @access  public
  539.      */
  540.     function apiVersion()
  541.     {
  542.         return '2.4.1';
  543.     }
  544.  
  545.     // }}}
  546.     // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  547.  
  548.     /**
  549.      * This method is used to communicate an error and invoke error
  550.      * callbacks etc.  Basically a wrapper for PEAR::raiseError
  551.      * without the message string.
  552.      *
  553.      * @param   mixed  int error code
  554.      *
  555.      * @param   int    error mode, see PEAR_Error docs
  556.      *
  557.      * @param   mixed  If error mode is PEAR_ERROR_TRIGGER, this is the
  558.      *                 error level (E_USER_NOTICE etc).  If error mode is
  559.      *                 PEAR_ERROR_CALLBACK, this is the callback function,
  560.      *                 either as a function name, or as an array of an
  561.      *                 object and method name.  For other error modes this
  562.      *                 parameter is ignored.
  563.      *
  564.      * @param   string Extra debug information.  Defaults to the last
  565.      *                 query and native error code.
  566.      *
  567.      * @return PEAR_Error instance of a PEAR Error object
  568.      *
  569.      * @access  private
  570.      * @see     PEAR_Error
  571.      */
  572.     function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  573.     {
  574.         $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  575.         return $err;
  576.     }
  577.  
  578.     // }}}
  579.     // {{{ function isError($data, $code = null)
  580.  
  581.     /**
  582.      * Tell whether a value is a MDB2 error.
  583.      *
  584.      * @param   mixed   the value to test
  585.      * @param   int     if is an error object, return true
  586.      *                        only if $code is a string and
  587.      *                        $db->getMessage() == $code or
  588.      *                        $code is an integer and $db->getCode() == $code
  589.      *
  590.      * @return  bool    true if parameter is an error
  591.      *
  592.      * @access  public
  593.      */
  594.     function isError($data, $code = null)
  595.     {
  596.         if (is_a($data, 'MDB2_Error')) {
  597.             if (is_null($code)) {
  598.                 return true;
  599.             } elseif (is_string($code)) {
  600.                 return $data->getMessage() === $code;
  601.             } else {
  602.                 $code = (array)$code;
  603.                 return in_array($data->getCode(), $code);
  604.             }
  605.         }
  606.         return false;
  607.     }
  608.  
  609.     // }}}
  610.     // {{{ function isConnection($value)
  611.  
  612.     /**
  613.      * Tell whether a value is a MDB2 connection
  614.      *
  615.      * @param   mixed   value to test
  616.      *
  617.      * @return  bool    whether $value is a MDB2 connection
  618.      *
  619.      * @access  public
  620.      */
  621.     function isConnection($value)
  622.     {
  623.         return is_a($value, 'MDB2_Driver_Common');
  624.     }
  625.  
  626.     // }}}
  627.     // {{{ function isResult($value)
  628.  
  629.     /**
  630.      * Tell whether a value is a MDB2 result
  631.      *
  632.      * @param   mixed   value to test
  633.      *
  634.      * @return  bool    whether $value is a MDB2 result
  635.      *
  636.      * @access  public
  637.      */
  638.     function isResult($value)
  639.     {
  640.         return is_a($value, 'MDB2_Result');
  641.     }
  642.  
  643.     // }}}
  644.     // {{{ function isResultCommon($value)
  645.  
  646.     /**
  647.      * Tell whether a value is a MDB2 result implementing the common interface
  648.      *
  649.      * @param   mixed   value to test
  650.      *
  651.      * @return  bool    whether $value is a MDB2 result implementing the common interface
  652.      *
  653.      * @access  public
  654.      */
  655.     function isResultCommon($value)
  656.     {
  657.         return is_a($value, 'MDB2_Result_Common');
  658.     }
  659.  
  660.     // }}}
  661.     // {{{ function isStatement($value)
  662.  
  663.     /**
  664.      * Tell whether a value is a MDB2 statement interface
  665.      *
  666.      * @param   mixed   value to test
  667.      *
  668.      * @return  bool    whether $value is a MDB2 statement interface
  669.      *
  670.      * @access  public
  671.      */
  672.     function isStatement($value)
  673.     {
  674.         return is_a($value, 'MDB2_Statement');
  675.     }
  676.  
  677.     // }}}
  678.     // {{{ function errorMessage($value = null)
  679.  
  680.     /**
  681.      * Return a textual error message for a MDB2 error code
  682.      *
  683.      * @param   int|array   integer error code,
  684.                                 null to get the current error code-message map,
  685.                                 or an array with a new error code-message map
  686.      *
  687.      * @return  string  error message, or false if the error code was
  688.      *                  not recognized
  689.      *
  690.      * @access  public
  691.      */
  692.     function errorMessage($value = null)
  693.     {
  694.         static $errorMessages;
  695.  
  696.         if (is_array($value)) {
  697.             $errorMessages = $value;
  698.             return MDB2_OK;
  699.         }
  700.  
  701.         if (!isset($errorMessages)) {
  702.             $errorMessages = array(
  703.                 MDB2_OK                       => 'no error',
  704.                 MDB2_ERROR                    => 'unknown error',
  705.                 MDB2_ERROR_ALREADY_EXISTS     => 'already exists',
  706.                 MDB2_ERROR_CANNOT_CREATE      => 'can not create',
  707.                 MDB2_ERROR_CANNOT_ALTER       => 'can not alter',
  708.                 MDB2_ERROR_CANNOT_REPLACE     => 'can not replace',
  709.                 MDB2_ERROR_CANNOT_DELETE      => 'can not delete',
  710.                 MDB2_ERROR_CANNOT_DROP        => 'can not drop',
  711.                 MDB2_ERROR_CONSTRAINT         => 'constraint violation',
  712.                 MDB2_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
  713.                 MDB2_ERROR_DIVZERO            => 'division by zero',
  714.                 MDB2_ERROR_INVALID            => 'invalid',
  715.                 MDB2_ERROR_INVALID_DATE       => 'invalid date or time',
  716.                 MDB2_ERROR_INVALID_NUMBER     => 'invalid number',
  717.                 MDB2_ERROR_MISMATCH           => 'mismatch',
  718.                 MDB2_ERROR_NODBSELECTED       => 'no database selected',
  719.                 MDB2_ERROR_NOSUCHFIELD        => 'no such field',
  720.                 MDB2_ERROR_NOSUCHTABLE        => 'no such table',
  721.                 MDB2_ERROR_NOT_CAPABLE        => 'MDB2 backend not capable',
  722.                 MDB2_ERROR_NOT_FOUND          => 'not found',
  723.                 MDB2_ERROR_NOT_LOCKED         => 'not locked',
  724.                 MDB2_ERROR_SYNTAX             => 'syntax error',
  725.                 MDB2_ERROR_UNSUPPORTED        => 'not supported',
  726.                 MDB2_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
  727.                 MDB2_ERROR_INVALID_DSN        => 'invalid DSN',
  728.                 MDB2_ERROR_CONNECT_FAILED     => 'connect failed',
  729.                 MDB2_ERROR_NEED_MORE_DATA     => 'insufficient data supplied',
  730.                 MDB2_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
  731.                 MDB2_ERROR_NOSUCHDB           => 'no such database',
  732.                 MDB2_ERROR_ACCESS_VIOLATION   => 'insufficient permissions',
  733.                 MDB2_ERROR_LOADMODULE         => 'error while including on demand module',
  734.                 MDB2_ERROR_TRUNCATED          => 'truncated',
  735.                 MDB2_ERROR_DEADLOCK           => 'deadlock detected',
  736.             );
  737.         }
  738.  
  739.         if (is_null($value)) {
  740.             return $errorMessages;
  741.         }
  742.  
  743.         if (PEAR::isError($value)) {
  744.             $value = $value->getCode();
  745.         }
  746.  
  747.         return isset($errorMessages[$value]) ?
  748.            $errorMessages[$value] : $errorMessages[MDB2_ERROR];
  749.     }
  750.  
  751.     // }}}
  752.     // {{{ function parseDSN($dsn)
  753.  
  754.     /**
  755.      * Parse a data source name.
  756.      *
  757.      * Additional keys can be added by appending a URI query string to the
  758.      * end of the DSN.
  759.      *
  760.      * The format of the supplied DSN is in its fullest form:
  761.      * <code>
  762.      *  phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
  763.      * </code>
  764.      *
  765.      * Most variations are allowed:
  766.      * <code>
  767.      *  phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
  768.      *  phptype://username:password@hostspec/database_name
  769.      *  phptype://username:password@hostspec
  770.      *  phptype://username@hostspec
  771.      *  phptype://hostspec/database
  772.      *  phptype://hostspec
  773.      *  phptype(dbsyntax)
  774.      *  phptype
  775.      * </code>
  776.      *
  777.      * @param   string  Data Source Name to be parsed
  778.      *
  779.      * @return  array   an associative array with the following keys:
  780.      *  + phptype:  Database backend used in PHP (mysql, odbc etc.)
  781.      *  + dbsyntax: Database used with regards to SQL syntax etc.
  782.      *  + protocol: Communication protocol to use (tcp, unix etc.)
  783.      *  + hostspec: Host specification (hostname[:port])
  784.      *  + database: Database to use on the DBMS server
  785.      *  + username: User name for login
  786.      *  + password: Password for login
  787.      *
  788.      * @access  public
  789.      * @author  Tomas V.V.Cox <cox@idecnet.com>
  790.      */
  791.     function parseDSN($dsn)
  792.     {
  793.         $parsed = $GLOBALS['_MDB2_dsninfo_default'];
  794.  
  795.         if (is_array($dsn)) {
  796.             $dsn = array_merge($parsed, $dsn);
  797.             if (!$dsn['dbsyntax']) {
  798.                 $dsn['dbsyntax'] = $dsn['phptype'];
  799.             }
  800.             return $dsn;
  801.         }
  802.  
  803.         // Find phptype and dbsyntax
  804.         if (($pos = strpos($dsn, '://')) !== false) {
  805.             $str = substr($dsn, 0, $pos);
  806.             $dsn = substr($dsn, $pos + 3);
  807.         } else {
  808.             $str = $dsn;
  809.             $dsn = null;
  810.         }
  811.  
  812.         // Get phptype and dbsyntax
  813.         // $str => phptype(dbsyntax)
  814.         if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
  815.             $parsed['phptype']  = $arr[1];
  816.             $parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
  817.         } else {
  818.             $parsed['phptype']  = $str;
  819.             $parsed['dbsyntax'] = $str;
  820.         }
  821.  
  822.         if (!count($dsn)) {
  823.             return $parsed;
  824.         }
  825.  
  826.         // Get (if found): username and password
  827.         // $dsn => username:password@protocol+hostspec/database
  828.         if (($at = strrpos($dsn,'@')) !== false) {
  829.             $str = substr($dsn, 0, $at);
  830.             $dsn = substr($dsn, $at + 1);
  831.             if (($pos = strpos($str, ':')) !== false) {
  832.                 $parsed['username'] = rawurldecode(substr($str, 0, $pos));
  833.                 $parsed['password'] = rawurldecode(substr($str, $pos + 1));
  834.             } else {
  835.                 $parsed['username'] = rawurldecode($str);
  836.             }
  837.         }
  838.  
  839.         // Find protocol and hostspec
  840.  
  841.         // $dsn => proto(proto_opts)/database
  842.         if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
  843.             $proto       = $match[1];
  844.             $proto_opts  = $match[2] ? $match[2] : false;
  845.             $dsn         = $match[3];
  846.  
  847.         // $dsn => protocol+hostspec/database (old format)
  848.         } else {
  849.             if (strpos($dsn, '+') !== false) {
  850.                 list($proto, $dsn) = explode('+', $dsn, 2);
  851.             }
  852.             if (   strpos($dsn, '//') === 0
  853.                 && strpos($dsn, '/', 2) !== false
  854.                 && $parsed['phptype'] == 'oci8'
  855.             ) {
  856.                 //oracle's "Easy Connect" syntax:
  857.                 //"username/password@[//]host[:port][/service_name]"
  858.                 //e.g. "scott/tiger@//mymachine:1521/oracle"
  859.                 $proto_opts = $dsn;
  860.                 $dsn = null;
  861.             } elseif (strpos($dsn, '/') !== false) {
  862.                 list($proto_opts, $dsn) = explode('/', $dsn, 2);
  863.             } else {
  864.                 $proto_opts = $dsn;
  865.                 $dsn = null;
  866.             }
  867.         }
  868.  
  869.         // process the different protocol options
  870.         $parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
  871.         $proto_opts = rawurldecode($proto_opts);
  872.         if (strpos($proto_opts, ':') !== false) {
  873.             list($proto_opts, $parsed['port']) = explode(':', $proto_opts);
  874.         }
  875.         if ($parsed['protocol'] == 'tcp') {
  876.             $parsed['hostspec'] = $proto_opts;
  877.         } elseif ($parsed['protocol'] == 'unix') {
  878.             $parsed['socket'] = $proto_opts;
  879.         }
  880.  
  881.         // Get dabase if any
  882.         // $dsn => database
  883.         if ($dsn) {
  884.             // /database
  885.             if (($pos = strpos($dsn, '?')) === false) {
  886.                 $parsed['database'] = $dsn;
  887.             // /database?param1=value1¶m2=value2
  888.             } else {
  889.                 $parsed['database'] = substr($dsn, 0, $pos);
  890.                 $dsn = substr($dsn, $pos + 1);
  891.                 if (strpos($dsn, '&') !== false) {
  892.                     $opts = explode('&', $dsn);
  893.                 } else { // database?param1=value1
  894.                     $opts = array($dsn);
  895.                 }
  896.                 foreach ($opts as $opt) {
  897.                     list($key, $value) = explode('=', $opt);
  898.                     if (!isset($parsed[$key])) {
  899.                         // don't allow params overwrite
  900.                         $parsed[$key] = rawurldecode($value);
  901.                     }
  902.                 }
  903.             }
  904.         }
  905.  
  906.         return $parsed;
  907.     }
  908.  
  909.     // }}}
  910.     // {{{ function fileExists($file)
  911.  
  912.     /**
  913.      * Checks if a file exists in the include path
  914.      *
  915.      * @param   string  filename
  916.      *
  917.      * @return  bool    true success and false on error
  918.      *
  919.      * @access  public
  920.      */
  921.     function fileExists($file)
  922.     {
  923.         // safe_mode does notwork with is_readable()
  924.         if (!@ini_get('safe_mode')) {
  925.              $dirs = explode(PATH_SEPARATOR, ini_get('include_path'));
  926.              foreach ($dirs as $dir) {
  927.                  if (is_readable($dir . DIRECTORY_SEPARATOR . $file)) {
  928.                      return true;
  929.                  }
  930.             }
  931.         } else {
  932.             $fp = @fopen($file, 'r', true);
  933.             if (is_resource($fp)) {
  934.                 @fclose($fp);
  935.                 return true;
  936.             }
  937.         }
  938.         return false;
  939.     }
  940.     // }}}
  941. }
  942.  
  943. // }}}
  944. // {{{ class MDB2_Error extends PEAR_Error
  945.  
  946. /**
  947.  * MDB2_Error implements a class for reporting portable database error
  948.  * messages.
  949.  *
  950.  * @package     MDB2
  951.  * @category    Database
  952.  * @author Stig Bakken <ssb@fast.no>
  953.  */
  954. class MDB2_Error extends PEAR_Error
  955. {
  956.     // {{{ constructor: function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN, $level = E_USER_NOTICE, $debuginfo = null)
  957.  
  958.     /**
  959.      * MDB2_Error constructor.
  960.      *
  961.      * @param   mixed   MDB2 error code, or string with error message.
  962.      * @param   int     what 'error mode' to operate in
  963.      * @param   int     what error level to use for $mode & PEAR_ERROR_TRIGGER
  964.      * @param   smixed   additional debug info, such as the last query
  965.      */
  966.     function MDB2_Error($code = MDB2_ERROR, $mode = PEAR_ERROR_RETURN,
  967.               $level = E_USER_NOTICE, $debuginfo = null)
  968.     {
  969.         if (is_null($code)) {
  970.             $code = MDB2_ERROR;
  971.         }
  972.         $this->PEAR_Error('MDB2 Error: '.MDB2::errorMessage($code), $code,
  973.             $mode, $level, $debuginfo);
  974.     }
  975.  
  976.     // }}}
  977. }
  978.  
  979. // }}}
  980. // {{{ class MDB2_Driver_Common extends PEAR
  981.  
  982. /**
  983.  * MDB2_Driver_Common: Base class that is extended by each MDB2 driver
  984.  *
  985.  * @package     MDB2
  986.  * @category    Database
  987.  * @author      Lukas Smith <smith@pooteeweet.org>
  988.  */
  989. class MDB2_Driver_Common extends PEAR
  990. {
  991.     // {{{ Variables (Properties)
  992.  
  993.     /**
  994.      * index of the MDB2 object within the $GLOBALS['_MDB2_databases'] array
  995.      * @var     int
  996.      * @access  public
  997.      */
  998.     var $db_index = 0;
  999.  
  1000.     /**
  1001.      * DSN used for the next query
  1002.      * @var     array
  1003.      * @access  protected
  1004.      */
  1005.     var $dsn = array();
  1006.  
  1007.     /**
  1008.      * DSN that was used to create the current connection
  1009.      * @var     array
  1010.      * @access  protected
  1011.      */
  1012.     var $connected_dsn = array();
  1013.  
  1014.     /**
  1015.      * connection resource
  1016.      * @var     mixed
  1017.      * @access  protected
  1018.      */
  1019.     var $connection = 0;
  1020.  
  1021.     /**
  1022.      * if the current opened connection is a persistent connection
  1023.      * @var     bool
  1024.      * @access  protected
  1025.      */
  1026.     var $opened_persistent;
  1027.  
  1028.     /**
  1029.      * the name of the database for the next query
  1030.      * @var     string
  1031.      * @access  protected
  1032.      */
  1033.     var $database_name = '';
  1034.  
  1035.     /**
  1036.      * the name of the database currently selected
  1037.      * @var     string
  1038.      * @access  protected
  1039.      */
  1040.     var $connected_database_name = '';
  1041.  
  1042.     /**
  1043.      * server version information
  1044.      * @var     string
  1045.      * @access  protected
  1046.      */
  1047.     var $connected_server_info = '';
  1048.  
  1049.     /**
  1050.      * list of all supported features of the given driver
  1051.      * @var     array
  1052.      * @access  public
  1053.      */
  1054.     var $supported = array(
  1055.         'sequences' => false,
  1056.         'indexes' => false,
  1057.         'affected_rows' => false,
  1058.         'summary_functions' => false,
  1059.         'order_by_text' => false,
  1060.         'transactions' => false,
  1061.         'savepoints' => false,
  1062.         'current_id' => false,
  1063.         'limit_queries' => false,
  1064.         'LOBs' => false,
  1065.         'replace' => false,
  1066.         'sub_selects' => false,
  1067.         'auto_increment' => false,
  1068.         'primary_key' => false,
  1069.         'result_introspection' => false,
  1070.         'prepared_statements' => false,
  1071.         'identifier_quoting' => false,
  1072.         'pattern_escaping' => false,
  1073.         'new_link' => false,
  1074.     );
  1075.  
  1076.     /**
  1077.      * Array of supported options that can be passed to the MDB2 instance.
  1078.      * 
  1079.      * The options can be set during object creation, using
  1080.      * MDB2::connect(), MDB2::factory() or MDB2::singleton(). The options can 
  1081.      * also be set after the object is created, using MDB2::setOptions() or 
  1082.      * MDB2_Driver_Common::setOption().
  1083.      * The list of available option includes:
  1084.      * <ul>
  1085.      *  <li>$options['ssl'] -> boolean: determines if ssl should be used for connections</li>
  1086.      *  <li>$options['field_case'] -> CASE_LOWER|CASE_UPPER: determines what case to force on field/table names</li>
  1087.      *  <li>$options['disable_query'] -> boolean: determines if queries should be executed</li>
  1088.      *  <li>$options['result_class'] -> string: class used for result sets</li>
  1089.      *  <li>$options['buffered_result_class'] -> string: class used for buffered result sets</li>
  1090.      *  <li>$options['result_wrap_class'] -> string: class used to wrap result sets into</li>
  1091.      *  <li>$options['result_buffering'] -> boolean should results be buffered or not?</li>
  1092.      *  <li>$options['fetch_class'] -> string: class to use when fetch mode object is used</li>
  1093.      *  <li>$options['persistent'] -> boolean: persistent connection?</li>
  1094.      *  <li>$options['debug'] -> integer: numeric debug level</li>
  1095.      *  <li>$options['debug_handler'] -> string: function/method that captures debug messages</li>
  1096.      *  <li>$options['debug_expanded_output'] -> bool: BC option to determine if more context information should be send to the debug handler</li>
  1097.      *  <li>$options['default_text_field_length'] -> integer: default text field length to use</li>
  1098.      *  <li>$options['lob_buffer_length'] -> integer: LOB buffer length</li>
  1099.      *  <li>$options['log_line_break'] -> string: line-break format</li>
  1100.      *  <li>$options['idxname_format'] -> string: pattern for index name</li>
  1101.      *  <li>$options['seqname_format'] -> string: pattern for sequence name</li>
  1102.      *  <li>$options['savepoint_format'] -> string: pattern for auto generated savepoint names</li>
  1103.      *  <li>$options['statement_format'] -> string: pattern for prepared statement names</li>
  1104.      *  <li>$options['seqcol_name'] -> string: sequence column name</li>
  1105.      *  <li>$options['quote_identifier'] -> boolean: if identifier quoting should be done when check_option is used</li>
  1106.      *  <li>$options['use_transactions'] -> boolean: if transaction use should be enabled</li>
  1107.      *  <li>$options['decimal_places'] -> integer: number of decimal places to handle</li>
  1108.      *  <li>$options['portability'] -> integer: portability constant</li>
  1109.      *  <li>$options['modules'] -> array: short to long module name mapping for __call()</li>
  1110.      *  <li>$options['emulate_prepared'] -> boolean: force prepared statements to be emulated</li>
  1111.      *  <li>$options['datatype_map'] -> array: map user defined datatypes to other primitive datatypes</li>
  1112.      *  <li>$options['datatype_map_callback'] -> array: callback function/method that should be called</li>
  1113.      * </ul>
  1114.      *
  1115.      * @var     array
  1116.      * @access  public
  1117.      * @see     MDB2::connect()
  1118.      * @see     MDB2::factory()
  1119.      * @see     MDB2::singleton()
  1120.      * @see     MDB2_Driver_Common::setOption()
  1121.      */
  1122.     var $options = array(
  1123.         'ssl' => false,
  1124.         'field_case' => CASE_LOWER,
  1125.         'disable_query' => false,
  1126.         'result_class' => 'MDB2_Result_%s',
  1127.         'buffered_result_class' => 'MDB2_BufferedResult_%s',
  1128.         'result_wrap_class' => false,
  1129.         'result_buffering' => true,
  1130.         'fetch_class' => 'stdClass',
  1131.         'persistent' => false,
  1132.         'debug' => 0,
  1133.         'debug_handler' => 'MDB2_defaultDebugOutput',
  1134.         'debug_expanded_output' => false,
  1135.         'default_text_field_length' => 4096,
  1136.         'lob_buffer_length' => 8192,
  1137.         'log_line_break' => "\n",
  1138.         'idxname_format' => '%s_idx',
  1139.         'seqname_format' => '%s_seq',
  1140.         'savepoint_format' => 'MDB2_SAVEPOINT_%s',
  1141.         'statement_format' => 'MDB2_STATEMENT_%1$s_%2$s',
  1142.         'seqcol_name' => 'sequence',
  1143.         'quote_identifier' => false,
  1144.         'use_transactions' => true,
  1145.         'decimal_places' => 2,
  1146.         'portability' => MDB2_PORTABILITY_ALL,
  1147.         'modules' => array(
  1148.             'ex' => 'Extended',
  1149.             'dt' => 'Datatype',
  1150.             'mg' => 'Manager',
  1151.             'rv' => 'Reverse',
  1152.             'na' => 'Native',
  1153.             'fc' => 'Function',
  1154.         ),
  1155.         'emulate_prepared' => false,
  1156.         'datatype_map' => array(),
  1157.         'datatype_map_callback' => array(),
  1158.         'nativetype_map_callback' => array(),
  1159.     );
  1160.  
  1161.     /**
  1162.      * string array
  1163.      * @var     string
  1164.      * @access  protected
  1165.      */
  1166.     var $string_quoting = array('start' => "'", 'end' => "'", 'escape' => false, 'escape_pattern' => false);
  1167.  
  1168.     /**
  1169.      * identifier quoting
  1170.      * @var     array
  1171.      * @access  protected
  1172.      */
  1173.     var $identifier_quoting = array('start' => '"', 'end' => '"', 'escape' => '"');
  1174.  
  1175.     /**
  1176.      * sql comments
  1177.      * @var     array
  1178.      * @access  protected
  1179.      */
  1180.     var $sql_comments = array(
  1181.         array('start' => '--', 'end' => "\n", 'escape' => false),
  1182.         array('start' => '/*', 'end' => '*/', 'escape' => false),
  1183.     );
  1184.  
  1185.     /**
  1186.      * comparision wildcards
  1187.      * @var     array
  1188.      * @access  protected
  1189.      */
  1190.     var $wildcards = array('%', '_');
  1191.  
  1192.     /**
  1193.      * column alias keyword
  1194.      * @var     string
  1195.      * @access  protected
  1196.      */
  1197.     var $as_keyword = ' AS ';
  1198.  
  1199.     /**
  1200.      * warnings
  1201.      * @var     array
  1202.      * @access  protected
  1203.      */
  1204.     var $warnings = array();
  1205.  
  1206.     /**
  1207.      * string with the debugging information
  1208.      * @var     string
  1209.      * @access  public
  1210.      */
  1211.     var $debug_output = '';
  1212.  
  1213.     /**
  1214.      * determine if there is an open transaction
  1215.      * @var     bool
  1216.      * @access  protected
  1217.      */
  1218.     var $in_transaction = false;
  1219.  
  1220.     /**
  1221.      * the smart transaction nesting depth
  1222.      * @var     int
  1223.      * @access  protected
  1224.      */
  1225.     var $nested_transaction_counter = null;
  1226.  
  1227.     /**
  1228.      * the first error that occured inside a nested transaction
  1229.      * @var     MDB2_Error|bool
  1230.      * @access  protected
  1231.      */
  1232.     var $has_transaction_error = false;
  1233.  
  1234.     /**
  1235.      * result offset used in the next query
  1236.      * @var     int
  1237.      * @access  protected
  1238.      */
  1239.     var $offset = 0;
  1240.  
  1241.     /**
  1242.      * result limit used in the next query
  1243.      * @var     int
  1244.      * @access  protected
  1245.      */
  1246.     var $limit = 0;
  1247.  
  1248.     /**
  1249.      * Database backend used in PHP (mysql, odbc etc.)
  1250.      * @var     string
  1251.      * @access  public
  1252.      */
  1253.     var $phptype;
  1254.  
  1255.     /**
  1256.      * Database used with regards to SQL syntax etc.
  1257.      * @var     string
  1258.      * @access  public
  1259.      */
  1260.     var $dbsyntax;
  1261.  
  1262.     /**
  1263.      * the last query sent to the driver
  1264.      * @var     string
  1265.      * @access  public
  1266.      */
  1267.     var $last_query;
  1268.  
  1269.     /**
  1270.      * the default fetchmode used
  1271.      * @var     int
  1272.      * @access  protected
  1273.      */
  1274.     var $fetchmode = MDB2_FETCHMODE_ORDERED;
  1275.  
  1276.     /**
  1277.      * array of module instances
  1278.      * @var     array
  1279.      * @access  protected
  1280.      */
  1281.     var $modules = array();
  1282.  
  1283.     /**
  1284.      * determines of the PHP4 destructor emulation has been enabled yet
  1285.      * @var     array
  1286.      * @access  protected
  1287.      */
  1288.     var $destructor_registered = true;
  1289.  
  1290.     // }}}
  1291.     // {{{ constructor: function __construct()
  1292.  
  1293.     /**
  1294.      * Constructor
  1295.      */
  1296.     function __construct()
  1297.     {
  1298.         end($GLOBALS['_MDB2_databases']);
  1299.         $db_index = key($GLOBALS['_MDB2_databases']) + 1;
  1300.         $GLOBALS['_MDB2_databases'][$db_index] = &$this;
  1301.         $this->db_index = $db_index;
  1302.     }
  1303.  
  1304.     // }}}
  1305.     // {{{ function MDB2_Driver_Common()
  1306.  
  1307.     /**
  1308.      * PHP 4 Constructor
  1309.      */
  1310.     function MDB2_Driver_Common()
  1311.     {
  1312.         $this->destructor_registered = false;
  1313.         $this->__construct();
  1314.     }
  1315.  
  1316.     // }}}
  1317.     // {{{ destructor: function __destruct()
  1318.  
  1319.     /**
  1320.      *  Destructor
  1321.      */
  1322.     function __destruct()
  1323.     {
  1324.         $this->disconnect(false);
  1325.     }
  1326.  
  1327.     // }}}
  1328.     // {{{ function free()
  1329.  
  1330.     /**
  1331.      * Free the internal references so that the instance can be destroyed
  1332.      *
  1333.      * @return  bool    true on success, false if result is invalid
  1334.      *
  1335.      * @access  public
  1336.      */
  1337.     function free()
  1338.     {
  1339.         unset($GLOBALS['_MDB2_databases'][$this->db_index]);
  1340.         unset($this->db_index);
  1341.         return MDB2_OK;
  1342.     }
  1343.  
  1344.     // }}}
  1345.     // {{{ function __toString()
  1346.  
  1347.     /**
  1348.      * String conversation
  1349.      *
  1350.      * @return  string representation of the object
  1351.      *
  1352.      * @access  public
  1353.      */
  1354.     function __toString()
  1355.     {
  1356.         $info = get_class($this);
  1357.         $info.= ': (phptype = '.$this->phptype.', dbsyntax = '.$this->dbsyntax.')';
  1358.         if ($this->connection) {
  1359.             $info.= ' [connected]';
  1360.         }
  1361.         return $info;
  1362.     }
  1363.  
  1364.     // }}}
  1365.     // {{{ function errorInfo($error = null)
  1366.  
  1367.     /**
  1368.      * This method is used to collect information about an error
  1369.      *
  1370.      * @param   mixed   error code or resource
  1371.      *
  1372.      * @return  array   with MDB2 errorcode, native error code, native message
  1373.      *
  1374.      * @access  public
  1375.      */
  1376.     function errorInfo($error = null)
  1377.     {
  1378.         return array($error, null, null);
  1379.     }
  1380.  
  1381.     // }}}
  1382.     // {{{ function &raiseError($code = null, $mode = null, $options = null, $userinfo = null)
  1383.  
  1384.     /**
  1385.      * This method is used to communicate an error and invoke error
  1386.      * callbacks etc.  Basically a wrapper for PEAR::raiseError
  1387.      * without the message string.
  1388.      *
  1389.      * @param   mixed   integer error code, or a PEAR error object (all other
  1390.      *                  parameters are ignored if this parameter is an object
  1391.      * @param   int     error mode, see PEAR_Error docs
  1392.      * @param   mixed   If error mode is PEAR_ERROR_TRIGGER, this is the
  1393.          *              error level (E_USER_NOTICE etc).  If error mode is
  1394.      *                  PEAR_ERROR_CALLBACK, this is the callback function,
  1395.      *                  either as a function name, or as an array of an
  1396.      *                  object and method name.  For other error modes this
  1397.      *                  parameter is ignored.
  1398.      * @param   string  Extra debug information.  Defaults to the last
  1399.      *                  query and native error code.
  1400.      * @param   string  name of the method that triggered the error
  1401.      *
  1402.      * @return PEAR_Error   instance of a PEAR Error object
  1403.      *
  1404.      * @access  public
  1405.      * @see     PEAR_Error
  1406.      */
  1407.     function &raiseError($code = null, $mode = null, $options = null, $userinfo = null, $method = null)
  1408.     {
  1409.         $userinfo = "[Error message: $userinfo]\n";
  1410.         // The error is yet a MDB2 error object
  1411.         if (PEAR::isError($code)) {
  1412.             // because we use the static PEAR::raiseError, our global
  1413.             // handler should be used if it is set
  1414.             if (is_null($mode) && !empty($this->_default_error_mode)) {
  1415.                 $mode    = $this->_default_error_mode;
  1416.                 $options = $this->_default_error_options;
  1417.             }
  1418.             if (is_null($userinfo)) {
  1419.                 $userinfo = $code->getUserinfo();
  1420.             }
  1421.             $code = $code->getCode();
  1422.         } elseif ($code == MDB2_ERROR_NOT_FOUND) {
  1423.             // extension not loaded: don't call $this->errorInfo() or the script
  1424.             // will die
  1425.         } elseif (isset($this->connection)) {
  1426.             if (!empty($this->last_query)) {
  1427.                 $userinfo.= "[Last executed query: {$this->last_query}]\n";
  1428.             }
  1429.             $native_errno = $native_msg = null;
  1430.             list($code, $native_errno, $native_msg) = $this->errorInfo($code);
  1431.             if (!is_null($native_errno) && $native_errno !== '') {
  1432.                 $userinfo.= "[Native code: $native_errno]\n";
  1433.             }
  1434.             if (!is_null($native_msg) && $native_msg !== '') {
  1435.                 $userinfo.= "[Native message: ". strip_tags($native_msg) ."]\n";
  1436.             }
  1437.             if (!is_null($method)) {
  1438.                 $userinfo = $method.': '.$userinfo;
  1439.             }
  1440.         }
  1441.  
  1442.         $err =& PEAR::raiseError(null, $code, $mode, $options, $userinfo, 'MDB2_Error', true);
  1443.         if ($err->getMode() !== PEAR_ERROR_RETURN
  1444.             && isset($this->nested_transaction_counter) && !$this->has_transaction_error) {
  1445.             $this->has_transaction_error =& $err;
  1446.         }
  1447.         return $err;
  1448.     }
  1449.  
  1450.     // }}}
  1451.     // {{{ function resetWarnings()
  1452.  
  1453.     /**
  1454.      * reset the warning array
  1455.      *
  1456.      * @return void
  1457.      *
  1458.      * @access  public
  1459.      */
  1460.     function resetWarnings()
  1461.     {
  1462.         $this->warnings = array();
  1463.     }
  1464.  
  1465.     // }}}
  1466.     // {{{ function getWarnings()
  1467.  
  1468.     /**
  1469.      * Get all warnings in reverse order.
  1470.      * This means that the last warning is the first element in the array
  1471.      *
  1472.      * @return  array   with warnings
  1473.      *
  1474.      * @access  public
  1475.      * @see     resetWarnings()
  1476.      */
  1477.     function getWarnings()
  1478.     {
  1479.         return array_reverse($this->warnings);
  1480.     }
  1481.  
  1482.     // }}}
  1483.     // {{{ function setFetchMode($fetchmode, $object_class = 'stdClass')
  1484.  
  1485.     /**
  1486.      * Sets which fetch mode should be used by default on queries
  1487.      * on this connection
  1488.      *
  1489.      * @param   int     MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC
  1490.      *                               or MDB2_FETCHMODE_OBJECT
  1491.      * @param   string  the class name of the object to be returned
  1492.      *                               by the fetch methods when the
  1493.      *                               MDB2_FETCHMODE_OBJECT mode is selected.
  1494.      *                               If no class is specified by default a cast
  1495.      *                               to object from the assoc array row will be
  1496.      *                               done.  There is also the possibility to use
  1497.      *                               and extend the 'MDB2_row' class.
  1498.      *
  1499.      * @return  mixed   MDB2_OK or MDB2 Error Object
  1500.      *
  1501.      * @access  public
  1502.      * @see     MDB2_FETCHMODE_ORDERED, MDB2_FETCHMODE_ASSOC, MDB2_FETCHMODE_OBJECT
  1503.      */
  1504.     function setFetchMode($fetchmode, $object_class = 'stdClass')
  1505.     {
  1506.         switch ($fetchmode) {
  1507.         case MDB2_FETCHMODE_OBJECT:
  1508.             $this->options['fetch_class'] = $object_class;
  1509.         case MDB2_FETCHMODE_ORDERED:
  1510.         case MDB2_FETCHMODE_ASSOC:
  1511.             $this->fetchmode = $fetchmode;
  1512.             break;
  1513.         default:
  1514.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1515.                 'invalid fetchmode mode', __FUNCTION__);
  1516.         }
  1517.  
  1518.         return MDB2_OK;
  1519.     }
  1520.  
  1521.     // }}}
  1522.     // {{{ function setOption($option, $value)
  1523.  
  1524.     /**
  1525.      * set the option for the db class
  1526.      *
  1527.      * @param   string  option name
  1528.      * @param   mixed   value for the option
  1529.      *
  1530.      * @return  mixed   MDB2_OK or MDB2 Error Object
  1531.      *
  1532.      * @access  public
  1533.      */
  1534.     function setOption($option, $value)
  1535.     {
  1536.         if (array_key_exists($option, $this->options)) {
  1537.             $this->options[$option] = $value;
  1538.             return MDB2_OK;
  1539.         }
  1540.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1541.             "unknown option $option", __FUNCTION__);
  1542.     }
  1543.  
  1544.     // }}}
  1545.     // {{{ function getOption($option)
  1546.  
  1547.     /**
  1548.      * Returns the value of an option
  1549.      *
  1550.      * @param   string  option name
  1551.      *
  1552.      * @return  mixed   the option value or error object
  1553.      *
  1554.      * @access  public
  1555.      */
  1556.     function getOption($option)
  1557.     {
  1558.         if (array_key_exists($option, $this->options)) {
  1559.             return $this->options[$option];
  1560.         }
  1561.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1562.             "unknown option $option", __FUNCTION__);
  1563.     }
  1564.  
  1565.     // }}}
  1566.     // {{{ function debug($message, $scope = '', $is_manip = null)
  1567.  
  1568.     /**
  1569.      * set a debug message
  1570.      *
  1571.      * @param   string  message that should be appended to the debug variable
  1572.      * @param   string  usually the method name that triggered the debug call:
  1573.      *                  for example 'query', 'prepare', 'execute', 'parameters',
  1574.      *                  'beginTransaction', 'commit', 'rollback'
  1575.      * @param   array   contains context information about the debug() call
  1576.      *                  common keys are: is_manip, time, result etc.
  1577.      *
  1578.      * @return void
  1579.      *
  1580.      * @access  public
  1581.      */
  1582.     function debug($message, $scope = '', $context = array())
  1583.     {
  1584.         if ($this->options['debug'] && $this->options['debug_handler']) {
  1585.             if (!$this->options['debug_expanded_output']) {
  1586.                 if (!empty($context['when']) && $context['when'] !== 'pre') {
  1587.                     return null;
  1588.                 }
  1589.                 $context = empty($context['is_manip']) ? false : $context['is_manip'];
  1590.             }
  1591.             return call_user_func_array($this->options['debug_handler'], array(&$this, $scope, $message, $context));
  1592.         }
  1593.         return null;
  1594.     }
  1595.  
  1596.     // }}}
  1597.     // {{{ function getDebugOutput()
  1598.  
  1599.     /**
  1600.      * output debug info
  1601.      *
  1602.      * @return  string  content of the debug_output class variable
  1603.      *
  1604.      * @access  public
  1605.      */
  1606.     function getDebugOutput()
  1607.     {
  1608.         return $this->debug_output;
  1609.     }
  1610.  
  1611.     // }}}
  1612.     // {{{ function escape($text)
  1613.  
  1614.     /**
  1615.      * Quotes a string so it can be safely used in a query. It will quote
  1616.      * the text so it can safely be used within a query.
  1617.      *
  1618.      * @param   string  the input string to quote
  1619.      * @param   bool    escape wildcards
  1620.      *
  1621.      * @return  string  quoted string
  1622.      *
  1623.      * @access  public
  1624.      */
  1625.     function escape($text, $escape_wildcards = false)
  1626.     {
  1627.         if ($escape_wildcards) {
  1628.             $text = $this->escapePattern($text);
  1629.         }
  1630.  
  1631.         $text = str_replace($this->string_quoting['end'], $this->string_quoting['escape'] . $this->string_quoting['end'], $text);
  1632.         return $text;
  1633.     }
  1634.  
  1635.     // }}}
  1636.     // {{{ function escapePattern($text)
  1637.  
  1638.     /**
  1639.      * Quotes pattern (% and _) characters in a string)
  1640.      *
  1641.      * EXPERIMENTAL
  1642.      *
  1643.      * WARNING: this function is experimental and may change signature at
  1644.      * any time until labelled as non-experimental
  1645.      *
  1646.      * @param   string  the input string to quote
  1647.      *
  1648.      * @return  string  quoted string
  1649.      *
  1650.      * @access  public
  1651.      */
  1652.     function escapePattern($text)
  1653.     {
  1654.         if ($this->string_quoting['escape_pattern']) {
  1655.             $text = str_replace($this->string_quoting['escape_pattern'], $this->string_quoting['escape_pattern'] . $this->string_quoting['escape_pattern'], $text);
  1656.             foreach ($this->wildcards as $wildcard) {
  1657.                 $text = str_replace($wildcard, $this->string_quoting['escape_pattern'] . $wildcard, $text);
  1658.             }
  1659.         }
  1660.         return $text;
  1661.     }
  1662.  
  1663.     // }}}
  1664.     // {{{ function quoteIdentifier($str, $check_option = false)
  1665.  
  1666.     /**
  1667.      * Quote a string so it can be safely used as a table or column name
  1668.      *
  1669.      * Delimiting style depends on which database driver is being used.
  1670.      *
  1671.      * NOTE: just because you CAN use delimited identifiers doesn't mean
  1672.      * you SHOULD use them.  In general, they end up causing way more
  1673.      * problems than they solve.
  1674.      *
  1675.      * Portability is broken by using the following characters inside
  1676.      * delimited identifiers:
  1677.      *   + backtick (<kbd>`</kbd>) -- due to MySQL
  1678.      *   + double quote (<kbd>"</kbd>) -- due to Oracle
  1679.      *   + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
  1680.      *
  1681.      * Delimited identifiers are known to generally work correctly under
  1682.      * the following drivers:
  1683.      *   + mssql
  1684.      *   + mysql
  1685.      *   + mysqli
  1686.      *   + oci8
  1687.      *   + pgsql
  1688.      *   + sqlite
  1689.      *
  1690.      * InterBase doesn't seem to be able to use delimited identifiers
  1691.      * via PHP 4.  They work fine under PHP 5.
  1692.      *
  1693.      * @param   string  identifier name to be quoted
  1694.      * @param   bool    check the 'quote_identifier' option
  1695.      *
  1696.      * @return  string  quoted identifier string
  1697.      *
  1698.      * @access  public
  1699.      */
  1700.     function quoteIdentifier($str, $check_option = false)
  1701.     {
  1702.         if ($check_option && !$this->options['quote_identifier']) {
  1703.             return $str;
  1704.         }
  1705.         $str = str_replace($this->identifier_quoting['end'], $this->identifier_quoting['escape'] . $this->identifier_quoting['end'], $str);
  1706.         return $this->identifier_quoting['start'] . $str . $this->identifier_quoting['end'];
  1707.     }
  1708.  
  1709.     // }}}
  1710.     // {{{ function getAsKeyword()
  1711.  
  1712.     /**
  1713.      * Gets the string to alias column
  1714.      *
  1715.      * @return string to use when aliasing a column
  1716.      */
  1717.     function getAsKeyword()
  1718.     {
  1719.         return $this->as_keyword;
  1720.     }
  1721.  
  1722.     // }}}
  1723.     // {{{ function getConnection()
  1724.  
  1725.     /**
  1726.      * Returns a native connection
  1727.      *
  1728.      * @return  mixed   a valid MDB2 connection object,
  1729.      *                  or a MDB2 error object on error
  1730.      *
  1731.      * @access  public
  1732.      */
  1733.     function getConnection()
  1734.     {
  1735.         $result = $this->connect();
  1736.         if (PEAR::isError($result)) {
  1737.             return $result;
  1738.         }
  1739.         return $this->connection;
  1740.     }
  1741.  
  1742.      // }}}
  1743.     // {{{ function _fixResultArrayValues(&$row, $mode)
  1744.  
  1745.     /**
  1746.      * Do all necessary conversions on result arrays to fix DBMS quirks
  1747.      *
  1748.      * @param   array   the array to be fixed (passed by reference)
  1749.      * @param   array   bit-wise addition of the required portability modes
  1750.      *
  1751.      * @return  void
  1752.      *
  1753.      * @access  protected
  1754.      */
  1755.     function _fixResultArrayValues(&$row, $mode)
  1756.     {
  1757.         switch ($mode) {
  1758.         case MDB2_PORTABILITY_EMPTY_TO_NULL:
  1759.             foreach ($row as $key => $value) {
  1760.                 if ($value === '') {
  1761.                     $row[$key] = null;
  1762.                 }
  1763.             }
  1764.             break;
  1765.         case MDB2_PORTABILITY_RTRIM:
  1766.             foreach ($row as $key => $value) {
  1767.                 if (is_string($value)) {
  1768.                     $row[$key] = rtrim($value);
  1769.                 }
  1770.             }
  1771.             break;
  1772.         case MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES:
  1773.             $tmp_row = array();
  1774.             foreach ($row as $key => $value) {
  1775.                 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1776.             }
  1777.             $row = $tmp_row;
  1778.             break;
  1779.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL):
  1780.             foreach ($row as $key => $value) {
  1781.                 if ($value === '') {
  1782.                     $row[$key] = null;
  1783.                 } elseif (is_string($value)) {
  1784.                     $row[$key] = rtrim($value);
  1785.                 }
  1786.             }
  1787.             break;
  1788.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1789.             $tmp_row = array();
  1790.             foreach ($row as $key => $value) {
  1791.                 if (is_string($value)) {
  1792.                     $value = rtrim($value);
  1793.                 }
  1794.                 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1795.             }
  1796.             $row = $tmp_row;
  1797.             break;
  1798.         case (MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1799.             $tmp_row = array();
  1800.             foreach ($row as $key => $value) {
  1801.                 if ($value === '') {
  1802.                     $value = null;
  1803.                 }
  1804.                 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1805.             }
  1806.             $row = $tmp_row;
  1807.             break;
  1808.         case (MDB2_PORTABILITY_RTRIM + MDB2_PORTABILITY_EMPTY_TO_NULL + MDB2_PORTABILITY_FIX_ASSOC_FIELD_NAMES):
  1809.             $tmp_row = array();
  1810.             foreach ($row as $key => $value) {
  1811.                 if ($value === '') {
  1812.                     $value = null;
  1813.                 } elseif (is_string($value)) {
  1814.                     $value = rtrim($value);
  1815.                 }
  1816.                 $tmp_row[preg_replace('/^(?:.*\.)?([^.]+)$/', '\\1', $key)] = $value;
  1817.             }
  1818.             $row = $tmp_row;
  1819.             break;
  1820.         }
  1821.     }
  1822.  
  1823.     // }}}
  1824.     // {{{ function &loadModule($module, $property = null, $phptype_specific = null)
  1825.  
  1826.     /**
  1827.      * loads a module
  1828.      *
  1829.      * @param   string  name of the module that should be loaded
  1830.      *                  (only used for error messages)
  1831.      * @param   string  name of the property into which the class will be loaded
  1832.      * @param   bool    if the class to load for the module is specific to the
  1833.      *                  phptype
  1834.      *
  1835.      * @return  object  on success a reference to the given module is returned
  1836.      *                  and on failure a PEAR error
  1837.      *
  1838.      * @access  public
  1839.      */
  1840.     function &loadModule($module, $property = null, $phptype_specific = null)
  1841.     {
  1842.         if (!$property) {
  1843.             $property = strtolower($module);
  1844.         }
  1845.  
  1846.         if (!isset($this->{$property})) {
  1847.             $version = $phptype_specific;
  1848.             if ($phptype_specific !== false) {
  1849.                 $version = true;
  1850.                 $class_name = 'MDB2_Driver_'.$module.'_'.$this->phptype;
  1851.                 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  1852.             }
  1853.             if ($phptype_specific === false
  1854.                 || (!MDB2::classExists($class_name) && !MDB2::fileExists($file_name))
  1855.             ) {
  1856.                 $version = false;
  1857.                 $class_name = 'MDB2_'.$module;
  1858.                 $file_name = str_replace('_', DIRECTORY_SEPARATOR, $class_name).'.php';
  1859.             }
  1860.  
  1861.             $err = MDB2::loadClass($class_name, $this->getOption('debug'));
  1862.             if (PEAR::isError($err)) {
  1863.                 return $err;
  1864.             }
  1865.  
  1866.             // load modul in a specific version
  1867.             if ($version) {
  1868.                 if (method_exists($class_name, 'getClassName')) {
  1869.                     $class_name_new = call_user_func(array($class_name, 'getClassName'), $this->db_index);
  1870.                     if ($class_name != $class_name_new) {
  1871.                         $class_name = $class_name_new;
  1872.                         $err = MDB2::loadClass($class_name, $this->getOption('debug'));
  1873.                         if (PEAR::isError($err)) {
  1874.                             return $err;
  1875.                         }
  1876.                     }
  1877.                 }
  1878.             }
  1879.  
  1880.             if (!MDB2::classExists($class_name)) {
  1881.                 $err =& $this->raiseError(MDB2_ERROR_LOADMODULE, null, null,
  1882.                     "unable to load module '$module' into property '$property'", __FUNCTION__);
  1883.                 return $err;
  1884.             }
  1885.             $this->{$property} =& new $class_name($this->db_index);
  1886.             $this->modules[$module] =& $this->{$property};
  1887.             if ($version) {
  1888.                 // this will be used in the connect method to determine if the module
  1889.                 // needs to be loaded with a different version if the server
  1890.                 // version changed in between connects
  1891.                 $this->loaded_version_modules[] = $property;
  1892.             }
  1893.         }
  1894.  
  1895.         return $this->{$property};
  1896.     }
  1897.  
  1898.     // }}}
  1899.     // {{{ function __call($method, $params)
  1900.  
  1901.     /**
  1902.      * Calls a module method using the __call magic method
  1903.      *
  1904.      * @param   string  Method name.
  1905.      * @param   array   Arguments.
  1906.      *
  1907.      * @return  mixed   Returned value.
  1908.      */
  1909.     function __call($method, $params)
  1910.     {
  1911.         $module = null;
  1912.         if (preg_match('/^([a-z]+)([A-Z])(.*)$/', $method, $match)
  1913.             && isset($this->options['modules'][$match[1]])
  1914.         ) {
  1915.             $module = $this->options['modules'][$match[1]];
  1916.             $method = strtolower($match[2]).$match[3];
  1917.             if (!isset($this->modules[$module]) || !is_object($this->modules[$module])) {
  1918.                 $result =& $this->loadModule($module);
  1919.                 if (PEAR::isError($result)) {
  1920.                     return $result;
  1921.                 }
  1922.             }
  1923.         } else {
  1924.             foreach ($this->modules as $key => $foo) {
  1925.                 if (is_object($this->modules[$key])
  1926.                     && method_exists($this->modules[$key], $method)
  1927.                 ) {
  1928.                     $module = $key;
  1929.                     break;
  1930.                 }
  1931.             }
  1932.         }
  1933.         if (!is_null($module)) {
  1934.             return call_user_func_array(array(&$this->modules[$module], $method), $params);
  1935.         }
  1936.         trigger_error(sprintf('Call to undefined function: %s::%s().', get_class($this), $method), E_USER_ERROR);
  1937.     }
  1938.  
  1939.     // }}}
  1940.     // {{{ function beginTransaction($savepoint = null)
  1941.  
  1942.     /**
  1943.      * Start a transaction or set a savepoint.
  1944.      *
  1945.      * @param   string  name of a savepoint to set
  1946.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  1947.      *
  1948.      * @access  public
  1949.      */
  1950.     function beginTransaction($savepoint = null)
  1951.     {
  1952.         $this->debug('Starting transaction', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  1953.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1954.             'transactions are not supported', __FUNCTION__);
  1955.     }
  1956.  
  1957.     // }}}
  1958.     // {{{ function commit($savepoint = null)
  1959.  
  1960.     /**
  1961.      * Commit the database changes done during a transaction that is in
  1962.      * progress or release a savepoint. This function may only be called when
  1963.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  1964.      * transaction is implicitly started after committing the pending changes.
  1965.      *
  1966.      * @param   string  name of a savepoint to release
  1967.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  1968.      *
  1969.      * @access  public
  1970.      */
  1971.     function commit($savepoint = null)
  1972.     {
  1973.         $this->debug('Committing transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  1974.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1975.             'commiting transactions is not supported', __FUNCTION__);
  1976.     }
  1977.  
  1978.     // }}}
  1979.     // {{{ function rollback($savepoint = null)
  1980.  
  1981.     /**
  1982.      * Cancel any database changes done during a transaction or since a specific
  1983.      * savepoint that is in progress. This function may only be called when
  1984.      * auto-committing is disabled, otherwise it will fail. Therefore, a new
  1985.      * transaction is implicitly started after canceling the pending changes.
  1986.      *
  1987.      * @param   string  name of a savepoint to rollback to
  1988.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  1989.      *
  1990.      * @access  public
  1991.      */
  1992.     function rollback($savepoint = null)
  1993.     {
  1994.         $this->debug('Rolling back transaction/savepoint', __FUNCTION__, array('is_manip' => true, 'savepoint' => $savepoint));
  1995.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  1996.             'rolling back transactions is not supported', __FUNCTION__);
  1997.     }
  1998.  
  1999.     // }}}
  2000.     // {{{ function inTransaction($ignore_nested = false)
  2001.  
  2002.     /**
  2003.      * If a transaction is currently open.
  2004.      *
  2005.      * @param   bool    if the nested transaction count should be ignored
  2006.      * @return  int|bool    - an integer with the nesting depth is returned if a
  2007.      *                      nested transaction is open
  2008.      *                      - true is returned for a normal open transaction
  2009.      *                      - false is returned if no transaction is open
  2010.      *
  2011.      * @access  public
  2012.      */
  2013.     function inTransaction($ignore_nested = false)
  2014.     {
  2015.         if (!$ignore_nested && isset($this->nested_transaction_counter)) {
  2016.             return $this->nested_transaction_counter;
  2017.         }
  2018.         return $this->in_transaction;
  2019.     }
  2020.  
  2021.     // }}}
  2022.     // {{{ function setTransactionIsolation($isolation)
  2023.  
  2024.     /**
  2025.      * Set the transacton isolation level.
  2026.      *
  2027.      * @param   string  standard isolation level
  2028.      *                  READ UNCOMMITTED (allows dirty reads)
  2029.      *                  READ COMMITTED (prevents dirty reads)
  2030.      *                  REPEATABLE READ (prevents nonrepeatable reads)
  2031.      *                  SERIALIZABLE (prevents phantom reads)
  2032.      * @param   array some transaction options:
  2033.      *                  'wait' => 'WAIT' | 'NO WAIT'
  2034.      *                  'rw'   => 'READ WRITE' | 'READ ONLY'
  2035.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  2036.      *
  2037.      * @access  public
  2038.      * @since   2.1.1
  2039.      */
  2040.     function setTransactionIsolation($isolation, $options = array())
  2041.     {
  2042.         $this->debug('Setting transaction isolation level', __FUNCTION__, array('is_manip' => true));
  2043.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2044.             'isolation level setting is not supported', __FUNCTION__);
  2045.     }
  2046.  
  2047.     // }}}
  2048.     // {{{ function beginNestedTransaction($savepoint = false)
  2049.  
  2050.     /**
  2051.      * Start a nested transaction.
  2052.      *
  2053.      * EXPERIMENTAL
  2054.      *
  2055.      * WARNING: this function is experimental and may change signature at
  2056.      * any time until labelled as non-experimental
  2057.      *
  2058.      * @return  mixed   MDB2_OK on success/savepoint name, a MDB2 error on failure
  2059.      *
  2060.      * @access  public
  2061.      * @since   2.1.1
  2062.      */
  2063.     function beginNestedTransaction()
  2064.     {
  2065.         if ($this->in_transaction) {
  2066.             ++$this->nested_transaction_counter;
  2067.             $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
  2068.             if ($this->supports('savepoints') && $savepoint) {
  2069.                 return $this->beginTransaction($savepoint);
  2070.             }
  2071.             return MDB2_OK;
  2072.         }
  2073.         $this->has_transaction_error = false;
  2074.         $result = $this->beginTransaction();
  2075.         $this->nested_transaction_counter = 1;
  2076.         return $result;
  2077.     }
  2078.  
  2079.     // }}}
  2080.     // {{{ function completeNestedTransaction($force_rollback = false, $release = false)
  2081.  
  2082.     /**
  2083.      * Finish a nested transaction by rolling back if an error occured or
  2084.      * committing otherwise.
  2085.      *
  2086.      * EXPERIMENTAL
  2087.      *
  2088.      * WARNING: this function is experimental and may change signature at
  2089.      * any time until labelled as non-experimental
  2090.      *
  2091.      * @param   bool    if the transaction should be rolled back regardless
  2092.      *                  even if no error was set within the nested transaction
  2093.      * @return  mixed   MDB_OK on commit/counter decrementing, false on rollback
  2094.      *                  and a MDB2 error on failure
  2095.      *
  2096.      * @access  public
  2097.      * @since   2.1.1
  2098.      */
  2099.     function completeNestedTransaction($force_rollback = false)
  2100.     {
  2101.         if ($this->nested_transaction_counter > 1) {
  2102.             $savepoint = sprintf($this->options['savepoint_format'], $this->nested_transaction_counter);
  2103.             if ($this->supports('savepoints') && $savepoint) {
  2104.                 if ($force_rollback || $this->has_transaction_error) {
  2105.                     $result = $this->rollback($savepoint);
  2106.                     if (!PEAR::isError($result)) {
  2107.                         $result = false;
  2108.                         $this->has_transaction_error = false;
  2109.                     }
  2110.                 } else {
  2111.                     $result = $this->commit($savepoint);
  2112.                 }
  2113.             } else {
  2114.                 $result = MDB2_OK;
  2115.             }
  2116.             --$this->nested_transaction_counter;
  2117.             return $result;
  2118.         }
  2119.  
  2120.         $this->nested_transaction_counter = null;
  2121.         $result = MDB2_OK;
  2122.  
  2123.         // transaction has not yet been rolled back
  2124.         if ($this->in_transaction) {
  2125.             if ($force_rollback || $this->has_transaction_error) {
  2126.                 $result = $this->rollback();
  2127.                 if (!PEAR::isError($result)) {
  2128.                     $result = false;
  2129.                 }
  2130.             } else {
  2131.                 $result = $this->commit();
  2132.             }
  2133.         }
  2134.         $this->has_transaction_error = false;
  2135.         return $result;
  2136.     }
  2137.  
  2138.     // }}}
  2139.     // {{{ function failNestedTransaction($error = null, $immediately = false)
  2140.  
  2141.     /**
  2142.      * Force setting nested transaction to failed.
  2143.      *
  2144.      * EXPERIMENTAL
  2145.      *
  2146.      * WARNING: this function is experimental and may change signature at
  2147.      * any time until labelled as non-experimental
  2148.      *
  2149.      * @param   mixed   value to return in getNestededTransactionError()
  2150.      * @param   bool    if the transaction should be rolled back immediately
  2151.      * @return  bool    MDB2_OK
  2152.      *
  2153.      * @access  public
  2154.      * @since   2.1.1
  2155.      */
  2156.     function failNestedTransaction($error = null, $immediately = false)
  2157.     {
  2158.         if (is_null($error)) {
  2159.             $error = $this->has_transaction_error ? $this->has_transaction_error : true;
  2160.         } elseif (!$error) {
  2161.             $error = true;
  2162.         }
  2163.         $this->has_transaction_error = $error;
  2164.         if (!$immediately) {
  2165.             return MDB2_OK;
  2166.         }
  2167.         return $this->rollback();
  2168.     }
  2169.  
  2170.     // }}}
  2171.     // {{{ function getNestedTransactionError()
  2172.  
  2173.     /**
  2174.      * The first error that occured since the transaction start.
  2175.      *
  2176.      * EXPERIMENTAL
  2177.      *
  2178.      * WARNING: this function is experimental and may change signature at
  2179.      * any time until labelled as non-experimental
  2180.      *
  2181.      * @return  MDB2_Error|bool     MDB2 error object if an error occured or false.
  2182.      *
  2183.      * @access  public
  2184.      * @since   2.1.1
  2185.      */
  2186.     function getNestedTransactionError()
  2187.     {
  2188.         return $this->has_transaction_error;
  2189.     }
  2190.  
  2191.     // }}}
  2192.     // {{{ connect()
  2193.  
  2194.     /**
  2195.      * Connect to the database
  2196.      *
  2197.      * @return true on success, MDB2 Error Object on failure
  2198.      */
  2199.     function connect()
  2200.     {
  2201.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2202.             'method not implemented', __FUNCTION__);
  2203.     }
  2204.  
  2205.     // }}}
  2206.     // {{{ setCharset($charset, $connection = null)
  2207.  
  2208.     /**
  2209.      * Set the charset on the current connection
  2210.      *
  2211.      * @param string    charset
  2212.      * @param resource  connection handle
  2213.      *
  2214.      * @return true on success, MDB2 Error Object on failure
  2215.      */
  2216.     function setCharset($charset, $connection = null)
  2217.     {
  2218.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2219.             'method not implemented', __FUNCTION__);
  2220.     }
  2221.  
  2222.     // }}}
  2223.     // {{{ function disconnect($force = true)
  2224.  
  2225.     /**
  2226.      * Log out and disconnect from the database.
  2227.      *
  2228.      * @param   bool    if the disconnect should be forced even if the
  2229.      *                  connection is opened persistently
  2230.      *
  2231.      * @return  mixed   true on success, false if not connected and error
  2232.      *                  object on error
  2233.      *
  2234.      * @access  public
  2235.      */
  2236.     function disconnect($force = true)
  2237.     {
  2238.         $this->connection = 0;
  2239.         $this->connected_dsn = array();
  2240.         $this->connected_database_name = '';
  2241.         $this->opened_persistent = null;
  2242.         $this->connected_server_info = '';
  2243.         $this->in_transaction = null;
  2244.         $this->nested_transaction_counter = null;
  2245.         return MDB2_OK;
  2246.     }
  2247.  
  2248.     // }}}
  2249.     // {{{ function setDatabase($name)
  2250.  
  2251.     /**
  2252.      * Select a different database
  2253.      *
  2254.      * @param   string  name of the database that should be selected
  2255.      *
  2256.      * @return  string  name of the database previously connected to
  2257.      *
  2258.      * @access  public
  2259.      */
  2260.     function setDatabase($name)
  2261.     {
  2262.         $previous_database_name = (isset($this->database_name)) ? $this->database_name : '';
  2263.         $this->database_name = $name;
  2264.         $this->disconnect(false);
  2265.         return $previous_database_name;
  2266.     }
  2267.  
  2268.     // }}}
  2269.     // {{{ function getDatabase()
  2270.  
  2271.     /**
  2272.      * Get the current database
  2273.      *
  2274.      * @return  string  name of the database
  2275.      *
  2276.      * @access  public
  2277.      */
  2278.     function getDatabase()
  2279.     {
  2280.         return $this->database_name;
  2281.     }
  2282.  
  2283.     // }}}
  2284.     // {{{ function setDSN($dsn)
  2285.  
  2286.     /**
  2287.      * set the DSN
  2288.      *
  2289.      * @param   mixed   DSN string or array
  2290.      *
  2291.      * @return  MDB2_OK
  2292.      *
  2293.      * @access  public
  2294.      */
  2295.     function setDSN($dsn)
  2296.     {
  2297.         $dsn_default = $GLOBALS['_MDB2_dsninfo_default'];
  2298.         $dsn = MDB2::parseDSN($dsn);
  2299.         if (array_key_exists('database', $dsn)) {
  2300.             $this->database_name = $dsn['database'];
  2301.             unset($dsn['database']);
  2302.         }
  2303.         $this->dsn = array_merge($dsn_default, $dsn);
  2304.         return $this->disconnect(false);
  2305.     }
  2306.  
  2307.     // }}}
  2308.     // {{{ function getDSN($type = 'string', $hidepw = false)
  2309.  
  2310.     /**
  2311.      * return the DSN as a string
  2312.      *
  2313.      * @param   string  format to return ("array", "string")
  2314.      * @param   string  string to hide the password with
  2315.      *
  2316.      * @return  mixed   DSN in the chosen type
  2317.      *
  2318.      * @access  public
  2319.      */
  2320.     function getDSN($type = 'string', $hidepw = false)
  2321.     {
  2322.         $dsn = array_merge($GLOBALS['_MDB2_dsninfo_default'], $this->dsn);
  2323.         $dsn['phptype'] = $this->phptype;
  2324.         $dsn['database'] = $this->database_name;
  2325.         if ($hidepw) {
  2326.             $dsn['password'] = $hidepw;
  2327.         }
  2328.         switch ($type) {
  2329.         // expand to include all possible options
  2330.         case 'string':
  2331.            $dsn = $dsn['phptype'].
  2332.                ($dsn['dbsyntax'] ? ('('.$dsn['dbsyntax'].')') : '').
  2333.                '://'.$dsn['username'].':'.
  2334.                 $dsn['password'].'@'.$dsn['hostspec'].
  2335.                 ($dsn['port'] ? (':'.$dsn['port']) : '').
  2336.                 '/'.$dsn['database'];
  2337.             break;
  2338.         case 'array':
  2339.         default:
  2340.             break;
  2341.         }
  2342.         return $dsn;
  2343.     }
  2344.  
  2345.     // }}}
  2346.     // {{{ function &standaloneQuery($query, $types = null, $is_manip = false)
  2347.  
  2348.    /**
  2349.      * execute a query as database administrator
  2350.      *
  2351.      * @param   string  the SQL query
  2352.      * @param   mixed   array that contains the types of the columns in
  2353.      *                        the result set
  2354.      * @param   bool    if the query is a manipulation query
  2355.      *
  2356.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  2357.      *
  2358.      * @access  public
  2359.      */
  2360.     function &standaloneQuery($query, $types = null, $is_manip = false)
  2361.     {
  2362.         $offset = $this->offset;
  2363.         $limit = $this->limit;
  2364.         $this->offset = $this->limit = 0;
  2365.         $query = $this->_modifyQuery($query, $is_manip, $limit, $offset);
  2366.  
  2367.         $connection = $this->getConnection();
  2368.         if (PEAR::isError($connection)) {
  2369.             return $connection;
  2370.         }
  2371.  
  2372.         $result =& $this->_doQuery($query, $is_manip, $connection, false);
  2373.         if (PEAR::isError($result)) {
  2374.             return $result;
  2375.         }
  2376.  
  2377.         if ($is_manip) {
  2378.             $affected_rows =  $this->_affectedRows($connection, $result);
  2379.             return $affected_rows;
  2380.         }
  2381.         $result =& $this->_wrapResult($result, $types, true, false, $limit, $offset);
  2382.         return $result;
  2383.     }
  2384.  
  2385.     // }}}
  2386.     // {{{ function _modifyQuery($query, $is_manip, $limit, $offset)
  2387.  
  2388.     /**
  2389.      * Changes a query string for various DBMS specific reasons
  2390.      *
  2391.      * @param   string  query to modify
  2392.      * @param   bool    if it is a DML query
  2393.      * @param   int  limit the number of rows
  2394.      * @param   int  start reading from given offset
  2395.      *
  2396.      * @return  string  modified query
  2397.      *
  2398.      * @access  protected
  2399.      */
  2400.     function _modifyQuery($query, $is_manip, $limit, $offset)
  2401.     {
  2402.         return $query;
  2403.     }
  2404.  
  2405.     // }}}
  2406.     // {{{ function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  2407.  
  2408.     /**
  2409.      * Execute a query
  2410.      * @param   string  query
  2411.      * @param   bool    if the query is a manipulation query
  2412.      * @param   resource connection handle
  2413.      * @param   string  database name
  2414.      *
  2415.      * @return  result or error object
  2416.      *
  2417.      * @access  protected
  2418.      */
  2419.     function &_doQuery($query, $is_manip = false, $connection = null, $database_name = null)
  2420.     {
  2421.         $this->last_query = $query;
  2422.         $result = $this->debug($query, 'query', array('is_manip' => $is_manip, 'when' => 'pre'));
  2423.         if ($result) {
  2424.             if (PEAR::isError($result)) {
  2425.                 return $result;
  2426.             }
  2427.             $query = $result;
  2428.         }
  2429.         $err =& $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2430.             'method not implemented', __FUNCTION__);
  2431.         return $err;
  2432.     }
  2433.  
  2434.     // }}}
  2435.     // {{{ function _affectedRows($connection, $result = null)
  2436.  
  2437.     /**
  2438.      * Returns the number of rows affected
  2439.      *
  2440.      * @param   resource result handle
  2441.      * @param   resource connection handle
  2442.      *
  2443.      * @return  mixed   MDB2 Error Object or the number of rows affected
  2444.      *
  2445.      * @access  private
  2446.      */
  2447.     function _affectedRows($connection, $result = null)
  2448.     {
  2449.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2450.             'method not implemented', __FUNCTION__);
  2451.     }
  2452.  
  2453.     // }}}
  2454.     // {{{ function &exec($query)
  2455.  
  2456.     /**
  2457.      * Execute a manipulation query to the database and return the number of affected rows
  2458.      *
  2459.      * @param   string  the SQL query
  2460.      *
  2461.      * @return  mixed   number of affected rows on success, a MDB2 error on failure
  2462.      *
  2463.      * @access  public
  2464.      */
  2465.     function &exec($query)
  2466.     {
  2467.         $offset = $this->offset;
  2468.         $limit = $this->limit;
  2469.         $this->offset = $this->limit = 0;
  2470.         $query = $this->_modifyQuery($query, true, $limit, $offset);
  2471.  
  2472.         $connection = $this->getConnection();
  2473.         if (PEAR::isError($connection)) {
  2474.             return $connection;
  2475.         }
  2476.  
  2477.         $result =& $this->_doQuery($query, true, $connection, $this->database_name);
  2478.         if (PEAR::isError($result)) {
  2479.             return $result;
  2480.         }
  2481.  
  2482.         $affectedRows = $this->_affectedRows($connection, $result);
  2483.         return $affectedRows;
  2484.     }
  2485.  
  2486.     // }}}
  2487.     // {{{ function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
  2488.  
  2489.     /**
  2490.      * Send a query to the database and return any results
  2491.      *
  2492.      * @param   string  the SQL query
  2493.      * @param   mixed   array that contains the types of the columns in
  2494.      *                        the result set
  2495.      * @param   mixed   string which specifies which result class to use
  2496.      * @param   mixed   string which specifies which class to wrap results in
  2497.      *
  2498.      * @return mixed   an MDB2_Result handle on success, a MDB2 error on failure
  2499.      *
  2500.      * @access  public
  2501.      */
  2502.     function &query($query, $types = null, $result_class = true, $result_wrap_class = false)
  2503.     {
  2504.         $offset = $this->offset;
  2505.         $limit = $this->limit;
  2506.         $this->offset = $this->limit = 0;
  2507.         $query = $this->_modifyQuery($query, false, $limit, $offset);
  2508.  
  2509.         $connection = $this->getConnection();
  2510.         if (PEAR::isError($connection)) {
  2511.             return $connection;
  2512.         }
  2513.  
  2514.         $result =& $this->_doQuery($query, false, $connection, $this->database_name);
  2515.         if (PEAR::isError($result)) {
  2516.             return $result;
  2517.         }
  2518.  
  2519.         $result =& $this->_wrapResult($result, $types, $result_class, $result_wrap_class, $limit, $offset);
  2520.         return $result;
  2521.     }
  2522.  
  2523.     // }}}
  2524.     // {{{ function &_wrapResult($result, $types = array(), $result_class = true, $result_wrap_class = false, $limit = null, $offset = null)
  2525.  
  2526.     /**
  2527.      * wrap a result set into the correct class
  2528.      *
  2529.      * @param   resource result handle
  2530.      * @param   mixed   array that contains the types of the columns in
  2531.      *                        the result set
  2532.      * @param   mixed   string which specifies which result class to use
  2533.      * @param   mixed   string which specifies which class to wrap results in
  2534.      * @param   string  number of rows to select
  2535.      * @param   string  first row to select
  2536.      *
  2537.      * @return mixed   an MDB2_Result, a MDB2 error on failure
  2538.      *
  2539.      * @access  protected
  2540.      */
  2541.     function &_wrapResult($result, $types = array(), $result_class = true,
  2542.         $result_wrap_class = false, $limit = null, $offset = null)
  2543.     {
  2544.         if ($types === true) {
  2545.             if ($this->supports('result_introspection')) {
  2546.                 $this->loadModule('Reverse', null, true);
  2547.                 $tableInfo = $this->reverse->tableInfo($result);
  2548.                 if (PEAR::isError($tableInfo)) {
  2549.                     return $tableInfo;
  2550.                 }
  2551.                 $types = array();
  2552.                 foreach ($tableInfo as $field) {
  2553.                     $types[] = $field['mdb2type'];
  2554.                 }
  2555.             } else {
  2556.                 $types = null;
  2557.             }
  2558.         }
  2559.  
  2560.         if ($result_class === true) {
  2561.             $result_class = $this->options['result_buffering']
  2562.                 ? $this->options['buffered_result_class'] : $this->options['result_class'];
  2563.         }
  2564.  
  2565.         if ($result_class) {
  2566.             $class_name = sprintf($result_class, $this->phptype);
  2567.             if (!MDB2::classExists($class_name)) {
  2568.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2569.                     'result class does not exist '.$class_name, __FUNCTION__);
  2570.                 return $err;
  2571.             }
  2572.             $result =& new $class_name($this, $result, $limit, $offset);
  2573.             if (!MDB2::isResultCommon($result)) {
  2574.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2575.                     'result class is not extended from MDB2_Result_Common', __FUNCTION__);
  2576.                 return $err;
  2577.             }
  2578.             if (!empty($types)) {
  2579.                 $err = $result->setResultTypes($types);
  2580.                 if (PEAR::isError($err)) {
  2581.                     $result->free();
  2582.                     return $err;
  2583.                 }
  2584.             }
  2585.         }
  2586.         if ($result_wrap_class === true) {
  2587.             $result_wrap_class = $this->options['result_wrap_class'];
  2588.         }
  2589.         if ($result_wrap_class) {
  2590.             if (!MDB2::classExists($result_wrap_class)) {
  2591.                 $err =& $this->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  2592.                     'result wrap class does not exist '.$result_wrap_class, __FUNCTION__);
  2593.                 return $err;
  2594.             }
  2595.             $result =& new $result_wrap_class($result, $this->fetchmode);
  2596.         }
  2597.         return $result;
  2598.     }
  2599.  
  2600.     // }}}
  2601.     // {{{ function getServerVersion($native = false)
  2602.  
  2603.     /**
  2604.      * return version information about the server
  2605.      *
  2606.      * @param   bool    determines if the raw version string should be returned
  2607.      *
  2608.      * @return  mixed   array with version information or row string
  2609.      *
  2610.      * @access  public
  2611.      */
  2612.     function getServerVersion($native = false)
  2613.     {
  2614.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2615.             'method not implemented', __FUNCTION__);
  2616.     }
  2617.  
  2618.     // }}}
  2619.     // {{{ function setLimit($limit, $offset = null)
  2620.  
  2621.     /**
  2622.      * set the range of the next query
  2623.      *
  2624.      * @param   string  number of rows to select
  2625.      * @param   string  first row to select
  2626.      *
  2627.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  2628.      *
  2629.      * @access  public
  2630.      */
  2631.     function setLimit($limit, $offset = null)
  2632.     {
  2633.         if (!$this->supports('limit_queries')) {
  2634.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2635.                 'limit is not supported by this driver', __FUNCTION__);
  2636.         }
  2637.         $limit = (int)$limit;
  2638.         if ($limit < 0) {
  2639.             return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2640.                 'it was not specified a valid selected range row limit', __FUNCTION__);
  2641.         }
  2642.         $this->limit = $limit;
  2643.         if (!is_null($offset)) {
  2644.             $offset = (int)$offset;
  2645.             if ($offset < 0) {
  2646.                 return $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2647.                     'it was not specified a valid first selected range row', __FUNCTION__);
  2648.             }
  2649.             $this->offset = $offset;
  2650.         }
  2651.         return MDB2_OK;
  2652.     }
  2653.  
  2654.     // }}}
  2655.     // {{{ function subSelect($query, $type = false)
  2656.  
  2657.     /**
  2658.      * simple subselect emulation: leaves the query untouched for all RDBMS
  2659.      * that support subselects
  2660.      *
  2661.      * @param   string  the SQL query for the subselect that may only
  2662.      *                      return a column
  2663.      * @param   string  determines type of the field
  2664.      *
  2665.      * @return  string  the query
  2666.      *
  2667.      * @access  public
  2668.      */
  2669.     function subSelect($query, $type = false)
  2670.     {
  2671.         if ($this->supports('sub_selects') === true) {
  2672.             return $query;
  2673.         }
  2674.  
  2675.         if (!$this->supports('sub_selects')) {
  2676.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2677.                 'method not implemented', __FUNCTION__);
  2678.         }
  2679.  
  2680.         $col = $this->queryCol($query, $type);
  2681.         if (PEAR::isError($col)) {
  2682.             return $col;
  2683.         }
  2684.         if (!is_array($col) || count($col) == 0) {
  2685.             return 'NULL';
  2686.         }
  2687.         if ($type) {
  2688.             $this->loadModule('Datatype', null, true);
  2689.             return $this->datatype->implodeArray($col, $type);
  2690.         }
  2691.         return implode(', ', $col);
  2692.     }
  2693.  
  2694.     // }}}
  2695.     // {{{ function replace($table, $fields)
  2696.  
  2697.     /**
  2698.      * Execute a SQL REPLACE query. A REPLACE query is identical to a INSERT
  2699.      * query, except that if there is already a row in the table with the same
  2700.      * key field values, the REPLACE query just updates its values instead of
  2701.      * inserting a new row.
  2702.      *
  2703.      * The REPLACE type of query does not make part of the SQL standards. Since
  2704.      * practically only MySQL and SQLite implement it natively, this type of
  2705.      * query isemulated through this method for other DBMS using standard types
  2706.      * of queries inside a transaction to assure the atomicity of the operation.
  2707.      *
  2708.      * @param   string  name of the table on which the REPLACE query will
  2709.      *       be executed.
  2710.      * @param   array   associative array   that describes the fields and the
  2711.      *       values that will be inserted or updated in the specified table. The
  2712.      *       indexes of the array are the names of all the fields of the table.
  2713.      *       The values of the array are also associative arrays that describe
  2714.      *       the values and other properties of the table fields.
  2715.      *
  2716.      *       Here follows a list of field properties that need to be specified:
  2717.      *
  2718.      *       value
  2719.      *           Value to be assigned to the specified field. This value may be
  2720.      *           of specified in database independent type format as this
  2721.      *           function can perform the necessary datatype conversions.
  2722.      *
  2723.      *           Default: this property is required unless the Null property is
  2724.      *           set to 1.
  2725.      *
  2726.      *       type
  2727.      *           Name of the type of the field. Currently, all types MDB2
  2728.      *           are supported except for clob and blob.
  2729.      *
  2730.      *           Default: no type conversion
  2731.      *
  2732.      *       null
  2733.      *           bool    property that indicates that the value for this field
  2734.      *           should be set to null.
  2735.      *
  2736.      *           The default value for fields missing in INSERT queries may be
  2737.      *           specified the definition of a table. Often, the default value
  2738.      *           is already null, but since the REPLACE may be emulated using
  2739.      *           an UPDATE query, make sure that all fields of the table are
  2740.      *           listed in this function argument array.
  2741.      *
  2742.      *           Default: 0
  2743.      *
  2744.      *       key
  2745.      *           bool    property that indicates that this field should be
  2746.      *           handled as a primary key or at least as part of the compound
  2747.      *           unique index of the table that will determine the row that will
  2748.      *           updated if it exists or inserted a new row otherwise.
  2749.      *
  2750.      *           This function will fail if no key field is specified or if the
  2751.      *           value of a key field is set to null because fields that are
  2752.      *           part of unique index they may not be null.
  2753.      *
  2754.      *           Default: 0
  2755.      *
  2756.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  2757.      *
  2758.      * @access  public
  2759.      */
  2760.     function replace($table, $fields)
  2761.     {
  2762.         if (!$this->supports('replace')) {
  2763.             return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  2764.                 'replace query is not supported', __FUNCTION__);
  2765.         }
  2766.         $count = count($fields);
  2767.         $condition = $values = array();
  2768.         for ($colnum = 0, reset($fields); $colnum < $count; next($fields), $colnum++) {
  2769.             $name = key($fields);
  2770.             if (isset($fields[$name]['null']) && $fields[$name]['null']) {
  2771.                 $value = 'NULL';
  2772.             } else {
  2773.                 $type = isset($fields[$name]['type']) ? $fields[$name]['type'] : null;
  2774.                 $value = $this->quote($fields[$name]['value'], $type);
  2775.             }
  2776.             $values[$name] = $value;
  2777.             if (isset($fields[$name]['key']) && $fields[$name]['key']) {
  2778.                 if ($value === 'NULL') {
  2779.                     return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  2780.                         'key value '.$name.' may not be NULL', __FUNCTION__);
  2781.                 }
  2782.                 $condition[] = $name . '=' . $value;
  2783.             }
  2784.         }
  2785.         if (empty($condition)) {
  2786.             return $this->raiseError(MDB2_ERROR_CANNOT_REPLACE, null, null,
  2787.                 'not specified which fields are keys', __FUNCTION__);
  2788.         }
  2789.  
  2790.         $result = null;
  2791.         $in_transaction = $this->in_transaction;
  2792.         if (!$in_transaction && PEAR::isError($result = $this->beginTransaction())) {
  2793.             return $result;
  2794.         }
  2795.  
  2796.         $connection = $this->getConnection();
  2797.         if (PEAR::isError($connection)) {
  2798.             return $connection;
  2799.         }
  2800.  
  2801.         $condition = ' WHERE '.implode(' AND ', $condition);
  2802.         $query = "DELETE FROM $table$condition";
  2803.         $result =& $this->_doQuery($query, true, $connection);
  2804.         if (!PEAR::isError($result)) {
  2805.             $affected_rows = $this->_affectedRows($connection, $result);
  2806.             $insert = implode(', ', array_keys($values));
  2807.             $values = implode(', ', $values);
  2808.             $query = "INSERT INTO $table ($insert) VALUES ($values)";
  2809.             $result =& $this->_doQuery($query, true, $connection);
  2810.             if (!PEAR::isError($result)) {
  2811.                 $affected_rows += $this->_affectedRows($connection, $result);;
  2812.             }
  2813.         }
  2814.  
  2815.         if (!$in_transaction) {
  2816.             if (PEAR::isError($result)) {
  2817.                 $this->rollback();
  2818.             } else {
  2819.                 $result = $this->commit();
  2820.             }
  2821.         }
  2822.  
  2823.         if (PEAR::isError($result)) {
  2824.             return $result;
  2825.         }
  2826.  
  2827.         return $affected_rows;
  2828.     }
  2829.  
  2830.     // }}}
  2831.     // {{{ function &prepare($query, $types = null, $result_types = null, $lobs = array())
  2832.  
  2833.     /**
  2834.      * Prepares a query for multiple execution with execute().
  2835.      * With some database backends, this is emulated.
  2836.      * prepare() requires a generic query as string like
  2837.      * 'INSERT INTO numbers VALUES(?,?)' or
  2838.      * 'INSERT INTO numbers VALUES(:foo,:bar)'.
  2839.      * The ? and :[a-zA-Z] and  are placeholders which can be set using
  2840.      * bindParam() and the query can be send off using the execute() method.
  2841.      *
  2842.      * @param   string  the query to prepare
  2843.      * @param   mixed   array that contains the types of the placeholders
  2844.      * @param   mixed   array that contains the types of the columns in
  2845.      *                        the result set or MDB2_PREPARE_RESULT, if set to
  2846.      *                        MDB2_PREPARE_MANIP the query is handled as a manipulation query
  2847.      * @param   mixed   key (field) value (parameter) pair for all lob placeholders
  2848.      *
  2849.      * @return  mixed   resource handle for the prepared query on success, 
  2850.      *                  a MDB2 error on failure
  2851.      *
  2852.      * @access  public
  2853.      * @see     bindParam, execute
  2854.      */
  2855.     function &prepare($query, $types = null, $result_types = null, $lobs = array())
  2856.     {
  2857.         $is_manip = ($result_types === MDB2_PREPARE_MANIP);
  2858.         $offset = $this->offset;
  2859.         $limit = $this->limit;
  2860.         $this->offset = $this->limit = 0;
  2861.         $result = $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'pre'));
  2862.         if ($result) {
  2863.             if (PEAR::isError($result)) {
  2864.                 return $result;
  2865.             }
  2866.             $query = $result;
  2867.         }
  2868.         $placeholder_type_guess = $placeholder_type = null;
  2869.         $question = '?';
  2870.         $colon = ':';
  2871.         $positions = array();
  2872.         $position = 0;
  2873.         $ignores = $this->sql_comments;
  2874.         $ignores[] = $this->string_quoting;
  2875.         $ignores[] = $this->identifier_quoting;
  2876.         while ($position < strlen($query)) {
  2877.             $q_position = strpos($query, $question, $position);
  2878.             $c_position = strpos($query, $colon, $position);
  2879.             if ($q_position && $c_position) {
  2880.                 $p_position = min($q_position, $c_position);
  2881.             } elseif ($q_position) {
  2882.                 $p_position = $q_position;
  2883.             } elseif ($c_position) {
  2884.                 $p_position = $c_position;
  2885.             } else {
  2886.                 break;
  2887.             }
  2888.             if (is_null($placeholder_type)) {
  2889.                 $placeholder_type_guess = $query[$p_position];
  2890.             }
  2891.  
  2892.             $new_pos = $this->_skipDelimitedStrings($query, $position, $p_position);
  2893.             if (PEAR::isError($new_pos)) {
  2894.                 return $new_pos;
  2895.             }
  2896.             if ($new_pos != $position) {
  2897.                 $position = $new_pos;
  2898.                 continue; //evaluate again starting from the new position
  2899.             }
  2900.  
  2901.             if ($query[$position] == $placeholder_type_guess) {
  2902.                 if (is_null($placeholder_type)) {
  2903.                     $placeholder_type = $query[$p_position];
  2904.                     $question = $colon = $placeholder_type;
  2905.                     if (!empty($types) && is_array($types)) {
  2906.                         if ($placeholder_type == ':') {
  2907.                             if (is_int(key($types))) {
  2908.                                 $types_tmp = $types;
  2909.                                 $types = array();
  2910.                                 $count = -1;
  2911.                             }
  2912.                         } else {
  2913.                             $types = array_values($types);
  2914.                         }
  2915.                     }
  2916.                 }
  2917.                 if ($placeholder_type == ':') {
  2918.                     $parameter = preg_replace('/^.{'.($position+1).'}([a-z0-9_]+).*$/si', '\\1', $query);
  2919.                     if ($parameter === '') {
  2920.                         $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2921.                             'named parameter with an empty name', __FUNCTION__);
  2922.                         return $err;
  2923.                     }
  2924.                     $positions[$p_position] = $parameter;
  2925.                     $query = substr_replace($query, '?', $position, strlen($parameter)+1);
  2926.                     // use parameter name in type array
  2927.                     if (isset($count) && isset($types_tmp[++$count])) {
  2928.                         $types[$parameter] = $types_tmp[$count];
  2929.                     }
  2930.                 } else {
  2931.                     $positions[$p_position] = count($positions);
  2932.                 }
  2933.                 $position = $p_position + 1;
  2934.             } else {
  2935.                 $position = $p_position;
  2936.             }
  2937.         }
  2938.         $class_name = 'MDB2_Statement_'.$this->phptype;
  2939.         $statement = null;
  2940.         $obj =& new $class_name($this, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  2941.         $this->debug($query, __FUNCTION__, array('is_manip' => $is_manip, 'when' => 'post', 'result' => $obj));
  2942.         return $obj;
  2943.     }
  2944.  
  2945.     // }}}
  2946.     // {{{ function _skipDelimitedStrings($query, $position, $p_position)
  2947.     
  2948.     /**
  2949.      * Utility method, used by prepare() to avoid replacing placeholders within delimited strings.
  2950.      * Check if the placeholder is contained within a delimited string.
  2951.      * If so, skip it and advance the position, otherwise return the current position,
  2952.      * which is valid
  2953.      *
  2954.      * @param string $query
  2955.      * @param integer $position current string cursor position
  2956.      * @param integer $p_position placeholder position
  2957.      *
  2958.      * @return mixed integer $new_position on success
  2959.      *               MDB2_Error on failure
  2960.      *
  2961.      * @access  protected
  2962.      */
  2963.     function _skipDelimitedStrings($query, $position, $p_position)
  2964.     {
  2965.         $ignores = $this->sql_comments;
  2966.         $ignores[] = $this->string_quoting;
  2967.         $ignores[] = $this->identifier_quoting;
  2968.         
  2969.         foreach ($ignores as $ignore) {
  2970.             if (!empty($ignore['start'])) {
  2971.                 if (is_int($start_quote = strpos($query, $ignore['start'], $position)) && $start_quote < $p_position) {
  2972.                     $end_quote = $start_quote;
  2973.                     do {
  2974.                         if (!is_int($end_quote = strpos($query, $ignore['end'], $end_quote + 1))) {
  2975.                             if ($ignore['end'] === "\n") {
  2976.                                 $end_quote = strlen($query) - 1;
  2977.                             } else {
  2978.                                 $err =& $this->raiseError(MDB2_ERROR_SYNTAX, null, null,
  2979.                                     'query with an unterminated text string specified', __FUNCTION__);
  2980.                                 return $err;
  2981.                             }
  2982.                         }
  2983.                     } while ($ignore['escape'] && $query[($end_quote - 1)] == $ignore['escape']);
  2984.                     $position = $end_quote + 1;
  2985.                     return $position;
  2986.                 }
  2987.             }
  2988.         }
  2989.         return $position;
  2990.     }
  2991.     
  2992.     // }}}
  2993.     // {{{ function quote($value, $type = null, $quote = true)
  2994.  
  2995.     /**
  2996.      * Convert a text value into a DBMS specific format that is suitable to
  2997.      * compose query statements.
  2998.      *
  2999.      * @param   string  text string value that is intended to be converted.
  3000.      * @param   string  type to which the value should be converted to
  3001.      * @param   bool    quote
  3002.      * @param   bool    escape wildcards
  3003.      *
  3004.      * @return  string  text string that represents the given argument value in
  3005.      *       a DBMS specific format.
  3006.      *
  3007.      * @access  public
  3008.      */
  3009.     function quote($value, $type = null, $quote = true, $escape_wildcards = false)
  3010.     {
  3011.         $result = $this->loadModule('Datatype', null, true);
  3012.         if (PEAR::isError($result)) {
  3013.             return $result;
  3014.         }
  3015.  
  3016.         return $this->datatype->quote($value, $type, $quote, $escape_wildcards);
  3017.     }
  3018.  
  3019.     // }}}
  3020.     // {{{ function getDeclaration($type, $name, $field)
  3021.  
  3022.     /**
  3023.      * Obtain DBMS specific SQL code portion needed to declare
  3024.      * of the given type
  3025.      *
  3026.      * @param   string  type to which the value should be converted to
  3027.      * @param   string  name the field to be declared.
  3028.      * @param   string  definition of the field
  3029.      *
  3030.      * @return  string  DBMS specific SQL code portion that should be used to
  3031.      *                 declare the specified field.
  3032.      *
  3033.      * @access  public
  3034.      */
  3035.     function getDeclaration($type, $name, $field)
  3036.     {
  3037.         $result = $this->loadModule('Datatype', null, true);
  3038.         if (PEAR::isError($result)) {
  3039.             return $result;
  3040.         }
  3041.         return $this->datatype->getDeclaration($type, $name, $field);
  3042.     }
  3043.  
  3044.     // }}}
  3045.     // {{{ function compareDefinition($current, $previous)
  3046.  
  3047.     /**
  3048.      * Obtain an array of changes that may need to applied
  3049.      *
  3050.      * @param   array   new definition
  3051.      * @param   array   old definition
  3052.      *
  3053.      * @return  array   containing all changes that will need to be applied
  3054.      *
  3055.      * @access  public
  3056.      */
  3057.     function compareDefinition($current, $previous)
  3058.     {
  3059.         $result = $this->loadModule('Datatype', null, true);
  3060.         if (PEAR::isError($result)) {
  3061.             return $result;
  3062.         }
  3063.         return $this->datatype->compareDefinition($current, $previous);
  3064.     }
  3065.  
  3066.     // }}}
  3067.     // {{{ function supports($feature)
  3068.  
  3069.     /**
  3070.      * Tell whether a DB implementation or its backend extension
  3071.      * supports a given feature.
  3072.      *
  3073.      * @param   string  name of the feature (see the MDB2 class doc)
  3074.      *
  3075.      * @return  bool|string if this DB implementation supports a given feature
  3076.      *                      false means no, true means native,
  3077.      *                      'emulated' means emulated
  3078.      *
  3079.      * @access  public
  3080.      */
  3081.     function supports($feature)
  3082.     {
  3083.         if (array_key_exists($feature, $this->supported)) {
  3084.             return $this->supported[$feature];
  3085.         }
  3086.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3087.             "unknown support feature $feature", __FUNCTION__);
  3088.     }
  3089.  
  3090.     // }}}
  3091.     // {{{ function getSequenceName($sqn)
  3092.  
  3093.     /**
  3094.      * adds sequence name formatting to a sequence name
  3095.      *
  3096.      * @param   string  name of the sequence
  3097.      *
  3098.      * @return  string  formatted sequence name
  3099.      *
  3100.      * @access  public
  3101.      */
  3102.     function getSequenceName($sqn)
  3103.     {
  3104.         return sprintf($this->options['seqname_format'],
  3105.             preg_replace('/[^a-z0-9_\$.]/i', '_', $sqn));
  3106.     }
  3107.  
  3108.     // }}}
  3109.     // {{{ function getIndexName($idx)
  3110.  
  3111.     /**
  3112.      * adds index name formatting to a index name
  3113.      *
  3114.      * @param   string  name of the index
  3115.      *
  3116.      * @return  string  formatted index name
  3117.      *
  3118.      * @access  public
  3119.      */
  3120.     function getIndexName($idx)
  3121.     {
  3122.         return sprintf($this->options['idxname_format'],
  3123.             preg_replace('/[^a-z0-9_\$]/i', '_', $idx));
  3124.     }
  3125.  
  3126.     // }}}
  3127.     // {{{ function nextID($seq_name, $ondemand = true)
  3128.  
  3129.     /**
  3130.      * Returns the next free id of a sequence
  3131.      *
  3132.      * @param   string  name of the sequence
  3133.      * @param   bool    when true missing sequences are automatic created
  3134.      *
  3135.      * @return  mixed   MDB2 Error Object or id
  3136.      *
  3137.      * @access  public
  3138.      */
  3139.     function nextID($seq_name, $ondemand = true)
  3140.     {
  3141.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3142.             'method not implemented', __FUNCTION__);
  3143.     }
  3144.  
  3145.     // }}}
  3146.     // {{{ function lastInsertID($table = null, $field = null)
  3147.  
  3148.     /**
  3149.      * Returns the autoincrement ID if supported or $id or fetches the current
  3150.      * ID in a sequence called: $table.(empty($field) ? '' : '_'.$field)
  3151.      *
  3152.      * @param   string  name of the table into which a new row was inserted
  3153.      * @param   string  name of the field into which a new row was inserted
  3154.      *
  3155.      * @return  mixed   MDB2 Error Object or id
  3156.      *
  3157.      * @access  public
  3158.      */
  3159.     function lastInsertID($table = null, $field = null)
  3160.     {
  3161.         return $this->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3162.             'method not implemented', __FUNCTION__);
  3163.     }
  3164.  
  3165.     // }}}
  3166.     // {{{ function currID($seq_name)
  3167.  
  3168.     /**
  3169.      * Returns the current id of a sequence
  3170.      *
  3171.      * @param   string  name of the sequence
  3172.      *
  3173.      * @return  mixed   MDB2 Error Object or id
  3174.      *
  3175.      * @access  public
  3176.      */
  3177.     function currID($seq_name)
  3178.     {
  3179.         $this->warnings[] = 'database does not support getting current
  3180.             sequence value, the sequence value was incremented';
  3181.         return $this->nextID($seq_name);
  3182.     }
  3183.  
  3184.     // }}}
  3185.     // {{{ function queryOne($query, $type = null, $colnum = 0)
  3186.  
  3187.     /**
  3188.      * Execute the specified query, fetch the value from the first column of
  3189.      * the first row of the result set and then frees
  3190.      * the result set.
  3191.      *
  3192.      * @param   string  the SELECT query statement to be executed.
  3193.      * @param   string  optional argument that specifies the expected
  3194.      *       datatype of the result set field, so that an eventual conversion
  3195.      *       may be performed. The default datatype is text, meaning that no
  3196.      *       conversion is performed
  3197.      * @param   int     the column number to fetch
  3198.      *
  3199.      * @return  mixed   MDB2_OK or field value on success, a MDB2 error on failure
  3200.      *
  3201.      * @access  public
  3202.      */
  3203.     function queryOne($query, $type = null, $colnum = 0)
  3204.     {
  3205.         $result = $this->query($query, $type);
  3206.         if (!MDB2::isResultCommon($result)) {
  3207.             return $result;
  3208.         }
  3209.  
  3210.         $one = $result->fetchOne($colnum);
  3211.         $result->free();
  3212.         return $one;
  3213.     }
  3214.  
  3215.     // }}}
  3216.     // {{{ function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
  3217.  
  3218.     /**
  3219.      * Execute the specified query, fetch the values from the first
  3220.      * row of the result set into an array and then frees
  3221.      * the result set.
  3222.      *
  3223.      * @param   string  the SELECT query statement to be executed.
  3224.      * @param   array   optional array argument that specifies a list of
  3225.      *       expected datatypes of the result set columns, so that the eventual
  3226.      *       conversions may be performed. The default list of datatypes is
  3227.      *       empty, meaning that no conversion is performed.
  3228.      * @param   int     how the array data should be indexed
  3229.      *
  3230.      * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
  3231.      *
  3232.      * @access  public
  3233.      */
  3234.     function queryRow($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT)
  3235.     {
  3236.         $result = $this->query($query, $types);
  3237.         if (!MDB2::isResultCommon($result)) {
  3238.             return $result;
  3239.         }
  3240.  
  3241.         $row = $result->fetchRow($fetchmode);
  3242.         $result->free();
  3243.         return $row;
  3244.     }
  3245.  
  3246.     // }}}
  3247.     // {{{ function queryCol($query, $type = null, $colnum = 0)
  3248.  
  3249.     /**
  3250.      * Execute the specified query, fetch the value from the first column of
  3251.      * each row of the result set into an array and then frees the result set.
  3252.      *
  3253.      * @param   string  the SELECT query statement to be executed.
  3254.      * @param   string  optional argument that specifies the expected
  3255.      *       datatype of the result set field, so that an eventual conversion
  3256.      *       may be performed. The default datatype is text, meaning that no
  3257.      *       conversion is performed
  3258.      * @param   int     the row number to fetch
  3259.      *
  3260.      * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
  3261.      *
  3262.      * @access  public
  3263.      */
  3264.     function queryCol($query, $type = null, $colnum = 0)
  3265.     {
  3266.         $result = $this->query($query, $type);
  3267.         if (!MDB2::isResultCommon($result)) {
  3268.             return $result;
  3269.         }
  3270.  
  3271.         $col = $result->fetchCol($colnum);
  3272.         $result->free();
  3273.         return $col;
  3274.     }
  3275.  
  3276.     // }}}
  3277.     // {{{ function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3278.  
  3279.     /**
  3280.      * Execute the specified query, fetch all the rows of the result set into
  3281.      * a two dimensional array and then frees the result set.
  3282.      *
  3283.      * @param   string  the SELECT query statement to be executed.
  3284.      * @param   array   optional array argument that specifies a list of
  3285.      *       expected datatypes of the result set columns, so that the eventual
  3286.      *       conversions may be performed. The default list of datatypes is
  3287.      *       empty, meaning that no conversion is performed.
  3288.      * @param   int     how the array data should be indexed
  3289.      * @param   bool    if set to true, the $all will have the first
  3290.      *       column as its first dimension
  3291.      * @param   bool    used only when the query returns exactly
  3292.      *       two columns. If true, the values of the returned array will be
  3293.      *       one-element arrays instead of scalars.
  3294.      * @param   bool    if true, the values of the returned array is
  3295.      *       wrapped in another array.  If the same key value (in the first
  3296.      *       column) repeats itself, the values will be appended to this array
  3297.      *       instead of overwriting the existing values.
  3298.      *
  3299.      * @return  mixed   MDB2_OK or data array on success, a MDB2 error on failure
  3300.      *
  3301.      * @access  public
  3302.      */
  3303.     function queryAll($query, $types = null, $fetchmode = MDB2_FETCHMODE_DEFAULT,
  3304.         $rekey = false, $force_array = false, $group = false)
  3305.     {
  3306.         $result = $this->query($query, $types);
  3307.         if (!MDB2::isResultCommon($result)) {
  3308.             return $result;
  3309.         }
  3310.  
  3311.         $all = $result->fetchAll($fetchmode, $rekey, $force_array, $group);
  3312.         $result->free();
  3313.         return $all;
  3314.     }
  3315.  
  3316.     // }}}
  3317. }
  3318.  
  3319. // }}}
  3320. // {{{ class MDB2_Result
  3321.  
  3322. /**
  3323.  * The dummy class that all user space result classes should extend from
  3324.  *
  3325.  * @package     MDB2
  3326.  * @category    Database
  3327.  * @author      Lukas Smith <smith@pooteeweet.org>
  3328.  */
  3329. class MDB2_Result
  3330. {
  3331. }
  3332.  
  3333. // }}}
  3334. // {{{ class MDB2_Result_Common extends MDB2_Result
  3335.  
  3336. /**
  3337.  * The common result class for MDB2 result objects
  3338.  *
  3339.  * @package     MDB2
  3340.  * @category    Database
  3341.  * @author      Lukas Smith <smith@pooteeweet.org>
  3342.  */
  3343. class MDB2_Result_Common extends MDB2_Result
  3344. {
  3345.     // {{{ Variables (Properties)
  3346.  
  3347.     var $db;
  3348.     var $result;
  3349.     var $rownum = -1;
  3350.     var $types = array();
  3351.     var $values = array();
  3352.     var $offset;
  3353.     var $offset_count = 0;
  3354.     var $limit;
  3355.     var $column_names;
  3356.  
  3357.     // }}}
  3358.     // {{{ constructor: function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3359.  
  3360.     /**
  3361.      * Constructor
  3362.      */
  3363.     function __construct(&$db, &$result, $limit = 0, $offset = 0)
  3364.     {
  3365.         $this->db =& $db;
  3366.         $this->result =& $result;
  3367.         $this->offset = $offset;
  3368.         $this->limit = max(0, $limit - 1);
  3369.     }
  3370.  
  3371.     // }}}
  3372.     // {{{ function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0)
  3373.  
  3374.     /**
  3375.      * PHP 4 Constructor
  3376.      */
  3377.     function MDB2_Result_Common(&$db, &$result, $limit = 0, $offset = 0)
  3378.     {
  3379.         $this->__construct($db, $result, $limit, $offset);
  3380.     }
  3381.  
  3382.     // }}}
  3383.     // {{{ function setResultTypes($types)
  3384.  
  3385.     /**
  3386.      * Define the list of types to be associated with the columns of a given
  3387.      * result set.
  3388.      *
  3389.      * This function may be called before invoking fetchRow(), fetchOne(),
  3390.      * fetchCol() and fetchAll() so that the necessary data type
  3391.      * conversions are performed on the data to be retrieved by them. If this
  3392.      * function is not called, the type of all result set columns is assumed
  3393.      * to be text, thus leading to not perform any conversions.
  3394.      *
  3395.      * @param   array   variable that lists the
  3396.      *       data types to be expected in the result set columns. If this array
  3397.      *       contains less types than the number of columns that are returned
  3398.      *       in the result set, the remaining columns are assumed to be of the
  3399.      *       type text. Currently, the types clob and blob are not fully
  3400.      *       supported.
  3401.      *
  3402.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3403.      *
  3404.      * @access  public
  3405.      */
  3406.     function setResultTypes($types)
  3407.     {
  3408.         $load = $this->db->loadModule('Datatype', null, true);
  3409.         if (PEAR::isError($load)) {
  3410.             return $load;
  3411.         }
  3412.         $types = $this->db->datatype->checkResultTypes($types);
  3413.         if (PEAR::isError($types)) {
  3414.             return $types;
  3415.         }
  3416.         $this->types = $types;
  3417.         return MDB2_OK;
  3418.     }
  3419.  
  3420.     // }}}
  3421.     // {{{ function seek($rownum = 0)
  3422.  
  3423.     /**
  3424.      * Seek to a specific row in a result set
  3425.      *
  3426.      * @param   int     number of the row where the data can be found
  3427.      *
  3428.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3429.      *
  3430.      * @access  public
  3431.      */
  3432.     function seek($rownum = 0)
  3433.     {
  3434.         $target_rownum = $rownum - 1;
  3435.         if ($this->rownum > $target_rownum) {
  3436.             return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3437.                 'seeking to previous rows not implemented', __FUNCTION__);
  3438.         }
  3439.         while ($this->rownum < $target_rownum) {
  3440.             $this->fetchRow();
  3441.         }
  3442.         return MDB2_OK;
  3443.     }
  3444.  
  3445.     // }}}
  3446.     // {{{ function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  3447.  
  3448.     /**
  3449.      * Fetch and return a row of data
  3450.      *
  3451.      * @param   int     how the array data should be indexed
  3452.      * @param   int     number of the row where the data can be found
  3453.      *
  3454.      * @return  int     data array on success, a MDB2 error on failure
  3455.      *
  3456.      * @access  public
  3457.      */
  3458.     function &fetchRow($fetchmode = MDB2_FETCHMODE_DEFAULT, $rownum = null)
  3459.     {
  3460.         $err =& $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3461.             'method not implemented', __FUNCTION__);
  3462.         return $err;
  3463.     }
  3464.  
  3465.     // }}}
  3466.     // {{{ function fetchOne($colnum = 0)
  3467.  
  3468.     /**
  3469.      * fetch single column from the next row from a result set
  3470.      *
  3471.      * @param   int     the column number to fetch
  3472.      * @param   int     number of the row where the data can be found
  3473.      *
  3474.      * @return  string  data on success, a MDB2 error on failure
  3475.      *
  3476.      * @access  public
  3477.      */
  3478.     function fetchOne($colnum = 0, $rownum = null)
  3479.     {
  3480.         $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  3481.         $row = $this->fetchRow($fetchmode, $rownum);
  3482.         if (!is_array($row) || PEAR::isError($row)) {
  3483.             return $row;
  3484.         }
  3485.         if (!array_key_exists($colnum, $row)) {
  3486.             return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
  3487.                 'column is not defined in the result set: '.$colnum, __FUNCTION__);
  3488.         }
  3489.         return $row[$colnum];
  3490.     }
  3491.  
  3492.     // }}}
  3493.     // {{{ function fetchCol($colnum = 0)
  3494.  
  3495.     /**
  3496.      * Fetch and return a column from the current row pointer position
  3497.      *
  3498.      * @param   int     the column number to fetch
  3499.      *
  3500.      * @return  mixed   data array on success, a MDB2 error on failure
  3501.      *
  3502.      * @access  public
  3503.      */
  3504.     function fetchCol($colnum = 0)
  3505.     {
  3506.         $column = array();
  3507.         $fetchmode = is_numeric($colnum) ? MDB2_FETCHMODE_ORDERED : MDB2_FETCHMODE_ASSOC;
  3508.         $row = $this->fetchRow($fetchmode);
  3509.         if (is_array($row)) {
  3510.             if (!array_key_exists($colnum, $row)) {
  3511.                 return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
  3512.                     'column is not defined in the result set: '.$colnum, __FUNCTION__);
  3513.             }
  3514.             do {
  3515.                 $column[] = $row[$colnum];
  3516.             } while (is_array($row = $this->fetchRow($fetchmode)));
  3517.         }
  3518.         if (PEAR::isError($row)) {
  3519.             return $row;
  3520.         }
  3521.         return $column;
  3522.     }
  3523.  
  3524.     // }}}
  3525.     // {{{ function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false, $force_array = false, $group = false)
  3526.  
  3527.     /**
  3528.      * Fetch and return all rows from the current row pointer position
  3529.      *
  3530.      * @param   int     $fetchmode  the fetch mode to use:
  3531.      *                            + MDB2_FETCHMODE_ORDERED
  3532.      *                            + MDB2_FETCHMODE_ASSOC
  3533.      *                            + MDB2_FETCHMODE_ORDERED | MDB2_FETCHMODE_FLIPPED
  3534.      *                            + MDB2_FETCHMODE_ASSOC | MDB2_FETCHMODE_FLIPPED
  3535.      * @param   bool    if set to true, the $all will have the first
  3536.      *       column as its first dimension
  3537.      * @param   bool    used only when the query returns exactly
  3538.      *       two columns. If true, the values of the returned array will be
  3539.      *       one-element arrays instead of scalars.
  3540.      * @param   bool    if true, the values of the returned array is
  3541.      *       wrapped in another array.  If the same key value (in the first
  3542.      *       column) repeats itself, the values will be appended to this array
  3543.      *       instead of overwriting the existing values.
  3544.      *
  3545.      * @return  mixed   data array on success, a MDB2 error on failure
  3546.      *
  3547.      * @access  public
  3548.      * @see     getAssoc()
  3549.      */
  3550.     function fetchAll($fetchmode = MDB2_FETCHMODE_DEFAULT, $rekey = false,
  3551.         $force_array = false, $group = false)
  3552.     {
  3553.         $all = array();
  3554.         $row = $this->fetchRow($fetchmode);
  3555.         if (PEAR::isError($row)) {
  3556.             return $row;
  3557.         } elseif (!$row) {
  3558.             return $all;
  3559.         }
  3560.  
  3561.         $shift_array = $rekey ? false : null;
  3562.         if (!is_null($shift_array)) {
  3563.             if (is_object($row)) {
  3564.                 $colnum = count(get_object_vars($row));
  3565.             } else {
  3566.                 $colnum = count($row);
  3567.             }
  3568.             if ($colnum < 2) {
  3569.                 return $this->db->raiseError(MDB2_ERROR_TRUNCATED, null, null,
  3570.                     'rekey feature requires atleast 2 column', __FUNCTION__);
  3571.             }
  3572.             $shift_array = (!$force_array && $colnum == 2);
  3573.         }
  3574.  
  3575.         if ($rekey) {
  3576.             do {
  3577.                 if (is_object($row)) {
  3578.                     $arr = get_object_vars($row);
  3579.                     $key = reset($arr);
  3580.                     unset($row->{$key});
  3581.                 } else {
  3582.                     if ($fetchmode & MDB2_FETCHMODE_ASSOC) {
  3583.                         $key = reset($row);
  3584.                         unset($row[key($row)]);
  3585.                     } else {
  3586.                         $key = array_shift($row);
  3587.                     }
  3588.                     if ($shift_array) {
  3589.                         $row = array_shift($row);
  3590.                     }
  3591.                 }
  3592.                 if ($group) {
  3593.                     $all[$key][] = $row;
  3594.                 } else {
  3595.                     $all[$key] = $row;
  3596.                 }
  3597.             } while (($row = $this->fetchRow($fetchmode)));
  3598.         } elseif ($fetchmode & MDB2_FETCHMODE_FLIPPED) {
  3599.             do {
  3600.                 foreach ($row as $key => $val) {
  3601.                     $all[$key][] = $val;
  3602.                 }
  3603.             } while (($row = $this->fetchRow($fetchmode)));
  3604.         } else {
  3605.             do {
  3606.                 $all[] = $row;
  3607.             } while (($row = $this->fetchRow($fetchmode)));
  3608.         }
  3609.  
  3610.         return $all;
  3611.     }
  3612.  
  3613.     // }}}
  3614.     // {{{ function rowCount()
  3615.     /**
  3616.      * Returns the actual row number that was last fetched (count from 0)
  3617.      * @return  int
  3618.      *
  3619.      * @access  public
  3620.      */
  3621.     function rowCount()
  3622.     {
  3623.         return $this->rownum + 1;
  3624.     }
  3625.  
  3626.     // }}}
  3627.     // {{{ function numRows()
  3628.  
  3629.     /**
  3630.      * Returns the number of rows in a result object
  3631.      *
  3632.      * @return  mixed   MDB2 Error Object or the number of rows
  3633.      *
  3634.      * @access  public
  3635.      */
  3636.     function numRows()
  3637.     {
  3638.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3639.             'method not implemented', __FUNCTION__);
  3640.     }
  3641.  
  3642.     // }}}
  3643.     // {{{ function nextResult()
  3644.  
  3645.     /**
  3646.      * Move the internal result pointer to the next available result
  3647.      *
  3648.      * @return  true on success, false if there is no more result set or an error object on failure
  3649.      *
  3650.      * @access  public
  3651.      */
  3652.     function nextResult()
  3653.     {
  3654.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3655.             'method not implemented', __FUNCTION__);
  3656.     }
  3657.  
  3658.     // }}}
  3659.     // {{{ function getColumnNames()
  3660.  
  3661.     /**
  3662.      * Retrieve the names of columns returned by the DBMS in a query result or
  3663.      * from the cache.
  3664.      *
  3665.      * @param   bool    If set to true the values are the column names,
  3666.      *                  otherwise the names of the columns are the keys.
  3667.      * @return  mixed   Array variable that holds the names of columns or an
  3668.      *                  MDB2 error on failure.
  3669.      *                  Some DBMS may not return any columns when the result set
  3670.      *                  does not contain any rows.
  3671.      *
  3672.      * @access  public
  3673.      */
  3674.     function getColumnNames($flip = false)
  3675.     {
  3676.         if (!isset($this->column_names)) {
  3677.             $result = $this->_getColumnNames();
  3678.             if (PEAR::isError($result)) {
  3679.                 return $result;
  3680.             }
  3681.             $this->column_names = $result;
  3682.         }
  3683.         if ($flip) {
  3684.             return array_flip($this->column_names);
  3685.         }
  3686.         return $this->column_names;
  3687.     }
  3688.  
  3689.     // }}}
  3690.     // {{{ function _getColumnNames()
  3691.  
  3692.     /**
  3693.      * Retrieve the names of columns returned by the DBMS in a query result.
  3694.      *
  3695.      * @return  mixed   Array variable that holds the names of columns as keys
  3696.      *                  or an MDB2 error on failure.
  3697.      *                  Some DBMS may not return any columns when the result set
  3698.      *                  does not contain any rows.
  3699.      *
  3700.      * @access  private
  3701.      */
  3702.     function _getColumnNames()
  3703.     {
  3704.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3705.             'method not implemented', __FUNCTION__);
  3706.     }
  3707.  
  3708.     // }}}
  3709.     // {{{ function numCols()
  3710.  
  3711.     /**
  3712.      * Count the number of columns returned by the DBMS in a query result.
  3713.      *
  3714.      * @return  mixed   integer value with the number of columns, a MDB2 error
  3715.      *       on failure
  3716.      *
  3717.      * @access  public
  3718.      */
  3719.     function numCols()
  3720.     {
  3721.         return $this->db->raiseError(MDB2_ERROR_UNSUPPORTED, null, null,
  3722.             'method not implemented', __FUNCTION__);
  3723.     }
  3724.  
  3725.     // }}}
  3726.     // {{{ function getResource()
  3727.  
  3728.     /**
  3729.      * return the resource associated with the result object
  3730.      *
  3731.      * @return  resource
  3732.      *
  3733.      * @access  public
  3734.      */
  3735.     function getResource()
  3736.     {
  3737.         return $this->result;
  3738.     }
  3739.  
  3740.     // }}}
  3741.     // {{{ function bindColumn($column, &$value, $type = null)
  3742.  
  3743.     /**
  3744.      * Set bind variable to a column.
  3745.      *
  3746.      * @param   int     column number or name
  3747.      * @param   mixed   variable reference
  3748.      * @param   string  specifies the type of the field
  3749.      *
  3750.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3751.      *
  3752.      * @access  public
  3753.      */
  3754.     function bindColumn($column, &$value, $type = null)
  3755.     {
  3756.         if (!is_numeric($column)) {
  3757.             $column_names = $this->getColumnNames();
  3758.             if ($this->db->options['portability'] & MDB2_PORTABILITY_FIX_CASE) {
  3759.                 if ($this->db->options['field_case'] == CASE_LOWER) {
  3760.                     $column = strtolower($column);
  3761.                 } else {
  3762.                     $column = strtoupper($column);
  3763.                 }
  3764.             }
  3765.             $column = $column_names[$column];
  3766.         }
  3767.         $this->values[$column] =& $value;
  3768.         if (!is_null($type)) {
  3769.             $this->types[$column] = $type;
  3770.         }
  3771.         return MDB2_OK;
  3772.     }
  3773.  
  3774.     // }}}
  3775.     // {{{ function _assignBindColumns($row)
  3776.  
  3777.     /**
  3778.      * Bind a variable to a value in the result row.
  3779.      *
  3780.      * @param   array   row data
  3781.      *
  3782.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3783.      *
  3784.      * @access  private
  3785.      */
  3786.     function _assignBindColumns($row)
  3787.     {
  3788.         $row = array_values($row);
  3789.         foreach ($row as $column => $value) {
  3790.             if (array_key_exists($column, $this->values)) {
  3791.                 $this->values[$column] = $value;
  3792.             }
  3793.         }
  3794.         return MDB2_OK;
  3795.     }
  3796.  
  3797.     // }}}
  3798.     // {{{ function free()
  3799.  
  3800.     /**
  3801.      * Free the internal resources associated with result.
  3802.      *
  3803.      * @return  bool    true on success, false if result is invalid
  3804.      *
  3805.      * @access  public
  3806.      */
  3807.     function free()
  3808.     {
  3809.         $this->result = false;
  3810.         return MDB2_OK;
  3811.     }
  3812.  
  3813.     // }}}
  3814. }
  3815.  
  3816. // }}}
  3817. // {{{ class MDB2_Row
  3818.  
  3819. /**
  3820.  * The simple class that accepts row data as an array
  3821.  *
  3822.  * @package     MDB2
  3823.  * @category    Database
  3824.  * @author      Lukas Smith <smith@pooteeweet.org>
  3825.  */
  3826. class MDB2_Row
  3827. {
  3828.     // {{{ constructor: function __construct(&$row)
  3829.  
  3830.     /**
  3831.      * constructor
  3832.      *
  3833.      * @param   resource    row data as array
  3834.      */
  3835.     function __construct(&$row)
  3836.     {
  3837.         foreach ($row as $key => $value) {
  3838.             $this->$key = &$row[$key];
  3839.         }
  3840.     }
  3841.  
  3842.     // }}}
  3843.     // {{{ function MDB2_Row(&$row)
  3844.  
  3845.     /**
  3846.      * PHP 4 Constructor
  3847.      *
  3848.      * @param   resource    row data as array
  3849.      */
  3850.     function MDB2_Row(&$row)
  3851.     {
  3852.         $this->__construct($row);
  3853.     }
  3854.  
  3855.     // }}}
  3856. }
  3857.  
  3858. // }}}
  3859. // {{{ class MDB2_Statement_Common
  3860.  
  3861. /**
  3862.  * The common statement class for MDB2 statement objects
  3863.  *
  3864.  * @package     MDB2
  3865.  * @category    Database
  3866.  * @author      Lukas Smith <smith@pooteeweet.org>
  3867.  */
  3868. class MDB2_Statement_Common
  3869. {
  3870.     // {{{ Variables (Properties)
  3871.  
  3872.     var $db;
  3873.     var $statement;
  3874.     var $query;
  3875.     var $result_types;
  3876.     var $types;
  3877.     var $values = array();
  3878.     var $limit;
  3879.     var $offset;
  3880.     var $is_manip;
  3881.  
  3882.     // }}}
  3883.     // {{{ constructor: function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3884.  
  3885.     /**
  3886.      * Constructor
  3887.      */
  3888.     function __construct(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3889.     {
  3890.         $this->db =& $db;
  3891.         $this->statement =& $statement;
  3892.         $this->positions = $positions;
  3893.         $this->query = $query;
  3894.         $this->types = (array)$types;
  3895.         $this->result_types = (array)$result_types;
  3896.         $this->limit = $limit;
  3897.         $this->is_manip = $is_manip;
  3898.         $this->offset = $offset;
  3899.     }
  3900.  
  3901.     // }}}
  3902.     // {{{ function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3903.  
  3904.     /**
  3905.      * PHP 4 Constructor
  3906.      */
  3907.     function MDB2_Statement_Common(&$db, &$statement, $positions, $query, $types, $result_types, $is_manip = false, $limit = null, $offset = null)
  3908.     {
  3909.         $this->__construct($db, $statement, $positions, $query, $types, $result_types, $is_manip, $limit, $offset);
  3910.     }
  3911.  
  3912.     // }}}
  3913.     // {{{ function bindValue($parameter, &$value, $type = null)
  3914.  
  3915.     /**
  3916.      * Set the value of a parameter of a prepared query.
  3917.      *
  3918.      * @param   int     the order number of the parameter in the query
  3919.      *       statement. The order number of the first parameter is 1.
  3920.      * @param   mixed   value that is meant to be assigned to specified
  3921.      *       parameter. The type of the value depends on the $type argument.
  3922.      * @param   string  specifies the type of the field
  3923.      *
  3924.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3925.      *
  3926.      * @access  public
  3927.      */
  3928.     function bindValue($parameter, $value, $type = null)
  3929.     {
  3930.         if (!is_numeric($parameter)) {
  3931.             $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
  3932.         }
  3933.         if (!in_array($parameter, $this->positions)) {
  3934.             return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  3935.                 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
  3936.         }
  3937.         $this->values[$parameter] = $value;
  3938.         if (!is_null($type)) {
  3939.             $this->types[$parameter] = $type;
  3940.         }
  3941.         return MDB2_OK;
  3942.     }
  3943.  
  3944.     // }}}
  3945.     // {{{ function bindValueArray($values, $types = null)
  3946.  
  3947.     /**
  3948.      * Set the values of multiple a parameter of a prepared query in bulk.
  3949.      *
  3950.      * @param   array   specifies all necessary information
  3951.      *       for bindValue() the array elements must use keys corresponding to
  3952.      *       the number of the position of the parameter.
  3953.      * @param   array   specifies the types of the fields
  3954.      *
  3955.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3956.      *
  3957.      * @access  public
  3958.      * @see     bindParam()
  3959.      */
  3960.     function bindValueArray($values, $types = null)
  3961.     {
  3962.         $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  3963.         $parameters = array_keys($values);
  3964.         foreach ($parameters as $key => $parameter) {
  3965.             $err = $this->bindValue($parameter, $values[$parameter], $types[$key]);
  3966.             if (PEAR::isError($err)) {
  3967.                 return $err;
  3968.             }
  3969.         }
  3970.         return MDB2_OK;
  3971.     }
  3972.  
  3973.     // }}}
  3974.     // {{{ function bindParam($parameter, &$value, $type = null)
  3975.  
  3976.     /**
  3977.      * Bind a variable to a parameter of a prepared query.
  3978.      *
  3979.      * @param   int     the order number of the parameter in the query
  3980.      *       statement. The order number of the first parameter is 1.
  3981.      * @param   mixed   variable that is meant to be bound to specified
  3982.      *       parameter. The type of the value depends on the $type argument.
  3983.      * @param   string  specifies the type of the field
  3984.      *
  3985.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  3986.      *
  3987.      * @access  public
  3988.      */
  3989.     function bindParam($parameter, &$value, $type = null)
  3990.     {
  3991.         if (!is_numeric($parameter)) {
  3992.             $parameter = preg_replace('/^:(.*)$/', '\\1', $parameter);
  3993.         }
  3994.         if (!in_array($parameter, $this->positions)) {
  3995.             return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  3996.                 'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
  3997.         }
  3998.         $this->values[$parameter] =& $value;
  3999.         if (!is_null($type)) {
  4000.             $this->types[$parameter] = $type;
  4001.         }
  4002.         return MDB2_OK;
  4003.     }
  4004.  
  4005.     // }}}
  4006.     // {{{ function bindParamArray(&$values, $types = null)
  4007.  
  4008.     /**
  4009.      * Bind the variables of multiple a parameter of a prepared query in bulk.
  4010.      *
  4011.      * @param   array   specifies all necessary information
  4012.      *       for bindParam() the array elements must use keys corresponding to
  4013.      *       the number of the position of the parameter.
  4014.      * @param   array   specifies the types of the fields
  4015.      *
  4016.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  4017.      *
  4018.      * @access  public
  4019.      * @see     bindParam()
  4020.      */
  4021.     function bindParamArray(&$values, $types = null)
  4022.     {
  4023.         $types = is_array($types) ? array_values($types) : array_fill(0, count($values), null);
  4024.         $parameters = array_keys($values);
  4025.         foreach ($parameters as $key => $parameter) {
  4026.             $err = $this->bindParam($parameter, $values[$parameter], $types[$key]);
  4027.             if (PEAR::isError($err)) {
  4028.                 return $err;
  4029.             }
  4030.         }
  4031.         return MDB2_OK;
  4032.     }
  4033.  
  4034.     // }}}
  4035.     // {{{ function &execute($values = null, $result_class = true, $result_wrap_class = false)
  4036.  
  4037.     /**
  4038.      * Execute a prepared query statement.
  4039.      *
  4040.      * @param   array   specifies all necessary information
  4041.      *       for bindParam() the array elements must use keys corresponding to
  4042.      *       the number of the position of the parameter.
  4043.      * @param   mixed   specifies which result class to use
  4044.      * @param   mixed   specifies which class to wrap results in
  4045.      *
  4046.      * @return  mixed   a result handle or MDB2_OK on success, a MDB2 error on failure
  4047.      *
  4048.      * @access  public
  4049.      */
  4050.     function &execute($values = null, $result_class = true, $result_wrap_class = false)
  4051.     {
  4052.         if (is_null($this->positions)) {
  4053.             return $this->db->raiseError(MDB2_ERROR, null, null,
  4054.                 'Prepared statement has already been freed', __FUNCTION__);
  4055.         }
  4056.  
  4057.         $values = (array)$values;
  4058.         if (!empty($values)) {
  4059.             $err = $this->bindValueArray($values);
  4060.             if (PEAR::isError($err)) {
  4061.                 return $this->db->raiseError(MDB2_ERROR, null, null,
  4062.                                             'Binding Values failed with message: ' . $err->getMessage(), __FUNCTION__);
  4063.             }
  4064.         }
  4065.         $result =& $this->_execute($result_class, $result_wrap_class);
  4066.         return $result;
  4067.     }
  4068.  
  4069.     // }}}
  4070.     // {{{ function &_execute($result_class = true, $result_wrap_class = false)
  4071.  
  4072.     /**
  4073.      * Execute a prepared query statement helper method.
  4074.      *
  4075.      * @param   mixed   specifies which result class to use
  4076.      * @param   mixed   specifies which class to wrap results in
  4077.      *
  4078.      * @return  mixed   MDB2_Result or integer on success, a MDB2 error on failure
  4079.      *
  4080.      * @access  private
  4081.      */
  4082.     function &_execute($result_class = true, $result_wrap_class = false)
  4083.     {
  4084.         $this->last_query = $this->query;
  4085.         $query = '';
  4086.         $last_position = 0;
  4087.         foreach ($this->positions as $current_position => $parameter) {
  4088.             if (!array_key_exists($parameter, $this->values)) {
  4089.                 return $this->db->raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  4090.                     'Unable to bind to missing placeholder: '.$parameter, __FUNCTION__);
  4091.             }
  4092.             $value = $this->values[$parameter];
  4093.             $query.= substr($this->query, $last_position, $current_position - $last_position);
  4094.             if (!isset($value)) {
  4095.                 $value_quoted = 'NULL';
  4096.             } else {
  4097.                 $type = !empty($this->types[$parameter]) ? $this->types[$parameter] : null;
  4098.                 $value_quoted = $this->db->quote($value, $type);
  4099.                 if (PEAR::isError($value_quoted)) {
  4100.                     return $value_quoted;
  4101.                 }
  4102.             }
  4103.             $query.= $value_quoted;
  4104.             $last_position = $current_position + 1;
  4105.         }
  4106.         $query.= substr($this->query, $last_position);
  4107.  
  4108.         $this->db->offset = $this->offset;
  4109.         $this->db->limit = $this->limit;
  4110.         if ($this->is_manip) {
  4111.             $result = $this->db->exec($query);
  4112.         } else {
  4113.             $result =& $this->db->query($query, $this->result_types, $result_class, $result_wrap_class);
  4114.         }
  4115.         return $result;
  4116.     }
  4117.  
  4118.     // }}}
  4119.     // {{{ function free()
  4120.  
  4121.     /**
  4122.      * Release resources allocated for the specified prepared query.
  4123.      *
  4124.      * @return  mixed   MDB2_OK on success, a MDB2 error on failure
  4125.      *
  4126.      * @access  public
  4127.      */
  4128.     function free()
  4129.     {
  4130.         if (is_null($this->positions)) {
  4131.             return $this->db->raiseError(MDB2_ERROR, null, null,
  4132.                 'Prepared statement has already been freed', __FUNCTION__);
  4133.         }
  4134.  
  4135.         $this->statement = null;
  4136.         $this->positions = null;
  4137.         $this->query = null;
  4138.         $this->types = null;
  4139.         $this->result_types = null;
  4140.         $this->limit = null;
  4141.         $this->is_manip = null;
  4142.         $this->offset = null;
  4143.         $this->values = null;
  4144.  
  4145.         return MDB2_OK;
  4146.     }
  4147.  
  4148.     // }}}
  4149. }
  4150.  
  4151. // }}}
  4152. // {{{ class MDB2_Module_Common
  4153.  
  4154. /**
  4155.  * The common modules class for MDB2 module objects
  4156.  *
  4157.  * @package     MDB2
  4158.  * @category    Database
  4159.  * @author      Lukas Smith <smith@pooteeweet.org>
  4160.  */
  4161. class MDB2_Module_Common
  4162. {
  4163.     // {{{ Variables (Properties)
  4164.  
  4165.     /**
  4166.      * contains the key to the global MDB2 instance array of the associated
  4167.      * MDB2 instance
  4168.      *
  4169.      * @var     int
  4170.      * @access  protected
  4171.      */
  4172.     var $db_index;
  4173.  
  4174.     // }}}
  4175.     // {{{ constructor: function __construct($db_index)
  4176.  
  4177.     /**
  4178.      * Constructor
  4179.      */
  4180.     function __construct($db_index)
  4181.     {
  4182.         $this->db_index = $db_index;
  4183.     }
  4184.  
  4185.     // }}}
  4186.     // {{{ function MDB2_Module_Common($db_index)
  4187.  
  4188.     /**
  4189.      * PHP 4 Constructor
  4190.      */
  4191.     function MDB2_Module_Common($db_index)
  4192.     {
  4193.         $this->__construct($db_index);
  4194.     }
  4195.  
  4196.     // }}}
  4197.     // {{{ function &getDBInstance()
  4198.  
  4199.     /**
  4200.      * Get the instance of MDB2 associated with the module instance
  4201.      *
  4202.      * @return  object  MDB2 instance or a MDB2 error on failure
  4203.      *
  4204.      * @access  public
  4205.      */
  4206.     function &getDBInstance()
  4207.     {
  4208.         if (isset($GLOBALS['_MDB2_databases'][$this->db_index])) {
  4209.             $result =& $GLOBALS['_MDB2_databases'][$this->db_index];
  4210.         } else {
  4211.             $result =& MDB2::raiseError(MDB2_ERROR_NOT_FOUND, null, null,
  4212.                 'could not find MDB2 instance');
  4213.         }
  4214.         return $result;
  4215.     }
  4216.  
  4217.     // }}}
  4218. }
  4219.  
  4220. // }}}
  4221. // {{{ function MDB2_closeOpenTransactions()
  4222.  
  4223. /**
  4224.  * Close any open transactions form persistent connections
  4225.  *
  4226.  * @return  void
  4227.  *
  4228.  * @access  public
  4229.  */
  4230.  
  4231. function MDB2_closeOpenTransactions()
  4232. {
  4233.     reset($GLOBALS['_MDB2_databases']);
  4234.     while (next($GLOBALS['_MDB2_databases'])) {
  4235.         $key = key($GLOBALS['_MDB2_databases']);
  4236.         if ($GLOBALS['_MDB2_databases'][$key]->opened_persistent
  4237.             && $GLOBALS['_MDB2_databases'][$key]->in_transaction
  4238.         ) {
  4239.             $GLOBALS['_MDB2_databases'][$key]->rollback();
  4240.         }
  4241.     }
  4242. }
  4243.  
  4244. // }}}
  4245. // {{{ function MDB2_defaultDebugOutput(&$db, $scope, $message, $is_manip = null)
  4246.  
  4247. /**
  4248.  * default debug output handler
  4249.  *
  4250.  * @param   object  reference to an MDB2 database object
  4251.  * @param   string  usually the method name that triggered the debug call:
  4252.  *                  for example 'query', 'prepare', 'execute', 'parameters',
  4253.  *                  'beginTransaction', 'commit', 'rollback'
  4254.  * @param   string  message that should be appended to the debug variable
  4255.  * @param   array   contains context information about the debug() call
  4256.  *                  common keys are: is_manip, time, result etc.
  4257.  *
  4258.  * @return  void|string optionally return a modified message, this allows
  4259.  *                      rewriting a query before being issued or prepared
  4260.  *
  4261.  * @access  public
  4262.  */
  4263. function MDB2_defaultDebugOutput(&$db, $scope, $message, $context = array())
  4264. {
  4265.     $db->debug_output.= $scope.'('.$db->db_index.'): ';
  4266.     $db->debug_output.= $message.$db->getOption('log_line_break');
  4267.     return $message;
  4268. }
  4269.  
  4270. // }}}
  4271. ?>