MDB2
[ class tree: MDB2 ] [ index: MDB2 ] [ all elements ]

Source for file MDB2.php

Documentation is available at MDB2.php

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

Documentation generated on Fri, 03 Nov 2006 08:30:34 -0500 by phpDocumentor 1.3.0. PEAR Logo Copyright © PHP Group 2004.