From dc7161a9602cc3cf42cf611517d9f8e0f2880d1a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <xiam@menteslibres.org>
Date: Sun, 2 Dec 2012 07:19:32 -0600
Subject: [PATCH] Code cleaning.

---
 mysql/README.md     |   4 +-
 mysql/collection.go | 562 +++++++++++++++++++++++++++++++++
 mysql/mysql.go      | 741 +++++++-------------------------------------
 3 files changed, 671 insertions(+), 636 deletions(-)
 create mode 100644 mysql/collection.go

diff --git a/mysql/README.md b/mysql/README.md
index e85846f8..bdc77f25 100644
--- a/mysql/README.md
+++ b/mysql/README.md
@@ -1,4 +1,6 @@
 # gosexy/db/mysql
 
-Please read the full docs, acknowledgements and examples at [http://gosexy.org/db/wrappers/mysql](http://gosexy.org/db/wrappers/mysql).
+Get the full docs, acknowledgements and examples at [http://gosexy.org/db/wrappers/mysql][1].
+
+1: http://gosexy.org/db/wrappers/mysql
 
diff --git a/mysql/collection.go b/mysql/collection.go
new file mode 100644
index 00000000..b7f71a2a
--- /dev/null
+++ b/mysql/collection.go
@@ -0,0 +1,562 @@
+/*
+  Copyright (c) 2012 JosĂŠ Carlos Nieto, http://xiam.menteslibres.org/
+
+  Permission is hereby granted, free of charge, to any person obtaining
+  a copy of this software and associated documentation files (the
+  "Software"), to deal in the Software without restriction, including
+  without limitation the rights to use, copy, modify, merge, publish,
+  distribute, sublicense, and/or sell copies of the Software, and to
+  permit persons to whom the Software is furnished to do so, subject to
+  the following conditions:
+
+  The above copyright notice and this permission notice shall be
+  included in all copies or substantial portions of the Software.
+
+  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+  NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+  LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+  OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+  WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+package mysql
+
+import (
+	_ "code.google.com/p/go-mysql-driver/mysql"
+	//_ "github.com/ziutek/mymysql/godrv"
+	"database/sql"
+	"fmt"
+	"github.com/gosexy/db"
+	"github.com/gosexy/sugar"
+	"github.com/gosexy/to"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+// Returns all items from a query.
+func (self *Table) myFetchAll(rows sql.Rows) []db.Item {
+
+	items := []db.Item{}
+
+	columns, _ := rows.Columns()
+
+	for i, _ := range columns {
+		columns[i] = strings.ToLower(columns[i])
+	}
+
+	res := map[string]*sql.RawBytes{}
+
+	fargs := []reflect.Value{}
+
+	for _, name := range columns {
+		res[name] = &sql.RawBytes{}
+		fargs = append(fargs, reflect.ValueOf(res[name]))
+	}
+
+	sn := reflect.ValueOf(&rows)
+	fn := sn.MethodByName("Scan")
+
+	for rows.Next() {
+		item := db.Item{}
+
+		ret := fn.Call(fargs)
+
+		if ret[0].IsNil() != true {
+			panic(ret[0].Elem().Interface().(error))
+		}
+
+		for _, name := range columns {
+			strval := fmt.Sprintf("%s", *res[name])
+
+			switch self.types[name] {
+			case reflect.Uint64:
+				intval, _ := strconv.Atoi(strval)
+				item[name] = uint64(intval)
+			case reflect.Int64:
+				intval, _ := strconv.Atoi(strval)
+				item[name] = intval
+			case reflect.Float64:
+				floatval, _ := strconv.ParseFloat(strval, 10)
+				item[name] = floatval
+			default:
+				item[name] = strval
+			}
+		}
+
+		items = append(items, item)
+	}
+
+	return items
+}
+
+// Calls an internal function.
+func (self *Table) invoke(fn string, terms []interface{}) []reflect.Value {
+
+	reflected := reflect.ValueOf(self)
+	method := reflected.MethodByName(fn)
+
+	args := make([]reflect.Value, len(terms))
+
+	itop := len(terms)
+	for i := 0; i < itop; i++ {
+		args[i] = reflect.ValueOf(terms[i])
+	}
+
+	exec := method.Call(args)
+
+	return exec
+}
+
+// A helper for preparing queries that use SET.
+func (self *Table) compileSet(term db.Set) (string, db.SqlArgs) {
+	sql := []string{}
+	args := db.SqlArgs{}
+
+	for key, arg := range term {
+		sql = append(sql, fmt.Sprintf("%s = ?", key))
+		args = append(args, fmt.Sprintf("%v", arg))
+	}
+
+	return strings.Join(sql, ", "), args
+}
+
+// A helper for preparing queries that have conditions.
+func (self *Table) compileConditions(term interface{}) (string, db.SqlArgs) {
+	sql := []string{}
+	args := db.SqlArgs{}
+
+	switch term.(type) {
+	case []interface{}:
+		itop := len(term.([]interface{}))
+
+		for i := 0; i < itop; i++ {
+			rsql, rargs := self.compileConditions(term.([]interface{})[i])
+			if rsql != "" {
+				sql = append(sql, rsql)
+				for j := 0; j < len(rargs); j++ {
+					args = append(args, rargs[j])
+				}
+			}
+		}
+
+		if len(sql) > 0 {
+			return "(" + strings.Join(sql, " AND ") + ")", args
+		}
+	case db.Or:
+		itop := len(term.(db.Or))
+
+		for i := 0; i < itop; i++ {
+			rsql, rargs := self.compileConditions(term.(db.Or)[i])
+			if rsql != "" {
+				sql = append(sql, rsql)
+				for j := 0; j < len(rargs); j++ {
+					args = append(args, rargs[j])
+				}
+			}
+		}
+
+		if len(sql) > 0 {
+			return "(" + strings.Join(sql, " OR ") + ")", args
+		}
+	case db.And:
+		itop := len(term.(db.Or))
+
+		for i := 0; i < itop; i++ {
+			rsql, rargs := self.compileConditions(term.(db.Or)[i])
+			if rsql != "" {
+				sql = append(sql, rsql)
+				for j := 0; j < len(rargs); j++ {
+					args = append(args, rargs[j])
+				}
+			}
+		}
+
+		if len(sql) > 0 {
+			return "(" + strings.Join(sql, " AND ") + ")", args
+		}
+	case db.Cond:
+		return marshal(term.(db.Cond))
+	}
+
+	return "", args
+}
+
+// Converts db.Cond{} structures into SQL before processing them in a query.
+func marshal(where db.Cond) (string, []string) {
+	var placeholder string
+
+	placeholders := []string{}
+	args := []string{}
+
+	for key, val := range where {
+		chunks := strings.Split(strings.Trim(key, " "), " ")
+
+		if len(chunks) >= 2 {
+			placeholder = fmt.Sprintf("%s %s ?", chunks[0], chunks[1])
+		} else {
+			placeholder = fmt.Sprintf("%s = ?", chunks[0])
+		}
+
+		placeholders = append(placeholders, placeholder)
+		args = append(args, fmt.Sprintf("%v", val))
+	}
+
+	return strings.Join(placeholders, " AND "), args
+}
+
+// Deletes all the rows in the collection.
+func (self *Table) Truncate() error {
+
+	_, err := self.parent.myExec(
+		"Exec",
+		fmt.Sprintf("TRUNCATE TABLE `%s`", self.Name()),
+	)
+
+	return err
+}
+
+// Deletes all the rows in the collection that match certain conditions.
+func (self *Table) Remove(terms ...interface{}) error {
+
+	conditions, cargs := self.compileConditions(terms)
+
+	if conditions == "" {
+		conditions = "1 = 1"
+	}
+
+	_, err := self.parent.myExec(
+		"Exec",
+		fmt.Sprintf("DELETE FROM `%s`", self.Name()),
+		fmt.Sprintf("WHERE %s", conditions), cargs,
+	)
+
+	return err
+}
+
+// Modifies all the rows in the collection that match certain conditions.
+func (self *Table) Update(terms ...interface{}) error {
+	var fields string
+	var fargs db.SqlArgs
+
+	conditions, cargs := self.compileConditions(terms)
+
+	for _, term := range terms {
+		switch term.(type) {
+		case db.Set:
+			{
+				fields, fargs = self.compileSet(term.(db.Set))
+			}
+		}
+	}
+
+	if conditions == "" {
+		conditions = "1 = 1"
+	}
+
+	_, err := self.parent.myExec(
+		"Exec",
+		fmt.Sprintf("UPDATE `%s` SET %s", self.Name(), fields), fargs,
+		fmt.Sprintf("WHERE %s", conditions), cargs,
+	)
+
+	return err
+}
+
+// Returns all the rows in the collection that match certain conditions.
+func (self *Table) FindAll(terms ...interface{}) []db.Item {
+	var itop int
+
+	var relate interface{}
+	var relateAll interface{}
+
+	fields := "*"
+	conditions := ""
+	limit := ""
+	offset := ""
+	sort := ""
+
+	// Analyzing
+	itop = len(terms)
+
+	for i := 0; i < itop; i++ {
+		term := terms[i]
+
+		switch term.(type) {
+		case db.Limit:
+			limit = fmt.Sprintf("LIMIT %v", term.(db.Limit))
+		case db.Sort:
+			sortBy := []string{}
+			for k, v := range term.(db.Sort) {
+				v = strings.ToUpper(to.String(v))
+				if v == "-1" {
+					v = "DESC"
+				}
+				if v == "1" {
+					v = "ASC"
+				}
+				sortBy = append(sortBy, fmt.Sprintf("%s %s", k, v))
+			}
+			sort = fmt.Sprintf("ORDER BY %s", strings.Join(sortBy, ", "))
+		case db.Offset:
+			offset = fmt.Sprintf("OFFSET %v", term.(db.Offset))
+		case db.Fields:
+			fields = strings.Join(term.(db.Fields), ", ")
+		case db.Relate:
+			relate = term.(db.Relate)
+		case db.RelateAll:
+			relateAll = term.(db.RelateAll)
+		}
+	}
+
+	conditions, args := self.compileConditions(terms)
+
+	if conditions == "" {
+		conditions = "1 = 1"
+	}
+
+	rows, _ := self.parent.myExec(
+		"Query",
+		fmt.Sprintf("SELECT %s FROM `%s`", fields, self.Name()),
+		fmt.Sprintf("WHERE %s", conditions), args,
+		sort, limit, offset,
+	)
+
+	result := self.myFetchAll(rows)
+
+	var relations []sugar.Tuple
+	var rcollection db.Collection
+
+	// This query is related to other collections.
+	if relate != nil {
+		for rname, rterms := range relate.(db.Relate) {
+
+			rcollection = nil
+
+			ttop := len(rterms)
+			for t := ttop - 1; t >= 0; t-- {
+				rterm := rterms[t]
+				switch rterm.(type) {
+				case db.Collection:
+					{
+						rcollection = rterm.(db.Collection)
+					}
+				}
+			}
+
+			if rcollection == nil {
+				rcollection = self.parent.ExistentCollection(rname)
+			}
+
+			relations = append(relations, sugar.Tuple{"all": false, "name": rname, "collection": rcollection, "terms": rterms})
+		}
+	}
+
+	if relateAll != nil {
+		for rname, rterms := range relateAll.(db.RelateAll) {
+			rcollection = nil
+
+			ttop := len(rterms)
+			for t := ttop - 1; t >= 0; t-- {
+				rterm := rterms[t]
+				switch rterm.(type) {
+				case db.Collection:
+					{
+						rcollection = rterm.(db.Collection)
+					}
+				}
+			}
+
+			if rcollection == nil {
+				rcollection = self.parent.ExistentCollection(rname)
+			}
+
+			relations = append(relations, sugar.Tuple{"all": true, "name": rname, "collection": rcollection, "terms": rterms})
+		}
+	}
+
+	var term interface{}
+
+	jtop := len(relations)
+
+	itop = len(result)
+	items := make([]db.Item, itop)
+
+	for i := 0; i < itop; i++ {
+
+		item := db.Item{}
+
+		// Default values.
+		for key, val := range result[i] {
+			item[key] = val
+		}
+
+		// Querying relations
+		for j := 0; j < jtop; j++ {
+
+			relation := relations[j]
+
+			terms := []interface{}{}
+
+			ktop := len(relation["terms"].(db.On))
+
+			for k := 0; k < ktop; k++ {
+
+				//term = tcopy[k]
+				term = relation["terms"].(db.On)[k]
+
+				switch term.(type) {
+				// Just waiting for db.Cond statements.
+				case db.Cond:
+					{
+						for wkey, wval := range term.(db.Cond) {
+							//if reflect.TypeOf(wval).Kind() == reflect.String { // does not always work.
+							if reflect.TypeOf(wval).Name() == "string" {
+								// Matching dynamic values.
+								matched, _ := regexp.MatchString("\\{.+\\}", wval.(string))
+								if matched {
+									// Replacing dynamic values.
+									kname := strings.Trim(wval.(string), "{}")
+									term = db.Cond{wkey: item[kname]}
+								}
+							}
+						}
+					}
+				}
+				terms = append(terms, term)
+			}
+
+			// Executing external query.
+			if relation["all"] == true {
+				value := relation["collection"].(*Table).invoke("FindAll", terms)
+				item[relation["name"].(string)] = value[0].Interface().([]db.Item)
+			} else {
+				value := relation["collection"].(*Table).invoke("Find", terms)
+				item[relation["name"].(string)] = value[0].Interface().(db.Item)
+			}
+
+		}
+
+		// Appending to results.
+		items[i] = item
+	}
+
+	return items
+}
+
+// Returns the number of rows in the current collection that match certain conditions.
+func (self *Table) Count(terms ...interface{}) (int, error) {
+
+	terms = append(terms, db.Fields{"COUNT(1) AS _total"})
+
+	result := self.invoke("FindAll", terms)
+
+	if len(result) > 0 {
+		response := result[0].Interface().([]db.Item)
+		if len(response) > 0 {
+			val, _ := strconv.Atoi(response[0]["_total"].(string))
+			return val, nil
+		}
+	}
+
+	return 0, nil
+}
+
+// Returns true if the collection exists.
+func (self *Table) Exists() bool {
+	result, err := self.parent.myExec(
+		"Query",
+		fmt.Sprintf(`
+				SELECT table_name
+					FROM information_schema.tables
+				WHERE table_schema = '%s' AND table_name = '%s'
+			`,
+			self.parent.Name(),
+			self.Name(),
+		),
+	)
+	if err != nil {
+		panic(err.Error())
+	}
+	if result.Next() == true {
+		return true
+	}
+	return false
+}
+
+// Returns the first row in the collection that matches certain conditions.
+func (self *Table) Find(terms ...interface{}) db.Item {
+
+	var item db.Item
+
+	terms = append(terms, db.Limit(1))
+
+	result := self.invoke("FindAll", terms)
+
+	if len(result) > 0 {
+		response := result[0].Interface().([]db.Item)
+		if len(response) > 0 {
+			item = response[0]
+		}
+	}
+
+	return item
+}
+
+// Inserts rows into the currently active collection.
+func (self *Table) Append(items ...interface{}) ([]db.Id, error) {
+
+	ids := []db.Id{}
+
+	itop := len(items)
+
+	for i := 0; i < itop; i++ {
+
+		values := []string{}
+		fields := []string{}
+
+		item := items[i]
+
+		for field, value := range item.(db.Item) {
+			fields = append(fields, field)
+			values = append(values, toInternal(value))
+		}
+
+		_, err := self.parent.myExec(
+			"Exec",
+			"INSERT INTO",
+			self.Name(),
+			sqlFields(fields),
+			"VALUES",
+			sqlValues(values),
+		)
+
+		res, _ := self.parent.myExec(
+			"Query",
+			"SELECT LAST_INSERT_ID()",
+		)
+
+		var lastId string
+
+		res.Next()
+
+		res.Scan(&lastId)
+
+		ids = append(ids, db.Id(lastId))
+
+		if err != nil {
+			return ids, err
+		}
+
+	}
+
+	return ids, nil
+}
+
+// Returns the collection's name.
+func (self *Table) Name() string {
+	return self.name
+}
diff --git a/mysql/mysql.go b/mysql/mysql.go
index e111cb7a..402feb4a 100644
--- a/mysql/mysql.go
+++ b/mysql/mysql.go
@@ -29,11 +29,8 @@ import (
 	"database/sql"
 	"fmt"
 	"github.com/gosexy/db"
-	"github.com/gosexy/sugar"
-	"github.com/gosexy/to"
 	"reflect"
 	"regexp"
-	"strconv"
 	"strings"
 	"time"
 )
@@ -44,16 +41,30 @@ const dateFormat = "2006-01-02 15:04:05.000000000"
 const timeFormat = "%d:%02d:%02d.%09d"
 
 func init() {
-	db.Register("mysql", &MysqlDataSource{})
+	db.Register("mysql", &Source{})
 }
 
-type myQuery struct {
+// MySQl datasource.
+type Source struct {
+	session     *sql.DB
+	config      db.DataSource
+	collections map[string]db.Collection
+}
+
+// Mysql table/collection.
+type Table struct {
+	parent *Source
+	name   string
+	types  map[string]reflect.Kind
+}
+
+type sqlQuery struct {
 	Query   []string
 	SqlArgs []string
 }
 
-func myCompile(terms []interface{}) *myQuery {
-	q := &myQuery{}
+func sqlCompile(terms []interface{}) *sqlQuery {
+	q := &sqlQuery{}
 
 	q.Query = []string{}
 
@@ -78,15 +89,15 @@ func myCompile(terms []interface{}) *myQuery {
 	return q
 }
 
-func myTable(name string) string {
+func sqlTable(name string) string {
 	return name
 }
 
-func myFields(names []string) string {
+func sqlFields(names []string) string {
 	return "(" + strings.Join(names, ", ") + ")"
 }
 
-func myValues(values []string) db.SqlValues {
+func sqlValues(values []string) db.SqlValues {
 	ret := make(db.SqlValues, len(values))
 	for i, _ := range values {
 		ret[i] = values[i]
@@ -94,71 +105,9 @@ func myValues(values []string) db.SqlValues {
 	return ret
 }
 
-// Stores driver's session data.
-type MysqlDataSource struct {
-	session     *sql.DB
-	config      db.DataSource
-	collections map[string]db.Collection
-}
-
-func (my *MysqlDataSource) Name() string {
-	return my.config.Database
-}
-
-// Returns all items from a query.
-func (t *MysqlTable) myFetchAll(rows sql.Rows) []db.Item {
-
-	items := []db.Item{}
-
-	columns, _ := rows.Columns()
-
-	for i, _ := range columns {
-		columns[i] = strings.ToLower(columns[i])
-	}
-
-	res := map[string]*sql.RawBytes{}
-
-	fargs := []reflect.Value{}
-
-	for _, name := range columns {
-		res[name] = &sql.RawBytes{}
-		fargs = append(fargs, reflect.ValueOf(res[name]))
-	}
-
-	sn := reflect.ValueOf(&rows)
-	fn := sn.MethodByName("Scan")
-
-	for rows.Next() {
-		item := db.Item{}
-
-		ret := fn.Call(fargs)
-
-		if ret[0].IsNil() != true {
-			panic(ret[0].Elem().Interface().(error))
-		}
-
-		for _, name := range columns {
-			strval := fmt.Sprintf("%s", *res[name])
-
-			switch t.types[name] {
-			case reflect.Uint64:
-				intval, _ := strconv.Atoi(strval)
-				item[name] = uint64(intval)
-			case reflect.Int64:
-				intval, _ := strconv.Atoi(strval)
-				item[name] = intval
-			case reflect.Float64:
-				floatval, _ := strconv.ParseFloat(strval, 10)
-				item[name] = floatval
-			default:
-				item[name] = strval
-			}
-		}
-
-		items = append(items, item)
-	}
-
-	return items
+// Returns database name.
+func (self *Source) Name() string {
+	return self.config.Database
 }
 
 func toInternal(val interface{}) string {
@@ -192,12 +141,12 @@ func toNative(val interface{}) interface{} {
 }
 
 // Executes a database/sql method.
-func (my *MysqlDataSource) myExec(method string, terms ...interface{}) (sql.Rows, error) {
+func (self *Source) myExec(method string, terms ...interface{}) (sql.Rows, error) {
 
-	sn := reflect.ValueOf(my.session)
+	sn := reflect.ValueOf(self.session)
 	fn := sn.MethodByName(method)
 
-	q := myCompile(terms)
+	q := sqlCompile(terms)
 
 	if Debug == true {
 		fmt.Printf("Q: %v\n", q.Query)
@@ -226,56 +175,49 @@ func (my *MysqlDataSource) myExec(method string, terms ...interface{}) (sql.Rows
 	return sql.Rows{}, nil
 }
 
-// Represents a MySQL table.
-type MysqlTable struct {
-	parent *MysqlDataSource
-	name   string
-	types  map[string]reflect.Kind
-}
-
-// Configures and returns a MySQL database session.
+// Configures and returns a database session.
 func Session(config db.DataSource) db.Database {
-	my := &MysqlDataSource{}
-	my.config = config
-	my.collections = make(map[string]db.Collection)
-	return my
+	self := &Source{}
+	self.config = config
+	self.collections = make(map[string]db.Collection)
+	return self
 }
 
-// Closes a previously opened MySQL database session.
-func (my *MysqlDataSource) Close() error {
-	if my.session != nil {
-		return my.session.Close()
+// Closes a previously opened database session.
+func (self *Source) Close() error {
+	if self.session != nil {
+		return self.session.Close()
 	}
 	return nil
 }
 
-// Opens a connection.
-func (self *MysqlDataSource) Setup(config db.DataSource) error {
+// Configures a datasource and tries to open a connection.
+func (self *Source) Setup(config db.DataSource) error {
 	self.config = config
 	self.collections = make(map[string]db.Collection)
 	return self.Open()
 }
 
-// Tries to open a connection to the current MySQL session.
-func (my *MysqlDataSource) Open() error {
+// Tries to open a connection to the current datasource.
+func (self *Source) Open() error {
 	var err error
 
-	if my.config.Host == "" {
-		my.config.Host = "127.0.0.1"
+	if self.config.Host == "" {
+		self.config.Host = "127.0.0.1"
 	}
 
-	if my.config.Port == 0 {
-		my.config.Port = 3306
+	if self.config.Port == 0 {
+		self.config.Port = 3306
 	}
 
-	if my.config.Database == "" {
+	if self.config.Database == "" {
 		panic("Database name is required.")
 	}
 
-	conn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", my.config.User, my.config.Password, my.config.Host, my.config.Port, my.config.Database)
-	//conn := fmt.Sprintf("tcp:%s*%s/%s/%s", my.config.Host, my.config.Database, my.config.User, my.config.Password)
+	conn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", self.config.User, self.config.Password, self.config.Host, self.config.Port, self.config.Database)
+	//conn := fmt.Sprintf("tcp:%s*%s/%s/%s", self.config.Host, self.config.Database, self.config.User, self.config.Password)
 
-	my.session, err = sql.Open("mysql", conn)
+	self.session, err = sql.Open("mysql", conn)
 
 	if err != nil {
 		return err
@@ -285,29 +227,29 @@ func (my *MysqlDataSource) Open() error {
 }
 
 // Changes the active database
-func (my *MysqlDataSource) Use(database string) error {
-	my.config.Database = database
-	my.session.Query(fmt.Sprintf("USE %s", database))
+func (self *Source) Use(database string) error {
+	self.config.Database = database
+	self.session.Query(fmt.Sprintf("USE %s", database))
 	return nil
 }
 
 // Deletes the currently active database.
-func (my *MysqlDataSource) Drop() error {
-	my.session.Query(fmt.Sprintf("DROP DATABASE %s", my.config.Database))
+func (self *Source) Drop() error {
+	self.session.Query(fmt.Sprintf("DROP DATABASE %s", self.config.Database))
 	return nil
 }
 
-// Returns a *sql.DB object that represents an internal session.
-func (my *MysqlDataSource) Driver() interface{} {
-	return my.session
+// Returns the *sql.DB underlying driver.
+func (self *Source) Driver() interface{} {
+	return self.session
 }
 
-// Returns the list of MySQL tables in the current database.
-func (my *MysqlDataSource) Collections() []string {
+// Returns the names of all the collection on the current database.
+func (self *Source) Collections() []string {
 	var collections []string
 	var collection string
 
-	rows, err := my.session.Query("SHOW TABLES")
+	rows, err := self.session.Query("SHOW TABLES")
 
 	if err == nil {
 		for rows.Next() {
@@ -319,554 +261,83 @@ func (my *MysqlDataSource) Collections() []string {
 	return collections
 }
 
-// Calls an internal function.
-func (t *MysqlTable) invoke(fn string, terms []interface{}) []reflect.Value {
-
-	self := reflect.ValueOf(t)
-	method := self.MethodByName(fn)
-
-	args := make([]reflect.Value, len(terms))
-
-	itop := len(terms)
-	for i := 0; i < itop; i++ {
-		args[i] = reflect.ValueOf(terms[i])
-	}
-
-	exec := method.Call(args)
-
-	return exec
-}
-
-// A helper for preparing queries that use SET.
-func (t *MysqlTable) compileSet(term db.Set) (string, db.SqlArgs) {
-	sql := []string{}
-	args := db.SqlArgs{}
-
-	for key, arg := range term {
-		sql = append(sql, fmt.Sprintf("%s = ?", key))
-		args = append(args, fmt.Sprintf("%v", arg))
-	}
-
-	return strings.Join(sql, ", "), args
-}
-
-// A helper for preparing queries that have conditions.
-func (t *MysqlTable) compileConditions(term interface{}) (string, db.SqlArgs) {
-	sql := []string{}
-	args := db.SqlArgs{}
-
-	switch term.(type) {
-	case []interface{}:
-		itop := len(term.([]interface{}))
-
-		for i := 0; i < itop; i++ {
-			rsql, rargs := t.compileConditions(term.([]interface{})[i])
-			if rsql != "" {
-				sql = append(sql, rsql)
-				for j := 0; j < len(rargs); j++ {
-					args = append(args, rargs[j])
-				}
-			}
-		}
-
-		if len(sql) > 0 {
-			return "(" + strings.Join(sql, " AND ") + ")", args
-		}
-	case db.Or:
-		itop := len(term.(db.Or))
-
-		for i := 0; i < itop; i++ {
-			rsql, rargs := t.compileConditions(term.(db.Or)[i])
-			if rsql != "" {
-				sql = append(sql, rsql)
-				for j := 0; j < len(rargs); j++ {
-					args = append(args, rargs[j])
-				}
-			}
-		}
-
-		if len(sql) > 0 {
-			return "(" + strings.Join(sql, " OR ") + ")", args
-		}
-	case db.And:
-		itop := len(term.(db.Or))
-
-		for i := 0; i < itop; i++ {
-			rsql, rargs := t.compileConditions(term.(db.Or)[i])
-			if rsql != "" {
-				sql = append(sql, rsql)
-				for j := 0; j < len(rargs); j++ {
-					args = append(args, rargs[j])
-				}
-			}
-		}
-
-		if len(sql) > 0 {
-			return "(" + strings.Join(sql, " AND ") + ")", args
-		}
-	case db.Cond:
-		return t.marshal(term.(db.Cond))
-	}
-
-	return "", args
-}
-
-// Converts db.Cond{} structures into SQL before processing them in a query.
-func (t *MysqlTable) marshal(where db.Cond) (string, []string) {
-	var placeholder string
-
-	placeholders := []string{}
-	args := []string{}
-
-	for key, val := range where {
-		chunks := strings.Split(strings.Trim(key, " "), " ")
-
-		if len(chunks) >= 2 {
-			placeholder = fmt.Sprintf("%s %s ?", chunks[0], chunks[1])
-		} else {
-			placeholder = fmt.Sprintf("%s = ?", chunks[0])
-		}
-
-		placeholders = append(placeholders, placeholder)
-		args = append(args, fmt.Sprintf("%v", val))
-	}
-
-	return strings.Join(placeholders, " AND "), args
-}
-
-// Deletes all the rows in the table.
-func (t *MysqlTable) Truncate() error {
-
-	_, err := t.parent.myExec(
-		"Exec",
-		fmt.Sprintf("TRUNCATE TABLE %s", myTable(t.name)),
-	)
-
-	return err
-}
-
-// Deletes all the rows in the table that match certain conditions.
-func (t *MysqlTable) Remove(terms ...interface{}) error {
-
-	conditions, cargs := t.compileConditions(terms)
-
-	if conditions == "" {
-		conditions = "1 = 1"
-	}
-
-	_, err := t.parent.myExec(
-		"Exec",
-		fmt.Sprintf("DELETE FROM %s", myTable(t.name)),
-		fmt.Sprintf("WHERE %s", conditions), cargs,
-	)
-
-	return err
-}
-
-// Modifies all the rows in the table that match certain conditions.
-func (t *MysqlTable) Update(terms ...interface{}) error {
-	var fields string
-	var fargs db.SqlArgs
-
-	conditions, cargs := t.compileConditions(terms)
-
-	for _, term := range terms {
-		switch term.(type) {
-		case db.Set:
-			{
-				fields, fargs = t.compileSet(term.(db.Set))
-			}
-		}
-	}
-
-	if conditions == "" {
-		conditions = "1 = 1"
-	}
-
-	_, err := t.parent.myExec(
-		"Exec",
-		fmt.Sprintf("UPDATE %s SET %s", myTable(t.name), fields), fargs,
-		fmt.Sprintf("WHERE %s", conditions), cargs,
-	)
-
-	return err
-}
-
-// Returns all the rows in the table that match certain conditions.
-func (t *MysqlTable) FindAll(terms ...interface{}) []db.Item {
-	var itop int
-
-	var relate interface{}
-	var relateAll interface{}
-
-	fields := "*"
-	conditions := ""
-	limit := ""
-	offset := ""
-	sort := ""
-
-	// Analyzing
-	itop = len(terms)
-
-	for i := 0; i < itop; i++ {
-		term := terms[i]
-
-		switch term.(type) {
-		case db.Limit:
-			limit = fmt.Sprintf("LIMIT %v", term.(db.Limit))
-		case db.Sort:
-			sortBy := []string{}
-			for k, v := range term.(db.Sort) {
-				v = strings.ToUpper(to.String(v))
-				if v == "-1" {
-					v = "DESC"
-				}
-				if v == "1" {
-					v = "ASC"
-				}
-				sortBy = append(sortBy, fmt.Sprintf("%s %s", k, v))
-			}
-			sort = fmt.Sprintf("ORDER BY %s", strings.Join(sortBy, ", "))
-		case db.Offset:
-			offset = fmt.Sprintf("OFFSET %v", term.(db.Offset))
-		case db.Fields:
-			fields = strings.Join(term.(db.Fields), ", ")
-		case db.Relate:
-			relate = term.(db.Relate)
-		case db.RelateAll:
-			relateAll = term.(db.RelateAll)
-		}
-	}
-
-	conditions, args := t.compileConditions(terms)
-
-	if conditions == "" {
-		conditions = "1 = 1"
-	}
-
-	rows, _ := t.parent.myExec(
-		"Query",
-		fmt.Sprintf("SELECT %s FROM %s", fields, myTable(t.name)),
-		fmt.Sprintf("WHERE %s", conditions), args,
-		sort, limit, offset,
-	)
-
-	result := t.myFetchAll(rows)
-
-	var relations []sugar.Tuple
-	var rcollection db.Collection
-
-	// This query is related to other collections.
-	if relate != nil {
-		for rname, rterms := range relate.(db.Relate) {
-
-			rcollection = nil
-
-			ttop := len(rterms)
-			for t := ttop - 1; t >= 0; t-- {
-				rterm := rterms[t]
-				switch rterm.(type) {
-				case db.Collection:
-					{
-						rcollection = rterm.(db.Collection)
-					}
-				}
-			}
-
-			if rcollection == nil {
-				rcollection = t.parent.ExistentCollection(rname)
-			}
-
-			relations = append(relations, sugar.Tuple{"all": false, "name": rname, "collection": rcollection, "terms": rterms})
-		}
-	}
-
-	if relateAll != nil {
-		for rname, rterms := range relateAll.(db.RelateAll) {
-			rcollection = nil
-
-			ttop := len(rterms)
-			for t := ttop - 1; t >= 0; t-- {
-				rterm := rterms[t]
-				switch rterm.(type) {
-				case db.Collection:
-					{
-						rcollection = rterm.(db.Collection)
-					}
-				}
-			}
-
-			if rcollection == nil {
-				rcollection = t.parent.ExistentCollection(rname)
-			}
-
-			relations = append(relations, sugar.Tuple{"all": true, "name": rname, "collection": rcollection, "terms": rterms})
-		}
-	}
-
-	var term interface{}
-
-	jtop := len(relations)
-
-	itop = len(result)
-	items := make([]db.Item, itop)
-
-	for i := 0; i < itop; i++ {
-
-		item := db.Item{}
-
-		// Default values.
-		for key, val := range result[i] {
-			item[key] = val
-		}
-
-		// Querying relations
-		for j := 0; j < jtop; j++ {
-
-			relation := relations[j]
-
-			terms := []interface{}{}
-
-			ktop := len(relation["terms"].(db.On))
-
-			for k := 0; k < ktop; k++ {
-
-				//term = tcopy[k]
-				term = relation["terms"].(db.On)[k]
-
-				switch term.(type) {
-				// Just waiting for db.Cond statements.
-				case db.Cond:
-					{
-						for wkey, wval := range term.(db.Cond) {
-							//if reflect.TypeOf(wval).Kind() == reflect.String { // does not always work.
-							if reflect.TypeOf(wval).Name() == "string" {
-								// Matching dynamic values.
-								matched, _ := regexp.MatchString("\\{.+\\}", wval.(string))
-								if matched {
-									// Replacing dynamic values.
-									kname := strings.Trim(wval.(string), "{}")
-									term = db.Cond{wkey: item[kname]}
-								}
-							}
-						}
-					}
-				}
-				terms = append(terms, term)
-			}
-
-			// Executing external query.
-			if relation["all"] == true {
-				value := relation["collection"].(*MysqlTable).invoke("FindAll", terms)
-				item[relation["name"].(string)] = value[0].Interface().([]db.Item)
-			} else {
-				value := relation["collection"].(*MysqlTable).invoke("Find", terms)
-				item[relation["name"].(string)] = value[0].Interface().(db.Item)
-			}
-
-		}
-
-		// Appending to results.
-		items[i] = item
-	}
-
-	return items
-}
-
-// Returns the number of rows in the current table that match certain conditions.
-func (t *MysqlTable) Count(terms ...interface{}) (int, error) {
-
-	terms = append(terms, db.Fields{"COUNT(1) AS _total"})
-
-	result := t.invoke("FindAll", terms)
-
-	if len(result) > 0 {
-		response := result[0].Interface().([]db.Item)
-		if len(response) > 0 {
-			val, _ := strconv.Atoi(response[0]["_total"].(string))
-			return val, nil
-		}
-	}
-
-	return 0, nil
-}
-
-func (t *MysqlTable) Exists() bool {
-	result, err := t.parent.myExec(
-		"Query",
-		fmt.Sprintf(`
-				SELECT table_name
-					FROM information_schema.tables
-				WHERE table_schema = '%s' AND table_name = '%s'
-			`,
-			t.parent.Name(),
-			t.Name(),
-		),
-	)
-	if err != nil {
-		panic(err.Error())
-	}
-	if result.Next() == true {
-		return true
-	}
-	return false
-}
-
-// Returns the first row in the table that matches certain conditions.
-func (t *MysqlTable) Find(terms ...interface{}) db.Item {
-
-	var item db.Item
-
-	terms = append(terms, db.Limit(1))
-
-	result := t.invoke("FindAll", terms)
-
-	if len(result) > 0 {
-		response := result[0].Interface().([]db.Item)
-		if len(response) > 0 {
-			item = response[0]
-		}
-	}
-
-	return item
-}
-
-// Inserts rows into the currently active table.
-func (t *MysqlTable) Append(items ...interface{}) ([]db.Id, error) {
-
-	ids := []db.Id{}
-
-	itop := len(items)
-
-	for i := 0; i < itop; i++ {
-
-		values := []string{}
-		fields := []string{}
-
-		item := items[i]
-
-		for field, value := range item.(db.Item) {
-			fields = append(fields, field)
-			values = append(values, toInternal(value))
-		}
-
-		_, err := t.parent.myExec(
-			"Exec",
-			"INSERT INTO",
-			myTable(t.name),
-			myFields(fields),
-			"VALUES",
-			myValues(values),
-		)
-
-		res, _ := t.parent.myExec(
-			"Query",
-			"SELECT LAST_INSERT_ID()",
-		)
-
-		var lastId string
-
-		res.Next()
-
-		res.Scan(&lastId)
-
-		ids = append(ids, db.Id(lastId))
-
-		if err != nil {
-			return ids, err
-		}
-
-	}
-
-	return ids, nil
-}
-
-func (t *MysqlTable) Name() string {
-	return t.name
-}
-
-func (my *MysqlDataSource) ExistentCollection(name string) db.Collection {
-	col, err := my.Collection(name)
+// Returns a collection. Panics if the collection does not exists.
+func (self *Source) ExistentCollection(name string) db.Collection {
+	col, err := self.Collection(name)
 	if err != nil {
 		panic(err.Error())
 	}
 	return col
 }
 
-// Returns a MySQL table structure by name.
-func (my *MysqlDataSource) Collection(name string) (db.Collection, error) {
-	var rerr error
+// Returns a collection by name.
+func (self *Source) Collection(name string) (db.Collection, error) {
 
-	if col, ok := my.collections[name]; ok == true {
+	if col, ok := self.collections[name]; ok == true {
 		return col, nil
 	}
 
-	t := &MysqlTable{}
+	table := &Table{}
 
-	t.parent = my
-	t.name = name
+	table.parent = self
+	table.name = name
 
 	// Table exists?
-	if t.Exists() == false {
-		rerr = fmt.Errorf("Table %s does not exists.", name)
+	if table.Exists() == false {
+		return table, fmt.Errorf("Table %s does not exists.", name)
 	}
 
 	// Fetching table datatypes and mapping to internal gotypes.
-	rows, err := t.parent.myExec(
+	rows, err := table.parent.myExec(
 		"Query",
-		"SHOW COLUMNS FROM", t.name,
+		"SHOW COLUMNS FROM", table.Name(),
 	)
 
-	if err == nil {
-
-		columns := t.myFetchAll(rows)
+	if err != nil {
+		return table, err
+	}
 
-		pattern, _ := regexp.Compile("^([a-z]+)\\(?([0-9,]+)?\\)?\\s?([a-z]*)?")
+	columns := table.myFetchAll(rows)
 
-		t.types = make(map[string]reflect.Kind, len(columns))
+	pattern, _ := regexp.Compile("^([a-z]+)\\(?([0-9,]+)?\\)?\\s?([a-z]*)?")
 
-		for _, column := range columns {
-			cname := strings.ToLower(column["field"].(string))
-			ctype := strings.ToLower(column["type"].(string))
-			results := pattern.FindStringSubmatch(ctype)
+	table.types = make(map[string]reflect.Kind, len(columns))
 
-			// Default properties.
-			dextra := ""
-			dtype := "varchar"
+	for _, column := range columns {
+		cname := strings.ToLower(column["field"].(string))
+		ctype := strings.ToLower(column["type"].(string))
+		results := pattern.FindStringSubmatch(ctype)
 
-			dtype = results[1]
+		// Default properties.
+		dextra := ""
+		dtype := "varchar"
 
-			if len(results) > 3 {
-				dextra = results[3]
-			}
+		dtype = results[1]
 
-			vtype := reflect.String
-
-			// Guessing datatypes.
-			switch dtype {
-			case "tinyint", "smallint", "mediumint", "int", "bigint":
-				if dextra == "unsigned" {
-					vtype = reflect.Uint64
-				} else {
-					vtype = reflect.Int64
-				}
-			case "decimal", "float", "double":
-				vtype = reflect.Float64
-			}
+		if len(results) > 3 {
+			dextra = results[3]
+		}
 
-			/*
-			 fmt.Printf("Imported %v (from %v)\n", vtype, dtype)
-			*/
+		vtype := reflect.String
 
-			t.types[cname] = vtype
+		// Guessing datatypes.
+		switch dtype {
+		case "tinyint", "smallint", "mediumint", "int", "bigint":
+			if dextra == "unsigned" {
+				vtype = reflect.Uint64
+			} else {
+				vtype = reflect.Int64
+			}
+		case "decimal", "float", "double":
+			vtype = reflect.Float64
 		}
-	} else {
-		rerr = err
-	}
 
-	if rerr == nil {
-		my.collections[name] = t
+		/*
+		 fmt.Printf("Imported %v (from %v)\n", vtype, dtype)
+		*/
+
+		table.types[cname] = vtype
 	}
 
-	return t, rerr
+	return table, nil
 }
-- 
GitLab