<?php
/**
 * MySQL RDO_Storage implementation.
 *
 * $Horde: framework/RDO/RDO/Storage/mysql.php,v 1.8.2.1 2005/10/18 11:01:23 jan Exp $
 *
 * @package RDO
 */
class RDO_Storage_mysql extends RDO_Storage_sql {

    /**
     * Human-readable name for this Storage driver.
     *
     * @var string $name
     */
    public $name = 'MySQL';

    /**
     * PostgreSQL database resource.
     *
     * @var resource $db
     */
    private $db = null;

    /**
     * MySQL RDO_Storage constructor. Sets parameters and connects to
     * the database.
     *
     * @param array $params Connection and other parameters.
     */
    protected function __construct($params)
    {
        parent::__construct($params);

        $this->connect();
    }

    /**
     * Free any resources that are open.
     */
    public function __destruct()
    {
        if ($this->db) {
            mysql_close($this->db);
        }
    }

    public function create(RDO_Mapper $mapper, $fields)
    {
        $meta = $mapper->describe();

        $sql = 'INSERT INTO ' . $meta->container;
        $keys = array();
        $values = array();
        foreach ($fields as $field => $value) {
            $keys[] = $field;
            $values[] = "'" . $this->quote($meta->getField($field), $value) . "'";
        }
        $sql .= ' (' . implode(', ', $keys) . ') VALUES (' . implode(', ', $values) . ')';

        $this->execute($sql);
        return mysql_insert_id($this->db);
    }

    public function update(RDO_Mapper $mapper, $id, $fields)
    {
        return $this->execute($this->qbuilder->update($mapper, $id, $fields));
    }

    public function delete(RDO_Mapper $mapper, $criteria)
    {
    }

    public function find(RDO_Mapper $mapper, $mode, $criteria)
    {
        return new RDO_Results_mysql($this, $mapper, $mode, $criteria);
    }

    /**
     * Take a string, wrap it in single quotes, and escape any quotes
     * or other special characters in it.
     *
     * @param string $data The string to quote.
     *
     * @return string Quoted string wrapped in single quotes.
     */
    public function quoteString($data)
    {
        return "'" . mysql_real_escape_string($data, $this->db) . "'";
    }

    /**
     * Use for SELECT and anything that returns rows.
     */
    public function query($sql)
    {
        $result = mysql_query($sql, $this->db);
        if (!$result) {
            throw new RDO_Exception(mysql_error() . ' ' . $sql);
        }
        return $result;
    }

    /**
     */
    public function queryOne($sql)
    {
        $result = mysql_query($sql, $this->db);
        if (!$result) {
            throw new RDO_Exception(mysql_error());
        }
        return @mysql_result($result, 0, 0);
    }

    /**
     * Use for INSERT, UPDATE, DELETE. Returns number of affected
     * rows.
     */
    public function execute($sql)
    {
        $result = mysql_query($sql, $this->db);
        if (!$result) {
            throw new RDO_Exception(mysql_error());
        }
        return mysql_affected_rows($this->db);
    }

    /**
     */
    public function begin()
    {
    }

    /**
     */
    public function commit()
    {
        if ($this->transaction_opcount > 0) {
            if ($this->_changed_db) {
                if (!@mysql_select_db($this->_changed_db, $this->db)) {
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
                }
            }
            $result = @mysql_query('COMMIT', $this->db);
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->db);
            $this->transaction_opcount = 0;
            if (!$result) {
                return $this->mysqlRaiseError();
            }
        }
        return DB_OK;
    }

    /**
     */
    public function rollback()
    {
        if ($this->transaction_opcount > 0) {
            if ($this->_changed_db) {
                if (!@mysql_select_db($this->_changed_db, $this->db)) {
                    return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
                }
            }
            $result = @mysql_query('ROLLBACK', $this->db);
            $result = @mysql_query('SET AUTOCOMMIT=1', $this->db);
            $this->transaction_opcount = 0;
            if (!$result) {
                return $this->mysqlRaiseError();
            }
        }
        return DB_OK;
    }

    /**
     */
    public function describe(RDO_Mapper $mapper, RDO_MetaData $metadata)
    {
        $r = $this->query('SHOW COLUMNS FROM ' . $metadata->container);
        if (!$r) {
            throw new RDO_Exception(mysql_error());
        }

        while ($f = mysql_fetch_assoc($r)) {
            $metadata->addField($f['Field'], $f);
            if ($f['Key'] == 'PRI') {
                $metadata->key = $f['Field'];
            }
        }
    }

    /**
     */
    public function free($result)
    {
        return @mysql_free_result($result);
    }

    /**
     */
    private function setDb($database)
    {
        mysql_select_db($database, $this->db);
    }

    /**
     */
    private function connect()
    {
        $this->db = mysql_connect('localhost', $this->params['username'], $this->params['password']);

        if (!empty($this->params['database'])) {
            $this->setDb($this->params['database']);
        }
    }

}

/**
 * @package RDO
 */
class RDO_Results_mysql extends RDO_Results {

    /**
     */
    public function count()
    {
        if (is_null($this->result)) {
            $this->rewind();
        }
        return mysql_num_rows($this->result);
    }

    /**
     * Implementation of the rewind() method for MySQL result sets.
     */
    public function rewind()
    {
        // Rewind only if never queried before or query was
        // successful.
        if (is_null($this->result) || is_resource($this->result)) {
            if ($this->result) {
                $this->storage->free($this->result);
            }
            $this->current = null;
            $this->index = null;
            $this->eof = true;

            $this->result = $this->storage->findNow($this->mapper, $this->mode, $this->criteria);
            if (!$this->result) {
                throw new RDO_Exception(mysql_error());
            }
            $this->next();
        }
    }

    /**
     * Implementation of the next() method for MySQL result sets.
     *
     * @return mixed The next row, or null if no more rows.
     */
    public function next()
    {
        if (is_null($this->result)) {
            $this->rewind();
        }

        if ($this->result) {
            $this->current = mysql_fetch_assoc($this->result);
            if (!$this->current) {
                $this->eof = true;
            } else {
                $this->eof = false;

                if (is_null($this->index)) {
                    $this->index = 0;
                } else {
                    ++$this->index;
                }
            }

            $this->current = $this->mapper->map($this->current);
        }

        return $this->current;
    }

}
