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