2012-12-31

PHP Database abstraction library

Documentation (together with my other libs)

Once, at times when there was no better option, I used my own lib to abstract the db data retrieval.

It used inheritancy to make it available for more databases (that was influenced by Java I was turing to from PHP at that time).

It provided methods for quite every aspect of a DB. But since I worked only with MySQL, only that implementation was written :)

Usage example

$aasAreas = Array();
$sSQL = "SELECT id, x, y, x2 AS r, popis FROM ".$oFW->GetOption('tables.areas')
       ." WHERE id_ad=".asq((int)$_GET['id']);
$oRes = $oFW->GetDB()->Select($sSQL);
if(!$oRes || !$oRes->IsOk()){ $sError = $oRes->GetError(); break; }
else while( $a = $oRes->FetchRow() ){
    $aasAreas[] = $a;
}

Another usage example

<?
header("Content-Type: text/plain");

$oMySQLConn = new cDBAccess_MySQL('localhost:3350', 'test', 'test');
$oMySQLConn->SetConnectionCharset('cp1250');  // cp1250_general_ci
$oMySQLConn->SetSelectMode( CDBA_SELECT_RETURNS_CRESULT_ON_ERROR );
$oMySQLConn->Connect();
$oResult =& $oMySQLConn->Select($sql = "SELECT 1 + 1");
$oResult =& $oMySQLConn->Select($sql = "SHOW DATABASES");
// var_dump($oResult);
if( (!$oResult) || !$oResult->IsOK() )
  //echo $oMySQLConn->GetError();
  echo $oResult->GetError();
else{
        //echo "\n".$oResult->GetCell(0,0);
        while($a = $oResult->FetchRow(MYSQL_NUM)){
                echo "\n". $a[0];
        }
}
$oMySQLConn->FreeResults();
echo "\n".$oResult->NumRows();

echo "\nErrors num: ". $oMySQLConn->GetErrorsNum();
foreach($oMySQLConn->GetErrors() as $i => $oError){
        echo "\n". $oError->Dump('_TXT_', 1);
        //echo "<br/>Error: ". var_export($oError, 1);
}
$oMySQLConn->ClearErrors();
foreach($oMySQLConn->GetErrors() as $i => $oError){
        echo "\n". $oError->Dump('_TXT_', 1);
}


//      CheckTable() test
echo " ".(int)$bTableOk = $oMySQLConn->CheckTable('i18n', Array(
        Array('name'=>'idstr', 'null'=>false, 'key'=>'primary',  'types'=>''),
        Array('name'=>'lang',  'null'=>false, 'key'=>'multiple', 'types'=>''),
        Array('name'=>'val',   'null'=>false, 'key'=>'none',     'types'=>''),
) );
?>

Base (abstract) class

<?php

/*****************************************************************
   cDBAccess - Yet another database access wrapper
   Written by Ondra Zizka @ Dynawest, ondra@dynawest.cz
   @version 1.0.5

         Changes:
         1.0.5 - Added:   $this->dbflags - flags for database connection.
         1.0.4 - Changed: SetConnectionCharset() sets internal variable.
         1.0.3 - Added:   GetQueriesCount() counts performed queries (those passed to database)
         1.0.2 - Introduced CDBA_SELECT_RETURNS_CRESULT_ON_ERROR:
                        and CDBA_SELECT_RETURNS_NULL_ON_ERROR:
                 cDBAccess::Select() has $iFlags param. When passed to with -"-,
                 it returns cDBA_Result object even upon failure.
                 The failure is detectable with cDBA_Result->IsOK().
               - function cDBA_Result::IsOK()
               - functions cDBA_Result::GetError(), cDBA_Result::GetErrno()
         1.0.1 - _Query() renamed to _Execute()
               - var $bConnected;
               - function GetLastError()
               - function GetLastErrorString()
               - function Disconnect()
               - function GetConnection()
               - function IsConnected()
*****************************************************************/



define('CDBA_SELECT_RETURNS_NULL_ON_ERROR', 0);
define('CDBA_SELECT_RETURNS_CRESULT_ON_ERROR', 1);


/**  class cDBAccess - Yet another database access wrapper  */
class cDBAccess{

        var $dbspoj;
        var $dbhost;
        var $dbuser;
        var $dbpass;
        var $dbflags;
        function SetFlags($iFlags){ $this->dbflags = $iFlags; }
        function GetFlags(){ return $this->dbflags; }

        var $sDatabase;
        var $sDatabaseDefault;
        var $sConnCharset;
        var $sConnCharsetDefault;

        var $bStrict;
        var $bConnected;
        var $iSelectMode = CDBA_SELECT_RETURNS_NULL_ON_ERROR;
        function GetSelectMode()            { return $this->iSelectMode; }
        function SetSelectMode($iMode){
                switch( $iMode ){
                        case CDBA_SELECT_RETURNS_NULL_ON_ERROR:
                        case CDBA_SELECT_RETURNS_CRESULT_ON_ERROR:
                                $this->iSelectMode = $iMode;
                                return true; break;
                        default: user_error('Unknown select mode ['.$iMode.']'); return false; break;
                }
        }

        var $aoErrors = Array();
        var $aoSelectResults = Array();
        var $iQueries = 0;
        function GetQueriesCount()         { return $this->iQueries; }
        function _IncQueriesCount(){ $this->iQueries++; }



        // Constructor
        function cDBAccess($dbhost="", $dbuser="", $dbpass="", $sDatabaseDefault=null, $sConnCharsetDefault=null){
                $this->dbhost           = $dbhost;                 //
                $this->dbuser           = $dbuser;                 //
                $this->dbpass           = $dbpass;                 //

                $this->sDatabase        = '';                      //
                $this->sDatabaseDefault = $sDatabaseDefault;       //
                $this->sConnCharset     = '';                      //
                $this->sConnCharsetDefault = $sConnCharsetDefault; //

                $this->bConnected       = false;
        }

        // Errors //
        function _AddError($sError, $iErrno, $sSQL){ $this->aoErrors[] = new cDBA_Error($sError, $iErrno, $sSQL); }
        function _SetError($sError, $iErrno, $sSQL){
                $this->ClearErrors();
                $this->_AddError($sError, $iErrno, $sSQL);
        }
        function GetErrors()   { return $this->aoErrors; }
        function GetErrorsNum(){ return Count($this->aoErrors); }
        function GetLastError(){ return (!is_array($this->aoErrors) || Count($this->aoErrors) <= 0) ? null : $this->aoErrors[Count($this->aoErrors)-1]; }
        function GetLastErrorString() {
                return (is_array($this->aoErrors) && Count($this->aoErrors) > 0) ? $this->aoErrors[Count($this->aoErrors)-1]->GetString() : ""; }
        function ClearErrors(){ $this->aoErrors = Array(); }


        /** Connection */
        function IsConnected(){ return $this->bConnected && is_resource($this->dbspoj); }

        function Disconnect(){
                if($this->bConnected)
                        $this->_Disconnect();
                $this->bConnected = false;
                $this->dbspoj = null;
        }
        /** Abstract - override */
        function _Disconnect(){}

        /**  Acquires and returns the real connection. Does not set the memberv value.  */
        function _Connect(){
                $this->Disconnect();

                // Verbose intentially to allow copy and paste...
                $spoj = null;
                if(!$spoj){
                        $this->_AddError("cDBAccess is an abstract class. Use some extended class.", 0,
                          "Host: [$this->dbhost], User: [$this->dbuser], Using password: ".($this->dbpass ? 'YES' : 'NO'));
                        return false;
                }
                $this->bConnected = true;
                return $spoj;
        }

        /**  Connects to the database [and sets the default database].  */
        function Connect(){
                $spoj = $this->_Connect();
                if(!$spoj){ return false; }
                $this->dbspoj = $spoj;

                // Default Charset
                if($this->sConnCharsetDefault){
                        $this->SetConnectionCharset($this->sConnCharsetDefault);
                }

                // Default database
                if($this->sDatabaseDefault){
                        $this->UseDatabase($this->sDatabaseDefault);
                }

                return true;
        }
        function GetConnection(){ return $this->dbspoj; }


        /**  Select the active database.  */
        function UseDatabase($sDatabase){ return false; }




        /**  Executes a SQL statement  */
        function _Execute($sSQL){
                if(!$this->dbspoj) do{
                        if(!$this->bStrict){
                                if($this->Connect()){ break; }
                        }
                        $this->_AddError('Can\'t execute when not connected.',  2006, $sSQL);
                }while(false);

                return null;
        }


        /**  Executes a SQL statement  */
        function &Execute($sSQL){
                $rRes = $this->_Execute($sSQL);
                $iRows = 0;
                return $this->CreateResult($rRes, $sSQL, $iRows);
        }


        /**  Makes a SELECT and returns the result.
         *   Result returned by reference to let the object know when you unset($res); .
         *   @returns  Depends on select mode set by SetSelectMode():
         *          CDBA_SELECT_RETURNS_NULL_ON_ERROR: Returns null on error.
         *      CDBA_SELECT_RETURNS_CRESULT_ON_ERROR: Returns cDBA_Result object with error info.
         *   @see cDBAccess::SetSelectMode()
         */
        function &Select($sSQL){
                $oRes = null;

                $rRes = $this->_Execute($sSQL);
                // OK
                if($rRes && 'resource' == gettype($rRes) && $this->IsSelectResult($rRes)){
                        $iRows = 0;//... set in subclass
                        $oRes =& $this->CreateResult($rRes, $sSQL, $iRows, 0,0);
                        $this->CollectResult($oRes);
                }
                // Error
                else if($this->iSelectMode == CDBA_SELECT_RETURNS_CRESULT_ON_ERROR){
                        $iRows = 0;
                        $sError = 1;//... set in subclass
                        $iErrno = 1;//... set in subclass
                        // !!! Incompatibility with oldre cDBAccess !!!
                        // ->> CDBA_SELECT_RETURNS_NULL_ON_ERROR
                        $oRes =& $this->CreateResult(null, $sSQL, 0, $sError, $iErrno);
                }
                return $oRes;
        }

        /**  Creates a result object. Needed to let subclass use another result class.  */
        function &CreateResult($rRes, $sSQL, $iRows, $sError, $iErrno){
                return new cDBA_Result($rRes, $sSQL, $iRows, $sError, $iErrno);
        }

        /**  Returns whether the result contains rows (comes from SELECT or SHOW)  */
        function IsSelectResult($rRes){ return false; }

        /**  Collects a result handler from SELECT to free it later.  */
        function CollectResult(&$oRes){
                $this->aoSelectResults[] = &$oRes;
        }

        /**  Releases results in stored handlers.  */
        function FreeResults(){
                foreach($this->aoSelectResults as $i => $oRes){
                        $this->aoSelectResults[$i]->FreeResult();
                        //$this->aoSelectResults[$i] = 0;
                        unset($this->aoSelectResults[$i]);
                }
        }


        /**  SetConnectionCharset() - sets connection charset.   [MySQL > 4.1.1]  */
        function SetConnectionCharset($sCharset){ $this->sConnCharset = $sCharset; return false; }



} /* class cDBAccess */




/*****************************************************************
   class cDBA_Error - cDBA Error container
*****************************************************************/
class cDBA_Error{
        var $sError;
        var $iErrno;
        var $sSQL;

        // Constructor
        function cDBA_Error($sError, $iErrno, $sSQL){
                $this->sError = $sError;    //
                $this->iErrno = $iErrno;    //
                $this->sSQL   = $sSQL;      //
        }

        function GetString(){ return $this->sError; }
        function GetNumber(){ return $this->iErrno; }
        function GetSql()   { return $this->sSQL; }

        // Dump() //
        function Dump($f='_DEF_', $bDebug=false){
                if(!$bDebug) return '[SQL error]';
                $sql = $this->sSQL;
                    if($f=='_DEF_'){ $f = '<div class="sql_error"><b>MySQL error:</b> <i>%s</i>'.($this->sSQL?'<br/><b>SQL:</b> %s':'')."</div>\n"; $sql = htmlspecialchars($this->sSQL); }
                elseif($f=='_PBB_'){ $f = '[block.sql_error][b]SQL error:[/b] %s'.($this->sSQL?'[br/][b]SQL:[/b] [span.sql]%s[/span]':'')."[/block]\n"; $sql = str_replace('[', '[[', $this->sSQL); }
                elseif($f=='_TXT_'){ $f = 'SQL error: %s'.($this->sSQL ? '  [SQL: %s]' : '')."\n"; }
                return sprintf($f, $this->sError, $sql);
        }

} /* class cDBA_Error */




/*****************************************************************
   class cDBA_Result - a class for SELECT result.
*****************************************************************/
class cDBA_Result{

        var $rRes;
        var $sSQL;
        var $iRows;
        var $sError;
        function GetError()       { return $this->sError; }
        //function SetError($sError){ $this->sError = $sError; }

        var $iErrno;
        function GetErrno()       { return $this->iErrno; }
        //function SetErrno($iErrno){ $this->iErrno = $iErrno; }


        // Constructor //
        function cDBA_Result(&$rRes, $sSQL, $iRows, $sError=null, $iErrno=null){
                $this->rRes  =& $rRes;    //
                $this->sSQL  =  $sSQL;    //
                $this->iRows =  $iRows;   //
                $this->sError = $sError;  //
                $this->iErrno = $iErrno;  //
        }


        /**  Error during query?  */
        function IsOK(){ return empty($this->iErrno) && empty($this->sError) && (TRUE == $this->rRes || is_resource($this->rRes) ); }

        /**  Frees a result.  */
        function FreeResult(){ $this->rRes = null; /*$this->sSQL = '';*/ $this->iRows = 0; return false; }

        /**  Returns a number of rows in a result.  */
        function NumRows(){ return $this->iRows; }

        /**  Returns content of one cell in a result.  */
        function GetCell($iRow, $iField){ return false; }

        function DataSeek($iOffset){ return null; }

        function FetchField($iOffset=null){ return ($iOffset == null) ? null : null; }

        function FieldSeek($iOffset){ return null; }

        function NumFields(){ return null; }

        function FieldType($iOffset){ return null; }

        function FieldTable($iOffset){ return null; }

        /**  @returns an array of objects with information about columns of the result.  */
        function GetColumns(){ return Array(); }

} /* class cDBA_Result */
?>

MySQL extension

<?php

require_once dirname(__FILE__).'/'.'lib.cDBAccess.php';
if(!function_exists('mysql_pconnect'))
        user_error('Function mysql_pconnect() not found! Needed in '.__FILE__);


define ('MYSQL_ERR_NO_SUCH_TABLE', 1146);
define ('MYSQL_ERR_DUPLICATE_KEY', 1022);
define ('MYSQL_ERR_DUPLICATE_ENTRY', 1062);
if(!defined('MYSQL_CLIENT_MULTI_RESULTS'))
        define('MYSQL_CLIENT_MULTI_RESULTS', 131072);

/*****************************************************************
   cDBAccess_MySQL - Extension of cDBAccess for MySQL
   Written by Ondra Zizka @ Dynawest, ondra@dynawest.cz
   @version 1.0.6

         History:
         1.0.6:
            - Added:  _Connect() - $this->dbflags - flags for database connection.
         1.0.5:
           - Changed: SetConnectionCharset() calls parent::SetConnectionCharset()
         1.0.4:
                - Added:   CheckTable()
                - Changed: GetCell() - removed mysql_data_seek(0) at the end of call.
         1.0.3:
                - Added:   _IncQueriesCount() - Counts queries performed.
                - Changed: Execute() and Select() now returns cDBA_Result_MySQL object, see IsOK().
                -
         1.0.2:
                - Changed function GetCell($iRow, $iField, $xDefault=null):
                        When $xDefault is set, returns it when coordinates are out of data bounds.
          - Several compatibility changes, documentation improvements.
         1.0.1:
           Upravena cResult_MySQL::FreeResult()  (rRes muze byt 1 pro dotazy bez vracenych dat)
                 Upravena cResult_MySQL::NumRows() (pokud rRes == 1, vraci ulozeny pocet z mysql_affected_rows())

*****************************************************************/
class cDBAccess_MySQL extends cDBAccess {

        function _Connect(){
                $spoj = @mysql_pconnect($this->dbhost, $this->dbuser, $this->dbpass, $this->dbflags);
                if(!$spoj){
                        $this->_AddError(@mysql_error(), @mysql_errno(),
                            "Connect() Host: $this->dbhost, User: $this->dbuser, Using password: ".($this->dbpass ? 'YES' : 'NO').", Flags: $this->dbflags");
                        $this->bConnected = false;
                        return false;
                }
                $this->bConnected = true;
                return $spoj;
        }

        /** Note: mysql_close() will not close persistent links created by mysql_pconnect().  */
        function _Disconnect(){ return true; /* @mysql_close($spoj); */ }


        function _AddError($sError, $iErrno, $sSQL){ $this->aoErrors[] = new cDBA_MySQL_Error($sError, $iErrno, $sSQL); }


        /**  Select the active database.  */
        function UseDatabase($sDatabase){
                $bDbOk = @mysql_select_db($sDatabase);
                if(!$bDbOk){
                        $this->_AddError(@mysql_error($this->dbspoj), @mysql_errno($this->dbspoj), "USE ".mysql_escape_string($this->sDatabaseDefault));
                        return false;
                }
                return true;
        }

        /**  Retrieves the ID generated for an AUTO_INCREMENT column by the previous INSERT query.  */
        function GetLastInsertId(){
                return @mysql_insert_id($this->dbspoj);
        }



        /**  Executes a SQL statement  */
        function _Execute($sSQL){
                if(!$this->dbspoj) do{
                        if(!$this->bStrict){
                                if($this->Connect()){ break; }
                        }
                        $this->_AddError('Can\'t execute when not connected.',  2006, $sSQL);
                        return null;
                }while(false);

                $this->_IncQueriesCount();

                // For SELECT, SHOW, DESCRIBE, EXPLAIN statements,  returns a resource on success  or FALSE on error.
                // For other type of SQL statements,                returns TRUE       on success  or FALSE on error.
                $rRes = mysql_query($sSQL, $this->dbspoj);
                if(!$rRes){
                        $this->_AddError(mysql_error($this->dbspoj), mysql_errno($this->dbspoj), $sSQL);
                }
                return $rRes;
        }


        /**  Executes a SQL statement.
         *   @returns false on failure,
         *            cResult_MySQL object on success (no matter whether rows were returned).
                      If no rows were returned, cResult_MySQL->rRes contains 1.  */
        function Execute($sSQL){
                if( !isset($sSQL) ) return $this->CreateResult(null, null, 0, '$sSQL parameter not passed to '.__METHOD__.'($sSQL).', 0);
                if( !$this->IsConnected() )
                        return $oRes =& $this->CreateResult(null, "", 0, "Not connected.", 1001);
                $rRes = $this->_Execute($sSQL);
                $oRes = false;

                // Error
                if( TRUE == $rRes || is_resource($rRes) ){
                        $iRows = mysql_affected_rows($this->dbspoj);
                        $oRes = $this->CreateResult($rRes, $sSQL, $iRows, null, null);
                        return $oRes;
                }
                // OK
                //if( false === $rRes || null === $rRes ){
                else if($this->iSelectMode == CDBA_SELECT_RETURNS_CRESULT_ON_ERROR){
                        $sError = mysql_error($this->dbspoj);
                        $iErrno = mysql_errno($this->dbspoj);
                        // !!! Incompatibility with oldre cDBAccess !!!
                        // ->> CDBA_SELECT_RETURNS_NULL_ON_ERROR
                        $oRes = $this->CreateResult(null, $sSQL, 0, $sError, $iErrno);
                }
                return $oRes;
        }

        /**  Makes a SELECT and returns the result.
         *   Result returned by reference to let the object know when you unset($res); .
         *   @returns  Depends on select mode set by SetSelectMode():
         *          CDBA_SELECT_RETURNS_NULL_ON_ERROR: Returns null on error.
         *      CDBA_SELECT_RETURNS_CRESULT_ON_ERROR: Returns cDBA_Result object.
         *   @see cDBAccess::SetSelectMode()
         */
        function &Select($sSQL){
                if( !isset($sSQL) ) return $this->CreateResult(null, null, 0, '$sSQL parameter not passed to '.__METHOD__.'($sSQL).', 0);
                if( !$this->IsConnected() ){
                        $oRes =& $this->CreateResult(null, "", 0, "Not connected.", 1001); return $oRes;
                }
                $rRes = $this->_Execute($sSQL);
                $oRes = null;
                // OK
                if($rRes && 'resource' == gettype($rRes) && $this->IsSelectResult($rRes)){
                        $iRows = mysql_num_rows($rRes);
                        $oRes =& $this->CreateResult($rRes, $sSQL, $iRows, null, null);
                        $this->CollectResult($oRes);
                }
                // Error
                else if($this->iSelectMode == CDBA_SELECT_RETURNS_CRESULT_ON_ERROR){
                        $sError = mysql_error($this->dbspoj);
                        $iErrno = mysql_errno($this->dbspoj);
                        // !!! Incompatibility with oldre cDBAccess !!!
                        // ->> CDBA_SELECT_RETURNS_NULL_ON_ERROR
                        $oRes =& $this->CreateResult(null, $sSQL, 0, $sError, $iErrno);
                }
                return $oRes;
        }


        /**  Performs a select and returns value from the specified cell. */
        function SelectCell($sSQL, $iRow=0, $iCol=0){
                $oRes = $this->Select($sSQL);
                if(null == $oRes || !$oRes->IsOK()) return null;
                $xVal = $oRes->GetCell($iRow, $iCol, FALSE);
                if( FALSE == $xVal )
                        return null;
                return $xVal;
        }


        /**  Returns whether the result contains rows (comes from SELECT or SHOW)  */
        function IsSelectResult($rRes){ return mysql_num_fields($rRes); }


        /**  Creates a result object. Needed to let subclass use another result class.  */
        function CreateResult($rRes, $sSQL, $iRows, $sError, $iErrno){
                $o = new cDBA_MySQL_Result($rRes, $sSQL, $iRows, $sError, $iErrno);
                return $o;
        }



        /**  SetConnectionCharset() - sets connection charset.   [MySQL > 4.1.1]  */
        function SetConnectionCharset($sCharset){
                //echo "\n".CallInfo();///
                parent::SetConnectionCharset($sCharset); // sets the internal variable
                $sSQL = "SET CHARACTER SET '".mysql_escape_string($sCharset)."'";
                $bSucc = $this->Execute($sSQL);
                //echo "<br>Succ: ".(int)$bSucc;///
                if(!$bSucc){
                        $this->_AddError("Error when setting CHARSET to [$sCharset].", 0, '');
                        //echo AdjustedPrintR(debug_print_backtrace());///
                        return false;
                }
        }


        /** Returns number of rows affected by the last query performed.
                @deprecated
                You should use cDBA_Result::NumRows() instead. */
        function AffectedRows(){ return mysql_affected_rows($this->dbspoj); }




        /**<***********************************************************************>
        *  Checks whether a table is in given format.                              *
        * @param sTable  table name
        * @param aaCols  expected columns. Members:
        *                ['name']: Column's name
        *                ['null']: true = can / false = can't be null / not set or null = whatever
        *                ['key']:  pri* = primary / uni* = unique / mul = normal multiple key / not set = whatever
        * @param bStrict  If true, no other columns accepted
        ***************************************************************************/
        function CheckTable($sTable, $aaCols, $bStrict=false){
                //echo "\n".CallInfo();///

                // Get columns
                $sql = "SHOW COLUMNS FROM ".$sTable;
                $oRes = $this->Select($sql);
                // ERR 1146: Table does not exist.
                if( null == $oRes || !$oRes->IsOK() )
                        return false;

                // Look for columns
                $iOk = 0;
                while($asRow = $oRes->FetchRow()){

                        // Find the column info for this column
                        $aColInfo = null;
                        foreach($aaCols as $aColInfoCur){
                                if($aColInfoCur['name'] == $asRow['Field']){
                                        $aColInfo = $aColInfoCur; break;
                                }
                        }

                        // Column not listed in check-list
                        if( null == $aColInfo ){
                                // If in strict mode, refuse any other column.
                                if( $bStrict ){ $iOk = 0; break; }
                                continue; // Else continue to next column.
                        }
                        // Column found in check-list, check properties
                        else{
                                // Type
                                $sType = substr($asRow['Type'], 0, strcspn($asRow['Type'],'(') );
                                //echo " ".$asRow['Field']." ".$sType." [".$aColInfo['types']."]";

                                if(!empty($aColInfo['types']) && !in_array($sType, explode(' ', $aColInfo['types'])))
                                        continue;
                                // NULL
                                if( isset($aColInfo['null']) && null !== $aColInfo['null'] ){
                                        if( ( $aColInfo['null'] ? 'YES' : 'NO') != $asRow['Null'] )
                                                continue;
                                }
                                // Index
                                if( isset($aColInfo['key']) ){
                                        $sKeyOpt = strtoupper(substr($aColInfo['key'],0,3));
                                        if($sKeyOpt == 'NON'){
                                                if( $bStrict &&  '' != $asRow['Key'] ) // none
                                                        continue;
                                        }
                                        elseif( $sKeyOpt != $asRow['Key'] ) // PRI, UNI, MUL
                                                continue;
                                }
                        }
                        // One more column is ok, count it.
                        $iOk++;
                }// while($asRow)

                $oRes->FreeResult();

                return Count($aaCols) == $iOk;
        }// cDBAccess_MySQL::CheckTable()



} /* class cDBAccess_MySQL */





/*****************************************************************
   class cDBA_MySQL_Result - a class for SELECT result.
*****************************************************************/
class cDBA_MySQL_Result extends cDBA_Result {
        var $rRes;
        var $sSQL;

        // Constructor //
        /*function cDBA_MySQL_Result(&$rRes, $sSQL, $iRows){
                $this->rRes  =& $rRes;    //
                $this->sSQL  =  $sSQL;    //
                $this->iRows =  $iRows;   //
        }*/

        /**  Frees a result.  */
        function FreeResult(){
                $bSucc = true;
                if( is_resource($this->rRes) )
                        $bSucc = mysql_free_result($this->rRes);
                //unset($this->rRes);
                $this->rRes = null; /*$this->sSQL = '';*/ $this->iRows = 0;
                return $bSucc;
        }

        /**  For a result created by queries returning some rows (SELECT, SHOW, DESCRIBE), returns a number of rows in a result.
             For other queries (done with Execute()) like UPDATE, INSERT, DELETE, returns a number of rows affected by that query. */
        function NumRows(){
                return is_resource($this->rRes) ? mysql_num_rows($this->rRes) : $this->iRows;
        }

        /**  Returns content of one cell in a result.  */
        function GetCell($iRow, $xField, $xDefault=null){
                if($iRow   >= $this->NumRows()   || $iRow   < 0)
                        return (null !== $xDefault) ? $xDefault : Array("Bad row index [$iRow/".$this->NumRows()."]");
                if( is_int( $xField ) )
                if($xField >= $this->NumFields() || $xField < 0)
                        return (null !== $xDefault) ? $xDefault : Array("Bad field index [$xField/".$this->$this->NumFields()."]");
                $ret = mysql_result($this->rRes, $iRow, $xField);
                //mysql_data_seek($this->rRes, 0);
                return $ret;
        }


        function FetchRow($iType=MYSQL_ASSOC){
                $ret = mysql_fetch_array($this->rRes, $iType);
                //if( !$ret ){ echo "x"; var_dump($ret); }
                if( $ret === FALSE || is_array($ret) ) return $ret;
                $aBT = debug_backtrace();
                echo "Not a MySQL result in ".$aBT[1]['file']." @ ".$aBT[1]['line'];
        }

        function DataSeek($iOffset){ return mysql_data_seek($this->rRes, $iOffset); }

        // Fields //

        function FetchField($iOffset=null){ return ($iOffset === null) ? mysql_fetch_field($this->rRes) : mysql_fetch_field($this->rRes, $iOffset); }

        function FieldSeek($iOffset){ return mysql_field_seek($this->rRes, $iOffset); }

        function NumFields(){ return mysql_num_fields($this->rRes); }

        function FieldType($iOffset){ return mysql_field_type($this->rRes, $iOffset); }

        function FieldTable($iOffset){ return mysql_field_table($this->rRes, $iOffset); }

        /**  @returns an array of objects with information about columns of the result.  */
        function GetColumns(){
                $aoColumns = Array();

                $iFieldsCount = $this->NumFields();
                for( $i = 0; $i < $iFieldsCount; $i++ ){
                        $aoColumns[] = $this->FetchField($i);
                }
                return $aoColumns;
        }

} /* class cDBA_Result */





/*****************************************************************
   class cDBA_MySQL_Error  -  specializes the Dump
*****************************************************************/
class cDBA_MySQL_Error extends cDBA_Error{

        //  Not needed, called default
        /*function cDBA_MySQL_Error($sError, $iErrno, $sSQL){
                parent::cDBA_Error($sError, $iErrno, $sSQL);
        }*/
        function Dump($f='_DEF_', $bDebug=false){
                if(!$bDebug) return '[MySQL error]';
                $sql = $this->sSQL;
                    if($f=='_DEF_'){ $f = '<div class="mysql_error"><b>MySQL error:</b> <i>%s</i>'.($this->sSQL?'<br/><b>SQL:</b> %s':'')."</div>\n"; $sql = htmlspecialchars($this->sSQL); }
                elseif($f=='_PBB_'){ $f = '[block.mysql_error][b]MySQL error:[/b] %s'.($this->sSQL?'[br/][b]SQL:[/b] [span.sql]%s[/span]':'')."[/block]\n"; $sql = str_replace('[', '[[', $this->sSQL); }
                elseif($f=='_TXT_'){ $f = 'MySQL error: %s'.($this->sSQL ? '  [SQL: %s]' : '')."\n"; }
                return sprintf($f, $this->sError, $sql);
                //$iErrno
        }
} /* cDBA_MySQL_Error */

0