From c378a7a891614c22df51e491f4e8a7ad40b20ec5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net>
Date: Sun, 4 Oct 2015 16:33:02 -0500
Subject: [PATCH] Using query builder to replace db.Result.

---
 builder.go                    |   4 +-
 builder/builder.go            | 315 ++++++++++++++++++++++++----------
 postgresql/collection.go      |   5 +-
 postgresql/database.go        |   4 +
 postgresql/database_test.go   |  60 +++++--
 util/sqlgen/column_value.go   |   8 +
 util/sqlutil/result/result.go | 277 +++++++-----------------------
 7 files changed, 342 insertions(+), 331 deletions(-)

diff --git a/builder.go b/builder.go
index f67adfc5..a112e295 100644
--- a/builder.go
+++ b/builder.go
@@ -17,6 +17,7 @@ type QueryBuilder interface {
 }
 
 type QuerySelector interface {
+	Columns(columns ...interface{}) QuerySelector
 	From(tables ...string) QuerySelector
 	Distinct() QuerySelector
 	Where(...interface{}) QuerySelector
@@ -33,8 +34,9 @@ type QuerySelector interface {
 	Limit(int) QuerySelector
 	Offset(int) QuerySelector
 
+	Iterator() Iterator
+
 	QueryGetter
-	Iterator
 	fmt.Stringer
 }
 
diff --git a/builder/builder.go b/builder/builder.go
index 568bd6b4..b594c3c1 100644
--- a/builder/builder.go
+++ b/builder/builder.go
@@ -5,6 +5,8 @@ import (
 	"errors"
 	"fmt"
 	"github.com/jmoiron/sqlx"
+	"github.com/jmoiron/sqlx/reflectx"
+	"reflect"
 	"regexp"
 	"strconv"
 	"strings"
@@ -15,8 +17,15 @@ import (
 
 type SelectMode uint8
 
+var mapper = reflectx.NewMapper("db")
+
 var (
-	reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`)
+	reInvisibleChars       = regexp.MustCompile(`[\s\r\n\t]+`)
+	reColumnCompareExclude = regexp.MustCompile(`[^a-zA-Z0-9]`)
+)
+
+var (
+	sqlPlaceholder = sqlgen.RawValue(`?`)
 )
 
 const (
@@ -28,6 +37,8 @@ type sqlDatabase interface {
 	Query(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Rows, error)
 	QueryRow(stmt *sqlgen.Statement, args ...interface{}) (*sqlx.Row, error)
 	Exec(stmt *sqlgen.Statement, args ...interface{}) (sql.Result, error)
+
+	TableColumns(tableName string) ([]string, error)
 }
 
 type Builder struct {
@@ -46,16 +57,12 @@ func (b *Builder) SelectAllFrom(table string) db.QuerySelector {
 }
 
 func (b *Builder) Select(columns ...interface{}) db.QuerySelector {
-	f, err := columnFragments(b.t, columns)
-
 	qs := &QuerySelector{
 		builder: b,
-		columns: sqlgen.JoinColumns(f...),
-		err:     err,
 	}
 
 	qs.stringer = &stringer{qs, b.t.Template}
-	return qs
+	return qs.Columns(columns...)
 }
 
 func (b *Builder) InsertInto(table string) db.QueryInserter {
@@ -80,8 +87,9 @@ func (b *Builder) DeleteFrom(table string) db.QueryDeleter {
 
 func (b *Builder) Update(table string) db.QueryUpdater {
 	qu := &QueryUpdater{
-		builder: b,
-		table:   table,
+		builder:      b,
+		table:        table,
+		columnValues: &sqlgen.ColumnValues{},
 	}
 
 	qu.stringer = &stringer{qu, b.t.Template}
@@ -195,9 +203,28 @@ type QueryUpdater struct {
 }
 
 func (qu *QueryUpdater) Set(terms ...interface{}) db.QueryUpdater {
-	cv, arguments := qu.builder.t.ToColumnValues(terms)
-	qu.columnValues = &cv
-	qu.arguments = append(qu.arguments, arguments...)
+	if len(terms) == 1 {
+		columns, _ := qu.builder.sess.TableColumns(qu.table)
+		ff, vv, _ := fieldValues(columns, terms[0])
+
+		cvs := make([]sqlgen.Fragment, len(ff))
+
+		for i := range ff {
+			cvs[i] = &sqlgen.ColumnValue{
+				Column:   sqlgen.ColumnWithName(ff[i]),
+				Operator: qu.builder.t.AssignmentOperator,
+				Value:    sqlPlaceholder,
+			}
+		}
+
+		qu.columnValues.Append(cvs...)
+		qu.arguments = append(qu.arguments, vv...)
+	} else if len(terms) > 1 {
+		cv, arguments := qu.builder.t.ToColumnValues(terms)
+		qu.columnValues.Append(cv.ColumnValues...)
+		qu.arguments = append(qu.arguments, arguments...)
+	}
+
 	return qu
 }
 
@@ -235,10 +262,14 @@ func (qu *QueryUpdater) statement() *sqlgen.Statement {
 	return stmt
 }
 
+type iterator struct {
+	cursor *sqlx.Rows // This is the main query cursor. It starts as a nil value.
+	err    error
+}
+
 type QuerySelector struct {
 	*stringer
 	mode      SelectMode
-	cursor    *sqlx.Rows // This is the main query cursor. It starts as a nil value.
 	builder   *Builder
 	table     string
 	where     *sqlgen.Where
@@ -257,6 +288,16 @@ func (qs *QuerySelector) From(tables ...string) db.QuerySelector {
 	return qs
 }
 
+func (qs *QuerySelector) Columns(columns ...interface{}) db.QuerySelector {
+	f, err := columnFragments(qs.builder.t, columns)
+	if err != nil {
+		qs.err = err
+		return qs
+	}
+	qs.columns = sqlgen.JoinColumns(f...)
+	return qs
+}
+
 func (qs *QuerySelector) Distinct() db.QuerySelector {
 	qs.mode = selectModeDistinct
 	return qs
@@ -426,88 +467,9 @@ func (qs *QuerySelector) QueryRow() (*sqlx.Row, error) {
 	return qs.builder.sess.QueryRow(qs.statement(), qs.arguments...)
 }
 
-func (qs *QuerySelector) Close() (err error) {
-	if qs.cursor != nil {
-		err = qs.cursor.Close()
-		qs.cursor = nil
-	}
-	return err
-}
-
-func (qs *QuerySelector) setCursor() (err error) {
-	if qs.cursor == nil {
-		qs.cursor, err = qs.builder.sess.Query(qs.statement(), qs.arguments...)
-	}
-	return err
-}
-
-func (qs *QuerySelector) One(dst interface{}) error {
-	if qs.err != nil {
-		return qs.err
-	}
-
-	if qs.cursor != nil {
-		return db.ErrQueryIsPending
-	}
-
-	defer qs.Close()
-
-	if !qs.Next(dst) {
-		return qs.Err()
-	}
-
-	return nil
-}
-
-func (qs *QuerySelector) All(dst interface{}) error {
-	var err error
-
-	if qs.err != nil {
-		return qs.err
-	}
-
-	if qs.cursor != nil {
-		return db.ErrQueryIsPending
-	}
-
-	err = qs.setCursor()
-
-	if err != nil {
-		return err
-	}
-
-	defer qs.Close()
-
-	// Fetching all results within the cursor.
-	err = sqlutil.FetchRows(qs.cursor, dst)
-
-	return err
-}
-
-func (qs *QuerySelector) Err() (err error) {
-	return qs.err
-}
-
-func (qs *QuerySelector) Next(dst interface{}) bool {
-	var err error
-
-	if qs.err != nil {
-		return false
-	}
-
-	if err = qs.setCursor(); err != nil {
-		qs.err = err
-		qs.Close()
-		return false
-	}
-
-	if err = sqlutil.FetchRow(qs.cursor, dst); err != nil {
-		qs.err = err
-		qs.Close()
-		return false
-	}
-
-	return true
+func (qs *QuerySelector) Iterator() db.Iterator {
+	rows, err := qs.builder.sess.Query(qs.statement(), qs.arguments...)
+	return &iterator{rows, err}
 }
 
 func columnFragments(template *sqlutil.TemplateWithUtils, columns []interface{}) ([]sqlgen.Fragment, error) {
@@ -585,3 +547,168 @@ func NewBuilder(sess sqlDatabase, t *sqlutil.TemplateWithUtils) *Builder {
 		t:    t,
 	}
 }
+
+func (iter *iterator) One(dst interface{}) error {
+	if iter.err != nil {
+		return iter.err
+	}
+
+	defer iter.Close()
+
+	if !iter.Next(dst) {
+		return iter.Err()
+	}
+
+	return nil
+}
+
+func (iter *iterator) All(dst interface{}) error {
+	var err error
+
+	if iter.err != nil {
+		return iter.err
+	}
+
+	defer iter.Close()
+
+	// Fetching all results within the cursor.
+	err = sqlutil.FetchRows(iter.cursor, dst)
+
+	return err
+}
+
+func (iter *iterator) Err() (err error) {
+	return iter.err
+}
+
+func (iter *iterator) Next(dst interface{}) bool {
+	var err error
+
+	if iter.err != nil {
+		return false
+	}
+
+	if err = sqlutil.FetchRow(iter.cursor, dst); err != nil {
+		iter.err = err
+		iter.Close()
+		return false
+	}
+
+	return true
+}
+
+func (iter *iterator) Close() (err error) {
+	if iter.cursor != nil {
+		err = iter.cursor.Close()
+		iter.cursor = nil
+	}
+	return err
+}
+
+func fieldValues(columns []string, item interface{}) ([]string, []interface{}, error) {
+	fields := []string{}
+	values := []interface{}{}
+
+	itemV := reflect.ValueOf(item)
+	itemT := itemV.Type()
+
+	if itemT.Kind() == reflect.Ptr {
+		// Single derefence. Just in case user passed a pointer to struct instead of a struct.
+		item = itemV.Elem().Interface()
+		itemV = reflect.ValueOf(item)
+		itemT = itemV.Type()
+	}
+
+	switch itemT.Kind() {
+
+	case reflect.Struct:
+
+		fieldMap := mapper.TypeMap(itemT).Names
+		nfields := len(fieldMap)
+
+		values = make([]interface{}, 0, nfields)
+		fields = make([]string, 0, nfields)
+
+		for _, fi := range fieldMap {
+			// log.Println("=>", fi.Name, fi.Options)
+
+			fld := reflectx.FieldByIndexesReadOnly(itemV, fi.Index)
+			if fld.Kind() == reflect.Ptr && fld.IsNil() {
+				continue
+			}
+
+			var value interface{}
+			if _, ok := fi.Options["stringarray"]; ok {
+				value = sqlutil.StringArray(fld.Interface().([]string))
+			} else if _, ok := fi.Options["int64array"]; ok {
+				value = sqlutil.Int64Array(fld.Interface().([]int64))
+			} else if _, ok := fi.Options["jsonb"]; ok {
+				value = sqlutil.JsonbType{fld.Interface()}
+			} else {
+				value = fld.Interface()
+			}
+
+			if _, ok := fi.Options["omitempty"]; ok {
+				if value == fi.Zero.Interface() {
+					continue
+				}
+			}
+
+			// TODO: columnLike stuff...?
+
+			fields = append(fields, fi.Name)
+			v, err := marshal(value)
+			if err != nil {
+				return nil, nil, err
+			}
+			values = append(values, v)
+		}
+
+	case reflect.Map:
+		nfields := itemV.Len()
+		values = make([]interface{}, nfields)
+		fields = make([]string, nfields)
+		mkeys := itemV.MapKeys()
+
+		for i, keyV := range mkeys {
+			valv := itemV.MapIndex(keyV)
+			fields[i] = columnLike(columns, fmt.Sprintf("%v", keyV.Interface()))
+
+			v, err := marshal(valv.Interface())
+			if err != nil {
+				return nil, nil, err
+			}
+
+			values[i] = v
+		}
+
+	default:
+		return nil, nil, db.ErrExpectingMapOrStruct
+	}
+
+	return fields, values, nil
+}
+
+func columnLike(columns []string, s string) string {
+	for _, name := range columns {
+		if normalizeColumn(s) == normalizeColumn(name) {
+			return name
+		}
+	}
+	return s
+}
+
+func marshal(v interface{}) (interface{}, error) {
+	if m, isMarshaler := v.(db.Marshaler); isMarshaler {
+		var err error
+		if v, err = m.MarshalDB(); err != nil {
+			return nil, err
+		}
+	}
+	return v, nil
+}
+
+// normalizeColumn prepares a column for comparison against another column.
+func normalizeColumn(s string) string {
+	return strings.ToLower(reColumnCompareExclude.ReplaceAllString(s, ""))
+}
diff --git a/postgresql/collection.go b/postgresql/collection.go
index 2d41d9dc..b1436045 100644
--- a/postgresql/collection.go
+++ b/postgresql/collection.go
@@ -41,9 +41,8 @@ type table struct {
 var _ = db.Collection(&table{})
 
 // Find creates a result set with the given conditions.
-func (t *table) Find(terms ...interface{}) db.Result {
-	where, arguments := template.ToWhereWithArguments(terms)
-	return result.NewResult(template, t, where, arguments)
+func (t *table) Find(conds ...interface{}) db.Result {
+	return result.NewResult(t.database.Builder(), t, conds)
 }
 
 // Truncate deletes all rows from the table.
diff --git a/postgresql/database.go b/postgresql/database.go
index 90a2e45e..6487869e 100644
--- a/postgresql/database.go
+++ b/postgresql/database.go
@@ -528,6 +528,10 @@ func (d *database) tableExists(names ...string) error {
 	return nil
 }
 
+func (d *database) TableColumns(tableName string) ([]string, error) {
+	return d.tableColumns(tableName)
+}
+
 func (d *database) tableColumns(tableName string) ([]string, error) {
 
 	// Making sure this table is allocated.
diff --git a/postgresql/database_test.go b/postgresql/database_test.go
index fc0b5bac..467eea64 100644
--- a/postgresql/database_test.go
+++ b/postgresql/database_test.go
@@ -2083,6 +2083,25 @@ func TestQueryBuilder(t *testing.T) {
 		b.Update("artist").Set("name = ?", "Artist").Where("id <", 5).String(),
 	)
 
+	assert.Equal(
+		`UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`,
+		b.Update("artist").Set(map[string]string{"name": "Artist"}).Where(db.Cond{"id <": 5}).String(),
+	)
+
+	assert.Equal(
+		`UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`,
+		b.Update("artist").Set(struct {
+			Nombre string `db:"name"`
+		}{"Artist"}).Where(db.Cond{"id <": 5}).String(),
+	)
+
+	assert.Equal(
+		`UPDATE "artist" SET "name" = $1, "last_name" = $2 WHERE ("id" < $3)`,
+		b.Update("artist").Set(struct {
+			Nombre string `db:"name"`
+		}{"Artist"}).Set(map[string]string{"last_name": "Foo"}).Where(db.Cond{"id <": 5}).String(),
+	)
+
 	assert.Equal(
 		`UPDATE "artist" SET "name" = $1 || ' ' || $2 || id, "id" = id + $3 WHERE (id > $4)`,
 		b.Update("artist").Set(
@@ -2114,32 +2133,35 @@ func TestQueryBuilder(t *testing.T) {
 
 	// Testing actual queries.
 
-	var artist artistType
-	var artists []artistType
+	/*
+		var artist artistType
+		var artists []artistType
 
-	err = b.SelectAllFrom("artist").All(&artists)
-	assert.NoError(err)
-	assert.True(len(artists) > 0)
+		err = b.SelectAllFrom("artist").Iterator().All(&artists)
+		assert.NoError(err)
+		assert.True(len(artists) > 0)
 
-	err = b.SelectAllFrom("artist").One(&artist)
-	assert.NoError(err)
-	assert.NotNil(artist)
+		err = b.SelectAllFrom("artist").Iterator().One(&artist)
+		assert.NoError(err)
+		assert.NotNil(artist)
 
-	var qs db.QuerySelector
+		var qs db.QuerySelector
 
-	qs = b.SelectAllFrom("artist")
-	for qs.Next(&artist) {
-		assert.Nil(qs.Err())
-		assert.NotNil(artist)
-	}
+		qs = b.SelectAllFrom("artist")
+		iter := qs.Iterator()
+		for iter.Next(&artist) {
+			assert.Nil(iter.Err())
+			assert.NotNil(artist)
+		}
 
-	assert.Nil(qs.Close())
+		assert.Nil(iter.Close())
 
-	qs = b.Select().From("artist a").Join("publications p").On("p1.id = a.id").Using("id")
-	assert.Error(qs.One(&artist), `Should not work because it attempts to use both "On()" and "Using()" in the same JOIN.`)
+		qs = b.Select().From("artist a").Join("publications p").On("p1.id = a.id").Using("id")
+		assert.Error(qs.Iterator().One(&artist), `Should not work because it attempts to use both "On()" and "Using()" in the same JOIN.`)
 
-	qs = b.Select().From("artist a").On("p1.id = a.id")
-	assert.Error(qs.One(&artist), `Should not work because it should put a "Join()" before "On()".`)
+		qs = b.Select().From("artist a").On("p1.id = a.id")
+		assert.Error(qs.Iterator().One(&artist), `Should not work because it should put a "Join()" before "On()".`)
+	*/
 }
 
 // TestExhaustConnections simulates a "too many connections" situation
diff --git a/util/sqlgen/column_value.go b/util/sqlgen/column_value.go
index 485a46f3..9674b41a 100644
--- a/util/sqlgen/column_value.go
+++ b/util/sqlgen/column_value.go
@@ -58,6 +58,14 @@ func JoinColumnValues(values ...Fragment) *ColumnValues {
 	return &ColumnValues{ColumnValues: values}
 }
 
+func (c *ColumnValues) Append(values ...Fragment) *ColumnValues {
+	for _, f := range values {
+		c.ColumnValues = append(c.ColumnValues, f)
+	}
+	c.hash = ""
+	return c
+}
+
 // Hash returns a unique identifier.
 func (c *ColumnValues) Hash() string {
 	if c.hash == "" {
diff --git a/util/sqlutil/result/result.go b/util/sqlutil/result/result.go
index c619ec5d..cc2345d0 100644
--- a/util/sqlutil/result/result.go
+++ b/util/sqlutil/result/result.go
@@ -22,104 +22,55 @@
 package result
 
 import (
-	"fmt"
-	"strings"
-
-	"github.com/jmoiron/sqlx"
 	"upper.io/db"
-	"upper.io/db/util/sqlgen"
-	"upper.io/db/util/sqlutil"
-)
-
-var (
-	sqlPlaceholder = sqlgen.RawValue(`?`)
 )
 
-type counter struct {
-	Total uint64 `db:"_t"`
-}
-
 type Result struct {
-	table     DataProvider
-	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{}
-	template  *sqlutil.TemplateWithUtils
+	b       db.QueryBuilder
+	dp      DataProvider
+	iter    db.Iterator
+	limit   int
+	offset  int
+	fields  []interface{}
+	columns []interface{}
+	orderBy []interface{}
+	groupBy []interface{}
+	conds   []interface{}
 }
 
 // NewResult creates and results a new result set on the given table, this set
 // is limited by the given sqlgen.Where conditions.
-func NewResult(template *sqlutil.TemplateWithUtils, p DataProvider, where sqlgen.Where, arguments []interface{}) *Result {
+func NewResult(b db.QueryBuilder, dp DataProvider, conds []interface{}) *Result {
 	return &Result{
-		table:     p,
-		where:     where,
-		arguments: arguments,
-		template:  template,
-	}
-}
-
-// 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 {
-		stmt := sqlgen.Statement{
-			Type:    sqlgen.Select,
-			Table:   sqlgen.TableWithName(r.table.Name()),
-			Columns: &r.columns,
-			Limit:   r.limit,
-			Offset:  r.offset,
-			Where:   &r.where,
-			OrderBy: &r.orderBy,
-			GroupBy: &r.groupBy,
-		}
-		r.cursor, err = r.table.Query(&stmt, r.arguments...)
+		b:     b,
+		dp:    dp,
+		conds: conds,
 	}
-	return err
 }
 
 // Sets conditions for reducing the working set.
-func (r *Result) Where(terms ...interface{}) db.Result {
-	w, a := r.template.ToWhereWithArguments(terms)
-	r.where = w
-	r.arguments = append(r.arguments, a...)
+func (r *Result) Where(conds ...interface{}) db.Result {
+	r.conds = conds
 	return r
 }
 
 // Determines the maximum limit of results to be returned.
 func (r *Result) Limit(n uint) db.Result {
-	r.limit = sqlgen.Limit(n)
+	r.limit = int(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)
+	r.offset = int(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 {
-	var columns []sqlgen.Fragment
-
-	for i := range fields {
-		switch v := fields[i].(type) {
-		case string:
-			columns = append(columns, sqlgen.ColumnWithName(v))
-		case sqlgen.Fragment:
-			columns = append(columns, v)
-		}
-	}
-
-	r.groupBy = *sqlgen.GroupByColumns(columns...)
-
+	r.groupBy = fields
 	return r
 }
 
@@ -127,195 +78,93 @@ func (r *Result) Group(fields ...interface{}) db.Result {
 // prefixed by - (minus) which means descending order, ascending order would be
 // used otherwise.
 func (r *Result) Sort(fields ...interface{}) db.Result {
-
-	var sortColumns sqlgen.SortColumns
-
-	for i := range fields {
-		var sort *sqlgen.SortColumn
-
-		switch value := fields[i].(type) {
-		case db.Raw:
-			sort = &sqlgen.SortColumn{
-				Column: sqlgen.RawValue(fmt.Sprintf(`%v`, value.Value)),
-				Order:  sqlgen.Ascendent,
-			}
-		case string:
-			if strings.HasPrefix(value, `-`) {
-				// Explicit descending order.
-				sort = &sqlgen.SortColumn{
-					Column: sqlgen.ColumnWithName(value[1:]),
-					Order:  sqlgen.Descendent,
-				}
-			} else {
-				// Ascending order.
-				sort = &sqlgen.SortColumn{
-					Column: sqlgen.ColumnWithName(value),
-					Order:  sqlgen.Ascendent,
-				}
-			}
-		}
-		sortColumns.Columns = append(sortColumns.Columns, sort)
-	}
-
-	r.orderBy.SortColumns = &sortColumns
-
+	r.orderBy = fields
 	return r
 }
 
 // Retrieves only the given fields.
 func (r *Result) Select(fields ...interface{}) db.Result {
-
-	r.columns = sqlgen.Columns{}
-
-	for i := range fields {
-		var col sqlgen.Fragment
-		switch value := fields[i].(type) {
-		case db.Func:
-			v := r.template.ToInterfaceArguments(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.RawValue(s)
-		case db.Raw:
-			col = sqlgen.RawValue(fmt.Sprintf(`%v`, value.Value))
-		default:
-			col = sqlgen.ColumnWithName(fmt.Sprintf(`%v`, value))
-		}
-		r.columns.Columns = append(r.columns.Columns, col)
-	}
-
+	r.fields = fields
 	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
+	return r.buildSelect().Iterator().All(dst)
 }
 
 // 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
+	return r.buildSelect().Iterator().One(dst)
 }
 
 // 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 r.iter == nil {
+		r.iter = r.buildSelect().Iterator()
 	}
-
-	if err = sqlutil.FetchRow(r.cursor, dst); err != nil {
-		r.Close()
-		return err
+	if !r.iter.Next(dst) {
+		return r.iter.Err()
 	}
-
 	return nil
 }
 
 // Removes the matching items from the collection.
 func (r *Result) Remove() error {
-	var err error
-
-	_, err = r.table.Exec(&sqlgen.Statement{
-		Type:  sqlgen.Delete,
-		Table: sqlgen.TableWithName(r.table.Name()),
-		Where: &r.where,
-	}, r.arguments...)
+	q := r.b.DeleteFrom(r.dp.Name()).
+		Where(r.conds...).
+		Limit(r.limit)
 
+	_, err := q.Exec()
 	return err
+}
 
+// Closes the result set.
+func (r *Result) Close() error {
+	if r.iter != nil {
+		return r.iter.Close()
+	}
+	return nil
 }
 
 // Updates matching items from the collection with values of the given map or
 // struct.
 func (r *Result) Update(values interface{}) error {
+	q := r.b.Update(r.dp.Name()).
+		Set(values).
+		Where(r.conds...).
+		Limit(r.limit)
 
-	ff, vv, err := r.table.FieldValues(values)
-	if err != nil {
-		return err
-	}
-
-	cvs := new(sqlgen.ColumnValues)
-
-	for i := range ff {
-		cvs.ColumnValues = append(cvs.ColumnValues, &sqlgen.ColumnValue{Column: sqlgen.ColumnWithName(ff[i]), Operator: r.template.AssignmentOperator, Value: sqlPlaceholder})
-	}
-
-	vv = append(vv, r.arguments...)
-
-	_, err = r.table.Exec(&sqlgen.Statement{
-		Type:         sqlgen.Update,
-		Table:        sqlgen.TableWithName(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
-	}
+	_, err := q.Exec()
 	return err
 }
 
 // Counts the elements within the main conditions of the set.
 func (r *Result) Count() (uint64, error) {
-	var count counter
+	counter := struct {
+		Count uint64 `db:"_t"`
+	}{}
 
-	row, err := r.table.QueryRow(&sqlgen.Statement{
-		Type:  sqlgen.Count,
-		Table: sqlgen.TableWithName(r.table.Name()),
-		Where: &r.where,
-	}, r.arguments...)
+	q := r.buildSelect()
+	q.Columns(db.Raw{"COUNT(1) AS _t"}).Limit(1)
 
-	if err != nil {
+	if err := q.Iterator().One(&counter); err != nil {
 		return 0, err
 	}
 
-	err = row.Scan(&count.Total)
-	if err != nil {
-		return 0, err
-	}
+	return counter.Count, nil
+}
+
+func (r *Result) buildSelect() db.QuerySelector {
+	q := r.b.Select(r.fields...)
+
+	q.From(r.dp.Name())
+	q.Where(r.conds...)
+	q.Limit(r.limit)
+	q.Offset(r.offset)
+
+	q.GroupBy(r.groupBy...)
+	q.OrderBy(r.orderBy...)
 
-	return count.Total, nil
+	return q
 }
-- 
GitLab