<?php
/**
 * YANA library
 *
 * @package yana
 * @licence http://creativecommons.org/licenses/by-nc-sa/2.5/
 */

/**
 * @ignore
 */
@include_once("DB.php");

/**
 * database API
 *
 * this class is a database abstraction api, that uses pear db
 *
 * @access      public
 * @package     yana
 * @subpackage  io
 */
class BufferedDbStream extends SerializeableObject
{
    /**#@+
     * @ignore
     * @access  private
     */

    /** @var  int    */   var $exists              = YANA_DATABASE_FAILURE;
    /** @var  Object */   var $database            = null;
    /** @var  string */   var $last_selected_table = "";
    /** @var  string */   var $filename            = "";
    /** @var  array  */   var $queue               = array();
    /** @var  array  */   var $cache               = array();
    /** @var  array  */   var $filter              = array("DEFAULT" => "");
    /** @var  array  */   var $joins               = array();

    /**#@-*/

   /**
    * @ignore
    * @access protected
    * @var object    */   var $structure           = null;


    /**
     * create a new instance
     *
     * Each database connection depends on a structure file
     * describing the database. Therefore you have to provide
     * a $filename of such structure file (without path or extension)
     *
     * These files are to be found in config/db/*.config
     *
     * @param string $filename
     * @param dbServer $server
     */
    function BufferedDbStream($filename = "", $server = null)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($filename); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($filename, "string");
        global $YANA;

        /* check input $filename */
        if (preg_match('/^([\w\d_]+)$/', $filename)) {
            if (isset($YANA)) {
                $filename = $YANA->getVar('DBDIR').$filename.".config";
            } else {
                $filename = "config/db/".$filename.".config";
            }
        }


        if (is_null($server)) {
            $server = new dbServer();
        }
        $this->database = $server->get();

        if (empty($this->database) || $this->isError($this->database)) {
            /* connection failed - abort */
            $this->exists = YANA_DATABASE_FAILURE;
            $this->handleError($this->database);
        } else {

            /* deactivate autocommit by default */
            $result = $this->database->autoCommit(false);
            if ($this->isError($result)) {
                $this->handleError($result);
            }

            if (empty($filename)) {
                /* no structure file available, estimate structure */
                $this->buildStructure(); /* ability to build structure on the fly */
                $this->exists = YANA_DATABASE_NO_FILE;
            } else if (!file_exists($filename) || !is_readable($filename)) {
                /* given file does not exist */
                unset($this->structure);
                $this->exists = YANA_DATABASE_NOT_AVAILABLE;
                $this->handleError("DB ERROR:  Structure file {$filename} not found.");
            } else {
                /* all fine, proceed */
                $this->exists = YANA_DATABASE_OK;
            }
        }
        $this->filename = $filename;
    }

    /**
     * Commit current transaction
     *
     * This writes all changes to the database
     *
     * @access public
     * @return boolean
     */ 
    function write()
    {

        if (!$this->_check_writeable()) {
            return false;
        }

        if (count($this->queue) == 0) {
            /* Buffer empty */
            return true;
        } else {
            for ($i=0; $i<count($this->queue); $i++)
            {
                /* for security reasons all delete queries will automatically be limited to 1 row at a time */
                if (preg_match('/^DELETE /', $this->queue[$i])) {
                    $result = $this->database->limitQuery($this->queue[$i],0,1);
                } else {
                    $result = $this->database->query($this->queue[$i]);
                }
                if (trim($this->queue[$i]) != '' && $this->isError($result)) {                    
                    $this->handleError($result,'SQL ERROR: Statement '.$this->queue[$i]);
                    $result = $this->database->rollback();
                    if ($this->isError($result)) {
                          $this->handleError($result, "SQL ERROR: Unable to rollback changes. Database might contain corrupt data. Please inform webmaster about time of event and anything you might have done to cause this failure.");
                    }
                    return false;
                }
            }
            $result = $this->database->commit();
            if ($this->isError($result)) {
                $this->handleError($result);
                return false;
            } else {
                $this->queue = array();
                return true;
            }
        }
    }

    /**
     * get values from the database
     *
     * This returns the values at adress $key
     * starting from $offset and limited to 
     * $limit results.
     *
     * @param mixed $key
     * @param string $search (optional)
     * @param string $filter (optional)
     * @param int $offset (optional)
     * @param int $limit (optional)
     * @access public
     * @return mixed
     */ 
    function get($key, $search = "", $filter = "", $offset = 0, $limit = 0)
    {
        if (is_array($key)) {
            if (isset($key['search'])) {
                $search = $key['search'];
            }
            if (isset($key['filter'])) {
                $filter = $key['filter'];
            }
            if (isset($key['offset'])) {
                $offset = $key['offset'];
            }
            if (isset($key['limit'])) {
                $limit = $key['limit'];
            }
            if (isset($key['key'])) {
                $key = $key['key'];
            } else {
                return false;
            }
        }

        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($key);    // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_string($search); // wrong argument type for argument 2, string expected');
        /* argument 3 */ assert('is_string($filter); // wrong argument type for argument 3, string expected');
        /* argument 4 */ assert('is_int($offset);    // wrong argument type for argument 4, integer expected');
        /* argument 5 */ assert('is_int($limit);     // wrong argument type for argument 5, integer expected');
                    
        /* optimistic type casting */
        /* argument 1 */ settype($key,    "string");
        /* argument 2 */ settype($search, "string");
        /* argument 3 */ settype($filter, "string");
        /* argument 4 */ settype($offset, "integer");
        /* argument 5 */ settype($limit,  "integer");

        /* lower-case input */
        $key    = strtolower($key);
        $filter = strtolower($filter);

        if (!$this->_check_available()) {
            return false;
        }

        if ($filter){
            $table = explode('.', $key);
            $table = $table[0];
            $old_filter = $this->getFilter($table);
            $this->setFilter($table, $filter);
            $return = $this->_computeRequest($key, $search, NULL, $offset, $limit);
            $this->setFilter($table, $old_filter);
            return $return;
        } else {
            return $this->_computeRequest($key, $search, NULL, $offset, $limit);
        }
    }

    /**
     * update or insert row
     *
     * insert $value at position $key
     *
     * If $key already exists, the previous value
     * gets updated, else the value is created
     *
     * This function returns bool(true) on success
     * and bool(false) on error. Note, that this
     * function does not auto-commit. This means,
     * changes to the database will NOT be saved
     * unless you call $bufferedDbStream->write().
     *
     * @param string $key
     * @param mixed $value
     * @access public
     * @return boolean
     */
    function insert($key, $value)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($key); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($key, "string");

        $key = strtolower($key);

        if (!$this->_check_writeable()) {
            return false;
        }

        if ($this->_computeRequest($key, "", $value)) {
            return Hashtable::set($this->cache, $key, $value);
        } else {
            $this->handleError("SQL ERROR: Insert on database failed for key '${key}'.", preg_replace('/\s/', ' ', makeConfig($value)));
            return false;
        }

    }

    /**
     * remove one row
     *
     * For security reasons all delete queries will automatically be limited to 1 row at a time.
     * While this might be seen as a limitation the far more valueable advantage is,
     * no user is able to destroy a whole table - wether by intention or by accident -
     * in only one query. (At least not via this API.)
     *
     * @access  public
     * @param   string  $key
     * @param   string  $search (optional)
     * @return  bool
     */
    function remove($key, $in_search = "") 
    {

        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($key);       // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_string($in_search); // wrong argument type for argument 2, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($key,       "string");
        /* argument 2 */ settype($in_search, "string");

        $key = strtolower($key);

        if (!$this->_check_writeable()) {
            return false;
        }

        $keyArray = explode(".", $key);
        settype($keyArray, "array");
        $this->last_selected_table = $keyArray[0];

        if (!empty($in_search)) {
            $search = $this->_computeSearchString($keyArray[0], $in_search, count($keyArray) == 1);
        } else {
            $search = "";
        }

        if ((count($keyArray) < 2 && empty($search)) || (count($keyArray) > 2) || ($keyArray[1] == '0') || (@$this->structure->constraint('DELETE', $keyArray[0], $keyArray[1]) === false)) {
            $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}' for function remove.");
            return false;
        }

        global $YANA;
        if (isset($YANA)) {
            $result = $this->get($key,$in_search,'',0,1);
            if (!empty($result)) {
                $YANA->report(new log('Deleting entry from '.$key.':', preg_replace('/\s/', ' ', makeConfig($result))));
            }
        }

        if ($keyArray[1] == "*") {
            /* remove last entry */
            array_push($this->queue,"DELETE FROM ".$this->quoteId(YANA_DATABASE_PREFIX.$keyArray[0]).( ($search) ? " WHERE $search " : '' )." ORDER BY ".$this->structure->getPrimaryKey($keyArray[0])." DESC");
        } else {
            /* remove entry with id $keyArray[1] */
            if (isset($keyArray[1]) && $keyArray[1] != '') {
                if (!empty($search)) {
                    $search .= ' AND ';
                }
                $search .= $this->structure->getPrimaryKey($keyArray[0])." = ".$this->quote($keyArray[1]);
            }
            array_push($this->queue,"DELETE FROM ".$this->quoteId(YANA_DATABASE_PREFIX.$keyArray[0])." WHERE ".( ($search) ? " $search " : '' ));
        }

        return true;
    }

    /**
     * join the resultsets for two tables
     *
     * results in an INNER JOIN $table1, $table2 WHERE $table1.$key1 = $table2.$key2
     *
     * @access public
     * @param string $table1
     * @param string $table2 (optional)
     * @param string $key1 (optional)
     * @param string $key2 (optional)
     * @return boolean
     */
    function join($table1, $table2 = "", $key1 = "", $key2 = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table1); // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_string($table2); // wrong argument type for argument 2, string expected');
        /* argument 3 */ assert('is_string($key1);   // wrong argument type for argument 3, string expected');
        /* argument 4 */ assert('is_string($key2);   // wrong argument type for argument 4, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table1, "string");
        /* argument 2 */ settype($table2, "string");
        /* argument 3 */ settype($key1,   "string");
        /* argument 4 */ settype($key2,   "string");

        /* lower-case input */
        $table1 = strtolower($table1);
        $table2 = strtolower($table2);
        $key1   = strtolower($key1);
        $key2   = strtolower($key2);

        if (!$this->_check_available()) {
            return false;
        }

        if (preg_match("/^[\w\d-_]*$|^\*$/i", $table1) &&
            preg_match("/^[\w\d-_]*$/i", $table2)      &&
            preg_match("/^[\w\d-_]*$/i", $key1)        &&
            preg_match("/^[\w\d-_]*$/i", $key2)) {

            if ($table1 == "" || $table1 == "*") {
                if (!$this->last_selected_table) {
                    $this->handleError("DB ERROR: Wrong parameter count. Unable to join tables, since no Table has been selected for function join.");
                    return false;
                } else {
                    $table1 = $this->last_selected_table;
                }
            }

            if ($this->structury->isStrict()) {

                if (!$this->structure->isTable($table1) || (!empty($table2) && !$this->structure->isTable($table2))) {
                    $this->handleError("SQL WARNING: Cannot join tables ".$table1." and ".$table2.". Table does not exist in structure file.");
                    return false;
                }
                if ($key1 && !$this->structure->isColumn($table1, $key1)) {
                    $this->handleError("SQL WARNING: Cannot join tables ".$table1." and ".$table2.". Field ".$key1." does not exist in table ".$table1.".");
                    return false;
                }
                if ($table2 && $key2 && !$this->structure->isColumn($table2, $key2)) {
                    $this->handleError("SQL WARNING: Cannot join tables ".$table1." and ".$table2.". Field ".$key2." does not exist in table ".$table2.".");
                    return false;
                }
            }


            if ($table2) {
                $this->joins[$table1][$table2]['TABLE'] = $table2;
                if ($key1) {
                    $this->joins[$table1][$table2]['FKEY']  = $key1;
                }
                if ($key1 && $key2) {
                    $this->joins[$table1][$table2]['TKEY']  = $key2;
                }
                return true;
            } else {
                $this->joins[$table1] = array();
                return true;
            }

        } else {
            $this->handleError("DB ERROR: Syntax error. Invalid argument for function join.");
            return false;
        }

    }

    /**
     * optional API bypass
     *
     * send a sql-statement directly, bypassing the API
     *
     * @access public
     * @param string $sqlStmt
     * @return mixed
     */
    function query($sqlStmt)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($sqlStmt); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($sqlStmt, "string");

        if (!$this->_check_available()) {
            return false;
        }

        if (is_int(strpos($sqlStmt, ';'))) {
            $sqlStmt = preg_replace("/;.*/", "", $sqlStmt);
        }

        return $this->database->query($sqlStmt);
    }

    /**
     * query building
     *
     * This function builds queries from input data.
     *
     * @access  private
     * @param   string  $key
     * @param   string  $search (optional)
     * @param   mixed   $value  (optional)
     * @param   int     $offset (optional)
     * @param   int     $limit  (optional)
     * @return  mixed
     *
     * @ignore
     */
    function _computeRequest($key, $search = "", $value = NULL, $offset = 0, $limit = 0)
    {
        assert('is_string($key) && !empty($key);');
        assert('is_string($search);');
        assert('is_int($offset) && $offset >= 0;');
        assert('is_int($limit) && $limit >= 0;');

        settype($offset, "integer");
        settype($limit, "integer");

        if (!$this->_check_available()) {
            return false;
        }

        $key = strtolower($key);
        $where = "";

        /* int */ $returnedType = 0; /* 0 = unknown, 1 = table, 2 = row, 3 = field, 4 = column */
        /* int */ $queryType    = 0; /* 0 = unknown, 1 = select, 2 = update, 3 = insert */
        /* int */ $useWildcard  = 0; /* 0 = none, 1 = foo.*, 2 = foo.*.foo */
        /* string */ $arrayKey  = '';

        /* 1) check if input is valid */

        settype($key, "string");
        settype($search, "string");
        $key = strtolower($key);
        if (!preg_match("/^((, ?)?([\w\d-_\.])+=([\w\d-_\@\. \&;])+)*$/i", $search)) {
            $this->handleError("DB ERROR: Syntax error. Invalid argument \$search: '${search}'.");
            return false;
        } else if (!preg_match("/^[\w\d-_]+(\.([\w\d-_]*|\*|\?)){0,}(\.\*)?$/i", $key)) {
            $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
            return false;
        }

        /* 2) prepare input for computation */

        if (preg_match("/\.\*?$/", $key)) {
            $useWildcard = 1;
        } else if (preg_match("/[\w\d-_]+\.\?\.([\w\d-_]+)$/i", $key)) {
            $key = preg_replace("/([\w\d-_]+)\.\?\.([\w\d-_]+)$/i", "\\1.*.\\2", $key);
            $useWildcard = 2;
        } else if (preg_match("/[\w\d-_]+\.\*\.([\w\d-_]+)$/i", $key)) {
            $useWildcard = 2; 
            $returnedType = 4;
        } else {
            $useWildcard = 0;
        }

        $key = preg_replace("/\.(\*)?$/", '', $key);
        $keyArray = explode(".", $key);

        /* 3) resolve foreign keys to get true adress */
        if (count($keyArray) > 3 &&  $this->structure->getType($keyArray[0],$keyArray[2]) == 'array') {
            $arrayKey = implode('.', array_slice($keyArray, 3));      
            $keyArray = array_slice($keyArray, 0,3);
        } else {

            while (count($keyArray) > 3 && $this->structure->isForeignKey($keyArray[0],$keyArray[2]))
            {
                $a = $this->_computeRequest($keyArray[0].".".$keyArray[1].".".$keyArray[2]);
                if (!$a) {
                    $this->handleError("DB ERROR: Unable to resolve foreign key '${key}'. Check if foreign key association has been broken.");
                    return false;
                }
                $foreignKeys = $this->structure->getForeignKeys($keyArray[0]);
                $foreignTable = $foreignKeys[strtoupper($keyArray[2])];
                $resultType = $this->structure->getType($foreignTable,$keyArray[2]);
                if ($resultType) {
                    @settype($a, $resultType);
                }
                @ array_shift($keyArray); @ array_shift($keyArray); @ array_shift($keyArray);
                array_unshift($keyArray, $a);
                array_unshift($keyArray, $foreignTable);
                unset($a);
                unset($foreignKeys);
                unset($foreignTable);
                unset($resultType);
            }
        }

        if(count($keyArray) > 3) {
            $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
            return false;
        } else {
            $key = implode('.', $keyArray);
            if ($returnedType == 0) {
                $returnedType = count($keyArray);
            }
        }

        /* 4) now that there is a resolved adress: if $value is set, check first if it is valid before going on */

        if ($returnedType>1) {
            /* Let's check the primary key first */
            $primaryKey = $this->structure->getPrimaryKey($keyArray[0]);
            $keyArray[1] = $this->structure->untaintInput($keyArray[0], $primaryKey, $keyArray[1], YANA_ESCAPE_SLASHED);
        }

        if (!is_null($value)) {

            if (!$this->_check_writeable()) {
                return false;
            }

            if ($returnedType==1 && ($useWildcard==1)) {
                $returnedType = 2;
                $queryType    = 3;
            }
            if ($returnedType==4) {
                $returnedType = count($keyArray);
            }

            if (!$queryType) {
                if ($returnedType==3) {
                    /* Inserting/Updating a field */
                    $queryType = ($this->exists($keyArray[0].'.'.$keyArray[1])) ? 2 : 3;
                } else {
                    /* Inserting/Updating smt. else */
                    $queryType = ($this->exists($key)) ? 2 : 3;
                }
            }

            switch ($returnedType) {

                /* Inserting/Updating a table */
                case 1:
                    if ($this->structure->isStrict()) {
                        $this->handleError("SQL ERROR: Inserting/Updating a table is illegal. Operation aborted.");
                    }
                    return false;
                break;

                /* Updating a row */
                case 2:
                    settype($value, "array");
                    $value = array_change_key_case($value, CASE_LOWER);

                    /* untaint raw data */
                    foreach ($value as $a => $b) 
                    {
                        if ($queryType == 2 && $this->structure->isReadonly($keyArray[0],$a)) {
                            $this->handleError("SQL ERROR: Database is readonly. Insert/Update operation aborted.");
                            return false;
                        }
                        $type = $this->structure->getType($keyArray[0],$a);
                        switch ($type)
                        {
                            case 'text':
                                $escape = YANA_ESCAPE_USERTEXT;
                            break;
                            case 'string':
                                $escape = YANA_ESCAPE_LINEBREAK;
                            break;
                            default:
                                $escape = YANA_ESCAPE_NONE;
                            break;
                        }
                        $value[$a] = $this->structure->untaintInput($keyArray[0], $a, $b, $escape);
                        /* if it is a foreign key, check wether there is a field with that id */
                        $foreignTable = $this->structure->getTableByForeignKey($keyArray[0],$a);
                        if ($this->structure->isForeignKey($keyArray[0],$a) && !$this->exists("$foreignTable.$b")) {
                            /* OK - there is none. Now let's check if there is one recently inserted field in cache */
                            if (!isset($this->cache[$foreignTable][$b])) {
                                $this->handleError("SQL Error: The value '${b}' for attribute '${a}' refers to a non-existing entry in table '${foreignTable}'. Insert/Update operation aborted.");
                                return false;
                            }
                        }
                        unset($foreignTable);
                    }
                    
                    /* check if all required data is available */
                    if ($queryType == 3) {
                        if ($this->structure->checkRow($keyArray[0],$value) === false) {
                            return false;
                        }
                    } /* end if */
                break;

                /* Updating a field */
                case 3:

                    /* untaint raw data */
                    if ($queryType == 2 && $this->structure->isReadonly($keyArray[0],$keyArray[2])) {
                        $this->handleError("SQL ERROR: Database is readonly. Insert/Update operation aborted.");
                        return false;
                    }
                    if ($this->structure->getType($keyArray[0], $keyArray[2]) == 'string') {
                        $escape = YANA_ESCAPE_LINEBREAK;
                    } else {
                        $escape = YANA_ESCAPE_NONE;
                    }
                    $value = $this->structure->untaintInput($keyArray[0], $keyArray[2], $value, $escape);

                    /* if it is a foreign key, check wether there is a field with that id */
                    if ($this->structure->isForeignKey($keyArray[0], $keyArray[2])) {
                        $foreignTable = $this->structure->getTableByForeignKey($keyArray[0],$keyArray[2]);
                        if (!$this->exists("${foreignTable}.${value}")) {
                            $this->handleError("SQL Error: The value '${value}' for attribute '".$keyArray[2]."' refers to a non-existing entry in table '${foreignTable}'. Insert/Update operation aborted.");
                            return false;
                        }
                    }

                break;

                /* error */
                default:
                    $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
                    return false;
                break;

            }

        } else { /* $value is not set - since there no value to set this will be a simple read operation */
            $queryType = 1;
        }

        $this->last_selected_table = $keyArray[0];

        /* 5) build sql-statement */

        switch ($queryType) {

            case 1:
                $sqlStmt = "SELECT";
                if (empty($keyArray[2])) {
                    $sqlStmt .= " * ";
                } else {
                    $sqlStmt .= " ".$keyArray[2]." ";
                }
                $sqlStmt .= "FROM ".$this->quoteId($keyArray[0])." ";
            break;

            case 2:
                if ($returnedType == 2) {
                    if (!is_array($value)) {
                        $this->handleError("DB ERROR: Syntax error. Invalid argument \$value.");
                        return false;
                    }
                    $sqlStmt = $this->_buildUpdate($keyArray[0],$value);
                } else {
                    $sqlStmt = $this->_buildUpdate($keyArray[0],array($keyArray[2] => $value));
                }
            break;
    
            case 3:
                if ($returnedType == 2) {
                    $temp = $this->structure->getPrimaryKey($keyArray[0]);
                    if (!isset($value[$temp]) && !empty($keyArray[1])) {
                        $value[$temp] = $keyArray[1];
                    }
                    $sqlStmt = 'INSERT INTO '.$this->quoteId(YANA_DATABASE_PREFIX.$keyArray[0]).' '.$this->_sqlValues($value).' ';
                    unset($temp);
                } else {
                    $this->handleError("SQL ERROR: Invalid insert operation. You may only insert rows (not fields or tables).");
                    return false;
                }
            break;

            default:
                $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
                return false;
            break;

        }

        /* 6) resolve joins */

        if ($queryType < 3 && $queryType > 0 && $useWildcard < 2) {
            $where = (!empty($keyArray[1])) ? $this->structure->getPrimaryKey($keyArray[0])." = ".$this->quote(addslashes($keyArray[1]))." " : "";
        }

        if ($queryType == 1) {
                $JOIN  = array();
                if (isset($this->joins[$keyArray[0]])) {
                    $JOIN = $this->joins[$keyArray[0]];
                }
                settype($JOIN, "array");

                foreach ($JOIN as $a => $b)
                {
                    if (count($JOIN) == 1 && !$b['TKEY'] && !$b['FKEY']) {
                        $sqlStmt .= "NATURAL JOIN ".$this->quoteId(YANA_DATABASE_PREFIX.$b['TABLE'])." ";
                    } else {
                        $sqlStmt .= ", ".$this->quoteId(YANA_DATABASE_PREFIX.$b['TABLE'])." ";
                        if ($b['FKEY'] && $b['TKEY']) {
                              $where .= ( ($where) ? "AND " : "" ).YANA_DATABASE_PREFIX.$keyArray[0].'.'.$b['FKEY'].' = '.YANA_DATABASE_PREFIX.$b['TABLE'].'.'.$b['TKEY'].' ';
                        } else if ($b['FKEY']) {
                              $where .= ( ($where) ? "AND " : "" ).YANA_DATABASE_PREFIX.$keyArray[0].'.'.$b['FKEY'].' = '.YANA_DATABASE_PREFIX.$b['TABLE'].'.'.$b['FKEY'].' ';
                        } else {
                              /* intenionally left blank */
                        }
                    }
                }
        }

        /* 7) resolve search clause */
        $where = trim($where);
        if (!empty($search)) {
            if (!empty($where)) {
                $where .= ' AND ';
            }
            if (count($keyArray) == 1 || $keyArray[1] == '?' || $keyArray[1] == '*') {
                $where .= $this->_computeSearchString($keyArray[0], $search, true);
            } else {
                $where .= $this->_computeSearchString($keyArray[0], $search, false);
            }
        }
        $sqlStmt .= ( (!empty($where)) ? "WHERE $where " : " " ).$this->getFilter($keyArray[0]);

        /* 8) check constraints  */
        switch ($queryType) {
            case 1:
                /* intenionally left blank */
            break;
            case 2:
                if (@$this->structure->constraint('UPDATE', $keyArray[0], $keyArray[2], $value) === false) {
                    return false;
                }
            break;
            case 3:
                if (@$this->structure->constraint('INSERT', $keyArray[0], $keyArray[2], $value) === false) {
                    return false;
                }
            break;
            default:
                $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
                return false;
            break;
        }

        if ($queryType == 2 || $queryType == 3) {
            array_push($this->queue,$sqlStmt);
            return true;
        } else if ($queryType == 1) {
            if ($offset>0 || $limit>0) {
                $result = $this->database->limitQuery($sqlStmt, $offset, $limit);
            } else {
                $result = $this->database->query($sqlStmt);
            }
            if ($this->isError($result)) {
                $this->handleError($result, "SQL WARNING: Query '${sqlStmt}' on database failed");
            } else {
                $REF = array();
                $id  = $this->structure->getPrimaryKey($this->getTable());

                for ($i=0; $i<$result->numRows(); $i++) 
                {
                    $row = $result->fetchRow(DB_FETCHMODE_ASSOC,$i);
                    if (!is_array($row)) {
                        $this->handleError("DB ERROR: The returned data for statement '${sqlStmt}' must be of type array. Instead database returned the following value '${row}'.");
                        break;
                    }
                    foreach ($row as $a => $b) 
                    {
                        if (!is_null($b)) {
                            switch ($returnedType) {
                                case 1:
                                    if (@$this->structure->constraint('SELECT', $keyArray[0], null, $b) !== false) {
                                        if ($this->structure->getType($keyArray[0],$a) !== 'array') {
                                            $REF[strtoupper($row[$id])][strtoupper($a)] = stripslashes($b);
                                        } else {
                                            if (is_array($b)) {
                                                $REF[strtoupper($row[$id])][strtoupper($a)] = $b;
                                            } else {
                                                $REF[strtoupper($row[$id])][strtoupper($a)] = getConfigFile(explode("\n",$b));
                                            }
                                        }
                                    } else {
                                        unset($REF[strtoupper($row[$id])]);
                                        break;
                                    }
                                break;

                                case 2:
                                    if (@$this->structure->constraint('SELECT', $keyArray[0], null, $b) !== false) {
                                        if ($this->structure->getType($keyArray[0],$a) !== 'array') {
                                            $REF[strtoupper($a)] = stripslashes($b);
                                        } else {
                                            if (is_array($b)) {
                                                $REF[strtoupper($a)] = $b;
                                            } else {
                                                $REF[strtoupper($a)] = getConfigFile(explode("\n",$b));
                                            }
                                        }
                                    } else {
                                        return array();
                                    }
                                break;

                                case 3:
                                    if (@$this->structure->constraint('SELECT', $keyArray[0], $keyArray[2], $b) !== false) {
                                        if ($this->structure->getType($keyArray[0],$keyArray[2]) !== 'array') {
                                            $REF = $b;
                                        } else {
                                            if (is_array($b)) {
                                                $REF = $b;
                                            } else {
                                                $REF = getConfigFile(explode("\n",$b));
                                            }
                                            if ($arrayKey !== "") {
                                                $REF = Hashtable::get($REF, strtolower($arrayKey));
                                                if (is_null($REF)) {
                                                    $REF = false;
                                                }
                                            }
                                        }
                                    } else {
                                        return false;
                                    }
                                break;
                                
                                case 4:
                                    if ($this->structure->constraint('SELECT', $keyArray[0], $keyArray[2], $b) !== false) {
                                        if ($this->structure->getType($keyArray[0],$keyArray[2]) !== 'array') {
                                            $REF[$i] = stripslashes($b);
                                        } else {
                                            if (is_array($b)) {
                                                $REF[$i] = $b;
                                            } else {
                                                $REF[$i] = getConfigFile(explode("\n",$b));
                                            }
                                        }
                                    }
                                break;

                                default:
                                    $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
                                    return false;
                                break;
                            } /* end switch */
                        } /* end if */
                    } /* end foreach */
                } /* end for */
                return $REF;
            } /* end else */
            return false;
        } else { /* end else if ($queryType == 1) */
            $this->handleError("DB ERROR: Syntax error. Invalid argument \$key: '${key}'.");
            return false;
        }
    }

    /**
     * getFilter
     *
     * This function is deprecated an will be removed in a future release
     *
     * @deprecated since Version 2.8
     *
     * @access  public
     * @param   string  $table (optional)
     * @return  string
     *
     * @ignore
     */
    function getFilter($table = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table);  // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table, "string");

        $table = strtolower($table);

        if ($table == "" || $table == "*") {
            if (!$this->last_selected_table) {
                return false;
            } else {
                $table = $this->last_selected_table;
            }
        }

        if (isset($this->filter[$table])) {
            return trim($this->filter[$table]);
        } else {
            return trim($this->filter["DEFAULT"]);
        }
    }

    /**
     * setFilter
     *
     * This function is deprecated an will be removed in a future release
     *
     * @deprecated since Version 2.8
     *
     * @access  public
     * @param   string  $table
     * @param   string  $filter
     *
     * @ignore
     */
    function setFilter($table, $filter)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table);  // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_string($filter); // wrong argument type for argument 2, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table,  "string");
        /* argument 2 */ settype($filter, "string");

        /* lower-case input */
        $table  = strtolower($table);
        $filter = strtolower($filter);

        if (! preg_match("/;/", $filter) && is_int(strpos($filter, ';'))) {
            $filter = preg_replace("/;.*/", "", $filter);
        }

        if ($table == "" || $table == "*") {
            if (!$this->last_selected_table) {
                return false;
            } else {
                $table = $this->last_selected_table;
            }
        } else {
            $this->last_selected_table = $table;
        }

        $this->filter[$table] = $filter;
    }

    /**
     * get the most recently queried table
     *
     * @access  public
     * @return  string
     */
    function getTable()
    {
        assert('is_string($this->last_selected_table);');
        return $this->last_selected_table;
    }

    /**
     * get the number of entries inside a table
     *
     * @access  public
     * @param   string  $table   (optional)
     * @param   string  $search  (optional)
     * @return  int
     */
    function length($table = "", $search = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table);  // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_string($search); // wrong argument type for argument 2, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table,  "string");
        /* argument 2 */ settype($search, "string");

        $table = strtolower($table);

        if (!$this->_check_available()) {
            return false;
        }

        if ($table == "" || $table == "*") {
            if (empty($this->last_selected_table)) {
                return false;
            } else {
                $table = $this->last_selected_table;
            }
        }

        if (!empty($search)) {
            $search = $this->_computeSearchString($table, $search, true);
            if ($search === false) {
                return false;
            }    
        }

        if (!$this->exists($table)) {
            return 0;
        }

        $result = $this->database->query("SELECT * FROM ".$this->quoteId($table). ( (!empty($search)) ? " WHERE ".$search : "" ) );

        if ($this->isError($result)) {
            return false;
        } else {
            return $result->numRows();
        }
    }

    /**
     * check wether a certain table has no entries
     *
     * Note: if no table is provided, the most recently used
     * table will be tested instead.
     *
     * @access  public
     * @param   string   $table  (optional)
     * @return  boolean
     */
    function isEmpty($table = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table, "string");

        $table = strtolower($table);

        if ($table == "" || $table == "*") {
            if (!$this->last_selected_table) {
                return false;
            } else {
                $table = $this->last_selected_table;
            }
        }

        return ($this->length($table) == 0);
    }

    /**
     * Check wether a certain key exists
     *
     * Returns bool(true), if the adress $key (table, row, column) is defined, and otherwise bool(false).
     * If no argument is provided, the function returns bool(true) if a database connection exists and bool(false) if not.
     *
     * @uses $bufferedDbStream->exists('table.5')
     *
     * @access  public
     * @param   string  $key  adress to check
     * @return  bool
     */
    function exists($key = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($key); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($key, "string");

        $key = preg_replace("/\.(\*)?$/", "", $key);
        $key = strtolower($key);

        if ($key == "" || $this->exists == YANA_DATABASE_FAILURE) {
            if ($this->exists == YANA_DATABASE_FAILURE) {
                return false;
            } else {
                return $this->exists;
            }
        } else {
            $key = strtolower($key);
            $key = explode('.', $key);
            /* int */ $result = 0;
            if (count($key) == 1) {
                return $this->structure->isTable($key[0]);
            } else if (count($key) < 0) {
                return false;
            } else if (count($key) == 2) {
                $result = $this->database->query("SELECT 1 FROM ".$this->quoteId($key[0])." WHERE ".$this->structure->getPrimaryKey($key[0])." = ".$this->quote($key[1]));
            } else if (count($key) == 3) {
                if ($key[1] === '*') {
                    $result = $this->database->query("SELECT 1 FROM ".$this->quoteId($key[0])." WHERE ".$key[2]." IS NOT NULL");
                } else {
                    $result = $this->database->query("SELECT 1 FROM ".$this->quoteId($key[0])." WHERE ".$this->structure->getPrimaryKey($key[0])." = ".$this->quote($key[1])." AND ".$key[2]." IS NOT NULL");
                }
            } else {
                /* syntax error */
                return false;
            }

            if ($this->isError($result)) {
                return false;
            } else {
                return ( $result->numRows() > 0 );
            }
        }
    }

    /**
     * check wether the current database is readonly
     *
     * @uses $bufferedDbStream->isWriteable()
     *
     * @access  public
     * @return  bool
     */
    function isWriteable()
    {
        if ($this->exists() && !$this->structure->isReadonly()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * resolve search clause
     *
     * @todo make this function accept other comparison operators like '<' and '>'
     *
     * @access  private
     * @param   string  $table
     * @param   string  $search
     * @param   bool    $is_table_scan   if scanning a whole table you may search for primary keys - otherwise not
     * @return  string
     * @ignore
     */
    function _computeSearchString($table, $search, $is_table_scan = false)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('preg_match("/^\w+$/s",$table); // invalid table name, may only contain alpha-numeric characters and can not be empty');
        /* argument 2 */ assert('is_string($search);            // wrong argument type for argument 2, string expected');
        /* argument 3 */ assert('is_bool($is_table_scan);       // wrong argument type for argument 3, boolean expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table,         'string');
        /* argument 2 */ settype($search,        'string');
        /* argument 3 */ settype($is_table_scan, 'bool');

        $table = strtolower($table);

        if (!preg_match("/^((, ?)?([\w\d-_\.])+=([\w\d-_\.\@ \&;])+)*$/i", $search)) {
            $this->handleError("SQL ERROR: Invalid where clause. The search string you provided contains syntax errors. Make sure it does only contain alpha-numeric characters and matches the following syntax: fieldname1=value1,fieldname2=value2");
            return false;
        }

        $search = preg_split("/, ?/", $search);
        settype($search, "array");
        foreach ($search as $a => $b) 
        {
            $search[$a] = explode("=", $b);
        }

        $result = "";
           
        if (count($search) > 0) {
             foreach ($search as $a => $b) 
             {
                 if (is_array($b) && count($b) == 2 && $b[0] && isset($b[1])) {

                     $b[0] = strtolower($b[0]);

                     /* catch exceptions */
                     if (!$is_table_scan && $this->structure->isStrict() && $this->structure->isPrimaryKey($table,$b[0])) {
                         $this->handleError("SQL ERROR: Invalid SQL statement. You are trying to search for a primary key. This is not allowed, since it might cause results wether to be ambigious or empty. Try setting 'USE_STRICT' to 'false' if you want to turn this message off and continue without checking.");
                         return false;
                     }
                     if ($this->structure->isStrict() && !$this->structure->isColumn($table,$b[0]) && !$this->structure->isForeignKey($table,$b[0])) {
                         $this->handleError("SQL ERROR: Field '".$b[0]."' does not exist in table '".$table."'. Perhaps your structure file is incomplete. Try setting 'USE_STRICT' to 'false' if you want to turn this message off and continue without checking.");
                         return false;
                     }
                     if ($this->structure->isForeignKey($table,$b[0]) || $this->structure->isPrimaryKey($table,$b[0])) {
                         $result .= ( ($result) ? "AND " : "" ).$b[0]." = ".$this->quote(addslashes($b[1]))." ";
                     } else {
                         $result .= ( ($result) ? "AND " : "" ).$b[0]." LIKE ".$this->quote("%".addslashes($b[1])."%")." ";
                     }
                 }
             }
        }

        return $result;
    }

    /**
     * import SQL from a file
     *
     * @uses $bufferedDbStream->importSQL('some_file.sql')
     *
     * @access  public
     * @return  bool
     * @name    buffereddbstream::importsql()
     * @param   string  $sqlFile
     */
    function importSQL($sqlFile)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($sqlFile); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($sqlFile, "string");

        if ($this->_check_writeable() === true) {
            if ($this->structure->isReadonly() || (!empty($sqlFile) && !is_readable($sqlFile))) {
                return false;
            } else if (!empty($sqlFile)) {
                $raw_data = @fread(@fopen($sqlFile, 'r'), @filesize($sqlFile));
                /* Entferne Kommentare und leere Zeilen */
                $raw_data = preg_replace("/\s*\#[^\n]*/i", "", $raw_data);
                $raw_data = preg_replace("/\s*\-\-[^\n]*/i", "", $raw_data);
                $raw_data = preg_replace("/;\s*\n\s*/i", "[NEXT_COMMAND]", $raw_data);
                $raw_data = preg_replace("/\s/", " ", $raw_data);

                $this->queue = array_merge($this->queue, explode("[NEXT_COMMAND]", $raw_data));
                return $this->write();
            } else {
                return true;
            }
        } else {
            return false;
        }
    }

    /**
     * get CSV string from a table
     *
     * @uses $bufferedDbStream->toString('guestbook')
     *
     * @access public
     * @param string $table (optional)
     * @return string
     */
    function toString($table = "")
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($table, "string");

        /* lower-case input */
        $table = strtolower($table);

        if ($this->exists === YANA_DATABASE_FAILURE || $this->exists === YANA_DATABASE_NOT_AVAILABLE) {
            return "No Database Connection: Database structure file not available or not loaded";
        }

        if ($table == "" || $table == "*") {
            if (empty($this->last_selected_table)) {
                return false;
            } else {
                $table = $this->last_selected_table;
            }
        }

        $text = "";

        if (!$this->exists($table)) {
            $text = "The selected Table ${table} does not exist.";
        } else {

            $a = $this->get($table);
            if(is_array($a)) {
                foreach ($a as $k => $e)
                {
                    for ($i=0; $i<count($e);$i++)
                    {
                        $b = current($e);
                        settype($b, "string");
                        $text .= $b;
                        if (next($e)) {
                              $text .= ", ";
                        }
                    }
                    $text .= "\n";
                }
            }
        }

        return $text;
    }
    
    /**
     * export database structure to a file
     *
     * Note: if the specified file already exists it will get
     * overwritten.
     *
     * Structure files need to have the extension '.config' and are
     * to be stored at the directory 'config/db/*.config'.
     *
     * @uses $bufferedDbStream->exportStructure('some_file.config')
     *
     * @access public
     * @param string $filename
     * @return boolean
     */
    function exportStructure($filename)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($filename); // wrong argument type for argument 1, string expected');

        /* optimistic type casting */
        /* argument 1 */ settype($filename, "string");

        if (!preg_match("/\.config$/", $filename)) {
            $this->handleError("ERROR: unexpected file extension. The filename '${filename}' should end with '.config'.");
            return false;
        }
        $file = new smlfile($filename);
        if (!$file->exists()) {
            $file->create();
        }
        $file->read();
        if ($file->set($this->structure->getStructure()) && $file->write()) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * handleError
     *
     * @access  private
     * @param   mixed   $error
     * @param   mixed   $additional_info (optional)
     * @ignore
     */
    function handleError($error, $additional_info = "")
    {
        global $YANA;

        if ($this->isError($error)) {
            if (isset($YANA)) {
                $YANA->report( new log("$additional_info", $error->getMessage()) );
            } else {
                if (is_string($additional_info)) {
                    trigger_error($error->getMessage()." $additional_info", E_USER_WARNING);
                } else {
                    trigger_error($error->getMessage(), E_USER_WARNING);
                }
            }
        } else if (is_string($error)) {   
            if (isset($YANA)) {      
                $YANA->report( new log("$error$additional_info") );
            } else {
                if (is_string($additional_info)) {
                    trigger_error("$error $additional_info", E_USER_WARNING);
                } else {
                    trigger_error("$error", E_USER_WARNING);
                }
            }
        }
    }

    /**
     * buildStructure
     *
     * {@internal
     *
     * WARNING: be aware that this won't work for all DBMS since
     * the type and ammount of information provided might vary.
     * ALSO: be aware that this won't work with PEAR-DB prior
     * to version 1.6.0
     *
     * ... or in other words: avoid this function in any productive
     * environment.
     *
     * It is only there as a "last-resort" in case the structure file
     * is not there as well as for debugging, testing and reverse
     * engineering your own database to a structure file when writing
     * new plugins (and by doing so possibly making your work a bit easier).
     *
     * }}
     *
     * @access private
     * @ignore
     */
    function buildStructure()
    {
        if (!isset($this->structure)) {
            $this->structure  = new StructureFile($this->filename);
            $this->structure->init();
        }
        $tables = $this->database->getListOf('tables');
        if (!$this->isError($tables)) {
            foreach ($tables as $table)
            {
                $result = $this->database->query('SELECT * FROM '.$this->quoteId($table).' WHERE 1=0');
                if ($this->isError($result)) {
                    $this->handleError("ERROR: unable to read structure of table '${table}'.");
                } else {
                    $result = $this->database->tableInfo($result, DB_TABLEINFO_ORDER);
                    if (!is_array($result)) {
                        $this->handleError("ERROR: unable to read structure of table '${table}'.");
                    } else {
                        $this->structure->addStructure($table, $result);
                    }
                }
            }
        } else {
            $this->handleError("ERROR: unable to read structure of database.");
        }
    }

    /**
     * read
     *
     * @access private
     * @return boolean
     * @ignore
     */
    function read()
    {
        if ($this->exists != YANA_DATABASE_OK) {
            return false;
        } else {
            $this->structure  = new StructureFile($this->filename);
            return ($this->structure->init() !== false);
        }
    }
    
    /**
     * available
     *
     * @access private
     * @return boolean
     * @ignore
     */
    function available() {
        if ((empty($this->source) && $this->exists == YANA_DATABASE_OK && !$this->read()) || empty($this->database)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * getFilename
     *
     * @access  protected
     * @return  string
     * @ignore
     */
    function getFilename()
    {
        return $this->filename;
    }

    /**
     * _sqlValues
     *
     * @access  protected
     * @param   array    $value
     * @return  string
     * @ignore
     */
    function _sqlValues($value)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_array($value); // wrong argument type for argument 1, array expected');

        /* optimistic type casting */
        /* argument 1 */ settype($value, "array");

        $k_value = array_keys($value);
        $v_value = array_values($value);

        $newfunc = create_function('&$item,$key,&$obj', '$item = $obj->quote($item);');
        array_walk($v_value, $newfunc, $this);
        $result  = '('.join(',', $k_value).')';
        $result .= ' VALUES('.join(',',$v_value).')';

        assert('is_string($result) && !empty($result);');
        return $result;
    }

    /**
     * isError
     *
     * @access  private
     * @param   mixed   $result
     * @return  boolean
     * @ignore
     */
    function isError($result)
    {
        return DB::isError($result);
    }

    /**
     * quote
     *
     * @access  private
     * @param   mixed   $value
     * @return  string
     * @ignore
     */
    function quote($value)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_scalar($value); // wrong argument type for argument 1, scalar expected');

        /* optimistic type casting */
        if (!is_scalar($value)) {
            $this->handleError("Your SQL statement contains an unexpected non-scalar value: '".print_r($value,true)."'");
            /* argument 1 */ settype($value, "string");
        }
        return $this->database->quoteSmart($value);
    }

    /**
     * quoteId
     *
     * @access  private
     * @param   mixed   $value
     * @return  string
     * @ignore
     */
    function quoteId($value) 
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($value); // Wrong argument type for argument 1. String expected, found \''.gettype($value).'\' instead.');

        /* optimistic type casting */
        /* argument 1 */ settype($value, "string");

        return $this->database->quoteIdentifier($value);
    }

    /**
     * _check_available
     *
     * @access  private
     * @return  bool
     * @ignore
     */
    function _check_available()
    {
        if ($this->available() === false) {
            $this->handleError("DB ERROR: Datebase is not available. In structure file '".$this->filename."'.");
            return false;
        } else {
            return true;
        }
    }

    /**
     * _check_writeable
     *
     * @access  private
     * @return  bool
     * @ignore
     */
    function _check_writeable()
    {
        if ($this->_check_available() === false) {
            return false;
        } else if (!$this->isWriteable()) {
            $this->handleError("DB ERROR: Datebase is not writeable. Check structure file '".$this->filename."' if attribute readonly is set to true.");
            return false;
        } else {
            return true;
        }
    }

    /**
     * build an update statement (without where clause)
     *
     * @access  private
     * @param   string  $table
     * @param   array   $values
     * @return  bool
     * @ignore
     *
     * @todo    test this with MSSQL
     */
    function _buildUpdate($table, $values)
    {
        /* pessimistic scanning */
        /* argument 1 */ assert('is_string($table); // wrong argument type for argument 1, string expected');
        /* argument 2 */ assert('is_array($values); // wrong argument type for argument 2, array expected');

        $sqlStmt = '';
        if (is_string($table) && !empty($table) && is_array($values)) {
            foreach ($values as $row => $value) {
                if (!empty($sqlStmt)) {
                    $sqlStmt .= ',';
                }
                $sqlStmt .= $row.'='.$this->quote($value);
            }
            $sqlStmt = 'UPDATE '.$this->quoteId(YANA_DATABASE_PREFIX.$table).' SET '.$sqlStmt. ' ';
            return $sqlStmt;
        } else {
            $this->handleError('SQL ERROR: Syntax error.');
            assert('empy($sqlStmt);');
            return $sqlStmt;
        }
    }

}

?>