From 9926fc5d0d7b67ce18bc7cc091fdd284c48fad0e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net>
Date: Thu, 28 May 2015 05:20:06 -0500
Subject: [PATCH] Adapting MySQL adapter to newer sqlgen.

---
 mysql/collection.go              | 228 ++++--------------
 mysql/database.go                | 396 +++++++++++++++----------------
 mysql/mysql.go                   |  69 ++++++
 mysql/result.go                  | 308 ------------------------
 mysql/{layout.go => template.go} |  62 +++--
 mysql/tx.go                      |  44 ----
 6 files changed, 330 insertions(+), 777 deletions(-)
 create mode 100644 mysql/mysql.go
 delete mode 100644 mysql/result.go
 rename mysql/{layout.go => template.go} (64%)
 delete mode 100644 mysql/tx.go

diff --git a/mysql/collection.go b/mysql/collection.go
index fb1ee860..8e8cf484 100644
--- a/mysql/collection.go
+++ b/mysql/collection.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
+// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -23,177 +23,26 @@ package mysql
 
 import (
 	"database/sql"
-	"fmt"
-	"reflect"
 	"strings"
 
 	"upper.io/db"
 	"upper.io/db/util/sqlgen"
 	"upper.io/db/util/sqlutil"
+	"upper.io/db/util/sqlutil/result"
 )
 
-const defaultOperator = `=`
-
 type table struct {
 	sqlutil.T
-	source *source
-	names  []string
-}
-
-func whereValues(term interface{}) (where sqlgen.Where, args []interface{}) {
-
-	args = []interface{}{}
-
-	switch t := term.(type) {
-	case []interface{}:
-		l := len(t)
-		where = make(sqlgen.Where, 0, l)
-		for _, cond := range t {
-			w, v := whereValues(cond)
-			args = append(args, v...)
-			where = append(where, w...)
-		}
-	case db.And:
-		and := make(sqlgen.And, 0, len(t))
-		for _, cond := range t {
-			k, v := whereValues(cond)
-			args = append(args, v...)
-			and = append(and, k...)
-		}
-		where = append(where, and)
-	case db.Or:
-		or := make(sqlgen.Or, 0, len(t))
-		for _, cond := range t {
-			k, v := whereValues(cond)
-			args = append(args, v...)
-			or = append(or, k...)
-		}
-		where = append(where, or)
-	case db.Raw:
-		if s, ok := t.Value.(string); ok == true {
-			where = append(where, sqlgen.Raw{s})
-		}
-	case db.Cond:
-		k, v := conditionValues(t)
-		args = append(args, v...)
-		for _, kk := range k {
-			where = append(where, kk)
-		}
-	case db.Constrainer:
-		k, v := conditionValues(t.Constraint())
-		args = append(args, v...)
-		for _, kk := range k {
-			where = append(where, kk)
-		}
-	default:
-		panic(fmt.Sprintf(db.ErrUnknownConditionType.Error(), reflect.TypeOf(t)))
-	}
-
-	return where, args
-}
-
-func interfaceArgs(value interface{}) (args []interface{}) {
-	if value == nil {
-		return nil
-	}
-
-	v := reflect.ValueOf(value)
-
-	switch v.Type().Kind() {
-	case reflect.Slice:
-		var i, total int
-
-		total = v.Len()
-		if total > 0 {
-			args = make([]interface{}, total)
-
-			for i = 0; i < total; i++ {
-				args[i] = v.Index(i).Interface()
-			}
-
-			return args
-		}
-		return nil
-	default:
-		args = []interface{}{value}
-	}
-
-	return args
-}
-
-func conditionValues(cond db.Cond) (columnValues sqlgen.ColumnValues, args []interface{}) {
-	args = []interface{}{}
-
-	for column, value := range cond {
-		var columnValue sqlgen.ColumnValue
-
-		// Guessing operator from input, or using a default one.
-		column := strings.TrimSpace(column)
-		chunks := strings.SplitN(column, ` `, 2)
-
-		columnValue.Column = sqlgen.Column{chunks[0]}
-
-		if len(chunks) > 1 {
-			columnValue.Operator = chunks[1]
-		} else {
-			columnValue.Operator = defaultOperator
-		}
-
-		switch value := value.(type) {
-		case db.Func:
-			// Catches functions.
-			v := interfaceArgs(value.Args)
-			columnValue.Operator = value.Name
-
-			if v == nil {
-				// A function with no arguments.
-				columnValue.Value = sqlgen.Value{sqlgen.Raw{`()`}}
-			} else {
-				// A function with one or more arguments.
-				columnValue.Value = sqlgen.Value{sqlgen.Raw{fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))}}
-			}
-
-			args = append(args, v...)
-		default:
-			// Catches everything else.
-			v := interfaceArgs(value)
-			l := len(v)
-			if v == nil || l == 0 {
-				// Nil value given.
-				columnValue.Value = sqlgen.Value{sqlgen.Raw{`NULL`}}
-			} else {
-				if l > 1 {
-					// Array value given.
-					columnValue.Value = sqlgen.Value{sqlgen.Raw{fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))}}
-				} else {
-					// Single value given.
-					columnValue.Value = sqlPlaceholder
-				}
-				args = append(args, v...)
-			}
-		}
-
-		columnValues = append(columnValues, columnValue)
-	}
-
-	return columnValues, args
+	*source
+	names []string
 }
 
-func (c *table) Find(terms ...interface{}) db.Result {
-	where, arguments := whereValues(terms)
-
-	result := &result{
-		table:     c,
-		where:     where,
-		arguments: arguments,
-	}
+var _ = db.Collection(&table{})
 
-	return result
-}
-
-func (c *table) tableN(i int) string {
-	if len(c.names) > i {
-		chunks := strings.SplitN(c.names[i], " ", 2)
+// tableN returns the nth name provided to the table.
+func (t *table) tableN(i int) string {
+	if len(t.names) > i {
+		chunks := strings.SplitN(t.names[i], " ", 2)
 		if len(chunks) > 0 {
 			return chunks[0]
 		}
@@ -201,55 +50,64 @@ func (c *table) tableN(i int) string {
 	return ""
 }
 
-// Deletes all the rows within the collection.
-func (c *table) Truncate() error {
+// Find creates a result set with the given conditions.
+func (t *table) Find(terms ...interface{}) db.Result {
+	where, arguments := sqlutil.ToWhereWithArguments(terms)
+	return result.NewResult(t, where, arguments)
+}
 
-	_, err := c.source.doExec(sqlgen.Statement{
-		Type:  sqlgen.SqlTruncate,
-		Table: sqlgen.Table{c.tableN(0)},
+// Truncate deletes all rows from the table.
+func (t *table) Truncate() error {
+	_, err := t.source.Exec(sqlgen.Statement{
+		Type:  sqlgen.Truncate,
+		Table: sqlgen.TableWithName(t.tableN(0)),
 	})
 
 	if err != nil {
 		return err
 	}
-
 	return nil
 }
 
 // Appends an item (map or struct) into the collection.
-func (c *table) Append(item interface{}) (interface{}, error) {
-
+func (t *table) Append(item interface{}) (interface{}, error) {
 	var pKey []string
-	var columns sqlgen.Columns
-	var values sqlgen.Values
-	var arguments []interface{}
 
-	cols, vals, err := c.FieldValues(item)
+	cols, vals, err := t.FieldValues(item)
 
 	if err != nil {
 		return nil, err
 	}
 
-	columns = make(sqlgen.Columns, 0, len(cols))
+	columns := new(sqlgen.Columns)
+
+	columns.Columns = make([]sqlgen.Fragment, 0, len(cols))
 	for i := range cols {
-		columns = append(columns, sqlgen.Column{cols[i]})
+		columns.Columns = append(columns.Columns, sqlgen.ColumnWithName(cols[i]))
 	}
 
+	values := new(sqlgen.Values)
+	var arguments []interface{}
+
 	arguments = make([]interface{}, 0, len(vals))
-	values = make(sqlgen.Values, 0, len(vals))
+	values.Values = make([]sqlgen.Fragment, 0, len(vals))
+
 	for i := range vals {
 		switch v := vals[i].(type) {
+		case *sqlgen.Value:
+			// Adding value.
+			values.Values = append(values.Values, v)
 		case sqlgen.Value:
 			// Adding value.
-			values = append(values, v)
+			values.Values = append(values.Values, &v)
 		default:
 			// Adding both value and placeholder.
-			values = append(values, sqlPlaceholder)
+			values.Values = append(values.Values, sqlPlaceholder)
 			arguments = append(arguments, v)
 		}
 	}
 
-	if pKey, err = c.source.getPrimaryKey(c.tableN(0)); err != nil {
+	if pKey, err = t.source.getPrimaryKey(t.tableN(0)); err != nil {
 		if err != sql.ErrNoRows {
 			// Can't tell primary key.
 			return nil, err
@@ -257,14 +115,14 @@ func (c *table) Append(item interface{}) (interface{}, error) {
 	}
 
 	stmt := sqlgen.Statement{
-		Type:    sqlgen.SqlInsert,
-		Table:   sqlgen.Table{c.tableN(0)},
+		Type:    sqlgen.Insert,
+		Table:   sqlgen.TableWithName(t.tableN(0)),
 		Columns: columns,
 		Values:  values,
 	}
 
 	var res sql.Result
-	if res, err = c.source.doExec(stmt, arguments...); err != nil {
+	if res, err = t.source.Exec(stmt, arguments...); err != nil {
 		return nil, err
 	}
 
@@ -318,13 +176,13 @@ func (c *table) Append(item interface{}) (interface{}, error) {
 }
 
 // Returns true if the collection exists.
-func (c *table) Exists() bool {
-	if err := c.source.tableExists(c.names...); err != nil {
+func (t *table) Exists() bool {
+	if err := t.source.tableExists(t.names...); err != nil {
 		return false
 	}
 	return true
 }
 
-func (c *table) Name() string {
-	return strings.Join(c.names, `, `)
+func (t *table) Name() string {
+	return strings.Join(t.names, `, `)
 }
diff --git a/mysql/database.go b/mysql/database.go
index 97bf5038..84bd8533 100644
--- a/mysql/database.go
+++ b/mysql/database.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
+// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -23,89 +23,49 @@ package mysql
 
 import (
 	"database/sql"
-	"os"
 	"strings"
 	"time"
 
-	// Importing MySQL driver.
-	_ "github.com/go-sql-driver/mysql"
+	_ "github.com/go-sql-driver/mysql" // MySQL driver.
 	"github.com/jmoiron/sqlx"
-	"upper.io/cache"
 	"upper.io/db"
 	"upper.io/db/util/schema"
 	"upper.io/db/util/sqlgen"
 	"upper.io/db/util/sqlutil"
+	"upper.io/db/util/sqlutil/tx"
 )
 
-const (
-	// Adapter is the public name of the adapter.
-	Adapter = `mysql`
-)
-
-var template *sqlgen.Template
-
 var (
-	sqlPlaceholder = sqlgen.Value{sqlgen.Raw{`?`}}
+	sqlPlaceholder = sqlgen.RawValue(`?`)
 )
 
 type source struct {
 	connURL db.ConnectionURL
 	session *sqlx.DB
-	tx      *tx
+	tx      *sqltx.Tx
 	schema  *schema.DatabaseSchema
 }
 
-type columnSchemaT struct {
-	Name string `db:"column_name"`
-}
-
-// Returns the string name of the database.
-func (s *source) Name() string {
-	return s.schema.Name
-}
-
-//  Ping verifies a connection to the database is still alive,
-//  establishing a connection if necessary.
-func (s *source) Ping() error {
-	return s.session.Ping()
+type tx struct {
+	*sqltx.Tx
+	*source
 }
 
-func (s *source) Clone() (db.Database, error) {
-	return s.clone()
-}
-
-func (s *source) Transaction() (db.Tx, error) {
-	var err error
-	var clone *source
-	var sqlTx *sqlx.Tx
-
-	if sqlTx, err = s.session.Beginx(); err != nil {
-		return nil, err
-	}
-
-	if clone, err = s.clone(); err != nil {
-		return nil, err
-	}
-
-	tx := &tx{source: clone, sqlTx: sqlTx}
-
-	clone.tx = tx
-
-	return tx, nil
-}
+var (
+	_ = db.Database(&source{})
+	_ = db.Tx(&tx{})
+)
 
-// Stores database settings.
-func (s *source) Setup(connURL db.ConnectionURL) error {
-	s.connURL = connURL
-	return s.Open()
+type columnSchemaT struct {
+	Name string `db:"column_name"`
 }
 
-// Returns the underlying *sqlx.DB instance.
+// Driver returns the underlying *sqlx.DB instance.
 func (s *source) Driver() interface{} {
 	return s.session
 }
 
-// Attempts to connect to a database using the stored settings.
+// Open attempts to connect to the database server using already stored settings.
 func (s *source) Open() error {
 	var err error
 
@@ -158,30 +118,38 @@ func (s *source) Open() error {
 	return nil
 }
 
-// Closes the current database session.
-func (s *source) Close() error {
-	if s.session != nil {
-		return s.session.Close()
-	}
-	return nil
+// Clone returns a cloned db.Database session, this is typically used for
+// transactions.
+func (s *source) Clone() (db.Database, error) {
+	return s.clone()
 }
 
-// Changes the active database.
-func (s *source) Use(database string) (err error) {
-	var conn ConnectionURL
+func (s *source) clone() (*source, error) {
+	src := &source{}
+	src.Setup(s.connURL)
 
-	if conn, err = ParseURL(s.connURL.String()); err != nil {
-		return err
+	if err := src.Open(); err != nil {
+		return nil, err
 	}
 
-	conn.Database = database
+	return src, nil
+}
 
-	s.connURL = conn
+// Ping checks whether a connection to the database is still alive by pinging
+// it, establishing a connection if necessary.
+func (s *source) Ping() error {
+	return s.session.Ping()
+}
 
-	return s.Open()
+// Close terminates the current database session.
+func (s *source) Close() error {
+	if s.session != nil {
+		return s.session.Close()
+	}
+	return nil
 }
 
-// Returns a collection instance by name.
+// Collection returns a table by name.
 func (s *source) Collection(names ...string) (db.Collection, error) {
 	var err error
 
@@ -190,7 +158,7 @@ func (s *source) Collection(names ...string) (db.Collection, error) {
 	}
 
 	if s.tx != nil {
-		if s.tx.done {
+		if s.tx.Done() {
 			return nil, sql.ErrTxDone
 		}
 	}
@@ -221,19 +189,7 @@ func (s *source) Collection(names ...string) (db.Collection, error) {
 	return col, nil
 }
 
-// Drops the currently active database.
-func (s *source) Drop() error {
-
-	_, err := s.doQuery(sqlgen.Statement{
-		Type:     sqlgen.SqlDropDatabase,
-		Database: sqlgen.Database{s.schema.Name},
-	})
-
-	return err
-}
-
-// Collections() Returns a list of non-system tables/collections contained
-// within the currently active database.
+// Collections returns a list of non-system tables from the database.
 func (s *source) Collections() (collections []string, err error) {
 
 	tablesInSchema := len(s.schema.Tables)
@@ -245,25 +201,23 @@ func (s *source) Collections() (collections []string, err error) {
 	}
 
 	stmt := sqlgen.Statement{
-		Type: sqlgen.SqlSelect,
-		Columns: sqlgen.Columns{
-			{`table_name`},
-		},
-		Table: sqlgen.Table{
-			`information_schema.tables`,
-		},
-		Where: sqlgen.Where{
-			sqlgen.ColumnValue{
-				sqlgen.Column{`table_schema`},
-				`=`,
-				sqlPlaceholder,
+		Type: sqlgen.Select,
+		Columns: sqlgen.JoinColumns(
+			sqlgen.ColumnWithName(`table_name`),
+		),
+		Table: sqlgen.TableWithName(`information_schema.tables`),
+		Where: sqlgen.WhereConditions(
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`table_schema`),
+				Operator: `=`,
+				Value:    sqlPlaceholder,
 			},
-		},
+		),
 	}
 
 	// Executing statement.
 	var rows *sqlx.Rows
-	if rows, err = s.doQuery(stmt, s.schema.Name); err != nil {
+	if rows, err = s.Query(stmt, s.schema.Name); err != nil {
 		return nil, err
 	}
 
@@ -289,25 +243,65 @@ func (s *source) Collections() (collections []string, err error) {
 	return collections, nil
 }
 
-func (s *source) clone() (*source, error) {
-	src := &source{}
-	src.Setup(s.connURL)
+// Use changes the active database.
+func (s *source) Use(database string) (err error) {
+	var conn ConnectionURL
 
-	if err := src.Open(); err != nil {
-		return nil, err
+	if conn, err = ParseURL(s.connURL.String()); err != nil {
+		return err
 	}
 
-	return src, nil
+	conn.Database = database
+
+	s.connURL = conn
+
+	return s.Open()
+}
+
+// Drop removes all tables from the current database.
+func (s *source) Drop() error {
+
+	_, err := s.Query(sqlgen.Statement{
+		Type:     sqlgen.DropDatabase,
+		Database: sqlgen.DatabaseWithName(s.schema.Name),
+	})
+
+	return err
+}
+
+// Setup stores database settings.
+func (s *source) Setup(connURL db.ConnectionURL) error {
+	s.connURL = connURL
+	return s.Open()
+}
+
+// Name returns the name of the database.
+func (s *source) Name() string {
+	return s.schema.Name
 }
 
-func debugLog(query string, args []interface{}, err error, start int64, end int64) {
-	if debugEnabled() == true {
-		d := sqlutil.Debug{query, args, err, start, end}
-		d.Print()
+// Transaction starts a transaction block and returns a db.Tx struct that can
+// be used to issue transactional queries.
+func (s *source) Transaction() (db.Tx, error) {
+	var err error
+	var clone *source
+	var sqlTx *sqlx.Tx
+
+	if sqlTx, err = s.session.Beginx(); err != nil {
+		return nil, err
+	}
+
+	if clone, err = s.clone(); err != nil {
+		return nil, err
 	}
+
+	clone.tx = sqltx.New(sqlTx)
+
+	return tx{Tx: clone.tx, source: clone}, nil
 }
 
-func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) {
+// Exec compiles and executes a statement that does not return any rows.
+func (s *source) Exec(stmt sqlgen.Statement, args ...interface{}) (sql.Result, error) {
 	var query string
 	var res sql.Result
 	var err error
@@ -317,7 +311,7 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result,
 
 	defer func() {
 		end = time.Now().UnixNano()
-		debugLog(query, args, err, start, end)
+		sqlutil.Log(query, args, err, start, end)
 	}()
 
 	if s.session == nil {
@@ -327,7 +321,7 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result,
 	query = stmt.Compile(template)
 
 	if s.tx != nil {
-		res, err = s.tx.sqlTx.Exec(query, args...)
+		res, err = s.tx.Exec(query, args...)
 	} else {
 		res, err = s.session.Exec(query, args...)
 	}
@@ -335,7 +329,8 @@ func (s *source) doExec(stmt sqlgen.Statement, args ...interface{}) (sql.Result,
 	return res, err
 }
 
-func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
+// Query compiles and executes a statement that returns rows.
+func (s *source) Query(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error) {
 	var rows *sqlx.Rows
 	var query string
 	var err error
@@ -345,7 +340,7 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows
 
 	defer func() {
 		end = time.Now().UnixNano()
-		debugLog(query, args, err, start, end)
+		sqlutil.Log(query, args, err, start, end)
 	}()
 
 	if s.session == nil {
@@ -355,7 +350,7 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows
 	query = stmt.Compile(template)
 
 	if s.tx != nil {
-		rows, err = s.tx.sqlTx.Queryx(query, args...)
+		rows, err = s.tx.Queryx(query, args...)
 	} else {
 		rows, err = s.session.Queryx(query, args...)
 	}
@@ -363,7 +358,8 @@ func (s *source) doQuery(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Rows
 	return rows, err
 }
 
-func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
+// QueryRow compiles and executes a statement that returns at most one row.
+func (s *source) QueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.Row, error) {
 	var query string
 	var row *sqlx.Row
 	var err error
@@ -373,7 +369,7 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R
 
 	defer func() {
 		end = time.Now().UnixNano()
-		debugLog(query, args, err, start, end)
+		sqlutil.Log(query, args, err, start, end)
 	}()
 
 	if s.session == nil {
@@ -383,7 +379,7 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R
 	query = stmt.Compile(template)
 
 	if s.tx != nil {
-		row = s.tx.sqlTx.QueryRowx(query, args...)
+		row = s.tx.QueryRowx(query, args...)
 	} else {
 		row = s.session.QueryRowx(query, args...)
 	}
@@ -391,13 +387,8 @@ func (s *source) doQueryRow(stmt sqlgen.Statement, args ...interface{}) (*sqlx.R
 	return row, err
 }
 
-func debugEnabled() bool {
-	if os.Getenv(db.EnvEnableDebug) != "" {
-		return true
-	}
-	return false
-}
-
+// populateSchema looks up for the table info in the database and populates its
+// schema for internal use.
 func (s *source) populateSchema() (err error) {
 	var collections []string
 
@@ -405,15 +396,15 @@ func (s *source) populateSchema() (err error) {
 
 	// Get database name.
 	stmt := sqlgen.Statement{
-		Type: sqlgen.SqlSelect,
-		Columns: sqlgen.Columns{
-			{sqlgen.Raw{`DATABASE()`}},
-		},
+		Type: sqlgen.Select,
+		Columns: sqlgen.JoinColumns(
+			sqlgen.RawValue(`DATABASE()`),
+		),
 	}
 
 	var row *sqlx.Row
 
-	if row, err = s.doQueryRow(stmt); err != nil {
+	if row, err = s.QueryRow(stmt); err != nil {
 		return err
 	}
 
@@ -449,18 +440,26 @@ func (s *source) tableExists(names ...string) error {
 		}
 
 		stmt = sqlgen.Statement{
-			Type:  sqlgen.SqlSelect,
-			Table: sqlgen.Table{`information_schema.tables`},
-			Columns: sqlgen.Columns{
-				{`table_name`},
-			},
-			Where: sqlgen.Where{
-				sqlgen.ColumnValue{sqlgen.Column{`table_schema`}, `=`, sqlPlaceholder},
-				sqlgen.ColumnValue{sqlgen.Column{`table_name`}, `=`, sqlPlaceholder},
-			},
+			Type:  sqlgen.Select,
+			Table: sqlgen.TableWithName(`information_schema.tables`),
+			Columns: sqlgen.JoinColumns(
+				sqlgen.ColumnWithName(`table_name`),
+			),
+			Where: sqlgen.WhereConditions(
+				&sqlgen.ColumnValue{
+					Column:   sqlgen.ColumnWithName(`table_schema`),
+					Operator: `=`,
+					Value:    sqlPlaceholder,
+				},
+				&sqlgen.ColumnValue{
+					Column:   sqlgen.ColumnWithName(`table_name`),
+					Operator: `=`,
+					Value:    sqlPlaceholder,
+				},
+			),
 		}
 
-		if rows, err = s.doQuery(stmt, s.schema.Name, names[i]); err != nil {
+		if rows, err = s.Query(stmt, s.schema.Name, names[i]); err != nil {
 			return db.ErrCollectionDoesNotExist
 		}
 
@@ -484,22 +483,30 @@ func (s *source) tableColumns(tableName string) ([]string, error) {
 	}
 
 	stmt := sqlgen.Statement{
-		Type:  sqlgen.SqlSelect,
-		Table: sqlgen.Table{`information_schema.columns`},
-		Columns: sqlgen.Columns{
-			{`column_name`},
-			{`data_type`},
-		},
-		Where: sqlgen.Where{
-			sqlgen.ColumnValue{sqlgen.Column{`table_schema`}, `=`, sqlPlaceholder},
-			sqlgen.ColumnValue{sqlgen.Column{`table_name`}, `=`, sqlPlaceholder},
-		},
+		Type:  sqlgen.Select,
+		Table: sqlgen.TableWithName(`information_schema.columns`),
+		Columns: sqlgen.JoinColumns(
+			sqlgen.ColumnWithName(`column_name`),
+			sqlgen.ColumnWithName(`data_type`),
+		),
+		Where: sqlgen.WhereConditions(
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`table_schema`),
+				Operator: `=`,
+				Value:    sqlPlaceholder,
+			},
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`table_name`),
+				Operator: `=`,
+				Value:    sqlPlaceholder,
+			},
+		),
 	}
 
 	var rows *sqlx.Rows
 	var err error
 
-	if rows, err = s.doQuery(stmt, s.schema.Name, tableName); err != nil {
+	if rows, err = s.Query(stmt, s.schema.Name, tableName); err != nil {
 		return nil, err
 	}
 
@@ -527,36 +534,46 @@ func (s *source) getPrimaryKey(tableName string) ([]string, error) {
 	}
 
 	stmt := sqlgen.Statement{
-		Type: sqlgen.SqlSelect,
-		Table: sqlgen.Table{
-			sqlgen.Raw{`
+		Type: sqlgen.Select,
+		Table: sqlgen.RawValue(`
 				information_schema.table_constraints AS t
 				JOIN information_schema.key_column_usage k
 				USING(constraint_name, table_schema, table_name)
-			`},
-		},
-		Columns: sqlgen.Columns{
-			{`k.column_name`},
-		},
-		Where: sqlgen.Where{
-			sqlgen.ColumnValue{sqlgen.Column{`t.constraint_type`}, `=`, sqlgen.Value{`primary key`}},
-			sqlgen.ColumnValue{sqlgen.Column{`t.table_schema`}, `=`, sqlPlaceholder},
-			sqlgen.ColumnValue{sqlgen.Column{`t.table_name`}, `=`, sqlPlaceholder},
-		},
-		OrderBy: sqlgen.OrderBy{
-			sqlgen.SortColumns{
-				{
-					sqlgen.Column{`k.ordinal_position`},
-					sqlgen.SqlSortAsc,
-				},
+		`),
+		Columns: sqlgen.JoinColumns(
+			sqlgen.ColumnWithName(`k.column_name`),
+		),
+		Where: sqlgen.WhereConditions(
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`t.constraint_type`),
+				Operator: `=`,
+				Value:    sqlgen.NewValue(`primary key`),
+			},
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`t.table_schema`),
+				Operator: `=`,
+				Value:    sqlPlaceholder,
 			},
+			&sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(`t.table_name`),
+				Operator: `=`,
+				Value:    sqlPlaceholder,
+			},
+		),
+		OrderBy: &sqlgen.OrderBy{
+			SortColumns: sqlgen.JoinSortColumns(
+				&sqlgen.SortColumn{
+					Column: sqlgen.ColumnWithName(`k.ordinal_position`),
+					Order:  sqlgen.Ascendent,
+				},
+			),
 		},
 	}
 
 	var rows *sqlx.Rows
 	var err error
 
-	if rows, err = s.doQuery(stmt, s.schema.Name, tableName); err != nil {
+	if rows, err = s.Query(stmt, s.schema.Name, tableName); err != nil {
 		return nil, err
 	}
 
@@ -572,40 +589,3 @@ func (s *source) getPrimaryKey(tableName string) ([]string, error) {
 
 	return tableSchema.PrimaryKey, nil
 }
-
-func init() {
-
-	template = &sqlgen.Template{
-		mysqlColumnSeparator,
-		mysqlIdentifierSeparator,
-		mysqlIdentifierQuote,
-		mysqlValueSeparator,
-		mysqlValueQuote,
-		mysqlAndKeyword,
-		mysqlOrKeyword,
-		mysqlNotKeyword,
-		mysqlDescKeyword,
-		mysqlAscKeyword,
-		mysqlDefaultOperator,
-		mysqlClauseGroup,
-		mysqlClauseOperator,
-		mysqlColumnValue,
-		mysqlTableAliasLayout,
-		mysqlColumnAliasLayout,
-		mysqlSortByColumnLayout,
-		mysqlWhereLayout,
-		mysqlOrderByLayout,
-		mysqlInsertLayout,
-		mysqlSelectLayout,
-		mysqlUpdateLayout,
-		mysqlDeleteLayout,
-		mysqlTruncateLayout,
-		mysqlDropDatabaseLayout,
-		mysqlDropTableLayout,
-		mysqlSelectCountLayout,
-		mysqlGroupByLayout,
-		cache.NewCache(),
-	}
-
-	db.Register(Adapter, &source{})
-}
diff --git a/mysql/mysql.go b/mysql/mysql.go
new file mode 100644
index 00000000..58e2a9ee
--- /dev/null
+++ b/mysql/mysql.go
@@ -0,0 +1,69 @@
+// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam
+//
+// 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 (
+	"upper.io/cache"
+	"upper.io/db"
+	"upper.io/db/util/sqlgen"
+)
+
+// Adapter is the public name of the adapter.
+const Adapter = `mysql`
+
+var template *sqlgen.Template
+
+func init() {
+	template = &sqlgen.Template{
+		ColumnSeparator:     adapterColumnSeparator,
+		IdentifierSeparator: adapterIdentifierSeparator,
+		IdentifierQuote:     adapterIdentifierQuote,
+		ValueSeparator:      adapterValueSeparator,
+		ValueQuote:          adapterValueQuote,
+		AndKeyword:          adapterAndKeyword,
+		OrKeyword:           adapterOrKeyword,
+		NotKeyword:          adapterNotKeyword,
+		DescKeyword:         adapterDescKeyword,
+		AscKeyword:          adapterAscKeyword,
+		DefaultOperator:     adapterDefaultOperator,
+		ClauseGroup:         adapterClauseGroup,
+		ClauseOperator:      adapterClauseOperator,
+		ColumnValue:         adapterColumnValue,
+		TableAliasLayout:    adapterTableAliasLayout,
+		ColumnAliasLayout:   adapterColumnAliasLayout,
+		SortByColumnLayout:  adapterSortByColumnLayout,
+		WhereLayout:         adapterWhereLayout,
+		OrderByLayout:       adapterOrderByLayout,
+		InsertLayout:        adapterInsertLayout,
+		SelectLayout:        adapterSelectLayout,
+		UpdateLayout:        adapterUpdateLayout,
+		DeleteLayout:        adapterDeleteLayout,
+		TruncateLayout:      adapterTruncateLayout,
+		DropDatabaseLayout:  adapterDropDatabaseLayout,
+		DropTableLayout:     adapterDropTableLayout,
+		CountLayout:         adapterSelectCountLayout,
+		GroupByLayout:       adapterGroupByLayout,
+		Cache:               cache.NewCache(),
+	}
+
+	db.Register(Adapter, &source{})
+}
diff --git a/mysql/result.go b/mysql/result.go
deleted file mode 100644
index b4fe80c8..00000000
--- a/mysql/result.go
+++ /dev/null
@@ -1,308 +0,0 @@
-// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
-//
-// 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 (
-	"fmt"
-	"strings"
-
-	"github.com/jmoiron/sqlx"
-	"upper.io/db"
-	"upper.io/db/util/sqlgen"
-	"upper.io/db/util/sqlutil"
-)
-
-type counter struct {
-	Total uint64 `db:"_t"`
-}
-
-type result struct {
-	table     *table
-	cursor    *sqlx.Rows // This is the main query cursor. It starts as a nil value.
-	limit     sqlgen.Limit
-	offset    sqlgen.Offset
-	columns   sqlgen.Columns
-	where     sqlgen.Where
-	orderBy   sqlgen.OrderBy
-	groupBy   sqlgen.GroupBy
-	arguments []interface{}
-}
-
-// Executes a SELECT statement that can feed Next(), All() or One().
-func (r *result) setCursor() error {
-	var err error
-	// We need a cursor, if the cursor does not exists yet then we create one.
-	if r.cursor == nil {
-		r.cursor, err = r.table.source.doQuery(sqlgen.Statement{
-			Type:    sqlgen.SqlSelect,
-			Table:   sqlgen.Table{r.table.Name()},
-			Columns: r.columns,
-			Limit:   r.limit,
-			Offset:  r.offset,
-			Where:   r.where,
-			OrderBy: r.orderBy,
-			GroupBy: r.groupBy,
-		}, r.arguments...)
-	}
-	return err
-}
-
-// Sets conditions for reducing the working set.
-func (r *result) Where(terms ...interface{}) db.Result {
-	r.where, r.arguments = whereValues(terms)
-	return r
-}
-
-// Determines the maximum limit of results to be returned.
-func (r *result) Limit(n uint) db.Result {
-	r.limit = sqlgen.Limit(n)
-	return r
-}
-
-// Determines how many documents will be skipped before starting to grab
-// results.
-func (r *result) Skip(n uint) db.Result {
-	r.offset = sqlgen.Offset(n)
-	return r
-}
-
-// Used to group results that have the same value in the same column or
-// columns.
-func (r *result) Group(fields ...interface{}) db.Result {
-
-	groupByColumns := make(sqlgen.GroupBy, 0, len(fields))
-
-	l := len(fields)
-
-	for i := 0; i < l; i++ {
-		switch value := fields[i].(type) {
-		// Maybe other types?
-		default:
-			groupByColumns = append(groupByColumns, sqlgen.Column{value})
-		}
-	}
-
-	r.groupBy = groupByColumns
-
-	return r
-}
-
-// Determines sorting of results according to the provided names. Fields may be
-// prefixed by - (minus) which means descending order, ascending order would be
-// used otherwise.
-func (r *result) Sort(fields ...interface{}) db.Result {
-
-	sortColumns := make(sqlgen.SortColumns, 0, len(fields))
-
-	l := len(fields)
-	for i := 0; i < l; i++ {
-		var sort sqlgen.SortColumn
-
-		switch value := fields[i].(type) {
-		case db.Raw:
-			sort = sqlgen.SortColumn{
-				sqlgen.Column{sqlgen.Raw{fmt.Sprintf(`%v`, value.Value)}},
-				sqlgen.SqlSortAsc,
-			}
-		case string:
-			if strings.HasPrefix(value, `-`) {
-				// Explicit descending order.
-				sort = sqlgen.SortColumn{
-					sqlgen.Column{value[1:]},
-					sqlgen.SqlSortDesc,
-				}
-			} else {
-				// Ascending order.
-				sort = sqlgen.SortColumn{
-					sqlgen.Column{value},
-					sqlgen.SqlSortAsc,
-				}
-			}
-		}
-		sortColumns = append(sortColumns, sort)
-	}
-
-	r.orderBy.SortColumns = sortColumns
-
-	return r
-}
-
-// Retrieves only the given fields.
-func (r *result) Select(fields ...interface{}) db.Result {
-
-	r.columns = make(sqlgen.Columns, 0, len(fields))
-
-	l := len(fields)
-	for i := 0; i < l; i++ {
-		var col sqlgen.Column
-		switch value := fields[i].(type) {
-		case db.Func:
-			v := interfaceArgs(value.Args)
-			var s string
-			if len(v) == 0 {
-				s = fmt.Sprintf(`%s()`, value.Name)
-			} else {
-				ss := make([]string, 0, len(v))
-				for j := range v {
-					ss = append(ss, fmt.Sprintf(`%v`, v[j]))
-				}
-				s = fmt.Sprintf(`%s(%s)`, value.Name, strings.Join(ss, `, `))
-			}
-			col = sqlgen.Column{sqlgen.Raw{s}}
-		case db.Raw:
-			col = sqlgen.Column{sqlgen.Raw{fmt.Sprintf(`%v`, value.Value)}}
-		default:
-			col = sqlgen.Column{value}
-		}
-		r.columns = append(r.columns, col)
-	}
-
-	return r
-}
-
-// Dumps all results into a pointer to an slice of structs or maps.
-func (r *result) All(dst interface{}) error {
-	var err error
-
-	if r.cursor != nil {
-		return db.ErrQueryIsPending
-	}
-
-	// Current cursor.
-	err = r.setCursor()
-
-	if err != nil {
-		return err
-	}
-
-	defer r.Close()
-
-	// Fetching all results within the cursor.
-	err = sqlutil.FetchRows(r.cursor, dst)
-
-	return err
-}
-
-// Fetches only one result from the resultset.
-func (r *result) One(dst interface{}) error {
-	var err error
-
-	if r.cursor != nil {
-		return db.ErrQueryIsPending
-	}
-
-	defer r.Close()
-
-	err = r.Next(dst)
-
-	return err
-}
-
-// Fetches the next result from the resultset.
-func (r *result) Next(dst interface{}) (err error) {
-
-	if err = r.setCursor(); err != nil {
-		r.Close()
-		return err
-	}
-
-	if err = sqlutil.FetchRow(r.cursor, dst); err != nil {
-		r.Close()
-		return err
-	}
-
-	return nil
-}
-
-// Removes the matching items from the collection.
-func (r *result) Remove() error {
-	var err error
-
-	_, err = r.table.source.doExec(sqlgen.Statement{
-		Type:  sqlgen.SqlDelete,
-		Table: sqlgen.Table{r.table.Name()},
-		Where: r.where,
-	}, r.arguments...)
-
-	return err
-
-}
-
-// Updates matching items from the collection with values of the given map or
-// struct.
-func (r *result) Update(values interface{}) error {
-
-	ff, vv, err := r.table.FieldValues(values)
-	if err != nil {
-		return err
-	}
-
-	total := len(ff)
-
-	cvs := make(sqlgen.ColumnValues, 0, total)
-
-	for i := 0; i < total; i++ {
-		cvs = append(cvs, sqlgen.ColumnValue{sqlgen.Column{ff[i]}, "=", sqlPlaceholder})
-	}
-
-	vv = append(vv, r.arguments...)
-
-	_, err = r.table.source.doExec(sqlgen.Statement{
-		Type:         sqlgen.SqlUpdate,
-		Table:        sqlgen.Table{r.table.Name()},
-		ColumnValues: cvs,
-		Where:        r.where,
-	}, vv...)
-
-	return err
-}
-
-// Closes the result set.
-func (r *result) Close() (err error) {
-	if r.cursor != nil {
-		err = r.cursor.Close()
-		r.cursor = nil
-	}
-	return err
-}
-
-// Counts the elements within the main conditions of the set.
-func (r *result) Count() (uint64, error) {
-	var count counter
-
-	row, err := r.table.source.doQueryRow(sqlgen.Statement{
-		Type:  sqlgen.SqlSelectCount,
-		Table: sqlgen.Table{r.table.Name()},
-		Where: r.where,
-	}, r.arguments...)
-
-	if err != nil {
-		return 0, err
-	}
-
-	err = row.Scan(&count.Total)
-	if err != nil {
-		return 0, err
-	}
-
-	return count.Total, nil
-}
diff --git a/mysql/layout.go b/mysql/template.go
similarity index 64%
rename from mysql/layout.go
rename to mysql/template.go
index c0ef3406..97af9cd2 100644
--- a/mysql/layout.go
+++ b/mysql/template.go
@@ -1,4 +1,4 @@
-// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
+// Copyright (c) 2012-2015 José Carlos Nieto, https://menteslibres.net/xiam
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
@@ -22,37 +22,37 @@
 package mysql
 
 const (
-	mysqlColumnSeparator     = `.`
-	mysqlIdentifierSeparator = `, `
-	mysqlIdentifierQuote     = "`{{.Raw}}`"
-	mysqlValueSeparator      = `, `
-	mysqlValueQuote          = `'{{.}}'`
-	mysqlAndKeyword          = `AND`
-	mysqlOrKeyword           = `OR`
-	mysqlNotKeyword          = `NOT`
-	mysqlDescKeyword         = `DESC`
-	mysqlAscKeyword          = `ASC`
-	mysqlDefaultOperator     = `=`
-	mysqlClauseGroup         = `({{.}})`
-	mysqlClauseOperator      = ` {{.}} `
-	mysqlColumnValue         = `{{.Column}} {{.Operator}} {{.Value}}`
-	mysqlTableAliasLayout    = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
-	mysqlColumnAliasLayout   = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
-	mysqlSortByColumnLayout  = `{{.Column}} {{.Sort}}`
-
-	mysqlOrderByLayout = `
+	adapterColumnSeparator     = `.`
+	adapterIdentifierSeparator = `, `
+	adapterIdentifierQuote     = "`{{.Value}}`"
+	adapterValueSeparator      = `, `
+	adapterValueQuote          = `'{{.}}'`
+	adapterAndKeyword          = `AND`
+	adapterOrKeyword           = `OR`
+	adapterNotKeyword          = `NOT`
+	adapterDescKeyword         = `DESC`
+	adapterAscKeyword          = `ASC`
+	adapterDefaultOperator     = `=`
+	adapterClauseGroup         = `({{.}})`
+	adapterClauseOperator      = ` {{.}} `
+	adapterColumnValue         = `{{.Column}} {{.Operator}} {{.Value}}`
+	adapterTableAliasLayout    = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
+	adapterColumnAliasLayout   = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}`
+	adapterSortByColumnLayout  = `{{.Column}} {{.Order}}`
+
+	adapterOrderByLayout = `
 		{{if .SortColumns}}
 			ORDER BY {{.SortColumns}}
 		{{end}}
 	`
 
-	mysqlWhereLayout = `
+	adapterWhereLayout = `
 		{{if .Conds}}
 			WHERE {{.Conds}}
 		{{end}}
 	`
 
-	mysqlSelectLayout = `
+	adapterSelectLayout = `
 		SELECT
 
 			{{if .Columns}}
@@ -79,19 +79,19 @@ const (
 				OFFSET {{.Offset}}
 			{{end}}
 	`
-	mysqlDeleteLayout = `
+	adapterDeleteLayout = `
 		DELETE
 			FROM {{.Table}}
 			{{.Where}}
 	`
-	mysqlUpdateLayout = `
+	adapterUpdateLayout = `
 		UPDATE
 			{{.Table}}
 		SET {{.ColumnValues}}
 			{{ .Where }}
 	`
 
-	mysqlSelectCountLayout = `
+	adapterSelectCountLayout = `
 		SELECT
 			COUNT(1) AS _t
 		FROM {{.Table}}
@@ -106,7 +106,7 @@ const (
 			{{end}}
 	`
 
-	mysqlInsertLayout = `
+	adapterInsertLayout = `
 		INSERT INTO {{.Table}}
 			({{.Columns}})
 		VALUES
@@ -114,23 +114,21 @@ const (
 		{{.Extra}}
 	`
 
-	mysqlTruncateLayout = `
+	adapterTruncateLayout = `
 		TRUNCATE TABLE {{.Table}}
 	`
 
-	mysqlDropDatabaseLayout = `
+	adapterDropDatabaseLayout = `
 		DROP DATABASE {{.Database}}
 	`
 
-	mysqlDropTableLayout = `
+	adapterDropTableLayout = `
 		DROP TABLE {{.Table}}
 	`
 
-	mysqlGroupByLayout = `
+	adapterGroupByLayout = `
 		{{if .GroupColumns}}
 			GROUP BY {{.GroupColumns}}
 		{{end}}
 	`
-
-	mysqlNull = `NULL`
 )
diff --git a/mysql/tx.go b/mysql/tx.go
deleted file mode 100644
index 844f11d0..00000000
--- a/mysql/tx.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// Copyright (c) 2012-2014 José Carlos Nieto, https://menteslibres.net/xiam
-//
-// 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 (
-	"github.com/jmoiron/sqlx"
-)
-
-type tx struct {
-	*source
-	sqlTx *sqlx.Tx
-	done  bool
-}
-
-func (t *tx) Commit() (err error) {
-	err = t.sqlTx.Commit()
-	if err == nil {
-		t.done = true
-	}
-	return err
-}
-
-func (t *tx) Rollback() error {
-	return t.sqlTx.Rollback()
-}
-- 
GitLab