diff --git a/Doctrine/Access.php b/Doctrine/Access.php
new file mode 100644
index 000000000..bf59f423c
--- /dev/null
+++ b/Doctrine/Access.php
@@ -0,0 +1,82 @@
+<?php
+/**
+ * Doctrine_Access
+ *
+ * the purpose of Doctrine_Access is to provice array access
+ * and property overload interface for subclasses
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+abstract class Doctrine_Access implements ArrayAccess {
+    /**
+     * setArray
+     * @param array $array          an array of key => value pairs
+     */
+    public function setArray(array $array) {
+        foreach($array as $k=>$v):
+            $this->set($k,$v);
+        endforeach;
+    }
+    /**
+     * __set -- an alias of set()
+     * @see set, offsetSet
+     * @param $name
+     * @param $value
+     */
+    public function __set($name,$value) {
+        $this->set($name,$value);
+    }
+    /**
+     * __get -- an alias of get()
+     * @see get,  offsetGet
+     * @param mixed $name
+     * @return mixed
+     */
+    public function __get($name) {
+        return $this->get($name);
+    }
+    /**
+     * @param mixed $offset
+     * @return boolean -- whether or not the data has a field $offset
+     */
+    public function offsetExists($offset) {
+        return (bool) isset($this->data[$offset]);
+    }
+    /**
+     * offsetGet -- an alias of get()
+     * @see get,  __get
+     * @param mixed $offset
+     * @return mixed
+     */
+    public function offsetGet($offset) {
+        return $this->get($offset);
+    }
+    /**
+     * sets $offset to $value
+     * @see set,  __set
+     * @param mixed $offset
+     * @param mixed $value
+     * @return void
+     */
+    public function offsetSet($offset, $value) {
+        if( ! isset($offset)) {
+            $this->add($value);
+        } else
+            $this->set($offset,$value);
+    }
+    /**
+     * unset a given offset
+     * @see set, offsetSet, __set
+     * @param mixed $offset
+     */
+    public function offsetUnset($offset) {
+        if($this instanceof Doctrine_Collection) {
+            return $this->remove($offset);
+        } else {
+            $this->set($offset,null);
+        }
+    }
+}
+?>
diff --git a/Doctrine/Association.php b/Doctrine/Association.php
new file mode 100644
index 000000000..567535a40
--- /dev/null
+++ b/Doctrine/Association.php
@@ -0,0 +1,38 @@
+<?php
+require_once("Relation.php");
+/**
+ * Doctrine_Association    this class takes care of association mapping
+ *                         (= many-to-many relationships, where the relationship is handled with an additional relational table
+ *                         which holds 2 foreign keys)
+ *
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+class Doctrine_Association extends Doctrine_Relation {
+    /**
+     * @var Doctrine_Table $associationTable
+     */
+    private $associationTable;
+    /**
+     * the constructor
+     * @param Doctrine_Table $table                 foreign factory object
+     * @param Doctrine_Table $associationTable      factory which handles the association
+     * @param string $local                         local field name
+     * @param string $foreign                       foreign field name
+     * @param integer $type                         type of relation
+     * @see Doctrine_Table constants
+     */
+    public function __construct(Doctrine_Table $table, Doctrine_Table $associationTable, $local, $foreign, $type) {
+        parent::__construct($table, $local, $foreign, $type);
+        $this->associationTable = $associationTable;
+    }
+    /**
+     * @return Doctrine_Table
+     */
+    public function getAssociationFactory() {
+        return $this->associationTable;
+    }
+}
+?>
diff --git a/Doctrine/Cache.php b/Doctrine/Cache.php
new file mode 100644
index 000000000..d11d8626c
--- /dev/null
+++ b/Doctrine/Cache.php
@@ -0,0 +1,71 @@
+<?php
+interface iDoctrine_Cache {
+    public function store(Doctrine_Record $record);
+    public function clean();
+    public function delete($id);
+    public function fetch($id);
+    public function exists($id);
+}
+class Doctrine_Cache implements iDoctrine_Cache {
+    /**
+     * implemented by child classes
+     * @param Doctrine_Record $record
+     * @return boolean
+     */
+    public function store(Doctrine_Record $record) {
+        return false;
+    }
+    /**
+     * implemented by child classes
+     * @return boolean
+     */
+    public function clean() {
+        return false;
+    }
+    /**
+     * implemented by child classes
+     * @return boolean
+     */
+    public function delete($id) {
+        return false;
+    }
+    /**
+     * implemented by child classes
+     * @throws InvalidKeyException
+     * @return Doctrine_Record                      found Data Access Object
+     */
+    public function fetch($id) {
+        throw new InvalidKeyException();
+    }
+    /**
+     * implemented by child classes
+     * @param array $keys
+     * @return boolean
+     */
+    public function fetchMultiple($keys) {
+        return false;                                     	
+    }
+    /**
+     * implemented by child classes
+     * @param integer $id
+     * @return boolean
+     */
+    public function exists($id) {
+        return false;
+    }
+    /**
+     * implemented by child classes
+     */
+    public function deleteMultiple($keys) {
+        return 0;
+    }
+    /**
+     * implemented by child classes
+     * @return integer
+     */
+    public function deleteAll() {
+        return 0;
+    }
+
+}
+?>
diff --git a/Doctrine/Collection.php b/Doctrine/Collection.php
new file mode 100644
index 000000000..838d35a5f
--- /dev/null
+++ b/Doctrine/Collection.php
@@ -0,0 +1,446 @@
+<?php
+require_once("Access.php");
+/**
+ * Doctrine_Collection
+ * Collection of Doctrine_Record objects.
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+class Doctrine_Collection extends Doctrine_Access implements Countable, IteratorAggregate {
+    /**
+     * @var array $data                     an array containing the data access objects of this collection
+     */
+    protected $data = array();
+    /**
+     * @var Doctrine_Table $table           each collection has only records of specified table
+     */
+    protected $table;
+    /**
+     * @var Doctrine_Record $reference      collection can belong to a record
+     */
+    protected $reference;
+    /**
+     * @var string $reference_field         the reference field of the collection
+     */
+    protected $reference_field;
+    /**
+     * @var Doctrine_Relation               the record this collection is related to, if any
+     */
+    protected $relation;
+    /**
+     * @var boolean $expandable             whether or not this collection has been expanded
+     */
+    protected $expandable = true;   
+    /**
+     * @var array $expanded
+     */
+    protected $expanded = array();
+    /**
+     * @var mixed $generator
+     */
+    protected $generator;
+    /**
+     * @var Doctrine_Null $null             used for extremely fast SQL null value testing
+     */
+    protected static $null;
+
+    /**
+     * constructor
+     */
+    public function __construct(Doctrine_Table $table) {
+        $this->table = $table;
+        
+        $name = $table->getAttribute(Doctrine::ATTR_COLL_KEY);
+        if($name !== null) {
+            $this->generator = new Doctrine_IndexGenerator($name);
+        }
+    }
+    /**
+     * initNullObject
+     */
+    public static function initNullObject(Doctrine_Null $null) {
+        self::$null = $null;
+    }
+    /**
+     * @return object Doctrine_Table
+     */
+    public function getTable() {
+        return $this->table;
+    }
+    /**
+     * whether or not an offset batch has been expanded
+     * @return boolean
+     */
+    public function isExpanded($offset) {
+        return isset($this->expanded[$offset]);
+    }
+    /**
+     * whether or not this collection is expandable
+     * @return boolean
+     */
+    public function isExpandable() {
+        return $this->expandable;
+    }
+    /**
+     * @param Doctrine_IndexGenerator $generator
+     * @return void
+     */
+    public function setGenerator($generator) {
+        $this->generator = $generator;
+    }
+    /**
+     * @return Doctrine_IndexGenerator
+     */
+    public function getGenerator() {
+        return $this->generator;
+    }
+    /** 
+     * @return array
+     */
+    public function getData() {
+        return $this->data;
+    }
+    /**
+     * @param array $data
+     */
+    public function addData(array $data) {
+        $this->data[] = $data;
+    }
+    /**
+     * @return mixed
+     */
+    public function getLast() {
+        return end($this->data);
+    }
+    /**
+     * @return void
+     */
+    public function setReference(Doctrine_Record $record,Doctrine_Relation $relation) {
+        $this->reference       = $record;
+        $this->relation        = $relation;
+
+        if($relation instanceof Doctrine_ForeignKey ||
+           $relation instanceof Doctrine_LocalKey) {
+           
+            $this->reference_field = $relation->getForeign();
+
+            $value = $record->get($relation->getLocal());
+
+            foreach($this->getNormalIterator() as $record) {
+                if($value !== null) {
+                    $record->rawSet($this->reference_field, $value);
+                } else {
+                    $record->rawSet($this->reference_field, $this->reference);
+                }
+            }
+        }
+    }
+    /**
+     * @return mixed
+     */
+    public function getReference() {
+        return $this->reference;
+    }
+    /**
+     * @return boolean
+     */
+    public function expand($key) {
+        $where  = array();
+        $params = array();
+        $limit  = null;
+        $offset = null;
+
+        switch(get_class($this)):
+            case "Doctrine_Collection_Offset":
+                $limit  = $this->getLimit();
+                $offset = (floor($key / $limit) * $limit);
+
+                if( ! $this->expandable && isset($this->expanded[$offset]))
+                    return false;
+                    
+                $fields = implode(", ",$this->table->getColumnNames());
+            break;
+            default: 
+                if( ! $this->expandable)
+                    return false;
+
+                if( ! isset($this->reference))
+                    return false;
+
+                $id = $this->reference->getID();
+
+                if(empty($id))
+                    return false;
+
+                switch(get_class($this)):
+                    case "Doctrine_Collection_Immediate":
+                        $fields = implode(", ",$this->table->getColumnNames());
+                    break;
+                    default:
+                        $fields = implode(", ",$this->table->getPrimaryKeys());
+                endswitch;
+
+
+        endswitch;
+
+        if(isset($this->relation)) {
+            if($this->relation instanceof Doctrine_ForeignKey) {
+                $params = array($this->reference->getID());
+                $where[] = $this->reference_field." = ?";
+
+                if( ! isset($offset)) {
+                    $ids = $this->getPrimaryKeys();
+    
+                    if( ! empty($ids)) {
+                        $where[] = $this->table->getIdentifier()." NOT IN (".substr(str_repeat("?, ",count($ids)),0,-2).")";
+                        $params  = array_merge($params,$ids);
+                    }
+
+                    $this->expandable = false;
+                }
+
+
+            } elseif($this->relation instanceof Doctrine_Association) {
+    
+                $asf     = $fk->getAssociationFactory();
+                $query   = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local."=".$this->getID();
+    
+                $table = $fk->getTable();
+                $graph   = new Doctrine_DQL_Parser($table->getSession());
+    
+                $q       = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$table->getIdentifier()." IN ($query)";
+    
+            }
+        }
+
+        $query  = "SELECT ".$fields." FROM ".$this->table->getTableName();
+        
+        // apply column aggregation inheritance
+        foreach($this->table->getInheritanceMap() as $k => $v) {
+            $where[]  = $k." = ?";
+            $params[] = $v;
+        }
+        if( ! empty($where)) {
+            $query .= " WHERE ".implode(" AND ",$where);
+        }
+
+        $params = array_merge($params, array_values($this->table->getInheritanceMap()));
+        
+        $coll   = $this->table->execute($query, $params, $limit, $offset);
+
+        if( ! isset($offset)) {
+            foreach($coll as $record) {
+                if(isset($this->reference_field))
+                    $record->rawSet($this->reference_field,$this->reference);
+
+                $this->reference->addReference($record);
+            }
+        } else {
+            $i = $offset;
+
+            foreach($coll as $record) {
+                if(isset($this->reference)) {
+                    $this->reference->addReference($record,$i);
+                } else
+                    $this->data[$i] = $record;
+                    
+                $i++;
+            }
+
+            $this->expanded[$offset] = true;
+
+            // check if the fetched collection's record count is smaller
+            // than the query limit, if so this collection has been expanded to its max size
+
+            if(count($coll) < $limit) {
+                $this->expandable = false;
+            }
+        }
+
+        return $coll;
+    }
+    /**
+     * @return boolean
+     */
+    public function remove($key) {
+        if( ! isset($this->data[$key]))
+            throw new InvalidKeyException();
+
+        $removed = $this->data[$key];
+        
+        unset($this->data[$key]);
+        return $removed;
+    }
+    /**
+     * @param mixed $key
+     * @return boolean
+     */
+    public function contains($key) {
+        return isset($this->data[$key]);
+    }
+    /**
+     * @param mixed $key
+     * @return object Doctrine_Record           return a specified record
+     */
+    public function get($key) {
+        if( ! isset($this->data[$key])) {
+            $this->expand($key);
+
+            if( ! isset($this->data[$key]))
+                $this->data[$key] = $this->table->create();
+
+            if(isset($this->reference_field)) {
+                $value = $this->reference->get($this->relation->getLocal());
+
+                if($value !== null) {
+                    $this->data[$key]->rawSet($this->reference_field, $value);
+                } else {
+                    $this->data[$key]->rawSet($this->reference_field, $this->reference);
+                }
+            }
+        }
+
+        return $this->data[$key];
+    }
+
+    /**
+     * @return array                an array containing all primary keys
+     */
+    public function getPrimaryKeys() {
+        $list = array();
+        $name = $this->table->getIdentifier();
+
+        foreach($this->data as $record):
+            if(is_array($record) && isset($record[$name])) {
+                $list[] = $record[$name];
+            } else {
+                $list[] = $record->getID();
+            }
+        endforeach;
+        return $list;
+    }
+    /**
+     * returns all keys
+     * @return array
+     */
+    public function getKeys() {
+        return array_keys($this->data);
+    }
+    /**
+     * count
+     * this class implements interface countable
+     * @return integer                              number of records in this collection
+     */
+    public function count() {
+        return count($this->data);
+    }
+    /**
+     * set 
+     * @param integer $key
+     * @param Doctrine_Record $record
+     * @return void
+     */
+    public function set($key,Doctrine_Record $record) {
+        if(isset($this->reference_field))
+            $record->rawSet($this->reference_field,$this->reference);
+
+        $this->data[$key] = $record;
+    }
+
+    /**
+     * adds a record to collection
+     * @param Doctrine_Record $record              record to be added
+     * @param string $key                          optional key for the record
+     * @return boolean
+     */
+    public function add(Doctrine_Record $record,$key = null) {
+        if(isset($this->reference_field))
+            $record->rawSet($this->reference_field,$this->reference);
+
+        if(isset($key)) {
+            if(isset($this->data[$key]))
+                return false;
+
+            $this->data[$key] = $record;
+            return true;
+        }
+
+        if(isset($this->generator)) {
+            $key = $this->generator->getIndex($record);
+            $this->data[$key] = $record;
+        } else
+            $this->data[] = $record;
+
+        return true;
+    }
+    /**
+     * @param Doctrine_Query $query
+     * @param integer $key              
+     */
+    public function populate(Doctrine_Query $query) {
+        $name = $this->table->getComponentName();
+
+        if($this instanceof Doctrine_Collection_Immediate ||
+           $this instanceof Doctrine_Collection_Offset) {
+
+            $data = $query->getData($name);
+            if(is_array($data)) {
+                foreach($data as $k=>$v):
+                    $this->table->setData($v);
+                    $this->add($this->table->getRecord());
+                endforeach;
+            }
+        } elseif($this instanceof Doctrine_Collection_Batch) {
+            $this->data = $query->getData($name);
+
+            if(isset($this->generator)) {
+                foreach($this->data as $k => $v) {
+                    $record = $this->get($k);
+                    $i = $this->generator->getIndex($record);
+                    $this->data[$i] = $record;
+                    unset($this->data[$k]);
+                }
+            }                                                    	
+        }
+    }
+    /**
+     * @return Doctrine_Iterator_Normal
+     */
+    public function getNormalIterator() {
+        return new Doctrine_Iterator_Normal($this);
+    }
+    /**
+     * save
+     * saves all records
+     */
+    public function save() {
+        $this->table->getSession()->saveCollection($this);
+    }
+    /**
+     * single shot delete
+     * deletes all records from this collection
+     * uses only one database query to perform this operation
+     * @return boolean
+     */
+    public function delete() {
+        $ids = $this->table->getSession()->deleteCollection($this);
+        $this->data = array();
+    }
+    /**
+     * getIterator
+     * @return object ArrayIterator
+     */
+    public function getIterator() {
+        $data = $this->data;
+        return new ArrayIterator($data);
+    }
+    /**
+     * returns a string representation of this object
+     */
+    public function __toString() {
+        return Doctrine_Lib::getCollectionAsString($this);
+    }
+}
+?>
diff --git a/Doctrine/Configurable.php b/Doctrine/Configurable.php
new file mode 100644
index 000000000..7cd5a393c
--- /dev/null
+++ b/Doctrine/Configurable.php
@@ -0,0 +1,174 @@
+<?php
+/**
+ * Doctrine_Configurable
+ * the base for Doctrine_Table, Doctrine_Manager and Doctrine_Session
+ *
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+abstract class Doctrine_Configurable {
+
+    /**
+     * @var array $attributes               an array of containing all attributes
+     */
+    private $attributes = array();
+    /**
+     * @var $parent                         the parents of this component
+     */
+    private $parent;
+    /**
+     * sets a given attribute
+     *
+     * @throws Doctrine_Exception           if the value is invalid
+     * @param integer $attribute
+     * @param mixed $value
+     * @return void
+     */
+    final public function setAttribute($attribute,$value) {
+        switch($attribute):
+            case Doctrine::ATTR_BATCH_SIZE:
+                if($value < 0)
+                    throw new Doctrine_Exception("Batch size should be greater than or equal to zero");
+            break;
+            case Doctrine::ATTR_CACHE_DIR:
+                if(substr(trim($value),0,6) == "%ROOT%") {
+                    $dir   = dirname(__FILE__);
+                    $value = $dir.substr($value,6);
+                }
+
+                if(! is_dir($value) && ! file_exists($value))
+                    mkdir($value,0777);
+            break;
+            case Doctrine::ATTR_CACHE_TTL:
+                if($value < 1)
+                    throw new Doctrine_Exception("Cache TimeToLive should be greater than or equal to 1");
+            break;
+            case Doctrine::ATTR_CACHE_SIZE:
+                if($value < 1)
+                    throw new Doctrine_Exception("Cache size should be greater than or equal to 1");
+            break;
+            case Doctrine::ATTR_CACHE_SLAM:
+                if($value < 0 || $value > 1) 
+                    throw new Doctrine_Exception("Cache slam defense should be a floating point number between 0 and 1");
+            break;
+            case Doctrine::ATTR_FETCHMODE:
+                 if($value < 0)
+                    throw new Doctrine_Exception("Unknown fetchmode. See Doctrine::FETCH_* constants.");
+            break;
+            case Doctrine::ATTR_LISTENER:
+                $this->setEventListener($value);
+            break;
+            case Doctrine::ATTR_PK_COLUMNS:
+                if( ! is_array($value)) 
+                    throw new Doctrine_Exception("The value of Doctrine::ATTR_PK_COLUMNS attribute must be an array");
+            break;
+            case Doctrine::ATTR_PK_TYPE:
+                if($value != Doctrine::INCREMENT_KEY && $value != Doctrine::UNIQUE_KEY)
+                    throw new Doctrine_Exception("The value of Doctrine::ATTR_PK_TYPE attribute must be either Doctrine::INCREMENT_KEY or Doctrine::UNIQUE_KEY");
+
+            break;
+            case Doctrine::ATTR_LOCKMODE:
+                if($this instanceof Doctrine_Session) {
+                    if($this->getState() != Doctrine_Session::STATE_OPEN)
+                        throw new Doctrine_Exception("Couldn't set lockmode. There are transactions open.");
+
+                } elseif($this instanceof Doctrine_Manager) {
+                    foreach($this as $session) {
+                        if($session->getState() != Doctrine_Session::STATE_OPEN)
+                            throw new Doctrine_Exception("Couldn't set lockmode. There are transactions open.");
+                    }
+                } else {
+                    throw new Doctrine_Exception("Lockmode attribute can only be set at the global or session level.");
+                }
+            break;
+            case Doctrine::ATTR_CREATE_TABLES:
+                $value = (bool) $value;
+            break;
+            case Doctrine::ATTR_COLL_LIMIT:
+                if($value < 1) {
+                    throw new Doctrine_Exception("Collection limit should be a value greater than or equal to 1.");
+                }
+            break;
+            case Doctrine::ATTR_COLL_KEY:
+                if( ! ($this instanceof Doctrine_Table)) 
+                    throw new Doctrine_Exception("This attribute can only be set at table level.");
+
+                if( ! $this->hasColumn($value)) 
+                    throw new Doctrine_Exception("Couldn't set collection key attribute. No such column '$value'");
+                    
+
+            break;
+            case Doctrine::ATTR_VLD:
+            
+            break;
+            case Doctrine::ATTR_CACHE:
+                if($value != Doctrine::CACHE_SQLITE && $value != Doctrine::CACHE_NONE)
+                    throw new Doctrine_Exception("Unknown cache container. See Doctrine::CACHE_* constants for availible containers.");
+            break;
+            default:
+                throw new Doctrine_Exception("Unknown attribute.");
+        endswitch;
+
+        $this->attributes[$attribute] = $value;
+
+    }
+    /**
+     * @param Doctrine_EventListener $listener
+     * @return void
+     */
+    final public function setEventListener(Doctrine_EventListener $listener) {
+        $i = Doctrine::ATTR_LISTENER;
+        $this->attributes[$i] = $listener;
+    }
+    /**
+     * returns the value of an attribute
+     *
+     * @param integer $attribute
+     * @return mixed
+     */
+    final public function getAttribute($attribute) {
+        $attribute = (int) $attribute;
+
+        if($attribute < 1 || $attribute > 16)
+            throw new InvalidKeyException();
+
+        if( ! isset($this->attributes[$attribute])) {
+            if(isset($this->parent))
+                return $this->parent->getAttribute($attribute);
+
+            return null;
+        }
+        return $this->attributes[$attribute];
+    }
+    /**
+     * getAttributes
+     * returns all attributes as an array
+     *
+     * @return array
+     */
+    final public function getAttributes() {
+        return $this->attributes;
+    }
+    /**
+     * sets a parent for this configurable component
+     * the parent must be configurable component itself
+     *
+     * @param Doctrine_Configurable $component
+     * @return void
+     */
+    final public function setParent(Doctrine_Configurable $component) {
+        $this->parent = $component;
+    }
+    /**
+     * getParent
+     * returns the parent of this component
+     *
+     * @return Doctrine_Configurable
+     */
+    final public function getParent() {
+        return $this->parent;
+    }
+}
+?>
diff --git a/Doctrine/DB.php b/Doctrine/DB.php
new file mode 100644
index 000000000..ab82c3876
--- /dev/null
+++ b/Doctrine/DB.php
@@ -0,0 +1,140 @@
+<?php
+class Doctrine_DB extends PDO implements Countable, IteratorAggregate {
+    /**
+     * default DSN
+     */
+    const DSN = "mysql://root:dc34@localhost/test";
+    /**
+     * executed queries
+     */
+    private $queries    = array();
+    /**
+     * execution times of the executed queries
+     */
+    private $exectimes  = array();
+
+    /**
+     * constructor
+     * @param string $dsn
+     * @param string $username
+     * @param string $password
+     */
+    public function __construct($dsn,$username,$password) {
+        parent::__construct($dsn,$username,$password);
+        $this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+        $this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array("Doctrine_DBStatement",array($this)));
+    }
+    
+    
+    public static function getConn($dsn,$username = null, $password = null) {
+        static $instance;
+        
+        if( ! isset($instance)) {
+            $instance = new Doctrine_DB($dsn,$username,$password);
+        }
+        return $instance;
+    }
+
+    /**
+     * @param string $dsn           PEAR::DB like DSN
+     * format:                      schema://user:password@address/dbname
+     */
+    public static function getConnection($dsn = null) {
+        static $instance = array();
+        $md5 = md5($dsn);
+
+        if( ! isset($instance[$md5])) {
+            if( ! isset($dsn)) {
+                $a = parse_url(self::DSN);
+            } else {
+                $a = parse_url($dsn);
+            }
+            $e = array();
+
+            $e[0] = $a["scheme"].":host=".$a["host"].";dbname=".substr($a["path"],1);
+            $e[1] = $a["user"];
+            $e[2] = $a["pass"];
+
+            $instance[$md5] = new Doctrine_DB($e[0],$e[1],$e[2]);
+        }
+        return $instance[$md5];
+    }
+    /**
+     * @param string $query         query to be executed
+     */
+    public function query($query) {
+        try {
+            $this->queries[] = $query;
+            $time = microtime();
+
+            $stmt = parent::query($query);
+
+            $this->exectimes[] = (microtime() - $time);
+            return $stmt;
+        } catch(PDOException $e) {
+            throw $e;
+        }
+    }
+    /**
+     * @param string $query         query to be prepared
+     */
+    public function prepare($query) {
+        $this->queries[] = $query;
+        return parent::prepare($query);
+    }
+    /**
+     * @param string $time          exectime of the last executed query
+     * @return void
+     */
+    public function addExecTime($time) {
+        $this->exectimes[] = $time;
+    }
+    
+    public function getExecTimes() {
+        return $this->exectimes;
+    }
+    /**
+     * @return array                an array of executed queries
+     */
+    public function getQueries() {
+        return $this->queries;
+    }
+    /**
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->queries);
+    }
+    /**
+     * returns the number of executed queries
+     * @return integer
+     */
+    public function count() {
+        return count($this->queries);
+    }
+
+}
+class Doctrine_DBStatement extends PDOStatement {
+    /**
+     * @param Doctrine_DB $dbh        Doctrine Database Handler
+     */
+    private $dbh;
+    /**
+     * @param Doctrine_DB $dbh
+     */
+    private function __construct(Doctrine_DB $dbh) {
+        $this->dbh = $dbh;
+    }
+    /**
+     * @param array $params
+     */
+    public function execute(array $params = null) {
+        $time     = microtime();
+        $result   = parent::execute($params);
+
+        $exectime = (microtime() - $time);
+        $this->dbh->addExecTime($exectime);
+        return $result;
+    }
+}
+?>
diff --git a/Doctrine/DataDict.php b/Doctrine/DataDict.php
new file mode 100644
index 000000000..f7d5f773b
--- /dev/null
+++ b/Doctrine/DataDict.php
@@ -0,0 +1,101 @@
+<?php
+class Doctrine_DataDict {
+    private $dbh;
+    public function __construct(PDO $dbh) {
+        $manager = Doctrine_Manager::getInstance();
+        require_once($manager->getRoot()."/adodb-hack/adodb.inc.php");
+
+        $this->dbh  = $dbh;
+        $this->dict = NewDataDictionary($dbh);
+    }
+
+    public function metaColumns(Doctrine_Table $table) {
+        return $this->dict->metaColumns($table->getTableName());
+    }
+
+
+    public function createTable($tablename, $columns) {
+        foreach($columns as $name => $args) {
+            $r[] = $name." ".$this->getADOType($args[0],$args[1])." ".str_replace("|"," ",$args[2]);
+        }
+
+
+        $r = implode(", ",$r);
+        $a = $this->dict->createTableSQL($tablename,$r);
+
+        $return = true;
+        foreach($a as $sql) {
+            try {
+                $this->dbh->query($sql);
+            } catch(PDOException $e) {
+                if($this->dbh->getAttribute(PDO::ATTR_DRIVER_NAME) == "sqlite")
+                    throw $e;
+                $return = false;
+            }
+        }
+
+        return $return;
+    }
+    /**
+     * converts doctrine type to adodb type
+     *
+     * @param string $type              column type
+     * @param integer $length           column length
+     */
+    public function getADOType($type,$length) {
+        switch($type):
+            case "string":
+            case "s":
+                if($length < 255)
+                    return "C($length)";
+                elseif($length < 4000) 
+                    return "X";
+                else
+                    return "X2";
+            break;
+            case "mbstring":
+                if($length < 255) 
+                    return "C2($length)";
+                
+                return "X2";
+            case "clob":
+                return "XL";
+            break;
+            case "d":
+            case "date":
+                return "D";
+            break;
+            case "float":
+            case "f":
+            case "double":
+                return "F";
+            break;
+            case "timestamp":
+            case "t":
+                return "T";
+            break;
+            case "boolean":
+            case "bool":
+                return "L";
+            break;
+            case "integer":
+            case "int":
+            case "i":
+                if(empty($length))
+                    return "I8";
+                elseif($length < 4)
+                    return "I1";
+                elseif($length < 6)
+                    return "I2";
+                elseif($length < 10)
+                    return "I4";
+                elseif($length <= 20)
+                    return "I8";
+                else
+                    throw new Doctrine_Exception("Too long integer (max length is 20).");
+
+            break;
+        endswitch;
+    }
+}
+?>
diff --git a/Doctrine/Debugger.php b/Doctrine/Debugger.php
new file mode 100644
index 000000000..dc81c8c96
--- /dev/null
+++ b/Doctrine/Debugger.php
@@ -0,0 +1,151 @@
+<?php
+require_once("EventListener.php");
+
+class Doctrine_DebugMessage {
+    private $code;
+    private $object;
+    public function __construct($object, $code) {
+        $this->object = $object;
+        $this->code   = $code;
+    }
+    final public function getCode() {
+        return $this->code;
+    }
+    final public function getObject() {
+        return $this->object;
+    }
+}
+class Doctrine_Debugger extends Doctrine_EventListener {
+    const EVENT_LOAD            = 1;
+    const EVENT_PRELOAD         = 2;
+    const EVENT_SLEEP           = 3;
+    const EVENT_WAKEUP          = 4;
+    const EVENT_UPDATE          = 5;
+    const EVENT_PREUPDATE       = 6;
+    const EVENT_CREATE          = 7;
+    const EVENT_PRECREATE       = 8;
+
+    const EVENT_SAVE            = 9;
+    const EVENT_PRESAVE         = 10;
+    const EVENT_INSERT          = 11;
+    const EVENT_PREINSERT       = 12;
+    const EVENT_DELETE          = 13;
+    const EVENT_PREDELETE       = 14;
+    const EVENT_EVICT           = 15;
+    const EVENT_PREEVICT        = 16;
+    const EVENT_CLOSE           = 17;
+    const EVENT_PRECLOSE        = 18;
+
+    const EVENT_OPEN            = 19;
+    const EVENT_COMMIT          = 20;
+    const EVENT_PRECOMMIT       = 21;
+    const EVENT_ROLLBACK        = 22;
+    const EVENT_PREROLLBACK     = 23;
+    const EVENT_BEGIN           = 24;
+    const EVENT_PREBEGIN        = 25;
+    const EVENT_COLLDELETE      = 26;
+    const EVENT_PRECOLLDELETE   = 27;
+    private $debug;
+    
+    public function getMessages() {
+        return $this->debug;                              	
+    }
+
+
+    public function onLoad(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_LOAD);
+    }
+    public function onPreLoad(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRELOAD);
+    }
+
+    public function onSleep(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_SLEEP);
+    }
+
+    public function onWakeUp(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_WAKEUP);
+    }
+
+    public function onUpdate(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_UPDATE);
+    }
+    public function onPreUpdate(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREUPDATE);
+    }
+
+    public function onCreate(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_CREATE);
+    }
+    public function onPreCreate(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRECREATE);
+    }
+
+    public function onSave(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_SAVE);
+    }
+    public function onPreSave(Doctrine_Record $record) {
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PRESAVE);
+    }
+
+    public function onInsert(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_INSERT);
+    }
+    public function onPreInsert(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREINSERT);
+    }
+
+    public function onDelete(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_DELETE);
+    }
+    public function onPreDelete(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREDELETE);
+    }
+
+    public function onEvict(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_EVICT);
+    }
+    public function onPreEvict(Doctrine_Record $record) { 
+        $this->debug[] = new Doctrine_DebugMessage($record,self::EVENT_PREEVICT);
+    }
+
+    public function onClose(Doctrine_Session $session) { 
+         $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_CLOSE);
+    }
+    public function onPreClose(Doctrine_Session $session) { 
+         $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PRECLOSE);
+    }
+
+    public function onOpen(Doctrine_Session $session) { 
+         $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_OPEN);
+    }
+
+    public function onTransactionCommit(Doctrine_Session $session) { 
+         $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_COMMIT);
+    }
+    public function onPreTransactionCommit(Doctrine_Session $session) { 
+        $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PRECOMMIT);
+    }
+
+    public function onTransactionRollback(Doctrine_Session $session) {
+        $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_ROLLBACK);
+    }
+    public function onPreTransactionRollback(Doctrine_Session $session) {
+        $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PREROLLBACK);
+    }
+
+    public function onTransactionBegin(Doctrine_Session $session) {
+        $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_BEGIN);
+    }
+    public function onPreTransactionBegin(Doctrine_Session $session) {
+        $this->debug[] = new Doctrine_DebugMessage($session,self::EVENT_PREBEGIN);
+    }
+    
+    public function onCollectionDelete(Doctrine_Collection $collection) { 
+        $this->debug[] = new Doctrine_DebugMessage($collection,self::EVENT_COLLDELETE);
+    }
+    public function onPreCollectionDelete(Doctrine_Collection $collection) {
+        $this->debug[] = new Doctrine_DebugMessage($collection,self::EVENT_PRECOLLDELETE);
+    }
+}
+?>
diff --git a/Doctrine/EventListener.php b/Doctrine/EventListener.php
new file mode 100644
index 000000000..24bb1ecd1
--- /dev/null
+++ b/Doctrine/EventListener.php
@@ -0,0 +1,124 @@
+<?php
+/**
+ * interface for event listening, forces all classes that extend 
+ * Doctrine_EventListener to have the same method arguments as their parent
+ */
+interface iDoctrine_EventListener {
+
+    public function onLoad(Doctrine_Record $record);
+    public function onPreLoad(Doctrine_Record $record);
+
+    public function onUpdate(Doctrine_Record $record);
+    public function onPreUpdate(Doctrine_Record $record);
+
+    public function onCreate(Doctrine_Record $record);
+    public function onPreCreate(Doctrine_Record $record);
+
+    public function onSave(Doctrine_Record $record);
+    public function onPreSave(Doctrine_Record $record);
+
+    public function onInsert(Doctrine_Record $record);
+    public function onPreInsert(Doctrine_Record $record);
+
+    public function onDelete(Doctrine_Record $record);
+    public function onPreDelete(Doctrine_Record $record);
+
+    public function onEvict(Doctrine_Record $record);
+    public function onPreEvict(Doctrine_Record $record);
+    
+    public function onSaveCascade(Doctrine_Record $record);
+    public function onPreSaveCascade(Doctrine_Record $record);
+    
+    public function onDeleteCascade(Doctrine_Record $record);
+    public function onPreDeleteCascade(Doctrine_Record $record);
+
+    public function onSleep(Doctrine_Record $record);
+    
+    public function onWakeUp(Doctrine_Record $record);
+    
+    public function onClose(Doctrine_Session $session);
+    public function onPreClose(Doctrine_Session $session);
+    
+    public function onOpen(Doctrine_Session $session);
+
+    public function onTransactionCommit(Doctrine_Session $session);
+    public function onPreTransactionCommit(Doctrine_Session $session);
+
+    public function onTransactionRollback(Doctrine_Session $session);
+    public function onPreTransactionRollback(Doctrine_Session $session);
+
+    public function onTransactionBegin(Doctrine_Session $session);
+    public function onPreTransactionBegin(Doctrine_Session $session);
+    
+    public function onCollectionDelete(Doctrine_Collection $collection);
+    public function onPreCollectionDelete(Doctrine_Collection $collection);
+}
+
+/**
+ * Doctrine_EventListener     all event listeners extend this base class
+ *                      the empty methods allow child classes to only implement the methods they need to implement
+ *
+ *
+ * @author      Konsta Vesterinen
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * @version     1.0 alpha
+ *
+ */
+abstract class Doctrine_EventListener implements iDoctrine_EventListener {
+
+    public function onLoad(Doctrine_Record $record) { }
+    public function onPreLoad(Doctrine_Record $record) { }
+
+    public function onSleep(Doctrine_Record $record) { }
+
+    public function onWakeUp(Doctrine_Record $record) { }
+
+    public function onUpdate(Doctrine_Record $record) { }
+    public function onPreUpdate(Doctrine_Record $record) { }
+
+    public function onCreate(Doctrine_Record $record) { }
+    public function onPreCreate(Doctrine_Record $record) { }
+
+    public function onSave(Doctrine_Record $record) { }
+    public function onPreSave(Doctrine_Record $record) { }
+
+    public function onInsert(Doctrine_Record $record) { }
+    public function onPreInsert(Doctrine_Record $record) { }
+
+    public function onDelete(Doctrine_Record $record) { }
+    public function onPreDelete(Doctrine_Record $record) { }
+
+    public function onEvict(Doctrine_Record $record) { }
+    public function onPreEvict(Doctrine_Record $record) { }
+
+    public function onSaveCascade(Doctrine_Record $record) { }
+    public function onPreSaveCascade(Doctrine_Record $record) { }
+
+    public function onDeleteCascade(Doctrine_Record $record) { }
+    public function onPreDeleteCascade(Doctrine_Record $record) { }
+
+    public function onClose(Doctrine_Session $session) { }
+    public function onPreClose(Doctrine_Session $session) { }
+
+    public function onOpen(Doctrine_Session $session) { }
+
+    public function onTransactionCommit(Doctrine_Session $session) { }
+    public function onPreTransactionCommit(Doctrine_Session $session) { }
+
+    public function onTransactionRollback(Doctrine_Session $session) { }
+    public function onPreTransactionRollback(Doctrine_Session $session) { }
+
+    public function onTransactionBegin(Doctrine_Session $session) { }
+    public function onPreTransactionBegin(Doctrine_Session $session) { }
+    
+    public function onCollectionDelete(Doctrine_Collection $collection) { }
+    public function onPreCollectionDelete(Doctrine_Collection $collection) { }
+}
+/**
+ * an emtpy listener all components use this by default
+ */
+class EmptyEventListener extends Doctrine_EventListener { }
+
+?>
diff --git a/Doctrine/Exception.php b/Doctrine/Exception.php
new file mode 100644
index 000000000..7e8461591
--- /dev/null
+++ b/Doctrine/Exception.php
@@ -0,0 +1,6 @@
+<?php
+class InvalidKeyException extends Exception { }
+class InvalidTypeException extends Exception { }
+class Doctrine_Exception extends Exception { }   
+class DQLException extends Doctrine_Exception { }
+?>
diff --git a/Doctrine/ForeignKey.php b/Doctrine/ForeignKey.php
new file mode 100644
index 000000000..701d93885
--- /dev/null
+++ b/Doctrine/ForeignKey.php
@@ -0,0 +1,7 @@
+<?php
+require_once("Relation.php");
+/**
+ * Foreign Key
+ */
+class Doctrine_ForeignKey extends Doctrine_Relation { }
+?>
diff --git a/Doctrine/Form.class.php b/Doctrine/Form.class.php
new file mode 100644
index 000000000..4a4befdda
--- /dev/null
+++ b/Doctrine/Form.class.php
@@ -0,0 +1,77 @@
+<?php
+class Doctrine_Form implements Iterator {
+    protected $record;
+    protected $elements = array();
+    protected $columns;
+    protected $current;
+    protected $keys;
+    protected $index;
+    protected $count;
+    public function __construct(Doctrine_Record $record) {
+        $this->record = $record;
+        $this->columns = $record->getTable()->getColumns();
+        $this->keys    = array_keys($this->columns);
+        $this->index   = 0;
+        $this->count   = count($this->keys);
+    }
+    public function current() {
+        $i = $this->index;
+        $column = $this->keys[$i];
+
+        $definitions = $this->columns[$column];
+
+        $e    = explode("|",$definitions[2]);
+        $enum = false;
+        foreach($e as $v) {
+            $e2 = explode(":",$v);
+            if($e2[0] == "enum") {
+                $enum = explode("-",$e2[1]);
+                break;
+            }
+        }
+        $length = $definitions[1];
+        if( ! in_array("autoincrement",$e) && ! in_array("protected",$e)) {
+            if($enum) {
+                $elements[$column] = "<select name='data[$column]'>\n";
+                foreach($enum as $k => $v) {
+                    if($this->record->get($column) == $k) {
+                        $str = 'selected';
+                    } else
+                        $str = '';
+
+                    $elements[$column] .= "    <option value='$k' $str>$v</option>\n";
+                }
+                $elements[$column] .= "</select>\n";
+            } else {
+                if($length <= 255) {
+                    $elements[$column] = "<input name='data[$column]' type='text' value='".$this->record->get($column)."' maxlength=$length \>\n";
+                } else {
+                    $elements[$column] = "<textarea name='data[$column]' cols=40 rows=10>".$this->record->get($column)."</textarea>\n";
+                }
+            }
+            return $elements[$column];
+        } else {
+            $this->index++;
+
+            if($this->index < $this->count)
+                return self::current();
+        }
+    }
+    public function key() {
+        $i = $this->index;
+        return $this->keys[$i];
+    }
+    public function next() {
+        $this->index++;
+    }
+    public function rewind() {
+        $this->index = 0;
+    }
+    public function valid() {
+        if($this->index >= $this->count)
+            return false;
+            
+        return true;
+    }
+}
+?>
diff --git a/Doctrine/Identifier.php b/Doctrine/Identifier.php
new file mode 100644
index 000000000..9bcc3a20b
--- /dev/null
+++ b/Doctrine/Identifier.php
@@ -0,0 +1,24 @@
+<?php
+class Doctrine_Identifier {
+    /**
+     * constant for unique identifier
+     */
+    const UNIQUE         = 0;
+    /**
+     * constant for auto_increment identifier
+     */
+    const AUTO_INCREMENT = 1;
+    /**
+     * constant for sequence identifier
+     */
+    const SEQUENCE       = 2;
+    /**
+     * constant for normal identifier
+     */
+    const NORMAL         = 3;
+    /**
+     * constant for composite identifier
+     */
+    const COMPOSITE      = 4;
+}
+?>
diff --git a/Doctrine/IdentityMap.class.php b/Doctrine/IdentityMap.class.php
new file mode 100644
index 000000000..4d0e15eed
--- /dev/null
+++ b/Doctrine/IdentityMap.class.php
@@ -0,0 +1,51 @@
+<?php
+abstract class Doctrine_Creator {
+    protected $_table;
+    /**
+     * constructor
+     *
+     * @param Doctrine_Table $table
+     */
+    public function __construct(Doctrine_Table $table) {
+        $this->_table = $_table;
+    }
+    
+
+    abstract public function get();
+}
+class Doctrine_IdentityMap {
+    private $identityMap = array();
+
+    /**
+     * first checks if record exists in identityMap, if not
+     * returns a new record
+     *
+     * @return Doctrine_Record
+     */
+    public function get() {
+        $key = $this->getIdentifier();
+
+        if( ! is_array($key))
+            $key = array($key);
+
+
+        foreach($key as $k) {
+            if( ! isset($this->data[$k]))
+                throw new Doctrine_Exception("No primary key found");
+
+            $id[] = $this->data[$k];
+        }
+        $id = implode(' ', $id);
+
+        if(isset($this->identityMap[$id]))
+            $record = $this->identityMap[$id];
+        else {
+            $record = new $this->name($this);
+            $this->identityMap[$id] = $record;
+        }
+        $this->data = array();
+
+        return $record;
+    }
+}
+?>
diff --git a/Doctrine/IndexGenerator.php b/Doctrine/IndexGenerator.php
new file mode 100644
index 000000000..01b1bcd9d
--- /dev/null
+++ b/Doctrine/IndexGenerator.php
@@ -0,0 +1,25 @@
+<?php
+class Doctrine_IndexGenerator {
+    /**
+     * @var string $name
+     */
+    private $name;
+    /**
+     * @param string $name
+     */
+    public function __construct($name) {
+        $this->name = $name;
+    }
+    /**
+     * @param Doctrine_Record $record
+     * @return mixed
+     */
+    public function getIndex(Doctrine_Record $record) {
+        $value = $record->get($this->name);
+        if($value === null)
+            throw new Doctrine_Exception("Couldn't create collection index. Record field '".$this->name."' was null.");
+
+        return $value;
+    }
+}
+?>
diff --git a/Doctrine/Iterator.php b/Doctrine/Iterator.php
new file mode 100644
index 000000000..61246fa3f
--- /dev/null
+++ b/Doctrine/Iterator.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * Doctrine_Iterator
+ * iterates through Doctrine_Collection
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+abstract class Doctrine_Iterator implements Iterator {
+    /**
+     * @var Doctrine_Collection $collection         
+     */
+    protected $collection;
+    /**
+     * @var array $keys
+     */
+    protected $keys;
+    /**
+     * @var mixed $key
+     */
+    protected $key;
+    /**
+     * @var integer $index
+     */
+    protected $index;
+    /**
+     * @var integer $count
+     */
+    protected $count;
+
+    /**
+     * constructor
+     * @var Doctrine_Collection $collection
+     */
+    public function __construct(Doctrine_Collection $collection) {
+        $this->collection = $collection;
+        $this->keys       = $this->collection->getKeys();
+        $this->count      = $this->collection->count();
+    }
+    /**
+     * rewinds the iterator
+     *
+     * @return void
+     */
+    public function rewind() {
+        $this->index = 0;
+        $i = $this->index;
+        if(isset($this->keys[$i]))
+            $this->key   = $this->keys[$i];
+    }
+
+    /**
+     * returns the current key
+     *
+     * @return integer
+     */
+    public function key() {
+        return $this->key;
+    }
+    /**
+     * returns the current record
+     *
+     * @return Doctrine_Record
+     */
+    public function current() {
+        return $this->collection->get($this->key);
+    }
+    /**
+     * advances the internal pointer
+     *
+     * @return void
+     */
+    public function next() {
+        $this->index++;
+        $i = $this->index;
+        if(isset($this->keys[$i]))
+            $this->key   = $this->keys[$i];
+    }
+}
+
+
+?>
diff --git a/Doctrine/Lib.php b/Doctrine/Lib.php
new file mode 100644
index 000000000..e8a9ba91b
--- /dev/null
+++ b/Doctrine/Lib.php
@@ -0,0 +1,140 @@
+<?php
+class Doctrine_Lib {
+    /**
+     * @param integer $state                the state of record
+     * @see Doctrine_Record::STATE_* constants
+     * @return string                       string representation of given state
+     */
+    public static function getRecordStateAsString($state) {
+        switch($state):
+            case Doctrine_Record::STATE_PROXY:
+                return "proxy";
+            break;
+            case Doctrine_Record::STATE_CLEAN:
+                return "persistent clean";
+            break;
+            case Doctrine_Record::STATE_DIRTY:
+                return "persistent dirty";
+            break;
+            case Doctrine_Record::STATE_TDIRTY:
+                return "transient dirty";
+            break;
+            case Doctrine_Record::STATE_TCLEAN:
+                return "transient clean";
+            break;
+        endswitch;
+    }
+    /**
+     * returns a string representation of Doctrine_Record object
+     * @param Doctrine_Record $record
+     * @return string
+     */
+    public function getRecordAsString(Doctrine_Record $record) {
+        $r[] = "<pre>";
+        $r[] = "Component  : ".$record->getTable()->getComponentName();
+        $r[] = "ID         : ".$record->getID();
+        $r[] = "References : ".count($record->getReferences());
+        $r[] = "State      : ".Doctrine_Lib::getRecordStateAsString($record->getState());
+        $r[] = "OID        : ".$record->getOID();
+        $r[] = "</pre>";
+        return implode("\n",$r)."<br />";
+    }
+    /**
+     * getStateAsString
+     * returns a given session state as string
+     * @param integer $state        session state
+     */
+    public static function getSessionStateAsString($state) {
+        switch($state):
+            case Doctrine_Session::STATE_OPEN:
+                return "open";
+            break;
+            case Doctrine_Session::STATE_CLOSED:
+                return "closed";
+            break;
+            case Doctrine_Session::STATE_BUSY:
+                return "busy";
+            break;
+            case Doctrine_Session::STATE_ACTIVE:
+                return "active";
+            break;
+        endswitch;
+    }
+    /**
+     * returns a string representation of Doctrine_Session object
+     * @param Doctrine_Session $session
+     * @return string
+     */
+    public function getSessionAsString(Doctrine_Session $session) {
+        $r[] = "<pre>";
+        $r[] = "Doctrine_Session object";
+        $r[] = "State               : ".Doctrine_Lib::getSessionStateAsString($session->getState());
+        $r[] = "Open Transactions   : ".$session->getTransactionLevel();
+        $r[] = "Open Factories      : ".$session->count();
+        $sum = 0;
+        $rsum = 0;
+        $csum = 0;
+        foreach($session->getTables() as $objTable) {
+            if($objTable->getCache() instanceof Doctrine_Cache_File) {
+                $sum += array_sum($objTable->getCache()->getStats());
+                $rsum += $objTable->getRepository()->count();
+                $csum += $objTable->getCache()->count();
+            }
+        }
+        $r[] = "Cache Hits          : ".$sum." hits ";
+        $r[] = "Cache               : ".$csum." objects ";
+
+        $r[] = "Repositories        : ".$rsum." objects ";
+        $queries = false;
+        if($session->getDBH() instanceof Doctrine_DB) {
+            $handler = "Doctrine Database Handler";
+            $queries = count($session->getDBH()->getQueries());
+            $sum     = array_sum($session->getDBH()->getExecTimes());
+        } elseif($session->getDBH() instanceof PDO) {
+            $handler = "PHP Native PDO Driver";
+        } else
+            $handler = "Unknown Database Handler";
+
+        $r[] = "DB Handler          : ".$handler;
+        if($queries) {
+            $r[] = "Executed Queries    : ".$queries;
+            $r[] = "Sum of Exec Times   : ".$sum;
+        }
+
+        $r[] = "</pre>";
+        return implode("\n",$r)."<br>";
+    }
+    /**
+     * returns a string representation of Doctrine_Table object
+     * @param Doctrine_Table $table
+     * @return string
+     */
+    public function getTableAsString(Doctrine_Table $table) {
+        $r[] = "<pre>";
+        $r[] = "Component   : ".$this->getComponentName();
+        $r[] = "Table       : ".$this->getTableName();
+        $r[] = "Repository  : ".$this->getRepository()->count()." objects";
+        if($table->getCache() instanceof Doctrine_Cache_File) {
+            $r[] = "Cache       : ".$this->getCache()->count()." objects";
+            $r[] = "Cache hits  : ".array_sum($this->getCache()->getStats())." hits";
+        }
+        $r[] = "</pre>";
+        return implode("\n",$r)."<br>";
+    }
+    /**
+     * returns a string representation of Doctrine_Collection object
+     * @param Doctrine_Collection $collection
+     * @return string
+     */
+    public function getCollectionAsString(Doctrine_Collection $collection) {
+        $r[] = "<pre>";
+        $r[] = get_class($collection);
+
+        foreach($collection as $key => $record) {
+            $r[] = "Key : ".$key." ID : ".$record->getID();
+        }
+        $r[] = "</pre>";
+        return implode("\n",$r);
+    }
+}
+?>
diff --git a/Doctrine/LocalKey.php b/Doctrine/LocalKey.php
new file mode 100644
index 000000000..dc43566a8
--- /dev/null
+++ b/Doctrine/LocalKey.php
@@ -0,0 +1,7 @@
+<?php
+require_once("Relation.php");
+/**
+ * Local Key
+ */
+class Doctrine_LocalKey extends Doctrine_Relation { }
+?>
diff --git a/Doctrine/Manager.php b/Doctrine/Manager.php
new file mode 100644
index 000000000..09c5e50ad
--- /dev/null
+++ b/Doctrine/Manager.php
@@ -0,0 +1,249 @@
+<?php
+require_once("Configurable.php");
+require_once("EventListener.php");
+/**
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * 
+ * Doctrine_Manager is the base component of all doctrine based projects. 
+ * It opens and keeps track of all sessions (database connections).
+ */
+class Doctrine_Manager extends Doctrine_Configurable implements Countable, IteratorAggregate {
+    /**
+     * @var array $session          an array containing all the opened sessions
+     */
+    private $sessions   = array();
+    /**
+     * @var integer $index          the incremented index
+     */
+    private $index      = 0;
+    /**
+     * @var integer $currIndex      the current session index
+     */
+    private $currIndex  = 0;
+    /**
+     * @var string $root            root directory
+     */
+    private $root; 
+    /**
+     * @var Doctrine_Null $null     Doctrine_Null object, used for extremely fast null value checking
+     */
+    private $null;
+
+    /**
+     * constructor
+     */
+    private function __construct() {
+        $this->root = dirname(__FILE__);
+        $this->null = new Doctrine_Null;
+
+        Doctrine_Record::initNullObject($this->null);
+        Doctrine_Collection::initNullObject($this->null);
+    }
+    /**
+     * @return Doctrine_Null
+     */
+    final public function getNullObject() {
+        return $this->null;
+    }
+    /**
+     * setDefaultAttributes
+     * sets default attributes
+     *
+     * @return boolean
+     */
+    final public function setDefaultAttributes() {
+        static $init = false;
+        if( ! $init) {
+            $init = true;
+            $attributes = array(
+                        Doctrine::ATTR_CACHE_DIR        => "%ROOT%".DIRECTORY_SEPARATOR."cachedir",
+                        Doctrine::ATTR_FETCHMODE        => Doctrine::FETCH_LAZY,
+                        Doctrine::ATTR_CACHE_TTL        => 100,
+                        Doctrine::ATTR_CACHE_SIZE       => 100,
+                        Doctrine::ATTR_CACHE            => Doctrine::CACHE_NONE,
+                        Doctrine::ATTR_BATCH_SIZE       => 5,
+                        Doctrine::ATTR_COLL_LIMIT       => 5,
+                        Doctrine::ATTR_LISTENER         => new EmptyEventListener(),
+                        Doctrine::ATTR_PK_COLUMNS       => array("id"),
+                        Doctrine::ATTR_PK_TYPE          => Doctrine::INCREMENT_KEY,
+                        Doctrine::ATTR_LOCKMODE         => 1,
+                        Doctrine::ATTR_VLD              => false,
+                        Doctrine::ATTR_CREATE_TABLES    => true
+                        );
+            foreach($attributes as $attribute => $value) {
+                $old = $this->getAttribute($attribute);
+                if($old === null)
+                    $this->setAttribute($attribute,$value);
+            }
+            return true;
+        }
+        return false;
+    }
+    /**
+     * returns the root directory of Doctrine
+     *
+     * @return string
+     */
+    final public function getRoot() {
+        return $this->root;
+    }
+    /**
+     * getInstance                  
+     * returns an instance of this class
+     * (this class uses the singleton pattern)
+     *
+     * @return Doctrine_Manager
+     */
+    final public static function getInstance() {
+        static $instance;
+        if( ! isset($instance))
+            $instance = new self();
+
+        return $instance;
+    }
+
+    /**
+     * openSession                          
+     * opens a new session and saves it to Doctrine_Manager->sessions
+     *
+     * @param PDO $pdo                      PDO database driver
+     * @param string $name                  name of the session, if empty numeric key is used
+     * @return Doctrine_Session
+     */
+    final public function openSession(PDO $pdo, $name = null) {
+        // initialize the default attributes
+        $this->setDefaultAttributes();
+
+        if($name !== null) {
+            $name = (string) $name;
+            if(isset($this->sessions[$name]))
+                throw new InvalidKeyException();
+        
+        } else {
+            $name = $this->index;
+            $this->index++;
+        }
+        switch($pdo->getAttribute(PDO::ATTR_DRIVER_NAME)):
+            case "mysql":
+                $this->sessions[$name] = new Doctrine_Session_Mysql($this,$pdo);
+            break;
+            case "sqlite":
+                $this->sessions[$name] = new Doctrine_Session_Sqlite($this,$pdo);
+            break;
+            case "pgsql":
+                $this->sessions[$name] = new Doctrine_Session_Pgsql($this,$pdo);
+            break;
+            case "oci":
+                $this->sessions[$name] = new Doctrine_Session_Oracle($this,$pdo);
+            break;
+            case "mssql":
+                $this->sessions[$name] = new Doctrine_Session_Mssql($this,$pdo);
+            break;
+            case "firebird":
+                $this->sessions[$name] = new Doctrine_Session_Firebird($this,$pdo);
+            break;
+            case "informix":
+                $this->sessions[$name] = new Doctrine_Session_Informix($this,$pdo);
+            break;
+        endswitch;
+
+
+        $this->currIndex = $name;
+        return $this->sessions[$name];
+    }
+    /**
+     * getSession
+     * @param integer $index
+     * @return object Doctrine_Session
+     * @throws InvalidKeyException
+     */
+    final public function getSession($index) {
+        if( ! isset($this->sessions[$index]))
+            throw new InvalidKeyException();
+
+        $this->currIndex = $index;
+        return $this->sessions[$index];
+    }
+    /**
+     * closes the session
+     *
+     * @param Doctrine_Session $session
+     * @return void
+     */
+    final public function closeSession(Doctrine_Session $session) {
+        $session->close();
+        unset($session);
+    }
+    /**
+     * getSessions
+     * returns all opened sessions
+     *
+     * @return array
+     */
+    final public function getSessions() {
+        return $this->sessions;
+    }
+    /**
+     * setCurrentSession
+     * sets the current session to $key
+     *
+     * @param mixed $key                        the session key
+     * @throws InvalidKeyException
+     * @return void
+     */
+    final public function setCurrentSession($key) {
+        $key = (string) $key;
+        if( ! isset($this->sessions[$key]))
+            throw new InvalidKeyException();
+        
+        $this->currIndex = $key;
+    }
+    /**
+     * count
+     * returns the number of opened sessions
+     *
+     * @return integer
+     */
+    public function count() {
+        return count($this->sessions);
+    }
+    /**
+     * getIterator
+     * returns an ArrayIterator that iterates through all sessions
+     *
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->sessions);
+    }
+    /**
+     * getCurrentSession
+     * returns the current session
+     *
+     * @throws Doctrine_Session_Exception       if there are no open sessions
+     * @return Doctrine_Session
+     */
+    final public function getCurrentSession() {
+        $i = $this->currIndex;
+        if( ! isset($this->sessions[$i]))
+            throw new Doctrine_Session_Exception();
+
+        return $this->sessions[$i];
+    }
+    /**
+     * __toString
+     * returns a string representation of this object
+     *
+     * @return string
+     */
+    public function __toString() {
+        $r[] = "<pre>";
+        $r[] = "Doctrine_Manager";
+        $r[] = "Sessions : ".count($this->sessions);
+        $r[] = "</pre>";
+        return implode("\n",$r);
+    }
+}
+?>
diff --git a/Doctrine/Module.class.php b/Doctrine/Module.class.php
new file mode 100644
index 000000000..4587521ee
--- /dev/null
+++ b/Doctrine/Module.class.php
@@ -0,0 +1,60 @@
+<?php
+class Doctrine_Module implements IteratorAggregate, Countable {
+    /**
+     * @var array $components   an array containing all the components in this module
+     */
+    private $components = array();
+    /**
+     * @var string $name        the name of this module
+     */
+    private $name;
+    /**
+     * constructor
+     *
+     * @param string $name      the name of this module
+     */
+    public function __construct($name) {
+        $this->name = $name;
+    }
+    /**
+     * returns the name of this module
+     *
+     * @return string
+     */
+    public function getName() {
+        return $this->name;
+    }
+    /**
+     * flush
+     * saves all components
+     *
+     * @return void
+     */
+    public function flush() {
+        $session = Doctrine_Manager::getInstance()->getCurrentSession();
+        
+        $tree = $session->buildFlushTree($this->components);
+    }
+    /**
+     * getIterator
+     * this class implements IteratorAggregate interface
+     * returns an iterator that iterates through the components
+     * in this module
+     *
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->components);
+    }
+    /**
+     * count
+     * this class implements Countable interface
+     * returns the number of components in this module
+     *
+     * @return integer
+     */
+    public function count() {
+        return count($this->components);
+    }
+}
+?>
diff --git a/Doctrine/Null.php b/Doctrine/Null.php
new file mode 100644
index 000000000..4c4012af7
--- /dev/null
+++ b/Doctrine/Null.php
@@ -0,0 +1,6 @@
+<?php
+/**
+ * Doctrine_Null
+ */
+class Doctrine_Null { }
+?>
diff --git a/Doctrine/Query.php b/Doctrine/Query.php
new file mode 100644
index 000000000..300105eb8
--- /dev/null
+++ b/Doctrine/Query.php
@@ -0,0 +1,1005 @@
+<?php
+require_once("Access.php");
+/**
+ * Doctrine_Query
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * @version     1.0 alpha
+ */
+class Doctrine_Query extends Doctrine_Access {
+    /**
+     * @var array $fetchmodes       an array containing all fetchmodes
+     */
+    private $fetchModes  = array();
+    /**
+     * @var array $tables           an array containing all the tables used in the query
+     */
+    private $tables      = array();
+    /**
+     * @var array $collections      an array containing all collections this parser has created/will create
+     */
+    private $collections = array();
+
+    private $joined      = array();
+    
+    private $joins       = array();
+    /**
+     * @var array $data             fetched data
+     */
+    private $data        = array();
+    /**
+     * @var Doctrine_Session $session     Doctrine_Session object
+     */
+    private $session;
+
+    private $inheritanceApplied = false;
+
+    private $aggregate  = false;
+    /**
+     * @var array $connectors       component connectors
+     */
+    private $connectors  = array();
+    /**
+     * @var array $dql              DQL query string parts
+     */
+    protected $dql = array(
+        "columns"   => array(),
+        "from"      => array(),
+        "join"      => array(),
+        "where"     => array(),
+        "group"     => array(),
+        "having"    => array(),
+        "orderby"   => array(),
+        "limit"     => false,
+        "offset"    => false,
+        );
+    /**
+     * @var array $parts            SQL query string parts
+     */
+    protected $parts = array(
+        "columns"   => array(),
+        "from"      => array(),
+        "join"      => array(),
+        "where"     => array(),
+        "group"     => array(),
+        "having"    => array(),
+        "orderby"   => array(),
+        "limit"     => false,
+        "offset"    => false,
+        );
+    /**
+     * constructor
+     *
+     * @param Doctrine_Session $session
+     */
+    public function __construct(Doctrine_Session $session) {
+        $this->session = $session;
+    }
+    /**
+     * clear
+     * resets all the variables
+     * 
+     * @return void
+     */
+    private function clear() {
+        $this->fetchModes   = array();
+        $this->tables       = array();
+
+        $this->parts = array(
+                  "columns"   => array(),
+                  "from"      => array(),
+                  "join"      => array(),
+                  "where"     => array(),
+                  "group"     => array(),
+                  "having"    => array(),
+                  "orderby"   => array(),
+                  "limit"     => false,
+                  "offset"    => false,
+                );
+        $this->inheritanceApplied = false;
+        $this->aggregate    = false;
+        $this->data         = array();
+        $this->connectors   = array();
+        $this->collections  = array();
+        $this->joined       = array();
+        $this->joins        = array();
+    }
+    /**
+     * loadFields      
+     * loads fields for a given table and
+     * constructs a little bit of sql for every field
+     *
+     * fields of the tables become: [tablename].[fieldname] as [tablename]__[fieldname]
+     *
+     * @access private
+     * @param object Doctrine_Table $table       a Doctrine_Table object
+     * @param integer $fetchmode                 fetchmode the table is using eg. Doctrine::FETCH_LAZY
+     * @param array $names                      fields to be loaded (only used in lazy property loading)
+     * @return void
+     */
+    private function loadFields(Doctrine_Table $table, $fetchmode, array $names) {
+        $name = $table->getComponentName();
+
+        switch($fetchmode):
+            case Doctrine::FETCH_OFFSET:
+                $this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT);
+            case Doctrine::FETCH_IMMEDIATE:
+                if( ! empty($names))
+                    throw new Doctrine_Exception("Lazy property loading can only be used with fetching strategies lazy, batch and lazyoffset.");
+
+                $names  = $table->getColumnNames();
+            break;
+            case Doctrine::FETCH_LAZY_OFFSET:
+                $this->limit = $table->getAttribute(Doctrine::ATTR_COLL_LIMIT);
+            case Doctrine::FETCH_LAZY:
+            case Doctrine::FETCH_BATCH:
+                $names = array_merge($table->getPrimaryKeys(), $names);
+            break;
+            default:
+                throw new Doctrine_Exception("Unknown fetchmode.");
+        endswitch;
+        $cname          = $table->getComponentName();
+        $this->fetchModes[$cname] = $fetchmode;
+        $tablename      = $table->getTableName();
+
+        $count = count($this->tables);
+        foreach($names as $name) {
+            if($count == 0) {
+                $this->parts["columns"][] = $tablename.".".$name;
+            } else {
+                $this->parts["columns"][] = $tablename.".".$name." AS ".$cname."__".$name;
+            }
+        }
+    }
+    /** 
+     * sets a query part
+     *
+     * @param string $name
+     * @param array $args
+     * @return void
+     */
+    public function __call($name, $args) {
+        $name = strtolower($name);
+        if(isset($this->parts[$name])) {
+            $method = "parse".ucwords($name);
+            switch($name):
+                case "where":
+                    $this->parts[$name] = array($this->$method($args[0]));
+                break;
+                case "limit":
+                case "offset":
+                    if($args[0] == null)
+                        $args[0] = false;
+
+                    $this->parts[$name] = $args[0];
+                break;
+                case "from":
+                    $this->parts['columns'] = array();
+                    $this->joins            = array();
+                    $this->tables           = array();
+                    $this->fetchModes       = array();
+                default:
+                    $this->parts[$name] = array();
+                    $this->$method($args[0]);
+            endswitch;
+        }
+
+        return $this;
+    }
+    /**
+     * returns a query part
+     *
+     * @param $name         query part name
+     * @return mixed
+     */
+    public function get($name) {
+        if( ! isset($this->parts[$name]))
+            return false;
+
+        return $this->parts[$name];
+    }
+    /**
+     * sets a query part
+     *
+     * @param $name         query part name
+     * @param $value        query part value
+     * @return boolean
+     */
+    public function set($name, $value) {
+
+        if(isset($this->parts[$name])) {
+            $method = "parse".ucwords($name);
+            switch($name):
+                case "where":
+                    $this->parts[$name] = array($this->$method($value));
+                break;
+                case "limit":
+                case "offset": 
+                    if($value == null)
+                        $value = false;
+
+                    $this->parts[$name] = $value;
+                break;
+                case "from":
+                    $this->parts['columns'] = array();
+                    $this->joins            = array();
+                    $this->tables           = array();
+                    $this->fetchModes       = array();
+                default:
+                    $this->parts[$name] = array();
+                    $this->$method($value);
+            endswitch;
+            
+            return true;
+        }
+        return false;
+    }
+    /**
+     * returns the built sql query
+     *
+     * @return string
+     */
+    final public function getQuery() {
+        if(empty($this->parts["columns"]) || empty($this->parts["from"]))
+            return false;
+
+        // build the basic query
+        $q = "SELECT ".implode(", ",$this->parts["columns"]).
+             " FROM ";
+        
+        foreach($this->parts["from"] as $tname => $bool) {
+            $a[] = $tname;
+        }
+        $q .= implode(", ",$a);
+        
+        if( ! empty($this->parts['join'])) {
+            foreach($this->parts['join'] as $part) {
+                $q .= " ".implode(' ', $part);
+            }
+        }
+
+        $this->applyInheritance();
+        if( ! empty($this->parts["where"]))
+            $q .= " WHERE ".implode(" ",$this->parts["where"]);
+
+        if( ! empty($this->parts["orderby"]))
+            $q .= " ORDER BY ".implode(", ",$this->parts["orderby"]);
+
+        if( ! empty($this->parts["limit"]) || ! empty($this->offset))
+            $q = $this->session->modifyLimitQuery($q,$this->parts["limit"],$this->offset);
+
+        return $q;
+    }
+    /**
+     * sql delete for mysql
+     */
+    final public function buildDelete() {
+        if(empty($this->parts["columns"]) || empty($this->parts["from"]))
+            return false;    
+        
+        $a = array_merge(array_keys($this->parts["from"]),$this->joined);
+        $q = "DELETE ".implode(", ",$a)." FROM ";
+        $a = array();
+
+        foreach($this->parts["from"] as $tname => $bool) {
+            $str = $tname;
+            if(isset($this->parts["join"][$tname]))
+                $str .= " ".$this->parts["join"][$tname];
+
+            $a[] = $str;
+        }
+
+        $q .= implode(", ",$a);
+        $this->applyInheritance();
+        if( ! empty($this->parts["where"]))
+            $q .= " WHERE ".implode(" ",$this->parts["where"]);
+
+        if( ! empty($this->parts["orderby"]))
+            $q .= " ORDER BY ".implode(", ",$this->parts["orderby"]);
+
+        if( ! empty($this->parts["limit"]) && ! empty($this->offset))
+            $q = $this->session->modifyLimitQuery($q,$this->parts["limit"],$this->offset);
+
+        return $q;
+    }
+    /**
+     * applyInheritance
+     * applies column aggregation inheritance to DQL query
+     *
+     * @return boolean
+     */
+    final public function applyInheritance() {
+        if($this->inheritanceApplied) 
+            return false;
+
+        // get the inheritance maps
+        $array = array();
+
+        foreach($this->tables as $objTable):
+            $tname = $objTable->getTableName();
+            $array[$tname][] = $objTable->getInheritanceMap();
+        endforeach;
+
+        // apply inheritance maps
+        $str = "";
+        $c = array();
+
+        foreach($array as $tname => $maps) {
+            $a = array();
+            foreach($maps as $map) {
+                $b = array();
+                foreach($map as $field=>$value) {
+                    $b[] = $tname.".$field = $value";
+                }
+                if( ! empty($b)) $a[] = implode(" AND ",$b);
+            }
+            if( ! empty($a)) $c[] = implode(" || ",$a);
+        }
+
+        $str .= implode(" || ",$c);
+
+        $this->addWhere($str);
+        $this->inheritanceApplied = true;
+        return true;
+    }
+    /**
+     * @param string $where
+     * @return boolean
+     */
+    final public function addWhere($where) {
+        if(empty($where))
+            return false;
+
+        if($this->parts["where"]) {
+            $this->parts["where"][] = "AND (".$where.")";
+        } else {
+            $this->parts["where"][] = "(".$where.")";
+        }
+        return true;
+    }
+    /**
+     * getData
+     * @param $key                      the component name
+     * @return array                    the data row for the specified component
+     */
+    final public function getData($key) {
+        if(isset($this->data[$key]) && is_array($this->data[$key]))
+            return $this->data[$key];
+
+        return array();
+    }
+    /**
+     * execute
+     * executes the dql query and populates all collections
+     *
+     * @param string $params
+     * @return Doctrine_Collection            the root collection
+     */
+    public function execute($params = array()) {
+        $this->data = array();
+        $this->collections = array();
+
+        switch(count($this->tables)):
+            case 0:
+                throw new DQLException();
+            break;
+            case 1:
+                $query = $this->getQuery();
+
+                $keys  = array_keys($this->tables);
+
+                $name  = $this->tables[$keys[0]]->getComponentName();
+                $stmt  = $this->session->execute($query,$params);
+
+                while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
+                    foreach($data as $key => $value):
+                        $e = explode("__",$key);
+                        if(count($e) > 1) {
+                            $data[$e[1]] = $value;
+                        } else {
+                            $data[$e[0]] = $value;
+                        }
+                        unset($data[$key]);
+                    endforeach;
+                    $this->data[$name][] = $data;
+                endwhile;
+
+                return $this->getCollection($keys[0]);
+            break;
+            default:
+                $query = $this->getQuery();
+
+                $keys  = array_keys($this->tables);
+                $root  = $keys[0];
+                $stmt  = $this->session->execute($query,$params);
+                
+                $previd = array();
+
+                $coll        = $this->getCollection($root);
+                $prev[$root] = $coll;
+
+                $array = $this->parseData($stmt);
+
+                $colls = array();    
+
+                foreach($array as $data) {
+                    /**
+                     * remove duplicated data rows and map data into objects
+                     */
+                    foreach($data as $key => $row) {
+                        if(empty($row))
+                            continue;
+
+                        $ids  = $this->tables[$key]->getIdentifier();
+
+                        if(is_array($ids)) {
+                        $emptyID = false;
+                            foreach($ids as $id) {
+                                if($row[$id] == null) {
+                                    $emptyID = true;
+                                    break;
+                                }
+                            }
+                        if($emptyID)
+                            continue;
+                        } else {
+                            if($row[$ids] === null)
+                                continue;
+                        }
+
+                        $name = $this->tables[$key]->getComponentName();
+
+                        if( ! isset($previd[$name]))
+                            $previd[$name] = array();
+
+
+                        if($previd[$name] !== $row) {
+                            // set internal data
+                            $this->tables[$name]->setData($row);
+
+                            // initialize a new record
+                            $record = $this->tables[$name]->getRecord();
+
+                            if($name == $root) {
+                                // add record into root collection
+                                $coll->add($record);
+                            } else {
+                                $pointer = $this->joins[$name];
+
+                                $fk = $this->tables[$pointer]->getForeignKey($this->tables[$pointer]->getAlias($name));
+
+                                switch($fk->getType()):
+                                    case Doctrine_Relation::ONE_COMPOSITE:
+                                    case Doctrine_Relation::ONE_AGGREGATE:
+                                        $last = $prev[$pointer]->getLast();
+
+                                        $last->rawSet($this->connectors[$name]->getLocal(), $record->getID());
+
+                                        $last->initSingleReference($record);
+                                        
+                                        $prev[$name] = $record;
+                                    break;
+                                    default:
+                                        // one-to-many relation or many-to-many relation
+                                        $last = $prev[$pointer]->getLast();
+    
+                                        if( ! $last->hasReference($name)) {
+                                            $prev[$name] = $this->getCollection($name);
+                                            $last->initReference($prev[$name],$this->connectors[$name]);
+                                        }
+                                        $last->addReference($record);
+                                endswitch;
+                            }
+                        }
+
+                        $previd[$name] = $row;
+                    }
+                }
+
+                return $coll;
+        endswitch;
+    }
+    /**
+     * parseData
+     * parses the data returned by PDOStatement
+     *
+     * @return array
+     */
+    public function parseData(PDOStatement $stmt) {
+        $array = array();
+        $keys  = array();
+        foreach(array_keys($this->tables) as $key) {
+            $k = strtolower($key);
+            $keys[$k] = $key;
+        }
+        while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
+            /**
+             * parse the data into two-dimensional array
+             */
+            foreach($data as $key => $value):
+                $e = explode("__",$key);
+
+                if(count($e) > 1) {
+                    $data[$keys[$e[0]]][$e[1]] = $value;
+                } else {
+                    $data[0][$e[0]] = $value;
+                }
+                unset($data[$key]);
+            endforeach;
+            $array[] = $data;
+        endwhile;
+        $stmt->closeCursor();
+        return $array;
+    }
+    /**
+     * returns a Doctrine_Table for given name
+     *
+     * @param string $name              component name
+     * @return Doctrine_Table
+     */
+    public function getTable($name) {
+        return $this->tables[$name];
+    }
+    /**
+     * getCollection
+     *
+     * @parma string $name              component name
+     * @param integer $index
+     */
+    private function getCollection($name) {
+        $table = $this->session->getTable($name);
+        switch($this->fetchModes[$name]):
+            case Doctrine::FETCH_BATCH:
+                $coll = new Doctrine_Collection_Batch($table);
+            break;
+            case Doctrine::FETCH_LAZY:
+                $coll = new Doctrine_Collection_Lazy($table);
+            break;
+            case Doctrine::FETCH_OFFSET:
+                $coll = new Doctrine_Collection_Offset($table);
+            break;
+            case Doctrine::FETCH_IMMEDIATE:
+                $coll = new Doctrine_Collection_Immediate($table);
+            break;
+            case Doctrine::FETCH_LAZY_OFFSET:
+                $coll = new Doctrine_Collection_LazyOffset($table);
+            break;
+        endswitch;
+
+        $coll->populate($this);
+        return $coll;
+    }
+    /**
+     * query the database with DQL (Doctrine Query Language)
+     *
+     * @param string $query                 DQL query
+     * @param array $params                 parameters
+     */
+    public function query($query,$params = array()) {
+        $this->parseQuery($query);
+
+        if($this->aggregate) {
+            $keys  = array_keys($this->tables);
+            $query = $this->getQuery();
+            $stmt  = $this->tables[$keys[0]]->getSession()->select($query,$this->parts["limit"],$this->offset);
+            $data  = $stmt->fetch(PDO::FETCH_ASSOC);
+            if(count($data) == 1) {
+                return current($data);
+            } else {
+                return $data;
+            }
+        } else {
+            return $this->execute($params);
+        }
+    }
+    /**
+     * DQL PARSER
+     *
+     * @param string $query         DQL query
+     * @return void
+     */
+    final public function parseQuery($query) {
+        $this->clear();
+        $e = self::bracketExplode($query," ","(",")");
+            
+        $parts = array();
+        foreach($e as $k=>$part):
+            switch(strtolower($part)):
+                case "select":
+                case "from":
+                case "where":
+                case "limit":
+                case "offset":
+                    $p = $part;
+                    $parts[$part] = array();
+                break;
+                case "order":
+                    $p = $part;
+                    $i = $k+1;
+                    if(isset($e[$i]) && strtolower($e[$i]) == "by") {
+                        $parts[$part] = array();
+                    }
+                break;
+                case "by":
+                    continue;
+                default:
+                    $parts[$p][] = $part;
+            endswitch;
+        endforeach;
+
+        foreach($parts as $k => $part) {
+            $part = implode(" ",$part);
+            switch(strtoupper($k)):
+                case "SELECT":
+                    $this->parseSelect($part);
+                break;
+                case "FROM":
+                    $this->parseFrom($part);
+                break;
+                case "WHERE":
+                    $this->addWhere($this->parseWhere($part));
+                break;
+                case "ORDER":
+                    $this->parseOrderBy($part);
+                break;  
+                case "LIMIT":
+                    $this->parts["limit"] = trim($part);
+                break;
+                case "OFFSET":
+                    $this->offset = trim($part);
+                break;
+            endswitch;
+        }
+    }
+    /**
+     * DQL ORDER BY PARSER
+     * parses the order by part of the query string
+     *
+     * @param string $str
+     * @return void
+     */
+    private function parseOrderBy($str) {
+        foreach(explode(",",trim($str)) as $r) {
+            $r = trim($r);
+            $e = explode(" ",$r);
+            $a = explode(".",$e[0]);
+    
+            if(count($a) > 1) {
+                $field     = array_pop($a);
+                $reference = implode(".",$a);
+                $name      = end($a);
+
+                $this->load($reference, false);
+                $tname     = $this->tables[$name]->getTableName();
+
+                $r = $tname.".".$field;
+                if(isset($e[1])) 
+                    $r .= " ".$e[1];
+            }
+            $this->parts["orderby"][] = $r;
+        }
+    }
+    /**
+     * DQL SELECT PARSER
+     * parses the select part of the query string
+     *
+     * @param string $str
+     * @return void
+     */
+    private function parseSelect($str) {
+        $this->aggregate = true;
+        foreach(explode(",",trim($str)) as $reference) {
+
+            $e = explode(" AS ",trim($reference));
+
+            $f = explode("(",$e[0]);
+            $a = explode(".",$f[1]);
+            $field = substr(array_pop($a),0,-1);
+
+            $reference = trim(implode(".",$a));
+
+            $objTable = $this->load($reference);
+            if(isset($e[1]))
+                $s = " AS $e[1]";
+
+            $this->parts["columns"][] = $f[0]."(".$objTable->getTableName().".$field)$s";
+
+        }
+    }
+    /**
+     * DQL FROM PARSER
+     * parses the from part of the query string
+
+     * @param string $str
+     * @return void
+     */
+    private function parseFrom($str) {
+        foreach(explode(",",trim($str)) as $reference) {
+            $reference = trim($reference);
+            $table = $this->load($reference);
+        }
+    }
+    /**
+     * returns Doctrine::FETCH_* constant
+     *
+     * @param string $mode
+     * @return integer
+     */
+    private function parseFetchMode($mode) {
+        switch(strtolower($mode)):
+            case "i":
+            case "immediate":
+                $fetchmode = Doctrine::FETCH_IMMEDIATE;
+            break;
+            case "b":
+            case "batch":
+                $fetchmode = Doctrine::FETCH_BATCH;
+            break;
+            case "l":
+            case "lazy":
+                $fetchmode = Doctrine::FETCH_LAZY;
+            break;
+            case "o":
+            case "offset":
+                $fetchmode = Doctrine::FETCH_OFFSET;
+            break;
+            case "lo":
+            case "lazyoffset":
+                $fetchmode = Doctrine::FETCH_LAZYOFFSET;
+            default:
+                throw new DQLException("Unknown fetchmode '$mode'. The availible fetchmodes are 'i', 'b' and 'l'.");
+        endswitch;
+        return $fetchmode;
+    }
+    /**
+     * DQL WHERE PARSER
+     * parses the where part of the query string
+     *
+     *
+     * @param string $str
+     * @return string
+     */
+    private function parseWhere($str) {
+        $tmp = trim($str);
+        $str = self::bracketTrim($tmp,"(",")");
+        
+        $brackets = false;
+        while($tmp != $str) {
+            $brackets = true;
+            $tmp = $str;
+            $str = self::bracketTrim($str,"(",")");
+        }
+
+        $parts = self::bracketExplode($str," && ","(",")");
+        if(count($parts) > 1) {
+            $ret = array();
+            foreach($parts as $part) {
+                $ret[] = $this->parseWhere($part);
+            }
+            $r = implode(" AND ",$ret);
+        } else {
+            $parts = self::bracketExplode($str," || ","(",")");
+            if(count($parts) > 1) {
+                $ret = array();
+                foreach($parts as $part) {
+                    $ret[] = $this->parseWhere($part);
+                }
+                $r = implode(" OR ",$ret);
+            } else {
+                return $this->loadWhere($parts[0]);
+            }
+        }
+        if($brackets)
+            return "(".$r.")";
+        else
+            return $r;
+    }
+    /**
+     * trims brackets
+     *
+     * @param string $str
+     * @param string $e1        the first bracket, usually '('
+     * @param string $e2        the second bracket, usually ')'
+     */
+    public static function bracketTrim($str,$e1,$e2) {
+        if(substr($str,0,1) == $e1 && substr($str,-1) == $e2)
+            return substr($str,1,-1);
+        else
+            return $str;
+    }
+    /**
+     * bracketExplode
+     * usage:
+     * $str = (age < 20 AND age > 18) AND email LIKE 'John@example.com'
+     * now exploding $str with parameters $d = ' AND ', $e1 = '(' and $e2 = ')'
+     * would return an array:
+     * array("(age < 20 AND age > 18)", "email LIKE 'John@example.com'")
+     *
+     * @param string $str
+     * @param string $d         the delimeter which explodes the string
+     * @param string $e1        the first bracket, usually '('
+     * @param string $e2        the second bracket, usually ')'
+     *
+     */
+    public static function bracketExplode($str,$d,$e1,$e2) {
+        $str = explode("$d",$str);
+        $i = 0;
+        $term = array();
+        foreach($str as $key=>$val) {
+            if (empty($term[$i])) {
+                $term[$i] = trim($val);
+                $s1 = substr_count($term[$i],"$e1");
+                $s2 = substr_count($term[$i],"$e2");
+                    if($s1 == $s2) $i++;
+            } else {
+                $term[$i] .= "$d".trim($val);
+                $c1 = substr_count($term[$i],"$e1");
+                $c2 = substr_count($term[$i],"$e2");
+                    if($c1 == $c2) $i++;
+            }
+        }
+        return $term;
+    }
+    /**
+     * loadWhere
+     *
+     * @param string $where
+     */
+    private function loadWhere($where) {
+        $e = explode(" ",$where);
+        $r = array_shift($e);
+        $a = explode(".",$r);
+
+
+        if(count($a) > 1) {
+            $field     = array_pop($a);
+            $operator  = array_shift($e);
+            $value     = implode(" ",$e);
+            $reference = implode(".",$a);
+
+            if(count($a) > 1)
+                $objTable = $this->tables[$a[0]]->getForeignKey(end($a))->getTable();
+            else
+                $objTable = $this->session->getTable(end($a));
+
+            $where     = $objTable->getTableName().".".$field." ".$operator." ".$value;
+
+            if(count($a) > 1 && isset($a[1])) {
+                $root = $a[0];
+                $fk = $this->tables[$root]->getForeignKey($a[1]);
+                if($fk instanceof Doctrine_Association) {
+                    $asf = $fk->getAssociationFactory();
+
+                    switch($fk->getType()):
+                        case Doctrine_Relation::ONE_AGGREGATE:
+                        case Doctrine_Relation::ONE_COMPOSITE:
+
+                        break;
+                        case Doctrine_Relation::MANY_AGGREGATE:
+                        case Doctrine_Relation::MANY_COMPOSITE:
+
+                            // subquery needed
+                            $where     = $objTable->getComponentName().".".$field." ".$operator." ".$value;
+                            $b = $fk->getTable()->getComponentName();
+
+                            $graph = new Doctrine_Query($this->session);
+                            $graph->parseQuery("FROM $b-l WHERE $where");
+                            $where = $this->tables[$root]->getTableName().".".$this->tables[$root]->getIdentifier()." IN (SELECT ".$fk->getLocal()." FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." IN (".$graph->getQuery()."))";
+                        break;
+                    endswitch;
+                } else
+                    $this->load($reference, false);
+
+            } else
+                $this->load($reference, false);
+        }
+        return $where;
+    }
+    /**
+     * @param string $path              the path of the loadable component
+     * @param integer $fetchmode        optional fetchmode, if not set the components default fetchmode will be used
+     * @throws DQLException
+     */
+    final public function load($path, $loadFields = true) {
+        $e = preg_split("/[.:]/",$path);
+        $index = 0;
+
+        foreach($e as $key => $fullname) {
+            try {
+                $e2 = preg_split("/[-(]/",$fullname);
+                $name = $e2[0];
+
+                if($key == 0) {
+
+                    $table = $this->session->getTable($name);
+
+                    $tname = $table->getTableName();
+                    $this->parts["from"][$tname] = true;
+
+                } else {
+
+                    $index += strlen($e[($key - 1)]) + 1;
+                    // the mark here is either '.' or ':'
+                    $mark  = substr($path,($index - 1),1);
+                       	
+
+                    $fk     = $table->getForeignKey($name);
+                    $name   = $fk->getTable()->getComponentName();
+
+                    $tname  = $table->getTableName();
+
+                    $tname2 = $fk->getTable()->getTableName();
+
+                    $this->connectors[$name] = $fk;
+
+                    if($fk instanceof Doctrine_ForeignKey ||
+                       $fk instanceof Doctrine_LocalKey) {
+
+                        switch($mark):
+                            case ":":
+                                $this->parts["join"][$tname][$tname2]  = "INNER JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
+                            break;
+                            case ".":
+                                $this->parts["join"][$tname][$tname2]  = "LEFT JOIN ".$tname2." ON ".$tname.".".$fk->getLocal()." = ".$tname2.".".$fk->getForeign();
+                            break;
+                        endswitch;
+
+                        $c = $table->getComponentName();
+                        $this->joins[$name] = $c;
+                    } elseif($fk instanceof Doctrine_Association) {
+                        $asf = $fk->getAssociationFactory();
+
+                        switch($fk->getType()):
+                            case Doctrine_Relation::ONE_AGGREGATE:
+                            case Doctrine_Relation::ONE_COMPOSITE:
+
+                            break;
+                            case Doctrine_Relation::MANY_AGGREGATE:
+                            case Doctrine_Relation::MANY_COMPOSITE:
+
+                                //$this->addWhere("SELECT ".$fk->getLocal()." FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." IN (SELECT ".$fk->getTable()->getComponentName().")");
+                                $this->parts["from"][$tname]  = true;
+                            break;
+                        endswitch;
+                    }
+
+                    $table = $fk->getTable();
+
+                }
+
+                if( ! isset($this->tables[$name])) {
+                    $this->tables[$name] = $table;    
+
+                    if($loadFields && ! $this->aggregate) {
+                        $fields = array();
+
+                        if(strpos($fullname, "-") === false) {
+                            $fetchmode = $table->getAttribute(Doctrine::ATTR_FETCHMODE);
+                            
+                            if(isset($e2[1]))
+                                $fields = explode(",",substr($e2[1],0,-1));
+
+                        } else {
+                            if(isset($e2[1])) {
+                                $fetchmode = $this->parseFetchMode($e2[1]);
+                            } else
+                                $fetchmode = $table->getAttribute(Doctrine::ATTR_FETCHMODE);
+                                
+                            if(isset($e2[2]))
+                                $fields = explode(",",substr($e2[2],0,-1));
+                        }
+
+                        $this->loadFields($table, $fetchmode, $fields);
+                    }
+                }
+
+            } catch(Exception $e) {
+                throw new DQLException($e->getMessage(),$e->getCode());
+            }
+        }
+    }
+}
+
+?>
diff --git a/Doctrine/Record.php b/Doctrine/Record.php
new file mode 100644
index 000000000..86d8980f7
--- /dev/null
+++ b/Doctrine/Record.php
@@ -0,0 +1,1110 @@
+<?php
+require_once("Access.php");
+/**
+ * Doctrine_Record
+ */
+abstract class Doctrine_Record extends Doctrine_Access implements Countable, IteratorAggregate {
+    /**
+     * STATE CONSTANTS
+     */
+
+    /**
+     * DIRTY STATE
+     * a Doctrine_Record is in dirty state when its properties are changed
+     */
+    const STATE_DIRTY       = 1;
+    /**
+     * TDIRTY STATE
+     * a Doctrine_Record is in transient dirty state when it is created and some of its fields are modified
+     * but it is NOT yet persisted into database
+     */
+    const STATE_TDIRTY      = 2;
+    /**
+     * CLEAN STATE
+     * a Doctrine_Record is in clean state when all of its properties are loaded from the database
+     * and none of its properties are changed
+     */
+    const STATE_CLEAN       = 3;
+    /**
+     * PROXY STATE
+     * a Doctrine_Record is in proxy state when its properties are not fully loaded
+     */
+    const STATE_PROXY       = 4;
+    /**
+     * NEW TCLEAN
+     * a Doctrine_Record is in transient clean state when it is created and none of its fields are modified
+     */
+    const STATE_TCLEAN      = 5;
+    /**
+     * DELETED STATE
+     * a Doctrine_Record turns into deleted state when it is deleted
+     */
+    const STATE_DELETED     = 6;
+
+    /**
+     * FETCHMODE CONSTANTS
+     */
+
+    /**
+     * @var object Doctrine_Table $table    the factory that created this data access object
+     */
+    protected $table;
+    /**
+     * @var integer $id                     the primary key of this object
+     */
+    protected $id;
+    /**
+     * @var array $data                     the record data
+     */
+    protected $data       = array();
+
+    /**
+     * @var array $modified                 an array containing properties that have been modified
+     */
+    private $modified   = array();
+    /**
+     * @var integer $state                  the state of this record
+     * @see STATE_* constants
+     */
+    private $state;
+    /**
+     * @var array $collections              the collections this record is in
+     */
+    private $collections = array();
+    /**
+     * @var mixed $references               an array containing all the references
+     */
+    private $references  = array();
+    /**
+     * @var mixed $originals                an array containing all the original references
+     */
+    private $originals   = array();
+    /**
+     * @var integer $index                  this index is used for creating object identifiers
+     */
+    private static $index = 1;
+    /**
+     * @var Doctrine_Null $nullObject       a Doctrine_Null object used for extremely fast 
+     *                                      SQL null value testing
+     */
+    private static $null;
+    /**
+     * @var integer $oid                    object identifier
+     */
+    private $oid;
+
+    /**
+     * constructor
+     * @param Doctrine_Table $table         a Doctrine_Table object
+     * @throws Doctrine_Session_Exception   if object is created using the new operator and there are no
+     *                                      open sessions
+     */
+    public function __construct($table = null) {
+        if(isset($table) && $table instanceof Doctrine_Table) {
+            $this->table = $table;
+            $exists  = ( ! $this->table->isNewEntry());
+        } else {
+            $this->table = Doctrine_Manager::getInstance()->getCurrentSession()->getTable(get_class($this));
+            $exists  = false;
+        }
+
+        // Check if the current session has the records table in its registry
+        // If not this is record is only used for creating table definition and setting up
+        // relations.
+
+        if($this->table->getSession()->hasTable($this->table->getComponentName())) {
+
+            $this->oid = self::$index;
+    
+            self::$index++;
+
+            $keys = $this->table->getPrimaryKeys();
+
+            if( ! $exists) {
+                // listen the onPreCreate event
+                $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreCreate($this);
+            } else {
+                // listen the onPreLoad event
+                $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreLoad($this);
+            }
+            // get the data array
+            $this->data = $this->table->getData();
+    
+            // get the column count
+            $count = count($this->data);
+
+            // clean data array
+            $cols = $this->cleanData();
+
+            $this->prepareIdentifiers($exists);
+
+            if( ! $exists) {
+    
+                if($cols > 0)
+                    $this->state = Doctrine_Record::STATE_TDIRTY;
+                else
+                    $this->state = Doctrine_Record::STATE_TCLEAN;
+
+                // listen the onCreate event
+                $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onCreate($this);
+
+            } else {
+                $this->state      = Doctrine_Record::STATE_CLEAN;
+
+                if($count < $this->table->getColumnCount()) {
+                    $this->state  = Doctrine_Record::STATE_PROXY;
+                }
+
+
+                // listen the onLoad event
+                $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onLoad($this);
+            }
+            $this->table->getRepository()->add($this);
+        }
+    }
+    /**
+     * initNullObject
+     */
+    public static function initNullObject(Doctrine_Null $null) {
+        self::$null = $null;
+    }
+    /** 
+     * setUp
+     * implemented by child classes
+     */
+    public function setUp() { }
+    /**
+     * return the object identifier
+     *
+     * @return integer
+     */
+    public function getOID() {
+        return $this->oid;
+    }
+    /**
+     * cleanData
+     * modifies data array
+     * example:
+     *
+     * $data = array("name"=>"John","lastname"=> null,"id"=>1,"unknown"=>"unknown");
+     * $names = array("name","lastname","id");
+     * $data after operation:
+     * $data = array("name"=>"John","lastname" => array(),"id"=>1);
+     */
+    private function cleanData() {
+        $cols = 0;
+        $tmp  = $this->data;
+        
+        $this->data = array();
+
+        foreach($this->table->getColumnNames() as $name) {
+            if( ! isset($tmp[$name])) {
+                $this->data[$name] = self::$null;
+
+            } else {
+                $cols++;
+                $this->data[$name] = $tmp[$name];
+            }
+        }
+
+        return $cols;
+    }
+    /**
+     * prepares identifiers
+     *
+     * @return void
+     */
+    private function prepareIdentifiers($exists = true) {
+        switch($this->table->getIdentifierType()):
+            case Doctrine_Identifier::AUTO_INCREMENT:
+            case Doctrine_Identifier::SEQUENCE:
+                if($exists) {
+                    $name = $this->table->getIdentifier();
+
+                    if(isset($this->data[$name]))
+                        $this->id = $this->data[$name];
+    
+                    unset($this->data[$name]);
+                }
+            break;
+            case Doctrine_Identifier::COMPOSITE:
+                $names      = $this->table->getIdentifier();
+                $this->id   = array();
+
+                foreach($names as $name) {
+                    if($this->data[$name] === self::$null)
+                        $this->id[$name] = null;
+                    else 
+                        $this->id[$name] = $this->data[$name];
+                }
+            break;
+        endswitch;
+    }
+    /**
+     * this method is automatically called when this Doctrine_Record is serialized
+     *
+     * @return array
+     */
+    public function __sleep() {
+        $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onSleep($this);
+
+        $this->table = $this->table->getComponentName();
+        // unset all vars that won't need to be serialized
+
+        unset($this->modified);
+        unset($this->associations);
+        unset($this->state);
+        unset($this->collections);
+        unset($this->references);    
+        unset($this->originals);
+        unset($this->oid);
+
+        foreach($this->data as $k=>$v) {
+            if($v instanceof Doctrine_Record)
+                $this->data[$k] = array();
+        }
+        return array_keys(get_object_vars($this));
+
+    }
+    /**
+     * unseralize
+     * this method is automatically called everytime a Doctrine_Record object is unserialized
+     *
+     * @return void
+     */
+    public function __wakeup() {
+
+        $this->modified = array();
+        $this->state    = Doctrine_Record::STATE_CLEAN;
+
+        $name       = $this->table;
+
+        $manager    = Doctrine_Manager::getInstance();
+        $sess       = $manager->getCurrentSession();
+
+        $this->oid  = self::$index;
+        self::$index++;
+
+        $this->table = $sess->getTable($name);
+
+        $this->table->getRepository()->add($this);
+
+        $this->cleanData();
+
+        //unset($this->data['id']);
+        
+        $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onWakeUp($this);
+
+    }
+    /**
+     * addCollection
+     * @param Doctrine_Collection $collection
+     * @param mixed $key
+     */
+    final public function addCollection(Doctrine_Collection $collection,$key = null) {
+        if($key !== null) {
+            if(isset($this->collections[$key])) 
+                throw InvalidKeyException();
+
+            $this->collections[$key] = $collection;
+        } else {
+            $this->collections[] = $collection;
+        }
+    }
+    /**
+     * getCollection
+     * @param integer $key
+     * @return Doctrine_Collection
+     */
+    final public function getCollection($key) {
+        return $this->collections[$key];
+    }
+    /**
+     * hasCollections
+     * whether or not this record is part of a collection
+     *
+     * @return boolean
+     */
+    final public function hasCollections() {
+        return (! empty($this->collections));
+    }
+    /**
+     * getState
+     * returns the current state of the object
+     *
+     * @see Doctrine_Record::STATE_* constants
+     * @return integer
+     */
+    final public function getState() {
+        return $this->state;
+    }
+    /**
+     * refresh   
+     * refresh internal data from the database
+     *
+     * @return boolean
+     */
+    final public function refresh() {
+        $id = $this->getID();
+        if( ! is_array($id))
+            $id = array($id);
+
+        if(empty($id))
+            return false;
+
+        $id = array_values($id);
+
+        $query          = $this->table->getQuery()." WHERE ".implode(" = ? && ",$this->table->getPrimaryKeys())." = ?";
+        $this->data     = $this->table->getSession()->execute($query,$id)->fetch(PDO::FETCH_ASSOC);
+
+        $this->modified = array();
+        $this->cleanData();
+
+        $this->prepareIdentifiers();
+
+        $this->state    = Doctrine_Record::STATE_CLEAN;
+
+        return true;
+    }
+    /**
+     * factoryRefresh
+     * @throws Doctrine_Exception
+     * @return void
+     */
+    final public function factoryRefresh() {
+        $data = $this->table->getData();
+        $id   = $this->id;
+        
+        $this->prepareIdentifiers();
+
+        if($this->id != $id)
+            throw new Doctrine_Record_Exception();
+
+        $this->data     = $data;
+
+        $this->cleanData();
+
+        $this->state    = Doctrine_Record::STATE_CLEAN;
+        $this->modified = array();
+    }
+    /**
+     * return the factory that created this data access object
+     * @return object Doctrine_Table        a Doctrine_Table object
+     */
+    final public function getTable() {
+        return $this->table;
+    }
+    /**
+     * return all the internal data
+     * @return array                    an array containing all the properties
+     */
+    final public function getData() {
+        return $this->data;
+    }
+    /**
+     * get
+     * returns a value of a property or a related component 
+     *
+     * @param $name                     name of the property or related component
+     * @throws InvalidKeyException
+     * @return mixed
+     */
+    public function get($name) {
+        if(isset($this->data[$name])) {
+
+            // check if the property is null (= it is the Doctrine_Null object located in self::$null)
+            if($this->data[$name] === self::$null) {
+
+                // no use trying to load the data from database if the Doctrine_Record is not a proxy
+                if($this->state == Doctrine_Record::STATE_PROXY) {
+                    if( ! empty($this->collections)) {
+                        foreach($this->collections as $collection) {
+                            $collection->load($this);
+                        }
+                    } else {
+                        $this->refresh();
+                    }
+                    $this->state = Doctrine_Record::STATE_CLEAN;
+                }
+
+                if($this->data[$name] === self::$null)
+                    return null;
+            }
+            return $this->data[$name];
+        }
+
+        if($name == $this->table->getIdentifier())
+            return $this->id;
+
+        if( ! isset($this->references[$name]))
+                $this->loadReference($name);
+
+
+        return $this->references[$name];
+    }
+    /**
+     * rawSet
+     * doctrine uses this function internally, not recommended for developers
+     *
+     * @param mixed $name               name of the property or reference
+     * @param mixed $value              value of the property or reference
+     */
+    final public function rawSet($name,$value) {
+        if($value instanceof Doctrine_Record)
+            $id = $value->getID();
+
+        if( ! empty($id))
+            $value = $id;
+            
+        if(isset($this->data[$name])) {
+            if($this->data[$name] === self::$null) {
+                if($this->data[$name] !== $value) {
+                    switch($this->state):
+                        case Doctrine_Record::STATE_CLEAN:
+                            $this->state = Doctrine_Record::STATE_DIRTY;
+                        break;
+                        case Doctrine_Record::STATE_TCLEAN:
+                            $this->state = Doctrine_Record::STATE_TDIRTY;
+                    endswitch;
+                }
+            }
+
+            if($this->state == Doctrine_Record::STATE_TCLEAN)
+                $this->state = Doctrine_Record::STATE_TDIRTY;
+
+            $this->data[$name] = $value;
+            $this->modified[]  = $name;
+        }
+    }
+    /**
+     * set
+     * method for altering properties and Doctrine_Record references
+     *
+     * @param mixed $name               name of the property or reference
+     * @param mixed $value              value of the property or reference
+     * @throws InvalidKeyException
+     * @throws InvalidTypeException
+     * @return void
+     */
+    public function set($name,$value) {
+        if(isset($this->data[$name])) {
+
+            if($value instanceof Doctrine_Record) {
+                $id = $value->getID();
+                
+                if( ! empty($id)) 
+                    $value = $value->getID();
+            }
+            
+            $old = $this->get($name);
+
+            if($old !== $value) {
+                $this->data[$name] = $value;
+                $this->modified[]  = $name;
+                switch($this->state):
+                    case Doctrine_Record::STATE_CLEAN:
+                    case Doctrine_Record::STATE_PROXY:
+                        $this->state = Doctrine_Record::STATE_DIRTY;
+                    break;
+                    case Doctrine_Record::STATE_TCLEAN:
+                        $this->state = Doctrine_Record::STATE_TDIRTY;
+                    break;
+                endswitch;
+            }
+        } else {
+            // if not found, throws InvalidKeyException
+
+            $fk = $this->table->getForeignKey($name);
+
+            // one-to-many or one-to-one relation
+            if($fk instanceof Doctrine_ForeignKey || 
+               $fk instanceof Doctrine_LocalKey) {
+                switch($fk->getType()):
+                    case Doctrine_Relation::MANY_COMPOSITE:
+                    case Doctrine_Relation::MANY_AGGREGATE:
+                        // one-to-many relation found
+                        if( ! ($value instanceof Doctrine_Collection))
+                            throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting one-to-many references.");
+
+                        $value->setReference($this,$fk);
+                    break;
+                    case Doctrine_Relation::ONE_COMPOSITE:
+                    case Doctrine_Relation::ONE_AGGREGATE:
+                        // one-to-one relation found
+                        if( ! ($value instanceof Doctrine_Record))
+                            throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Record when setting one-to-one references.");
+
+                        if($fk->getLocal() == $this->table->getIdentifier()) {
+                            $this->references[$name]->set($fk->getForeign(),$this);
+                        } else {
+                            $this->set($fk->getLocal(),$value);
+                        }
+                    break;
+                endswitch;
+
+            } elseif($fk instanceof Doctrine_Association) {
+                // join table relation found
+                if( ! ($value instanceof Doctrine_Collection))
+                    throw new Doctrine_Exception("Couldn't call Doctrine::set(), second argument should be an instance of Doctrine_Collection when setting one-to-many references.");
+            }
+
+            $this->references[$name] = $value;
+        }
+    }
+    /**
+     * __isset
+     *
+     * @param string $name
+     * @return boolean
+     */
+    public function __isset($name) {
+        if(isset($this->data[$name]))
+            return true;
+
+        if(isset($this->references[$name]))
+            return true;
+
+        return false;
+    }
+    /**
+     * @param string $name
+     * @return void
+     */
+    public function __unset($name) {
+        if(isset($this->data[$name]))
+            $this->data[$name] = array();
+
+        // todo: what to do with references ?
+    }
+    /**
+     * applies the changes made to this object into database
+     * this method is smart enough to know if any changes are made
+     * and whether to use INSERT or UPDATE statement
+     *
+     * this method also saves the related composites
+     *
+     * @return void
+     */
+    final public function save() {
+        $this->table->getSession()->beginTransaction();
+
+        // listen the onPreSave event
+        $this->table->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($this);
+
+        $saveLater = $this->table->getSession()->saveRelated($this);
+
+        $this->table->getSession()->save($this);
+
+        foreach($saveLater as $fk) {
+            $table   = $fk->getTable();
+            $alias   = $this->table->getAlias($table->getComponentName());
+
+            if(isset($this->references[$alias])) {
+                $obj = $this->references[$alias];
+                $obj->save();
+            }
+        }
+
+        // save the MANY-TO-MANY associations
+
+        $this->saveAssociations();
+            
+        $this->table->getSession()->commit();
+    }
+    /**
+     * returns an array of modified fields and associated values
+     * @return array
+     */
+    final public function getModified() {
+        $a = array();
+
+        foreach($this->modified as $k=>$v) {
+            $a[$v] = $this->data[$v];
+        }
+        return $a;
+    }
+    /**
+     * returns an array of modified fields and values with data preparation
+     * adds column aggregation inheritance and converts Records into primary key values
+     *
+     * @return array
+     */
+    final public function getPrepared() {
+        $a = array();
+
+        foreach($this->table->getInheritanceMap() as $k => $v) {
+            $this->set($k,$v);                                                       	
+        }
+
+        foreach($this->modified as $k => $v) {
+            if($this->data[$v] instanceof Doctrine_Record) {
+                $this->data[$v] = $this->data[$v]->getID();
+            }
+            $a[$v] = $this->data[$v];
+        }
+
+        return $a;
+    }
+    /**
+     * this class implements countable interface
+     * @return integer                      the number of columns
+     */
+    public function count() {
+        return count($this->data);
+    }
+    /**
+     * getIterator
+     * @return ArrayIterator                an ArrayIterator that iterates through the data
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->data);
+    }
+    /**
+     * saveAssociations
+     * save the associations of many-to-many relations
+     * this method also deletes associations that do not exist anymore
+     * @return void
+     */
+    final public function saveAssociations() {
+        foreach($this->table->getForeignKeys() as $fk):
+            $table   = $fk->getTable();
+            $name    = $table->getComponentName();
+            $alias   = $this->table->getAlias($name);
+
+            if($fk instanceof Doctrine_Association) {
+                switch($fk->getType()):
+                    case Doctrine_Relation::MANY_COMPOSITE:
+
+                    break;
+                    case Doctrine_Relation::MANY_AGGREGATE:
+                        $asf     = $fk->getAssociationFactory();
+
+                        if(isset($this->references[$alias])) {
+
+                            $new = $this->references[$alias];
+
+                            if( ! isset($this->originals[$alias])) {
+                                $this->loadReference($alias);
+                            }
+
+                            $r = $this->getRelationOperations($alias,$new);
+
+                            foreach($r["delete"] as $record) {
+                                $query = "DELETE FROM ".$asf->getTableName()." WHERE ".$fk->getForeign()." = ?"
+                                                                            ." && ".$fk->getLocal()." = ?";
+                                $this->table->getSession()->execute($query, array($record->getID(),$this->getID()));
+                            }
+                            foreach($r["add"] as $record) {
+                                $reldao = $asf->create();
+                                $reldao->set($fk->getForeign(),$record);
+                                $reldao->set($fk->getLocal(),$this);
+                                $reldao->save();
+                            }  
+                            $this->originals[$alias] = clone $this->references[$alias];
+                        }
+                    break;
+                endswitch;
+            } elseif($fk instanceof Doctrine_ForeignKey || 
+                     $fk instanceof Doctrine_LocalKey) {
+                
+                switch($fk->getType()):
+                    case Doctrine_Relation::ONE_COMPOSITE:
+                        if(isset($this->originals[$alias]) && $this->originals[$alias]->getID() != $this->references[$alias]->getID())
+                            $this->originals[$alias]->delete();
+                    
+                    break;
+                    case Doctrine_Relation::MANY_COMPOSITE:
+                        if(isset($this->references[$alias])) {
+                            $new = $this->references[$alias];
+
+                            if( ! isset($this->originals[$alias]))
+                                $this->loadReference($alias);
+
+                            $r = $this->getRelationOperations($alias,$new);
+
+                            foreach($r["delete"] as $record) {
+                                $record->delete();
+                            }
+                            
+                            $this->originals[$alias] = clone $this->references[$alias];
+                        }
+                    break;
+                endswitch;
+            }
+        endforeach;
+    }
+    /**
+     * get the records that need to be added
+     * and/or deleted in order to change the old collection
+     * to the new one
+     *
+     * The algorithm here is very simple and definitely not
+     * the fastest one, since we have to iterate through the collections twice.
+     * the complexity of this algorithm is O(n^2)
+     *
+     * First we iterate through the new collection and get the
+     * records that do not exist in the old collection (Doctrine_Records that need to be added).
+     *
+     * Then we iterate through the old collection and get the records
+     * that do not exists in the new collection (Doctrine_Records that need to be deleted).
+     */
+    final public function getRelationOperations($name, Doctrine_Collection $new) {
+        $r["add"]    = array();
+        $r["delete"] = array();
+
+
+
+        foreach($new as $k=>$record) {
+
+            $found = false;
+
+            if($record->getID() !== null) {
+                foreach($this->originals[$name] as $k2 => $record2) {
+                    if($record2->getID() == $record->getID()) {
+                        $found = true;
+                        break;
+                    }
+                }
+            }
+            if( ! $found) {
+                $this->originals[$name][] = $record;
+                $r["add"][] = $record;
+            }
+        }
+
+        foreach($this->originals[$name] as $k => $record) {
+            if($record->getID() === null)
+                continue;
+
+            $found = false;
+            foreach($new as $k2=>$record2) {
+                if($record2->getID() == $record->getID()) {
+                    $found = true;
+                    break;
+                }
+            }
+
+            if( ! $found)  {
+                $r["delete"][] = $record;
+                unset($this->originals[$name][$k]);
+            }
+        }
+
+        return $r;
+    }
+    /**
+     * getOriginals
+     */
+    final public function getOriginals($name) {
+        if( ! isset($this->originals[$name]))
+            throw new InvalidKeyException();
+
+        return $this->originals[$name];
+    }
+    /**
+     * deletes this data access object and all the related composites
+     * this operation is isolated by a transaction
+     *
+     * this event can be listened by the onPreDelete and onDelete listeners
+     *
+     * @return boolean      true on success, false on failure
+     */
+    public function delete() {
+        $this->table->getSession()->delete($this);
+    }
+    /**
+     * returns a copy of this object
+     * @return DAO
+     */
+    public function copy() {
+        return $this->table->create($this->data);
+    }
+    /**
+     * @param integer $id
+     * @return void
+     */
+    final public function setID($id = false) {
+        if($id === false) {
+            $this->id       = false;
+            $this->cleanData();
+            $this->state    = Doctrine_Record::STATE_TCLEAN;
+            $this->modified = array();
+        } elseif($id === true) {
+            $this->prepareIdentifiers(false);
+            $this->state    = Doctrine_Record::STATE_CLEAN;
+            $this->modified = array();
+        } else {
+            $this->id       = $id;
+            $this->state    = Doctrine_Record::STATE_CLEAN;
+            $this->modified = array();
+        }
+    }
+    /**
+     * return the primary key(s) this object is pointing at
+     * @return mixed id
+     */
+    final public function getID() {
+        return $this->id;
+    }
+    /**
+     * getLast
+     * this method is used internally be Doctrine_Query
+     * it is needed to provide compatibility between
+     * records and collections
+     *
+     * @return Doctrine_Record
+     */
+    public function getLast() {
+        return $this;
+    }
+    /**
+     * hasRefence
+     * @param string $name
+     * @return boolean
+     */
+    public function hasReference($name) {
+        return isset($this->references[$name]);
+    }
+    /**
+     * initalizes a one-to-one relation
+     *
+     * @param Doctrine_Record $record
+     * @param Doctrine_Relation $connector
+     * @return void
+     */
+    public function initSingleReference(Doctrine_Record $record) {
+        $name = $this->table->getAlias($record->getTable()->getComponentName());
+        $this->references[$name] = $record;
+    }
+    /**
+     * initalizes a one-to-many / many-to-many relation
+     *
+     * @param Doctrine_Collection $coll
+     * @param Doctrine_Relation $connector
+     * @return void
+     */
+    public function initReference(Doctrine_Collection $coll, Doctrine_Relation $connector) {
+        $name = $this->table->getAlias($coll->getTable()->getComponentName());
+        $coll->setReference($this, $connector);
+        $this->references[$name] = $coll;
+        $this->originals[$name]  = clone $coll;
+    }
+    /**
+     * addReference
+     * @param Doctrine_Record $record
+     * @param mixed $key
+     * @return void
+     */
+    public function addReference(Doctrine_Record $record, $key = null) {
+        $name = $this->table->getAlias($record->getTable()->getComponentName());
+
+        $this->references[$name]->add($record, $key);
+        $this->originals[$name]->add($record, $key);
+    }
+    /**
+     * getReferences
+     * @return array    all references
+     */
+    public function getReferences() {
+        return $this->references;
+    }
+
+    /**
+     * @throws InvalidKeyException
+     * @param name
+     * @return void
+     */
+    final public function loadReference($name) {
+        $fk      = $this->table->getForeignKey($name);
+        $table   = $fk->getTable();
+
+        $local   = $fk->getLocal();
+        $foreign = $fk->getForeign();
+        $graph   = $table->getQueryObject();
+        $type    = $fk->getType();
+
+        switch($this->getState()):
+            case Doctrine_Record::STATE_TDIRTY:
+            case Doctrine_Record::STATE_TCLEAN:
+
+                if($type == Doctrine_Relation::ONE_COMPOSITE ||
+                   $type == Doctrine_Relation::ONE_AGGREGATE) {
+
+                    // ONE-TO-ONE
+                    $this->references[$name] = $table->create();
+
+                    if($fk instanceof Doctrine_ForeignKey) {
+                        $this->references[$name]->set($fk->getForeign(),$this);
+                    } else {
+                        $this->set($fk->getLocal(),$this->references[$name]);
+                    }
+                } else {
+                    $this->references[$name] = new Doctrine_Collection($table);
+                    if($fk instanceof Doctrine_ForeignKey) {
+                        // ONE-TO-MANY
+                        $this->references[$name]->setReference($this,$fk);
+                    }
+                    $this->originals[$name]  = new Doctrine_Collection($table);
+                }
+            break;
+            case Doctrine_Record::STATE_DIRTY:
+            case Doctrine_Record::STATE_CLEAN:
+            case Doctrine_Record::STATE_PROXY:
+
+                 switch($fk->getType()):
+                    case Doctrine_Relation::ONE_COMPOSITE:
+                    case Doctrine_Relation::ONE_AGGREGATE:
+
+                        // ONE-TO-ONE
+                        $id      = $this->get($local);
+
+                        if($fk instanceof Doctrine_LocalKey) {
+
+                            if(empty($id)) {
+                                $this->references[$name] = $table->create();
+                                $this->set($fk->getLocal(),$this->references[$name]);
+                            } else {
+                                try {
+                                    $this->references[$name] = $table->find($id);
+                                } catch(Doctrine_Find_Exception $e) {
+
+                                }
+                            }
+
+                        } elseif ($fk instanceof Doctrine_ForeignKey) {
+
+                            if(empty($id)) {
+                                $this->references[$name] = $table->create();
+                                $this->references[$name]->set($fk->getForeign(), $this);
+                            } else {
+                                $dql  = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$fk->getForeign()." = ?";
+                                $coll = $graph->query($dql, array($id));
+                                $this->references[$name] = $coll[0];
+                                $this->references[$name]->set($fk->getForeign(), $this);
+                            }
+                        }
+                    break;
+                    default:
+                        // ONE-TO-MANY
+                        if($fk instanceof Doctrine_ForeignKey) {
+                            $id      = $this->get($local);
+                            $query = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$fk->getForeign()." = ?";
+                            $coll = $graph->query($query,array($id));
+
+                            $this->references[$name] = $coll;
+                            $this->references[$name]->setReference($this, $fk);
+    
+                            $this->originals[$name]  = clone $coll;
+
+                        } elseif($fk instanceof Doctrine_Association) {            
+                            $asf     = $fk->getAssociationFactory();
+                            $query   = "SELECT ".$foreign." FROM ".$asf->getTableName()." WHERE ".$local." = ?";
+        
+                            $graph   = new Doctrine_Query($table->getSession());
+                            $query   = "FROM ".$table->getComponentName()." WHERE ".$table->getComponentName().".".$table->getIdentifier()." IN ($query)";
+
+                            $coll    = $graph->query($query, array($this->getID()));
+        
+                            $this->references[$name] = $coll;
+                            $this->originals[$name]  = clone $coll;
+
+                        }
+                 endswitch;
+            break;
+        endswitch;
+    }
+
+    /**
+     * binds One-to-One composite relation
+     *
+     * @param string $objTableName
+     * @param string $fkField
+     * @return void
+     */
+    final public function ownsOne($componentName,$foreignKey, $localKey = null) {
+        $this->table->bind($componentName,$foreignKey,Doctrine_Relation::ONE_COMPOSITE, $localKey);
+    }
+    /**
+     * binds One-to-Many composite relation
+     *
+     * @param string $objTableName
+     * @param string $fkField
+     * @return void
+     */
+    final public function ownsMany($componentName,$foreignKey, $localKey = null) {
+        $this->table->bind($componentName,$foreignKey,Doctrine_Relation::MANY_COMPOSITE, $localKey);
+    }
+    /**
+     * binds One-to-One aggregate relation
+     *
+     * @param string $objTableName
+     * @param string $fkField
+     * @return void
+     */
+    final public function hasOne($componentName,$foreignKey, $localKey = null) {
+        $this->table->bind($componentName,$foreignKey,Doctrine_Relation::ONE_AGGREGATE, $localKey);
+    }
+    /**
+     * binds One-to-Many aggregate relation
+     *
+     * @param string $objTableName
+     * @param string $fkField
+     * @return void
+     */
+    final public function hasMany($componentName,$foreignKey, $localKey = null) {
+        $this->table->bind($componentName,$foreignKey,Doctrine_Relation::MANY_AGGREGATE, $localKey);
+    }
+    /**
+     * setInheritanceMap
+     * @param array $inheritanceMap
+     * @return void
+     */
+    final public function setInheritanceMap(array $inheritanceMap) {
+        $this->table->setInheritanceMap($inheritanceMap);
+    }
+    /**
+     * setPrimaryKey
+     * @param mixed $key
+     */
+    final public function setPrimaryKey($key) {
+        $this->table->setPrimaryKey($key);
+    }
+    /**
+     * setTableName
+     * @param string $name              table name
+     * @return void
+     */
+    final public function setTableName($name) {
+        $this->table->setTableName($name);
+    }
+    /**
+     * setAttribute
+     * @param integer $attribute
+     * @param mixed $value
+     * @see Doctrine::ATTR_* constants
+     * @return void
+     */
+    final public function setAttribute($attribute, $value) {
+        $this->table->setAttribute($attribute,$value);
+    }
+    /**
+     * hasColumn
+     * sets a column definition
+     *
+     * @param string $name
+     * @param string $type
+     * @param integer $length
+     * @param mixed $options
+     * @return void
+     */
+    final public function hasColumn($name, $type, $length = 20, $options = "") {
+        $this->table->setColumn($name, $type, $length, $options);
+    }
+    /**
+     * returns a string representation of this object
+     */
+    public function __toString() {
+        return Doctrine_Lib::getRecordAsString($this);
+    }
+}
+?>
diff --git a/Doctrine/Relation.php b/Doctrine/Relation.php
new file mode 100644
index 000000000..47b67dfd3
--- /dev/null
+++ b/Doctrine/Relation.php
@@ -0,0 +1,101 @@
+<?php
+/**
+ * Doctrine_Relation
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+class Doctrine_Relation {
+    /**
+     * RELATION CONSTANTS
+     */
+
+    /**
+     * constant for ONE_TO_ONE and MANY_TO_ONE aggregate relationships
+     */
+    const ONE_AGGREGATE         = 0;
+    /**
+     * constant for ONE_TO_ONE and MANY_TO_ONE composite relationships
+     */
+    const ONE_COMPOSITE         = 1;
+    /**
+     * constant for MANY_TO_MANY and ONE_TO_MANY aggregate relationships
+     */
+    const MANY_AGGREGATE        = 2;
+    /**
+     * constant for MANY_TO_MANY and ONE_TO_MANY composite relationships
+     */
+    const MANY_COMPOSITE        = 3;
+    
+
+    /**
+     * @var Doctrine_Table $table     foreign factory
+     */
+    private $table;
+    /**
+     * @var string $local           local field
+     */
+    private $local;
+    /**
+     * @var string $foreign         foreign field
+     */
+    private $foreign;
+    /**
+     * @var integer $type           bind type
+     */
+    private $type;
+    /**
+     * @param Doctrine_Table $table
+     * @param string $local
+     * @param string $foreign
+     * @param integer $type
+     */
+    public function __construct(Doctrine_Table $table,$local,$foreign,$type) {
+        $this->table    = $table;
+        $this->local    = $local;
+        $this->foreign  = $foreign;
+        $this->type     = $type;
+    }
+    /**
+     * @return integer              bind type 1 or 0
+     */
+    public function getType() {
+        return $this->type;
+    }
+    /**
+     * @return object Doctrine_Table    foreign factory object
+     */
+    public function getTable() {
+        return $this->table;
+    }
+    /**
+     * @return string               the name of the local column
+     */
+    public function getLocal() {
+        return $this->local;
+    }
+    /**
+     * @return string               the name of the foreign column where
+     *                              the local column is pointing at
+     */
+    public function getForeign() {
+        return $this->foreign;
+    }
+    /**
+     * __toString
+     */
+    public function __toString() {
+        $r[] = "<pre>";
+        $r[] = "Class       : ".get_class($this);
+        $r[] = "Component   : ".$this->table->getComponentName();
+        $r[] = "Table       : ".$this->table->getTableName();
+        $r[] = "Local key   : ".$this->local;
+        $r[] = "Foreign key : ".$this->foreign;
+        $r[] = "Type        : ".$this->type;
+        $r[] = "</pre>";
+        return implode("\n", $r);
+    }
+}
+
+?>
diff --git a/Doctrine/Repository.php b/Doctrine/Repository.php
new file mode 100644
index 000000000..d3952d659
--- /dev/null
+++ b/Doctrine/Repository.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Doctrine_Repository
+ * each record is added into Doctrine_Repository at the same time they are created,
+ * loaded from the database or retrieved from the cache
+ *
+ * @author      Konsta Vesterinen
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * @version     1.0 alpha
+ *
+ */
+class Doctrine_Repository implements Countable, IteratorAggregate {
+    /**
+     * @var object Doctrine_Table $table
+     */
+    private $table;
+    /**
+     * @var array $registry
+     * an array of all records
+     * keys representing record object identifiers
+     */
+    private $registry = array();
+    /**
+     * constructor
+     */
+    public function __construct(Doctrine_Table $table) {
+        $this->table = $table;
+    }
+    /** 
+     * @return object Doctrine_Table
+     */
+    public function getTable() {
+        return $this->table;
+    }
+    /**
+     * add
+     * @param Doctrine_Record $record       record to be added into registry
+     */
+    public function add(Doctrine_Record $record) {
+        $oid = $record->getOID();
+
+        if(isset($this->registry[$oid]))
+            return false;
+
+        $this->registry[$oid] = $record;
+
+        return true;
+    }
+    /**
+     * get
+     * @param integer $oid
+     * @throws InvalidKeyException
+     */
+    public function get($oid) {
+        if( ! isset($this->registry[$oid]))
+            throw new InvalidKeyException();
+
+        return $this->registry[$oid];
+    }
+    /**
+     * count
+     * Doctrine_Registry implements interface Countable
+     * @return integer                      the number of records this registry has
+     */
+    public function count() {
+        return count($this->registry);
+    }
+    /**
+     * @param integer $oid                  object identifier
+     * @return boolean                      whether ot not the operation was successful
+     */
+    public function evict($oid) {
+        if( ! isset($this->registry[$oid]))
+            return false;
+            
+        unset($this->registry[$oid]);
+        return true;
+    }
+    /**
+     * @return integer                      number of records evicted
+     */
+    public function evictAll() {
+        $evicted = 0;
+        foreach($this->registry as $oid=>$record) {
+            if($this->evict($oid))
+                $evicted++;
+        }
+        return $evicted;
+    }
+    /**
+     * getIterator
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->registry);
+    }
+    /**
+     * contains
+     * @param integer $oid                  object identifier
+     */
+    public function contains($oid) {
+        return isset($this->registry[$oid]);
+    }
+    /**
+     * loadAll
+     * @return void
+     */
+    public function loadAll() {
+        $this->table->findAll();
+    }
+}
+?>
diff --git a/Doctrine/Session.php b/Doctrine/Session.php
new file mode 100644
index 000000000..8b02049ba
--- /dev/null
+++ b/Doctrine/Session.php
@@ -0,0 +1,934 @@
+<?php
+require_once("Configurable.php");
+require_once("Record.php");
+/**
+ * Doctrine_Session
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * @version     1.0 alpha
+ */
+abstract class Doctrine_Session extends Doctrine_Configurable implements Countable, IteratorAggregate {
+    /**
+     * Doctrine_Session is in open state when it is opened and there are no active transactions
+     */
+    const STATE_OPEN        = 0;
+    /**
+     * Doctrine_Session is in closed state when it is closed
+     */
+    const STATE_CLOSED      = 1;
+    /**
+     * Doctrine_Session is in active state when it has one active transaction
+     */
+    const STATE_ACTIVE      = 2;
+    /**
+     * Doctrine_Session is in busy state when it has multiple active transactions
+     */
+    const STATE_BUSY        = 3;
+    /**
+     * @var $dbh                            the database handle
+     */
+    private $dbh;
+    /**
+     * @see Doctrine_Session::STATE_* constants
+     * @var boolean $state                  the current state of the session
+     */
+    private $state              = 0;
+    /**
+     * @var integer $transaction_level      the nesting level of transactions, used by transaction methods
+     */
+    private $transaction_level  = 0;
+
+    /**
+     * @var PDO $cacheHandler               cache handler
+     */
+    private $cacheHandler;
+    /**
+     * @var array $tables                   an array containing all the initialized Doctrine_Table objects
+     *                                      keys representing Doctrine_Table component names and values as Doctrine_Table objects
+     */
+    protected $tables           = array();
+    /**
+     * @var Doctrine_Validator $validator   transaction validator
+     */
+    protected $validator;
+    /**
+     * @var array $update                   two dimensional pending update list, the records in
+     *                                      this list will be updated when transaction is committed
+     */
+    protected $update           = array();
+    /**
+     * @var array $insert                   two dimensional pending insert list, the records in
+     *                                      this list will be inserted when transaction is committed
+     */
+    protected $insert           = array();
+    /**
+     * @var array $delete                   two dimensional pending delete list, the records in
+     *                                      this list will be deleted when transaction is committed
+     */
+    protected $delete           = array();
+
+
+
+
+    /**
+     * the constructor
+     * @param PDO $pdo  -- database handle
+     */
+    public function __construct(Doctrine_Manager $manager,PDO $pdo) {
+        $this->dbh      = $pdo;
+
+        $this->setParent($manager);
+
+        $this->state = Doctrine_Session::STATE_OPEN;
+
+        $this->dbh->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
+        $this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);   
+
+        switch($this->getAttribute(Doctrine::ATTR_CACHE)):
+            case Doctrine::CACHE_SQLITE:
+                $dir = $this->getAttribute(Doctrine::ATTR_CACHE_DIR).DIRECTORY_SEPARATOR;
+                $dsn = "sqlite:".$dir."data.cache";
+
+                $this->cacheHandler = Doctrine_DB::getConn($dsn);
+                $this->cacheHandler->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+                $this->cacheHandler->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
+            break;
+        endswitch;
+
+        $this->getAttribute(Doctrine::ATTR_LISTENER)->onOpen($this);
+    }
+
+    public function getCacheHandler() {
+        return $this->cacheHandler;
+    }
+    /**
+     * returns the state of this session
+     *
+     * @see Doctrine_Session::STATE_* constants
+     * @return integer          the session state
+     */
+    public function getState() {
+        return $this->state;
+    }
+    /**
+     * returns the manager that created this session
+     *
+     * @return Doctrine_Manager
+     */
+    public function getManager() {
+        return $this->getParent();
+    }
+    /**
+     * returns the database handler of which this session uses
+     *
+     * @return object PDO       the database handler
+     */
+    public function getDBH() {
+        return $this->dbh;
+    }
+    /**
+     * query
+     * queries the database with Doctrine Query Language
+     *
+     * @param string $query         DQL query
+     * @param array $params         query parameters
+     */
+    final public function query($query,array $params = array()) {
+        $parser = new Doctrine_Query($this);
+
+        return $parser->query($query, $params);
+    }
+    /**
+     * queries the database with limit and offset
+     * added to the query and returns a PDOStatement object
+     *
+     * @param string $query
+     * @param integer $limit
+     * @param integer $offset
+     * @return PDOStatement
+     */
+    public function select($query,$limit = 0,$offset = 0) {
+        if($limit > 0 || $offset > 0)
+            $query = $this->modifyLimitQuery($query,$limit,$offset);
+
+        return $this->dbh->query($query);
+    }
+    /**
+     * @param string $query     sql query
+     * @param array $params     query parameters
+     *
+     * @return PDOStatement
+     */
+    public function execute($query, array $params = array()) {
+        if( ! empty($params)) {
+            $stmt = $this->dbh->prepare($query);
+            $stmt->execute($params);
+            return $stmt;
+        } else {
+            return $this->dbh->query($query);
+        }
+    }
+    /**
+     * whether or not this session has table $name initialized
+     *
+     * @param $mixed $name
+     * @return boolean
+     */
+    public function hasTable($name) {
+        return isset($this->tables[$name]);
+    }
+    /**
+     * returns a table object for given component name
+     *
+     * @param string $name              component name
+     * @return object Doctrine_Table
+     */
+    public function getTable($name) {
+        if(isset($this->tables[$name]))
+            return $this->tables[$name];
+
+        $class = $name."Table";
+
+        if(class_exists($class) && in_array("Doctrine_Table", class_parents($class))) {
+            return new $class($name);
+        } else {
+
+            return new Doctrine_Table($name);
+        }
+    }
+    /**
+     * returns an array of all initialized tables
+     *
+     * @return array
+     */
+    public function getTables() {
+        return $this->tables;
+    }
+    /**
+     * returns an iterator that iterators through all 
+     * initialized table objects
+     *
+     * @return ArrayIterator
+     */
+    public function getIterator() {
+        return new ArrayIterator($this->tables);
+    }
+    /**
+     * returns the count of initialized table objects
+     *
+     * @return integer
+     */
+    public function count() {
+        return count($this->tables);
+    }
+    /**
+     * @param $objTable             a Doctrine_Table object to be added into registry
+     * @return boolean
+     */
+    public function addTable(Doctrine_Table $objTable) {
+        $name = $objTable->getComponentName();
+
+        if(isset($this->tables[$name]))
+            return false;
+
+        $this->tables[$name] = $objTable;
+        return true;
+    }
+    /**
+     * creates a record
+     *
+     * create                       creates a record
+     * @param string $name          component name
+     * @return Doctrine_Record      Doctrine_Record object
+     */
+    public function create($name) {
+        return $this->getTable($name)->create();
+    }
+
+
+    /**
+     * buildFlushTree
+     * builds a flush tree that is used in transactions
+     *
+     * @return array
+     */
+    public function buildFlushTree(array $tables) {
+        $tree = array();
+        foreach($tables as $k => $table) {
+            $k = $k.$table;
+            if( ! ($table instanceof Doctrine_Table))
+                $table = $this->getTable($table);
+
+            $nm     = $table->getComponentName();
+
+            $index  = array_search($nm,$tree);
+            if($index === false) {
+                $tree[] = $nm;
+                $index  = max(array_keys($tree));
+
+                //print "$k -- adding <b>$nm</b>...<br \>";
+            }
+
+            $rels = $table->getForeignKeys();
+            
+            // group relations
+            
+            foreach($rels as $key => $rel) {
+                if($rel instanceof Doctrine_ForeignKey) {
+                    unset($rels[$key]);
+                    array_unshift($rels, $rel);
+                }
+            }
+
+            foreach($rels as $rel) {
+                $name   = $rel->getTable()->getComponentName();
+                $index2 = array_search($name,$tree);
+                $type   = $rel->getType();
+
+                // skip self-referenced relations
+                if($name === $nm)
+                    continue;
+
+                if($rel instanceof Doctrine_ForeignKey) {
+                    if($index2 !== false) {
+                        if($index2 >= $index)
+                            continue;
+
+                        unset($tree[$index]);
+                        array_splice($tree,$index2,0,$nm);
+                        $index = $index2;
+                        
+                        //print "$k -- pushing $nm into $index2...<br \>";
+
+                    } else {
+                        $tree[] = $name;
+                        //print "$k -- adding $nm :$name...<br>";
+                    }
+
+                } elseif($rel instanceof Doctrine_LocalKey) {
+                    if($index2 !== false) {
+                        if($index2 <= $index)
+                            continue;
+
+                        unset($tree[$index2]);
+                        array_splice($tree,$index,0,$name);
+
+                        //print "$k -- pushing $name into <b>$index</b>...<br \>";
+
+                    } else {
+                        //array_splice($tree, $index, 0, $name);
+                        array_unshift($tree,$name);
+                        $index++;
+
+                        //print "$k -- pushing <b>$name</b> into 0...<br \>";
+                    }
+                } elseif($rel instanceof Doctrine_Association) {
+                    $t = $rel->getAssociationFactory();
+                    $n = $t->getComponentName();
+                    
+                    if($index2 !== false)
+                        unset($tree[$index2]);
+                    
+                    array_splice($tree,$index, 0,$name);
+                    $index++;
+
+                    $index3 = array_search($n,$tree);
+
+                    if($index3 !== false) {
+                        if($index3 >= $index)
+                            continue;
+
+                        unset($tree[$index]);
+                        array_splice($tree,$index3,0,$n);
+                        $index = $index2;
+
+                        //print "$k -- pushing $nm into $index3...<br \>";
+
+                    } else {
+                        $tree[] = $n;
+                        //print "$k -- adding $nm :$name...<br>";
+                    }
+                }
+                //print_r($tree);
+            }
+            //print_r($tree);
+
+        }
+        return array_values($tree);
+    }
+
+    /**
+     * flush                        
+     * saves all the records from all tables
+     * this operation is isolated using a transaction
+     *
+     * @return void
+     */
+    public function flush() {
+        $this->beginTransaction();
+        $this->saveAll();
+        $this->commit();
+    }
+    /**
+     * saveAll                      
+     * saves all the records from all tables
+     *
+     * @return void
+     */
+    private function saveAll() {
+        $tree = $this->buildFlushTree($this->tables);
+
+        foreach($tree as $name) {
+            $table = $this->tables[$name];
+
+            foreach($table->getRepository() as $record) {
+                $this->save($record);
+            }
+        }
+        foreach($tree as $name) {
+            $table = $this->tables[$name];
+            foreach($table->getRepository() as $record) {
+                $record->saveAssociations();
+            }
+        }
+    }
+    /**
+     * clear
+     * clears all repositories
+     *
+     * @return void
+     */
+    public function clear() {
+        foreach($this->tables as $k => $table) {
+            $table->getRepository()->evictAll();
+            $table->clear();
+        }
+        $this->tables = array();
+    }
+    /**
+     * close
+     * closes the session
+     *
+     * @return void
+     */
+    public function close() {
+        $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreClose($this);
+
+        $this->clear();
+        $this->state = Doctrine_Session::STATE_CLOSED;
+        
+        $this->getAttribute(Doctrine::ATTR_LISTENER)->onClose($this);
+    }
+    /**
+     * get the current transaction nesting level
+     *
+     * @return integer
+     */
+    public function getTransactionLevel() {
+        return $this->transaction_level;
+    }
+    /**
+     * beginTransaction
+     * starts a new transaction
+     * @return void
+     */
+    public function beginTransaction() {
+        if($this->transaction_level == 0) {
+
+            if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_PESSIMISTIC) {
+                $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this);
+                $this->dbh->beginTransaction();
+                $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this);
+            }
+            $this->state  = Doctrine_Session::STATE_ACTIVE;
+        } else {
+            $this->state = Doctrine_Session::STATE_BUSY;
+        }
+        $this->transaction_level++;
+    }
+    /**
+     * commits the current transaction
+     * if lockmode is optimistic this method starts a transaction 
+     * and commits it instantly
+     * @return void
+     */
+    public function commit() {
+
+        $this->transaction_level--;
+    
+        if($this->transaction_level == 0) {
+    
+    
+            if($this->getAttribute(Doctrine::ATTR_LOCKMODE) == Doctrine::LOCK_OPTIMISTIC) {
+                $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionBegin($this);
+    
+                $this->dbh->beginTransaction();
+    
+                $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionBegin($this);
+            }
+    
+            if($this->getAttribute(Doctrine::ATTR_VLD))
+                $this->validator = new Doctrine_Validator();
+
+            try {
+                
+                $this->bulkInsert();
+                $this->bulkUpdate();
+                $this->bulkDelete();
+
+                if($this->getAttribute(Doctrine::ATTR_VLD)) {
+                    if($this->validator->hasErrors()) {
+                        $this->rollback();
+                        throw new Doctrine_Validator_Exception($this->validator);
+                    }
+                }
+
+            } catch(PDOException $e) {
+                $this->rollback();
+
+                throw new Doctrine_Exception($e->getMessage());
+            }
+            $this->dbh->commit();
+            $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionCommit($this);
+
+            $this->delete = array();
+            $this->state  = Doctrine_Session::STATE_OPEN;
+    
+            $this->validator = null;
+    
+        } elseif($this->transaction_level == 1)
+            $this->state = Doctrine_Session::STATE_ACTIVE;
+    }
+    /**
+     * rollback
+     * rolls back all transactions
+     * @return void
+     */
+    public function rollback() {
+        $this->getAttribute(Doctrine::ATTR_LISTENER)->onPreTransactionRollback($this);
+
+        $this->transaction_level = 0;
+        $this->dbh->rollback();
+        $this->state = Doctrine_Session::STATE_OPEN;
+
+        $this->getAttribute(Doctrine::ATTR_LISTENER)->onTransactionRollback($this);
+    }
+    /**
+     * bulkInsert
+     * inserts all the objects in the pending insert list into database
+     * @return void
+     */
+    public function bulkInsert() {
+        if(empty($this->insert))
+            return false;
+
+        foreach($this->insert as $name => $inserts) {
+            if( ! isset($inserts[0]))
+                continue;
+
+            $record    = $inserts[0];
+            $table     = $record->getTable();
+            $seq       = $table->getSequenceName();
+
+            $increment = false;
+            $keys      = $table->getPrimaryKeys();
+            $id        = null;
+
+            if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
+                $increment = true;
+            }
+
+            foreach($inserts as $k => $record) {
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
+                // listen the onPreInsert event
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreInsert($record);
+
+
+                $this->insert($record);
+                if($increment) {
+                    if($k == 0) {
+                        // record uses auto_increment column
+    
+                        $id = $table->getMaxIdentifier();
+                    }
+    
+                    $record->setID($id);
+                    $id++;
+                } else
+                    $record->setID(true);
+
+                // listen the onInsert event
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onInsert($record);
+
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
+            }
+        }
+        $this->insert = array();
+        return true;
+    }
+    /**
+     * returns maximum identifier values
+     *
+     * @param array $names          an array of component names
+     * @return array
+     */   
+    public function getMaximumValues(array $names) {
+        $values = array();
+        foreach($names as $name) {
+            $table     = $this->tables[$name];
+            $keys      = $table->getPrimaryKeys();
+            $tablename = $table->getTableName();
+
+            if(count($keys) == 1 && $keys[0] == $table->getIdentifier()) {
+                // record uses auto_increment column
+
+                $sql    = "SELECT MAX(".$table->getIdentifier().") FROM ".$tablename;
+                $stmt   = $this->dbh->query($sql);
+                $data   = $stmt->fetch(PDO::FETCH_NUM);
+                $values[$tablename] = $data[0];
+
+                $stmt->closeCursor();
+            }
+        }
+        return $values;
+    }
+    /**
+     * bulkUpdate
+     * updates all objects in the pending update list
+     *
+     * @return void
+     */
+    public function bulkUpdate() {
+        foreach($this->update as $name => $updates) {
+            $ids = array();
+
+            foreach($updates as $k => $record) {
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreSave($record);
+                // listen the onPreUpdate event
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onPreUpdate($record);
+
+                $this->update($record);
+                // listen the onUpdate event
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onUpdate($record);
+
+                $record->getTable()->getAttribute(Doctrine::ATTR_LISTENER)->onSave($record);
+
+                $ids[] = $record->getID();
+            }
+            if(isset($record))
+                $record->getTable()->getCache()->deleteMultiple($ids);
+        }
+        $this->update = array();
+    }
+    /**
+     * bulkDelete
+     * deletes all records from the pending delete list
+     *
+     * @return void
+     */
+    public function bulkDelete() {
+        foreach($this->delete as $name => $deletes) {
+            $record = false;
+            $ids    = array();
+            foreach($deletes as $k => $record) {
+                $ids[] = $record->getID();
+                $record->setID(false);
+            }
+            if($record instanceof Doctrine_Record) {
+                $table  = $record->getTable();
+
+                $params  = substr(str_repeat("?, ",count($ids)),0,-2);
+                $query   = "DELETE FROM ".$record->getTable()->getTableName()." WHERE ".$table->getIdentifier()." IN(".$params.")";
+                $this->execute($query,$ids);
+
+                $record->getTable()->getCache()->deleteMultiple($ids);
+            }
+        }
+        $this->delete = array();
+    }
+    /**
+     * saves a collection
+     *
+     * @param Doctrine_Collection $coll
+     * @return void
+     */
+    public function saveCollection(Doctrine_Collection $coll) {
+        $this->beginTransaction();
+
+        foreach($coll as $key=>$record):
+                $record->save();
+        endforeach;
+
+        $this->commit();
+    }
+    /**
+     * deletes all records from collection
+     *
+     * @param Doctrine_Collection $coll
+     * @return void
+     */
+    public function deleteCollection(Doctrine_Collection $coll) {
+        $this->beginTransaction();
+        foreach($coll as $k=>$record) {
+            $record->delete();
+        }
+        $this->commit();
+    }
+    /**
+     * saves the given record
+     *
+     * @param Doctrine_Record $record
+     * @return void
+     */
+    public function save(Doctrine_Record $record) {
+        switch($record->getState()):
+            case Doctrine_Record::STATE_TDIRTY:
+                $this->addInsert($record);
+            break;
+            case Doctrine_Record::STATE_DIRTY:
+            case Doctrine_Record::STATE_PROXY:
+                $this->addUpdate($record);
+            break;
+            case Doctrine_Record::STATE_CLEAN:
+            case Doctrine_Record::STATE_TCLEAN:
+                // do nothing
+            break;
+        endswitch;
+    }
+    /**
+     * saves all related records to $record
+     *
+     * @param Doctrine_Record $record
+     */
+    final public function saveRelated(Doctrine_Record $record) {
+        $saveLater = array();
+        foreach($record->getReferences() as $k=>$v) {
+            $fk = $record->getTable()->getForeignKey($k);
+            if($fk instanceof Doctrine_ForeignKey ||
+               $fk instanceof Doctrine_LocalKey) {
+                switch($fk->getType()):
+                    case Doctrine_Relation::ONE_COMPOSITE:
+                    case Doctrine_Relation::MANY_COMPOSITE:
+                        $local = $fk->getLocal();
+                        $foreign = $fk->getForeign();
+
+                        if($record->getTable()->hasPrimaryKey($fk->getLocal())) {
+                            switch($record->getState()):
+                                case Doctrine_Record::STATE_TDIRTY:
+                                case Doctrine_Record::STATE_TCLEAN:
+                                    $saveLater[$k] = $fk;
+                                break;
+                                case Doctrine_Record::STATE_CLEAN:
+                                case Doctrine_Record::STATE_DIRTY:
+                                    $v->save();    
+                                break;
+                            endswitch;
+                        } else {
+                            // ONE-TO-ONE relationship
+                            $obj = $record->get($fk->getTable()->getComponentName());
+
+                            if($obj->getState() != Doctrine_Record::STATE_TCLEAN)
+                                $obj->save();
+
+                        }
+                    break;
+                endswitch;
+            } elseif($fk instanceof Doctrine_Association) {
+                $v->save();
+            }
+        }
+        return $saveLater;
+    }
+    /**
+     * updates the given record
+     *
+     * @param Doctrine_Record $record
+     * @return boolean
+     */
+    private function update(Doctrine_Record $record) {
+        $array = $record->getModified();
+
+        if(empty($array))
+            return false;
+
+        $set   = array();
+        foreach($array as $name => $value):
+                $set[] = $name." = ?";
+
+                if($value instanceof Doctrine_Record) {
+                    switch($value->getState()):
+                        case Doctrine_Record::STATE_TCLEAN:
+                        case Doctrine_Record::STATE_TDIRTY:
+                            $record->save();
+                        default:
+                            $array[$name] = $value->getID();
+                            $record->set($name, $value->getID());
+                    endswitch;
+                }
+        endforeach;
+
+        if(isset($this->validator)) {
+            if( ! $this->validator->validateRecord($record)) {
+                return false;
+            }
+        }
+
+        $params   = array_values($array);
+        $id       = $record->getID();
+
+
+        if( ! is_array($id))
+            $id = array($id);
+
+        $id     = array_values($id);
+        $params = array_merge($params, $id);
+
+
+        $sql  = "UPDATE ".$record->getTable()->getTableName()." SET ".implode(", ",$set)." WHERE ".implode(" = ? && ",$record->getTable()->getPrimaryKeys())." = ?";
+
+        $stmt = $this->dbh->prepare($sql);
+        $stmt->execute($params);
+
+        $record->setID(true);
+
+        return true;
+    }
+    /**
+     * inserts a record into database
+     *
+     * @param Doctrine_Record $record
+     * @return boolean
+     */
+    private function insert(Doctrine_Record $record) {
+        $array = $record->getPrepared();
+
+        if(empty($array))
+            return false;
+
+        $seq = $record->getTable()->getSequenceName();
+
+        if( ! empty($seq)) {
+            $id             = $this->getNextID($seq);
+            $name           = $record->getTable()->getIdentifier();
+            $array[$name]   = $id;
+        }
+
+        if(isset($this->validator)) {
+            if( ! $this->validator->validateRecord($record)) {
+                return false;
+            }
+        }
+
+        $strfields = join(", ", array_keys($array));
+        $strvalues = substr(str_repeat("?, ",count($array)),0,-2);
+
+        $sql  = "INSERT INTO ".$record->getTable()->getTableName()." (".$strfields.") VALUES (".$strvalues.")";
+
+        $stmt = $this->dbh->prepare($sql);
+
+        $stmt->execute(array_values($array));
+
+        return true;
+    }
+    /**
+     * deletes all related composites
+     * this method is always called internally when a record is deleted
+     *
+     * @return void
+     */
+    final public function deleteComposites(Doctrine_Record $record) {
+        foreach($record->getTable()->getForeignKeys() as $fk) {
+            switch($fk->getType()):
+                case Doctrine_Relation::ONE_COMPOSITE:
+                case Doctrine_Relation::MANY_COMPOSITE:
+                    $obj = $record->get($fk->getTable()->getComponentName());
+                    $obj->delete();
+                break;
+            endswitch;
+        }
+    }
+    /**
+     * deletes this data access object and all the related composites
+     * this operation is isolated by a transaction
+     * 
+     * this event can be listened by the onPreDelete and onDelete listeners
+     *
+     * @return boolean      true on success, false on failure
+     */
+    final public function delete(Doctrine_Record $record) {
+        switch($record->getState()):
+            case Doctrine_Record::STATE_PROXY:
+            case Doctrine_Record::STATE_CLEAN:
+            case Doctrine_Record::STATE_DIRTY:
+                $this->beginTransaction();
+
+                $this->deleteComposites($record);
+                $this->addDelete($record);
+
+                $this->commit();
+                return true;
+            break;
+            default:
+                return false;
+        endswitch;    
+    }
+    /**
+     * adds record into pending insert list
+     * @param Doctrine_Record $record
+     */
+    public function addInsert(Doctrine_Record $record) {
+        $name = $record->getTable()->getComponentName();
+        $this->insert[$name][] = $record;
+    }
+    /**
+     * adds record into penging update list
+     * @param Doctrine_Record $record
+     */
+    public function addUpdate(Doctrine_Record $record) {
+        $name = $record->getTable()->getComponentName();
+        $this->update[$name][] = $record;
+    }
+    /**
+     * adds record into pending delete list
+     * @param Doctrine_Record $record
+     */
+    public function addDelete(Doctrine_Record $record) {
+        $name = $record->getTable()->getComponentName();
+        $this->delete[$name][] = $record;
+    }
+    /**
+     * returns the pending insert list
+     *
+     * @return array
+     */
+    public function getInserts() {
+        return $this->insert;
+    }
+    /**
+     * returns the pending update list
+     *
+     * @return array
+     */
+    public function getUpdates() {
+        return $this->update;
+    }
+    /**
+     * returns the pending delete list
+     *
+     * @return array
+     */
+    public function getDeletes() {
+        return $this->delete;
+    }
+
+    /**
+     * returns a string representation of this object
+     * @return string
+     */
+    public function __toString() {
+        return Doctrine_Lib::getSessionAsString($this);
+    }
+}
+?>
diff --git a/Doctrine/Statement.class.php b/Doctrine/Statement.class.php
new file mode 100644
index 000000000..39cd71374
--- /dev/null
+++ b/Doctrine/Statement.class.php
@@ -0,0 +1,188 @@
+<?php
+require_once("Access.php");
+/**
+ * Doctrine_Statement
+ *
+ * Doctrine_Statement is a wrapper for PDOStatement with DQL support
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+class Doctrine_Statement extends Doctrine_Access {
+    /**
+     * @var Doctrine_Query $query
+     */
+    private $query;
+    /**
+     * @var PDOStatement $stmt
+     */
+    private $stmt;
+    /**
+     * @var array $reserved
+     */
+    private $reserved = array();
+
+    /**
+     * constructor
+     *
+     * @param Doctrine_Query $query
+     * @param PDOStatement $stmt
+     */
+    public function __construct(Doctrine_Query $query, PDOStatement $stmt) {
+        $this->query = $query;
+        $this->stmt  = $stmt;
+    }
+    public function set($name, $value) { }
+    public function get($name) { }
+    /**
+     * getCollection
+     * returns Doctrine_Collection object
+     *
+     * @parma string $name              component name
+     * @param integer $index
+     * @return Doctrine_Collection
+     */
+    private function getCollection($name) {
+        $table = $this->session->getTable($name);
+        switch($this->fetchModes[$name]):
+            case Doctrine::FETCH_BATCH:
+                $coll = new Doctrine_Collection_Batch($table);
+            break;
+            case Doctrine::FETCH_LAZY:
+                $coll = new Doctrine_Collection_Lazy($table);
+            break;
+            case Doctrine::FETCH_OFFSET:
+                $coll = new Doctrine_Collection_Offset($table);
+            break;
+            case Doctrine::FETCH_IMMEDIATE:
+                $coll = new Doctrine_Collection_Immediate($table);
+            break;
+            case Doctrine::FETCH_LAZY_OFFSET:
+                $coll = new Doctrine_Collection_LazyOffset($table);
+            break;
+        endswitch;
+
+        $coll->populate($this);
+        return $coll;
+    }
+    /**
+     * execute
+     * executes the dql query, populates all collections
+     * and returns the root collection
+     *
+     * @param array $params
+     * @return Doctrine_Collection
+     */
+    public function execute($params = array()) {
+        switch(count($this->tables)):
+            case 0:
+                throw new DQLException();
+            break;
+            case 1:
+                $query = $this->getQuery();
+
+                $keys  = array_keys($this->tables);
+    
+                $name  = $this->tables[$keys[0]]->getComponentName();
+                $stmt  = $this->session->execute($query,$params);
+
+                while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
+                    foreach($data as $key => $value):
+                        $e = explode("__",$key);
+                        if(count($e) > 1) {
+                            $data[$e[1]] = $value;
+                        } else {
+                            $data[$e[0]] = $value;
+                        }
+                        unset($data[$key]);
+                    endforeach;
+                    $this->data[$name][] = $data;
+                endwhile;
+
+                return $this->getCollection($keys[0]);
+            break;
+            default:
+                $query = $this->getQuery();
+
+                $keys  = array_keys($this->tables);
+                $root  = $keys[0];
+                $stmt  = $this->session->execute($query,$params);
+                
+                $previd = array();
+
+                $coll = $this->getCollection($root);
+
+                $array = $this->parseData($stmt);
+
+                foreach($array as $data):
+
+                    /**
+                     * remove duplicated data rows and map data into objects
+                     */
+                    foreach($data as $key => $row):
+                        if(empty($row))
+                            continue;
+
+                        $key  = ucwords($key);
+                        $name = $this->tables[$key]->getComponentName();
+
+                        if( ! isset($previd[$name]))
+                            $previd[$name] = array();
+
+
+                        if($previd[$name] !== $row) {
+                            $this->tables[$name]->setData($row);
+                            $record = $this->tables[$name]->getRecord();
+
+                            if($name == $root) {
+                                $this->tables[$name]->setData($row);
+                                $record = $this->tables[$name]->getRecord();
+                                $coll->add($record);
+                            } else {
+                                $last = $coll->getLast();
+
+                                if( ! $last->hasReference($name)) {
+                                    $last->initReference($this->getCollection($name),$this->connectors[$name]);
+                                }
+                                $last->addReference($record);
+                            }
+                        }
+
+                        $previd[$name] = $row;
+                    endforeach;
+                endforeach;
+
+                return $coll;
+        endswitch;
+    }
+    /**
+     * parseData
+     * parses the data returned by PDOStatement
+     *
+     * @param PDOStatement $stmt
+     * @return array
+     */
+    public function parseData(PDOStatement $stmt) {
+        $array = array();
+        while($data = $stmt->fetch(PDO::FETCH_ASSOC)):
+            /**
+             * parse the data into two-dimensional array
+             */
+            foreach($data as $key => $value):
+                $e = explode("__",$key);
+
+                if(count($e) > 1) {
+                    $data[$e[0]][$e[1]] = $value;
+                } else {
+                    $data[0][$e[0]] = $value;
+                }
+                unset($data[$key]);
+            endforeach;
+            $array[] = $data;
+        endwhile;
+        $stmt->closeCursor();
+        return $array;
+    }
+}
+?>
diff --git a/Doctrine/Table.php b/Doctrine/Table.php
new file mode 100644
index 000000000..440f5281e
--- /dev/null
+++ b/Doctrine/Table.php
@@ -0,0 +1,870 @@
+<?php
+require_once("Exception/Find.class.php");
+require_once("Exception/Mapping.class.php");
+require_once("Exception/PrimaryKey.class.php");
+require_once("Configurable.php");
+/**
+ * Doctrine_Table   represents a database table
+ *                  each Doctrine_Table holds the information of foreignKeys and associations
+ * 
+ *
+ * @author      Konsta Vesterinen
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ * @version     1.0 alpha
+ */
+class Doctrine_Table extends Doctrine_Configurable {
+    /**
+     * @var boolean $isNewEntry                         whether ot not this table created a new record or not, used only internally
+     */
+    private $isNewEntry       = false;
+    /**
+     * @var array $data                                 temporary data which is then loaded into Doctrine_Record::$data
+     */
+    private $data             = array();
+    /**
+     * @var array $relations                            an array containing all the Doctrine_Relation objects for this table
+     */
+    private $relations        = array();
+    /**
+     * @var array $primaryKeys                          an array containing all primary key column names
+     */
+    private $primaryKeys      = array();
+    /**
+     * @var mixed $identifier
+     */
+    private $identifier;
+    /**
+     * @var integer $identifierType
+     */
+    private $identifierType;
+    /**
+     * @var string $query                               cached simple query
+     */
+    private $query;
+    /**
+     * @var Doctrine_Session $session                   Doctrine_Session object that created this table
+     */
+    private $session;
+    /**
+     * @var string $name                                name of the component, for example component name of the GroupTable is 'Group'
+     */
+    private $name;
+    /**
+     * @var string $tableName                           database table name, in most cases this is the same as component name but in some cases
+     *                                                  where one-table-multi-class inheritance is used this will be the name of the inherited table
+     */
+    private $tableName;
+    /**
+     * @var string $sequenceName                        Some databases need sequences instead of auto incrementation primary keys, you can set specific
+     *                                                  sequence for your table by calling setSequenceName()
+     */
+    private $sequenceName;
+    /**
+     * @var array $identityMap                          first level cache
+     */
+    private $identityMap        = array();
+    
+    private $repository;
+    
+    /**
+     * @var Doctrine_Cache $cache                       second level cache
+     */
+    private $cache;
+    /**
+     * @var array $columns                              an array of column definitions
+     */
+    private $columns;
+    /**
+     * @var array $bound                                bound relations
+     */
+    private $bound              = array();
+    /**
+     * @var array $boundAliases                         bound relation aliases
+     */
+    private $boundAliases       = array();
+    /**
+     * @var integer $columnCount                        cached column count
+     */
+    private $columnCount;
+
+
+    /**
+     * @var array $inheritanceMap                       inheritanceMap is used for inheritance mapping, keys representing columns and values
+     *                                                  the column values that should correspond to child classes
+     */
+    private $inheritanceMap     = array();
+    /**
+     * @var array $parents                              the parent classes of this component
+     */
+    private $parents            = array();
+
+    /**
+     * the constructor
+     * @throws Doctrine_ManagerException        if there are no opened sessions
+     * @throws Doctrine_TableException          if there is already an instance of this table
+     * @return void
+     */
+    public function __construct($name) {
+        $this->session = Doctrine_Manager::getInstance()->getCurrentSession();
+
+        $this->setParent($this->session);
+
+        $this->name = $name;
+
+        if( ! class_exists($name) || empty($name))
+            throw new Doctrine_Exception("Couldn't find class $name");
+
+        $record = new $name($this);
+
+
+        $names = array();
+
+        $class = $name;
+        
+
+
+        // get parent classes
+
+        do {
+            if($class == "Doctrine_Record") break;
+
+           	$name  = $class;
+            $names[] = $name;
+        } while($class = get_parent_class($class));
+
+        // reverse names
+        $names = array_reverse($names);
+
+
+        // create database table
+        if(method_exists($record,"setTableDefinition")) {
+            $record->setTableDefinition();
+
+            $this->columnCount = count($this->columns);
+
+            if(isset($this->columns)) {
+                $method    = new ReflectionMethod($this->name,"setTableDefinition");
+                $class     = $method->getDeclaringClass();
+
+                if( ! isset($this->tableName))
+                    $this->tableName = strtolower($class->getName());
+
+                switch(count($this->primaryKeys)):
+                    case 0:
+                        $this->columns = array_merge(array("id" => array("integer",11,"autoincrement|primary")), $this->columns);
+                        $this->primaryKeys[] = "id";
+                        $this->identifier = "id";
+                        $this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
+                    break;
+                    default:
+                        if(count($this->primaryKeys) > 1) {
+                            $this->identifier = $this->primaryKeys;
+                            $this->identifierType = Doctrine_Identifier::COMPOSITE;
+                                                         	
+                        } else {
+                            foreach($this->primaryKeys as $pk) {
+                                $o = $this->columns[$pk][2];
+                                $e = explode("|",$o);
+                                $found = false;
+    
+
+                                foreach($e as $option) {
+                                    if($found)
+                                        break;
+    
+                                    $e2 = explode(":",$option);
+    
+                                    switch(strtolower($e2[0])):
+                                        case "unique":
+                                            $this->identifierType = Doctrine_Identifier::UNIQUE;
+                                            $found = true;
+                                        break;
+                                        case "autoincrement":
+                                            $this->identifierType = Doctrine_Identifier::AUTO_INCREMENT;
+                                            $found = true;
+                                        break;
+                                        case "seq":
+                                            $this->identifierType = Doctrine_Identifier::SEQUENCE;
+                                            $found = true;
+                                        break;
+                                    endswitch;
+                                }
+                                if( ! isset($this->identifierType))
+                                    $this->identifierType = Doctrine_Identifier::NORMAL;
+                                     
+                                $this->identifier = $pk;
+                            }
+                        }
+                endswitch;
+
+                if($this->getAttribute(Doctrine::ATTR_CREATE_TABLES)) {
+                    $dict      = new Doctrine_DataDict($this->getSession()->getDBH());
+                    $dict->createTable($this->tableName, $this->columns);
+                }
+
+            }
+        } else {
+            throw new Doctrine_Exception("Class '$name' has no table definition.");
+        }
+        
+        $record->setUp();
+
+        // save parents
+        array_pop($names);
+        $this->parents   = $names;
+
+        $this->query     = "SELECT ".implode(", ",array_keys($this->columns))." FROM ".$this->getTableName();
+
+        // check if an instance of this table is already initialized
+        if( ! $this->session->addTable($this))
+            throw new Doctrine_Table_Exception();
+
+        $this->initComponents();
+    }
+    /**
+     * initializes components this table uses
+     *
+     * @return void
+     */
+    final public function initComponents() {
+        $this->repository = new Doctrine_Repository($this);
+        switch($this->getAttribute(Doctrine::ATTR_CACHE)):
+            case Doctrine::CACHE_SQLITE:
+                $this->cache       = new Doctrine_Cache_Sqlite($this);
+            break;
+            case Doctrine::CACHE_NONE:
+                $this->cache       = new Doctrine_Cache($this);
+            break;
+        endswitch;
+    }
+    /**
+     * @return Doctrine_Repository
+     */
+    public function getRepository() {
+        return $this->repository;
+    }
+    /**
+     * setColumn
+     * @param string $name
+     * @param string $type
+     * @param integer $length
+     * @param mixed $options
+     * @return void
+     */
+    final public function setColumn($name, $type, $length, $options = "") {
+        $this->columns[$name] = array($type,$length,$options);
+        
+        $e = explode("|",$options);
+        if(in_array("primary",$e)) {
+            $this->primaryKeys[] = $name;
+        }
+    }
+    /**
+     * @return mixed
+     */
+    final public function getIdentifier() {
+        return $this->identifier;
+    }
+    /**
+     * @return integer
+     */
+    final public function getIdentifierType() {
+        return $this->identifierType;
+    }
+    /**
+     * hasColumn
+     * @return boolean
+     */
+    final public function hasColumn($name) {
+        return isset($this->columns[$name]);
+    }
+    /**
+     * @param mixed $key
+     * @return void
+     */
+    final public function setPrimaryKey($key) {
+        switch(gettype($key)):
+            case "array":
+                $this->primaryKeys = array_values($key);
+            break;
+            case "string":
+                $this->primaryKeys[] = $key;
+            break;
+        endswitch;
+    }
+    /**
+     * returns all primary keys
+     * @return array
+     */
+    final public function getPrimaryKeys() {
+        return $this->primaryKeys;
+    }
+    /**
+     * @return boolean
+     */
+    final public function hasPrimaryKey($key) {
+        return in_array($key,$this->primaryKeys);
+    }
+    /**
+     * @param $sequence
+     * @return void
+     */
+    final public function setSequenceName($sequence) {
+        $this->sequenceName = $sequence;
+    }
+    /**
+     * @return string   sequence name
+     */
+    final public function getSequenceName() {
+        return $this->sequenceName;
+    }
+    /**
+     * setInheritanceMap
+     * @param array $inheritanceMap
+     * @return void
+     */
+    final public function setInheritanceMap(array $inheritanceMap) {
+        $this->inheritanceMap = $inheritanceMap;
+    }
+    /**
+     * @return array        inheritance map (array keys as fields)
+     */
+    final public function getInheritanceMap() {
+        return $this->inheritanceMap;
+    }
+    /**
+     * return all composite paths in the form [component1].[component2]. . .[componentN]
+     * @return array
+     */
+    final public function getCompositePaths() {
+        $array = array();
+        $name  = $this->getComponentName();
+        foreach($this->bound as $k=>$a) {
+            try {
+            $fk = $this->getForeignKey($k);
+            switch($fk->getType()):
+                case Doctrine_Relation::ONE_COMPOSITE:
+                case Doctrine_Relation::MANY_COMPOSITE:
+                    $n = $fk->getTable()->getComponentName();
+                    $array[] = $name.".".$n;
+                    $e = $fk->getTable()->getCompositePaths();
+                    if( ! empty($e)) {
+                        foreach($e as $name) {
+                            $array[] = $name.".".$n.".".$name;
+                        }
+                    }
+                break;
+            endswitch;
+            } catch(InvalidKeyException $e) {
+                                            	
+            }
+        }
+        return $array;
+    }
+    /**
+     * returns all bound relations
+     *
+     * @return array
+     */
+    final public function getBounds() {
+        return $this->bound;
+    }
+    /**
+     * returns a bound relation array
+     *
+     * @param string $name
+     * @return array
+     */
+    final public function getBound($name) {
+        if( ! isset($this->bound[$name])) 
+            throw new InvalidKeyException();
+
+        return $this->bound[$name];
+    }
+    /**
+     * returns a bound relation array
+     *
+     * @param string $name
+     * @return array
+     */
+    final public function getBoundForName($name) {
+        foreach($this->bound as $k => $bound) {
+            if($bound[3] == $name) {
+                return $this->bound[$k];
+            }
+        }
+        throw new InvalidKeyException();
+    }
+    /**
+     * returns the alias for given component name
+     *
+     * @param string $name
+     * @return string
+     */
+    final public function getAlias($name) {
+        if(isset($this->boundAliases[$name]))
+            return $this->boundAliases[$name];
+            
+        return $name;
+    }
+    /**
+     * returns component name for given alias
+     * 
+     * @param string $alias
+     * @return string
+     */
+    final public function getAliasName($alias) {
+        if($name = array_search($this->boundAliases,$alias))
+            return $name;
+            
+        throw new InvalidKeyException();
+    }
+    /**
+     * unbinds all relations
+     * 
+     * @return void
+     */
+    final public function unbindAll() {
+        $this->bound        = array();
+        $this->relations    = array();
+        $this->boundAliases = array();
+    }
+    /**
+     * unbinds a relation
+     * returns true on success, false on failure
+     *
+     * @param $name
+     * @return boolean
+     */
+    final public function unbind() {
+        if( ! isset($this->bound[$name]))
+            return false;
+        
+        unset($this->bound[$name]);
+
+        if(isset($this->relations[$name]))
+            unset($this->relations[$name]);
+
+        if(isset($this->boundAliases[$name]))
+            unset($this->boundAliases[$name]);
+
+        return true;
+    }
+    /**
+     * binds a relation
+     *
+     * @param string $name
+     * @param string $field
+     * @return void
+     */
+    final public function bind($name,$field,$type,$localKey) {
+        if(isset($this->relations[$name]))
+            throw new InvalidKeyException();
+
+        $e          = explode(" as ",$name);
+        $name       = $e[0];
+
+        if(isset($e[1])) {
+            $alias = $e[1];
+            $this->boundAliases[$name] = $alias;
+        } else
+            $alias = $name;
+
+
+        $this->bound[$alias] = array($field,$type,$localKey,$name);
+    }
+    /**
+     * getComponentName
+     * @return string                   the component name
+     */
+    final public function getComponentName() {
+        return $this->name;
+    }
+    /**
+     * @return Doctrine_Session
+     */
+    final public function getSession() {
+        return $this->session;
+    }
+    /**
+     * @return Doctrine_Cache
+     */
+    final public function getCache() {
+        return $this->cache;
+    }
+    /**
+     * @param string $name              component name of which a foreign key object is bound
+     * @return Doctrine_Relation
+     */
+    final public function getForeignKey($name) {
+        if(isset($this->relations[$name]))
+            return $this->relations[$name];
+
+        if(isset($this->bound[$name])) {
+            $type       = $this->bound[$name][1];
+            $local      = $this->bound[$name][2];
+            list($component, $foreign) = explode(".",$this->bound[$name][0]);
+            $alias      = $name;
+            $name       = $this->bound[$alias][3];
+
+            $table      = $this->session->getTable($name);
+
+            if($component == $this->name || in_array($component, $this->parents)) {
+
+                // ONE-TO-ONE
+                if($type == Doctrine_Relation::ONE_COMPOSITE ||
+                   $type == Doctrine_Relation::ONE_AGGREGATE) {
+                    if( ! isset($local))
+                        $local = $table->getIdentifier();
+
+                    $relation = new Doctrine_LocalKey($table,$foreign,$local,$type);
+                } else
+                    throw new Doctrine_Mapping_Exception("Only one-to-one relations are possible when local reference key is used.");
+
+            } elseif($component == $name || ($component == $alias && $name == $this->name)) {
+                if( ! isset($local))
+                    $local = $this->identifier;
+
+                // ONE-TO-MANY or ONE-TO-ONE
+                $relation = new Doctrine_ForeignKey($table,$local,$foreign,$type);
+
+            } else {
+                // MANY-TO-MANY
+                // only aggregate relations allowed
+
+                if($type != Doctrine_Relation::MANY_AGGREGATE) 
+                    throw new Doctrine_Mapping_Exception("Only aggregate relations are allowed for many-to-many relations");
+
+                $classes = array_merge($this->parents, array($this->name));
+
+                foreach(array_reverse($classes) as $class) {
+                    try {
+                        $bound = $table->getBoundForName($class);
+                        break;
+                    } catch(InvalidKeyException $exc) { }
+
+                }
+                if( ! isset($local))
+                    $local = $this->identifier;
+
+                $e2    = explode(".",$bound[0]);
+                $fields = explode("-",$e2[1]);
+
+                if($e2[0] != $component)
+                    throw new Doctrine_Mapping_Exception($e2[0]." doesn't match ".$component);
+
+                $associationTable = $this->session->getTable($e2[0]);
+
+                if(count($fields) > 1) {
+                    // SELF-REFERENCING THROUGH JOIN TABLE
+                    $this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$fields[0],Doctrine_Relation::MANY_COMPOSITE);
+                    
+                    $relation = new Doctrine_Association($table,$associationTable,$fields[0],$fields[1],$type);
+                } else {
+                    // NORMAL MANY-TO-MANY RELATIONSHIP
+                    $this->relations[$e2[0]] = new Doctrine_ForeignKey($associationTable,$local,$e2[1],Doctrine_Relation::MANY_COMPOSITE);
+
+                    $relation = new Doctrine_Association($table,$associationTable,$e2[1],$foreign,$type);
+                }
+
+            }
+            $this->relations[$alias] = $relation;
+            return $this->relations[$alias];
+        }
+        throw new InvalidKeyException();
+    }
+    /**
+     * returns an array containing all foreign key objects
+     *
+     * @return array
+     */
+    final public function getForeignKeys() {
+        $a = array();
+        foreach($this->bound as $k=>$v) {
+            $this->getForeignKey($k);
+        }
+
+        return $this->relations;
+    }
+    /**
+     * sets the database table name
+     *
+     * @param string $name              database table name
+     * @return void
+     */
+    final public function setTableName($name) {
+        $this->tableName = $name;
+    }
+
+    /**
+     * returns the database table name
+     *
+     * @return string
+     */
+    final public function getTableName() {
+        return $this->tableName;
+    }
+    /**
+     * create
+     * creates a new record
+     *
+     * @param $array                    an array where keys are field names and values representing field values
+     * @return Doctrine_Record
+     */
+    public function create(array $array = array()) {
+        $this->data         = $array;   
+        $this->isNewEntry   = true;
+        $record = new $this->name($this);
+        $this->isNewEntry   = false;
+        $this->data         = array();
+        return $record;
+    }
+    /**
+     * finds a record by its identifier
+     *
+     * @param $id                       database row id
+     * @throws Doctrine_Find_Exception
+     * @return Doctrine_Record          a record for given database identifier
+     */
+    public function find($id) {
+        if($id !== null) {
+            if( ! is_array($id))
+                $id = array($id);
+            else 
+                $id = array_values($id);
+
+            $query  = $this->query." WHERE ".implode(" = ? AND ",$this->primaryKeys)." = ?";
+            $query  = $this->applyInheritance($query);
+            
+            $params = array_merge($id, array_values($this->inheritanceMap));
+
+            $this->data = $this->session->execute($query,$params)->fetch(PDO::FETCH_ASSOC);
+
+            if($this->data === false)
+                throw new Doctrine_Find_Exception();
+        }
+        return $this->getRecord();
+    }
+    /**
+     * applyInheritance
+     * @param $where                    query where part to be modified
+     * @return string                   query where part with column aggregation inheritance added
+     */
+    final public function applyInheritance($where) {
+        if( ! empty($this->inheritanceMap)) {
+            $a = array();
+            foreach($this->inheritanceMap as $field => $value) {
+                $a[] = $field." = ?";
+            }
+            $i = implode(" AND ",$a);
+            $where .= " AND $i";
+        }
+        return $where;
+    }
+    /**
+     * findAll
+     * returns a collection of records
+     *
+     * @return Doctrine_Collection
+     */
+    public function findAll() {
+        $graph = new Doctrine_Query($this->session);
+        $users = $graph->query("FROM ".$this->name);
+        return $users;
+    }
+    /**
+     * findBySql
+     * finds records with given sql where clause
+     * returns a collection of records
+     *
+     * @param string $sql               SQL after WHERE clause
+     * @param array $params             query parameters
+     * @return Doctrine_Collection
+     */
+    public function findBySql($sql, array $params = array()) {
+        $q = new Doctrine_Query($this->session);
+        $users = $q->query("FROM ".$this->name." WHERE ".$sql, $params);
+        return $users;
+    }
+    /**
+     * clear
+     * clears the first level cache (identityMap)
+     *
+     * @return void
+     */
+    public function clear() {
+        $this->identityMap = array();
+    }
+    /**
+     * getRecord
+     * first checks if record exists in identityMap, if not
+     * returns a new record
+     *
+     * @return Doctrine_Record
+     */
+    public function getRecord() {
+        $key = $this->getIdentifier();
+
+        if( ! is_array($key))
+            $key = array($key);
+
+
+        foreach($key as $k) {
+            if( ! isset($this->data[$k]))
+                throw new Doctrine_Exception("No primary key found");
+            
+            $id[] = $this->data[$k];
+        }
+        $id = implode(' ', $id);
+
+        if(isset($this->identityMap[$id]))
+            $record = $this->identityMap[$id];
+        else {
+            $record = new $this->name($this);
+            $this->identityMap[$id] = $record;
+        }
+        $this->data = array();
+
+        return $record;
+    }
+    /**
+     * @param $id                       database row id
+     * @throws Doctrine_Find_Exception
+     * @return DAOProxy                 a proxy for given identifier
+     */
+    final public function getProxy($id = null) {
+        if($id !== null) {
+            $query = "SELECT ".implode(", ",$this->primaryKeys)." FROM ".$this->getTableName()." WHERE ".implode(" = ? && ",$this->primaryKeys)." = ?";
+            $query = $this->applyInheritance($query);
+            
+            $params = array_merge(array($id), array_values($this->inheritanceMap));
+
+            $this->data = $this->session->execute($query,$params)->fetch(PDO::FETCH_ASSOC);
+
+            if($this->data === false)
+                throw new Doctrine_Find_Exception();
+        }
+        return $this->getRecord();
+    }
+    /**
+     * getTableDescription
+     * @return Doctrine_Table_Description               
+     */
+    final public function getTableDescription() {
+        return $this->columns;
+    }
+    /**
+     * @return Doctrine_Query                           a Doctrine_Query object
+     */
+    public function getQueryObject() {
+        $graph = new Doctrine_Query($this->getSession());
+        $graph->load($this->getComponentName());
+        return $graph;
+    }
+    /**
+     * execute
+     * @param string $query
+     * @param array $array
+     * @param integer $limit
+     * @param integer $offset
+     */
+    public function execute($query, array $array = array(), $limit = null, $offset = null) {
+        $coll  = new Doctrine_Collection($this);
+        $query = $this->session->modifyLimitQuery($query,$limit,$offset);
+        if( ! empty($array)) {
+            $stmt = $this->session->getDBH()->prepare($query);
+            $stmt->execute($array);
+        } else {
+            $stmt = $this->session->getDBH()->query($query);
+        }
+        $data = $stmt->fetchAll(PDO::FETCH_ASSOC);
+        $stmt->closeCursor();
+
+        foreach($data as $row) {
+            $this->data = $row;
+            $record = $this->getRecord();
+            $coll->add($record);
+        }
+        return $coll;
+    }
+    /**
+     * @return integer
+     */
+    final public function getColumnCount() {
+        return $this->columnCount;                                       	
+    }
+    /**
+     * returns all columns and their definitions
+     *
+     * @return array
+     */
+    final public function getColumns() {
+        return $this->columns;
+    }
+    /**
+     * returns an array containing all the column names
+     *
+     * @return array
+     */
+    public function getColumnNames() {
+        return array_keys($this->columns);
+    }
+    /**
+     * setData
+     * doctrine uses this function internally
+     * users are strongly discouraged to use this function
+     *
+     * @param array $data               internal data
+     * @return void
+     */
+    public function setData(array $data) {
+        $this->data = $data;
+    }
+    /**
+     * returns the maximum primary key value
+     *
+     * @return integer
+     */
+    final public function getMaxIdentifier() {
+        $sql  = "SELECT MAX(".$this->getIdentifier().") FROM ".$this->getTableName();
+        $stmt = $this->session->getDBH()->query($sql);
+        $data = $stmt->fetch(PDO::FETCH_NUM);
+        return isset($data[0])?$data[0]:1;
+    }
+    /**
+     * return whether or not a newly created object is new or not
+     *
+     * @return boolean
+     */
+    final public function isNewEntry() {
+        return $this->isNewEntry;
+    }
+    /**
+     * returns simple cached query
+     *
+     * @return string
+     */
+    final public function getQuery() {
+        return $this->query;
+    }
+    /**
+     * returns internal data, used by Doctrine_Record instances 
+     * when retrieving data from database
+     *
+     * @return array
+     */
+    final public function getData() {
+        return $this->data;
+    }
+    /**
+     * returns a string representation of this object
+     *
+     * @return string
+     */
+    public function __toString() {
+        return Doctrine_Lib::getTableAsString($this);
+    }
+}
+?>
diff --git a/Doctrine/Validator.php b/Doctrine/Validator.php
new file mode 100644
index 000000000..e3548c9de
--- /dev/null
+++ b/Doctrine/Validator.php
@@ -0,0 +1,186 @@
+<?php
+/**
+ * Doctrine_Validator
+ * Doctrine_Session uses this class for transaction validation
+ *
+ * @package     Doctrine ORM
+ * @url         www.phpdoctrine.com
+ * @license     LGPL
+ */
+class Doctrine_Validator {
+    /**
+     * ERROR CONSTANTS
+     */
+
+    /**
+     * constant for length validation error
+     */
+    const ERR_LENGTH    = 0;
+    /**
+     * constant for type validation error
+     */
+    const ERR_TYPE      = 1;
+    /**
+     * constant for general validation error
+     */
+    const ERR_VALID     = 2;
+    /**
+     * constant for unique validation error
+     */
+    const ERR_UNIQUE    = 3;
+    /**
+     * constant for blank validation error
+     */
+    const ERR_BLANK     = 4;
+    /**
+     * constant for date validation error
+     */
+    const ERR_DATE      = 5;
+    /**
+     * constant for null validation error
+     */
+    const ERR_NULL      = 6;
+    /**
+     * constant for enum validation error
+     */
+    const ERR_ENUM      = 7;
+    /**
+     * constant for range validation error
+     */
+    const ERR_RANGE     = 8;
+
+
+
+    
+    /**
+     * @var array $stack            error stack
+     */
+    private $stack      = array();
+    /**
+     * @var array $validators       an array of validator objects
+     */
+    private static $validators = array();
+    /**
+     * returns a validator object
+     *
+     * @param string $name
+     * @return Doctrine_Validator_Interface
+     */
+    public static function getValidator($name) {
+        if( ! isset(self::$validators[$name])) {
+            $class = "Doctrine_Validator_".ucwords(strtolower($name));
+            if(class_exists($class)) {
+                self::$validators[$name] = new $class;
+            } elseif(class_exists($name."Validator")) {
+                self::$validators[$name] = new $name."Validator";
+            } else 
+                throw new Doctrine_Exception("Validator named '$name' not availible.");
+        }
+        return self::$validators[$name];
+    }
+    /**
+     * validates a given record and saves possible errors
+     * in Doctrine_Validator::$stack
+     *
+     * @param Doctrine_Record $record
+     * @return void
+     */
+    public function validateRecord(Doctrine_Record $record) {
+        $modified = $record->getModified();
+        $columns  = $record->getTable()->getColumns();
+        $name     = $record->getTable()->getComponentName();
+
+        $err      = array();
+        foreach($modified as $key => $value) {
+            $column = $columns[$key];
+
+            if(strlen($value) > $column[1]) {
+                $err[$key] = Doctrine_Validator::ERR_LENGTH;
+                continue;
+            }
+
+            if(self::gettype($value) !== $column[0]) {
+                $err[$key] = Doctrine_Validator::ERR_TYPE;
+                continue;
+            }
+
+            $e = explode("|",$column[2]);
+
+            foreach($e as $k => $arg) {
+                if(empty($arg) || $arg == "primary" || $arg == "protected" || $arg == "autoincrement")
+                    continue;
+
+                $args = explode(":",$arg);
+                if( ! isset($args[1])) 
+                    $args[1] = '';
+
+                $validator = self::getValidator($args[0]);
+                if( ! $validator->validate($record, $key, $value, $args[1])) {
+                    switch(strtolower($args[0])):
+                        case "unique":
+                            $err[$key] = Doctrine_Validator::ERR_UNIQUE;
+                        break;
+                        case "notnull":
+                            $err[$key] = Doctrine_Validator::ERR_NULL;
+                        break;
+                        case "notblank":
+                            $err[$key] = Doctrine_Validator::ERR_BLANK;
+                        break;
+                        case "enum":
+                            $err[$key] = Doctrine_Validator::ERR_VALID;
+                        break;
+                        default:
+                            $err[$key] = Doctrine_Validator::ERR_VALID;
+                        break;
+                    endswitch;
+                }
+                
+                // errors found quit validation looping for this column
+                if(isset($err[$key]))
+                    break;
+            }
+        }
+
+        if( ! empty($err)) {
+            $this->stack[$name][] = $err;
+            return false;
+        }
+        
+        return true;
+    }
+    /**
+     * whether or not this validator has errors
+     *
+     * @return boolean
+     */
+    public function hasErrors() {
+        return (count($this->stack) > 0);
+    }
+    /**
+     * returns the error stack
+     *
+     * @return array
+     */
+    public function getErrorStack() {
+        return $this->stack;
+    }
+    /**
+     * returns the type of loosely typed variable
+     *
+     * @param mixed $var
+     * @return string
+     */
+    public static function gettype($var) {
+        $type = gettype($var);
+        switch($type):
+            case "string":
+                if(preg_match("/^[0-9]+$/",$var)) return "integer";
+                elseif(is_numeric($var)) return "float";
+                else return $type;
+            break;
+            default:
+            return $type;
+        endswitch;
+    }
+}
+?>