From f3de91d79f009917652ec6b4917af4de18e74afe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Carlos=20Nieto?= <jose.carlos@menteslibres.net> Date: Wed, 7 Oct 2015 08:54:26 -0500 Subject: [PATCH] Promoting builder to its own project and moving some non-public subpackages to internal. --- builder/builder.go | 772 ------------------- builder/builder_test.go | 236 ------ builder/template_test.go | 193 ----- {util => internal}/adapter/nonexistent.go | 0 {util => internal}/schema/schema.go | 0 internal/sqladapter/collection.go | 2 +- internal/sqladapter/database.go | 12 +- mongo/database.go | 2 +- mongo/result.go | 2 +- mysql/collection.go | 6 +- mysql/database.go | 10 +- mysql/database_test.go | 2 +- mysql/mysql.go | 4 +- mysql/tx.go | 2 +- postgresql/collection.go | 4 +- postgresql/database.go | 2 +- postgresql/postgresql.go | 4 +- postgresql/tx.go | 2 +- ql/collection.go | 6 +- ql/database.go | 10 +- ql/database_test.go | 2 +- ql/ql.go | 4 +- ql/tx.go | 2 +- sqlite/collection.go | 6 +- sqlite/database.go | 10 +- sqlite/database_test.go | 2 +- sqlite/sqlite.go | 4 +- sqlite/tx.go | 2 +- util/sqlgen/column.go | 91 --- util/sqlgen/column_test.go | 119 --- util/sqlgen/column_value.go | 101 --- util/sqlgen/column_value_test.go | 156 ---- util/sqlgen/columns.go | 63 -- util/sqlgen/columns_test.go | 73 -- util/sqlgen/database.go | 37 - util/sqlgen/database_test.go | 53 -- util/sqlgen/default.go | 192 ----- util/sqlgen/group_by.go | 50 -- util/sqlgen/group_by_test.go | 73 -- util/sqlgen/interfaces.go | 15 - util/sqlgen/join.go | 177 ----- util/sqlgen/join_test.go | 250 ------ util/sqlgen/order_by.go | 164 ---- util/sqlgen/order_by_test.go | 143 ---- util/sqlgen/raw.go | 38 - util/sqlgen/raw_test.go | 72 -- util/sqlgen/statement.go | 130 ---- util/sqlgen/statement_test.go | 885 ---------------------- util/sqlgen/table.go | 110 --- util/sqlgen/table_test.go | 136 ---- util/sqlgen/template.go | 88 --- util/sqlgen/utilities.go | 157 ---- util/sqlgen/utilities_test.go | 284 ------- util/sqlgen/value.go | 163 ---- util/sqlgen/value_test.go | 99 --- util/sqlgen/where.go | 123 --- util/sqlgen/where_test.go | 113 --- util/sqlutil/convert.go | 291 ------- util/sqlutil/debug.go | 83 -- util/sqlutil/fetch.go | 293 ------- util/sqlutil/result/result.go | 170 ----- util/sqlutil/scanner.go | 193 ----- util/sqlutil/tx/tx.go | 46 -- 63 files changed, 51 insertions(+), 6483 deletions(-) delete mode 100644 builder/builder.go delete mode 100644 builder/builder_test.go delete mode 100644 builder/template_test.go rename {util => internal}/adapter/nonexistent.go (100%) rename {util => internal}/schema/schema.go (100%) delete mode 100644 util/sqlgen/column.go delete mode 100644 util/sqlgen/column_test.go delete mode 100644 util/sqlgen/column_value.go delete mode 100644 util/sqlgen/column_value_test.go delete mode 100644 util/sqlgen/columns.go delete mode 100644 util/sqlgen/columns_test.go delete mode 100644 util/sqlgen/database.go delete mode 100644 util/sqlgen/database_test.go delete mode 100644 util/sqlgen/default.go delete mode 100644 util/sqlgen/group_by.go delete mode 100644 util/sqlgen/group_by_test.go delete mode 100644 util/sqlgen/interfaces.go delete mode 100644 util/sqlgen/join.go delete mode 100644 util/sqlgen/join_test.go delete mode 100644 util/sqlgen/order_by.go delete mode 100644 util/sqlgen/order_by_test.go delete mode 100644 util/sqlgen/raw.go delete mode 100644 util/sqlgen/raw_test.go delete mode 100644 util/sqlgen/statement.go delete mode 100644 util/sqlgen/statement_test.go delete mode 100644 util/sqlgen/table.go delete mode 100644 util/sqlgen/table_test.go delete mode 100644 util/sqlgen/template.go delete mode 100644 util/sqlgen/utilities.go delete mode 100644 util/sqlgen/utilities_test.go delete mode 100644 util/sqlgen/value.go delete mode 100644 util/sqlgen/value_test.go delete mode 100644 util/sqlgen/where.go delete mode 100644 util/sqlgen/where_test.go delete mode 100644 util/sqlutil/convert.go delete mode 100644 util/sqlutil/debug.go delete mode 100644 util/sqlutil/fetch.go delete mode 100644 util/sqlutil/result/result.go delete mode 100644 util/sqlutil/scanner.go delete mode 100644 util/sqlutil/tx/tx.go diff --git a/builder/builder.go b/builder/builder.go deleted file mode 100644 index 7cedc38b..00000000 --- a/builder/builder.go +++ /dev/null @@ -1,772 +0,0 @@ -package builder - -import ( - "database/sql" - "errors" - "fmt" - "github.com/jmoiron/sqlx" - "github.com/jmoiron/sqlx/reflectx" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" -) - -type SelectMode uint8 - -var mapper = reflectx.NewMapper("db") - -type fieldValue struct { - fields []string - values []interface{} -} - -func (fv *fieldValue) Len() int { - return len(fv.fields) -} - -func (fv *fieldValue) Swap(i, j int) { - fv.fields[i], fv.fields[j] = fv.fields[j], fv.fields[i] - fv.values[i], fv.values[j] = fv.values[j], fv.values[i] -} - -func (fv *fieldValue) Less(i, j int) bool { - return fv.fields[i] < fv.fields[j] -} - -var ( - reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) - reColumnCompareExclude = regexp.MustCompile(`[^a-zA-Z0-9]`) -) - -var ( - sqlPlaceholder = sqlgen.RawValue(`?`) -) - -const ( - selectModeAll SelectMode = iota - selectModeDistinct -) - -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) -} - -type Builder struct { - sess sqlDatabase - t *sqlutil.TemplateWithUtils -} - -func (b *Builder) Exec(query interface{}, args ...interface{}) (sql.Result, error) { - switch q := query.(type) { - case *sqlgen.Statement: - return b.sess.Exec(q, args...) - default: - return nil, errors.New("Unsupported query type.") - } -} - -func (b *Builder) SelectAllFrom(table string) db.QuerySelector { - qs := &QuerySelector{ - builder: b, - table: table, - } - - qs.stringer = &stringer{qs, b.t.Template} - return qs -} - -func (b *Builder) Select(columns ...interface{}) db.QuerySelector { - qs := &QuerySelector{ - builder: b, - } - - qs.stringer = &stringer{qs, b.t.Template} - return qs.Columns(columns...) -} - -func (b *Builder) InsertInto(table string) db.QueryInserter { - qi := &QueryInserter{ - builder: b, - table: table, - } - - qi.stringer = &stringer{qi, b.t.Template} - return qi -} - -func (b *Builder) DeleteFrom(table string) db.QueryDeleter { - qd := &QueryDeleter{ - builder: b, - table: table, - } - - qd.stringer = &stringer{qd, b.t.Template} - return qd -} - -func (b *Builder) Update(table string) db.QueryUpdater { - qu := &QueryUpdater{ - builder: b, - table: table, - columnValues: &sqlgen.ColumnValues{}, - } - - qu.stringer = &stringer{qu, b.t.Template} - return qu -} - -type QueryInserter struct { - *stringer - builder *Builder - table string - values []*sqlgen.Values - columns []sqlgen.Fragment - arguments []interface{} - extra string -} - -func (qi *QueryInserter) Extra(s string) db.QueryInserter { - qi.extra = s - return qi -} - -func (qi *QueryInserter) Exec() (sql.Result, error) { - return qi.builder.sess.Exec(qi.statement()) -} - -func (qi *QueryInserter) Query() (*sqlx.Rows, error) { - return qi.builder.sess.Query(qi.statement(), qi.arguments...) -} - -func (qi *QueryInserter) QueryRow() (*sqlx.Row, error) { - return qi.builder.sess.QueryRow(qi.statement(), qi.arguments...) -} - -func (qi *QueryInserter) Iterator() db.Iterator { - rows, err := qi.builder.sess.Query(qi.statement(), qi.arguments...) - return &iterator{rows, err} -} - -func (qi *QueryInserter) Columns(columns ...string) db.QueryInserter { - l := len(columns) - f := make([]sqlgen.Fragment, l) - for i := 0; i < l; i++ { - f[i] = sqlgen.ColumnWithName(columns[i]) - } - qi.columns = append(qi.columns, f...) - return qi -} - -func (qi *QueryInserter) Values(values ...interface{}) db.QueryInserter { - if len(qi.columns) == 0 && len(values) == 1 { - ff, vv, _ := Map(values[0]) - - columns, vals, arguments, _ := qi.builder.t.ToColumnsValuesAndArguments(ff, vv) - - qi.arguments = append(qi.arguments, arguments...) - qi.values = append(qi.values, vals) - - for _, c := range columns.Columns { - qi.columns = append(qi.columns, c) - } - } else if len(qi.columns) == 0 || len(values) == len(qi.columns) { - qi.arguments = append(qi.arguments, values...) - - l := len(values) - placeholders := make([]sqlgen.Fragment, l) - for i := 0; i < l; i++ { - placeholders[i] = sqlgen.RawValue(`?`) - } - qi.values = append(qi.values, sqlgen.NewValueGroup(placeholders...)) - } - - return qi -} - -func (qi *QueryInserter) statement() *sqlgen.Statement { - stmt := &sqlgen.Statement{ - Type: sqlgen.Insert, - Table: sqlgen.TableWithName(qi.table), - Extra: sqlgen.Extra(qi.extra), - } - - if len(qi.values) > 0 { - stmt.Values = sqlgen.JoinValueGroups(qi.values...) - } - - if len(qi.columns) > 0 { - stmt.Columns = sqlgen.JoinColumns(qi.columns...) - } - return stmt -} - -type QueryDeleter struct { - *stringer - builder *Builder - table string - limit int - where *sqlgen.Where - arguments []interface{} -} - -func (qd *QueryDeleter) Where(terms ...interface{}) db.QueryDeleter { - where, arguments := qd.builder.t.ToWhereWithArguments(terms) - qd.where = &where - qd.arguments = append(qd.arguments, arguments...) - return qd -} - -func (qd *QueryDeleter) Limit(limit int) db.QueryDeleter { - qd.limit = limit - return qd -} - -func (qd *QueryDeleter) Exec() (sql.Result, error) { - return qd.builder.sess.Exec(qd.statement(), qd.arguments...) -} - -func (qd *QueryDeleter) statement() *sqlgen.Statement { - stmt := &sqlgen.Statement{ - Type: sqlgen.Delete, - Table: sqlgen.TableWithName(qd.table), - } - - if qd.Where != nil { - stmt.Where = qd.where - } - - if qd.limit != 0 { - stmt.Limit = sqlgen.Limit(qd.limit) - } - - return stmt -} - -type QueryUpdater struct { - *stringer - builder *Builder - table string - columnValues *sqlgen.ColumnValues - limit int - where *sqlgen.Where - arguments []interface{} -} - -func (qu *QueryUpdater) Set(terms ...interface{}) db.QueryUpdater { - if len(terms) == 1 { - ff, vv, _ := Map(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 -} - -func (qu *QueryUpdater) Where(terms ...interface{}) db.QueryUpdater { - where, arguments := qu.builder.t.ToWhereWithArguments(terms) - qu.where = &where - qu.arguments = append(qu.arguments, arguments...) - return qu -} - -func (qu *QueryUpdater) Exec() (sql.Result, error) { - return qu.builder.sess.Exec(qu.statement(), qu.arguments...) -} - -func (qu *QueryUpdater) Limit(limit int) db.QueryUpdater { - qu.limit = limit - return qu -} - -func (qu *QueryUpdater) statement() *sqlgen.Statement { - stmt := &sqlgen.Statement{ - Type: sqlgen.Update, - Table: sqlgen.TableWithName(qu.table), - ColumnValues: qu.columnValues, - } - - if qu.Where != nil { - stmt.Where = qu.where - } - - if qu.limit != 0 { - stmt.Limit = sqlgen.Limit(qu.limit) - } - - 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 - builder *Builder - table string - where *sqlgen.Where - groupBy *sqlgen.GroupBy - orderBy sqlgen.OrderBy - limit sqlgen.Limit - offset sqlgen.Offset - columns *sqlgen.Columns - joins []*sqlgen.Join - arguments []interface{} - err error -} - -func (qs *QuerySelector) From(tables ...string) db.QuerySelector { - qs.table = strings.Join(tables, ",") - 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 -} - -func (qs *QuerySelector) Where(terms ...interface{}) db.QuerySelector { - where, arguments := qs.builder.t.ToWhereWithArguments(terms) - qs.where = &where - qs.arguments = append(qs.arguments, arguments...) - return qs -} - -func (qs *QuerySelector) GroupBy(columns ...interface{}) db.QuerySelector { - var fragments []sqlgen.Fragment - fragments, qs.err = columnFragments(qs.builder.t, columns) - if fragments != nil { - qs.groupBy = sqlgen.GroupByColumns(fragments...) - } - return qs -} - -func (qs *QuerySelector) OrderBy(columns ...interface{}) db.QuerySelector { - var sortColumns sqlgen.SortColumns - - for i := range columns { - var sort *sqlgen.SortColumn - - switch value := columns[i].(type) { - case db.Raw: - sort = &sqlgen.SortColumn{ - Column: sqlgen.RawValue(fmt.Sprintf(`%v`, value.Value)), - } - case string: - if strings.HasPrefix(value, `-`) { - sort = &sqlgen.SortColumn{ - Column: sqlgen.ColumnWithName(value[1:]), - Order: sqlgen.Descendent, - } - } else { - sort = &sqlgen.SortColumn{ - Column: sqlgen.ColumnWithName(value), - Order: sqlgen.Ascendent, - } - } - } - sortColumns.Columns = append(sortColumns.Columns, sort) - } - - qs.orderBy.SortColumns = &sortColumns - - return qs -} - -func (qs *QuerySelector) Using(columns ...interface{}) db.QuerySelector { - if len(qs.joins) == 0 { - qs.err = errors.New(`Cannot use Using() without a preceding Join() expression.`) - return qs - } - - lastJoin := qs.joins[len(qs.joins)-1] - - if lastJoin.On != nil { - qs.err = errors.New(`Cannot use Using() and On() with the same Join() expression.`) - return qs - } - - fragments, err := columnFragments(qs.builder.t, columns) - if err != nil { - qs.err = err - return qs - } - - lastJoin.Using = sqlgen.UsingColumns(fragments...) - return qs -} - -func (qs *QuerySelector) pushJoin(t string, tables []interface{}) db.QuerySelector { - if qs.joins == nil { - qs.joins = []*sqlgen.Join{} - } - - tableNames := make([]string, len(tables)) - for i := range tables { - tableNames[i] = fmt.Sprintf("%s", tables[i]) - } - - qs.joins = append(qs.joins, - &sqlgen.Join{ - Type: t, - Table: sqlgen.TableWithName(strings.Join(tableNames, ", ")), - }, - ) - - return qs -} - -func (qs *QuerySelector) FullJoin(tables ...interface{}) db.QuerySelector { - return qs.pushJoin("FULL", tables) -} - -func (qs *QuerySelector) CrossJoin(tables ...interface{}) db.QuerySelector { - return qs.pushJoin("CROSS", tables) -} - -func (qs *QuerySelector) RightJoin(tables ...interface{}) db.QuerySelector { - return qs.pushJoin("RIGHT", tables) -} - -func (qs *QuerySelector) LeftJoin(tables ...interface{}) db.QuerySelector { - return qs.pushJoin("LEFT", tables) -} - -func (qs *QuerySelector) Join(tables ...interface{}) db.QuerySelector { - return qs.pushJoin("", tables) -} - -func (qs *QuerySelector) On(terms ...interface{}) db.QuerySelector { - if len(qs.joins) == 0 { - qs.err = errors.New(`Cannot use On() without a preceding Join() expression.`) - return qs - } - - lastJoin := qs.joins[len(qs.joins)-1] - - if lastJoin.On != nil { - qs.err = errors.New(`Cannot use Using() and On() with the same Join() expression.`) - return qs - } - - w, a := qs.builder.t.ToWhereWithArguments(terms) - o := sqlgen.On(w) - lastJoin.On = &o - - qs.arguments = append(qs.arguments, a...) - return qs -} - -func (qs *QuerySelector) Limit(n int) db.QuerySelector { - qs.limit = sqlgen.Limit(n) - return qs -} - -func (qs *QuerySelector) Offset(n int) db.QuerySelector { - qs.offset = sqlgen.Offset(n) - return qs -} - -func (qs *QuerySelector) statement() *sqlgen.Statement { - return &sqlgen.Statement{ - Type: sqlgen.Select, - Table: sqlgen.TableWithName(qs.table), - Columns: qs.columns, - Limit: qs.limit, - Offset: qs.offset, - Joins: sqlgen.JoinConditions(qs.joins...), - Where: qs.where, - OrderBy: &qs.orderBy, - GroupBy: qs.groupBy, - } -} - -func (qs *QuerySelector) Query() (*sqlx.Rows, error) { - return qs.builder.sess.Query(qs.statement(), qs.arguments...) -} - -func (qs *QuerySelector) QueryRow() (*sqlx.Row, error) { - return qs.builder.sess.QueryRow(qs.statement(), qs.arguments...) -} - -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) { - l := len(columns) - f := make([]sqlgen.Fragment, l) - - for i := 0; i < l; i++ { - switch v := columns[i].(type) { - case db.Func: - var s string - a := template.ToInterfaceArguments(v.Args) - if len(a) == 0 { - s = fmt.Sprintf(`%s()`, v.Name) - } else { - ss := make([]string, 0, len(a)) - for j := range a { - ss = append(ss, fmt.Sprintf(`%v`, a[j])) - } - s = fmt.Sprintf(`%s(%s)`, v.Name, strings.Join(ss, `, `)) - } - f[i] = sqlgen.RawValue(s) - case db.Raw: - f[i] = sqlgen.RawValue(fmt.Sprintf("%v", v.Value)) - case sqlgen.Fragment: - f[i] = v - case string: - f[i] = sqlgen.ColumnWithName(v) - case interface{}: - f[i] = sqlgen.ColumnWithName(fmt.Sprintf("%v", v)) - default: - return nil, fmt.Errorf("Unexpected argument type %T for Select() argument.", v) - } - } - - return f, nil -} - -type hasStatement interface { - statement() *sqlgen.Statement -} - -type stringer struct { - i hasStatement - t *sqlgen.Template -} - -func (s *stringer) String() string { - if s != nil && s.i != nil { - q := s.compileAndReplacePlaceholders(s.i.statement()) - q = reInvisibleChars.ReplaceAllString(q, ` `) - return strings.TrimSpace(q) - } - return "" -} - -func (s *stringer) compileAndReplacePlaceholders(stmt *sqlgen.Statement) (query string) { - buf := stmt.Compile(s.t) - - j := 1 - for i := range buf { - if buf[i] == '?' { - query = query + "$" + strconv.Itoa(j) - j++ - } else { - query = query + string(buf[i]) - } - } - - return query -} - -func NewBuilder(sess sqlDatabase, t *sqlgen.Template) *Builder { - return &Builder{ - sess: sess, - t: sqlutil.NewTemplateWithUtils(t), - } -} - -func (iter *iterator) Scan(dst ...interface{}) error { - if iter.err != nil { - return iter.err - } - return iter.cursor.Scan(dst...) -} - -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 - } - - switch len(dst) { - case 0: - if ok := iter.cursor.Next(); !ok { - iter.err = iter.cursor.Err() - iter.Close() - return false - } - return true - case 1: - if err = sqlutil.FetchRow(iter.cursor, dst[0]); err != nil { - iter.err = err - iter.Close() - return false - } - return true - } - - iter.err = db.ErrUnsupported - return false -} - -func (iter *iterator) Close() (err error) { - if iter.cursor != nil { - err = iter.cursor.Close() - iter.cursor = nil - } - return err -} - -func Map(item interface{}) ([]string, []interface{}, error) { - var fv fieldValue - - 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) - - fv.values = make([]interface{}, 0, nfields) - fv.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 - } - } - - fv.fields = append(fv.fields, fi.Name) - v, err := marshal(value) - if err != nil { - return nil, nil, err - } - fv.values = append(fv.values, v) - } - - case reflect.Map: - nfields := itemV.Len() - fv.values = make([]interface{}, nfields) - fv.fields = make([]string, nfields) - mkeys := itemV.MapKeys() - - for i, keyV := range mkeys { - valv := itemV.MapIndex(keyV) - fv.fields[i] = fmt.Sprintf("%v", keyV.Interface()) - - v, err := marshal(valv.Interface()) - if err != nil { - return nil, nil, err - } - - fv.values[i] = v - } - default: - return nil, nil, db.ErrExpectingMapOrStruct - } - - sort.Sort(&fv) - - return fv.fields, fv.values, nil -} - -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 -} diff --git a/builder/builder_test.go b/builder/builder_test.go deleted file mode 100644 index aa510156..00000000 --- a/builder/builder_test.go +++ /dev/null @@ -1,236 +0,0 @@ -package builder - -import ( - "github.com/stretchr/testify/assert" - "testing" - "upper.io/db" - "upper.io/db/util/sqlutil" -) - -func TestSelect(t *testing.T) { - - b := &Builder{t: sqlutil.NewTemplateWithUtils(&testTemplate)} - assert := assert.New(t) - - assert.Equal( - `SELECT * FROM "artist"`, - b.SelectAllFrom("artist").String(), - ) - - assert.Equal( - `SELECT * FROM "artist"`, - b.Select().From("artist").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" LIMIT -1 OFFSET 5`, - b.Select().From("artist").Limit(-1).Offset(5).String(), - ) - - assert.Equal( - `SELECT "id" FROM "artist"`, - b.Select("id").From("artist").String(), - ) - - assert.Equal( - `SELECT "id", "name" FROM "artist"`, - b.Select("id", "name").From("artist").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE ("name" = $1)`, - b.SelectAllFrom("artist").Where("name", "Haruki").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE (name LIKE $1)`, - b.SelectAllFrom("artist").Where("name LIKE ?", `%F%`).String(), - ) - - assert.Equal( - `SELECT "id" FROM "artist" WHERE (name LIKE $1 OR name LIKE $2)`, - b.Select("id").From("artist").Where(`name LIKE ? OR name LIKE ?`, `%Miya%`, `F%`).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE ("id" > $1)`, - b.SelectAllFrom("artist").Where("id >", 2).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE (id <= 2 AND name != $1)`, - b.SelectAllFrom("artist").Where("id <= 2 AND name != ?", "A").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE ("id" IN ($1, $2, $3, $4))`, - b.SelectAllFrom("artist").Where("id IN", []int{1, 9, 8, 7}).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" WHERE (name IS NOT NULL)`, - b.SelectAllFrom("artist").Where("name IS NOT NULL").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" AS "a", "publication" AS "p" WHERE (p.author_id = a.id) LIMIT 1`, - b.Select().From("artist a", "publication as p").Where("p.author_id = a.id").Limit(1).String(), - ) - - assert.Equal( - `SELECT "id" FROM "artist" NATURAL JOIN "publication"`, - b.Select("id").From("artist").Join("publication").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.author_id = a.id) LIMIT 1`, - b.SelectAllFrom("artist a").Join("publication p").On("p.author_id = a.id").Limit(1).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.author_id = a.id) WHERE ("a"."id" = $1) LIMIT 1`, - b.SelectAllFrom("artist a").Join("publication p").On("p.author_id = a.id").Where("a.id", 2).Limit(1).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" JOIN "publication" AS "p" ON (p.author_id = a.id) WHERE (a.id = 2) LIMIT 1`, - b.SelectAllFrom("artist").Join("publication p").On("p.author_id = a.id").Where("a.id = 2").Limit(1).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" AS "a" JOIN "publication" AS "p" ON (p.title LIKE $1 OR p.title LIKE $2) WHERE (a.id = $3) LIMIT 1`, - b.SelectAllFrom("artist a").Join("publication p").On("p.title LIKE ? OR p.title LIKE ?", "%Totoro%", "%Robot%").Where("a.id = ?", 2).Limit(1).String(), - ) - - assert.Equal( - `SELECT * FROM "artist" AS "a" LEFT JOIN "publication" AS "p1" ON (p1.id = a.id) RIGHT JOIN "publication" AS "p2" ON (p2.id = a.id)`, - b.SelectAllFrom("artist a"). - LeftJoin("publication p1").On("p1.id = a.id"). - RightJoin("publication p2").On("p2.id = a.id"). - String(), - ) - - assert.Equal( - `SELECT * FROM "artist" CROSS JOIN "publication"`, - b.SelectAllFrom("artist").CrossJoin("publication").String(), - ) - - assert.Equal( - `SELECT * FROM "artist" JOIN "publication" USING ("id")`, - b.SelectAllFrom("artist").Join("publication").Using("id").String(), - ) - - assert.Equal( - `SELECT DATE()`, - b.Select(db.Raw{"DATE()"}).String(), - ) -} - -func TestInsert(t *testing.T) { - b := &Builder{t: sqlutil.NewTemplateWithUtils(&testTemplate)} - assert := assert.New(t) - - assert.Equal( - `INSERT INTO "artist" VALUES ($1, $2), ($3, $4), ($5, $6)`, - b.InsertInto("artist"). - Values(10, "Ryuichi Sakamoto"). - Values(11, "Alondra de la Parra"). - Values(12, "Haruki Murakami"). - String(), - ) - - assert.Equal( - `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, - b.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).String(), - ) - - assert.Equal( - `INSERT INTO "artist" ("id", "name") VALUES ($1, $2) RETURNING "id"`, - b.InsertInto("artist").Values(map[string]string{"id": "12", "name": "Chavela Vargas"}).Extra(`RETURNING "id"`).String(), - ) - - assert.Equal( - `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, - b.InsertInto("artist").Values(map[string]interface{}{"name": "Chavela Vargas", "id": 12}).String(), - ) - - assert.Equal( - `INSERT INTO "artist" ("id", "name") VALUES ($1, $2)`, - b.InsertInto("artist").Values(struct { - ID int `db:"id"` - Name string `db:"name"` - }{12, "Chavela Vargas"}).String(), - ) - - assert.Equal( - `INSERT INTO "artist" ("name", "id") VALUES ($1, $2)`, - b.InsertInto("artist").Columns("name", "id").Values("Chavela Vargas", 12).String(), - ) -} - -func TestUpdate(t *testing.T) { - b := &Builder{t: sqlutil.NewTemplateWithUtils(&testTemplate)} - assert := assert.New(t) - - assert.Equal( - `UPDATE "artist" SET "name" = $1`, - b.Update("artist").Set("name", "Artist").String(), - ) - - assert.Equal( - `UPDATE "artist" SET "name" = $1 WHERE ("id" < $2)`, - 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( - "name = ? || ' ' || ? || id", "Artist", "#", - "id = id + ?", 10, - ).Where("id > ?", 0).String(), - ) -} - -func TestDelete(t *testing.T) { - b := &Builder{t: sqlutil.NewTemplateWithUtils(&testTemplate)} - assert := assert.New(t) - - assert.Equal( - `DELETE FROM "artist" WHERE (name = $1) LIMIT 1`, - b.DeleteFrom("artist").Where("name = ?", "Chavela Vargas").Limit(1).String(), - ) - - assert.Equal( - `DELETE FROM "artist" WHERE (id > 5)`, - b.DeleteFrom("artist").Where("id > 5").String(), - ) -} - -func TestTruncate(t *testing.T) { - b := &Builder{t: sqlutil.NewTemplateWithUtils(&testTemplate)} - assert := assert.New(t) - - assert.Equal( - `TRUNCATE TABLE "artist" RESTART IDENTITY`, - b.TruncateTable("artist").Extra("RESTART IDENTITY").String(), - ) -} diff --git a/builder/template_test.go b/builder/template_test.go deleted file mode 100644 index 157e23bd..00000000 --- a/builder/template_test.go +++ /dev/null @@ -1,193 +0,0 @@ -package builder - -import ( - "upper.io/cache" - "upper.io/db/util/sqlgen" -) - -const ( - defaultColumnSeparator = `.` - defaultIdentifierSeparator = `, ` - defaultIdentifierQuote = `"{{.Value}}"` - defaultValueSeparator = `, ` - defaultValueQuote = `'{{.}}'` - defaultAndKeyword = `AND` - defaultOrKeyword = `OR` - defaultNotKeyword = `NOT` - defaultDescKeyword = `DESC` - defaultAscKeyword = `ASC` - defaultDefaultOperator = `=` - defaultAssignmentOperator = `=` - defaultClauseGroup = `({{.}})` - defaultClauseOperator = ` {{.}} ` - defaultColumnValue = `{{.Column}} {{.Operator}} {{.Value}}` - defaultTableAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - defaultColumnAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - defaultSortByColumnLayout = `{{.Column}} {{.Order}}` - - defaultOrderByLayout = ` - {{if .SortColumns}} - ORDER BY {{.SortColumns}} - {{end}} - ` - - defaultWhereLayout = ` - {{if .Conds}} - WHERE {{.Conds}} - {{end}} - ` - - defaultUsingLayout = ` - {{if .Columns}} - USING ({{.Columns}}) - {{end}} - ` - - defaultJoinLayout = ` - {{if .Table}} - {{ if .On }} - {{.Type}} JOIN {{.Table}} - {{.On}} - {{ else if .Using }} - {{.Type}} JOIN {{.Table}} - {{.Using}} - {{ else if .Type | eq "CROSS" }} - {{.Type}} JOIN {{.Table}} - {{else}} - NATURAL {{.Type}} JOIN {{.Table}} - {{end}} - {{end}} - ` - - defaultOnLayout = ` - {{if .Conds}} - ON {{.Conds}} - {{end}} - ` - - defaultSelectLayout = ` - SELECT - - {{if .Columns}} - {{.Columns}} - {{else}} - * - {{end}} - - {{if .Table}} - FROM {{.Table}} - {{end}} - - {{.Joins}} - - {{.Where}} - - {{.GroupBy}} - - {{.OrderBy}} - - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - defaultDeleteLayout = ` - DELETE - FROM {{.Table}} - {{.Where}} - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - defaultUpdateLayout = ` - UPDATE - {{.Table}} - SET {{.ColumnValues}} - {{ .Where }} - ` - - defaultCountLayout = ` - SELECT - COUNT(1) AS _t - FROM {{.Table}} - {{.Where}} - - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - - defaultInsertLayout = ` - INSERT INTO {{.Table}} - {{if .Columns }}({{.Columns}}){{end}} - VALUES - {{.Values}} - {{.Extra}} - ` - - defaultTruncateLayout = ` - TRUNCATE TABLE {{.Table}} {{.Extra}} - ` - - defaultDropDatabaseLayout = ` - DROP DATABASE {{.Database}} - ` - - defaultDropTableLayout = ` - DROP TABLE {{.Table}} - ` - - defaultGroupByColumnLayout = `{{.Column}}` - - defaultGroupByLayout = ` - {{if .GroupColumns}} - GROUP BY {{.GroupColumns}} - {{end}} - ` -) - -var testTemplate = sqlgen.Template{ - ColumnSeparator: defaultColumnSeparator, - IdentifierSeparator: defaultIdentifierSeparator, - IdentifierQuote: defaultIdentifierQuote, - ValueSeparator: defaultValueSeparator, - ValueQuote: defaultValueQuote, - AndKeyword: defaultAndKeyword, - OrKeyword: defaultOrKeyword, - NotKeyword: defaultNotKeyword, - DescKeyword: defaultDescKeyword, - AscKeyword: defaultAscKeyword, - DefaultOperator: defaultDefaultOperator, - AssignmentOperator: defaultAssignmentOperator, - ClauseGroup: defaultClauseGroup, - ClauseOperator: defaultClauseOperator, - ColumnValue: defaultColumnValue, - TableAliasLayout: defaultTableAliasLayout, - ColumnAliasLayout: defaultColumnAliasLayout, - SortByColumnLayout: defaultSortByColumnLayout, - WhereLayout: defaultWhereLayout, - OnLayout: defaultOnLayout, - UsingLayout: defaultUsingLayout, - JoinLayout: defaultJoinLayout, - OrderByLayout: defaultOrderByLayout, - InsertLayout: defaultInsertLayout, - SelectLayout: defaultSelectLayout, - UpdateLayout: defaultUpdateLayout, - DeleteLayout: defaultDeleteLayout, - TruncateLayout: defaultTruncateLayout, - DropDatabaseLayout: defaultDropDatabaseLayout, - DropTableLayout: defaultDropTableLayout, - CountLayout: defaultCountLayout, - GroupByLayout: defaultGroupByLayout, - Cache: cache.NewCache(), -} diff --git a/util/adapter/nonexistent.go b/internal/adapter/nonexistent.go similarity index 100% rename from util/adapter/nonexistent.go rename to internal/adapter/nonexistent.go diff --git a/util/schema/schema.go b/internal/schema/schema.go similarity index 100% rename from util/schema/schema.go rename to internal/schema/schema.go diff --git a/internal/sqladapter/collection.go b/internal/sqladapter/collection.go index e86e3cb4..7dbd06cb 100644 --- a/internal/sqladapter/collection.go +++ b/internal/sqladapter/collection.go @@ -2,7 +2,7 @@ package sqladapter import ( "upper.io/db" - "upper.io/db/util/sqlutil/result" + "upper.io/db/internal/sqlutil/result" ) type Collection interface { diff --git a/internal/sqladapter/database.go b/internal/sqladapter/database.go index cdf408fb..294d80fa 100644 --- a/internal/sqladapter/database.go +++ b/internal/sqladapter/database.go @@ -8,12 +8,12 @@ import ( "github.com/jmoiron/sqlx" "upper.io/cache" "upper.io/db" - "upper.io/db/builder" - "upper.io/db/util/adapter" - "upper.io/db/util/schema" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/tx" + "upper.io/builder" + "upper.io/db/internal/adapter" + "upper.io/db/internal/schema" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/tx" ) type PartialDatabase interface { diff --git a/mongo/database.go b/mongo/database.go index 7a0a0acc..7277cb48 100644 --- a/mongo/database.go +++ b/mongo/database.go @@ -29,7 +29,7 @@ import ( "gopkg.in/mgo.v2" "upper.io/db" - "upper.io/db/util/adapter" + "upper.io/db/internal/adapter" ) // Adapter holds the name of the mongodb adapter. diff --git a/mongo/result.go b/mongo/result.go index 70823c8c..6839d39d 100644 --- a/mongo/result.go +++ b/mongo/result.go @@ -32,7 +32,7 @@ import ( "gopkg.in/mgo.v2" "gopkg.in/mgo.v2/bson" "upper.io/db" - "upper.io/db/util/sqlutil" + "upper.io/db/internal/sqlutil" ) // result represents a query result. diff --git a/mysql/collection.go b/mysql/collection.go index fb5cccd4..429099da 100644 --- a/mysql/collection.go +++ b/mysql/collection.go @@ -26,9 +26,9 @@ import ( "strings" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/result" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/result" ) type table struct { diff --git a/mysql/database.go b/mysql/database.go index b3fe31a3..aa1c567b 100644 --- a/mysql/database.go +++ b/mysql/database.go @@ -31,11 +31,11 @@ import ( "github.com/jmoiron/sqlx" "upper.io/cache" "upper.io/db" - "upper.io/db/util/adapter" - "upper.io/db/util/schema" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/adapter" + "upper.io/db/internal/schema" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/tx" ) var ( diff --git a/mysql/database_test.go b/mysql/database_test.go index 2f356e82..87f1325e 100644 --- a/mysql/database_test.go +++ b/mysql/database_test.go @@ -36,7 +36,7 @@ import ( "github.com/jmoiron/sqlx" "upper.io/db" - "upper.io/db/util/sqlutil" + "upper.io/db/internal/sqlutil" ) const ( diff --git a/mysql/mysql.go b/mysql/mysql.go index f5ea9f57..d46c200c 100644 --- a/mysql/mysql.go +++ b/mysql/mysql.go @@ -24,8 +24,8 @@ package mysql // import "upper.io/db/mysql" import ( "upper.io/cache" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" ) // Adapter is the public name of the adapter. diff --git a/mysql/tx.go b/mysql/tx.go index 0d0d1738..21b6ecf8 100644 --- a/mysql/tx.go +++ b/mysql/tx.go @@ -22,7 +22,7 @@ package mysql import ( - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/sqlutil/tx" ) type tx struct { diff --git a/postgresql/collection.go b/postgresql/collection.go index 50a8c304..4c1c7272 100644 --- a/postgresql/collection.go +++ b/postgresql/collection.go @@ -27,9 +27,9 @@ import ( "strings" "upper.io/db" - "upper.io/db/builder" + "upper.io/builder" "upper.io/db/internal/sqladapter" - "upper.io/db/util/sqlgen" + "upper.io/builder/sqlgen" ) type table struct { diff --git a/postgresql/database.go b/postgresql/database.go index 9c249779..dfd11052 100644 --- a/postgresql/database.go +++ b/postgresql/database.go @@ -29,7 +29,7 @@ import ( _ "github.com/lib/pq" // PostgreSQL driver. "upper.io/db" "upper.io/db/internal/sqladapter" - "upper.io/db/util/sqlgen" + "upper.io/builder/sqlgen" ) type database struct { diff --git a/postgresql/postgresql.go b/postgresql/postgresql.go index 2c952d11..f0d51db4 100644 --- a/postgresql/postgresql.go +++ b/postgresql/postgresql.go @@ -24,8 +24,8 @@ package postgresql // import "upper.io/db/postgresql" import ( "upper.io/cache" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" ) // Adapter is the public name of the adapter. diff --git a/postgresql/tx.go b/postgresql/tx.go index 89560de7..5a91a828 100644 --- a/postgresql/tx.go +++ b/postgresql/tx.go @@ -23,7 +23,7 @@ package postgresql import ( "upper.io/db" - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/sqlutil/tx" ) type tx struct { diff --git a/ql/collection.go b/ql/collection.go index b6b5fcf3..3687cc4b 100644 --- a/ql/collection.go +++ b/ql/collection.go @@ -27,9 +27,9 @@ import ( "strings" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/result" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/result" ) type table struct { diff --git a/ql/database.go b/ql/database.go index 69a6f861..80607d77 100644 --- a/ql/database.go +++ b/ql/database.go @@ -34,11 +34,11 @@ import ( "github.com/jmoiron/sqlx" "upper.io/cache" "upper.io/db" - "upper.io/db/util/adapter" - "upper.io/db/util/schema" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/adapter" + "upper.io/db/internal/schema" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/tx" ) var ( diff --git a/ql/database_test.go b/ql/database_test.go index bd6abdb0..d7f0f20c 100644 --- a/ql/database_test.go +++ b/ql/database_test.go @@ -40,7 +40,7 @@ import ( "github.com/jmoiron/sqlx" "upper.io/db" - "upper.io/db/util/sqlutil" + "upper.io/db/internal/sqlutil" ) const ( diff --git a/ql/ql.go b/ql/ql.go index 3249a413..3710a8c0 100644 --- a/ql/ql.go +++ b/ql/ql.go @@ -24,8 +24,8 @@ package ql // import "upper.io/db/ql" import ( "upper.io/cache" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" ) // Adapter is the public name of the adapter. diff --git a/ql/tx.go b/ql/tx.go index b6659a48..9bf3b26d 100644 --- a/ql/tx.go +++ b/ql/tx.go @@ -22,7 +22,7 @@ package ql import ( - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/sqlutil/tx" ) type tx struct { diff --git a/sqlite/collection.go b/sqlite/collection.go index 59442646..fe5d27ea 100644 --- a/sqlite/collection.go +++ b/sqlite/collection.go @@ -27,9 +27,9 @@ import ( "database/sql" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/result" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/result" ) type table struct { diff --git a/sqlite/database.go b/sqlite/database.go index 052f672d..453d6745 100644 --- a/sqlite/database.go +++ b/sqlite/database.go @@ -34,11 +34,11 @@ import ( _ "github.com/mattn/go-sqlite3" // SQLite3 driver. "upper.io/cache" "upper.io/db" - "upper.io/db/util/adapter" - "upper.io/db/util/schema" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/adapter" + "upper.io/db/internal/schema" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" + "upper.io/db/internal/sqlutil/tx" ) var ( diff --git a/sqlite/database_test.go b/sqlite/database_test.go index 321753c1..a7557e31 100644 --- a/sqlite/database_test.go +++ b/sqlite/database_test.go @@ -42,7 +42,7 @@ import ( "github.com/jmoiron/sqlx" "upper.io/db" - "upper.io/db/util/sqlutil" + "upper.io/db/internal/sqlutil" ) const ( diff --git a/sqlite/sqlite.go b/sqlite/sqlite.go index 753c217a..865d6b15 100644 --- a/sqlite/sqlite.go +++ b/sqlite/sqlite.go @@ -24,8 +24,8 @@ package sqlite // import "upper.io/db/sqlite" import ( "upper.io/cache" "upper.io/db" - "upper.io/db/util/sqlgen" - "upper.io/db/util/sqlutil" + "upper.io/builder/sqlgen" + "upper.io/db/internal/sqlutil" ) // Adapter is the public name of the adapter. diff --git a/sqlite/tx.go b/sqlite/tx.go index 99bbe9f9..a7032308 100644 --- a/sqlite/tx.go +++ b/sqlite/tx.go @@ -22,7 +22,7 @@ package sqlite import ( - "upper.io/db/util/sqlutil/tx" + "upper.io/db/internal/sqlutil/tx" ) type tx struct { diff --git a/util/sqlgen/column.go b/util/sqlgen/column.go deleted file mode 100644 index 4eb395e1..00000000 --- a/util/sqlgen/column.go +++ /dev/null @@ -1,91 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -type columnT struct { - Name string - Alias string -} - -// Column represents a SQL column. -type Column struct { - Name interface{} - hash string -} - -// ColumnWithName creates and returns a Column with the given name. -func ColumnWithName(name string) *Column { - return &Column{Name: name} -} - -// Hash returns a unique identifier. -func (c *Column) Hash() string { - if c.hash == "" { - var s string - - switch t := c.Name.(type) { - case Fragment: - s = t.Hash() - case fmt.Stringer: - s = t.String() - case string: - s = t - default: - s = fmt.Sprintf("%v", c.Name) - } - - c.hash = fmt.Sprintf(`Column{Name:%q}`, s) - } - - return c.hash -} - -// Compile transforms the ColumnValue into an equivalent SQL representation. -func (c *Column) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(c); ok { - return z - } - - switch value := c.Name.(type) { - case string: - input := trimString(value) - - chunks := separateByAS(input) - - if len(chunks) == 1 { - chunks = separateBySpace(input) - } - - name := chunks[0] - - nameChunks := strings.SplitN(name, layout.ColumnSeparator, 2) - - for i := range nameChunks { - nameChunks[i] = trimString(nameChunks[i]) - nameChunks[i] = mustParse(layout.IdentifierQuote, Raw{Value: nameChunks[i]}) - } - - name = strings.Join(nameChunks, layout.ColumnSeparator) - - var alias string - - if len(chunks) > 1 { - alias = trimString(chunks[1]) - alias = mustParse(layout.IdentifierQuote, Raw{Value: alias}) - } - - compiled = mustParse(layout.ColumnAliasLayout, columnT{name, alias}) - case Raw: - compiled = value.String() - default: - compiled = fmt.Sprintf("%v", c.Name) - } - - layout.Write(c, compiled) - - return -} diff --git a/util/sqlgen/column_test.go b/util/sqlgen/column_test.go deleted file mode 100644 index e5fdb7d8..00000000 --- a/util/sqlgen/column_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package sqlgen - -import ( - "fmt" - "testing" -) - -func TestColumnHash(t *testing.T) { - var s, e string - - column := Column{Name: "role.name"} - - s = column.Hash() - e = fmt.Sprintf(`Column{Name:"%s"}`, column.Name) - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnString(t *testing.T) { - var s, e string - - column := Column{Name: "role.name"} - - s = column.Compile(defaultTemplate) - e = `"role"."name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnAs(t *testing.T) { - var s, e string - - column := Column{Name: "role.name as foo"} - - s = column.Compile(defaultTemplate) - e = `"role"."name" AS "foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnImplicitAs(t *testing.T) { - var s, e string - - column := Column{Name: "role.name foo"} - - s = column.Compile(defaultTemplate) - e = `"role"."name" AS "foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnRaw(t *testing.T) { - var s, e string - - column := Column{Name: Raw{Value: "role.name As foo"}} - - s = column.Compile(defaultTemplate) - e = `role.name As foo` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkColumnWithName(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = ColumnWithName("a") - } -} - -func BenchmarkColumnHash(b *testing.B) { - c := Column{Name: "name"} - for i := 0; i < b.N; i++ { - c.Hash() - } -} - -func BenchmarkColumnCompile(b *testing.B) { - c := Column{Name: "name"} - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkColumnCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - c := Column{Name: "name"} - c.Compile(defaultTemplate) - } -} - -func BenchmarkColumnWithDotCompile(b *testing.B) { - c := Column{Name: "role.name"} - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkColumnWithImplicitAsKeywordCompile(b *testing.B) { - c := Column{Name: "role.name foo"} - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkColumnWithAsKeywordCompile(b *testing.B) { - c := Column{Name: "role.name AS foo"} - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/column_value.go b/util/sqlgen/column_value.go deleted file mode 100644 index 9674b41a..00000000 --- a/util/sqlgen/column_value.go +++ /dev/null @@ -1,101 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -// ColumnValue represents a bundle between a column and a corresponding value. -type ColumnValue struct { - Column Fragment - Operator string - Value Fragment - hash string -} - -type columnValueT struct { - Column string - Operator string - Value string -} - -// Hash returns a unique identifier. -func (c *ColumnValue) Hash() string { - if c.hash == "" { - c.hash = fmt.Sprintf(`ColumnValue{Name:%q, Operator:%q, Value:%q}`, c.Column.Hash(), c.Operator, c.Value.Hash()) - } - return c.hash -} - -// Compile transforms the ColumnValue into an equivalent SQL representation. -func (c *ColumnValue) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(c); ok { - return z - } - - data := columnValueT{ - c.Column.Compile(layout), - c.Operator, - c.Value.Compile(layout), - } - - compiled = mustParse(layout.ColumnValue, data) - - layout.Write(c, compiled) - - return -} - -// ColumnValues represents an array of ColumnValue -type ColumnValues struct { - ColumnValues []Fragment - hash string -} - -// JoinColumnValues returns an array of ColumnValue -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 == "" { - s := make([]string, len(c.ColumnValues)) - for i := range c.ColumnValues { - s[i] = c.ColumnValues[i].Hash() - } - c.hash = fmt.Sprintf("ColumnValues{ColumnValues:{%s}}", strings.Join(s, ", ")) - } - return c.hash -} - -// Compile transforms the ColumnValues into its SQL representation. -func (c *ColumnValues) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(c); ok { - return z - } - - l := len(c.ColumnValues) - - out := make([]string, l) - - for i := range c.ColumnValues { - out[i] = c.ColumnValues[i].Compile(layout) - } - - compiled = strings.Join(out, layout.IdentifierSeparator) - - layout.Write(c, compiled) - - return -} diff --git a/util/sqlgen/column_value_test.go b/util/sqlgen/column_value_test.go deleted file mode 100644 index 9a954697..00000000 --- a/util/sqlgen/column_value_test.go +++ /dev/null @@ -1,156 +0,0 @@ -package sqlgen - -import ( - "fmt" - "testing" -) - -func TestColumnValueHash(t *testing.T) { - var s, e string - - c := &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)} - - s = c.Hash() - e = fmt.Sprintf(`ColumnValue{Name:%q, Operator:%q, Value:%q}`, c.Column.Hash(), c.Operator, c.Value.Hash()) - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnValuesHash(t *testing.T) { - var s, e string - - c := JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)}, - &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(2)}, - ) - - s = c.Hash() - - e = fmt.Sprintf(`ColumnValues{ColumnValues:{%s, %s}}`, c.ColumnValues[0].Hash(), c.ColumnValues[1].Hash()) - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnValue(t *testing.T) { - var s, e string - var cv *ColumnValue - - cv = &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)} - - s = cv.Compile(defaultTemplate) - e = `"id" = '1'` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - cv = &ColumnValue{Column: ColumnWithName("date"), Operator: "=", Value: NewValue(RawValue("NOW()"))} - - s = cv.Compile(defaultTemplate) - e = `"date" = NOW()` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestColumnValues(t *testing.T) { - var s, e string - - cvs := JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)}, - &ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(&Raw{Value: "100"})}, - &ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")}, - &ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(&Raw{Value: "NOW()"})}, - &ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(&Raw{Value: "NOW()"})}, - ) - - s = cvs.Compile(defaultTemplate) - e = `"id" > '8', "other"."id" < 100, "name" = 'Haruki Murakami', "created" >= NOW(), "modified" <= NOW()` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkNewColumnValue(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = &ColumnValue{Column: ColumnWithName("a"), Operator: "=", Value: NewValue(Raw{Value: "7"})} - } -} - -func BenchmarkColumnValueHash(b *testing.B) { - cv := &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)} - for i := 0; i < b.N; i++ { - cv.Hash() - } -} - -func BenchmarkColumnValueCompile(b *testing.B) { - cv := &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)} - for i := 0; i < b.N; i++ { - cv.Compile(defaultTemplate) - } -} - -func BenchmarkColumnValueCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - cv := &ColumnValue{Column: ColumnWithName("id"), Operator: "=", Value: NewValue(1)} - cv.Compile(defaultTemplate) - } -} - -func BenchmarkJoinColumnValues(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)}, - &ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})}, - &ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")}, - &ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})}, - &ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})}, - ) - } -} - -func BenchmarkColumnValuesHash(b *testing.B) { - cvs := JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)}, - &ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})}, - &ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")}, - &ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})}, - &ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})}, - ) - for i := 0; i < b.N; i++ { - cvs.Hash() - } -} - -func BenchmarkColumnValuesCompile(b *testing.B) { - cvs := JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)}, - &ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})}, - &ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")}, - &ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})}, - &ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})}, - ) - for i := 0; i < b.N; i++ { - cvs.Compile(defaultTemplate) - } -} - -func BenchmarkColumnValuesCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - cvs := JoinColumnValues( - &ColumnValue{Column: ColumnWithName("id"), Operator: ">", Value: NewValue(8)}, - &ColumnValue{Column: ColumnWithName("other.id"), Operator: "<", Value: NewValue(Raw{Value: "100"})}, - &ColumnValue{Column: ColumnWithName("name"), Operator: "=", Value: NewValue("Haruki Murakami")}, - &ColumnValue{Column: ColumnWithName("created"), Operator: ">=", Value: NewValue(Raw{Value: "NOW()"})}, - &ColumnValue{Column: ColumnWithName("modified"), Operator: "<=", Value: NewValue(Raw{Value: "NOW()"})}, - ) - cvs.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/columns.go b/util/sqlgen/columns.go deleted file mode 100644 index eb105180..00000000 --- a/util/sqlgen/columns.go +++ /dev/null @@ -1,63 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -// Columns represents an array of Column. -type Columns struct { - Columns []Fragment - hash string -} - -// Hash returns a unique identifier. -func (c *Columns) Hash() string { - if c.hash == "" { - s := make([]string, len(c.Columns)) - for i := range c.Columns { - s[i] = c.Columns[i].Hash() - } - c.hash = fmt.Sprintf("Columns{Columns:{%s}}", strings.Join(s, ", ")) - } - return c.hash -} - -// JoinColumns creates and returns an array of Column. -func JoinColumns(columns ...Fragment) *Columns { - return &Columns{Columns: columns} -} - -// OnConditions creates and retuens a new On. -func OnConditions(conditions ...Fragment) *On { - return &On{Conditions: conditions} -} - -// UsingColumns builds a Using from the given columns. -func UsingColumns(columns ...Fragment) *Using { - return &Using{Columns: columns} -} - -// Compile transforms the Columns into an equivalent SQL representation. -func (c *Columns) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(c); ok { - return z - } - - l := len(c.Columns) - - if l > 0 { - out := make([]string, l) - - for i := 0; i < l; i++ { - out[i] = c.Columns[i].Compile(layout) - } - - compiled = strings.Join(out, layout.IdentifierSeparator) - } - - layout.Write(c, compiled) - - return -} diff --git a/util/sqlgen/columns_test.go b/util/sqlgen/columns_test.go deleted file mode 100644 index a4f43979..00000000 --- a/util/sqlgen/columns_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestColumns(t *testing.T) { - var s, e string - - columns := JoinColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - - s = columns.Compile(defaultTemplate) - e = `"id", "customer", "service_id", "role"."name", "role"."id"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkJoinColumns(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = JoinColumns( - &Column{Name: "a"}, - &Column{Name: "b"}, - &Column{Name: "c"}, - ) - } -} - -func BenchmarkColumnsHash(b *testing.B) { - c := JoinColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - for i := 0; i < b.N; i++ { - c.Hash() - } -} - -func BenchmarkColumnsCompile(b *testing.B) { - c := JoinColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkColumnsCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - c := JoinColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - c.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/database.go b/util/sqlgen/database.go deleted file mode 100644 index df7001dd..00000000 --- a/util/sqlgen/database.go +++ /dev/null @@ -1,37 +0,0 @@ -package sqlgen - -import ( - "fmt" -) - -// Database represents a SQL database. -type Database struct { - Name string - hash string -} - -// DatabaseWithName returns a Database with the given name. -func DatabaseWithName(name string) *Database { - return &Database{Name: name} -} - -// Hash returns a unique identifier. -func (d *Database) Hash() string { - if d.hash == "" { - d.hash = fmt.Sprintf(`Database{Name:%q}`, d.Name) - } - return d.hash -} - -// Compile transforms the Database into an equivalent SQL representation. -func (d *Database) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(d); ok { - return c - } - - compiled = mustParse(layout.IdentifierQuote, Raw{Value: d.Name}) - - layout.Write(d, compiled) - - return -} diff --git a/util/sqlgen/database_test.go b/util/sqlgen/database_test.go deleted file mode 100644 index 33b1ad82..00000000 --- a/util/sqlgen/database_test.go +++ /dev/null @@ -1,53 +0,0 @@ -package sqlgen - -import ( - "fmt" - "testing" -) - -func TestDatabaseHash(t *testing.T) { - var s, e string - - column := Database{Name: "users"} - - s = column.Hash() - e = fmt.Sprintf(`Database{Name:"%s"}`, column.Name) - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestDatabaseCompile(t *testing.T) { - var s, e string - - column := Database{Name: "name"} - - s = column.Compile(defaultTemplate) - e = `"name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkDatabaseHash(b *testing.B) { - c := Database{Name: "name"} - for i := 0; i < b.N; i++ { - c.Hash() - } -} - -func BenchmarkDatabaseCompile(b *testing.B) { - c := Database{Name: "name"} - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkDatabaseCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - c := Database{Name: "name"} - c.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/default.go b/util/sqlgen/default.go deleted file mode 100644 index db531ccd..00000000 --- a/util/sqlgen/default.go +++ /dev/null @@ -1,192 +0,0 @@ -package sqlgen - -import ( - "upper.io/cache" -) - -const ( - defaultColumnSeparator = `.` - defaultIdentifierSeparator = `, ` - defaultIdentifierQuote = `"{{.Value}}"` - defaultValueSeparator = `, ` - defaultValueQuote = `'{{.}}'` - defaultAndKeyword = `AND` - defaultOrKeyword = `OR` - defaultNotKeyword = `NOT` - defaultDescKeyword = `DESC` - defaultAscKeyword = `ASC` - defaultDefaultOperator = `=` - defaultAssignmentOperator = `=` - defaultClauseGroup = `({{.}})` - defaultClauseOperator = ` {{.}} ` - defaultColumnValue = `{{.Column}} {{.Operator}} {{.Value}}` - defaultTableAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - defaultColumnAliasLayout = `{{.Name}}{{if .Alias}} AS {{.Alias}}{{end}}` - defaultSortByColumnLayout = `{{.Column}} {{.Order}}` - - defaultOrderByLayout = ` - {{if .SortColumns}} - ORDER BY {{.SortColumns}} - {{end}} - ` - - defaultWhereLayout = ` - {{if .Conds}} - WHERE {{.Conds}} - {{end}} - ` - - defaultUsingLayout = ` - {{if .Columns}} - USING ({{.Columns}}) - {{end}} - ` - - defaultJoinLayout = ` - {{if .Table}} - {{ if .On }} - {{.Type}} JOIN {{.Table}} - {{.On}} - {{ else if .Using }} - {{.Type}} JOIN {{.Table}} - {{.Using}} - {{ else if .Type | eq "CROSS" }} - {{.Type}} JOIN {{.Table}} - {{else}} - NATURAL {{.Type}} JOIN {{.Table}} - {{end}} - {{end}} - ` - - defaultOnLayout = ` - {{if .Conds}} - ON {{.Conds}} - {{end}} - ` - - defaultSelectLayout = ` - SELECT - - {{if .Columns}} - {{.Columns}} - {{else}} - * - {{end}} - - {{if .Table}} - FROM {{.Table}} - {{end}} - - {{.Joins}} - - {{.Where}} - - {{.GroupBy}} - - {{.OrderBy}} - - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - defaultDeleteLayout = ` - DELETE - FROM {{.Table}} - {{.Where}} - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - defaultUpdateLayout = ` - UPDATE - {{.Table}} - SET {{.ColumnValues}} - {{ .Where }} - ` - - defaultCountLayout = ` - SELECT - COUNT(1) AS _t - FROM {{.Table}} - {{.Where}} - - {{if .Limit}} - LIMIT {{.Limit}} - {{end}} - - {{if .Offset}} - OFFSET {{.Offset}} - {{end}} - ` - - defaultInsertLayout = ` - INSERT INTO {{.Table}} - {{if .Columns }}({{.Columns}}){{end}} - VALUES - {{.Values}} - {{.Extra}} - ` - - defaultTruncateLayout = ` - TRUNCATE TABLE {{.Table}} - ` - - defaultDropDatabaseLayout = ` - DROP DATABASE {{.Database}} - ` - - defaultDropTableLayout = ` - DROP TABLE {{.Table}} - ` - - defaultGroupByColumnLayout = `{{.Column}}` - - defaultGroupByLayout = ` - {{if .GroupColumns}} - GROUP BY {{.GroupColumns}} - {{end}} - ` -) - -var defaultTemplate = &Template{ - ColumnSeparator: defaultColumnSeparator, - IdentifierSeparator: defaultIdentifierSeparator, - IdentifierQuote: defaultIdentifierQuote, - ValueSeparator: defaultValueSeparator, - ValueQuote: defaultValueQuote, - AndKeyword: defaultAndKeyword, - OrKeyword: defaultOrKeyword, - NotKeyword: defaultNotKeyword, - DescKeyword: defaultDescKeyword, - AscKeyword: defaultAscKeyword, - DefaultOperator: defaultDefaultOperator, - AssignmentOperator: defaultAssignmentOperator, - ClauseGroup: defaultClauseGroup, - ClauseOperator: defaultClauseOperator, - ColumnValue: defaultColumnValue, - TableAliasLayout: defaultTableAliasLayout, - ColumnAliasLayout: defaultColumnAliasLayout, - SortByColumnLayout: defaultSortByColumnLayout, - WhereLayout: defaultWhereLayout, - OnLayout: defaultOnLayout, - UsingLayout: defaultUsingLayout, - JoinLayout: defaultJoinLayout, - OrderByLayout: defaultOrderByLayout, - InsertLayout: defaultInsertLayout, - SelectLayout: defaultSelectLayout, - UpdateLayout: defaultUpdateLayout, - DeleteLayout: defaultDeleteLayout, - TruncateLayout: defaultTruncateLayout, - DropDatabaseLayout: defaultDropDatabaseLayout, - DropTableLayout: defaultDropTableLayout, - CountLayout: defaultCountLayout, - GroupByLayout: defaultGroupByLayout, - Cache: cache.NewCache(), -} diff --git a/util/sqlgen/group_by.go b/util/sqlgen/group_by.go deleted file mode 100644 index fe8ed3f3..00000000 --- a/util/sqlgen/group_by.go +++ /dev/null @@ -1,50 +0,0 @@ -package sqlgen - -import ( - "fmt" -) - -// GroupBy represents a SQL's "group by" statement. -type GroupBy struct { - Columns Fragment - hash string -} - -type groupByT struct { - GroupColumns string -} - -// Hash returns a unique identifier. -func (g *GroupBy) Hash() string { - if g.hash == "" { - if g.Columns != nil { - g.hash = fmt.Sprintf(`GroupBy(%s)`, g.Columns.Hash()) - } - } - return g.hash -} - -// GroupByColumns creates and returns a GroupBy with the given column. -func GroupByColumns(columns ...Fragment) *GroupBy { - return &GroupBy{Columns: JoinColumns(columns...)} -} - -// Compile transforms the GroupBy into an equivalent SQL representation. -func (g *GroupBy) Compile(layout *Template) (compiled string) { - - if c, ok := layout.Read(g); ok { - return c - } - - if g.Columns != nil { - data := groupByT{ - GroupColumns: g.Columns.Compile(layout), - } - - compiled = mustParse(layout.GroupByLayout, data) - } - - layout.Write(g, compiled) - - return -} diff --git a/util/sqlgen/group_by_test.go b/util/sqlgen/group_by_test.go deleted file mode 100644 index c6c6a6f3..00000000 --- a/util/sqlgen/group_by_test.go +++ /dev/null @@ -1,73 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestGroupBy(t *testing.T) { - var s, e string - - columns := GroupByColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - - s = columns.Compile(defaultTemplate) - e = `GROUP BY "id", "customer", "service_id", "role"."name", "role"."id"` - - if trim(s) != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkGroupByColumns(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = GroupByColumns( - &Column{Name: "a"}, - &Column{Name: "b"}, - &Column{Name: "c"}, - ) - } -} - -func BenchmarkGroupByHash(b *testing.B) { - c := GroupByColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - for i := 0; i < b.N; i++ { - c.Hash() - } -} - -func BenchmarkGroupByCompile(b *testing.B) { - c := GroupByColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - for i := 0; i < b.N; i++ { - c.Compile(defaultTemplate) - } -} - -func BenchmarkGroupByCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - c := GroupByColumns( - &Column{Name: "id"}, - &Column{Name: "customer"}, - &Column{Name: "service_id"}, - &Column{Name: "role.name"}, - &Column{Name: "role.id"}, - ) - c.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/interfaces.go b/util/sqlgen/interfaces.go deleted file mode 100644 index a077715a..00000000 --- a/util/sqlgen/interfaces.go +++ /dev/null @@ -1,15 +0,0 @@ -package sqlgen - -import ( - "upper.io/cache" -) - -// Fragment is any interface that can be both cached and compiled. -type Fragment interface { - cache.Hashable - compilable -} - -type compilable interface { - Compile(*Template) string -} diff --git a/util/sqlgen/join.go b/util/sqlgen/join.go deleted file mode 100644 index ea336c48..00000000 --- a/util/sqlgen/join.go +++ /dev/null @@ -1,177 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -type innerJoinT struct { - Type string - Table string - On string - Using string -} - -// Joins represents the union of different join conditions. -type Joins struct { - Conditions []Fragment - hash string -} - -// Hash returns a unique identifier. -func (j *Joins) Hash() string { - if j.hash == "" { - hash := make([]string, len(j.Conditions)) - for i := range j.Conditions { - hash[i] = j.Conditions[i].Hash() - } - j.hash = fmt.Sprintf(`Join{%s}`, strings.Join(hash, `, `)) - } - return j.hash -} - -// Compile transforms the Where into an equivalent SQL representation. -func (j *Joins) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(j); ok { - return c - } - - l := len(j.Conditions) - - chunks := make([]string, 0, l) - - if l > 0 { - for i := 0; i < l; i++ { - chunks = append(chunks, j.Conditions[i].Compile(layout)) - } - } - - compiled = strings.Join(chunks, " ") - - layout.Write(j, compiled) - - return -} - -// JoinConditions creates a Joins object. -func JoinConditions(joins ...*Join) *Joins { - fragments := make([]Fragment, len(joins)) - for i := range fragments { - fragments[i] = joins[i] - } - return &Joins{Conditions: fragments} -} - -// Join represents a generic JOIN statement. -type Join struct { - Type string - *Table - *On - *Using - hash string -} - -// Hash returns a unique string given a JOIN. -func (j *Join) Hash() string { - if j.hash == "" { - if j.Table != nil && j.Table.Hash() != "" { - j.hash = fmt.Sprintf(`Join{%s}`, strings.Join([]string{ - j.Type, - j.Table.Hash(), - getHash(j.On), - getHash(j.Using), - }, ", ")) - } - } - return j.hash -} - -// Compile transforms the Join into its equivalent SQL representation. -func (j *Join) Compile(layout *Template) (compiled string) { - - if c, ok := layout.Read(j); ok { - return c - } - - if j.Table != nil { - data := innerJoinT{ - Type: j.Type, - Table: j.Table.Compile(layout), - On: layout.doCompile(j.On), - Using: layout.doCompile(j.Using), - } - compiled = mustParse(layout.JoinLayout, data) - } - - layout.Write(j, compiled) - - return -} - -// On represents JOIN conditions. -type On Where - -// Hash returns a unique identifier. -func (o *On) Hash() string { - if o.hash == "" { - w := Where(*o) - o.hash = fmt.Sprintf(`On{%s}`, w.Hash()) - } - return o.hash -} - -// Compile transforms the On into an equivalent SQL representation. -func (o *On) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(o); ok { - return c - } - - grouped := groupCondition(layout, o.Conditions, mustParse(layout.ClauseOperator, layout.AndKeyword)) - - if grouped != "" { - compiled = mustParse(layout.OnLayout, conds{grouped}) - } - - layout.Write(o, compiled) - - return -} - -// Using represents a USING function. -type Using Columns - -type usingT struct { - Columns string -} - -// Hash returns a unique identifier. -func (u *Using) Hash() string { - if u.hash == "" { - c := Columns(*u) - u.hash = fmt.Sprintf(`Using{%s}`, c.Hash()) - } - return u.hash -} - -// Compile transforms the Using into an equivalent SQL representation. -func (u *Using) Compile(layout *Template) (compiled string) { - if u == nil { - return "" - } - - if c, ok := layout.Read(u); ok { - return c - } - - if len(u.Columns) > 0 { - c := Columns(*u) - data := usingT{ - Columns: c.Compile(layout), - } - compiled = mustParse(layout.UsingLayout, data) - } - - layout.Write(u, compiled) - - return -} diff --git a/util/sqlgen/join_test.go b/util/sqlgen/join_test.go deleted file mode 100644 index 9fd06cf1..00000000 --- a/util/sqlgen/join_test.go +++ /dev/null @@ -1,250 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestOnAndRawOrAnd(t *testing.T) { - var s, e string - - on := OnConditions( - JoinWithAnd( - &ColumnValue{Column: &Column{Name: "id"}, Operator: ">", Value: NewValue(&Raw{Value: "8"})}, - &ColumnValue{Column: &Column{Name: "id"}, Operator: "<", Value: NewValue(&Raw{Value: "99"})}, - ), - &ColumnValue{Column: &Column{Name: "name"}, Operator: "=", Value: NewValue("John")}, - &Raw{Value: "city_id = 728"}, - JoinWithOr( - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Smith")}, - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Reyes")}, - ), - JoinWithAnd( - &ColumnValue{Column: &Column{Name: "age"}, Operator: ">", Value: NewValue(&Raw{Value: "18"})}, - &ColumnValue{Column: &Column{Name: "age"}, Operator: "<", Value: NewValue(&Raw{Value: "41"})}, - ), - ) - - s = trim(on.Compile(defaultTemplate)) - e = `ON (("id" > 8 AND "id" < 99) AND "name" = 'John' AND city_id = 728 AND ("last_name" = 'Smith' OR "last_name" = 'Reyes') AND ("age" > 18 AND "age" < 41))` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestUsing(t *testing.T) { - var s, e string - - using := UsingColumns( - &Column{Name: "country"}, - &Column{Name: "state"}, - ) - - s = trim(using.Compile(defaultTemplate)) - e = `USING ("country", "state")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestJoinOn(t *testing.T) { - var s, e string - - join := JoinConditions( - &Join{ - Table: TableWithName("countries c"), - On: OnConditions( - &ColumnValue{ - Column: &Column{Name: "p.country_id"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.id"}), - }, - &ColumnValue{ - Column: &Column{Name: "p.country_code"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.code"}), - }, - ), - }, - ) - - s = trim(join.Compile(defaultTemplate)) - e = `JOIN "countries" AS "c" ON ("p"."country_id" = "a"."id" AND "p"."country_code" = "a"."code")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestInnerJoinOn(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Type: "INNER", - Table: TableWithName("countries c"), - On: OnConditions( - &ColumnValue{ - Column: &Column{Name: "p.country_id"}, - Operator: "=", - Value: NewValue(ColumnWithName("a.id")), - }, - &ColumnValue{ - Column: &Column{Name: "p.country_code"}, - Operator: "=", - Value: NewValue(ColumnWithName("a.code")), - }, - ), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `INNER JOIN "countries" AS "c" ON ("p"."country_id" = "a"."id" AND "p"."country_code" = "a"."code")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestLeftJoinUsing(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Type: "LEFT", - Table: TableWithName("countries"), - Using: UsingColumns(ColumnWithName("name")), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `LEFT JOIN "countries" USING ("name")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestNaturalJoinOn(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Table: TableWithName("countries"), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `NATURAL JOIN "countries"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestNaturalInnerJoinOn(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Type: "INNER", - Table: TableWithName("countries"), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `NATURAL INNER JOIN "countries"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestCrossJoin(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Type: "CROSS", - Table: TableWithName("countries"), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `CROSS JOIN "countries"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestMultipleJoins(t *testing.T) { - var s, e string - - join := JoinConditions(&Join{ - Type: "LEFT", - Table: TableWithName("countries"), - }, &Join{ - Table: TableWithName("cities"), - }) - - s = trim(join.Compile(defaultTemplate)) - e = `NATURAL LEFT JOIN "countries" NATURAL JOIN "cities"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkJoin(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = JoinConditions(&Join{ - Table: TableWithName("countries c"), - On: OnConditions( - &ColumnValue{ - Column: &Column{Name: "p.country_id"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.id"}), - }, - &ColumnValue{ - Column: &Column{Name: "p.country_code"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.code"}), - }, - ), - }) - } -} - -func BenchmarkCompileJoin(b *testing.B) { - j := JoinConditions(&Join{ - Table: TableWithName("countries c"), - On: OnConditions( - &ColumnValue{ - Column: &Column{Name: "p.country_id"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.id"}), - }, - &ColumnValue{ - Column: &Column{Name: "p.country_code"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.code"}), - }, - ), - }) - for i := 0; i < b.N; i++ { - j.Compile(defaultTemplate) - } -} - -func BenchmarkCompileJoinNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - j := JoinConditions(&Join{ - Table: TableWithName("countries c"), - On: OnConditions( - &ColumnValue{ - Column: &Column{Name: "p.country_id"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.id"}), - }, - &ColumnValue{ - Column: &Column{Name: "p.country_code"}, - Operator: "=", - Value: NewValue(&Column{Name: "a.code"}), - }, - ), - }) - j.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/order_by.go b/util/sqlgen/order_by.go deleted file mode 100644 index d437cde3..00000000 --- a/util/sqlgen/order_by.go +++ /dev/null @@ -1,164 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -// Order represents the order in which SQL results are sorted. -type Order uint8 - -// Possible values for Order -const ( - DefaultOrder = Order(iota) - Ascendent - Descendent -) - -// SortColumn represents the column-order relation in an ORDER BY clause. -type SortColumn struct { - Column Fragment - Order - hash string -} - -type sortColumnT struct { - Column string - Order string -} - -// SortColumns represents the columns in an ORDER BY clause. -type SortColumns struct { - Columns []Fragment - hash string -} - -// OrderBy represents an ORDER BY clause. -type OrderBy struct { - SortColumns Fragment - hash string -} - -type orderByT struct { - SortColumns string -} - -// JoinSortColumns creates and returns an array of column-order relations. -func JoinSortColumns(values ...Fragment) *SortColumns { - return &SortColumns{Columns: values} -} - -// JoinWithOrderBy creates an returns an OrderBy using the given SortColumns. -func JoinWithOrderBy(sc *SortColumns) *OrderBy { - return &OrderBy{SortColumns: sc} -} - -// Hash returns a unique identifier. -func (s *SortColumn) Hash() string { - if s.hash == "" { - s.hash = fmt.Sprintf(`SortColumn{Column:%s, Order:%s}`, s.Column.Hash(), s.Order.Hash()) - } - return s.hash -} - -// Compile transforms the SortColumn into an equivalent SQL representation. -func (s *SortColumn) Compile(layout *Template) (compiled string) { - - if c, ok := layout.Read(s); ok { - return c - } - - data := sortColumnT{ - Column: s.Column.Compile(layout), - Order: s.Order.Compile(layout), - } - - compiled = mustParse(layout.SortByColumnLayout, data) - - layout.Write(s, compiled) - - return -} - -// Hash returns a unique identifier. -func (s *SortColumns) Hash() string { - if s.hash == "" { - h := make([]string, len(s.Columns)) - for i := range s.Columns { - h[i] = s.Columns[i].Hash() - } - s.hash = fmt.Sprintf(`SortColumns(%s)`, strings.Join(h, `, `)) - } - return s.hash -} - -// Compile transforms the SortColumns into an equivalent SQL representation. -func (s *SortColumns) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(s); ok { - return z - } - - z := make([]string, len(s.Columns)) - - for i := range s.Columns { - z[i] = s.Columns[i].Compile(layout) - } - - compiled = strings.Join(z, layout.IdentifierSeparator) - - layout.Write(s, compiled) - - return -} - -// Hash returns a unique identifier. -func (s *OrderBy) Hash() string { - if s.hash == "" { - if s.SortColumns != nil { - s.hash = `OrderBy(` + s.SortColumns.Hash() + `)` - } - } - return s.hash -} - -// Compile transforms the SortColumn into an equivalent SQL representation. -func (s *OrderBy) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(s); ok { - return z - } - - if s.SortColumns != nil { - data := orderByT{ - SortColumns: s.SortColumns.Compile(layout), - } - compiled = mustParse(layout.OrderByLayout, data) - } - - layout.Write(s, compiled) - - return -} - -// Hash returns a unique identifier. -func (s Order) Hash() string { - switch s { - case Ascendent: - return `Order{ASC}` - case Descendent: - return `Order{DESC}` - } - return `Order{DEFAULT}` -} - -// Compile transforms the SortColumn into an equivalent SQL representation. -func (s Order) Compile(layout *Template) string { - switch s { - case Ascendent: - return layout.AscKeyword - case Descendent: - return layout.DescKeyword - } - return "" -} diff --git a/util/sqlgen/order_by_test.go b/util/sqlgen/order_by_test.go deleted file mode 100644 index bbb7ac84..00000000 --- a/util/sqlgen/order_by_test.go +++ /dev/null @@ -1,143 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestOrderBy(t *testing.T) { - o := JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - ) - - s := o.Compile(defaultTemplate) - e := `ORDER BY "foo"` - - if trim(s) != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestOrderByDesc(t *testing.T) { - o := JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}, Order: Descendent}, - ), - ) - - s := o.Compile(defaultTemplate) - e := `ORDER BY "foo" DESC` - - if trim(s) != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkOrderBy(b *testing.B) { - for i := 0; i < b.N; i++ { - JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - ) - } -} - -func BenchmarkOrderByHash(b *testing.B) { - o := OrderBy{ - SortColumns: JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - } - for i := 0; i < b.N; i++ { - o.Hash() - } -} - -func BenchmarkCompileOrderByCompile(b *testing.B) { - o := OrderBy{ - SortColumns: JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - } - for i := 0; i < b.N; i++ { - o.Compile(defaultTemplate) - } -} - -func BenchmarkCompileOrderByCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - o := JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - ) - o.Compile(defaultTemplate) - } -} - -func BenchmarkCompileOrderCompile(b *testing.B) { - o := Descendent - for i := 0; i < b.N; i++ { - o.Compile(defaultTemplate) - } -} - -func BenchmarkCompileOrderCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - o := Descendent - o.Compile(defaultTemplate) - } -} - -func BenchmarkSortColumnHash(b *testing.B) { - s := &SortColumn{Column: &Column{Name: "foo"}} - for i := 0; i < b.N; i++ { - s.Hash() - } -} - -func BenchmarkSortColumnCompile(b *testing.B) { - s := &SortColumn{Column: &Column{Name: "foo"}} - for i := 0; i < b.N; i++ { - s.Compile(defaultTemplate) - } -} - -func BenchmarkSortColumnCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - s := &SortColumn{Column: &Column{Name: "foo"}} - s.Compile(defaultTemplate) - } -} - -func BenchmarkSortColumnsHash(b *testing.B) { - s := JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - &SortColumn{Column: &Column{Name: "bar"}}, - ) - for i := 0; i < b.N; i++ { - s.Hash() - } -} - -func BenchmarkSortColumnsCompile(b *testing.B) { - s := JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - &SortColumn{Column: &Column{Name: "bar"}}, - ) - for i := 0; i < b.N; i++ { - s.Compile(defaultTemplate) - } -} - -func BenchmarkSortColumnsCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - s := JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - &SortColumn{Column: &Column{Name: "bar"}}, - ) - s.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/raw.go b/util/sqlgen/raw.go deleted file mode 100644 index ca16c26c..00000000 --- a/util/sqlgen/raw.go +++ /dev/null @@ -1,38 +0,0 @@ -package sqlgen - -import ( - "fmt" -) - -var ( - _ = fmt.Stringer(&Raw{}) -) - -// Raw represents a value that is meant to be used in a query without escaping. -type Raw struct { - Value string // Value should not be modified after assigned. - hash string -} - -// RawValue creates and returns a new raw value. -func RawValue(v string) *Raw { - return &Raw{Value: v} -} - -// Hash returns a unique identifier. -func (r *Raw) Hash() string { - if r.hash == "" { - r.hash = `Raw{Value:"` + r.Value + `"}` - } - return r.hash -} - -// Compile returns the raw value. -func (r *Raw) Compile(*Template) string { - return r.Value -} - -// String returns the raw value. -func (r *Raw) String() string { - return r.Value -} diff --git a/util/sqlgen/raw_test.go b/util/sqlgen/raw_test.go deleted file mode 100644 index 9ad57ad6..00000000 --- a/util/sqlgen/raw_test.go +++ /dev/null @@ -1,72 +0,0 @@ -package sqlgen - -import ( - "fmt" - "testing" -) - -func TestRawString(t *testing.T) { - var s, e string - - raw := &Raw{Value: "foo"} - - s = raw.Compile(defaultTemplate) - e = `foo` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestRawCompile(t *testing.T) { - var s, e string - - raw := &Raw{Value: "foo"} - - s = raw.Compile(defaultTemplate) - e = `foo` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestRawHash(t *testing.T) { - var s, e string - - raw := &Raw{Value: "foo"} - - s = raw.Hash() - e = fmt.Sprintf(`Raw{Value:"%s"}`, raw) - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkRawCreate(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = Raw{Value: "foo"} - } -} - -func BenchmarkRawString(b *testing.B) { - raw := &Raw{Value: "foo"} - for i := 0; i < b.N; i++ { - raw.String() - } -} - -func BenchmarkRawCompile(b *testing.B) { - raw := &Raw{Value: "foo"} - for i := 0; i < b.N; i++ { - raw.Compile(defaultTemplate) - } -} - -func BenchmarkRawHash(b *testing.B) { - raw := &Raw{Value: "foo"} - for i := 0; i < b.N; i++ { - raw.Hash() - } -} diff --git a/util/sqlgen/statement.go b/util/sqlgen/statement.go deleted file mode 100644 index 67de2340..00000000 --- a/util/sqlgen/statement.go +++ /dev/null @@ -1,130 +0,0 @@ -package sqlgen - -import ( - "reflect" - "strconv" - "strings" - - "upper.io/cache" -) - -// Statement represents different kinds of SQL statements. -type Statement struct { - Type - Table Fragment - Database Fragment - Columns Fragment - Values Fragment - ColumnValues Fragment - OrderBy Fragment - GroupBy Fragment - Joins Fragment - Where Fragment - Extra - - Limit - Offset - - hash string -} - -type statementT struct { - Table string - Database string - Columns string - Values string - ColumnValues string - OrderBy string - GroupBy string - Extra string - Where string - Joins string - Limit - Offset -} - -func (layout *Template) doCompile(c Fragment) string { - if c != nil && !reflect.ValueOf(c).IsNil() { - return c.Compile(layout) - } - return "" -} - -func getHash(h cache.Hashable) string { - if h != nil && !reflect.ValueOf(h).IsNil() { - return h.Hash() - } - return "" -} - -// Hash returns a unique identifier. -func (s *Statement) Hash() string { - if s.hash == "" { - parts := strings.Join([]string{ - strconv.Itoa(int(s.Type)), - getHash(s.Table), - getHash(s.Database), - strconv.Itoa(int(s.Limit)), - strconv.Itoa(int(s.Offset)), - getHash(s.Columns), - getHash(s.Values), - getHash(s.ColumnValues), - getHash(s.OrderBy), - getHash(s.GroupBy), - string(s.Extra), - getHash(s.Where), - getHash(s.Joins), - }, ";") - - s.hash = `Statement{` + parts + `}` - } - return s.hash -} - -// Compile transforms the Statement into an equivalent SQL query. -func (s *Statement) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(s); ok { - return z - } - - data := statementT{ - Table: layout.doCompile(s.Table), - Database: layout.doCompile(s.Database), - Limit: s.Limit, - Offset: s.Offset, - Columns: layout.doCompile(s.Columns), - Values: layout.doCompile(s.Values), - ColumnValues: layout.doCompile(s.ColumnValues), - OrderBy: layout.doCompile(s.OrderBy), - GroupBy: layout.doCompile(s.GroupBy), - Extra: string(s.Extra), - Where: layout.doCompile(s.Where), - Joins: layout.doCompile(s.Joins), - } - - switch s.Type { - case Truncate: - compiled = mustParse(layout.TruncateLayout, data) - case DropTable: - compiled = mustParse(layout.DropTableLayout, data) - case DropDatabase: - compiled = mustParse(layout.DropDatabaseLayout, data) - case Count: - compiled = mustParse(layout.CountLayout, data) - case Select: - compiled = mustParse(layout.SelectLayout, data) - case Delete: - compiled = mustParse(layout.DeleteLayout, data) - case Update: - compiled = mustParse(layout.UpdateLayout, data) - case Insert: - compiled = mustParse(layout.InsertLayout, data) - default: - panic("Unknown template type.") - } - - layout.Write(s, compiled) - - return compiled -} diff --git a/util/sqlgen/statement_test.go b/util/sqlgen/statement_test.go deleted file mode 100644 index 0f3dfb56..00000000 --- a/util/sqlgen/statement_test.go +++ /dev/null @@ -1,885 +0,0 @@ -package sqlgen - -import ( - "regexp" - "strings" - "testing" -) - -var ( - reInvisible = regexp.MustCompile(`[\t\n\r]`) - reSpace = regexp.MustCompile(`\s+`) -) - -func trim(a string) string { - a = reInvisible.ReplaceAllString(strings.TrimSpace(a), " ") - a = reSpace.ReplaceAllString(strings.TrimSpace(a), " ") - return a -} - -func TestTruncateTable(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Truncate, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `TRUNCATE TABLE "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestDropTable(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: DropTable, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `DROP TABLE "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestDropDatabase(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: DropDatabase, - Database: &Database{Name: "table_name"}, - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `DROP DATABASE "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestCount(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Count, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT COUNT(1) AS _t FROM "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestCountRelation(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Count, - Table: TableWithName("information_schema.tables"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT COUNT(1) AS _t FROM "information_schema"."tables"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestCountWhere(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Count, - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "a"}, Operator: "=", Value: NewValue(RawValue("7"))}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT COUNT(1) AS _t FROM "table_name" WHERE ("a" = 7)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectStarFrom(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectStarFromAlias(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName("table.name AS foo"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "table"."name" AS "foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectStarFromRawWhere(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName("table.name AS foo"), - Where: WhereConditions( - &Raw{Value: "foo.id = bar.foo_id"}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "table"."name" AS "foo" WHERE (foo.id = bar.foo_id)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - stmt = Statement{ - Type: Select, - Table: TableWithName("table.name AS foo"), - Where: WhereConditions( - &Raw{Value: "foo.id = bar.foo_id"}, - &Raw{Value: "baz.id = exp.baz_id"}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "table"."name" AS "foo" WHERE (foo.id = bar.foo_id AND baz.id = exp.baz_id)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectStarFromMany(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName("first.table AS foo, second.table as BAR, third.table aS baz"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "first"."table" AS "foo", "second"."table" AS "BAR", "third"."table" AS "baz"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectArtistNameFrom(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName("artist"), - Columns: JoinColumns( - &Column{Name: "artist.name"}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "artist"."name" FROM "artist"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectJoin(t *testing.T) { - var s, e string - - stmt := Statement{ - Type: Select, - Table: TableWithName("artist a"), - Columns: JoinColumns( - &Column{Name: "a.name"}, - ), - Joins: JoinConditions(&Join{ - Table: TableWithName("books b"), - On: OnConditions( - &ColumnValue{ - Column: ColumnWithName("b.author_id"), - Operator: `=`, - Value: NewValue(ColumnWithName("a.id")), - }, - ), - }), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "a"."name" FROM "artist" AS "a" JOIN "books" AS "b" ON ("b"."author_id" = "a"."id")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectJoinUsing(t *testing.T) { - var s, e string - - stmt := Statement{ - Type: Select, - Table: TableWithName("artist a"), - Columns: JoinColumns( - &Column{Name: "a.name"}, - ), - Joins: JoinConditions(&Join{ - Table: TableWithName("books b"), - Using: UsingColumns( - ColumnWithName("artist_id"), - ColumnWithName("country"), - ), - }), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "a"."name" FROM "artist" AS "a" JOIN "books" AS "b" USING ("artist_id", "country")` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectUnfinishedJoin(t *testing.T) { - var s, e string - - stmt := Statement{ - Type: Select, - Table: TableWithName("artist a"), - Columns: JoinColumns( - &Column{Name: "a.name"}, - ), - Joins: JoinConditions(&Join{}), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "a"."name" FROM "artist" AS "a"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectNaturalJoin(t *testing.T) { - var s, e string - - stmt := Statement{ - Type: Select, - Table: TableWithName("artist"), - Joins: JoinConditions(&Join{ - Table: TableWithName("books"), - }), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT * FROM "artist" NATURAL JOIN "books"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectRawFrom(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Table: TableWithName(`artist`), - Columns: JoinColumns( - &Column{Name: `artist.name`}, - &Column{Name: Raw{Value: `CONCAT(artist.name, " ", artist.last_name)`}}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "artist"."name", CONCAT(artist.name, " ", artist.last_name) FROM "artist"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectFieldsFrom(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectFieldsFromWithLimitOffset(t *testing.T) { - var s, e string - var stmt Statement - - // LIMIT only. - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Limit: 42, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" LIMIT 42` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // OFFSET only. - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Offset: 17, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" OFFSET 17` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // LIMIT AND OFFSET. - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Limit: 42, - Offset: 17, - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" LIMIT 42 OFFSET 17` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestStatementGroupBy(t *testing.T) { - var s, e string - var stmt Statement - - // Simple GROUP BY - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - GroupBy: GroupByColumns( - &Column{Name: "foo"}, - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" GROUP BY "foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - GroupBy: GroupByColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" GROUP BY "foo", "bar"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectFieldsFromWithOrderBy(t *testing.T) { - var s, e string - var stmt Statement - - // Simple ORDER BY - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - OrderBy: JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}}, - ), - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" ORDER BY "foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // ORDER BY field ASC - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - OrderBy: JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}, Order: Ascendent}, - ), - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" ORDER BY "foo" ASC` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // ORDER BY field DESC - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - OrderBy: JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}, Order: Descendent}, - ), - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" ORDER BY "foo" DESC` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // ORDER BY many fields - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - OrderBy: JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: "foo"}, Order: Descendent}, - &SortColumn{Column: &Column{Name: "bar"}, Order: Ascendent}, - &SortColumn{Column: &Column{Name: "baz"}, Order: Descendent}, - ), - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" ORDER BY "foo" DESC, "bar" ASC, "baz" DESC` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - // ORDER BY function - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - OrderBy: JoinWithOrderBy( - JoinSortColumns( - &SortColumn{Column: &Column{Name: Raw{Value: "FOO()"}}, Order: Descendent}, - &SortColumn{Column: &Column{Name: Raw{Value: "BAR()"}}, Order: Ascendent}, - ), - ), - Table: TableWithName("table_name"), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" ORDER BY FOO() DESC, BAR() ASC` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectFieldsFromWhere(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" WHERE ("baz" = '99')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestSelectFieldsFromWhereLimitOffset(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Select, - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ), - Limit: 10, - Offset: 23, - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `SELECT "foo", "bar", "baz" FROM "table_name" WHERE ("baz" = '99') LIMIT 10 OFFSET 23` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestDelete(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Delete, - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `DELETE FROM "table_name" WHERE ("baz" = '99')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestUpdate(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Update, - Table: TableWithName("table_name"), - ColumnValues: JoinColumnValues( - &ColumnValue{Column: &Column{Name: "foo"}, Operator: "=", Value: NewValue(76)}, - ), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `UPDATE "table_name" SET "foo" = '76' WHERE ("baz" = '99')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - stmt = Statement{ - Type: Update, - Table: TableWithName("table_name"), - ColumnValues: JoinColumnValues( - &ColumnValue{Column: &Column{Name: "foo"}, Operator: "=", Value: NewValue(76)}, - &ColumnValue{Column: &Column{Name: "bar"}, Operator: "=", Value: NewValue(Raw{Value: "88"})}, - ), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `UPDATE "table_name" SET "foo" = '76', "bar" = 88 WHERE ("baz" = '99')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestInsert(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Insert, - Table: TableWithName("table_name"), - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Values: NewValueGroup( - &Value{V: "1"}, - &Value{V: 2}, - &Value{V: Raw{Value: "3"}}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `INSERT INTO "table_name" ("foo", "bar", "baz") VALUES ('1', '2', 3)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestInsertMultiple(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Insert, - Table: TableWithName("table_name"), - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Values: JoinValueGroups( - NewValueGroup( - NewValue("1"), - NewValue("2"), - NewValue(RawValue("3")), - ), - NewValueGroup( - NewValue(RawValue("4")), - NewValue(RawValue("5")), - NewValue(RawValue("6")), - ), - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `INSERT INTO "table_name" ("foo", "bar", "baz") VALUES ('1', '2', 3), (4, 5, 6)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestInsertExtra(t *testing.T) { - var s, e string - var stmt Statement - - stmt = Statement{ - Type: Insert, - Table: TableWithName("table_name"), - Extra: "RETURNING id", - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Values: NewValueGroup( - &Value{V: "1"}, - &Value{V: 2}, - &Value{V: Raw{Value: "3"}}, - ), - } - - s = trim(stmt.Compile(defaultTemplate)) - e = `INSERT INTO "table_name" ("foo", "bar", "baz") VALUES ('1', '2', 3) RETURNING id` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkStatementSimpleQuery(b *testing.B) { - stmt := Statement{ - Type: Count, - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "a"}, Operator: "=", Value: NewValue(Raw{Value: "7"})}, - ), - } - - for i := 0; i < b.N; i++ { - _ = stmt.Compile(defaultTemplate) - } -} - -func BenchmarkStatementSimpleQueryHash(b *testing.B) { - stmt := Statement{ - Type: Count, - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "a"}, Operator: "=", Value: NewValue(Raw{Value: "7"})}, - ), - } - - for i := 0; i < b.N; i++ { - _ = stmt.Hash() - } -} - -func BenchmarkStatementSimpleQueryNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - stmt := Statement{ - Type: Count, - Table: TableWithName("table_name"), - Where: WhereConditions( - &ColumnValue{Column: &Column{Name: "a"}, Operator: "=", Value: NewValue(Raw{Value: "7"})}, - ), - } - _ = stmt.Compile(defaultTemplate) - } -} - -func BenchmarkStatementComplexQuery(b *testing.B) { - stmt := Statement{ - Type: Insert, - Table: TableWithName("table_name"), - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Values: NewValueGroup( - &Value{V: "1"}, - &Value{V: 2}, - &Value{V: Raw{Value: "3"}}, - ), - } - - for i := 0; i < b.N; i++ { - _ = stmt.Compile(defaultTemplate) - } -} - -func BenchmarkStatementComplexQueryNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - stmt := Statement{ - Type: Insert, - Table: TableWithName("table_name"), - Columns: JoinColumns( - &Column{Name: "foo"}, - &Column{Name: "bar"}, - &Column{Name: "baz"}, - ), - Values: NewValueGroup( - &Value{V: "1"}, - &Value{V: 2}, - &Value{V: Raw{Value: "3"}}, - ), - } - _ = stmt.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/table.go b/util/sqlgen/table.go deleted file mode 100644 index 43dcda62..00000000 --- a/util/sqlgen/table.go +++ /dev/null @@ -1,110 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -type tableT struct { - Name string - Alias string -} - -// Table struct represents a SQL table. -type Table struct { - Name interface{} - hash string -} - -func quotedTableName(layout *Template, input string) string { - input = trimString(input) - - // chunks := reAliasSeparator.Split(input, 2) - chunks := separateByAS(input) - - if len(chunks) == 1 { - // chunks = reSpaceSeparator.Split(input, 2) - chunks = separateBySpace(input) - } - - name := chunks[0] - - nameChunks := strings.SplitN(name, layout.ColumnSeparator, 2) - - for i := range nameChunks { - // nameChunks[i] = strings.TrimSpace(nameChunks[i]) - nameChunks[i] = trimString(nameChunks[i]) - nameChunks[i] = mustParse(layout.IdentifierQuote, Raw{Value: nameChunks[i]}) - } - - name = strings.Join(nameChunks, layout.ColumnSeparator) - - var alias string - - if len(chunks) > 1 { - // alias = strings.TrimSpace(chunks[1]) - alias = trimString(chunks[1]) - alias = mustParse(layout.IdentifierQuote, Raw{Value: alias}) - } - - return mustParse(layout.TableAliasLayout, tableT{name, alias}) -} - -// TableWithName creates an returns a Table with the given name. -func TableWithName(name string) *Table { - return &Table{Name: name} -} - -// Hash returns a string hash of the table value. -func (t *Table) Hash() string { - if t.hash == "" { - var s string - - switch v := t.Name.(type) { - case Fragment: - s = v.Hash() - case fmt.Stringer: - s = v.String() - case string: - s = v - default: - s = fmt.Sprintf("%v", t.Name) - } - - t.hash = fmt.Sprintf(`Table{Name:%q}`, s) - } - - return t.hash -} - -// Compile transforms a table struct into a SQL chunk. -func (t *Table) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(t); ok { - return z - } - - switch value := t.Name.(type) { - case string: - if t.Name == "" { - return - } - - // Splitting tables by a comma - parts := separateByComma(value) - - l := len(parts) - - for i := 0; i < l; i++ { - parts[i] = quotedTableName(layout, parts[i]) - } - - compiled = strings.Join(parts, layout.IdentifierSeparator) - case Raw: - compiled = value.String() - } - - layout.Write(t, compiled) - - return -} diff --git a/util/sqlgen/table_test.go b/util/sqlgen/table_test.go deleted file mode 100644 index df9528de..00000000 --- a/util/sqlgen/table_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestTableSimple(t *testing.T) { - var s, e string - - table := TableWithName("artist") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableCompound(t *testing.T) { - var s, e string - - table := TableWithName("artist.foo") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"."foo"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableCompoundAlias(t *testing.T) { - var s, e string - - table := TableWithName("artist.foo AS baz") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"."foo" AS "baz"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableImplicitAlias(t *testing.T) { - var s, e string - - table := TableWithName("artist.foo baz") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"."foo" AS "baz"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableMultiple(t *testing.T) { - var s, e string - - table := TableWithName("artist.foo, artist.bar, artist.baz") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"."foo", "artist"."bar", "artist"."baz"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableMultipleAlias(t *testing.T) { - var s, e string - - table := TableWithName("artist.foo AS foo, artist.bar as bar, artist.baz As baz") - - s = trim(table.Compile(defaultTemplate)) - e = `"artist"."foo" AS "foo", "artist"."bar" AS "bar", "artist"."baz" AS "baz"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableMinimal(t *testing.T) { - var s, e string - - table := TableWithName("a") - - s = trim(table.Compile(defaultTemplate)) - e = `"a"` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestTableEmpty(t *testing.T) { - var s, e string - - table := TableWithName("") - - s = trim(table.Compile(defaultTemplate)) - e = `` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkTableWithName(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = TableWithName("foo") - } -} - -func BenchmarkTableHash(b *testing.B) { - t := TableWithName("name") - for i := 0; i < b.N; i++ { - t.Hash() - } -} - -func BenchmarkTableCompile(b *testing.B) { - t := TableWithName("name") - for i := 0; i < b.N; i++ { - t.Compile(defaultTemplate) - } -} - -func BenchmarkTableCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - t := TableWithName("name") - t.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/template.go b/util/sqlgen/template.go deleted file mode 100644 index 5592353f..00000000 --- a/util/sqlgen/template.go +++ /dev/null @@ -1,88 +0,0 @@ -package sqlgen - -import ( - "bytes" - "text/template" - - "upper.io/cache" -) - -// Type is the type of SQL query the statement represents. -type Type uint - -// Values for Type. -const ( - Truncate = Type(iota) - DropTable - DropDatabase - Count - Insert - Select - Update - Delete -) - -type ( - // Limit represents the SQL limit in a query. - Limit int - // Offset represents the SQL offset in a query. - Offset int - // Extra represents any custom SQL that is to be appended to the query. - Extra string -) - -var ( - parsedTemplates = make(map[string]*template.Template) -) - -// Template is an SQL template. -type Template struct { - ColumnSeparator string - IdentifierSeparator string - IdentifierQuote string - ValueSeparator string - ValueQuote string - AndKeyword string - OrKeyword string - NotKeyword string - DescKeyword string - AscKeyword string - DefaultOperator string - AssignmentOperator string - ClauseGroup string - ClauseOperator string - ColumnValue string - TableAliasLayout string - ColumnAliasLayout string - SortByColumnLayout string - WhereLayout string - OnLayout string - UsingLayout string - JoinLayout string - OrderByLayout string - InsertLayout string - SelectLayout string - UpdateLayout string - DeleteLayout string - TruncateLayout string - DropDatabaseLayout string - DropTableLayout string - CountLayout string - GroupByLayout string - *cache.Cache -} - -func mustParse(text string, data interface{}) string { - var b bytes.Buffer - var ok bool - - if _, ok = parsedTemplates[text]; !ok { - parsedTemplates[text] = template.Must(template.New("").Parse(text)) - } - - if err := parsedTemplates[text].Execute(&b, data); err != nil { - panic("There was an error compiling the following template:\n" + text + "\nError was: " + err.Error()) - } - - return b.String() -} diff --git a/util/sqlgen/utilities.go b/util/sqlgen/utilities.go deleted file mode 100644 index 305ab209..00000000 --- a/util/sqlgen/utilities.go +++ /dev/null @@ -1,157 +0,0 @@ -package sqlgen - -import ( - "strings" -) - -const ( - stageExpect = iota - stageCapture - stageClose -) - -// isBlankSymbol returns true if the given byte is either space, tab, carriage -// return or newline. -func isBlankSymbol(in byte) bool { - return in == ' ' || in == '\t' || in == '\r' || in == '\n' -} - -// trimString returns a slice of s with a leading and trailing blank symbols -// (as defined by isBlankSymbol) removed. -func trimString(s string) string { - - // This conversion is rather slow. - // return string(trimBytes([]byte(s))) - - start, end := 0, len(s)-1 - - if end < start { - return "" - } - - for isBlankSymbol(s[start]) { - start++ - if start >= end { - return "" - } - } - - for isBlankSymbol(s[end]) { - end-- - } - - return s[start : end+1] -} - -// trimBytes returns a slice of s with a leading and trailing blank symbols (as -// defined by isBlankSymbol) removed. -func trimBytes(s []byte) []byte { - - start, end := 0, len(s)-1 - - if end < start { - return []byte{} - } - - for isBlankSymbol(s[start]) { - start++ - if start >= end { - return []byte{} - } - } - - for isBlankSymbol(s[end]) { - end-- - } - - return s[start : end+1] -} - -/* -// Separates by a comma, ignoring spaces too. -// This was slower than strings.Split. -func separateByComma(in string) (out []string) { - - out = []string{} - - start, lim := 0, len(in)-1 - - for start < lim { - var end int - - for end = start; end <= lim; end++ { - // Is a comma? - if in[end] == ',' { - break - } - } - - out = append(out, trimString(in[start:end])) - - start = end + 1 - } - - return -} -*/ - -// Separates by a comma, ignoring spaces too. -func separateByComma(in string) (out []string) { - out = strings.Split(in, ",") - for i := range out { - out[i] = trimString(out[i]) - } - return -} - -// Separates by spaces, ignoring spaces too. -func separateBySpace(in string) (out []string) { - if len(in) == 0 { - return []string{""} - } - - pre := strings.Split(in, " ") - out = make([]string, 0, len(pre)) - - for i := range pre { - pre[i] = trimString(pre[i]) - if pre[i] != "" { - out = append(out, pre[i]) - } - } - - return -} - -func separateByAS(in string) (out []string) { - out = []string{} - - if len(in) < 6 { - // The minimum expression with the AS keyword is "x AS y", 6 chars. - return []string{in} - } - - start, lim := 0, len(in)-1 - - for start <= lim { - var end int - - for end = start; end <= lim; end++ { - if end > 3 && isBlankSymbol(in[end]) && isBlankSymbol(in[end-3]) { - if (in[end-1] == 's' || in[end-1] == 'S') && (in[end-2] == 'a' || in[end-2] == 'A') { - break - } - } - } - - if end < lim { - out = append(out, trimString(in[start:end-3])) - } else { - out = append(out, trimString(in[start:end])) - } - - start = end + 1 - } - - return -} diff --git a/util/sqlgen/utilities_test.go b/util/sqlgen/utilities_test.go deleted file mode 100644 index 4d5ec550..00000000 --- a/util/sqlgen/utilities_test.go +++ /dev/null @@ -1,284 +0,0 @@ -package sqlgen - -import ( - "bytes" - "regexp" - "strings" - "testing" - "unicode" -) - -const ( - blankSymbol = ' ' - stringWithCommas = "Hello,,World!,Enjoy" - stringWithSpaces = " Hello World! Enjoy" - stringWithASKeyword = "table.Name AS myTableAlias" -) - -var ( - bytesWithLeadingBlanks = []byte(" Hello world! ") - stringWithLeadingBlanks = string(bytesWithLeadingBlanks) -) - -func TestUtilIsBlankSymbol(t *testing.T) { - if isBlankSymbol(' ') == false { - t.Fail() - } - if isBlankSymbol('\n') == false { - t.Fail() - } - if isBlankSymbol('\t') == false { - t.Fail() - } - if isBlankSymbol('\r') == false { - t.Fail() - } - if isBlankSymbol('x') == true { - t.Fail() - } -} - -func TestUtilTrimBytes(t *testing.T) { - var trimmed []byte - - trimmed = trimBytes([]byte(" \t\nHello World! \n")) - if string(trimmed) != "Hello World!" { - t.Fatalf("Got: %s\n", string(trimmed)) - } - - trimmed = trimBytes([]byte("Nope")) - if string(trimmed) != "Nope" { - t.Fatalf("Got: %s\n", string(trimmed)) - } - - trimmed = trimBytes([]byte("")) - if string(trimmed) != "" { - t.Fatalf("Got: %s\n", string(trimmed)) - } - - trimmed = trimBytes([]byte(" ")) - if string(trimmed) != "" { - t.Fatalf("Got: %s\n", string(trimmed)) - } - - trimmed = trimBytes(nil) - if string(trimmed) != "" { - t.Fatalf("Got: %s\n", string(trimmed)) - } -} - -func TestUtilSeparateByComma(t *testing.T) { - chunks := separateByComma("Hello,,World!,Enjoy") - - if len(chunks) != 4 { - t.Fatal() - } - - if chunks[0] != "Hello" { - t.Fatal() - } - if chunks[1] != "" { - t.Fatal() - } - if chunks[2] != "World!" { - t.Fatal() - } - if chunks[3] != "Enjoy" { - t.Fatal() - } -} - -func TestUtilSeparateBySpace(t *testing.T) { - chunks := separateBySpace(" Hello World! Enjoy") - - if len(chunks) != 3 { - t.Fatal() - } - - if chunks[0] != "Hello" { - t.Fatal() - } - if chunks[1] != "World!" { - t.Fatal() - } - if chunks[2] != "Enjoy" { - t.Fatal() - } -} - -func TestUtilSeparateByAS(t *testing.T) { - var chunks []string - - var tests = []string{ - `table.Name AS myTableAlias`, - `table.Name AS myTableAlias`, - "table.Name\tAS\r\nmyTableAlias", - } - - for _, test := range tests { - chunks = separateByAS(test) - - if len(chunks) != 2 { - t.Fatalf(`Expecting 2 results.`) - } - - if chunks[0] != "table.Name" { - t.Fatal(`Expecting first result to be "table.Name".`) - } - if chunks[1] != "myTableAlias" { - t.Fatal(`Expecting second result to be myTableAlias.`) - } - } - - // Single character. - chunks = separateByAS("a") - - if len(chunks) != 1 { - t.Fatalf(`Expecting 1 results.`) - } - - if chunks[0] != "a" { - t.Fatal(`Expecting first result to be "a".`) - } - - // Empty name - chunks = separateByAS("") - - if len(chunks) != 1 { - t.Fatalf(`Expecting 1 results.`) - } - - if chunks[0] != "" { - t.Fatal(`Expecting first result to be "".`) - } - - // Single name - chunks = separateByAS(" A Single Table ") - - if len(chunks) != 1 { - t.Fatalf(`Expecting 1 results.`) - } - - if chunks[0] != "A Single Table" { - t.Fatal(`Expecting first result to be "ASingleTable".`) - } - - // Minimal expression. - chunks = separateByAS("a AS b") - - if len(chunks) != 2 { - t.Fatalf(`Expecting 2 results.`) - } - - if chunks[0] != "a" { - t.Fatal(`Expecting first result to be "a".`) - } - - if chunks[1] != "b" { - t.Fatal(`Expecting first result to be "b".`) - } - - // Minimal expression with spaces. - chunks = separateByAS(" a AS b ") - - if len(chunks) != 2 { - t.Fatalf(`Expecting 2 results.`) - } - - if chunks[0] != "a" { - t.Fatal(`Expecting first result to be "a".`) - } - - if chunks[1] != "b" { - t.Fatal(`Expecting first result to be "b".`) - } - - // Minimal expression + 1 with spaces. - chunks = separateByAS(" a AS bb ") - - if len(chunks) != 2 { - t.Fatalf(`Expecting 2 results.`) - } - - if chunks[0] != "a" { - t.Fatal(`Expecting first result to be "a".`) - } - - if chunks[1] != "bb" { - t.Fatal(`Expecting first result to be "bb".`) - } -} - -func BenchmarkUtilIsBlankSymbol(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = isBlankSymbol(blankSymbol) - } -} - -func BenchmarkUtilStdlibIsBlankSymbol(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = unicode.IsSpace(blankSymbol) - } -} - -func BenchmarkUtilTrimBytes(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = trimBytes(bytesWithLeadingBlanks) - } -} -func BenchmarkUtilStdlibBytesTrimSpace(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = bytes.TrimSpace(bytesWithLeadingBlanks) - } -} - -func BenchmarkUtilTrimString(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = trimString(stringWithLeadingBlanks) - } -} - -func BenchmarkUtilStdlibStringsTrimSpace(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = strings.TrimSpace(stringWithLeadingBlanks) - } -} - -func BenchmarkUtilSeparateByComma(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = separateByComma(stringWithCommas) - } -} - -func BenchmarkUtilRegExpSeparateByComma(b *testing.B) { - sep := regexp.MustCompile(`\s*?,\s*?`) - for i := 0; i < b.N; i++ { - _ = sep.Split(stringWithCommas, -1) - } -} - -func BenchmarkUtilSeparateBySpace(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = separateBySpace(stringWithSpaces) - } -} - -func BenchmarkUtilRegExpSeparateBySpace(b *testing.B) { - sep := regexp.MustCompile(`\s+`) - for i := 0; i < b.N; i++ { - _ = sep.Split(stringWithSpaces, -1) - } -} - -func BenchmarkUtilSeparateByAS(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = separateByAS(stringWithASKeyword) - } -} - -func BenchmarkUtilRegExpSeparateByAS(b *testing.B) { - sep := regexp.MustCompile(`(?i:\s+AS\s+)`) - for i := 0; i < b.N; i++ { - _ = sep.Split(stringWithASKeyword, -1) - } -} diff --git a/util/sqlgen/value.go b/util/sqlgen/value.go deleted file mode 100644 index ab5a4606..00000000 --- a/util/sqlgen/value.go +++ /dev/null @@ -1,163 +0,0 @@ -package sqlgen - -import ( - //"database/sql/driver" - "fmt" - //"log" - "strings" -) - -// ValueGroups represents an array of value groups. -type ValueGroups struct { - Values []*Values - hash string -} - -// Values represents an array of Value. -type Values struct { - Values []Fragment - hash string -} - -// Value represents an escaped SQL value. -type Value struct { - V interface{} - hash string -} - -// NewValue creates and returns a Value. -func NewValue(v interface{}) *Value { - return &Value{V: v} -} - -// NewValueGroup creates and returns an array of values. -func NewValueGroup(v ...Fragment) *Values { - return &Values{Values: v} -} - -// Hash returns a unique identifier. -func (v *Value) Hash() string { - if v.hash == "" { - switch t := v.V.(type) { - case Fragment: - v.hash = `Value(` + t.Hash() + `)` - case string: - v.hash = `Value(` + t + `)` - default: - v.hash = fmt.Sprintf(`Value(%v)`, v.V) - } - } - return v.hash -} - -// Compile transforms the Value into an equivalent SQL representation. -func (v *Value) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(v); ok { - return z - } - - switch t := v.V.(type) { - case Raw: - compiled = t.Compile(layout) - case Fragment: - compiled = t.Compile(layout) - default: - compiled = mustParse(layout.ValueQuote, RawValue(fmt.Sprintf(`%v`, v.V))) - } - - layout.Write(v, compiled) - - return -} - -/* -func (v *Value) Scan(src interface{}) error { - log.Println("Scan(", src, ") on", v.V) - return nil -} - -func (v *Value) Value() (driver.Value, error) { - log.Println("Value() on", v.V) - return v.V, nil -} -*/ - -// Hash returns a unique identifier. -func (vs *Values) Hash() string { - if vs.hash == "" { - hash := make([]string, len(vs.Values)) - for i := range vs.Values { - hash[i] = vs.Values[i].Hash() - } - vs.hash = `Values(` + strings.Join(hash, `,`) + `)` - } - return vs.hash -} - -// Compile transforms the Values into an equivalent SQL representation. -func (vs *Values) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(vs); ok { - return c - } - - l := len(vs.Values) - if l > 0 { - chunks := make([]string, 0, l) - for i := 0; i < l; i++ { - chunks = append(chunks, vs.Values[i].Compile(layout)) - } - compiled = mustParse(layout.ClauseGroup, strings.Join(chunks, layout.ValueSeparator)) - } - layout.Write(vs, compiled) - return -} - -/* -func (vs Values) Scan(src interface{}) error { - log.Println("Values.Scan(", src, ")") - return nil -} - -func (vs Values) Value() (driver.Value, error) { - log.Println("Values.Value()") - return vs, nil -} -*/ - -// Hash returns a unique identifier. -func (vg *ValueGroups) Hash() string { - if vg.hash == "" { - l := len(vg.Values) - hashes := make([]string, l) - for i := 0; i < l; i++ { - hashes[i] = vg.Values[i].Hash() - } - vg.hash = fmt.Sprintf(`ValueGroups(%v)`, strings.Join(hashes, ", ")) - } - return vg.hash -} - -// Compile transforms the ValueGroups into an equivalent SQL representation. -func (vg *ValueGroups) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(vg); ok { - return c - } - - l := len(vg.Values) - if l > 0 { - chunks := make([]string, 0, l) - for i := 0; i < l; i++ { - chunks = append(chunks, vg.Values[i].Compile(layout)) - } - compiled = strings.Join(chunks, layout.ValueSeparator) - } - - layout.Write(vg, compiled) - return -} - -// JoinValueGroups creates a new *ValueGroups object. -func JoinValueGroups(values ...*Values) *ValueGroups { - return &ValueGroups{Values: values} -} diff --git a/util/sqlgen/value_test.go b/util/sqlgen/value_test.go deleted file mode 100644 index 84c3c004..00000000 --- a/util/sqlgen/value_test.go +++ /dev/null @@ -1,99 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestValue(t *testing.T) { - var s, e string - var val *Value - - val = NewValue(1) - - s = val.Compile(defaultTemplate) - e = `'1'` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } - - val = NewValue(&Raw{Value: "NOW()"}) - - s = val.Compile(defaultTemplate) - e = `NOW()` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestValues(t *testing.T) { - var s, e string - - val := NewValueGroup( - &Value{V: &Raw{Value: "1"}}, - &Value{V: &Raw{Value: "2"}}, - &Value{V: "3"}, - ) - - s = val.Compile(defaultTemplate) - e = `(1, 2, '3')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkValue(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = NewValue("a") - } -} - -func BenchmarkValueHash(b *testing.B) { - v := NewValue("a") - for i := 0; i < b.N; i++ { - _ = v.Hash() - } -} - -func BenchmarkValueCompile(b *testing.B) { - v := NewValue("a") - for i := 0; i < b.N; i++ { - _ = v.Compile(defaultTemplate) - } -} - -func BenchmarkValueCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - v := NewValue("a") - _ = v.Compile(defaultTemplate) - } -} - -func BenchmarkValues(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = NewValueGroup(NewValue("a"), NewValue("b")) - } -} - -func BenchmarkValuesHash(b *testing.B) { - vs := NewValueGroup(NewValue("a"), NewValue("b")) - for i := 0; i < b.N; i++ { - _ = vs.Hash() - } -} - -func BenchmarkValuesCompile(b *testing.B) { - vs := NewValueGroup(NewValue("a"), NewValue("b")) - for i := 0; i < b.N; i++ { - _ = vs.Compile(defaultTemplate) - } -} - -func BenchmarkValuesCompileNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - vs := NewValueGroup(NewValue("a"), NewValue("b")) - _ = vs.Compile(defaultTemplate) - } -} diff --git a/util/sqlgen/where.go b/util/sqlgen/where.go deleted file mode 100644 index 64ea9f10..00000000 --- a/util/sqlgen/where.go +++ /dev/null @@ -1,123 +0,0 @@ -package sqlgen - -import ( - "fmt" - "strings" -) - -// Or represents an SQL OR operator. -type Or Where - -// And represents an SQL AND operator. -type And Where - -// Where represents an SQL WHERE clause. -type Where struct { - Conditions []Fragment - hash string -} - -type conds struct { - Conds string -} - -// WhereConditions creates and retuens a new Where. -func WhereConditions(conditions ...Fragment) *Where { - return &Where{Conditions: conditions} -} - -// JoinWithOr creates and returns a new Or. -func JoinWithOr(conditions ...Fragment) *Or { - return &Or{Conditions: conditions} -} - -// JoinWithAnd creates and returns a new And. -func JoinWithAnd(conditions ...Fragment) *And { - return &And{Conditions: conditions} -} - -// Hash returns a unique identifier. -func (w *Where) Hash() string { - if w.hash == "" { - hash := make([]string, len(w.Conditions)) - for i := range w.Conditions { - hash[i] = w.Conditions[i].Hash() - } - w.hash = fmt.Sprintf(`Where{%s}`, strings.Join(hash, `, `)) - } - return w.hash -} - -// Hash returns a unique identifier. -func (o *Or) Hash() string { - w := Where(*o) - return `Or(` + w.Hash() + `)` -} - -// Hash returns a unique identifier. -func (a *And) Hash() string { - w := Where(*a) - return `Or(` + w.Hash() + `)` -} - -// Compile transforms the Or into an equivalent SQL representation. -func (o *Or) Compile(layout *Template) (compiled string) { - - if z, ok := layout.Read(o); ok { - return z - } - - compiled = groupCondition(layout, o.Conditions, mustParse(layout.ClauseOperator, layout.OrKeyword)) - - layout.Write(o, compiled) - - return -} - -// Compile transforms the And into an equivalent SQL representation. -func (a *And) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(a); ok { - return c - } - - compiled = groupCondition(layout, a.Conditions, mustParse(layout.ClauseOperator, layout.AndKeyword)) - - layout.Write(a, compiled) - - return -} - -// Compile transforms the Where into an equivalent SQL representation. -func (w *Where) Compile(layout *Template) (compiled string) { - if c, ok := layout.Read(w); ok { - return c - } - - grouped := groupCondition(layout, w.Conditions, mustParse(layout.ClauseOperator, layout.AndKeyword)) - - if grouped != "" { - compiled = mustParse(layout.WhereLayout, conds{grouped}) - } - - layout.Write(w, compiled) - - return -} - -func groupCondition(layout *Template, terms []Fragment, joinKeyword string) string { - l := len(terms) - - chunks := make([]string, 0, l) - - if l > 0 { - for i := 0; i < l; i++ { - chunks = append(chunks, terms[i].Compile(layout)) - } - } - - if len(chunks) > 0 { - return mustParse(layout.ClauseGroup, strings.Join(chunks, joinKeyword)) - } - - return "" -} diff --git a/util/sqlgen/where_test.go b/util/sqlgen/where_test.go deleted file mode 100644 index c4b2a182..00000000 --- a/util/sqlgen/where_test.go +++ /dev/null @@ -1,113 +0,0 @@ -package sqlgen - -import ( - "testing" -) - -func TestWhereAnd(t *testing.T) { - var s, e string - - and := JoinWithAnd( - &ColumnValue{Column: &Column{Name: "id"}, Operator: ">", Value: NewValue(&Raw{Value: "8"})}, - &ColumnValue{Column: &Column{Name: "id"}, Operator: "<", Value: NewValue(&Raw{Value: "99"})}, - &ColumnValue{Column: &Column{Name: "name"}, Operator: "=", Value: NewValue("John")}, - ) - - s = and.Compile(defaultTemplate) - e = `("id" > 8 AND "id" < 99 AND "name" = 'John')` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestWhereOr(t *testing.T) { - var s, e string - - or := JoinWithOr( - &ColumnValue{Column: &Column{Name: "id"}, Operator: "=", Value: NewValue(&Raw{Value: "8"})}, - &ColumnValue{Column: &Column{Name: "id"}, Operator: "=", Value: NewValue(&Raw{Value: "99"})}, - ) - - s = or.Compile(defaultTemplate) - e = `("id" = 8 OR "id" = 99)` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestWhereAndOr(t *testing.T) { - var s, e string - - and := JoinWithAnd( - &ColumnValue{Column: &Column{Name: "id"}, Operator: ">", Value: NewValue(&Raw{Value: "8"})}, - &ColumnValue{Column: &Column{Name: "id"}, Operator: "<", Value: NewValue(&Raw{Value: "99"})}, - &ColumnValue{Column: &Column{Name: "name"}, Operator: "=", Value: NewValue("John")}, - JoinWithOr( - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Smith")}, - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Reyes")}, - ), - ) - - s = and.Compile(defaultTemplate) - e = `("id" > 8 AND "id" < 99 AND "name" = 'John' AND ("last_name" = 'Smith' OR "last_name" = 'Reyes'))` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func TestWhereAndRawOrAnd(t *testing.T) { - var s, e string - - where := WhereConditions( - JoinWithAnd( - &ColumnValue{Column: &Column{Name: "id"}, Operator: ">", Value: NewValue(&Raw{Value: "8"})}, - &ColumnValue{Column: &Column{Name: "id"}, Operator: "<", Value: NewValue(&Raw{Value: "99"})}, - ), - &ColumnValue{Column: &Column{Name: "name"}, Operator: "=", Value: NewValue("John")}, - &Raw{Value: "city_id = 728"}, - JoinWithOr( - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Smith")}, - &ColumnValue{Column: &Column{Name: "last_name"}, Operator: "=", Value: NewValue("Reyes")}, - ), - JoinWithAnd( - &ColumnValue{Column: &Column{Name: "age"}, Operator: ">", Value: NewValue(&Raw{Value: "18"})}, - &ColumnValue{Column: &Column{Name: "age"}, Operator: "<", Value: NewValue(&Raw{Value: "41"})}, - ), - ) - - s = trim(where.Compile(defaultTemplate)) - e = `WHERE (("id" > 8 AND "id" < 99) AND "name" = 'John' AND city_id = 728 AND ("last_name" = 'Smith' OR "last_name" = 'Reyes') AND ("age" > 18 AND "age" < 41))` - - if s != e { - t.Fatalf("Got: %s, Expecting: %s", s, e) - } -} - -func BenchmarkWhere(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ) - } -} - -func BenchmarkCompileWhere(b *testing.B) { - w := WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ) - for i := 0; i < b.N; i++ { - w.Compile(defaultTemplate) - } -} - -func BenchmarkCompileWhereNoCache(b *testing.B) { - for i := 0; i < b.N; i++ { - w := WhereConditions( - &ColumnValue{Column: &Column{Name: "baz"}, Operator: "=", Value: NewValue(99)}, - ) - w.Compile(defaultTemplate) - } -} diff --git a/util/sqlutil/convert.go b/util/sqlutil/convert.go deleted file mode 100644 index 1033a6eb..00000000 --- a/util/sqlutil/convert.go +++ /dev/null @@ -1,291 +0,0 @@ -package sqlutil - -import ( - "fmt" - "reflect" - "strings" - - "upper.io/db" - "upper.io/db/util/sqlgen" -) - -var ( - sqlPlaceholder = sqlgen.RawValue(`?`) - sqlNull = sqlgen.RawValue(`NULL`) - sqlIsOperator = `IS` - sqlInOperator = `IN` - sqlDefaultOperator = `=` -) - -type TemplateWithUtils struct { - *sqlgen.Template -} - -func NewTemplateWithUtils(template *sqlgen.Template) *TemplateWithUtils { - return &TemplateWithUtils{template} -} - -// ToWhereWithArguments converts the given db.Cond parameters into a sqlgen.Where -// value. -func (tu *TemplateWithUtils) ToWhereWithArguments(term interface{}) (where sqlgen.Where, args []interface{}) { - args = []interface{}{} - - switch t := term.(type) { - case []interface{}: - if len(t) > 0 { - if s, ok := t[0].(string); ok { - if strings.ContainsAny(s, "?") || len(t) == 1 { - where.Conditions = []sqlgen.Fragment{sqlgen.RawValue(s)} - args = t[1:] - } else { - cond := db.Cond{} - - if len(t) > 2 { - cond[s] = t[1:] - } else { - cond[s] = t[1] - } - - cv, v := tu.ToColumnValues(cond) - - args = append(args, v...) - for i := range cv.ColumnValues { - where.Conditions = append(where.Conditions, cv.ColumnValues[i]) - } - } - return - } - } - for i := range t { - w, v := tu.ToWhereWithArguments(t[i]) - if len(w.Conditions) == 0 { - continue - } - args = append(args, v...) - where.Conditions = append(where.Conditions, w.Conditions...) - } - return - case db.And: - var op sqlgen.And - for i := range t { - w, v := tu.ToWhereWithArguments(t[i]) - if len(w.Conditions) == 0 { - continue - } - args = append(args, v...) - op.Conditions = append(op.Conditions, w.Conditions...) - } - if len(op.Conditions) > 0 { - where.Conditions = append(where.Conditions, &op) - } - return - case db.Or: - var op sqlgen.Or - for i := range t { - w, v := tu.ToWhereWithArguments(t[i]) - if len(w.Conditions) == 0 { - continue - } - args = append(args, v...) - op.Conditions = append(op.Conditions, w.Conditions...) - } - if len(op.Conditions) > 0 { - where.Conditions = append(where.Conditions, &op) - } - return - case db.Raw: - if s, ok := t.Value.(string); ok { - where.Conditions = append(where.Conditions, sqlgen.RawValue(s)) - } - return - case db.Cond: - cv, v := tu.ToColumnValues(t) - args = append(args, v...) - for i := range cv.ColumnValues { - where.Conditions = append(where.Conditions, cv.ColumnValues[i]) - } - return - case db.Constrainer: - cv, v := tu.ToColumnValues(t.Constraint()) - args = append(args, v...) - for i := range cv.ColumnValues { - where.Conditions = append(where.Conditions, cv.ColumnValues[i]) - } - return - } - - panic(fmt.Sprintf(db.ErrUnknownConditionType.Error(), term)) -} - -// ToInterfaceArguments converts the given value into an array of interfaces. -func (tu *TemplateWithUtils) ToInterfaceArguments(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 -} - -// ToColumnValues converts the given db.Cond into a sqlgen.ColumnValues struct. -func (tu *TemplateWithUtils) ToColumnValues(term interface{}) (cv sqlgen.ColumnValues, args []interface{}) { - args = []interface{}{} - - switch t := term.(type) { - case []interface{}: - l := len(t) - for i := 0; i < l; i++ { - column := t[i].(string) - - if !strings.ContainsAny(column, "=") { - column = fmt.Sprintf("%s = ?", column) - } - - chunks := strings.SplitN(column, "=", 2) - - column = chunks[0] - format := strings.TrimSpace(chunks[1]) - - columnValue := sqlgen.ColumnValue{ - Column: sqlgen.ColumnWithName(column), - Operator: "=", - Value: sqlgen.RawValue(format), - } - - ps := strings.Count(format, "?") - if i+ps < l { - for j := 0; j < ps; j++ { - args = append(args, t[i+j+1]) - } - i = i + ps - } else { - panic(fmt.Sprintf("Format string %q has more placeholders than given arguments.", format)) - } - - cv.ColumnValues = append(cv.ColumnValues, &columnValue) - } - return cv, args - // Return error. - case db.Cond: - for column, value := range t { - columnValue := sqlgen.ColumnValue{} - - // Guessing operator from input, or using a default one. - column := strings.TrimSpace(column) - chunks := strings.SplitN(column, ` `, 2) - - columnValue.Column = sqlgen.ColumnWithName(chunks[0]) - - if len(chunks) > 1 { - columnValue.Operator = chunks[1] - } - - switch value := value.(type) { - case db.Func: - v := tu.ToInterfaceArguments(value.Args) - columnValue.Operator = value.Name - - if v == nil { - // A function with no arguments. - columnValue.Value = sqlgen.RawValue(`()`) - } else { - // A function with one or more arguments. - columnValue.Value = sqlgen.RawValue(fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))) - } - - args = append(args, v...) - default: - v := tu.ToInterfaceArguments(value) - - if v == nil { - // Nil value given. - columnValue.Value = sqlNull - if columnValue.Operator == "" { - columnValue.Operator = sqlIsOperator - } - } else { - if len(v) > 1 { - // Array value given. - columnValue.Value = sqlgen.RawValue(fmt.Sprintf(`(?%s)`, strings.Repeat(`, ?`, len(v)-1))) - if columnValue.Operator == "" { - columnValue.Operator = sqlInOperator - } - } else { - // Single value given. - columnValue.Value = sqlPlaceholder - } - args = append(args, v...) - } - } - - // Using guessed operator if no operator was given. - if columnValue.Operator == "" { - if tu.DefaultOperator != "" { - columnValue.Operator = tu.DefaultOperator - } else { - columnValue.Operator = sqlDefaultOperator - } - } - - cv.ColumnValues = append(cv.ColumnValues, &columnValue) - } - return cv, args - } - - panic("Unknown map type.") -} - -// ToColumnsValuesAndArguments maps the given columnNames and columnValues into -// sqlgen's Columns and Values, it also extracts and returns query arguments. -func (tu *TemplateWithUtils) ToColumnsValuesAndArguments(columnNames []string, columnValues []interface{}) (*sqlgen.Columns, *sqlgen.Values, []interface{}, error) { - var arguments []interface{} - - columns := new(sqlgen.Columns) - - columns.Columns = make([]sqlgen.Fragment, 0, len(columnNames)) - for i := range columnNames { - columns.Columns = append(columns.Columns, sqlgen.ColumnWithName(columnNames[i])) - } - - values := new(sqlgen.Values) - - arguments = make([]interface{}, 0, len(columnValues)) - values.Values = make([]sqlgen.Fragment, 0, len(columnValues)) - - for i := range columnValues { - switch v := columnValues[i].(type) { - case *sqlgen.Value: - // Adding value. - values.Values = append(values.Values, v) - case sqlgen.Value: - // Adding value. - values.Values = append(values.Values, &v) - default: - // Adding both value and placeholder. - values.Values = append(values.Values, sqlPlaceholder) - arguments = append(arguments, v) - } - } - - return columns, values, arguments, nil -} diff --git a/util/sqlutil/debug.go b/util/sqlutil/debug.go deleted file mode 100644 index 2357b59a..00000000 --- a/util/sqlutil/debug.go +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. -// -// 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 sqlutil - -import ( - "fmt" - "log" - "os" - "regexp" - "strings" - - "upper.io/db" -) - -var ( - reInvisibleChars = regexp.MustCompile(`[\s\r\n\t]+`) - reColumnCompareExclude = regexp.MustCompile(`[^a-zA-Z0-9]`) -) - -func init() { - if os.Getenv(db.EnvEnableDebug) != "" { - db.Debug = true - } -} - -// Debug is used for printing SQL queries and arguments. -type Debug struct { - SQL string - Args []interface{} - Err error - Start int64 - End int64 -} - -// Print prints a debug message to stdlog. -func (d *Debug) Print() { - d.SQL = reInvisibleChars.ReplaceAllString(d.SQL, ` `) - d.SQL = strings.TrimSpace(d.SQL) - - s := make([]string, 0, 3) - - if d.SQL != "" { - s = append(s, fmt.Sprintf(`Q: %s`, d.SQL)) - } - - if len(d.Args) > 0 { - s = append(s, fmt.Sprintf(`A: %v`, d.Args)) - } - - if d.Err != nil { - s = append(s, fmt.Sprintf(`E: %q`, d.Err)) - } - - s = append(s, fmt.Sprintf(`T: %0.5fs`, float64(d.End-d.Start)/float64(1e9))) - - log.Printf("\n\t%s\n\n", strings.Join(s, "\n\t")) -} - -func Log(query string, args []interface{}, err error, start int64, end int64) { - if db.Debug { - d := Debug{query, args, err, start, end} - d.Print() - } -} diff --git a/util/sqlutil/fetch.go b/util/sqlutil/fetch.go deleted file mode 100644 index 823bf64b..00000000 --- a/util/sqlutil/fetch.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. -// -// 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 sqlutil - -import ( - "encoding/json" - "reflect" - - "github.com/jmoiron/sqlx" - "github.com/jmoiron/sqlx/reflectx" - "upper.io/db" -) - -// FetchRow receives a *sqlx.Rows value and tries to map all the rows into a -// single struct given by the pointer `dst`. -func FetchRow(rows *sqlx.Rows, dst interface{}) error { - var columns []string - var err error - - dstv := reflect.ValueOf(dst) - - if dstv.IsNil() || dstv.Kind() != reflect.Ptr { - return db.ErrExpectingPointer - } - - itemV := dstv.Elem() - - if columns, err = rows.Columns(); err != nil { - return err - } - - reset(dst) - - next := rows.Next() - - if next == false { - if err = rows.Err(); err != nil { - return err - } - return db.ErrNoMoreRows - } - - itemT := itemV.Type() - item, err := fetchResult(itemT, rows, columns) - - if err != nil { - return err - } - - if itemT.Kind() == reflect.Ptr { - itemV.Set(item) - } else { - itemV.Set(reflect.Indirect(item)) - } - - return nil -} - -// FetchRows receives a *sqlx.Rows value and tries to map all the rows into a -// slice of structs given by the pointer `dst`. -func FetchRows(rows *sqlx.Rows, dst interface{}) error { - var err error - - defer rows.Close() - - // Destination. - dstv := reflect.ValueOf(dst) - - if dstv.IsNil() || dstv.Kind() != reflect.Ptr { - return db.ErrExpectingPointer - } - - if dstv.Elem().Kind() != reflect.Slice { - return db.ErrExpectingSlicePointer - } - - if dstv.Kind() != reflect.Ptr || dstv.Elem().Kind() != reflect.Slice || dstv.IsNil() { - return db.ErrExpectingSliceMapStruct - } - - var columns []string - if columns, err = rows.Columns(); err != nil { - return err - } - - slicev := dstv.Elem() - itemT := slicev.Type().Elem() - - reset(dst) - - for rows.Next() { - item, err := fetchResult(itemT, rows, columns) - if err != nil { - return err - } - if itemT.Kind() == reflect.Ptr { - slicev = reflect.Append(slicev, item) - } else { - slicev = reflect.Append(slicev, reflect.Indirect(item)) - } - } - - dstv.Elem().Set(slicev) - - return nil -} - -func fetchResult(itemT reflect.Type, rows *sqlx.Rows, columns []string) (reflect.Value, error) { - var item reflect.Value - var err error - - objT := itemT - - switch objT.Kind() { - case reflect.Map: - item = reflect.MakeMap(objT) - case reflect.Struct: - item = reflect.New(objT) - case reflect.Ptr: - objT = itemT.Elem() - if objT.Kind() != reflect.Struct { - return item, db.ErrExpectingMapOrStruct - } - item = reflect.New(objT) - default: - return item, db.ErrExpectingMapOrStruct - } - - switch objT.Kind() { - - case reflect.Struct: - - values := make([]interface{}, len(columns)) - typeMap := rows.Mapper.TypeMap(itemT) - fieldMap := typeMap.Names - wrappedValues := map[*reflectx.FieldInfo]interface{}{} - - for i, k := range columns { - fi, ok := fieldMap[k] - if !ok { - values[i] = new(interface{}) - continue - } - - // TODO: refactor into a nice pattern - if _, ok := fi.Options["stringarray"]; ok { - values[i] = &[]byte{} - wrappedValues[fi] = values[i] - } else if _, ok := fi.Options["int64array"]; ok { - values[i] = &[]byte{} - wrappedValues[fi] = values[i] - } else if _, ok := fi.Options["jsonb"]; ok { - values[i] = &[]byte{} - wrappedValues[fi] = values[i] - } else { - f := reflectx.FieldByIndexes(item, fi.Index) - values[i] = f.Addr().Interface() - } - - if u, ok := values[i].(db.Unmarshaler); ok { - values[i] = scanner{u} - } - } - - // Scanner - for reads - // Valuer - for writes - - // OptionTypes - // - before/after scan - // - before/after valuer.. - - if err = rows.Scan(values...); err != nil { - return item, err - } - - // TODO: move this stuff out of here.. find a nice pattern - for fi, v := range wrappedValues { - var opt string - if _, ok := fi.Options["stringarray"]; ok { - opt = "stringarray" - } else if _, ok := fi.Options["int64array"]; ok { - opt = "int64array" - } else if _, ok := fi.Options["jsonb"]; ok { - opt = "jsonb" - } - - b := v.(*[]byte) - - f := reflectx.FieldByIndexesReadOnly(item, fi.Index) - - switch opt { - case "stringarray": - v := StringArray{} - err := v.Scan(*b) - if err != nil { - return item, err - } - f.Set(reflect.ValueOf(v)) - case "int64array": - v := Int64Array{} - err := v.Scan(*b) - if err != nil { - return item, err - } - f.Set(reflect.ValueOf(v)) - case "jsonb": - if len(*b) == 0 { - continue - } - - var vv reflect.Value - t := reflect.PtrTo(f.Type()) - - switch t.Kind() { - case reflect.Map: - vv = reflect.MakeMap(t) - case reflect.Slice: - vv = reflect.MakeSlice(t, 0, 0) - default: - vv = reflect.New(t) - } - - err := json.Unmarshal(*b, vv.Interface()) - if err != nil { - return item, err - } - - vv = vv.Elem().Elem() - - if !vv.IsValid() || (vv.Kind() == reflect.Ptr && vv.IsNil()) { - continue - } - - f.Set(vv) - } - } - - case reflect.Map: - - columns, err := rows.Columns() - if err != nil { - return item, err - } - - values := make([]interface{}, len(columns)) - for i := range values { - if itemT.Elem().Kind() == reflect.Interface { - values[i] = new(interface{}) - } else { - values[i] = reflect.New(itemT.Elem()).Interface() - } - } - - if err = rows.Scan(values...); err != nil { - return item, err - } - - for i, column := range columns { - item.SetMapIndex(reflect.ValueOf(column), reflect.Indirect(reflect.ValueOf(values[i]))) - } - - } - - return item, nil -} - -func reset(data interface{}) error { - // Resetting element. - v := reflect.ValueOf(data).Elem() - t := v.Type() - z := reflect.Zero(t) - v.Set(z) - return nil -} diff --git a/util/sqlutil/result/result.go b/util/sqlutil/result/result.go deleted file mode 100644 index 2e50932e..00000000 --- a/util/sqlutil/result/result.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. -// -// 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 result - -import ( - "upper.io/db" -) - -type Result struct { - b db.QueryBuilder - table string - 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(b db.QueryBuilder, table string, conds []interface{}) *Result { - return &Result{ - b: b, - table: table, - conds: conds, - } -} - -// Sets conditions for reducing the working set. -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 = 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 = 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 { - r.groupBy = fields - 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 { - r.orderBy = fields - return r -} - -// Retrieves only the given fields. -func (r *Result) Select(fields ...interface{}) db.Result { - 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 { - return r.buildSelect().Iterator().All(dst) -} - -// Fetches only one result from the resultset. -func (r *Result) One(dst interface{}) error { - return r.buildSelect().Iterator().One(dst) -} - -// Fetches the next result from the resultset. -func (r *Result) Next(dst interface{}) (err error) { - if r.iter == nil { - r.iter = r.buildSelect().Iterator() - } - if !r.iter.Next(dst) { - return r.iter.Err() - } - return nil -} - -// Removes the matching items from the collection. -func (r *Result) Remove() error { - q := r.b.DeleteFrom(r.table). - 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.table). - Set(values). - Where(r.conds...). - Limit(r.limit) - - _, err := q.Exec() - return err -} - -// Counts the elements within the main conditions of the set. -func (r *Result) Count() (uint64, error) { - counter := struct { - Count uint64 `db:"_t"` - }{} - - q := r.buildSelect() - q.Columns(db.Raw{"COUNT(1) AS _t"}).Limit(1) - - if err := q.Iterator().One(&counter); err != nil { - return 0, err - } - - return counter.Count, nil -} - -func (r *Result) buildSelect() db.QuerySelector { - q := r.b.Select(r.fields...) - - q.From(r.table) - q.Where(r.conds...) - q.Limit(r.limit) - q.Offset(r.offset) - - q.GroupBy(r.groupBy...) - q.OrderBy(r.orderBy...) - - return q -} diff --git a/util/sqlutil/scanner.go b/util/sqlutil/scanner.go deleted file mode 100644 index 5fed1f43..00000000 --- a/util/sqlutil/scanner.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. -// -// 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 sqlutil - -import ( - "database/sql" - "database/sql/driver" - "encoding/json" - "errors" - "strconv" - "strings" - - "upper.io/db" -) - -type scanner struct { - v db.Unmarshaler -} - -func (u scanner) Scan(v interface{}) error { - return u.v.UnmarshalDB(v) -} - -var _ sql.Scanner = scanner{} - -//------ - -type JsonbType struct { - V interface{} -} - -func (j *JsonbType) Scan(src interface{}) error { - b, ok := src.([]byte) - if !ok { - return errors.New("Scan source was not []bytes") - } - - v := JsonbType{} - if err := json.Unmarshal(b, &v.V); err != nil { - return err - } - *j = v - return nil -} - -func (j JsonbType) Value() (driver.Value, error) { - b, err := json.Marshal(j.V) - if err != nil { - return nil, err - } - return b, nil -} - -//------ - -type StringArray []string - -func (a *StringArray) Scan(src interface{}) error { - if src == nil { - *a = StringArray{} - return nil - } - b, ok := src.([]byte) - if !ok { - return errors.New("Scan source was not []bytes") - } - if len(b) == 0 { - return nil - } - s := string(b)[1 : len(b)-1] - if s == "" { - return nil - } - results := strings.Split(s, ",") - *a = StringArray(results) - return nil -} - -// Value implements the driver.Valuer interface. -func (a StringArray) Value() (driver.Value, error) { - if a == nil { - return nil, nil - } - - if n := len(a); n > 0 { - // There will be at least two curly brackets, 2*N bytes of quotes, - // and N-1 bytes of delimiters. - b := make([]byte, 1, 1+3*n) - b[0] = '{' - - b = appendArrayQuotedString(b, a[0]) - for i := 1; i < n; i++ { - b = append(b, ',') - b = appendArrayQuotedString(b, a[i]) - } - - return append(b, '}'), nil - } - - return []byte{'{', '}'}, nil -} - -func appendArrayQuotedString(b []byte, v string) []byte { - b = append(b, '"') - for { - i := strings.IndexAny(v, `"\`) - if i < 0 { - b = append(b, v...) - break - } - if i > 0 { - b = append(b, v[:i]...) - } - b = append(b, '\\', v[i]) - v = v[i+1:] - } - return append(b, '"') -} - -//------ - -type Int64Array []int64 - -func (a *Int64Array) Scan(src interface{}) error { - if src == nil { - return nil - } - b, ok := src.([]byte) - if !ok { - return errors.New("Scan source was not []bytes") - } - if len(b) == 0 { - return nil - } - - s := string(b)[1 : len(b)-1] - results := make([]int64, 0) - if s != "" { - parts := strings.Split(s, ",") - for _, n := range parts { - i, err := strconv.ParseInt(n, 10, 64) - if err != nil { - return err - } - results = append(results, i) - } - } - *a = Int64Array(results) - return nil -} - -// Value implements the driver.Valuer interface. -func (a Int64Array) Value() (driver.Value, error) { - if a == nil { - return nil, nil - } - - if n := len(a); n > 0 { - // There will be at least two curly brackets, N bytes of values, - // and N-1 bytes of delimiters. - b := make([]byte, 1, 1+2*n) - b[0] = '{' - - b = strconv.AppendInt(b, a[0], 10) - for i := 1; i < n; i++ { - b = append(b, ',') - b = strconv.AppendInt(b, a[i], 10) - } - - return append(b, '}'), nil - } - - return []byte{'{', '}'}, nil -} diff --git a/util/sqlutil/tx/tx.go b/util/sqlutil/tx/tx.go deleted file mode 100644 index 18ebbf3c..00000000 --- a/util/sqlutil/tx/tx.go +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) 2012-2015 The upper.io/db authors. All rights reserved. -// -// 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 sqltx - -import ( - "github.com/jmoiron/sqlx" -) - -type Tx struct { - *sqlx.Tx - done bool -} - -func New(tx *sqlx.Tx) *Tx { - return &Tx{Tx: tx} -} - -func (t *Tx) Done() bool { - return t.done -} - -func (t *Tx) Commit() (err error) { - if err = t.Tx.Commit(); err == nil { - t.done = true - } - return err -} -- GitLab