From a7babfb7cf8f67ff128c3f8f2f3711a128df2009 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Fri, 31 Mar 2017 12:54:39 +0200 Subject: [PATCH] tech: updated xorm libs --- pkg/tsdb/mysql/mysql.go | 2 +- vendor/github.com/go-xorm/builder/LICENSE | 27 + vendor/github.com/go-xorm/builder/README.md | 175 + vendor/github.com/go-xorm/builder/builder.go | 190 + .../go-xorm/builder/builder_delete.go | 22 + .../go-xorm/builder/builder_insert.go | 64 + .../go-xorm/builder/builder_select.go | 53 + .../go-xorm/builder/builder_update.go | 41 + vendor/github.com/go-xorm/builder/circle.yml | 12 + vendor/github.com/go-xorm/builder/cond.go | 87 + vendor/github.com/go-xorm/builder/cond_and.go | 59 + .../go-xorm/builder/cond_between.go | 40 + .../go-xorm/builder/cond_compare.go | 154 + vendor/github.com/go-xorm/builder/cond_eq.go | 96 + .../github.com/go-xorm/builder/cond_expr.go | 39 + vendor/github.com/go-xorm/builder/cond_in.go | 239 + .../github.com/go-xorm/builder/cond_like.go | 41 + vendor/github.com/go-xorm/builder/cond_neq.go | 78 + vendor/github.com/go-xorm/builder/cond_not.go | 53 + .../github.com/go-xorm/builder/cond_notin.go | 236 + .../github.com/go-xorm/builder/cond_null.go | 59 + vendor/github.com/go-xorm/builder/cond_or.go | 67 + vendor/github.com/go-xorm/builder/doc.go | 120 + vendor/github.com/go-xorm/builder/error.go | 16 + vendor/github.com/go-xorm/core/.gitignore | 1 - vendor/github.com/go-xorm/core/README.md | 2 + vendor/github.com/go-xorm/core/benchmark.sh | 0 vendor/github.com/go-xorm/core/circle.yml | 14 + vendor/github.com/go-xorm/core/column.go | 22 +- vendor/github.com/go-xorm/core/db.go | 310 +- vendor/github.com/go-xorm/core/db_test.go | 659 --- vendor/github.com/go-xorm/core/dialect.go | 30 +- vendor/github.com/go-xorm/core/error.go | 2 - vendor/github.com/go-xorm/core/ilogger.go | 31 +- vendor/github.com/go-xorm/core/mapper_test.go | 45 - vendor/github.com/go-xorm/core/pk_test.go | 33 - vendor/github.com/go-xorm/core/rows.go | 380 ++ vendor/github.com/go-xorm/core/table.go | 37 +- vendor/github.com/go-xorm/core/type.go | 43 +- vendor/github.com/go-xorm/xorm/.gitignore | 28 - vendor/github.com/go-xorm/xorm/.gitmodules | 6 - vendor/github.com/go-xorm/xorm/.gopmfile | 2 - .../github.com/go-xorm/xorm/CONTRIBUTING.md | 4 + vendor/github.com/go-xorm/xorm/README.md | 63 +- vendor/github.com/go-xorm/xorm/README_CN.md | 44 +- vendor/github.com/go-xorm/xorm/VERSION | 2 +- vendor/github.com/go-xorm/xorm/circle.yml | 25 + vendor/github.com/go-xorm/xorm/convert.go | 249 ++ .../{mssql_dialect.go => dialect_mssql.go} | 81 +- .../{mysql_dialect.go => dialect_mysql.go} | 126 +- .../{oracle_dialect.go => dialect_oracle.go} | 123 +- ...ostgres_dialect.go => dialect_postgres.go} | 180 +- ...{sqlite3_dialect.go => dialect_sqlite3.go} | 45 +- vendor/github.com/go-xorm/xorm/doc.go | 63 +- vendor/github.com/go-xorm/xorm/engine.go | 894 ++-- vendor/github.com/go-xorm/xorm/error.go | 21 +- .../github.com/go-xorm/xorm/gen_reserved.sh | 0 .../github.com/go-xorm/xorm/goracle_driver.go | 42 - vendor/github.com/go-xorm/xorm/helpers.go | 212 +- .../github.com/go-xorm/xorm/helpers_test.go | 22 - vendor/github.com/go-xorm/xorm/logger.go | 139 +- vendor/github.com/go-xorm/xorm/lru_cacher.go | 57 +- .../github.com/go-xorm/xorm/memory_store.go | 6 +- .../github.com/go-xorm/xorm/mymysql_driver.go | 69 - .../github.com/go-xorm/xorm/mysql_driver.go | 54 - vendor/github.com/go-xorm/xorm/oci8_driver.go | 41 - vendor/github.com/go-xorm/xorm/odbc_driver.go | 38 - vendor/github.com/go-xorm/xorm/pq_driver.go | 119 - vendor/github.com/go-xorm/xorm/processors.go | 14 +- vendor/github.com/go-xorm/xorm/rows.go | 85 +- vendor/github.com/go-xorm/xorm/session.go | 3828 +---------------- .../github.com/go-xorm/xorm/session_cols.go | 84 + .../github.com/go-xorm/xorm/session_cond.go | 70 + .../go-xorm/xorm/session_convert.go | 670 +++ .../github.com/go-xorm/xorm/session_delete.go | 238 + .../github.com/go-xorm/xorm/session_find.go | 461 ++ vendor/github.com/go-xorm/xorm/session_get.go | 192 + .../github.com/go-xorm/xorm/session_insert.go | 543 +++ .../go-xorm/xorm/session_iterate.go | 42 + vendor/github.com/go-xorm/xorm/session_raw.go | 144 + .../github.com/go-xorm/xorm/session_schema.go | 489 +++ vendor/github.com/go-xorm/xorm/session_sum.go | 137 + vendor/github.com/go-xorm/xorm/session_tx.go | 83 + .../github.com/go-xorm/xorm/session_update.go | 356 ++ .../github.com/go-xorm/xorm/sqlite3_driver.go | 20 - vendor/github.com/go-xorm/xorm/statement.go | 810 ++-- vendor/github.com/go-xorm/xorm/syslogger.go | 64 +- vendor/github.com/go-xorm/xorm/tag.go | 281 ++ vendor/github.com/go-xorm/xorm/types.go | 12 + vendor/github.com/go-xorm/xorm/xorm.go | 20 +- vendor/vendor.json | 22 + 91 files changed, 8656 insertions(+), 6343 deletions(-) create mode 100644 vendor/github.com/go-xorm/builder/LICENSE create mode 100644 vendor/github.com/go-xorm/builder/README.md create mode 100644 vendor/github.com/go-xorm/builder/builder.go create mode 100644 vendor/github.com/go-xorm/builder/builder_delete.go create mode 100644 vendor/github.com/go-xorm/builder/builder_insert.go create mode 100644 vendor/github.com/go-xorm/builder/builder_select.go create mode 100644 vendor/github.com/go-xorm/builder/builder_update.go create mode 100644 vendor/github.com/go-xorm/builder/circle.yml create mode 100644 vendor/github.com/go-xorm/builder/cond.go create mode 100644 vendor/github.com/go-xorm/builder/cond_and.go create mode 100644 vendor/github.com/go-xorm/builder/cond_between.go create mode 100644 vendor/github.com/go-xorm/builder/cond_compare.go create mode 100644 vendor/github.com/go-xorm/builder/cond_eq.go create mode 100644 vendor/github.com/go-xorm/builder/cond_expr.go create mode 100644 vendor/github.com/go-xorm/builder/cond_in.go create mode 100644 vendor/github.com/go-xorm/builder/cond_like.go create mode 100644 vendor/github.com/go-xorm/builder/cond_neq.go create mode 100644 vendor/github.com/go-xorm/builder/cond_not.go create mode 100644 vendor/github.com/go-xorm/builder/cond_notin.go create mode 100644 vendor/github.com/go-xorm/builder/cond_null.go create mode 100644 vendor/github.com/go-xorm/builder/cond_or.go create mode 100644 vendor/github.com/go-xorm/builder/doc.go create mode 100644 vendor/github.com/go-xorm/builder/error.go delete mode 100644 vendor/github.com/go-xorm/core/.gitignore mode change 100644 => 100755 vendor/github.com/go-xorm/core/benchmark.sh create mode 100644 vendor/github.com/go-xorm/core/circle.yml delete mode 100644 vendor/github.com/go-xorm/core/db_test.go delete mode 100644 vendor/github.com/go-xorm/core/mapper_test.go delete mode 100644 vendor/github.com/go-xorm/core/pk_test.go create mode 100644 vendor/github.com/go-xorm/core/rows.go delete mode 100644 vendor/github.com/go-xorm/xorm/.gitignore delete mode 100644 vendor/github.com/go-xorm/xorm/.gitmodules delete mode 100644 vendor/github.com/go-xorm/xorm/.gopmfile create mode 100644 vendor/github.com/go-xorm/xorm/circle.yml create mode 100644 vendor/github.com/go-xorm/xorm/convert.go rename vendor/github.com/go-xorm/xorm/{mssql_dialect.go => dialect_mssql.go} (87%) rename vendor/github.com/go-xorm/xorm/{mysql_dialect.go => dialect_mysql.go} (83%) rename vendor/github.com/go-xorm/xorm/{oracle_dialect.go => dialect_oracle.go} (89%) rename vendor/github.com/go-xorm/xorm/{postgres_dialect.go => dialect_postgres.go} (90%) rename vendor/github.com/go-xorm/xorm/{sqlite3_dialect.go => dialect_sqlite3.go} (94%) mode change 100644 => 100755 vendor/github.com/go-xorm/xorm/gen_reserved.sh delete mode 100644 vendor/github.com/go-xorm/xorm/goracle_driver.go delete mode 100644 vendor/github.com/go-xorm/xorm/helpers_test.go delete mode 100644 vendor/github.com/go-xorm/xorm/mymysql_driver.go delete mode 100644 vendor/github.com/go-xorm/xorm/mysql_driver.go delete mode 100644 vendor/github.com/go-xorm/xorm/oci8_driver.go delete mode 100644 vendor/github.com/go-xorm/xorm/odbc_driver.go delete mode 100644 vendor/github.com/go-xorm/xorm/pq_driver.go create mode 100644 vendor/github.com/go-xorm/xorm/session_cols.go create mode 100644 vendor/github.com/go-xorm/xorm/session_cond.go create mode 100644 vendor/github.com/go-xorm/xorm/session_convert.go create mode 100644 vendor/github.com/go-xorm/xorm/session_delete.go create mode 100644 vendor/github.com/go-xorm/xorm/session_find.go create mode 100644 vendor/github.com/go-xorm/xorm/session_get.go create mode 100644 vendor/github.com/go-xorm/xorm/session_insert.go create mode 100644 vendor/github.com/go-xorm/xorm/session_iterate.go create mode 100644 vendor/github.com/go-xorm/xorm/session_raw.go create mode 100644 vendor/github.com/go-xorm/xorm/session_schema.go create mode 100644 vendor/github.com/go-xorm/xorm/session_sum.go create mode 100644 vendor/github.com/go-xorm/xorm/session_tx.go create mode 100644 vendor/github.com/go-xorm/xorm/session_update.go delete mode 100644 vendor/github.com/go-xorm/xorm/sqlite3_driver.go create mode 100644 vendor/github.com/go-xorm/xorm/tag.go create mode 100644 vendor/github.com/go-xorm/xorm/types.go diff --git a/pkg/tsdb/mysql/mysql.go b/pkg/tsdb/mysql/mysql.go index 3a5aad7c51f..62624515ca5 100644 --- a/pkg/tsdb/mysql/mysql.go +++ b/pkg/tsdb/mysql/mysql.go @@ -65,7 +65,7 @@ func (e *MysqlExecutor) initEngine() error { e.log.Debug("getEngine", "connection", cnnstr) engine, err := xorm.NewEngine("mysql", cnnstr) - engine.SetMaxConns(10) + engine.SetMaxOpenConns(10) engine.SetMaxIdleConns(10) if err != nil { return err diff --git a/vendor/github.com/go-xorm/builder/LICENSE b/vendor/github.com/go-xorm/builder/LICENSE new file mode 100644 index 00000000000..614d5e28298 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016 The Xorm Authors +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/go-xorm/builder/README.md b/vendor/github.com/go-xorm/builder/README.md new file mode 100644 index 00000000000..1628736374d --- /dev/null +++ b/vendor/github.com/go-xorm/builder/README.md @@ -0,0 +1,175 @@ +# SQL builder + +[![CircleCI](https://circleci.com/gh/go-xorm/builder/tree/master.svg?style=svg)](https://circleci.com/gh/go-xorm/builder/tree/master) + +Package builder is a lightweight and fast SQL builder for Go and XORM. + +Make sure you have installed Go 1.1+ and then: + + go get github.com/go-xorm/builder + +# Insert + +```Go +sql, args, err := Insert(Eq{"c": 1, "d": 2}).Into("table1").ToSQL() +``` + +# Select + +```Go +sql, args, err := Select("c, d").From("table1").Where(Eq{"a": 1}).ToSQL() + +sql, args, err = Select("c, d").From("table1").LeftJoin("table2", Eq{"table1.id": 1}.And(Lt{"table2.id": 3})). + RightJoin("table3", "table2.id = table3.tid").Where(Eq{"a": 1}).ToSQL() +``` + +# Update + +```Go +sql, args, err := Update(Eq{"a": 2}).From("table1").Where(Eq{"a": 1}).ToSQL() +``` + +# Delete + +```Go +sql, args, err := Delete(Eq{"a": 1}).From("table1").ToSQL() +``` + +# Conditions + +* `Eq` is a redefine of a map, you can give one or more conditions to `Eq` + +```Go +import . "github.com/go-xorm/builder" + +sql, args, _ := ToSQL(Eq{"a":1}) +// a=? [1] +sql, args, _ := ToSQL(Eq{"b":"c"}.And(Eq{"c": 0})) +// b=? AND c=? ["c", 0] +sql, args, _ := ToSQL(Eq{"b":"c", "c":0}) +// b=? AND c=? ["c", 0] +sql, args, _ := ToSQL(Eq{"b":"c"}.Or(Eq{"b":"d"})) +// b=? OR b=? ["c", "d"] +sql, args, _ := ToSQL(Eq{"b": []string{"c", "d"}}) +// b IN (?,?) ["c", "d"] +sql, args, _ := ToSQL(Eq{"b": 1, "c":[]int{2, 3}}) +// b=? AND c IN (?,?) [1, 2, 3] +``` + +* `Neq` is the same to `Eq` + +```Go +import . "github.com/go-xorm/builder" + +sql, args, _ := ToSQL(Neq{"a":1}) +// a<>? [1] +sql, args, _ := ToSQL(Neq{"b":"c"}.And(Neq{"c": 0})) +// b<>? AND c<>? ["c", 0] +sql, args, _ := ToSQL(Neq{"b":"c", "c":0}) +// b<>? AND c<>? ["c", 0] +sql, args, _ := ToSQL(Neq{"b":"c"}.Or(Neq{"b":"d"})) +// b<>? OR b<>? ["c", "d"] +sql, args, _ := ToSQL(Neq{"b": []string{"c", "d"}}) +// b NOT IN (?,?) ["c", "d"] +sql, args, _ := ToSQL(Neq{"b": 1, "c":[]int{2, 3}}) +// b<>? AND c NOT IN (?,?) [1, 2, 3] +``` + +* `Gt`, `Gte`, `Lt`, `Lte` + +```Go +import . "github.com/go-xorm/builder" + +sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2})) +// a>? AND b>=? [1, 2] +sql, args, _ := ToSQL(Lt{"a", 1}.Or(Lte{"b", 2})) +// a? [1, %c%, 2] +``` + +* `Or(conds ...Cond)`, Or can connect one or more conditions via Or + +```Go +import . "github.com/go-xorm/builder" + +sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2})) +// a=? OR b LIKE ? OR d<>? [1, %c%, 2] +sql, args, _ := ToSQL(Or(Eq{"a":1}, And(Like{"b", "c"}, Neq{"d", 2}))) +// a=? OR (b LIKE ? AND d<>?) [1, %c%, 2] +``` + +* `Between` + +```Go +import . "github.com/go-xorm/builder" + +sql, args, _ := ToSQL(Between{"a", 1, 2}) +// a BETWEEN 1 AND 2 +``` + +* Define yourself conditions + +Since `Cond` is an interface. + +```Go +type Cond interface { + WriteTo(Writer) error + And(...Cond) Cond + Or(...Cond) Cond + IsValid() bool +} +``` + +You can define yourself conditions and compose with other `Cond`. \ No newline at end of file diff --git a/vendor/github.com/go-xorm/builder/builder.go b/vendor/github.com/go-xorm/builder/builder.go new file mode 100644 index 00000000000..1253b9887e1 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder.go @@ -0,0 +1,190 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +type optype byte + +const ( + condType optype = iota // only conditions + selectType // select + insertType // insert + updateType // update + deleteType // delete +) + +type join struct { + joinType string + joinTable string + joinCond Cond +} + +// Builder describes a SQL statement +type Builder struct { + optype + tableName string + cond Cond + selects []string + joins []join + inserts Eq + updates []Eq +} + +// Select creates a select Builder +func Select(cols ...string) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Select(cols...) +} + +// Insert creates an insert Builder +func Insert(eq Eq) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Insert(eq) +} + +// Update creates an update Builder +func Update(updates ...Eq) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Update(updates...) +} + +// Delete creates a delete Builder +func Delete(conds ...Cond) *Builder { + builder := &Builder{cond: NewCond()} + return builder.Delete(conds...) +} + +// Where sets where SQL +func (b *Builder) Where(cond Cond) *Builder { + b.cond = b.cond.And(cond) + return b +} + +// From sets the table name +func (b *Builder) From(tableName string) *Builder { + b.tableName = tableName + return b +} + +// Into sets insert table name +func (b *Builder) Into(tableName string) *Builder { + b.tableName = tableName + return b +} + +// Join sets join table and contions +func (b *Builder) Join(joinType, joinTable string, joinCond interface{}) *Builder { + switch joinCond.(type) { + case Cond: + b.joins = append(b.joins, join{joinType, joinTable, joinCond.(Cond)}) + case string: + b.joins = append(b.joins, join{joinType, joinTable, Expr(joinCond.(string))}) + } + + return b +} + +// InnerJoin sets inner join +func (b *Builder) InnerJoin(joinTable string, joinCond interface{}) *Builder { + return b.Join("INNER", joinTable, joinCond) +} + +// LeftJoin sets left join SQL +func (b *Builder) LeftJoin(joinTable string, joinCond interface{}) *Builder { + return b.Join("LEFT", joinTable, joinCond) +} + +// RightJoin sets right join SQL +func (b *Builder) RightJoin(joinTable string, joinCond interface{}) *Builder { + return b.Join("RIGHT", joinTable, joinCond) +} + +// CrossJoin sets cross join SQL +func (b *Builder) CrossJoin(joinTable string, joinCond interface{}) *Builder { + return b.Join("CROSS", joinTable, joinCond) +} + +// FullJoin sets full join SQL +func (b *Builder) FullJoin(joinTable string, joinCond interface{}) *Builder { + return b.Join("FULL", joinTable, joinCond) +} + +// Select sets select SQL +func (b *Builder) Select(cols ...string) *Builder { + b.selects = cols + b.optype = selectType + return b +} + +// And sets AND condition +func (b *Builder) And(cond Cond) *Builder { + b.cond = And(b.cond, cond) + return b +} + +// Or sets OR condition +func (b *Builder) Or(cond Cond) *Builder { + b.cond = Or(b.cond, cond) + return b +} + +// Insert sets insert SQL +func (b *Builder) Insert(eq Eq) *Builder { + b.inserts = eq + b.optype = insertType + return b +} + +// Update sets update SQL +func (b *Builder) Update(updates ...Eq) *Builder { + b.updates = updates + b.optype = updateType + return b +} + +// Delete sets delete SQL +func (b *Builder) Delete(conds ...Cond) *Builder { + b.cond = b.cond.And(conds...) + b.optype = deleteType + return b +} + +// WriteTo implements Writer interface +func (b *Builder) WriteTo(w Writer) error { + switch b.optype { + case condType: + return b.cond.WriteTo(w) + case selectType: + return b.selectWriteTo(w) + case insertType: + return b.insertWriteTo(w) + case updateType: + return b.updateWriteTo(w) + case deleteType: + return b.deleteWriteTo(w) + } + + return ErrNotSupportType +} + +// ToSQL convert a builder to SQL and args +func (b *Builder) ToSQL() (string, []interface{}, error) { + w := NewWriter() + if err := b.WriteTo(w); err != nil { + return "", nil, err + } + + return w.writer.String(), w.args, nil +} + +// ToSQL convert a builder or condtions to SQL and args +func ToSQL(cond interface{}) (string, []interface{}, error) { + switch cond.(type) { + case Cond: + return condToSQL(cond.(Cond)) + case *Builder: + return cond.(*Builder).ToSQL() + } + return "", nil, ErrNotSupportType +} diff --git a/vendor/github.com/go-xorm/builder/builder_delete.go b/vendor/github.com/go-xorm/builder/builder_delete.go new file mode 100644 index 00000000000..743f1a4a91b --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_delete.go @@ -0,0 +1,22 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "errors" + "fmt" +) + +func (b *Builder) deleteWriteTo(w Writer) error { + if len(b.tableName) <= 0 { + return errors.New("no table indicated") + } + + if _, err := fmt.Fprintf(w, "DELETE FROM %s WHERE ", b.tableName); err != nil { + return err + } + + return b.cond.WriteTo(w) +} diff --git a/vendor/github.com/go-xorm/builder/builder_insert.go b/vendor/github.com/go-xorm/builder/builder_insert.go new file mode 100644 index 00000000000..decec931304 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_insert.go @@ -0,0 +1,64 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "bytes" + "errors" + "fmt" +) + +func (b *Builder) insertWriteTo(w Writer) error { + if len(b.tableName) <= 0 { + return errors.New("no table indicated") + } + if len(b.inserts) <= 0 { + return errors.New("no column to be update") + } + + if _, err := fmt.Fprintf(w, "INSERT INTO %s (", b.tableName); err != nil { + return err + } + + var args = make([]interface{}, 0) + var bs []byte + var valBuffer = bytes.NewBuffer(bs) + var i = 0 + for col, value := range b.inserts { + fmt.Fprint(w, col) + if e, ok := value.(expr); ok { + fmt.Fprint(valBuffer, e.sql) + args = append(args, e.args...) + } else { + fmt.Fprint(valBuffer, "?") + args = append(args, value) + } + + if i != len(b.inserts)-1 { + if _, err := fmt.Fprint(w, ","); err != nil { + return err + } + if _, err := fmt.Fprint(valBuffer, ","); err != nil { + return err + } + } + i = i + 1 + } + + if _, err := fmt.Fprint(w, ") Values ("); err != nil { + return err + } + + if _, err := w.Write(valBuffer.Bytes()); err != nil { + return err + } + if _, err := fmt.Fprint(w, ")"); err != nil { + return err + } + + w.Append(args...) + + return nil +} diff --git a/vendor/github.com/go-xorm/builder/builder_select.go b/vendor/github.com/go-xorm/builder/builder_select.go new file mode 100644 index 00000000000..05f116e0052 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_select.go @@ -0,0 +1,53 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "errors" + "fmt" +) + +func (b *Builder) selectWriteTo(w Writer) error { + if len(b.tableName) <= 0 { + return errors.New("no table indicated") + } + + if _, err := fmt.Fprint(w, "SELECT "); err != nil { + return err + } + if len(b.selects) > 0 { + for i, s := range b.selects { + if _, err := fmt.Fprint(w, s); err != nil { + return err + } + if i != len(b.selects)-1 { + if _, err := fmt.Fprint(w, ","); err != nil { + return err + } + } + } + } else { + if _, err := fmt.Fprint(w, "*"); err != nil { + return err + } + } + + if _, err := fmt.Fprintf(w, " FROM %s", b.tableName); err != nil { + return err + } + + for _, v := range b.joins { + fmt.Fprintf(w, " %s JOIN %s ON ", v.joinType, v.joinTable) + if err := v.joinCond.WriteTo(w); err != nil { + return err + } + } + + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + + return b.cond.WriteTo(w) +} diff --git a/vendor/github.com/go-xorm/builder/builder_update.go b/vendor/github.com/go-xorm/builder/builder_update.go new file mode 100644 index 00000000000..182af830fdc --- /dev/null +++ b/vendor/github.com/go-xorm/builder/builder_update.go @@ -0,0 +1,41 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "errors" + "fmt" +) + +func (b *Builder) updateWriteTo(w Writer) error { + if len(b.tableName) <= 0 { + return errors.New("no table indicated") + } + if len(b.updates) <= 0 { + return errors.New("no column to be update") + } + + if _, err := fmt.Fprintf(w, "UPDATE %s SET ", b.tableName); err != nil { + return err + } + + for i, s := range b.updates { + if err := s.opWriteTo(",", w); err != nil { + return err + } + + if i != len(b.updates)-1 { + if _, err := fmt.Fprint(w, ","); err != nil { + return err + } + } + } + + if _, err := fmt.Fprint(w, " WHERE "); err != nil { + return err + } + + return b.cond.WriteTo(w) +} diff --git a/vendor/github.com/go-xorm/builder/circle.yml b/vendor/github.com/go-xorm/builder/circle.yml new file mode 100644 index 00000000000..b2a8bfc9ef4 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/circle.yml @@ -0,0 +1,12 @@ +dependencies: + override: + # './...' is a relative pattern which means all subdirectories + - go get -t -d -v ./... + - go build -v + - go get -u github.com/golang/lint/golint + +test: + override: + # './...' is a relative pattern which means all subdirectories + - golint ./... + - go test -v -race \ No newline at end of file diff --git a/vendor/github.com/go-xorm/builder/cond.go b/vendor/github.com/go-xorm/builder/cond.go new file mode 100644 index 00000000000..77dd139bfef --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond.go @@ -0,0 +1,87 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "bytes" + "io" +) + +// Writer defines the interface +type Writer interface { + io.Writer + Append(...interface{}) +} + +var _ Writer = NewWriter() + +// BytesWriter implments Writer and save SQL in bytes.Buffer +type BytesWriter struct { + writer *bytes.Buffer + buffer []byte + args []interface{} +} + +// NewWriter creates a new string writer +func NewWriter() *BytesWriter { + w := &BytesWriter{} + w.writer = bytes.NewBuffer(w.buffer) + return w +} + +// Write writes data to Writer +func (s *BytesWriter) Write(buf []byte) (int, error) { + return s.writer.Write(buf) +} + +// Append appends args to Writer +func (s *BytesWriter) Append(args ...interface{}) { + s.args = append(s.args, args...) +} + +// Cond defines an interface +type Cond interface { + WriteTo(Writer) error + And(...Cond) Cond + Or(...Cond) Cond + IsValid() bool +} + +type condEmpty struct{} + +var _ Cond = condEmpty{} + +// NewCond creates an empty condition +func NewCond() Cond { + return condEmpty{} +} + +func (condEmpty) WriteTo(w Writer) error { + return nil +} + +func (condEmpty) And(conds ...Cond) Cond { + return And(conds...) +} + +func (condEmpty) Or(conds ...Cond) Cond { + return Or(conds...) +} + +func (condEmpty) IsValid() bool { + return false +} + +func condToSQL(cond Cond) (string, []interface{}, error) { + if cond == nil || !cond.IsValid() { + return "", nil, nil + } + + w := NewWriter() + if err := cond.WriteTo(w); err != nil { + return "", nil, err + } + return w.writer.String(), w.args, nil +} diff --git a/vendor/github.com/go-xorm/builder/cond_and.go b/vendor/github.com/go-xorm/builder/cond_and.go new file mode 100644 index 00000000000..9c30e9c2e51 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_and.go @@ -0,0 +1,59 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +type condAnd []Cond + +var _ Cond = condAnd{} + +// And generates AND conditions +func And(conds ...Cond) Cond { + var result = make(condAnd, 0, len(conds)) + for _, cond := range conds { + if cond == nil || !cond.IsValid() { + continue + } + result = append(result, cond) + } + return result +} + +func (and condAnd) WriteTo(w Writer) error { + for i, cond := range and { + _, isOr := cond.(condOr) + if isOr { + fmt.Fprint(w, "(") + } + + err := cond.WriteTo(w) + if err != nil { + return err + } + + if isOr { + fmt.Fprint(w, ")") + } + + if i != len(and)-1 { + fmt.Fprint(w, " AND ") + } + } + + return nil +} + +func (and condAnd) And(conds ...Cond) Cond { + return And(and, And(conds...)) +} + +func (and condAnd) Or(conds ...Cond) Cond { + return Or(and, Or(conds...)) +} + +func (and condAnd) IsValid() bool { + return len(and) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_between.go b/vendor/github.com/go-xorm/builder/cond_between.go new file mode 100644 index 00000000000..f2b29ed15bb --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_between.go @@ -0,0 +1,40 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// Between implmentes between condition +type Between struct { + Col string + LessVal interface{} + MoreVal interface{} +} + +var _ Cond = Between{} + +// WriteTo write data to Writer +func (between Between) WriteTo(w Writer) error { + if _, err := fmt.Fprintf(w, "%s BETWEEN ? AND ?", between.Col); err != nil { + return err + } + w.Append(between.LessVal, between.MoreVal) + return nil +} + +// And implments And with other conditions +func (between Between) And(conds ...Cond) Cond { + return And(between, And(conds...)) +} + +// Or implments Or with other conditions +func (between Between) Or(conds ...Cond) Cond { + return Or(between, Or(conds...)) +} + +// IsValid tests if the condition is valid +func (between Between) IsValid() bool { + return len(between.Col) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_compare.go b/vendor/github.com/go-xorm/builder/cond_compare.go new file mode 100644 index 00000000000..e10ef7447c9 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_compare.go @@ -0,0 +1,154 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// WriteMap writes conditions' SQL to Writer, op could be =, <>, >, <, <=, >= and etc. +func WriteMap(w Writer, data map[string]interface{}, op string) error { + var args = make([]interface{}, 0, len(data)) + var i = 0 + for k, v := range data { + switch v.(type) { + case expr: + if _, err := fmt.Fprintf(w, "%s%s(", k, op); err != nil { + return err + } + + if err := v.(expr).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case *Builder: + if _, err := fmt.Fprintf(w, "%s%s(", k, op); err != nil { + return err + } + + if err := v.(*Builder).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + default: + if _, err := fmt.Fprintf(w, "%s%s?", k, op); err != nil { + return err + } + args = append(args, v) + } + if i != len(data)-1 { + if _, err := fmt.Fprint(w, " AND "); err != nil { + return err + } + } + i = i + 1 + } + w.Append(args...) + return nil +} + +// Lt defines < condition +type Lt map[string]interface{} + +var _ Cond = Lt{} + +// WriteTo write SQL to Writer +func (lt Lt) WriteTo(w Writer) error { + return WriteMap(w, lt, "<") +} + +// And implements And with other conditions +func (lt Lt) And(conds ...Cond) Cond { + return condAnd{lt, And(conds...)} +} + +// Or implements Or with other conditions +func (lt Lt) Or(conds ...Cond) Cond { + return condOr{lt, Or(conds...)} +} + +// IsValid tests if this Eq is valid +func (lt Lt) IsValid() bool { + return len(lt) > 0 +} + +// Lte defines <= condition +type Lte map[string]interface{} + +var _ Cond = Lte{} + +// WriteTo write SQL to Writer +func (lte Lte) WriteTo(w Writer) error { + return WriteMap(w, lte, "<=") +} + +// And implements And with other conditions +func (lte Lte) And(conds ...Cond) Cond { + return And(lte, And(conds...)) +} + +// Or implements Or with other conditions +func (lte Lte) Or(conds ...Cond) Cond { + return Or(lte, Or(conds...)) +} + +// IsValid tests if this Eq is valid +func (lte Lte) IsValid() bool { + return len(lte) > 0 +} + +// Gt defines > condition +type Gt map[string]interface{} + +var _ Cond = Gt{} + +// WriteTo write SQL to Writer +func (gt Gt) WriteTo(w Writer) error { + return WriteMap(w, gt, ">") +} + +// And implements And with other conditions +func (gt Gt) And(conds ...Cond) Cond { + return And(gt, And(conds...)) +} + +// Or implements Or with other conditions +func (gt Gt) Or(conds ...Cond) Cond { + return Or(gt, Or(conds...)) +} + +// IsValid tests if this Eq is valid +func (gt Gt) IsValid() bool { + return len(gt) > 0 +} + +// Gte defines >= condition +type Gte map[string]interface{} + +var _ Cond = Gte{} + +// WriteTo write SQL to Writer +func (gte Gte) WriteTo(w Writer) error { + return WriteMap(w, gte, ">=") +} + +// And implements And with other conditions +func (gte Gte) And(conds ...Cond) Cond { + return And(gte, And(conds...)) +} + +// Or implements Or with other conditions +func (gte Gte) Or(conds ...Cond) Cond { + return Or(gte, Or(conds...)) +} + +// IsValid tests if this Eq is valid +func (gte Gte) IsValid() bool { + return len(gte) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_eq.go b/vendor/github.com/go-xorm/builder/cond_eq.go new file mode 100644 index 00000000000..8777727ffc6 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_eq.go @@ -0,0 +1,96 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// Incr implements a type used by Eq +type Incr int + +// Decr implements a type used by Eq +type Decr int + +// Eq defines equals conditions +type Eq map[string]interface{} + +var _ Cond = Eq{} + +func (eq Eq) opWriteTo(op string, w Writer) error { + var i = 0 + for k, v := range eq { + switch v.(type) { + case []int, []int64, []string, []int32, []int16, []int8, []uint, []uint64, []uint32, []uint16, []interface{}: + if err := In(k, v).WriteTo(w); err != nil { + return err + } + case expr: + if _, err := fmt.Fprintf(w, "%s=(", k); err != nil { + return err + } + + if err := v.(expr).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case *Builder: + if _, err := fmt.Fprintf(w, "%s=(", k); err != nil { + return err + } + + if err := v.(*Builder).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case Incr: + if _, err := fmt.Fprintf(w, "%s=%s+?", k, k); err != nil { + return err + } + w.Append(int(v.(Incr))) + case Decr: + if _, err := fmt.Fprintf(w, "%s=%s-?", k, k); err != nil { + return err + } + w.Append(int(v.(Decr))) + default: + if _, err := fmt.Fprintf(w, "%s=?", k); err != nil { + return err + } + w.Append(v) + } + if i != len(eq)-1 { + if _, err := fmt.Fprint(w, op); err != nil { + return err + } + } + i = i + 1 + } + return nil +} + +// WriteTo writes SQL to Writer +func (eq Eq) WriteTo(w Writer) error { + return eq.opWriteTo(" AND ", w) +} + +// And implements And with other conditions +func (eq Eq) And(conds ...Cond) Cond { + return And(eq, And(conds...)) +} + +// Or implements Or with other conditions +func (eq Eq) Or(conds ...Cond) Cond { + return Or(eq, Or(conds...)) +} + +// IsValid tests if this Eq is valid +func (eq Eq) IsValid() bool { + return len(eq) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_expr.go b/vendor/github.com/go-xorm/builder/cond_expr.go new file mode 100644 index 00000000000..e5ed572b150 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_expr.go @@ -0,0 +1,39 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +type expr struct { + sql string + args []interface{} +} + +var _ Cond = expr{} + +// Expr generate customerize SQL +func Expr(sql string, args ...interface{}) Cond { + return expr{sql, args} +} + +func (expr expr) WriteTo(w Writer) error { + if _, err := fmt.Fprint(w, expr.sql); err != nil { + return err + } + w.Append(expr.args...) + return nil +} + +func (expr expr) And(conds ...Cond) Cond { + return And(expr, And(conds...)) +} + +func (expr expr) Or(conds ...Cond) Cond { + return Or(expr, Or(conds...)) +} + +func (expr expr) IsValid() bool { + return len(expr.sql) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_in.go b/vendor/github.com/go-xorm/builder/cond_in.go new file mode 100644 index 00000000000..71093e4b491 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_in.go @@ -0,0 +1,239 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "reflect" + "strings" +) + +type condIn struct { + col string + vals []interface{} +} + +var _ Cond = condIn{} + +// In generates IN condition +func In(col string, values ...interface{}) Cond { + return condIn{col, values} +} + +func (condIn condIn) handleBlank(w Writer) error { + if _, err := fmt.Fprintf(w, "%s IN ()", condIn.col); err != nil { + return err + } + return nil +} + +func (condIn condIn) WriteTo(w Writer) error { + if len(condIn.vals) <= 0 { + return condIn.handleBlank(w) + } + + switch condIn.vals[0].(type) { + case []int8: + vals := condIn.vals[0].([]int8) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int16: + vals := condIn.vals[0].([]int16) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int: + vals := condIn.vals[0].([]int) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int32: + vals := condIn.vals[0].([]int32) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int64: + vals := condIn.vals[0].([]int64) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint8: + vals := condIn.vals[0].([]uint8) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint16: + vals := condIn.vals[0].([]uint16) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint: + vals := condIn.vals[0].([]uint) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint32: + vals := condIn.vals[0].([]uint32) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint64: + vals := condIn.vals[0].([]uint64) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []string: + vals := condIn.vals[0].([]string) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []interface{}: + vals := condIn.vals[0].([]interface{}) + if len(vals) <= 0 { + return condIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + w.Append(vals...) + case expr: + val := condIn.vals[0].(expr) + if _, err := fmt.Fprintf(w, "%s IN (", condIn.col); err != nil { + return err + } + if err := val.WriteTo(w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case *Builder: + bd := condIn.vals[0].(*Builder) + if _, err := fmt.Fprintf(w, "%s IN (", condIn.col); err != nil { + return err + } + if err := bd.WriteTo(w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + default: + v := reflect.ValueOf(condIn.vals[0]) + if v.Kind() == reflect.Slice { + l := v.Len() + if l == 0 { + return condIn.handleBlank(w) + } + + questionMark := strings.Repeat("?,", l) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + + for i := 0; i < l; i++ { + w.Append(v.Index(i).Interface()) + } + } else { + questionMark := strings.Repeat("?,", len(condIn.vals)) + if _, err := fmt.Fprintf(w, "%s IN (%s)", condIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + w.Append(condIn.vals...) + } + } + return nil +} + +func (condIn condIn) And(conds ...Cond) Cond { + return And(condIn, And(conds...)) +} + +func (condIn condIn) Or(conds ...Cond) Cond { + return Or(condIn, Or(conds...)) +} + +func (condIn condIn) IsValid() bool { + return len(condIn.col) > 0 && len(condIn.vals) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_like.go b/vendor/github.com/go-xorm/builder/cond_like.go new file mode 100644 index 00000000000..9291f12c9bf --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_like.go @@ -0,0 +1,41 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// Like defines like condition +type Like [2]string + +var _ Cond = Like{"", ""} + +// WriteTo write SQL to Writer +func (like Like) WriteTo(w Writer) error { + if _, err := fmt.Fprintf(w, "%s LIKE ?", like[0]); err != nil { + return err + } + // FIXME: if use other regular express, this will be failed. but for compitable, keep this + if like[1][0] == '%' || like[1][len(like[1])-1] == '%' { + w.Append(like[1]) + } else { + w.Append("%" + like[1] + "%") + } + return nil +} + +// And implements And with other conditions +func (like Like) And(conds ...Cond) Cond { + return And(like, And(conds...)) +} + +// Or implements Or with other conditions +func (like Like) Or(conds ...Cond) Cond { + return Or(like, Or(conds...)) +} + +// IsValid tests if this condition is valid +func (like Like) IsValid() bool { + return len(like[0]) > 0 && len(like[1]) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_neq.go b/vendor/github.com/go-xorm/builder/cond_neq.go new file mode 100644 index 00000000000..d07b2b18e80 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_neq.go @@ -0,0 +1,78 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// Neq defines not equal conditions +type Neq map[string]interface{} + +var _ Cond = Neq{} + +// WriteTo writes SQL to Writer +func (neq Neq) WriteTo(w Writer) error { + var args = make([]interface{}, 0, len(neq)) + var i = 0 + for k, v := range neq { + switch v.(type) { + case []int, []int64, []string, []int32, []int16, []int8: + if err := NotIn(k, v).WriteTo(w); err != nil { + return err + } + case expr: + if _, err := fmt.Fprintf(w, "%s<>(", k); err != nil { + return err + } + + if err := v.(expr).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case *Builder: + if _, err := fmt.Fprintf(w, "%s<>(", k); err != nil { + return err + } + + if err := v.(*Builder).WriteTo(w); err != nil { + return err + } + + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + default: + if _, err := fmt.Fprintf(w, "%s<>?", k); err != nil { + return err + } + args = append(args, v) + } + if i != len(neq)-1 { + if _, err := fmt.Fprint(w, " AND "); err != nil { + return err + } + } + i = i + 1 + } + w.Append(args...) + return nil +} + +// And implements And with other conditions +func (neq Neq) And(conds ...Cond) Cond { + return And(neq, And(conds...)) +} + +// Or implements Or with other conditions +func (neq Neq) Or(conds ...Cond) Cond { + return Or(neq, Or(conds...)) +} + +// IsValid tests if this condition is valid +func (neq Neq) IsValid() bool { + return len(neq) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_not.go b/vendor/github.com/go-xorm/builder/cond_not.go new file mode 100644 index 00000000000..294a7e07280 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_not.go @@ -0,0 +1,53 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// Not defines NOT condition +type Not [1]Cond + +var _ Cond = Not{} + +// WriteTo writes SQL to Writer +func (not Not) WriteTo(w Writer) error { + if _, err := fmt.Fprint(w, "NOT "); err != nil { + return err + } + switch not[0].(type) { + case condAnd, condOr: + if _, err := fmt.Fprint(w, "("); err != nil { + return err + } + } + + if err := not[0].WriteTo(w); err != nil { + return err + } + + switch not[0].(type) { + case condAnd, condOr: + if _, err := fmt.Fprint(w, ")"); err != nil { + return err + } + } + + return nil +} + +// And implements And with other conditions +func (not Not) And(conds ...Cond) Cond { + return And(not, And(conds...)) +} + +// Or implements Or with other conditions +func (not Not) Or(conds ...Cond) Cond { + return Or(not, Or(conds...)) +} + +// IsValid tests if this condition is valid +func (not Not) IsValid() bool { + return not[0] != nil && not[0].IsValid() +} diff --git a/vendor/github.com/go-xorm/builder/cond_notin.go b/vendor/github.com/go-xorm/builder/cond_notin.go new file mode 100644 index 00000000000..9be44cb2afe --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_notin.go @@ -0,0 +1,236 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import ( + "fmt" + "reflect" + "strings" +) + +type condNotIn condIn + +var _ Cond = condNotIn{} + +// NotIn generate NOT IN condition +func NotIn(col string, values ...interface{}) Cond { + return condNotIn{col, values} +} + +func (condNotIn condNotIn) handleBlank(w Writer) error { + if _, err := fmt.Fprintf(w, "%s NOT IN ()", condNotIn.col); err != nil { + return err + } + return nil +} + +func (condNotIn condNotIn) WriteTo(w Writer) error { + if len(condNotIn.vals) <= 0 { + return condNotIn.handleBlank(w) + } + + switch condNotIn.vals[0].(type) { + case []int8: + vals := condNotIn.vals[0].([]int8) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int16: + vals := condNotIn.vals[0].([]int16) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int: + vals := condNotIn.vals[0].([]int) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int32: + vals := condNotIn.vals[0].([]int32) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []int64: + vals := condNotIn.vals[0].([]int64) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint8: + vals := condNotIn.vals[0].([]uint8) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint16: + vals := condNotIn.vals[0].([]uint16) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint: + vals := condNotIn.vals[0].([]uint) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint32: + vals := condNotIn.vals[0].([]uint32) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []uint64: + vals := condNotIn.vals[0].([]uint64) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []string: + vals := condNotIn.vals[0].([]string) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + for _, val := range vals { + w.Append(val) + } + case []interface{}: + vals := condNotIn.vals[0].([]interface{}) + if len(vals) <= 0 { + return condNotIn.handleBlank(w) + } + questionMark := strings.Repeat("?,", len(vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + w.Append(vals...) + case expr: + val := condNotIn.vals[0].(expr) + if _, err := fmt.Fprintf(w, "%s NOT IN (", condNotIn.col); err != nil { + return err + } + if err := val.WriteTo(w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + case *Builder: + val := condNotIn.vals[0].(*Builder) + if _, err := fmt.Fprintf(w, "%s NOT IN (", condNotIn.col); err != nil { + return err + } + if err := val.WriteTo(w); err != nil { + return err + } + if _, err := fmt.Fprintf(w, ")"); err != nil { + return err + } + default: + v := reflect.ValueOf(condNotIn.vals[0]) + if v.Kind() == reflect.Slice { + l := v.Len() + if l == 0 { + return condNotIn.handleBlank(w) + } + + questionMark := strings.Repeat("?,", l) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + + for i := 0; i < l; i++ { + w.Append(v.Index(i).Interface()) + } + } else { + questionMark := strings.Repeat("?,", len(condNotIn.vals)) + if _, err := fmt.Fprintf(w, "%s NOT IN (%s)", condNotIn.col, questionMark[:len(questionMark)-1]); err != nil { + return err + } + w.Append(condNotIn.vals...) + } + } + return nil +} + +func (condNotIn condNotIn) And(conds ...Cond) Cond { + return And(condNotIn, And(conds...)) +} + +func (condNotIn condNotIn) Or(conds ...Cond) Cond { + return Or(condNotIn, Or(conds...)) +} + +func (condNotIn condNotIn) IsValid() bool { + return len(condNotIn.col) > 0 && len(condNotIn.vals) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_null.go b/vendor/github.com/go-xorm/builder/cond_null.go new file mode 100644 index 00000000000..bf2aaf8518a --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_null.go @@ -0,0 +1,59 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +// IsNull defines IS NULL condition +type IsNull [1]string + +var _ Cond = IsNull{""} + +// WriteTo write SQL to Writer +func (isNull IsNull) WriteTo(w Writer) error { + _, err := fmt.Fprintf(w, "%s IS NULL", isNull[0]) + return err +} + +// And implements And with other conditions +func (isNull IsNull) And(conds ...Cond) Cond { + return And(isNull, And(conds...)) +} + +// Or implements Or with other conditions +func (isNull IsNull) Or(conds ...Cond) Cond { + return Or(isNull, Or(conds...)) +} + +// IsValid tests if this condition is valid +func (isNull IsNull) IsValid() bool { + return len(isNull[0]) > 0 +} + +// NotNull defines NOT NULL condition +type NotNull [1]string + +var _ Cond = NotNull{""} + +// WriteTo write SQL to Writer +func (notNull NotNull) WriteTo(w Writer) error { + _, err := fmt.Fprintf(w, "%s IS NOT NULL", notNull[0]) + return err +} + +// And implements And with other conditions +func (notNull NotNull) And(conds ...Cond) Cond { + return And(notNull, And(conds...)) +} + +// Or implements Or with other conditions +func (notNull NotNull) Or(conds ...Cond) Cond { + return Or(notNull, Or(conds...)) +} + +// IsValid tests if this condition is valid +func (notNull NotNull) IsValid() bool { + return len(notNull[0]) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/cond_or.go b/vendor/github.com/go-xorm/builder/cond_or.go new file mode 100644 index 00000000000..35c3da0251e --- /dev/null +++ b/vendor/github.com/go-xorm/builder/cond_or.go @@ -0,0 +1,67 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "fmt" + +type condOr []Cond + +var _ Cond = condOr{} + +// Or sets OR conditions +func Or(conds ...Cond) Cond { + var result = make(condOr, 0, len(conds)) + for _, cond := range conds { + if cond == nil || !cond.IsValid() { + continue + } + result = append(result, cond) + } + return result +} + +// WriteTo implments Cond +func (o condOr) WriteTo(w Writer) error { + for i, cond := range o { + var needQuote bool + switch cond.(type) { + case condAnd: + needQuote = true + case Eq: + needQuote = (len(cond.(Eq)) > 1) + } + + if needQuote { + fmt.Fprint(w, "(") + } + + err := cond.WriteTo(w) + if err != nil { + return err + } + + if needQuote { + fmt.Fprint(w, ")") + } + + if i != len(o)-1 { + fmt.Fprint(w, " OR ") + } + } + + return nil +} + +func (o condOr) And(conds ...Cond) Cond { + return And(o, And(conds...)) +} + +func (o condOr) Or(conds ...Cond) Cond { + return Or(o, Or(conds...)) +} + +func (o condOr) IsValid() bool { + return len(o) > 0 +} diff --git a/vendor/github.com/go-xorm/builder/doc.go b/vendor/github.com/go-xorm/builder/doc.go new file mode 100644 index 00000000000..162b150f101 --- /dev/null +++ b/vendor/github.com/go-xorm/builder/doc.go @@ -0,0 +1,120 @@ +// Copyright 2016 The XORM Authors. All rights reserved. +// Use of this source code is governed by a BSD +// license that can be found in the LICENSE file. + +/* + +Package builder is a simple and powerful sql builder for Go. + +Make sure you have installed Go 1.1+ and then: + + go get github.com/go-xorm/builder + +WARNNING: Currently, only query conditions are supported. Below is the supported conditions. + +1. Eq is a redefine of a map, you can give one or more conditions to Eq + + import . "github.com/go-xorm/builder" + + sql, args, _ := ToSQL(Eq{"a":1}) + // a=? [1] + sql, args, _ := ToSQL(Eq{"b":"c"}.And(Eq{"c": 0})) + // b=? AND c=? ["c", 0] + sql, args, _ := ToSQL(Eq{"b":"c", "c":0}) + // b=? AND c=? ["c", 0] + sql, args, _ := ToSQL(Eq{"b":"c"}.Or(Eq{"b":"d"})) + // b=? OR b=? ["c", "d"] + sql, args, _ := ToSQL(Eq{"b": []string{"c", "d"}}) + // b IN (?,?) ["c", "d"] + sql, args, _ := ToSQL(Eq{"b": 1, "c":[]int{2, 3}}) + // b=? AND c IN (?,?) [1, 2, 3] + +2. Neq is the same to Eq + + import . "github.com/go-xorm/builder" + + sql, args, _ := ToSQL(Neq{"a":1}) + // a<>? [1] + sql, args, _ := ToSQL(Neq{"b":"c"}.And(Neq{"c": 0})) + // b<>? AND c<>? ["c", 0] + sql, args, _ := ToSQL(Neq{"b":"c", "c":0}) + // b<>? AND c<>? ["c", 0] + sql, args, _ := ToSQL(Neq{"b":"c"}.Or(Neq{"b":"d"})) + // b<>? OR b<>? ["c", "d"] + sql, args, _ := ToSQL(Neq{"b": []string{"c", "d"}}) + // b NOT IN (?,?) ["c", "d"] + sql, args, _ := ToSQL(Neq{"b": 1, "c":[]int{2, 3}}) + // b<>? AND c NOT IN (?,?) [1, 2, 3] + +3. Gt, Gte, Lt, Lte + + import . "github.com/go-xorm/builder" + + sql, args, _ := ToSQL(Gt{"a", 1}.And(Gte{"b", 2})) + // a>? AND b>=? [1, 2] + sql, args, _ := ToSQL(Lt{"a", 1}.Or(Lte{"b", 2})) + // a? [1, %c%, 2] + +9. Or(conds ...Cond), Or can connect one or more conditions via Or + + import . "github.com/go-xorm/builder" + + sql, args, _ := ToSQL(Or(Eq{"a":1}, Like{"b", "c"}, Neq{"d", 2})) + // a=? OR b LIKE ? OR d<>? [1, %c%, 2] + sql, args, _ := ToSQL(Or(Eq{"a":1}, And(Like{"b", "c"}, Neq{"d", 2}))) + // a=? OR (b LIKE ? AND d<>?) [1, %c%, 2] + +10. Between + + import . "github.com/go-xorm/builder" + + sql, args, _ := ToSQL(Between("a", 1, 2)) + // a BETWEEN 1 AND 2 + +11. define yourself conditions +Since Cond is a interface, you can define yourself conditions and compare with them +*/ +package builder diff --git a/vendor/github.com/go-xorm/builder/error.go b/vendor/github.com/go-xorm/builder/error.go new file mode 100644 index 00000000000..d7ac51ea1fb --- /dev/null +++ b/vendor/github.com/go-xorm/builder/error.go @@ -0,0 +1,16 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package builder + +import "errors" + +var ( + // ErrNotSupportType not supported SQL type error + ErrNotSupportType = errors.New("not supported SQL type") + // ErrNoNotInConditions no NOT IN params error + ErrNoNotInConditions = errors.New("No NOT IN conditions") + // ErrNoInConditions no IN params error + ErrNoInConditions = errors.New("No IN conditions") +) diff --git a/vendor/github.com/go-xorm/core/.gitignore b/vendor/github.com/go-xorm/core/.gitignore deleted file mode 100644 index 98e6ef67fad..00000000000 --- a/vendor/github.com/go-xorm/core/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.db diff --git a/vendor/github.com/go-xorm/core/README.md b/vendor/github.com/go-xorm/core/README.md index 0ae94a584ab..09b72c74b39 100644 --- a/vendor/github.com/go-xorm/core/README.md +++ b/vendor/github.com/go-xorm/core/README.md @@ -1,5 +1,7 @@ Core is a lightweight wrapper of sql.DB. +[![CircleCI](https://circleci.com/gh/go-xorm/core/tree/master.svg?style=svg)](https://circleci.com/gh/go-xorm/core/tree/master) + # Open ```Go db, _ := core.Open(db, connstr) diff --git a/vendor/github.com/go-xorm/core/benchmark.sh b/vendor/github.com/go-xorm/core/benchmark.sh old mode 100644 new mode 100755 diff --git a/vendor/github.com/go-xorm/core/circle.yml b/vendor/github.com/go-xorm/core/circle.yml new file mode 100644 index 00000000000..006e1230bcc --- /dev/null +++ b/vendor/github.com/go-xorm/core/circle.yml @@ -0,0 +1,14 @@ +dependencies: + override: + # './...' is a relative pattern which means all subdirectories + - go get -t -d -v ./... + - go build -v + +database: + override: + - mysql -u root -e "CREATE DATABASE core_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" + +test: + override: + # './...' is a relative pattern which means all subdirectories + - go test -v -race \ No newline at end of file diff --git a/vendor/github.com/go-xorm/core/column.go b/vendor/github.com/go-xorm/core/column.go index 0e0def08c2f..c59d01021fa 100644 --- a/vendor/github.com/go-xorm/core/column.go +++ b/vendor/github.com/go-xorm/core/column.go @@ -16,13 +16,14 @@ const ( // database column type Column struct { Name string + TableName string FieldName string SQLType SQLType Length int Length2 int Nullable bool Default string - Indexes map[string]bool + Indexes map[string]int IsPrimaryKey bool IsAutoIncrement bool MapType int @@ -31,7 +32,6 @@ type Column struct { IsDeleted bool IsCascade bool IsVersion bool - fieldPath []string DefaultIsEmpty bool EnumOptions map[string]int SetOptions map[string]int @@ -42,13 +42,14 @@ type Column struct { func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { return &Column{ Name: name, + TableName: "", FieldName: fieldName, SQLType: sqlType, Length: len1, Length2: len2, Nullable: nullable, Default: "", - Indexes: make(map[string]bool), + Indexes: make(map[string]int), IsPrimaryKey: false, IsAutoIncrement: false, MapType: TWOSIDES, @@ -57,7 +58,6 @@ func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable IsDeleted: false, IsCascade: false, IsVersion: false, - fieldPath: nil, DefaultIsEmpty: false, EnumOptions: make(map[string]int), } @@ -119,12 +119,10 @@ func (col *Column) ValueOf(bean interface{}) (*reflect.Value, error) { func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { var fieldValue reflect.Value - if col.fieldPath == nil { - col.fieldPath = strings.Split(col.FieldName, ".") - } + fieldPath := strings.Split(col.FieldName, ".") if dataStruct.Type().Kind() == reflect.Map { - keyValue := reflect.ValueOf(col.fieldPath[len(col.fieldPath)-1]) + keyValue := reflect.ValueOf(fieldPath[len(fieldPath)-1]) fieldValue = dataStruct.MapIndex(keyValue) return &fieldValue, nil } else if dataStruct.Type().Kind() == reflect.Interface { @@ -132,19 +130,19 @@ func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { dataStruct = &structValue } - level := len(col.fieldPath) - fieldValue = dataStruct.FieldByName(col.fieldPath[0]) + level := len(fieldPath) + fieldValue = dataStruct.FieldByName(fieldPath[0]) for i := 0; i < level-1; i++ { if !fieldValue.IsValid() { break } if fieldValue.Kind() == reflect.Struct { - fieldValue = fieldValue.FieldByName(col.fieldPath[i+1]) + fieldValue = fieldValue.FieldByName(fieldPath[i+1]) } else if fieldValue.Kind() == reflect.Ptr { if fieldValue.IsNil() { fieldValue.Set(reflect.New(fieldValue.Type().Elem())) } - fieldValue = fieldValue.Elem().FieldByName(col.fieldPath[i+1]) + fieldValue = fieldValue.Elem().FieldByName(fieldPath[i+1]) } else { return nil, fmt.Errorf("field %v is not valid", col.FieldName) } diff --git a/vendor/github.com/go-xorm/core/db.go b/vendor/github.com/go-xorm/core/db.go index 169d855315e..6111c4b332f 100644 --- a/vendor/github.com/go-xorm/core/db.go +++ b/vendor/github.com/go-xorm/core/db.go @@ -4,9 +4,9 @@ import ( "database/sql" "database/sql/driver" "errors" + "fmt" "reflect" "regexp" - "sync" ) func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { @@ -15,12 +15,19 @@ func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { return "", []interface{}{}, ErrNoMapPointer } - args := make([]interface{}, 0) + args := make([]interface{}, 0, len(vv.Elem().MapKeys())) + var err error query = re.ReplaceAllStringFunc(query, func(src string) string { - args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) + v := vv.Elem().MapIndex(reflect.ValueOf(src[1:])) + if !v.IsValid() { + err = fmt.Errorf("map key %s is missing", src[1:]) + } else { + args = append(args, v.Interface()) + } return "?" }) - return query, args, nil + + return query, args, err } func StructToSlice(query string, st interface{}) (string, []interface{}, error) { @@ -95,82 +102,12 @@ func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { return db.Query(query, args...) } -type Row struct { - rows *Rows - // One of these two will be non-nil: - err error // deferred error for easy chaining -} - -func (row *Row) Columns() ([]string, error) { - if row.err != nil { - return nil, row.err - } - return row.rows.Columns() -} - -func (row *Row) Scan(dest ...interface{}) error { - if row.err != nil { - return row.err - } - defer row.rows.Close() - - for _, dp := range dest { - if _, ok := dp.(*sql.RawBytes); ok { - return errors.New("sql: RawBytes isn't allowed on Row.Scan") - } - } - - if !row.rows.Next() { - if err := row.rows.Err(); err != nil { - return err - } - return sql.ErrNoRows - } - err := row.rows.Scan(dest...) - if err != nil { - return err - } - // Make sure the query can be processed to completion with no errors. - if err := row.rows.Close(); err != nil { - return err - } - - return nil -} - -func (row *Row) ScanStructByName(dest interface{}) error { - if row.err != nil { - return row.err - } - return row.rows.ScanStructByName(dest) -} - -func (row *Row) ScanStructByIndex(dest interface{}) error { - if row.err != nil { - return row.err - } - return row.rows.ScanStructByIndex(dest) -} - -// scan data to a slice's pointer, slice's length should equal to columns' number -func (row *Row) ScanSlice(dest interface{}) error { - if row.err != nil { - return row.err - } - return row.rows.ScanSlice(dest) -} - -// scan data to a map's pointer -func (row *Row) ScanMap(dest interface{}) error { - if row.err != nil { - return row.err - } - return row.rows.ScanMap(dest) -} - func (db *DB) QueryRow(query string, args ...interface{}) *Row { rows, err := db.Query(query, args...) - return &Row{rows, err} + if err != nil { + return &Row{nil, err} + } + return &Row{rows, nil} } func (db *DB) QueryRowMap(query string, mp interface{}) *Row { @@ -328,44 +265,6 @@ func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { return db.DB.Exec(query, args...) } -type Rows struct { - *sql.Rows - Mapper IMapper -} - -// scan data to a struct's pointer according field index -func (rs *Rows) ScanStructByIndex(dest ...interface{}) error { - if len(dest) == 0 { - return errors.New("at least one struct") - } - - vvvs := make([]reflect.Value, len(dest)) - for i, s := range dest { - vv := reflect.ValueOf(s) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return errors.New("dest should be a struct's pointer") - } - - vvvs[i] = vv.Elem() - } - - cols, err := rs.Columns() - if err != nil { - return err - } - newDest := make([]interface{}, len(cols)) - - var i = 0 - for _, vvv := range vvvs { - for j := 0; j < vvv.NumField(); j++ { - newDest[i] = vvv.Field(j).Addr().Interface() - i = i + 1 - } - } - - return rs.Rows.Scan(newDest...) -} - type EmptyScanner struct { } @@ -373,185 +272,6 @@ func (EmptyScanner) Scan(src interface{}) error { return nil } -var ( - fieldCache = make(map[reflect.Type]map[string]int) - fieldCacheMutex sync.RWMutex -) - -func fieldByName(v reflect.Value, name string) reflect.Value { - t := v.Type() - fieldCacheMutex.RLock() - cache, ok := fieldCache[t] - fieldCacheMutex.RUnlock() - if !ok { - cache = make(map[string]int) - for i := 0; i < v.NumField(); i++ { - cache[t.Field(i).Name] = i - } - fieldCacheMutex.Lock() - fieldCache[t] = cache - fieldCacheMutex.Unlock() - } - - if i, ok := cache[name]; ok { - return v.Field(i) - } - - return reflect.Zero(t) -} - -// scan data to a struct's pointer according field name -func (rs *Rows) ScanStructByName(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return errors.New("dest should be a struct's pointer") - } - - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - var v EmptyScanner - for j, name := range cols { - f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name)) - if f.IsValid() { - newDest[j] = f.Addr().Interface() - } else { - newDest[j] = &v - } - } - - return rs.Rows.Scan(newDest...) -} - -type cacheStruct struct { - value reflect.Value - idx int -} - -var ( - reflectCache = make(map[reflect.Type]*cacheStruct) - reflectCacheMutex sync.RWMutex -) - -func ReflectNew(typ reflect.Type) reflect.Value { - reflectCacheMutex.RLock() - cs, ok := reflectCache[typ] - reflectCacheMutex.RUnlock() - - const newSize = 200 - - if !ok || cs.idx+1 > newSize-1 { - cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0} - reflectCacheMutex.Lock() - reflectCache[typ] = cs - reflectCacheMutex.Unlock() - } else { - reflectCacheMutex.Lock() - cs.idx = cs.idx + 1 - reflectCacheMutex.Unlock() - } - return cs.value.Index(cs.idx).Addr() -} - -// scan data to a slice's pointer, slice's length should equal to columns' number -func (rs *Rows) ScanSlice(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Slice { - return errors.New("dest should be a slice's pointer") - } - - vvv := vv.Elem() - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - - for j := 0; j < len(cols); j++ { - if j >= vvv.Len() { - newDest[j] = reflect.New(vvv.Type().Elem()).Interface() - } else { - newDest[j] = vvv.Index(j).Addr().Interface() - } - } - - err = rs.Rows.Scan(newDest...) - if err != nil { - return err - } - - srcLen := vvv.Len() - for i := srcLen; i < len(cols); i++ { - vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem()) - } - return nil -} - -// scan data to a map's pointer -func (rs *Rows) ScanMap(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return errors.New("dest should be a map's pointer") - } - - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - vvv := vv.Elem() - - for i, _ := range cols { - newDest[i] = ReflectNew(vvv.Type().Elem()).Interface() - //v := reflect.New(vvv.Type().Elem()) - //newDest[i] = v.Interface() - } - - err = rs.Rows.Scan(newDest...) - if err != nil { - return err - } - - for i, name := range cols { - vname := reflect.ValueOf(name) - vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) - } - - return nil -} - -/*func (rs *Rows) ScanMap(dest interface{}) error { - vv := reflect.ValueOf(dest) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return errors.New("dest should be a map's pointer") - } - - cols, err := rs.Columns() - if err != nil { - return err - } - - newDest := make([]interface{}, len(cols)) - err = rs.ScanSlice(newDest) - if err != nil { - return err - } - - vvv := vv.Elem() - - for i, name := range cols { - vname := reflect.ValueOf(name) - vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) - } - - return nil -}*/ - type Tx struct { *sql.Tx Mapper IMapper diff --git a/vendor/github.com/go-xorm/core/db_test.go b/vendor/github.com/go-xorm/core/db_test.go deleted file mode 100644 index 94c4ea4f9f6..00000000000 --- a/vendor/github.com/go-xorm/core/db_test.go +++ /dev/null @@ -1,659 +0,0 @@ -package core - -import ( - "errors" - "fmt" - "os" - "testing" - "time" - - _ "github.com/go-sql-driver/mysql" - _ "github.com/mattn/go-sqlite3" -) - -var ( - //dbtype string = "sqlite3" - dbtype string = "mysql" - createTableSql string -) - -type User struct { - Id int64 - Name string - Title string - Age float32 - Alias string - NickName string - Created NullTime -} - -func init() { - switch dbtype { - case "sqlite3": - createTableSql = "CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, " + - "`title` TEXT NULL, `age` FLOAT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL, `created` datetime);" - case "mysql": - createTableSql = "CREATE TABLE IF NOT EXISTS `user` (`id` INTEGER PRIMARY KEY AUTO_INCREMENT NOT NULL, `name` TEXT NULL, " + - "`title` TEXT NULL, `age` FLOAT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL, `created` datetime);" - default: - panic("no db type") - } -} - -func testOpen() (*DB, error) { - switch dbtype { - case "sqlite3": - os.Remove("./test.db") - return Open("sqlite3", "./test.db") - case "mysql": - return Open("mysql", "root:@/core_test?charset=utf8") - default: - panic("no db type") - } -} - -func BenchmarkOriQuery(b *testing.B) { - b.StopTimer() - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (`name`, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - var Id int64 - var Name, Title, Alias, NickName string - var Age float32 - var Created NullTime - err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created) - if err != nil { - b.Error(err) - } - //fmt.Println(Id, Name, Title, Age, Alias, NickName) - } - rows.Close() - } -} - -func BenchmarkStructQuery(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (`name`, title, age, alias, nick_name, created) values (?,?,?,?,?, ?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - var user User - err = rows.ScanStructByIndex(&user) - if err != nil { - b.Error(err) - } - if user.Name != "xlw" { - fmt.Println(user) - b.Error(errors.New("name should be xlw")) - } - } - rows.Close() - } -} - -func BenchmarkStruct2Query(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (`name`, title, age, alias, nick_name, created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - db.Mapper = NewCacheMapper(&SnakeMapper{}) - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - var user User - err = rows.ScanStructByName(&user) - if err != nil { - b.Error(err) - } - if user.Name != "xlw" { - fmt.Println(user) - b.Error(errors.New("name should be xlw")) - } - } - rows.Close() - } -} - -func BenchmarkSliceInterfaceQuery(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (`name`, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - cols, err := rows.Columns() - if err != nil { - b.Error(err) - } - - for rows.Next() { - slice := make([]interface{}, len(cols)) - err = rows.ScanSlice(&slice) - if err != nil { - b.Error(err) - } - fmt.Println(slice) - if *slice[1].(*string) != "xlw" { - fmt.Println(slice) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -} - -/*func BenchmarkSliceBytesQuery(b *testing.B) { - b.StopTimer() - os.Remove("./test.db") - db, err := Open("sqlite3", "./test.db") - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - cols, err := rows.Columns() - if err != nil { - b.Error(err) - } - - for rows.Next() { - slice := make([][]byte, len(cols)) - err = rows.ScanSlice(&slice) - if err != nil { - b.Error(err) - } - if string(slice[1]) != "xlw" { - fmt.Println(slice) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -} -*/ - -func BenchmarkSliceStringQuery(b *testing.B) { - b.StopTimer() - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name, created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - cols, err := rows.Columns() - if err != nil { - b.Error(err) - } - - for rows.Next() { - slice := make([]*string, len(cols)) - err = rows.ScanSlice(&slice) - if err != nil { - b.Error(err) - } - if (*slice[1]) != "xlw" { - fmt.Println(slice) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -} - -func BenchmarkMapInterfaceQuery(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - m := make(map[string]interface{}) - err = rows.ScanMap(&m) - if err != nil { - b.Error(err) - } - if m["name"].(string) != "xlw" { - fmt.Println(m) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -} - -/*func BenchmarkMapBytesQuery(b *testing.B) { - b.StopTimer() - os.Remove("./test.db") - db, err := Open("sqlite3", "./test.db") - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - m := make(map[string][]byte) - err = rows.ScanMap(&m) - if err != nil { - b.Error(err) - } - if string(m["name"]) != "xlw" { - fmt.Println(m) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -} -*/ -/* -func BenchmarkMapStringQuery(b *testing.B) { - b.StopTimer() - os.Remove("./test.db") - db, err := Open("sqlite3", "./test.db") - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - rows, err := db.Query("select * from user") - if err != nil { - b.Error(err) - } - - for rows.Next() { - m := make(map[string]string) - err = rows.ScanMap(&m) - if err != nil { - b.Error(err) - } - if m["name"] != "xlw" { - fmt.Println(m) - b.Error(errors.New("name should be xlw")) - } - } - - rows.Close() - } -}*/ - -func BenchmarkExec(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - b.StartTimer() - - for i := 0; i < b.N; i++ { - _, err = db.Exec("insert into user (`name`, title, age, alias, nick_name,created) values (?,?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao", time.Now()) - if err != nil { - b.Error(err) - } - } -} - -func BenchmarkExecMap(b *testing.B) { - b.StopTimer() - - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - b.StartTimer() - - mp := map[string]interface{}{ - "name": "xlw", - "title": "tester", - "age": 1.2, - "alias": "lunny", - "nick_name": "lunny xiao", - "created": time.Now(), - } - - for i := 0; i < b.N; i++ { - _, err = db.ExecMap("insert into user (`name`, title, age, alias, nick_name, created) "+ - "values (?name,?title,?age,?alias,?nick_name,?created)", - &mp) - if err != nil { - b.Error(err) - } - } -} - -func TestExecMap(t *testing.T) { - db, err := testOpen() - if err != nil { - t.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - t.Error(err) - } - - mp := map[string]interface{}{ - "name": "xlw", - "title": "tester", - "age": 1.2, - "alias": "lunny", - "nick_name": "lunny xiao", - "created": time.Now(), - } - - _, err = db.ExecMap("insert into user (`name`, title, age, alias, nick_name,created) "+ - "values (?name,?title,?age,?alias,?nick_name,?created)", - &mp) - if err != nil { - t.Error(err) - } - - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } - - for rows.Next() { - var user User - err = rows.ScanStructByName(&user) - if err != nil { - t.Error(err) - } - fmt.Println("--", user) - } -} - -func TestExecStruct(t *testing.T) { - db, err := testOpen() - if err != nil { - t.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - t.Error(err) - } - - user := User{Name: "xlw", - Title: "tester", - Age: 1.2, - Alias: "lunny", - NickName: "lunny xiao", - Created: NullTime(time.Now()), - } - - _, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) "+ - "values (?Name,?Title,?Age,?Alias,?NickName,?Created)", - &user) - if err != nil { - t.Error(err) - } - - rows, err := db.QueryStruct("select * from user where `name` = ?Name", &user) - if err != nil { - t.Error(err) - } - - for rows.Next() { - var user User - err = rows.ScanStructByName(&user) - if err != nil { - t.Error(err) - } - fmt.Println("1--", user) - } -} - -func BenchmarkExecStruct(b *testing.B) { - b.StopTimer() - db, err := testOpen() - if err != nil { - b.Error(err) - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - b.Error(err) - } - - b.StartTimer() - - user := User{Name: "xlw", - Title: "tester", - Age: 1.2, - Alias: "lunny", - NickName: "lunny xiao", - Created: NullTime(time.Now()), - } - - for i := 0; i < b.N; i++ { - _, err = db.ExecStruct("insert into user (`name`, title, age, alias, nick_name,created) "+ - "values (?Name,?Title,?Age,?Alias,?NickName,?Created)", - &user) - if err != nil { - b.Error(err) - } - } -} diff --git a/vendor/github.com/go-xorm/core/dialect.go b/vendor/github.com/go-xorm/core/dialect.go index 45bc5c20883..74478301e45 100644 --- a/vendor/github.com/go-xorm/core/dialect.go +++ b/vendor/github.com/go-xorm/core/dialect.go @@ -20,6 +20,7 @@ type Uri struct { Laddr string Raddr string Timeout time.Duration + Schema string } // a dialect is a driver's wrapper @@ -84,7 +85,7 @@ type Base struct { dialect Dialect driverName string dataSourceName string - Logger ILogger + logger ILogger *Uri } @@ -93,7 +94,7 @@ func (b *Base) DB() *DB { } func (b *Base) SetLogger(logger ILogger) { - b.Logger = logger + b.logger = logger } func (b *Base) Init(db *DB, dialect Dialect, uri *Uri, drivername, dataSourceName string) error { @@ -151,10 +152,8 @@ func (db *Base) DropTableSql(tableName string) string { } func (db *Base) HasRecords(query string, args ...interface{}) (bool, error) { + db.LogSQL(query, args) rows, err := db.DB().Query(query, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", query, args) - } if err != nil { return false, err } @@ -277,17 +276,32 @@ func (b *Base) ForUpdateSql(query string) string { return query + " FOR UPDATE" } +func (b *Base) LogSQL(sql string, args []interface{}) { + if b.logger != nil && b.logger.IsShowSQL() { + if len(args) > 0 { + b.logger.Infof("[SQL] %v %v", sql, args) + } else { + b.logger.Infof("[SQL] %v", sql) + } + } +} + var ( - dialects = map[DbType]func() Dialect{} + dialects = map[string]func() Dialect{} ) +// RegisterDialect register database dialect func RegisterDialect(dbName DbType, dialectFunc func() Dialect) { if dialectFunc == nil { panic("core: Register dialect is nil") } - dialects[dbName] = dialectFunc // !nashtsai! allow override dialect + dialects[strings.ToLower(string(dbName))] = dialectFunc // !nashtsai! allow override dialect } +// QueryDialect query if registed database dialect func QueryDialect(dbName DbType) Dialect { - return dialects[dbName]() + if d, ok := dialects[strings.ToLower(string(dbName))]; ok { + return d() + } + return nil } diff --git a/vendor/github.com/go-xorm/core/error.go b/vendor/github.com/go-xorm/core/error.go index 13c179251d9..640e6036e66 100644 --- a/vendor/github.com/go-xorm/core/error.go +++ b/vendor/github.com/go-xorm/core/error.go @@ -5,6 +5,4 @@ import "errors" var ( ErrNoMapPointer = errors.New("mp should be a map's pointer") ErrNoStructPointer = errors.New("mp should be a struct's pointer") - //ErrNotExist = errors.New("Not exist") - //ErrIgnore = errors.New("Ignore") ) diff --git a/vendor/github.com/go-xorm/core/ilogger.go b/vendor/github.com/go-xorm/core/ilogger.go index e7f08b372cb..c8d78496054 100644 --- a/vendor/github.com/go-xorm/core/ilogger.go +++ b/vendor/github.com/go-xorm/core/ilogger.go @@ -4,25 +4,28 @@ type LogLevel int const ( // !nashtsai! following level also match syslog.Priority value - LOG_UNKNOWN LogLevel = iota - 2 - LOG_OFF LogLevel = iota - 1 - LOG_ERR LogLevel = iota + 3 + LOG_DEBUG LogLevel = iota + LOG_INFO LOG_WARNING - LOG_INFO LogLevel = iota + 6 - LOG_DEBUG + LOG_ERR + LOG_OFF + LOG_UNKNOWN ) // logger interface type ILogger interface { - Debug(v ...interface{}) (err error) - Debugf(format string, v ...interface{}) (err error) - Err(v ...interface{}) (err error) - Errf(format string, v ...interface{}) (err error) - Info(v ...interface{}) (err error) - Infof(format string, v ...interface{}) (err error) - Warning(v ...interface{}) (err error) - Warningf(format string, v ...interface{}) (err error) + Debug(v ...interface{}) + Debugf(format string, v ...interface{}) + Error(v ...interface{}) + Errorf(format string, v ...interface{}) + Info(v ...interface{}) + Infof(format string, v ...interface{}) + Warn(v ...interface{}) + Warnf(format string, v ...interface{}) Level() LogLevel - SetLevel(l LogLevel) (err error) + SetLevel(l LogLevel) + + ShowSQL(show ...bool) + IsShowSQL() bool } diff --git a/vendor/github.com/go-xorm/core/mapper_test.go b/vendor/github.com/go-xorm/core/mapper_test.go deleted file mode 100644 index 043087a2af1..00000000000 --- a/vendor/github.com/go-xorm/core/mapper_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package core - -import ( - "testing" -) - -func TestGonicMapperFromObj(t *testing.T) { - testCases := map[string]string{ - "HTTPLib": "http_lib", - "id": "id", - "ID": "id", - "IDa": "i_da", - "iDa": "i_da", - "IDAa": "id_aa", - "aID": "a_id", - "aaID": "aa_id", - "aaaID": "aaa_id", - "MyREalFunkYLONgNAME": "my_r_eal_funk_ylo_ng_name", - } - - for in, expected := range testCases { - out := gonicCasedName(in) - if out != expected { - t.Errorf("Given %s, expected %s but got %s", in, expected, out) - } - } -} - -func TestGonicMapperToObj(t *testing.T) { - testCases := map[string]string{ - "http_lib": "HTTPLib", - "id": "ID", - "ida": "Ida", - "id_aa": "IDAa", - "aa_id": "AaID", - "my_r_eal_funk_ylo_ng_name": "MyREalFunkYloNgName", - } - - for in, expected := range testCases { - out := LintGonicMapper.Table2Obj(in) - if out != expected { - t.Errorf("Given %s, expected %s but got %s", in, expected, out) - } - } -} diff --git a/vendor/github.com/go-xorm/core/pk_test.go b/vendor/github.com/go-xorm/core/pk_test.go deleted file mode 100644 index 05486086e6a..00000000000 --- a/vendor/github.com/go-xorm/core/pk_test.go +++ /dev/null @@ -1,33 +0,0 @@ -package core - -import ( - "fmt" - "reflect" - "testing" -) - -func TestPK(t *testing.T) { - p := NewPK(1, 3, "string") - str, err := p.ToString() - if err != nil { - t.Error(err) - } - fmt.Println(str) - - s := &PK{} - err = s.FromString(str) - if err != nil { - t.Error(err) - } - fmt.Println(s) - - if len(*p) != len(*s) { - t.Fatal("p", *p, "should be equal", *s) - } - - for i, ori := range *p { - if ori != (*s)[i] { - t.Fatal("ori", ori, reflect.ValueOf(ori), "should be equal", (*s)[i], reflect.ValueOf((*s)[i])) - } - } -} diff --git a/vendor/github.com/go-xorm/core/rows.go b/vendor/github.com/go-xorm/core/rows.go new file mode 100644 index 00000000000..c4dec23e0de --- /dev/null +++ b/vendor/github.com/go-xorm/core/rows.go @@ -0,0 +1,380 @@ +package core + +import ( + "database/sql" + "errors" + "reflect" + "sync" +) + +type Rows struct { + *sql.Rows + Mapper IMapper +} + +func (rs *Rows) ToMapString() ([]map[string]string, error) { + cols, err := rs.Columns() + if err != nil { + return nil, err + } + + var results = make([]map[string]string, 0, 10) + for rs.Next() { + var record = make(map[string]string, len(cols)) + err = rs.ScanMap(&record) + if err != nil { + return nil, err + } + results = append(results, record) + } + return results, nil +} + +// scan data to a struct's pointer according field index +func (rs *Rows) ScanStructByIndex(dest ...interface{}) error { + if len(dest) == 0 { + return errors.New("at least one struct") + } + + vvvs := make([]reflect.Value, len(dest)) + for i, s := range dest { + vv := reflect.ValueOf(s) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return errors.New("dest should be a struct's pointer") + } + + vvvs[i] = vv.Elem() + } + + cols, err := rs.Columns() + if err != nil { + return err + } + newDest := make([]interface{}, len(cols)) + + var i = 0 + for _, vvv := range vvvs { + for j := 0; j < vvv.NumField(); j++ { + newDest[i] = vvv.Field(j).Addr().Interface() + i = i + 1 + } + } + + return rs.Rows.Scan(newDest...) +} + +var ( + fieldCache = make(map[reflect.Type]map[string]int) + fieldCacheMutex sync.RWMutex +) + +func fieldByName(v reflect.Value, name string) reflect.Value { + t := v.Type() + fieldCacheMutex.RLock() + cache, ok := fieldCache[t] + fieldCacheMutex.RUnlock() + if !ok { + cache = make(map[string]int) + for i := 0; i < v.NumField(); i++ { + cache[t.Field(i).Name] = i + } + fieldCacheMutex.Lock() + fieldCache[t] = cache + fieldCacheMutex.Unlock() + } + + if i, ok := cache[name]; ok { + return v.Field(i) + } + + return reflect.Zero(t) +} + +// scan data to a struct's pointer according field name +func (rs *Rows) ScanStructByName(dest interface{}) error { + vv := reflect.ValueOf(dest) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return errors.New("dest should be a struct's pointer") + } + + cols, err := rs.Columns() + if err != nil { + return err + } + + newDest := make([]interface{}, len(cols)) + var v EmptyScanner + for j, name := range cols { + f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name)) + if f.IsValid() { + newDest[j] = f.Addr().Interface() + } else { + newDest[j] = &v + } + } + + return rs.Rows.Scan(newDest...) +} + +type cacheStruct struct { + value reflect.Value + idx int +} + +var ( + reflectCache = make(map[reflect.Type]*cacheStruct) + reflectCacheMutex sync.RWMutex +) + +func ReflectNew(typ reflect.Type) reflect.Value { + reflectCacheMutex.RLock() + cs, ok := reflectCache[typ] + reflectCacheMutex.RUnlock() + + const newSize = 200 + + if !ok || cs.idx+1 > newSize-1 { + cs = &cacheStruct{reflect.MakeSlice(reflect.SliceOf(typ), newSize, newSize), 0} + reflectCacheMutex.Lock() + reflectCache[typ] = cs + reflectCacheMutex.Unlock() + } else { + reflectCacheMutex.Lock() + cs.idx = cs.idx + 1 + reflectCacheMutex.Unlock() + } + return cs.value.Index(cs.idx).Addr() +} + +// scan data to a slice's pointer, slice's length should equal to columns' number +func (rs *Rows) ScanSlice(dest interface{}) error { + vv := reflect.ValueOf(dest) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Slice { + return errors.New("dest should be a slice's pointer") + } + + vvv := vv.Elem() + cols, err := rs.Columns() + if err != nil { + return err + } + + newDest := make([]interface{}, len(cols)) + + for j := 0; j < len(cols); j++ { + if j >= vvv.Len() { + newDest[j] = reflect.New(vvv.Type().Elem()).Interface() + } else { + newDest[j] = vvv.Index(j).Addr().Interface() + } + } + + err = rs.Rows.Scan(newDest...) + if err != nil { + return err + } + + srcLen := vvv.Len() + for i := srcLen; i < len(cols); i++ { + vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem()) + } + return nil +} + +// scan data to a map's pointer +func (rs *Rows) ScanMap(dest interface{}) error { + vv := reflect.ValueOf(dest) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return errors.New("dest should be a map's pointer") + } + + cols, err := rs.Columns() + if err != nil { + return err + } + + newDest := make([]interface{}, len(cols)) + vvv := vv.Elem() + + for i, _ := range cols { + newDest[i] = ReflectNew(vvv.Type().Elem()).Interface() + //v := reflect.New(vvv.Type().Elem()) + //newDest[i] = v.Interface() + } + + err = rs.Rows.Scan(newDest...) + if err != nil { + return err + } + + for i, name := range cols { + vname := reflect.ValueOf(name) + vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) + } + + return nil +} + +/*func (rs *Rows) ScanMap(dest interface{}) error { + vv := reflect.ValueOf(dest) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return errors.New("dest should be a map's pointer") + } + + cols, err := rs.Columns() + if err != nil { + return err + } + + newDest := make([]interface{}, len(cols)) + err = rs.ScanSlice(newDest) + if err != nil { + return err + } + + vvv := vv.Elem() + + for i, name := range cols { + vname := reflect.ValueOf(name) + vvv.SetMapIndex(vname, reflect.ValueOf(newDest[i]).Elem()) + } + + return nil +}*/ +type Row struct { + rows *Rows + // One of these two will be non-nil: + err error // deferred error for easy chaining +} + +func (row *Row) Columns() ([]string, error) { + if row.err != nil { + return nil, row.err + } + return row.rows.Columns() +} + +func (row *Row) Scan(dest ...interface{}) error { + if row.err != nil { + return row.err + } + defer row.rows.Close() + + for _, dp := range dest { + if _, ok := dp.(*sql.RawBytes); ok { + return errors.New("sql: RawBytes isn't allowed on Row.Scan") + } + } + + if !row.rows.Next() { + if err := row.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := row.rows.Scan(dest...) + if err != nil { + return err + } + // Make sure the query can be processed to completion with no errors. + return row.rows.Close() +} + +func (row *Row) ScanStructByName(dest interface{}) error { + if row.err != nil { + return row.err + } + defer row.rows.Close() + + if !row.rows.Next() { + if err := row.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := row.rows.ScanStructByName(dest) + if err != nil { + return err + } + // Make sure the query can be processed to completion with no errors. + return row.rows.Close() +} + +func (row *Row) ScanStructByIndex(dest interface{}) error { + if row.err != nil { + return row.err + } + defer row.rows.Close() + + if !row.rows.Next() { + if err := row.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := row.rows.ScanStructByIndex(dest) + if err != nil { + return err + } + // Make sure the query can be processed to completion with no errors. + return row.rows.Close() +} + +// scan data to a slice's pointer, slice's length should equal to columns' number +func (row *Row) ScanSlice(dest interface{}) error { + if row.err != nil { + return row.err + } + defer row.rows.Close() + + if !row.rows.Next() { + if err := row.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := row.rows.ScanSlice(dest) + if err != nil { + return err + } + + // Make sure the query can be processed to completion with no errors. + return row.rows.Close() +} + +// scan data to a map's pointer +func (row *Row) ScanMap(dest interface{}) error { + if row.err != nil { + return row.err + } + defer row.rows.Close() + + if !row.rows.Next() { + if err := row.rows.Err(); err != nil { + return err + } + return sql.ErrNoRows + } + err := row.rows.ScanMap(dest) + if err != nil { + return err + } + + // Make sure the query can be processed to completion with no errors. + return row.rows.Close() +} + +func (row *Row) ToMapString() (map[string]string, error) { + cols, err := row.Columns() + if err != nil { + return nil, err + } + + var record = make(map[string]string, len(cols)) + err = row.ScanMap(&record) + if err != nil { + return nil, err + } + + return record, nil +} diff --git a/vendor/github.com/go-xorm/core/table.go b/vendor/github.com/go-xorm/core/table.go index aba1f96e748..e6f6a7518b5 100644 --- a/vendor/github.com/go-xorm/core/table.go +++ b/vendor/github.com/go-xorm/core/table.go @@ -47,19 +47,40 @@ func NewTable(name string, t reflect.Type) *Table { } } -func (table *Table) GetColumn(name string) *Column { - if c, ok := table.columnsMap[strings.ToLower(name)]; ok { - return c[0] +func (table *Table) columnsByName(name string) []*Column { + + n := len(name) + + for k := range table.columnsMap { + if len(k) != n { + continue + } + if strings.EqualFold(k, name) { + return table.columnsMap[k] + } } return nil } -func (table *Table) GetColumnIdx(name string, idx int) *Column { - if c, ok := table.columnsMap[strings.ToLower(name)]; ok { - if idx < len(c) { - return c[idx] - } +func (table *Table) GetColumn(name string) *Column { + + cols := table.columnsByName(name) + + if cols != nil { + return cols[0] } + + return nil +} + +func (table *Table) GetColumnIdx(name string, idx int) *Column { + + cols := table.columnsByName(name) + + if cols != nil && idx < len(cols) { + return cols[idx] + } + return nil } diff --git a/vendor/github.com/go-xorm/core/type.go b/vendor/github.com/go-xorm/core/type.go index c7c4d7bd08f..86048225176 100644 --- a/vendor/github.com/go-xorm/core/type.go +++ b/vendor/github.com/go-xorm/core/type.go @@ -54,7 +54,7 @@ func (s *SQLType) IsNumeric() bool { } func (s *SQLType) IsJson() bool { - return s.Name == Json + return s.Name == Json || s.Name == Jsonb } var ( @@ -105,7 +105,8 @@ var ( Serial = "SERIAL" BigSerial = "BIGSERIAL" - Json = "JSON" + Json = "JSON" + Jsonb = "JSONB" SqlTypes = map[string]int{ Bit: NUMERIC_TYPE, @@ -116,9 +117,10 @@ var ( Integer: NUMERIC_TYPE, BigInt: NUMERIC_TYPE, - Enum: TEXT_TYPE, - Set: TEXT_TYPE, - Json: TEXT_TYPE, + Enum: TEXT_TYPE, + Set: TEXT_TYPE, + Json: TEXT_TYPE, + Jsonb: TEXT_TYPE, Char: TEXT_TYPE, Varchar: TEXT_TYPE, @@ -205,6 +207,7 @@ var ( StringType = reflect.TypeOf(c_EMPTY_STRING) BoolType = reflect.TypeOf(c_BOOL_DEFAULT) ByteType = reflect.TypeOf(c_BYTE_DEFAULT) + BytesType = reflect.SliceOf(ByteType) TimeType = reflect.TypeOf(c_TIME_DEFAULT) ) @@ -235,6 +238,7 @@ var ( PtrTimeType = reflect.PtrTo(TimeType) ) +// Type2SQLType generate SQLType acorrding Go's type func Type2SQLType(t reflect.Type) (st SQLType) { switch k := t.Kind(); k { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32: @@ -265,40 +269,13 @@ func Type2SQLType(t reflect.Type) (st SQLType) { st = SQLType{Text, 0, 0} } case reflect.Ptr: - st, _ = ptrType2SQLType(t) + st = Type2SQLType(t.Elem()) default: st = SQLType{Text, 0, 0} } return } -func ptrType2SQLType(t reflect.Type) (st SQLType, has bool) { - has = true - - switch t { - case reflect.TypeOf(&c_EMPTY_STRING): - st = SQLType{Varchar, 255, 0} - return - case reflect.TypeOf(&c_BOOL_DEFAULT): - st = SQLType{Bool, 0, 0} - case reflect.TypeOf(&c_COMPLEX64_DEFAULT), reflect.TypeOf(&c_COMPLEX128_DEFAULT): - st = SQLType{Varchar, 64, 0} - case reflect.TypeOf(&c_FLOAT32_DEFAULT): - st = SQLType{Float, 0, 0} - case reflect.TypeOf(&c_FLOAT64_DEFAULT): - st = SQLType{Double, 0, 0} - case reflect.TypeOf(&c_INT64_DEFAULT), reflect.TypeOf(&c_UINT64_DEFAULT): - st = SQLType{BigInt, 0, 0} - case reflect.TypeOf(&c_TIME_DEFAULT): - st = SQLType{DateTime, 0, 0} - case reflect.TypeOf(&c_INT_DEFAULT), reflect.TypeOf(&c_INT32_DEFAULT), reflect.TypeOf(&c_INT8_DEFAULT), reflect.TypeOf(&c_INT16_DEFAULT), reflect.TypeOf(&c_UINT_DEFAULT), reflect.TypeOf(&c_UINT32_DEFAULT), reflect.TypeOf(&c_UINT8_DEFAULT), reflect.TypeOf(&c_UINT16_DEFAULT): - st = SQLType{Int, 0, 0} - default: - has = false - } - return -} - // default sql type change to go types func SQLType2Type(st SQLType) reflect.Type { name := strings.ToUpper(st.Name) diff --git a/vendor/github.com/go-xorm/xorm/.gitignore b/vendor/github.com/go-xorm/xorm/.gitignore deleted file mode 100644 index cd5e8f73a59..00000000000 --- a/vendor/github.com/go-xorm/xorm/.gitignore +++ /dev/null @@ -1,28 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so -*.db - -# Folders -_obj -_test - -# Architecture specific extensions/prefixes -*.[568vq] -[568vq].out - -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -vendor - -*.log -.vendor -temp_test.go diff --git a/vendor/github.com/go-xorm/xorm/.gitmodules b/vendor/github.com/go-xorm/xorm/.gitmodules deleted file mode 100644 index 75d2c8eaf73..00000000000 --- a/vendor/github.com/go-xorm/xorm/.gitmodules +++ /dev/null @@ -1,6 +0,0 @@ -[submodule "docs/manual-en-US"] - path = docs/manual-en-US - url = https://github.com/go-xorm/manual-en-US.git -[submodule "docs/manual-zh-CN"] - path = docs/manual-zh-CN - url = https://github.com/go-xorm/manual-zh-CN.git diff --git a/vendor/github.com/go-xorm/xorm/.gopmfile b/vendor/github.com/go-xorm/xorm/.gopmfile deleted file mode 100644 index ecd6e20e16c..00000000000 --- a/vendor/github.com/go-xorm/xorm/.gopmfile +++ /dev/null @@ -1,2 +0,0 @@ -[target] -path = github.com/go-xorm/xorm \ No newline at end of file diff --git a/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md b/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md index 90f630ea8a3..e0f6cfcdf23 100644 --- a/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md +++ b/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md @@ -9,6 +9,10 @@ conventions when submitting patches. * [fork a repo](https://help.github.com/articles/fork-a-repo) * [creating a pull request ](https://help.github.com/articles/creating-a-pull-request) +### Language + +Since `xorm` is a world-wide open source project, please describe your issues or code changes in English as soon as possible. + ### Sign your codes with comments ``` // !! your comments diff --git a/vendor/github.com/go-xorm/xorm/README.md b/vendor/github.com/go-xorm/xorm/README.md index 4b4685ea72b..bdcbab68cfa 100644 --- a/vendor/github.com/go-xorm/xorm/README.md +++ b/vendor/github.com/go-xorm/xorm/README.md @@ -2,9 +2,11 @@ Xorm is a simple and powerful ORM for Go. -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![CircleCI](https://circleci.com/gh/go-xorm/xorm/tree/master.svg?style=svg)](https://circleci.com/gh/go-xorm/xorm/tree/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +# Notice + +The last master version is not backwards compatible. You should use `engine.ShowSQL()` and `engine.Logger().SetLevel()` instead of `engine.ShowSQL = `, `engine.ShowInfo = ` and so on. # Features @@ -26,6 +28,7 @@ Xorm is a simple and powerful ORM for Go. * Optimistic Locking support +* SQL Builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder) # Drivers Support @@ -43,14 +46,26 @@ Drivers for Go's sql package which currently support database/sql includes: * MsSql: [github.com/denisenkom/go-mssqldb](https://github.com/denisenkom/go-mssqldb) -* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) - * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (experiment) -* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (experiment) - # Changelog +* **v0.6.0** + * remove support for ql + * add query condition builder support via [github.com/go-xorm/builder](https://github.com/go-xorm/builder), so `Where`, `And`, `Or` +methods can use `builder.Cond` as parameter + * add Sum, SumInt, SumInt64 and NotIn methods + * some bugs fixed + +* **v0.5.0** + * logging interface changed + * some bugs fixed + +* **v0.4.5** + * many bugs fixed + * extends support unlimited deepth + * Delete Limit support + * **v0.4.4** * ql database expriment support * tidb database expriment support @@ -58,26 +73,10 @@ Drivers for Go's sql package which currently support database/sql includes: * select ForUpdate support * many bugs fixed -* **v0.4.3** - * Json column type support - * oracle expirement support - * bug fixed - -* **v0.4.2** - * Transaction will auto rollback if not Rollback or Commit be called. - * Gonic Mapper support - * bug fixed - -[More changelogs ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16) +[More changes ...](https://github.com/go-xorm/manual-en-US/tree/master/chapter-16) # Installation -If you have [gopm](https://github.com/gpmgo/gopm) installed, - - gopm get github.com/go-xorm/xorm - -Or - go get github.com/go-xorm/xorm # Documents @@ -124,7 +123,7 @@ results, err := engine.Query("select * from user") affected, err := engine.Exec("update user set age = ? where name = ?", age, name) ``` -* Insert one or multipe records to database +* Insert one or multiple records to database ```Go affected, err := engine.Insert(&user) @@ -173,7 +172,7 @@ err := engine.Table("user").Select("user.*, detail.*") // SELECT user.*, detail.* FROM user INNER JOIN detail WHERE user.name = ? limit 0 offset 10 ``` -* Query multiple records and record by record handle, there two methods Iterate and Rows +* Query multiple records and record by record handle, there are two methods Iterate and Rows ```Go err := engine.Iterate(&User{Name:name}, func(idx int, bean interface{}) error { @@ -191,7 +190,7 @@ for rows.Next() { } ``` -* Update one or more records, default will update non-empty and non-zero fields except to use Cols, AllCols and etc. +* Update one or more records, default will update non-empty and non-zero fields except when you use Cols, AllCols and so on. ```Go affected, err := engine.Id(1).Update(&user) @@ -201,7 +200,7 @@ affected, err := engine.Update(&user, &User{Name:name}) // UPDATE user SET ... Where name = ? var ids = []int64{1, 2, 3} -affected, err := engine.In(ids).Update(&user) +affected, err := engine.In("id", ids).Update(&user) // UPDATE user SET ... Where id IN (?, ?, ?) // force update indicated columns by Cols @@ -216,11 +215,12 @@ affected, err := engine.Id(1).AllCols().Update(&user) // UPDATE user SET name=?,age=?,salt=?,passwd=?,updated=? Where id = ? ``` -* Delete one or more records, Delete MUST has conditon +* Delete one or more records, Delete MUST have condition ```Go affected, err := engine.Where(...).Delete(&user) // DELETE FROM user Where ... +affected, err := engine.Id(2).Delete(&user) ``` * Count records @@ -230,6 +230,13 @@ counts, err := engine.Count(&user) // SELECT count(*) AS total FROM user ``` +* Query conditions builder + +```Go +err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users) +// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?) +``` + # Cases * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) diff --git a/vendor/github.com/go-xorm/xorm/README_CN.md b/vendor/github.com/go-xorm/xorm/README_CN.md index 4466bc707cd..fafef1a5d84 100644 --- a/vendor/github.com/go-xorm/xorm/README_CN.md +++ b/vendor/github.com/go-xorm/xorm/README_CN.md @@ -4,9 +4,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 -[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![CircleCI](https://circleci.com/gh/go-xorm/xorm/tree/master.svg?style=svg)](https://circleci.com/gh/go-xorm/xorm/tree/master) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/go-xorm/xorm?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) -[![Build Status](https://drone.io/github.com/go-xorm/tests/status.png)](https://drone.io/github.com/go-xorm/tests/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) +# 注意 + +最新的版本有不兼容的更新,您必须使用 `engine.ShowSQL()` 和 `engine.Logger().SetLevel()` 来替代 `engine.ShowSQL = `, `engine.ShowInfo = ` 等等。 ## 特性 @@ -28,6 +30,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * 支持记录版本(即乐观锁) +* 内置SQL Builder支持 + ## 驱动支持 目前支持的Go数据库驱动和对应的数据库如下: @@ -48,10 +52,24 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * Oracle: [github.com/mattn/go-oci8](https://github.com/mattn/go-oci8) (试验性支持) -* ql: [github.com/cznic/ql](https://github.com/cznic/ql) (试验性支持) - ## 更新日志 +* **v0.6.0** + * 去除对 ql 的支持 + * 新增条件查询分析器 [github.com/go-xorm/builder](https://github.com/go-xorm/builder), 从因此 `Where, And, Or` 函数 +将可以用 `builder.Cond` 作为条件组合 + * 新增 Sum, SumInt, SumInt64 和 NotIn 函数 + * Bug修正 + +* **v0.5.0** + * logging接口进行不兼容改变 + * Bug修正 + +* **v0.4.5** + * bug修正 + * extends 支持无限级 + * Delete Limit 支持 + * **v0.4.4** * Tidb 数据库支持 * QL 试验性支持 @@ -59,21 +77,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * ForUpdate 支持 * bug修正 -* **v0.4.3** - * Json 字段类型支持 - * oracle实验性支持 - * bug修正 - [更多更新日志...](https://github.com/go-xorm/manual-zh-CN/tree/master/chapter-16) ## 安装 -推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: - - gopm get github.com/go-xorm/xorm - -或者您也可以使用go工具进行安装: - go get github.com/go-xorm/xorm ## 文档 @@ -226,6 +233,13 @@ counts, err := engine.Count(&user) // SELECT count(*) AS total FROM user ``` +* 条件编辑器 + +```Go +err := engine.Where(builder.NotIn("a", 1, 2).And(builder.In("b", "c", "d", "e"))).Find(&users) +// SELECT id, name ... FROM user WHERE a NOT IN (?, ?) AND b IN (?, ?, ?) +``` + # 案例 * [github.com/m3ng9i/qreader](https://github.com/m3ng9i/qreader) diff --git a/vendor/github.com/go-xorm/xorm/VERSION b/vendor/github.com/go-xorm/xorm/VERSION index d1a98fc7b2e..22c1aa4d020 100644 --- a/vendor/github.com/go-xorm/xorm/VERSION +++ b/vendor/github.com/go-xorm/xorm/VERSION @@ -1 +1 @@ -xorm v0.4.5.0204 +xorm v0.6.0.1022 \ No newline at end of file diff --git a/vendor/github.com/go-xorm/xorm/circle.yml b/vendor/github.com/go-xorm/xorm/circle.yml new file mode 100644 index 00000000000..8faa627d102 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/circle.yml @@ -0,0 +1,25 @@ +dependencies: + override: + # './...' is a relative pattern which means all subdirectories + - go get -t -d -v ./... + - go get -t -d -v github.com/go-xorm/tests + - go build -v + +database: + override: + - mysql -u root -e "CREATE DATABASE xorm_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" + - mysql -u root -e "CREATE DATABASE xorm_test1 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" + - mysql -u root -e "CREATE DATABASE xorm_test2 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" + - mysql -u root -e "CREATE DATABASE xorm_test3 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci" + - createdb -p 5432 -e -U postgres xorm_test + - createdb -p 5432 -e -U postgres xorm_test1 + - createdb -p 5432 -e -U postgres xorm_test2 + - createdb -p 5432 -e -U postgres xorm_test3 + +test: + override: + # './...' is a relative pattern which means all subdirectories + - go test -v -race + - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./sqlite3.sh + - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./mysql.sh + - cd /home/ubuntu/.go_workspace/src/github.com/go-xorm/tests && ./postgres.sh \ No newline at end of file diff --git a/vendor/github.com/go-xorm/xorm/convert.go b/vendor/github.com/go-xorm/xorm/convert.go new file mode 100644 index 00000000000..87f0d3f1ec5 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/convert.go @@ -0,0 +1,249 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "database/sql/driver" + "errors" + "fmt" + "reflect" + "strconv" + "time" +) + +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +func strconvErr(err error) error { + if ne, ok := err.(*strconv.NumError); ok { + return ne.Err + } + return err +} + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } else { + c := make([]byte, len(b)) + copy(c, b) + return c + } +} + +func asString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + rv := reflect.ValueOf(src) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10) + case reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'g', -1, 64) + case reflect.Float32: + return strconv.FormatFloat(rv.Float(), 'g', -1, 32) + case reflect.Bool: + return strconv.FormatBool(rv.Bool()) + } + return fmt.Sprintf("%v", src) +} + +func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(buf, rv.Int(), 10), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.AppendUint(buf, rv.Uint(), 10), true + case reflect.Float32: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true + case reflect.Float64: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true + case reflect.Bool: + return strconv.AppendBool(buf, rv.Bool()), true + case reflect.String: + s := rv.String() + return append(buf, s...), true + } + return +} + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func convertAssign(dest, src interface{}) error { + // Common cases, without reflect. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = s + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = string(s) + return nil + case *interface{}: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + } + + case time.Time: + switch d := dest.(type) { + case *string: + *d = s.Format(time.RFC3339Nano) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s.Format(time.RFC3339Nano)) + return nil + } + case nil: + switch d := dest.(type) { + case *interface{}: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = asString(src) + return nil + } + case *[]byte: + sv = reflect.ValueOf(src) + if b, ok := asBytes(nil, sv); ok { + *d = b + return nil + } + case *bool: + bv, err := driver.Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *interface{}: + *d = src + return nil + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + if dpv.IsNil() { + return errNilPtr + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { + switch b := src.(type) { + case []byte: + dv.Set(reflect.ValueOf(cloneBytes(b))) + default: + dv.Set(sv) + } + return nil + } + + if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { + dv.Set(sv.Convert(dv.Type())) + return nil + } + + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } else { + dv.Set(reflect.New(dv.Type().Elem())) + return convertAssign(dv.Interface(), src) + } + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + case reflect.String: + dv.SetString(asString(src)) + return nil + } + + return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) +} diff --git a/vendor/github.com/go-xorm/xorm/mssql_dialect.go b/vendor/github.com/go-xorm/xorm/dialect_mssql.go similarity index 87% rename from vendor/github.com/go-xorm/xorm/mssql_dialect.go rename to vendor/github.com/go-xorm/xorm/dialect_mssql.go index 41989b91807..70fcaf6ea08 100644 --- a/vendor/github.com/go-xorm/xorm/mssql_dialect.go +++ b/vendor/github.com/go-xorm/xorm/dialect_mssql.go @@ -13,10 +13,6 @@ import ( "github.com/go-xorm/core" ) -// func init() { -// RegisterDialect("mssql", &mssql{}) -// } - var ( mssqlReservedWords = map[string]bool{ "ADD": true, @@ -247,10 +243,13 @@ func (db *mssql) SqlType(c *core.Column) string { c.Length = 7 case core.MediumInt: res = core.Int - case core.MediumText, core.TinyText, core.LongText, core.Json: - res = core.Text + case core.Text, core.MediumText, core.TinyText, core.LongText, core.Json: + res = core.Varchar + "(MAX)" case core.Double: res = core.Real + case core.Uuid: + res = core.Varchar + c.Length = 40 default: res = t } @@ -259,8 +258,9 @@ func (db *mssql) SqlType(c *core.Column) string { return core.Int } - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) + if hasLen2 { res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" } else if hasLen1 { @@ -334,9 +334,12 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) { func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{} - s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale -from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id -where a.object_id=object_id('` + tableName + `')` + s := `select a.name as name, b.name as ctype,a.max_length,a.precision,a.scale,a.is_nullable as nullable, + replace(replace(isnull(c.text,''),'(',''),')','') as vdefault + from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id + left join sys.syscomments c on a.default_object_id=c.id + where a.object_id=object_id('` + tableName + `')` + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) if err != nil { @@ -347,32 +350,38 @@ where a.object_id=object_id('` + tableName + `')` cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { - var name, ctype, precision, scale string - var maxLen int - err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale) + var name, ctype, vdefault string + var maxLen, precision, scale int + var nullable bool + err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale, &nullable, &vdefault) if err != nil { return nil, nil, err } col := new(core.Column) - col.Indexes = make(map[string]bool) - col.Length = maxLen + col.Indexes = make(map[string]int) col.Name = strings.Trim(name, "` ") - + col.Nullable = nullable + col.Default = vdefault ct := strings.ToUpper(ctype) + if ct == "DECIMAL" { + col.Length = precision + col.Length2 = scale + } else { + col.Length = maxLen + } switch ct { case "DATETIMEOFFSET": - col.SQLType = core.SQLType{core.TimeStampz, 0, 0} + col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0} case "NVARCHAR": - col.SQLType = core.SQLType{core.NVarchar, 0, 0} + col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: 0, DefaultLength2: 0} case "IMAGE": - col.SQLType = core.SQLType{core.VarBinary, 0, 0} + col.SQLType = core.SQLType{Name: core.VarBinary, DefaultLength: 0, DefaultLength2: 0} default: if _, ok := core.SqlTypes[ct]; ok { - col.SQLType = core.SQLType{ct, 0, 0} + col.SQLType = core.SQLType{Name: ct, DefaultLength: 0, DefaultLength2: 0} } else { - return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v", - ct, tableName, col.Name)) + return nil, nil, fmt.Errorf("Unknown colType %v for %v - %v", ct, tableName, col.Name) } } @@ -394,6 +403,7 @@ where a.object_id=object_id('` + tableName + `')` func (db *mssql) GetTables() ([]*core.Table, error) { args := []interface{}{} s := `select name from sysobjects where xtype ='U'` + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) if err != nil { @@ -428,6 +438,7 @@ INNER JOIN SYS.COLUMNS C ON IXS.OBJECT_ID=C.OBJECT_ID AND IXCS.COLUMN_ID=C.COLUMN_ID WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? ` + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) if err != nil { @@ -459,7 +470,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? colName = strings.Trim(colName, "` ") if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { - indexName = indexName[5+len(tableName) : len(indexName)] + indexName = indexName[5+len(tableName):] } var index *core.Index @@ -516,3 +527,25 @@ func (db *mssql) ForUpdateSql(query string) string { func (db *mssql) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}} } + +type odbcDriver struct { +} + +func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + kv := strings.Split(dataSourceName, ";") + var dbName string + + for _, c := range kv { + vv := strings.Split(strings.TrimSpace(c), "=") + if len(vv) == 2 { + switch strings.ToLower(vv[0]) { + case "database": + dbName = vv[1] + } + } + } + if dbName == "" { + return nil, errors.New("no db name provided") + } + return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil +} diff --git a/vendor/github.com/go-xorm/xorm/mysql_dialect.go b/vendor/github.com/go-xorm/xorm/dialect_mysql.go similarity index 83% rename from vendor/github.com/go-xorm/xorm/mysql_dialect.go rename to vendor/github.com/go-xorm/xorm/dialect_mysql.go index 76a84a7fa70..55cfdd7640b 100644 --- a/vendor/github.com/go-xorm/xorm/mysql_dialect.go +++ b/vendor/github.com/go-xorm/xorm/dialect_mysql.go @@ -8,6 +8,7 @@ import ( "crypto/tls" "errors" "fmt" + "regexp" "strconv" "strings" "time" @@ -15,10 +16,6 @@ import ( "github.com/go-xorm/core" ) -// func init() { -// RegisterDialect("mysql", &mysql{}) -// } - var ( mysqlReservedWords = map[string]bool{ "ADD": true, @@ -206,7 +203,7 @@ func (db *mysql) SqlType(c *core.Column) string { res = core.Enum res += "(" opts := "" - for v, _ := range c.EnumOptions { + for v := range c.EnumOptions { opts += fmt.Sprintf(",'%v'", v) } res += strings.TrimLeft(opts, ",") @@ -215,7 +212,7 @@ func (db *mysql) SqlType(c *core.Column) string { res = core.Set res += "(" opts := "" - for v, _ := range c.SetOptions { + for v := range c.SetOptions { opts += fmt.Sprintf(",'%v'", v) } res += strings.TrimLeft(opts, ",") @@ -231,8 +228,8 @@ func (db *mysql) SqlType(c *core.Column) string { res = t } - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) if res == core.BigInt && !hasLen1 && !hasLen2 { c.Length = 20 @@ -303,12 +300,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column args := []interface{}{db.DbName, tableName} s := "SELECT `COLUMN_NAME`, `IS_NULLABLE`, `COLUMN_DEFAULT`, `COLUMN_TYPE`," + " `COLUMN_KEY`, `EXTRA` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } - if err != nil { return nil, nil, err } @@ -318,7 +312,7 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column colSeq := make([]string, 0) for rows.Next() { col := new(core.Column) - col.Indexes = make(map[string]bool) + col.Indexes = make(map[string]int) var columnName, isNullable, colType, colKey, extra string var colDefault *string @@ -380,9 +374,9 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column col.Length = len1 col.Length2 = len2 if _, ok := core.SqlTypes[colType]; ok { - col.SQLType = core.SQLType{colType, len1, len2} + col.SQLType = core.SQLType{Name: colType, DefaultLength: len1, DefaultLength2: len2} } else { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType)) + return nil, nil, fmt.Errorf("Unknown colType %v", colType) } if colKey == "PRI" { @@ -414,12 +408,10 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column func (db *mysql) GetTables() ([]*core.Table, error) { args := []interface{}{db.DbName} s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from " + - "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB')" + "`INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? AND (`ENGINE`='MyISAM' OR `ENGINE` = 'InnoDB' OR `ENGINE` = 'TokuDB')" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -445,11 +437,9 @@ func (db *mysql) GetTables() ([]*core.Table, error) { func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { args := []interface{}{db.DbName, tableName} s := "SELECT `INDEX_NAME`, `NON_UNIQUE`, `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ?" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -477,7 +467,7 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { colName = strings.Trim(colName, "` ") var isRegular bool if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { - indexName = indexName[5+len(tableName) : len(indexName)] + indexName = indexName[5+len(tableName):] isRegular = true } @@ -498,3 +488,93 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*core.Index, error) { func (db *mysql) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}} } + +type mymysqlDriver struct { +} + +func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + db := &core.Uri{DbType: core.MYSQL} + + pd := strings.SplitN(dataSourceName, "*", 2) + if len(pd) == 2 { + // Parse protocol part of URI + p := strings.SplitN(pd[0], ":", 2) + if len(p) != 2 { + return nil, errors.New("Wrong protocol part of URI") + } + db.Proto = p[0] + options := strings.Split(p[1], ",") + db.Raddr = options[0] + for _, o := range options[1:] { + kv := strings.SplitN(o, "=", 2) + var k, v string + if len(kv) == 2 { + k, v = kv[0], kv[1] + } else { + k, v = o, "true" + } + switch k { + case "laddr": + db.Laddr = v + case "timeout": + to, err := time.ParseDuration(v) + if err != nil { + return nil, err + } + db.Timeout = to + default: + return nil, errors.New("Unknown option: " + k) + } + } + // Remove protocol part + pd = pd[1:] + } + // Parse database part of URI + dup := strings.SplitN(pd[0], "/", 3) + if len(dup) != 3 { + return nil, errors.New("Wrong database part of URI") + } + db.DbName = dup[0] + db.User = dup[1] + db.Passwd = dup[2] + + return db, nil +} + +type mysqlDriver struct { +} + +func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + dsnPattern := regexp.MustCompile( + `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] + `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] + `\/(?P.*?)` + // /dbname + `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] + matches := dsnPattern.FindStringSubmatch(dataSourceName) + //tlsConfigRegister := make(map[string]*tls.Config) + names := dsnPattern.SubexpNames() + + uri := &core.Uri{DbType: core.MYSQL} + + for i, match := range matches { + switch names[i] { + case "dbname": + uri.DbName = match + case "params": + if len(match) > 0 { + kvs := strings.Split(match, "&") + for _, kv := range kvs { + splits := strings.Split(kv, "=") + if len(splits) == 2 { + switch splits[0] { + case "charset": + uri.Charset = splits[1] + } + } + } + } + + } + } + return uri, nil +} diff --git a/vendor/github.com/go-xorm/xorm/oracle_dialect.go b/vendor/github.com/go-xorm/xorm/dialect_oracle.go similarity index 89% rename from vendor/github.com/go-xorm/xorm/oracle_dialect.go rename to vendor/github.com/go-xorm/xorm/dialect_oracle.go index dc64c00e5e9..8c43aa4cecb 100644 --- a/vendor/github.com/go-xorm/xorm/oracle_dialect.go +++ b/vendor/github.com/go-xorm/xorm/dialect_oracle.go @@ -7,16 +7,13 @@ package xorm import ( "errors" "fmt" + "regexp" "strconv" "strings" "github.com/go-xorm/core" ) -// func init() { -// RegisterDialect("oracle", &oracle{}) -// } - var ( oracleReservedWords = map[string]bool{ "ACCESS": true, @@ -530,8 +527,9 @@ func (db *oracle) SqlType(c *core.Column) string { res = t } - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) + if hasLen2 { res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" } else if hasLen1 { @@ -581,14 +579,14 @@ func (db *oracle) DropTableSql(tableName string) string { return fmt.Sprintf("DROP TABLE `%s`", tableName) } -func (b *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string { +func (db *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, charset string) string { var sql string sql = "CREATE TABLE " if tableName == "" { tableName = table.Name } - sql += b.Quote(tableName) + " (" + sql += db.Quote(tableName) + " (" pkList := table.PrimaryKeys @@ -597,7 +595,7 @@ func (b *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, chars /*if col.IsPrimaryKey && len(pkList) == 1 { sql += col.String(b.dialect) } else {*/ - sql += col.StringNoPk(b) + sql += col.StringNoPk(db) //} sql = strings.TrimSpace(sql) sql += ", " @@ -605,17 +603,17 @@ func (b *oracle) CreateTableSql(table *core.Table, tableName, storeEngine, chars if len(pkList) > 0 { sql += "PRIMARY KEY ( " - sql += b.Quote(strings.Join(pkList, b.Quote(","))) + sql += db.Quote(strings.Join(pkList, db.Quote(","))) sql += " ), " } sql = sql[:len(sql)-2] + ")" - if b.SupportEngine() && storeEngine != "" { + if db.SupportEngine() && storeEngine != "" { sql += " ENGINE=" + storeEngine } - if b.SupportCharset() { + if db.SupportCharset() { if len(charset) == 0 { - charset = b.URI().Charset + charset = db.URI().Charset } if len(charset) > 0 { sql += " DEFAULT CHARSET " + charset @@ -637,9 +635,7 @@ func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { func (db *oracle) MustDropTable(tableName string) error { sql, args := db.TableCheckSql(tableName) - if db.Logger != nil { - db.Logger.Info("[sql]", sql, args) - } + db.LogSQL(sql, args) rows, err := db.DB().Query(sql, args...) if err != nil { @@ -652,9 +648,8 @@ func (db *oracle) MustDropTable(tableName string) error { } sql = "Drop Table \"" + tableName + "\"" - if db.Logger != nil { - db.Logger.Info("[sql]", sql) - } + db.LogSQL(sql, args) + _, err = db.DB().Exec(sql) return err } @@ -669,10 +664,9 @@ func (db *oracle) IsColumnExist(tableName, colName string) (bool, error) { args := []interface{}{tableName, colName} query := "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = :1" + " AND column_name = :2" + db.LogSQL(query, args) + rows, err := db.DB().Query(query, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", query, args) - } if err != nil { return false, err } @@ -688,11 +682,9 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum args := []interface{}{tableName} s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, nil, err } @@ -702,7 +694,7 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum colSeq := make([]string, 0) for rows.Next() { col := new(core.Column) - col.Indexes = make(map[string]bool) + col.Indexes = make(map[string]int) var colName, colDefault, nullable, dataType, dataPrecision, dataScale *string var dataLen int @@ -743,23 +735,23 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum switch dt { case "VARCHAR2": - col.SQLType = core.SQLType{core.Varchar, len1, len2} + col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: len1, DefaultLength2: len2} case "NVARCHAR2": - col.SQLType = core.SQLType{core.NVarchar, len1, len2} + col.SQLType = core.SQLType{Name: core.NVarchar, DefaultLength: len1, DefaultLength2: len2} case "TIMESTAMP WITH TIME ZONE": - col.SQLType = core.SQLType{core.TimeStampz, 0, 0} + col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0} case "NUMBER": - col.SQLType = core.SQLType{core.Double, len1, len2} + col.SQLType = core.SQLType{Name: core.Double, DefaultLength: len1, DefaultLength2: len2} case "LONG", "LONG RAW": - col.SQLType = core.SQLType{core.Text, 0, 0} + col.SQLType = core.SQLType{Name: core.Text, DefaultLength: 0, DefaultLength2: 0} case "RAW": - col.SQLType = core.SQLType{core.Binary, 0, 0} + col.SQLType = core.SQLType{Name: core.Binary, DefaultLength: 0, DefaultLength2: 0} case "ROWID": - col.SQLType = core.SQLType{core.Varchar, 18, 0} + col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 18, DefaultLength2: 0} case "AQ$_SUBSCRIBERS": ignore = true default: - col.SQLType = core.SQLType{strings.ToUpper(dt), len1, len2} + col.SQLType = core.SQLType{Name: strings.ToUpper(dt), DefaultLength: len1, DefaultLength2: len2} } if ignore { @@ -767,7 +759,7 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum } if _, ok := core.SqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v %v", *dataType, col.SQLType)) + return nil, nil, fmt.Errorf("Unknown colType %v %v", *dataType, col.SQLType) } col.Length = dataLen @@ -787,11 +779,9 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum func (db *oracle) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT table_name FROM user_tables" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -814,11 +804,9 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) { args := []interface{}{tableName} s := "SELECT t.column_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -856,5 +844,56 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) { } func (db *oracle) Filters() []core.Filter { - return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{":", 1}, &core.IdFilter{}} + return []core.Filter{&core.QuoteFilter{}, &core.SeqFilter{Prefix: ":", Start: 1}, &core.IdFilter{}} +} + +type goracleDriver struct { +} + +func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + db := &core.Uri{DbType: core.ORACLE} + dsnPattern := regexp.MustCompile( + `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] + `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] + `\/(?P.*?)` + // /dbname + `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] + matches := dsnPattern.FindStringSubmatch(dataSourceName) + //tlsConfigRegister := make(map[string]*tls.Config) + names := dsnPattern.SubexpNames() + + for i, match := range matches { + switch names[i] { + case "dbname": + db.DbName = match + } + } + if db.DbName == "" { + return nil, errors.New("dbname is empty") + } + return db, nil +} + +type oci8Driver struct { +} + +//dataSourceName=user/password@ipv4:port/dbname +//dataSourceName=user/password@[ipv6]:port/dbname +func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + db := &core.Uri{DbType: core.ORACLE} + dsnPattern := regexp.MustCompile( + `^(?P.*)\/(?P.*)@` + // user:password@ + `(?P.*)` + // ip:port + `\/(?P.*)`) // dbname + matches := dsnPattern.FindStringSubmatch(dataSourceName) + names := dsnPattern.SubexpNames() + for i, match := range matches { + switch names[i] { + case "dbname": + db.DbName = match + } + } + if db.DbName == "" { + return nil, errors.New("dbname is empty") + } + return db, nil } diff --git a/vendor/github.com/go-xorm/xorm/postgres_dialect.go b/vendor/github.com/go-xorm/xorm/dialect_postgres.go similarity index 90% rename from vendor/github.com/go-xorm/xorm/postgres_dialect.go rename to vendor/github.com/go-xorm/xorm/dialect_postgres.go index 972a214f6fd..05fc1235ef4 100644 --- a/vendor/github.com/go-xorm/xorm/postgres_dialect.go +++ b/vendor/github.com/go-xorm/xorm/dialect_postgres.go @@ -7,15 +7,14 @@ package xorm import ( "errors" "fmt" + "net/url" + "sort" "strconv" "strings" "github.com/go-xorm/core" ) -// func init() { -// RegisterDialect("postgres", &postgres{}) -// } // from http://www.postgresql.org/docs/current/static/sql-keywords-appendix.html var ( postgresReservedWords = map[string]bool{ @@ -787,6 +786,11 @@ func (db *postgres) SqlType(c *core.Column) string { return core.Serial } return core.Integer + case core.BigInt: + if c.IsAutoIncrement { + return core.BigSerial + } + return core.BigInt case core.Serial, core.BigSerial: c.IsAutoIncrement = true c.Nullable = false @@ -816,8 +820,9 @@ func (db *postgres) SqlType(c *core.Column) string { res = t } - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) + hasLen1 := (c.Length > 0) + hasLen2 := (c.Length2 > 0) + if hasLen2 { res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" } else if hasLen1 { @@ -836,6 +841,7 @@ func (db *postgres) IsReserved(name string) bool { } func (db *postgres) Quote(name string) string { + name = strings.Replace(name, ".", `"."`, -1) return "\"" + name + "\"" } @@ -882,9 +888,10 @@ func (db *postgres) ModifyColumnSql(tableName string, col *core.Column) string { } func (db *postgres) DropIndexSql(tableName string, index *core.Index) string { - quote := db.Quote //var unique string - var idxName string = index.Name + quote := db.Quote + idxName := index.Name + if !strings.HasPrefix(idxName, "UQE_") && !strings.HasPrefix(idxName, "IDX_") { if index.Type == core.UniqueType { @@ -900,10 +907,9 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { args := []interface{}{tableName, colName} query := "SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" + " AND column_name = $2" + db.LogSQL(query, args) + rows, err := db.DB().Query(query, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", query, args) - } if err != nil { return false, err } @@ -913,8 +919,8 @@ func (db *postgres) IsColumnExist(tableName, colName string) (bool, error) { } func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { - pgSchema := "public" - args := []interface{}{tableName,pgSchema} + // FIXME: the schema should be replaced by user custom's + args := []interface{}{tableName, "public"} s := `SELECT column_name, column_default, is_nullable, data_type, character_maximum_length, numeric_precision, numeric_precision_radix , CASE WHEN p.contype = 'p' THEN true ELSE false END AS primarykey, CASE WHEN p.contype = 'u' THEN true ELSE false END AS uniquekey @@ -926,11 +932,9 @@ FROM pg_attribute f LEFT JOIN pg_class AS g ON p.confrelid = g.oid LEFT JOIN INFORMATION_SCHEMA.COLUMNS s ON s.column_name=f.attname AND c.relname=s.table_name WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.attnum > 0 ORDER BY f.attnum;` + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, nil, err } @@ -941,7 +945,7 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att for rows.Next() { col := new(core.Column) - col.Indexes = make(map[string]bool) + col.Indexes = make(map[string]int) var colName, isNullable, dataType string var maxLenStr, colDefault, numPrecision, numRadix *string @@ -978,22 +982,24 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att switch dataType { case "character varying", "character": - col.SQLType = core.SQLType{core.Varchar, 0, 0} + col.SQLType = core.SQLType{Name: core.Varchar, DefaultLength: 0, DefaultLength2: 0} case "timestamp without time zone": - col.SQLType = core.SQLType{core.DateTime, 0, 0} + col.SQLType = core.SQLType{Name: core.DateTime, DefaultLength: 0, DefaultLength2: 0} case "timestamp with time zone": - col.SQLType = core.SQLType{core.TimeStampz, 0, 0} + col.SQLType = core.SQLType{Name: core.TimeStampz, DefaultLength: 0, DefaultLength2: 0} case "double precision": - col.SQLType = core.SQLType{core.Double, 0, 0} + col.SQLType = core.SQLType{Name: core.Double, DefaultLength: 0, DefaultLength2: 0} case "boolean": - col.SQLType = core.SQLType{core.Bool, 0, 0} + col.SQLType = core.SQLType{Name: core.Bool, DefaultLength: 0, DefaultLength2: 0} case "time without time zone": - col.SQLType = core.SQLType{core.Time, 0, 0} + col.SQLType = core.SQLType{Name: core.Time, DefaultLength: 0, DefaultLength2: 0} + case "oid": + col.SQLType = core.SQLType{Name: core.BigInt, DefaultLength: 0, DefaultLength2: 0} default: - col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0} + col.SQLType = core.SQLType{Name: strings.ToUpper(dataType), DefaultLength: 0, DefaultLength2: 0} } if _, ok := core.SqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) + return nil, nil, fmt.Errorf("Unknown colType: %v", dataType) } col.Length = maxLen @@ -1015,13 +1021,12 @@ WHERE c.relkind = 'r'::char AND c.relname = $1 AND s.table_schema = $2 AND f.att } func (db *postgres) GetTables() ([]*core.Table, error) { - args := []interface{}{} - s := "SELECT tablename FROM pg_tables where schemaname = 'public'" + // FIXME: replace public to user customrize schema + args := []interface{}{"public"} + s := fmt.Sprintf("SELECT tablename FROM pg_tables WHERE schemaname = $1") + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -1042,13 +1047,12 @@ func (db *postgres) GetTables() ([]*core.Table, error) { } func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) { - args := []interface{}{tableName} - s := "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname='public' AND tablename=$1" + // FIXME: replace the public schema to user specify schema + args := []interface{}{"public", tableName} + s := fmt.Sprintf("SELECT indexname, indexdef FROM pg_indexes WHERE schemaname=$1 AND tablename=$2") + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -1076,7 +1080,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { - newIdxName := indexName[5+len(tableName) : len(indexName)] + newIdxName := indexName[5+len(tableName):] if newIdxName != "" { indexName = newIdxName } @@ -1092,5 +1096,109 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) } func (db *postgres) Filters() []core.Filter { - return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{"$", 1}} + return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{Prefix: "$", Start: 1}} +} + +type pqDriver struct { +} + +type values map[string]string + +func (vs values) Set(k, v string) { + vs[k] = v +} + +func (vs values) Get(k string) (v string) { + return vs[k] +} + +func errorf(s string, args ...interface{}) { + panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) +} + +func parseURL(connstr string) (string, error) { + u, err := url.Parse(connstr) + if err != nil { + return "", err + } + + if u.Scheme != "postgresql" && u.Scheme != "postgres" { + return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) + } + + var kvs []string + escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) + accrue := func(k, v string) { + if v != "" { + kvs = append(kvs, k+"="+escaper.Replace(v)) + } + } + + if u.User != nil { + v := u.User.Username() + accrue("user", v) + + v, _ = u.User.Password() + accrue("password", v) + } + + i := strings.Index(u.Host, ":") + if i < 0 { + accrue("host", u.Host) + } else { + accrue("host", u.Host[:i]) + accrue("port", u.Host[i+1:]) + } + + if u.Path != "" { + accrue("dbname", u.Path[1:]) + } + + q := u.Query() + for k := range q { + accrue(k, q.Get(k)) + } + + sort.Strings(kvs) // Makes testing easier (not a performance concern) + return strings.Join(kvs, " "), nil +} + +func parseOpts(name string, o values) { + if len(name) == 0 { + return + } + + name = strings.TrimSpace(name) + + ps := strings.Split(name, " ") + for _, p := range ps { + kv := strings.Split(p, "=") + if len(kv) < 2 { + errorf("invalid option: %q", p) + } + o.Set(kv[0], kv[1]) + } +} + +func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + db := &core.Uri{DbType: core.POSTGRES} + o := make(values) + var err error + if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") { + dataSourceName, err = parseURL(dataSourceName) + if err != nil { + return nil, err + } + } + parseOpts(dataSourceName, o) + + db.DbName = o.Get("dbname") + if db.DbName == "" { + return nil, errors.New("dbname is empty") + } + /*db.Schema = o.Get("schema") + if len(db.Schema) == 0 { + db.Schema = "public" + }*/ + return db, nil } diff --git a/vendor/github.com/go-xorm/xorm/sqlite3_dialect.go b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go similarity index 94% rename from vendor/github.com/go-xorm/xorm/sqlite3_dialect.go rename to vendor/github.com/go-xorm/xorm/dialect_sqlite3.go index 9f29d587cdb..c190c4d900f 100644 --- a/vendor/github.com/go-xorm/xorm/sqlite3_dialect.go +++ b/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go @@ -237,9 +237,10 @@ func (db *sqlite3) TableCheckSql(tableName string) (string, []interface{}) { } func (db *sqlite3) DropIndexSql(tableName string, index *core.Index) string { - quote := db.Quote //var unique string - var idxName string = index.Name + quote := db.Quote + idxName := index.Name + if !strings.HasPrefix(idxName, "UQE_") && !strings.HasPrefix(idxName, "IDX_") { if index.Type == core.UniqueType { @@ -264,10 +265,8 @@ func (db *sqlite3) ForUpdateSql(query string) string { func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) { args := []interface{}{tableName} query := "SELECT name FROM sqlite_master WHERE type='table' and name = ? and ((sql like '%`" + colName + "`%') or (sql like '%[" + colName + "]%'))" + db.LogSQL(query, args) rows, err := db.DB().Query(query, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", query, args) - } if err != nil { return false, err } @@ -282,11 +281,8 @@ func (db *sqlite3) IsColumnExist(tableName, colName string) (bool, error) { func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{tableName} s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" - + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, nil, err } @@ -316,15 +312,15 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu colStr = reg.ReplaceAllString(colStr, ",") fields := strings.Fields(strings.TrimSpace(colStr)) col := new(core.Column) - col.Indexes = make(map[string]bool) + col.Indexes = make(map[string]int) col.Nullable = true col.DefaultIsEmpty = true for idx, field := range fields { if idx == 0 { - col.Name = strings.Trim(field, "`[] ") + col.Name = strings.Trim(strings.Trim(field, "`[] "), `"`) continue } else if idx == 1 { - col.SQLType = core.SQLType{field, 0, 0} + col.SQLType = core.SQLType{Name: field, DefaultLength: 0, DefaultLength2: 0} } switch field { case "PRIMARY": @@ -354,11 +350,9 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*core.Colu func (db *sqlite3) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT name FROM sqlite_master WHERE type='table'" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -382,11 +376,9 @@ func (db *sqlite3) GetTables() ([]*core.Table, error) { func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) { args := []interface{}{tableName} s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?" + db.LogSQL(s, args) rows, err := db.DB().Query(s, args...) - if db.Logger != nil { - db.Logger.Info("[sql]", s, args) - } if err != nil { return nil, err } @@ -394,16 +386,16 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) indexes := make(map[string]*core.Index, 0) for rows.Next() { - var tmpSql sql.NullString - err = rows.Scan(&tmpSql) + var tmpSQL sql.NullString + err = rows.Scan(&tmpSQL) if err != nil { return nil, err } - if !tmpSql.Valid { + if !tmpSQL.Valid { continue } - sql := tmpSql.String + sql := tmpSQL.String index := new(core.Index) nNStart := strings.Index(sql, "INDEX") @@ -414,7 +406,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) indexName := strings.Trim(sql[nNStart+6:nNEnd], "` []") if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { - index.Name = indexName[5+len(tableName) : len(indexName)] + index.Name = indexName[5+len(tableName):] } else { index.Name = indexName } @@ -442,3 +434,10 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*core.Index, error) func (db *sqlite3) Filters() []core.Filter { return []core.Filter{&core.IdFilter{}} } + +type sqlite3Driver struct { +} + +func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { + return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil +} diff --git a/vendor/github.com/go-xorm/xorm/doc.go b/vendor/github.com/go-xorm/xorm/doc.go index 54ce2b985b7..5b36fcd80ba 100644 --- a/vendor/github.com/go-xorm/xorm/doc.go +++ b/vendor/github.com/go-xorm/xorm/doc.go @@ -1,4 +1,4 @@ -// Copyright 2013 - 2015 The Xorm Authors. All rights reserved. +// Copyright 2013 - 2016 The XORM Authors. All rights reserved. // Use of this source code is governed by a BSD // license that can be found in the LICENSE file. @@ -24,7 +24,7 @@ Generally, one engine for an application is enough. You can set it as package va Raw Methods -Xorm also support raw sql execution: +XORM also support raw SQL execution: 1. query a SQL string, the returned results is []map[string][]byte @@ -36,9 +36,9 @@ Xorm also support raw sql execution: ORM Methods -There are 7 major ORM methods and many helpful methods to use to operate database. +There are 8 major ORM methods and many helpful methods to use to operate database. -1. Insert one or multipe records to database +1. Insert one or multiple records to database affected, err := engine.Insert(&struct) // INSERT INTO struct () values () @@ -58,10 +58,18 @@ There are 7 major ORM methods and many helpful methods to use to operate databas 3. Query multiple records from database - sliceOfStructs := new(Struct) - err := engine.Find(sliceOfStructs) + var sliceOfStructs []Struct + err := engine.Find(&sliceOfStructs) // SELECT * FROM user + var mapOfStructs = make(map[int64]Struct) + err := engine.Find(&mapOfStructs) + // SELECT * FROM user + + var int64s []int64 + err := engine.Table("user").Cols("id").Find(&int64s) + // SELECT id FROM user + 4. Query multiple records and record by record handle, there two methods, one is Iterate, another is Rows @@ -81,7 +89,7 @@ another is Rows affected, err := engine.Id(...).Update(&user) // UPDATE user SET ... -6. Delete one or more records, Delete MUST has conditon +6. Delete one or more records, Delete MUST has condition affected, err := engine.Where(...).Delete(&user) // DELETE FROM user Where ... @@ -91,20 +99,31 @@ another is Rows counts, err := engine.Count(&user) // SELECT count(*) AS total FROM user +8. Sum records + + sumFloat64, err := engine.Sum(&user, "id") + // SELECT sum(id) from user + + sumFloat64s, err := engine.Sums(&user, "id1", "id2") + // SELECT sum(id1), sum(id2) from user + + sumInt64s, err := engine.SumsInt(&user, "id1", "id2") + // SELECT sum(id1), sum(id2) from user + Conditions -The above 7 methods could use with condition methods chainable. -Attention: the above 7 methods should be the last chainable method. +The above 8 methods could use with condition methods chainable. +Attention: the above 8 methods should be the last chainable method. -1. Id, In +1. ID, In - engine.Id(1).Get(&user) // for single primary key + engine.ID(1).Get(&user) // for single primary key // SELECT * FROM user WHERE id = 1 - engine.Id(core.PK{1, 2}).Get(&user) // for composite primary keys + engine.ID(core.PK{1, 2}).Get(&user) // for composite primary keys // SELECT * FROM user WHERE id1 = 1 AND id2 = 2 engine.In("id", 1, 2, 3).Find(&users) // SELECT * FROM user WHERE id IN (1, 2, 3) - engine.In("id", []int{1, 2, 3}) + engine.In("id", []int{1, 2, 3}).Find(&users) // SELECT * FROM user WHERE id IN (1, 2, 3) 2. Where, And, Or @@ -127,28 +146,30 @@ Attention: the above 7 methods should be the last chainable method. // SELECT TOP 5 * FROM user // for mssql // SELECT * FROM user LIMIT .. OFFSET 0 //for other databases -5. Sql, let you custom SQL +5. SQL, let you custom SQL - engine.Sql("select * from user").Find() + var users []User + engine.SQL("select * from user").Find(&users) 6. Cols, Omit, Distinct - engine.Cols("col1, col2").Find() + var users []*User + engine.Cols("col1, col2").Find(&users) // SELECT col1, col2 FROM user engine.Cols("col1", "col2").Where().Update(user) // UPDATE user set col1 = ?, col2 = ? Where ... - engine.Omit("col1").Find() + engine.Omit("col1").Find(&users) // SELECT col2, col3 FROM user - engine.Omit("col1").Insert() + engine.Omit("col1").Insert(&user) // INSERT INTO table (non-col1) VALUES () - engine.Distinct("col1").Find() + engine.Distinct("col1").Find(&users) // SELECT DISTINCT col1 FROM user 7. Join, GroupBy, Having - engine.GroupBy("name").Having("name='xlw'").Find() + engine.GroupBy("name").Having("name='xlw'").Find(&users) //SELECT * FROM user GROUP BY name HAVING name='xlw' - engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find() + engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find(&users) //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id More usage, please visit http://xorm.io/docs diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/github.com/go-xorm/xorm/engine.go index afb38766db3..134e6b147d9 100644 --- a/vendor/github.com/go-xorm/xorm/engine.go +++ b/vendor/github.com/go-xorm/xorm/engine.go @@ -36,54 +36,82 @@ type Engine struct { mutex *sync.RWMutex Cacher core.Cacher - ShowSQL bool + showSQL bool + showExecTime bool - // !nashtsai! TODO ought to deprecate these but having logger to control its log level - ShowInfo bool - ShowErr bool - ShowDebug bool - ShowWarn bool - // --227 - - Logger core.ILogger + logger core.ILogger TZLocation *time.Location + DatabaseTZ *time.Location // The timezone of the database disableGlobalCache bool + + tagHandlers map[string]tagHandler } +// ShowSQL show SQL statement or not on logger if log level is great than INFO +func (engine *Engine) ShowSQL(show ...bool) { + engine.logger.ShowSQL(show...) + if len(show) == 0 { + engine.showSQL = true + } else { + engine.showSQL = show[0] + } +} + +// ShowExecTime show SQL statement and execute time or not on logger if log level is great than INFO +func (engine *Engine) ShowExecTime(show ...bool) { + if len(show) == 0 { + engine.showExecTime = true + } else { + engine.showExecTime = show[0] + } +} + +// Logger return the logger interface +func (engine *Engine) Logger() core.ILogger { + return engine.logger +} + +// SetLogger set the new logger func (engine *Engine) SetLogger(logger core.ILogger) { - engine.Logger = logger + engine.logger = logger engine.dialect.SetLogger(logger) } +// SetDisableGlobalCache disable global cache or not func (engine *Engine) SetDisableGlobalCache(disable bool) { if engine.disableGlobalCache != disable { engine.disableGlobalCache = disable } } +// DriverName return the current sql driver's name func (engine *Engine) DriverName() string { return engine.dialect.DriverName() } +// DataSourceName return the current connection string func (engine *Engine) DataSourceName() string { return engine.dialect.DataSourceName() } +// SetMapper set the name mapping rules func (engine *Engine) SetMapper(mapper core.IMapper) { engine.SetTableMapper(mapper) engine.SetColumnMapper(mapper) } +// SetTableMapper set the table name mapping rule func (engine *Engine) SetTableMapper(mapper core.IMapper) { engine.TableMapper = mapper } +// SetColumnMapper set the column name mapping rule func (engine *Engine) SetColumnMapper(mapper core.IMapper) { engine.ColumnMapper = mapper } -// If engine's database support batch insert records like +// SupportInsertMany If engine's database support batch insert records like // "insert into user values (name, age), (name, age)". // When the return is ture, then engine.Insert(&users) will // generate batch sql and exeute. @@ -91,29 +119,69 @@ func (engine *Engine) SupportInsertMany() bool { return engine.dialect.SupportInsertMany() } -// Engine's database use which charactor as quote. +// QuoteStr Engine's database use which character as quote. // mysql, sqlite use ` and postgres use " func (engine *Engine) QuoteStr() string { return engine.dialect.QuoteStr() } -// Use QuoteStr quote the string sql -func (engine *Engine) Quote(sql string) string { - if len(sql) == 0 { - return sql +// Quote Use QuoteStr quote the string sql +func (engine *Engine) Quote(value string) string { + value = strings.TrimSpace(value) + if len(value) == 0 { + return value } - if string(sql[0]) == engine.dialect.QuoteStr() || sql[0] == '`' { - return sql + + if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' { + return value } + + value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1) + + return engine.dialect.QuoteStr() + value + engine.dialect.QuoteStr() +} + +// QuoteTo quotes string and writes into the buffer +func (engine *Engine) QuoteTo(buf *bytes.Buffer, value string) { + + if buf == nil { + return + } + + value = strings.TrimSpace(value) + if value == "" { + return + } + + if string(value[0]) == engine.dialect.QuoteStr() || value[0] == '`' { + buf.WriteString(value) + return + } + + value = strings.Replace(value, ".", engine.dialect.QuoteStr()+"."+engine.dialect.QuoteStr(), -1) + + buf.WriteString(engine.dialect.QuoteStr()) + buf.WriteString(value) + buf.WriteString(engine.dialect.QuoteStr()) +} + +func (engine *Engine) quote(sql string) string { return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr() } -// A simple wrapper to dialect's core.SqlType method +// SqlType will be depracated, please use SQLType instead +// +// Deprecated: use SQLType instead func (engine *Engine) SqlType(c *core.Column) string { + return engine.SQLType(c) +} + +// SQLType A simple wrapper to dialect's core.SqlType method +func (engine *Engine) SQLType(c *core.Column) string { return engine.dialect.SqlType(c) } -// Database's autoincrement statement +// AutoIncrStr Database's autoincrement statement func (engine *Engine) AutoIncrStr() string { return engine.dialect.AutoIncrStr() } @@ -123,22 +191,17 @@ func (engine *Engine) SetMaxOpenConns(conns int) { engine.db.SetMaxOpenConns(conns) } -// @Deprecated -func (engine *Engine) SetMaxConns(conns int) { - engine.SetMaxOpenConns(conns) -} - -// SetMaxIdleConns +// SetMaxIdleConns set the max idle connections on pool, default is 2 func (engine *Engine) SetMaxIdleConns(conns int) { engine.db.SetMaxIdleConns(conns) } -// SetDefaltCacher set the default cacher. Xorm's default not enable cacher. +// SetDefaultCacher set the default cacher. Xorm's default not enable cacher. func (engine *Engine) SetDefaultCacher(cacher core.Cacher) { engine.Cacher = cacher } -// If you has set default cacher, and you want temporilly stop use cache, +// NoCache If you has set default cacher, and you want temporilly stop use cache, // you can use NoCache() func (engine *Engine) NoCache() *Session { session := engine.NewSession() @@ -146,13 +209,14 @@ func (engine *Engine) NoCache() *Session { return session.NoCache() } +// NoCascade If you do not want to auto cascade load object func (engine *Engine) NoCascade() *Session { session := engine.NewSession() session.IsAutoClose = true return session.NoCascade() } -// Set a table use a special cacher +// MapCacher Set a table use a special cacher func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { v := rValue(bean) tb := engine.autoMapType(v) @@ -164,15 +228,17 @@ func (engine *Engine) NewDB() (*core.DB, error) { return core.OpenDialect(engine.dialect) } +// DB return the wrapper of sql.DB func (engine *Engine) DB() *core.DB { return engine.db } +// Dialect return database dialect func (engine *Engine) Dialect() core.Dialect { return engine.dialect } -// New a session +// NewSession New a session func (engine *Engine) NewSession() *Session { session := &Session{Engine: engine} session.Init() @@ -188,130 +254,72 @@ func (engine *Engine) Close() error { func (engine *Engine) Ping() error { session := engine.NewSession() defer session.Close() - engine.LogInfo("PING DATABASE", engine.DriverName) + engine.logger.Infof("PING DATABASE %v", engine.DriverName()) return session.Ping() } // logging sql func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { - if engine.ShowSQL { - engine.overrideLogLevel(core.LOG_INFO) + if engine.showSQL && !engine.showExecTime { if len(sqlArgs) > 0 { - engine.Logger.Infof("[sql] %v [args] %v", sqlStr, sqlArgs) + engine.logger.Infof("[SQL] %v %v", sqlStr, sqlArgs) } else { - engine.Logger.Infof("[sql] %v", sqlStr) + engine.logger.Infof("[SQL] %v", sqlStr) } } } -func (engine *Engine) LogSQLQueryTime(sqlStr string, args interface{}, executionBlock func() (*core.Stmt, *core.Rows, error)) (*core.Stmt, *core.Rows, error) { - if engine.ShowDebug { +func (engine *Engine) logSQLQueryTime(sqlStr string, args []interface{}, executionBlock func() (*core.Stmt, *core.Rows, error)) (*core.Stmt, *core.Rows, error) { + if engine.showSQL && engine.showExecTime { b4ExecTime := time.Now() stmt, res, err := executionBlock() execDuration := time.Since(b4ExecTime) - engine.LogDebugf("[time] %s - args %v - query took: %vns", sqlStr, args, execDuration.Nanoseconds()) + if len(args) > 0 { + engine.logger.Infof("[SQL] %s %v - took: %v", sqlStr, args, execDuration) + } else { + engine.logger.Infof("[SQL] %s - took: %v", sqlStr, execDuration) + } return stmt, res, err - } else { - return executionBlock() } + return executionBlock() } -func (engine *Engine) LogSQLExecutionTime(sqlStr string, args interface{}, executionBlock func() (sql.Result, error)) (sql.Result, error) { - if engine.ShowDebug { +func (engine *Engine) logSQLExecutionTime(sqlStr string, args []interface{}, executionBlock func() (sql.Result, error)) (sql.Result, error) { + if engine.showSQL && engine.showExecTime { b4ExecTime := time.Now() res, err := executionBlock() execDuration := time.Since(b4ExecTime) - engine.LogDebugf("[time] %s - args %v - execution took: %vns", sqlStr, args, execDuration.Nanoseconds()) + if len(args) > 0 { + engine.logger.Infof("[sql] %s [args] %v - took: %v", sqlStr, args, execDuration) + } else { + engine.logger.Infof("[sql] %s - took: %v", sqlStr, execDuration) + } return res, err - } else { - return executionBlock() } + return executionBlock() } -// logging error -func (engine *Engine) overrideLogLevel(overrideLevel core.LogLevel) { - logLevel := engine.Logger.Level() - if logLevel == core.LOG_UNKNOWN { - // intend to left empty - } else if logLevel < overrideLevel { // TODO can remove if deprecated engine.ShowErr - engine.Logger.SetLevel(core.LOG_ERR) // try override logger's log level - } - +// Sql provides raw sql input parameter. When you have a complex SQL statement +// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. +// +// Deprecated: use SQL instead. +func (engine *Engine) Sql(querystring string, args ...interface{}) *Session { + return engine.SQL(querystring, args...) } -func (engine *Engine) LogError(contents ...interface{}) { - if engine.ShowErr { - engine.overrideLogLevel(core.LOG_ERR) - engine.Logger.Err(contents...) - } -} - -func (engine *Engine) LogErrorf(format string, contents ...interface{}) { - if engine.ShowErr { - engine.overrideLogLevel(core.LOG_ERR) - engine.Logger.Errf(format, contents...) - } -} - -// logging info -func (engine *Engine) LogInfo(contents ...interface{}) { - if engine.ShowInfo { - engine.overrideLogLevel(core.LOG_INFO) - engine.Logger.Info(contents...) - } -} - -func (engine *Engine) LogInfof(format string, contents ...interface{}) { - if engine.ShowErr { - engine.overrideLogLevel(core.LOG_INFO) - engine.Logger.Infof(format, contents...) - } -} - -// logging debug -func (engine *Engine) LogDebug(contents ...interface{}) { - if engine.ShowDebug { - engine.overrideLogLevel(core.LOG_DEBUG) - engine.Logger.Debug(contents...) - } -} - -func (engine *Engine) LogDebugf(format string, contents ...interface{}) { - if engine.ShowDebug { - engine.overrideLogLevel(core.LOG_DEBUG) - engine.Logger.Debugf(format, contents...) - } -} - -// logging warn -func (engine *Engine) LogWarn(contents ...interface{}) { - if engine.ShowWarn { - engine.overrideLogLevel(core.LOG_WARNING) - engine.Logger.Warning(contents...) - } -} - -func (engine *Engine) LogWarnf(format string, contents ...interface{}) { - if engine.ShowWarn { - engine.overrideLogLevel(core.LOG_WARNING) - engine.Logger.Warningf(format, contents...) - } -} - -// Sql method let's you manualy write raw sql and operate +// SQL method let's you manually write raw SQL and operate // For example: // -// engine.Sql("select * from user").Find(&users) +// engine.SQL("select * from user").Find(&users) // // This code will execute "select * from user" and set the records to users -// -func (engine *Engine) Sql(querystring string, args ...interface{}) *Session { +func (engine *Engine) SQL(query interface{}, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true - return session.Sql(querystring, args...) + return session.SQL(query, args...) } -// Default if your struct has "created" or "updated" filed tag, the fields +// NoAutoTime Default if your struct has "created" or "updated" filed tag, the fields // will automatically be filled with current time when Insert or Update // invoked. Call NoAutoTime if you dont' want to fill automatically. func (engine *Engine) NoAutoTime() *Session { @@ -320,13 +328,14 @@ func (engine *Engine) NoAutoTime() *Session { return session.NoAutoTime() } +// NoAutoCondition disable auto generate Where condition from bean or not func (engine *Engine) NoAutoCondition(no ...bool) *Session { session := engine.NewSession() session.IsAutoClose = true return session.NoAutoCondition(no...) } -// Retrieve all tables, columns, indexes' informations from database. +// DBMetas Retrieve all tables, columns, indexes' informations from database. func (engine *Engine) DBMetas() ([]*core.Table, error) { tables, err := engine.dialect.GetTables() if err != nil { @@ -341,8 +350,6 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { for _, name := range colSeq { table.AddColumn(cols[name]) } - //table.Columns = cols - //table.ColumnsSeq = colSeq indexes, err := engine.dialect.GetIndexes(table.Name) if err != nil { return nil, err @@ -352,9 +359,9 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { for _, index := range indexes { for _, name := range index.Cols { if col := table.GetColumn(name); col != nil { - col.Indexes[index.Name] = true + col.Indexes[index.Name] = index.Type } else { - return nil, fmt.Errorf("Unknown col "+name+" in indexes %v of table", index, table.ColumnsSeq()) + return nil, fmt.Errorf("Unknown col %s in index %v of table %v, columns %v", name, index.Name, table.Name, table.ColumnsSeq()) } } } @@ -362,29 +369,58 @@ func (engine *Engine) DBMetas() ([]*core.Table, error) { return tables, nil } -/* -dump database all table structs and data to a file -*/ -func (engine *Engine) DumpAllToFile(fp string) error { +// DumpAllToFile dump database all table structs and data to a file +func (engine *Engine) DumpAllToFile(fp string, tp ...core.DbType) error { f, err := os.Create(fp) if err != nil { return err } defer f.Close() - return engine.DumpAll(f) + return engine.DumpAll(f, tp...) } -/* -dump database all table structs and data to w -*/ -func (engine *Engine) DumpAll(w io.Writer) error { +// DumpAll dump database all table structs and data to w +func (engine *Engine) DumpAll(w io.Writer, tp ...core.DbType) error { tables, err := engine.DBMetas() if err != nil { return err } + return engine.DumpTables(tables, w, tp...) +} - _, err = io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s*/\n\n", - Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"))) +// DumpTablesToFile dump specified tables to SQL file. +func (engine *Engine) DumpTablesToFile(tables []*core.Table, fp string, tp ...core.DbType) error { + f, err := os.Create(fp) + if err != nil { + return err + } + defer f.Close() + return engine.DumpTables(tables, f, tp...) +} + +// DumpTables dump specify tables to io.Writer +func (engine *Engine) DumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { + return engine.dumpTables(tables, w, tp...) +} + +// dumpTables dump database all table structs and data to w with specify db type +func (engine *Engine) dumpTables(tables []*core.Table, w io.Writer, tp ...core.DbType) error { + var dialect core.Dialect + var distDBName string + if len(tp) == 0 { + dialect = engine.dialect + distDBName = string(engine.dialect.DBType()) + } else { + dialect = core.QueryDialect(tp[0]) + if dialect == nil { + return errors.New("Unsupported database type") + } + dialect.Init(nil, engine.dialect.URI(), "", "") + distDBName = string(tp[0]) + } + + _, err := io.WriteString(w, fmt.Sprintf("/*Generated by xorm v%s %s, from %s to %s*/\n\n", + Version, time.Now().In(engine.TZLocation).Format("2006-01-02 15:04:05"), engine.dialect.DBType(), strings.ToUpper(distDBName))) if err != nil { return err } @@ -396,29 +432,26 @@ func (engine *Engine) DumpAll(w io.Writer) error { return err } } - _, err = io.WriteString(w, engine.dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n") + _, err = io.WriteString(w, dialect.CreateTableSql(table, "", table.StoreEngine, "")+";\n") if err != nil { return err } for _, index := range table.Indexes { - _, err = io.WriteString(w, engine.dialect.CreateIndexSql(table.Name, index)+";\n") + _, err = io.WriteString(w, dialect.CreateIndexSql(table.Name, index)+";\n") if err != nil { return err } } - rows, err := engine.DB().Query("SELECT * FROM " + engine.Quote(table.Name)) - if err != nil { - return err - } + cols := table.ColumnsSeq() + colNames := dialect.Quote(strings.Join(cols, dialect.Quote(", "))) - cols, err := rows.Columns() + rows, err := engine.DB().Query("SELECT " + colNames + " FROM " + engine.Quote(table.Name)) if err != nil { return err } - if len(cols) == 0 { - continue - } + defer rows.Close() + for rows.Next() { dest := make([]interface{}, len(cols)) err = rows.ScanSlice(&dest) @@ -426,7 +459,7 @@ func (engine *Engine) DumpAll(w io.Writer) error { return err } - _, err = io.WriteString(w, "INSERT INTO "+engine.Quote(table.Name)+" ("+engine.Quote(strings.Join(cols, engine.Quote(", ")))+") VALUES (") + _, err = io.WriteString(w, "INSERT INTO "+dialect.Quote(table.Name)+" ("+colNames+") VALUES (") if err != nil { return err } @@ -434,14 +467,22 @@ func (engine *Engine) DumpAll(w io.Writer) error { var temp string for i, d := range dest { col := table.GetColumn(cols[i]) + if col == nil { + return errors.New("unknow column error") + } + if d == nil { temp += ", NULL" } else if col.SQLType.IsText() || col.SQLType.IsTime() { var v = fmt.Sprintf("%s", d) - temp += ", '" + strings.Replace(v, "'", "''", -1) + "'" + if strings.HasSuffix(v, " +0000 UTC") { + temp += fmt.Sprintf(", '%s'", v[0:len(v)-len(" +0000 UTC")]) + } else { + temp += ", '" + strings.Replace(v, "'", "''", -1) + "'" + } } else if col.SQLType.IsBlob() { if reflect.TypeOf(d).Kind() == reflect.Slice { - temp += fmt.Sprintf(", %s", engine.dialect.FormatBytes(d.([]byte))) + temp += fmt.Sprintf(", %s", dialect.FormatBytes(d.([]byte))) } else if reflect.TypeOf(d).Kind() == reflect.String { temp += fmt.Sprintf(", '%s'", d.(string)) } @@ -449,13 +490,29 @@ func (engine *Engine) DumpAll(w io.Writer) error { switch reflect.TypeOf(d).Kind() { case reflect.Slice: temp += fmt.Sprintf(", %s", string(d.([]byte))) + case reflect.Int16, reflect.Int8, reflect.Int32, reflect.Int64, reflect.Int: + if col.SQLType.Name == core.Bool { + temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Int() > 0)) + } else { + temp += fmt.Sprintf(", %v", d) + } + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + if col.SQLType.Name == core.Bool { + temp += fmt.Sprintf(", %v", strconv.FormatBool(reflect.ValueOf(d).Uint() > 0)) + } else { + temp += fmt.Sprintf(", %v", d) + } default: temp += fmt.Sprintf(", %v", d) } } else { s := fmt.Sprintf("%v", d) if strings.Contains(s, ":") || strings.Contains(s, "-") { - temp += fmt.Sprintf(", '%s'", s) + if strings.HasSuffix(s, " +0000 UTC") { + temp += fmt.Sprintf(", '%s'", s[0:len(s)-len(" +0000 UTC")]) + } else { + temp += fmt.Sprintf(", '%s'", s) + } } else { temp += fmt.Sprintf(", %s", s) } @@ -466,11 +523,46 @@ func (engine *Engine) DumpAll(w io.Writer) error { return err } } + + // FIXME: Hack for postgres + if string(dialect.DBType()) == core.POSTGRES && table.AutoIncrColumn() != nil { + _, err = io.WriteString(w, "SELECT setval('table_id_seq', COALESCE((SELECT MAX("+table.AutoIncrColumn().Name+") FROM "+dialect.Quote(table.Name)+"), 1), false);\n") + if err != nil { + return err + } + } } return nil } -// use cascade or not +func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) { + v := rValue(beanOrTableName) + if v.Type().Kind() == reflect.String { + return beanOrTableName.(string), nil + } else if v.Type().Kind() == reflect.Struct { + return engine.tbName(v), nil + } + return "", errors.New("bean should be a struct or struct's point") +} + +func (engine *Engine) tbName(v reflect.Value) string { + if tb, ok := v.Interface().(TableName); ok { + return tb.TableName() + } + + if v.Type().Kind() == reflect.Ptr { + if tb, ok := reflect.Indirect(v).Interface().(TableName); ok { + return tb.TableName() + } + } else if v.CanAddr() { + if tb, ok := v.Addr().Interface().(TableName); ok { + return tb.TableName() + } + } + return engine.TableMapper.Obj2Table(reflect.Indirect(v).Type().Name()) +} + +// Cascade use cascade or not func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { session := engine.NewSession() session.IsAutoClose = true @@ -478,48 +570,55 @@ func (engine *Engine) Cascade(trueOrFalse ...bool) *Session { } // Where method provide a condition query -func (engine *Engine) Where(querystring string, args ...interface{}) *Session { +func (engine *Engine) Where(query interface{}, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true - return session.Where(querystring, args...) + return session.Where(query, args...) } -// Id mehtod provoide a condition as (id) = ? +// Id will be depracated, please use ID instead func (engine *Engine) Id(id interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Id(id) } -// Apply before Processor, affected bean is passed to closure arg +// ID method provoide a condition as (id) = ? +func (engine *Engine) ID(id interface{}) *Session { + session := engine.NewSession() + session.IsAutoClose = true + return session.ID(id) +} + +// Before apply before Processor, affected bean is passed to closure arg func (engine *Engine) Before(closures func(interface{})) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Before(closures) } -// Apply after insert Processor, affected bean is passed to closure arg +// After apply after insert Processor, affected bean is passed to closure arg func (engine *Engine) After(closures func(interface{})) *Session { session := engine.NewSession() session.IsAutoClose = true return session.After(closures) } -// set charset when create table, only support mysql now +// Charset set charset when create table, only support mysql now func (engine *Engine) Charset(charset string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Charset(charset) } -// set store engine when create table, only support mysql now +// StoreEngine set store engine when create table, only support mysql now func (engine *Engine) StoreEngine(storeEngine string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.StoreEngine(storeEngine) } -// use for distinct columns. Caution: when you are using cache, +// Distinct use for distinct columns. Caution: when you are using cache, // distinct will not be cached because cache system need id, // but distinct will not provide id func (engine *Engine) Distinct(columns ...string) *Session { @@ -528,114 +627,116 @@ func (engine *Engine) Distinct(columns ...string) *Session { return session.Distinct(columns...) } +// Select customerize your select columns or contents func (engine *Engine) Select(str string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Select(str) } -// only use the paramters as select or update columns +// Cols only use the parameters as select or update columns func (engine *Engine) Cols(columns ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Cols(columns...) } +// AllCols indicates that all columns should be use func (engine *Engine) AllCols() *Session { session := engine.NewSession() session.IsAutoClose = true return session.AllCols() } +// MustCols specify some columns must use even if they are empty func (engine *Engine) MustCols(columns ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.MustCols(columns...) } -// Xorm automatically retrieve condition according struct, but +// UseBool xorm automatically retrieve condition according struct, but // if struct has bool field, it will ignore them. So use UseBool // to tell system to do not ignore them. -// If no paramters, it will use all the bool field of struct, or -// it will use paramters's columns +// If no parameters, it will use all the bool field of struct, or +// it will use parameters's columns func (engine *Engine) UseBool(columns ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.UseBool(columns...) } -// Only not use the paramters as select or update columns +// Omit only not use the parameters as select or update columns func (engine *Engine) Omit(columns ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Omit(columns...) } -// Set null when column is zero-value and nullable for update +// Nullable set null when column is zero-value and nullable for update func (engine *Engine) Nullable(columns ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Nullable(columns...) } -// This method will generate "column IN (?, ?)" +// In will generate "column IN (?, ?)" func (engine *Engine) In(column string, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.In(column, args...) } -// Method Inc provides a update string like "column = column + ?" +// Incr provides a update string like "column = column + ?" func (engine *Engine) Incr(column string, arg ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Incr(column, arg...) } -// Method Decr provides a update string like "column = column - ?" +// Decr provides a update string like "column = column - ?" func (engine *Engine) Decr(column string, arg ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Decr(column, arg...) } -// Method SetExpr provides a update string like "column = {expression}" +// SetExpr provides a update string like "column = {expression}" func (engine *Engine) SetExpr(column string, expression string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.SetExpr(column, expression) } -// Temporarily change the Get, Find, Update's table +// Table temporarily change the Get, Find, Update's table func (engine *Engine) Table(tableNameOrBean interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Table(tableNameOrBean) } -// set the table alias +// Alias set the table alias func (engine *Engine) Alias(alias string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Alias(alias) } -// This method will generate "LIMIT start, limit" +// Limit will generate "LIMIT start, limit" func (engine *Engine) Limit(limit int, start ...int) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Limit(limit, start...) } -// Method Desc will generate "ORDER BY column1 DESC, column2 DESC" -// This will +// Desc will generate "ORDER BY column1 DESC, column2 DESC" func (engine *Engine) Desc(colNames ...string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Desc(colNames...) } -// Method Asc will generate "ORDER BY column1,column2 Asc" +// Asc will generate "ORDER BY column1,column2 Asc" // This method can chainable use. // // engine.Desc("name").Asc("age").Find(&users) @@ -647,28 +748,28 @@ func (engine *Engine) Asc(colNames ...string) *Session { return session.Asc(colNames...) } -// Method OrderBy will generate "ORDER BY order" +// OrderBy will generate "ORDER BY order" func (engine *Engine) OrderBy(order string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.OrderBy(order) } -// The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (engine *Engine) Join(join_operator string, tablename interface{}, condition string) *Session { +// Join the join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (engine *Engine) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true - return session.Join(join_operator, tablename, condition) + return session.Join(joinOperator, tablename, condition, args...) } -// Generate Group By statement +// GroupBy generate group by statement func (engine *Engine) GroupBy(keys string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.GroupBy(keys) } -// Generate Having statement +// Having generate having statement func (engine *Engine) Having(conditions string) *Session { session := engine.NewSession() session.IsAutoClose = true @@ -678,42 +779,55 @@ func (engine *Engine) Having(conditions string) *Session { func (engine *Engine) autoMapType(v reflect.Value) *core.Table { t := v.Type() engine.mutex.Lock() + defer engine.mutex.Unlock() table, ok := engine.Tables[t] if !ok { - table = engine.mapType(v) - engine.Tables[t] = table - if engine.Cacher != nil { - if v.CanAddr() { - engine.GobRegister(v.Addr().Interface()) - } else { - engine.GobRegister(v.Interface()) + var err error + table, err = engine.mapType(v) + if err != nil { + engine.logger.Error(err) + } else { + engine.Tables[t] = table + if engine.Cacher != nil { + if v.CanAddr() { + engine.GobRegister(v.Addr().Interface()) + } else { + engine.GobRegister(v.Interface()) + } } } } - engine.mutex.Unlock() return table } +// GobRegister register one struct to gob for cache use func (engine *Engine) GobRegister(v interface{}) *Engine { //fmt.Printf("Type: %[1]T => Data: %[1]#v\n", v) gob.Register(v) return engine } -func (engine *Engine) TableInfo(bean interface{}) *core.Table { +// Table table struct +type Table struct { + *core.Table + Name string +} + +// TableInfo get table info according to bean's content +func (engine *Engine) TableInfo(bean interface{}) *Table { v := rValue(bean) - return engine.autoMapType(v) + return &Table{engine.autoMapType(v), engine.tbName(v)} } func addIndex(indexName string, table *core.Table, col *core.Column, indexType int) { if index, ok := table.Indexes[indexName]; ok { index.AddColumn(col.Name) - col.Indexes[index.Name] = true + col.Indexes[index.Name] = indexType } else { index := core.NewIndex(indexName, indexType) index.AddColumn(col.Name) table.AddIndex(index) - col.Indexes[index.Name] = true + col.Indexes[index.Name] = indexType } } @@ -726,23 +840,34 @@ func (engine *Engine) newTable() *core.Table { return table } +// TableName table name interface to define customerize table name type TableName interface { TableName() string } -func (engine *Engine) mapType(v reflect.Value) *core.Table { +var ( + tpTableName = reflect.TypeOf((*TableName)(nil)).Elem() +) + +func (engine *Engine) mapType(v reflect.Value) (*core.Table, error) { t := v.Type() table := engine.newTable() if tb, ok := v.Interface().(TableName); ok { table.Name = tb.TableName() } else { - table.Name = engine.TableMapper.Obj2Table(t.Name()) + if v.CanAddr() { + if tb, ok = v.Addr().Interface().(TableName); ok { + table.Name = tb.TableName() + } + } + if table.Name == "" { + table.Name = engine.TableMapper.Obj2Table(t.Name()) + } } table.Type = t var idFieldColName string - var err error var hasCacheTag, hasNoCacheTag bool for i := 0; i < t.NumField(); i++ { @@ -755,195 +880,101 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { if ormTagStr != "" { col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, - IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)} + IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]int)} tags := splitTag(ormTagStr) if len(tags) > 0 { if tags[0] == "-" { continue } + + var ctx = tagContext{ + table: table, + col: col, + fieldValue: fieldValue, + indexNames: make(map[string]int), + engine: engine, + } + if strings.ToUpper(tags[0]) == "EXTENDS" { - if fieldValue.Kind() == reflect.Struct { - parentTable := engine.mapType(fieldValue) - for _, col := range parentTable.Columns() { - col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName) - table.AddColumn(col) - } - - continue - } else if fieldValue.Kind() == reflect.Ptr { - f := fieldValue.Type().Elem() - if f.Kind() == reflect.Struct { - fieldValue = fieldValue.Elem() - if !fieldValue.IsValid() || fieldValue.IsNil() { - fieldValue = reflect.New(f).Elem() - } - } - - parentTable := engine.mapType(fieldValue) - for _, col := range parentTable.Columns() { - col.FieldName = fmt.Sprintf("%v.%v", t.Field(i).Name, col.FieldName) - table.AddColumn(col) - } - - continue + if err := ExtendsTagHandler(&ctx); err != nil { + return nil, err } - //TODO: warning + continue } - indexNames := make(map[string]int) - var isIndex, isUnique bool - var preKey string for j, key := range tags { + if ctx.ignoreNext { + ctx.ignoreNext = false + continue + } + k := strings.ToUpper(key) - switch { - case k == "<-": - col.MapType = core.ONLYFROMDB - case k == "->": - col.MapType = core.ONLYTODB - case k == "PK": - col.IsPrimaryKey = true - col.Nullable = false - case k == "NULL": - if j == 0 { - col.Nullable = true - } else { - col.Nullable = (strings.ToUpper(tags[j-1]) != "NOT") - } - // TODO: for postgres how add autoincr? - /*case strings.HasPrefix(k, "AUTOINCR(") && strings.HasSuffix(k, ")"): - col.IsAutoIncrement = true + ctx.tagName = k - autoStart := k[len("AUTOINCR")+1 : len(k)-1] - autoStartInt, err := strconv.Atoi(autoStart) - if err != nil { - engine.LogError(err) + pStart := strings.Index(k, "(") + if pStart == 0 { + return nil, errors.New("( could not be the first charactor") } - col.AutoIncrStart = autoStartInt*/ - case k == "AUTOINCR": - col.IsAutoIncrement = true - //col.AutoIncrStart = 1 - case k == "DEFAULT": - col.Default = tags[j+1] - case k == "CREATED": - col.IsCreated = true - case k == "VERSION": - col.IsVersion = true - col.Default = "1" - case k == "UTC": - col.TimeZone = time.UTC - case k == "LOCAL": - col.TimeZone = time.Local - case strings.HasPrefix(k, "LOCALE(") && strings.HasSuffix(k, ")"): - location := k[len("INDEX")+1 : len(k)-1] - col.TimeZone, err = time.LoadLocation(location) - if err != nil { - engine.LogError(err) + if pStart > -1 { + if !strings.HasSuffix(k, ")") { + return nil, errors.New("cannot match ) charactor") } - case k == "UPDATED": - col.IsUpdated = true - case k == "DELETED": - col.IsDeleted = true - case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): - indexName := k[len("INDEX")+1 : len(k)-1] - indexNames[indexName] = core.IndexType - case k == "INDEX": - isIndex = true - case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"): - indexName := k[len("UNIQUE")+1 : len(k)-1] - indexNames[indexName] = core.UniqueType - case k == "UNIQUE": - isUnique = true - case k == "NOTNULL": - col.Nullable = false - case k == "CACHE": - if !hasCacheTag { - hasCacheTag = true - } - case k == "NOCACHE": - if !hasNoCacheTag { - hasNoCacheTag = true - } - case k == "NOT": - default: - if strings.HasPrefix(k, "'") && strings.HasSuffix(k, "'") { - if preKey != "DEFAULT" { - col.Name = key[1 : len(key)-1] - } - } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") { - fs := strings.Split(k, "(") - if _, ok := core.SqlTypes[fs[0]]; !ok { - preKey = k - continue - } - col.SQLType = core.SQLType{fs[0], 0, 0} - if fs[0] == core.Enum && fs[1][0] == '\'' { //enum - options := strings.Split(fs[1][0:len(fs[1])-1], ",") - col.EnumOptions = make(map[string]int) - for k, v := range options { - v = strings.TrimSpace(v) - v = strings.Trim(v, "'") - col.EnumOptions[v] = k - } - } else if fs[0] == core.Set && fs[1][0] == '\'' { //set - options := strings.Split(fs[1][0:len(fs[1])-1], ",") - col.SetOptions = make(map[string]int) - for k, v := range options { - v = strings.TrimSpace(v) - v = strings.Trim(v, "'") - col.SetOptions[v] = k - } - } else { - fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",") - if len(fs2) == 2 { - col.Length, err = strconv.Atoi(fs2[0]) - if err != nil { - engine.LogError(err) - } - col.Length2, err = strconv.Atoi(fs2[1]) - if err != nil { - engine.LogError(err) - } - } else if len(fs2) == 1 { - col.Length, err = strconv.Atoi(fs2[0]) - if err != nil { - engine.LogError(err) - } - } - } - } else { - if _, ok := core.SqlTypes[k]; ok { - col.SQLType = core.SQLType{k, 0, 0} - } else if key != col.Default { - col.Name = key - } - } - engine.dialect.SqlType(col) + ctx.tagName = k[:pStart] + ctx.params = strings.Split(k[pStart+1:len(k)-1], ",") + } + + if j > 0 { + ctx.preTag = strings.ToUpper(tags[j-1]) + } + if j < len(tags)-1 { + ctx.nextTag = strings.ToUpper(tags[j+1]) + } else { + ctx.nextTag = "" + } + + if h, ok := engine.tagHandlers[ctx.tagName]; ok { + if err := h(&ctx); err != nil { + return nil, err + } + } else { + if strings.HasPrefix(key, "'") && strings.HasSuffix(key, "'") { + col.Name = key[1 : len(key)-1] + } else { + col.Name = key + } + } + + if ctx.hasCacheTag { + hasCacheTag = true + } + if ctx.hasNoCacheTag { + hasNoCacheTag = true } - preKey = k } + if col.SQLType.Name == "" { col.SQLType = core.Type2SQLType(fieldType) } + engine.dialect.SqlType(col) if col.Length == 0 { col.Length = col.SQLType.DefaultLength } if col.Length2 == 0 { col.Length2 = col.SQLType.DefaultLength2 } - if col.Name == "" { col.Name = engine.ColumnMapper.Obj2Table(t.Field(i).Name) } - if isUnique { - indexNames[col.Name] = core.UniqueType - } else if isIndex { - indexNames[col.Name] = core.IndexType + if ctx.isUnique { + ctx.indexNames[col.Name] = core.UniqueType + } else if ctx.isIndex { + ctx.indexNames[col.Name] = core.IndexType } - for indexName, indexType := range indexNames { + for indexName, indexType := range ctx.indexNames { addIndex(indexName, table, col, indexType) } } @@ -951,11 +982,11 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { var sqlType core.SQLType if fieldValue.CanAddr() { if _, ok := fieldValue.Addr().Interface().(core.Conversion); ok { - sqlType = core.SQLType{core.Text, 0, 0} + sqlType = core.SQLType{Name: core.Text} } } if _, ok := fieldValue.Interface().(core.Conversion); ok { - sqlType = core.SQLType{core.Text, 0, 0} + sqlType = core.SQLType{Name: core.Text} } else { sqlType = core.Type2SQLType(fieldType) } @@ -985,51 +1016,56 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { if hasCacheTag { if engine.Cacher != nil { // !nash! use engine's cacher if provided - engine.Logger.Info("enable cache on table:", table.Name) + engine.logger.Info("enable cache on table:", table.Name) table.Cacher = engine.Cacher } else { - engine.Logger.Info("enable LRU cache on table:", table.Name) + engine.logger.Info("enable LRU cache on table:", table.Name) table.Cacher = NewLRUCacher2(NewMemoryStore(), time.Hour, 10000) // !nashtsai! HACK use LRU cacher for now } } if hasNoCacheTag { - engine.Logger.Info("no cache on table:", table.Name) + engine.logger.Info("no cache on table:", table.Name) table.Cacher = nil } - return table + return table, nil } -// Map a struct to a table -func (engine *Engine) mapping(beans ...interface{}) (e error) { - engine.mutex.Lock() - defer engine.mutex.Unlock() - for _, bean := range beans { - v := rValue(bean) - engine.Tables[v.Type()] = engine.mapType(v) - } - return -} - -// If a table has any reocrd +// IsTableEmpty if a table has any reocrd func (engine *Engine) IsTableEmpty(bean interface{}) (bool, error) { session := engine.NewSession() defer session.Close() return session.IsTableEmpty(bean) } -// If a table is exist +// IsTableExist if a table is exist func (engine *Engine) IsTableExist(beanOrTableName interface{}) (bool, error) { session := engine.NewSession() defer session.Close() return session.IsTableExist(beanOrTableName) } +// IdOf get id from one struct +// +// Deprecated: use IDOf instead. func (engine *Engine) IdOf(bean interface{}) core.PK { + return engine.IDOf(bean) +} + +// IDOf get id from one struct +func (engine *Engine) IDOf(bean interface{}) core.PK { return engine.IdOfV(reflect.ValueOf(bean)) } +// IdOfV get id from one value of struct +// +// Deprecated: use IDOfV instead. func (engine *Engine) IdOfV(rv reflect.Value) core.PK { + return engine.IDOfV(rv) +} + +// IDOfV get id from one value of struct +func (engine *Engine) IDOfV(rv reflect.Value) core.PK { v := reflect.Indirect(rv) table := engine.autoMapType(v) pk := make([]interface{}, len(table.PrimaryKeys)) @@ -1047,14 +1083,14 @@ func (engine *Engine) IdOfV(rv reflect.Value) core.PK { return core.PK(pk) } -// create indexes +// CreateIndexes create indexes func (engine *Engine) CreateIndexes(bean interface{}) error { session := engine.NewSession() defer session.Close() return session.CreateIndexes(bean) } -// create uniques +// CreateUniques create uniques func (engine *Engine) CreateUniques(bean interface{}) error { session := engine.NewSession() defer session.Close() @@ -1072,39 +1108,43 @@ func (engine *Engine) getCacher(v reflect.Value) core.Cacher { return engine.Cacher } -// If enabled cache, clear the cache bean +// ClearCacheBean if enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { - t := rType(bean) + v := rValue(bean) + t := v.Type() if t.Kind() != reflect.Struct { return errors.New("error params") } - table := engine.TableInfo(bean) + tableName := engine.tbName(v) + table := engine.autoMapType(v) cacher := table.Cacher if cacher == nil { cacher = engine.Cacher } if cacher != nil { - cacher.ClearIds(table.Name) - cacher.DelBean(table.Name, id) + cacher.ClearIds(tableName) + cacher.DelBean(tableName, id) } return nil } -// If enabled cache, clear some tables' cache +// ClearCache if enabled cache, clear some tables' cache func (engine *Engine) ClearCache(beans ...interface{}) error { for _, bean := range beans { - t := rType(bean) + v := rValue(bean) + t := v.Type() if t.Kind() != reflect.Struct { return errors.New("error params") } - table := engine.TableInfo(bean) + tableName := engine.tbName(v) + table := engine.autoMapType(v) cacher := table.Cacher if cacher == nil { cacher = engine.Cacher } if cacher != nil { - cacher.ClearIds(table.Name) - cacher.ClearBeans(table.Name) + cacher.ClearIds(tableName) + cacher.ClearBeans(tableName) } } return nil @@ -1115,11 +1155,13 @@ func (engine *Engine) ClearCache(beans ...interface{}) error { // If you change some field, you should change the database manually. func (engine *Engine) Sync(beans ...interface{}) error { for _, bean := range beans { - table := engine.TableInfo(bean) + v := rValue(bean) + tableName := engine.tbName(v) + table := engine.autoMapType(v) s := engine.NewSession() defer s.Close() - isExist, err := s.Table(bean).isTableExist(table.Name) + isExist, err := s.Table(bean).isTableExist(tableName) if err != nil { return err } @@ -1133,7 +1175,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { if err != nil { return err }*/ - var isEmpty bool = false + var isEmpty bool if isEmpty { err = engine.DropTables(bean) if err != nil { @@ -1145,16 +1187,13 @@ func (engine *Engine) Sync(beans ...interface{}) error { } } else { for _, col := range table.Columns() { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - isExist, err := session.Engine.dialect.IsColumnExist(table.Name, col.Name) + isExist, err := engine.dialect.IsColumnExist(tableName, col.Name) if err != nil { return err } if !isExist { session := engine.NewSession() - session.Statement.RefTable = table + session.Statement.setRefValue(v) defer session.Close() err = session.addColumn(col.Name) if err != nil { @@ -1165,33 +1204,33 @@ func (engine *Engine) Sync(beans ...interface{}) error { for name, index := range table.Indexes { session := engine.NewSession() - session.Statement.RefTable = table + session.Statement.setRefValue(v) defer session.Close() if index.Type == core.UniqueType { //isExist, err := session.isIndexExist(table.Name, name, true) - isExist, err := session.isIndexExist2(table.Name, index.Cols, true) + isExist, err := session.isIndexExist2(tableName, index.Cols, true) if err != nil { return err } if !isExist { session := engine.NewSession() - session.Statement.RefTable = table + session.Statement.setRefValue(v) defer session.Close() - err = session.addUnique(table.Name, name) + err = session.addUnique(tableName, name) if err != nil { return err } } } else if index.Type == core.IndexType { - isExist, err := session.isIndexExist2(table.Name, index.Cols, false) + isExist, err := session.isIndexExist2(tableName, index.Cols, false) if err != nil { return err } if !isExist { session := engine.NewSession() - session.Statement.RefTable = table + session.Statement.setRefValue(v) defer session.Close() - err = session.addIndex(table.Name, name) + err = session.addIndex(tableName, name) if err != nil { return err } @@ -1205,6 +1244,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { return nil } +// Sync2 synchronize structs to database tables func (engine *Engine) Sync2(beans ...interface{}) error { s := engine.NewSession() defer s.Close() @@ -1243,8 +1283,9 @@ func (engine *Engine) dropAll() error { // CreateTables create tabls according bean func (engine *Engine) CreateTables(beans ...interface{}) error { session := engine.NewSession() - err := session.Begin() defer session.Close() + + err := session.Begin() if err != nil { return err } @@ -1259,6 +1300,7 @@ func (engine *Engine) CreateTables(beans ...interface{}) error { return session.Commit() } +// DropTables drop specify tables func (engine *Engine) DropTables(beans ...interface{}) error { session := engine.NewSession() defer session.Close() @@ -1291,7 +1333,7 @@ func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) return session.Exec(sql, args...) } -// Exec a raw sql and return records as []map[string][]byte +// Query a raw sql and return records as []map[string][]byte func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { session := engine.NewSession() defer session.Close() @@ -1305,7 +1347,7 @@ func (engine *Engine) Insert(beans ...interface{}) (int64, error) { return session.Insert(beans...) } -// Insert only one record +// InsertOne insert only one record func (engine *Engine) InsertOne(bean interface{}) (int64, error) { session := engine.NewSession() defer session.Close() @@ -1356,22 +1398,42 @@ func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error { return session.Iterate(bean, fun) } -// Return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields +// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields // are conditions. func (engine *Engine) Rows(bean interface{}) (*Rows, error) { session := engine.NewSession() return session.Rows(bean) } -// Count counts the records. bean's non-empty fields -// are conditions. +// Count counts the records. bean's non-empty fields are conditions. func (engine *Engine) Count(bean interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.Count(bean) } -// Import SQL DDL file +// Sum sum the records by some column. bean's non-empty fields are conditions. +func (engine *Engine) Sum(bean interface{}, colName string) (float64, error) { + session := engine.NewSession() + defer session.Close() + return session.Sum(bean, colName) +} + +// Sums sum the records by some columns. bean's non-empty fields are conditions. +func (engine *Engine) Sums(bean interface{}, colNames ...string) ([]float64, error) { + session := engine.NewSession() + defer session.Close() + return session.Sums(bean, colNames...) +} + +// SumsInt like Sums but return slice of int64 instead of float64. +func (engine *Engine) SumsInt(bean interface{}, colNames ...string) ([]int64, error) { + session := engine.NewSession() + defer session.Close() + return session.SumsInt(bean, colNames...) +} + +// ImportFile SQL DDL file func (engine *Engine) ImportFile(ddlPath string) ([]sql.Result, error) { file, err := os.Open(ddlPath) if err != nil { @@ -1381,7 +1443,7 @@ func (engine *Engine) ImportFile(ddlPath string) ([]sql.Result, error) { return engine.Import(file) } -// Import SQL DDL file +// Import SQL DDL from io.Reader func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) { var results []sql.Result var lastError error @@ -1405,40 +1467,42 @@ func (engine *Engine) Import(r io.Reader) ([]sql.Result, error) { scanner.Split(semiColSpliter) for scanner.Scan() { - query := scanner.Text() - query = strings.Trim(query, " \t") + query := strings.Trim(scanner.Text(), " \t\n\r") if len(query) > 0 { + engine.logSQL(query) result, err := engine.DB().Exec(query) results = append(results, result) if err != nil { - lastError = err + return nil, err + //lastError = err } } } + return results, lastError } -var ( - NULL_TIME time.Time -) - +// TZTime change one time to xorm time location func (engine *Engine) TZTime(t time.Time) time.Time { - if NULL_TIME != t { // if time is not initialized it's not suitable for Time.In() + if !t.IsZero() { // if time is not initialized it's not suitable for Time.In() return t.In(engine.TZLocation) } return t } +// NowTime return current time func (engine *Engine) NowTime(sqlTypeName string) interface{} { t := time.Now() return engine.FormatTime(sqlTypeName, t) } +// NowTime2 return current time func (engine *Engine) NowTime2(sqlTypeName string) (interface{}, time.Time) { t := time.Now() return engine.FormatTime(sqlTypeName, t), t } +// FormatTime format time func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{}) { return engine.formatTime(engine.TZLocation, sqlTypeName, t) } @@ -1457,6 +1521,8 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T return t } if tz != nil { + t = t.In(tz) + } else { t = engine.TZTime(t) } switch sqlTypeName { @@ -1489,7 +1555,7 @@ func (engine *Engine) formatTime(tz *time.Location, sqlTypeName string, t time.T return } -// Always disable struct tag "deleted" +// Unscoped always disable struct tag "deleted" func (engine *Engine) Unscoped() *Session { session := engine.NewSession() session.IsAutoClose = true diff --git a/vendor/github.com/go-xorm/xorm/error.go b/vendor/github.com/go-xorm/xorm/error.go index 61537a34626..2a334f47c23 100644 --- a/vendor/github.com/go-xorm/xorm/error.go +++ b/vendor/github.com/go-xorm/xorm/error.go @@ -9,11 +9,18 @@ import ( ) var ( - ErrParamsType error = errors.New("Params type error") - ErrTableNotFound error = errors.New("Not found table") - ErrUnSupportedType error = errors.New("Unsupported type error") - ErrNotExist error = errors.New("Not exist error") - ErrCacheFailed error = errors.New("Cache failed") - ErrNeedDeletedCond error = errors.New("Delete need at least one condition") - ErrNotImplemented error = errors.New("Not implemented.") + // ErrParamsType params error + ErrParamsType = errors.New("Params type error") + // ErrTableNotFound table not found error + ErrTableNotFound = errors.New("Not found table") + // ErrUnSupportedType unsupported error + ErrUnSupportedType = errors.New("Unsupported type error") + // ErrNotExist record is not exist error + ErrNotExist = errors.New("Not exist error") + // ErrCacheFailed cache failed error + ErrCacheFailed = errors.New("Cache failed") + // ErrNeedDeletedCond delete needs less one condition error + ErrNeedDeletedCond = errors.New("Delete need at least one condition") + // ErrNotImplemented not implemented + ErrNotImplemented = errors.New("Not implemented") ) diff --git a/vendor/github.com/go-xorm/xorm/gen_reserved.sh b/vendor/github.com/go-xorm/xorm/gen_reserved.sh old mode 100644 new mode 100755 diff --git a/vendor/github.com/go-xorm/xorm/goracle_driver.go b/vendor/github.com/go-xorm/xorm/goracle_driver.go deleted file mode 100644 index 9fcde48f059..00000000000 --- a/vendor/github.com/go-xorm/xorm/goracle_driver.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "regexp" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("goracle", &goracleDriver{}) -// } - -type goracleDriver struct { -} - -func (cfg *goracleDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - db := &core.Uri{DbType: core.ORACLE} - dsnPattern := regexp.MustCompile( - `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] - `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] - `\/(?P.*?)` + // /dbname - `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] - matches := dsnPattern.FindStringSubmatch(dataSourceName) - //tlsConfigRegister := make(map[string]*tls.Config) - names := dsnPattern.SubexpNames() - - for i, match := range matches { - switch names[i] { - case "dbname": - db.DbName = match - } - } - if db.DbName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} diff --git a/vendor/github.com/go-xorm/xorm/helpers.go b/vendor/github.com/go-xorm/xorm/helpers.go index 7fa85d11664..398ec679fe0 100644 --- a/vendor/github.com/go-xorm/xorm/helpers.go +++ b/vendor/github.com/go-xorm/xorm/helpers.go @@ -5,6 +5,7 @@ package xorm import ( + "errors" "fmt" "reflect" "sort" @@ -15,6 +16,86 @@ import ( "github.com/go-xorm/core" ) +// str2PK convert string value to primary key value according to tp +func str2PKValue(s string, tp reflect.Type) (reflect.Value, error) { + var err error + var result interface{} + var defReturn = reflect.Zero(tp) + + switch tp.Kind() { + case reflect.Int: + result, err = strconv.Atoi(s) + if err != nil { + return defReturn, fmt.Errorf("convert %s as int: %s", s, err.Error()) + } + case reflect.Int8: + x, err := strconv.Atoi(s) + if err != nil { + return defReturn, fmt.Errorf("convert %s as int8: %s", s, err.Error()) + } + result = int8(x) + case reflect.Int16: + x, err := strconv.Atoi(s) + if err != nil { + return defReturn, fmt.Errorf("convert %s as int16: %s", s, err.Error()) + } + result = int16(x) + case reflect.Int32: + x, err := strconv.Atoi(s) + if err != nil { + return defReturn, fmt.Errorf("convert %s as int32: %s", s, err.Error()) + } + result = int32(x) + case reflect.Int64: + result, err = strconv.ParseInt(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as int64: %s", s, err.Error()) + } + case reflect.Uint: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as uint: %s", s, err.Error()) + } + result = uint(x) + case reflect.Uint8: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as uint8: %s", s, err.Error()) + } + result = uint8(x) + case reflect.Uint16: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as uint16: %s", s, err.Error()) + } + result = uint16(x) + case reflect.Uint32: + x, err := strconv.ParseUint(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as uint32: %s", s, err.Error()) + } + result = uint32(x) + case reflect.Uint64: + result, err = strconv.ParseUint(s, 10, 64) + if err != nil { + return defReturn, fmt.Errorf("convert %s as uint64: %s", s, err.Error()) + } + case reflect.String: + result = s + default: + return defReturn, errors.New("unsupported convert type") + } + return reflect.ValueOf(result).Convert(tp), nil +} + +func str2PK(s string, tp reflect.Type) (interface{}, error) { + v, err := str2PKValue(s, tp) + if err != nil { + return nil, err + } + return v.Interface(), nil +} + func splitTag(tag string) (tags []string) { tag = strings.TrimSpace(tag) var hasQuote = false @@ -30,7 +111,7 @@ func splitTag(tag string) (tags []string) { } } if lastIdx < len(tag) { - tags = append(tags, strings.TrimSpace(tag[lastIdx:len(tag)])) + tags = append(tags, strings.TrimSpace(tag[lastIdx:])) } return } @@ -75,15 +156,55 @@ func isZero(k interface{}) bool { return false } -func int64ToInt(id int64, k reflect.Kind) interface{} { - var v interface{} = id - switch k { +func isStructZero(v reflect.Value) bool { + if !v.IsValid() { + return true + } + + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + switch field.Kind() { + case reflect.Ptr: + field = field.Elem() + fallthrough + case reflect.Struct: + if !isStructZero(field) { + return false + } + default: + if field.CanInterface() && !isZero(field.Interface()) { + return false + } + } + } + return true +} + +func isArrayValueZero(v reflect.Value) bool { + if !v.IsValid() || v.Len() == 0 { + return true + } + + for i := 0; i < v.Len(); i++ { + if !isZero(v.Index(i).Interface()) { + return false + } + } + + return true +} + +func int64ToIntValue(id int64, tp reflect.Type) reflect.Value { + var v interface{} + switch tp.Kind() { case reflect.Int16: v = int16(id) case reflect.Int32: v = int32(id) case reflect.Int: v = int(id) + case reflect.Int64: + v = id case reflect.Uint16: v = uint16(id) case reflect.Uint32: @@ -93,7 +214,11 @@ func int64ToInt(id int64, k reflect.Kind) interface{} { case reflect.Uint: v = uint(id) } - return v + return reflect.ValueOf(v).Convert(tp) +} + +func int64ToInt(id int64, tp reflect.Type) interface{} { + return int64ToIntValue(id, tp).Interface() } func isPKZero(pk core.PK) bool { @@ -105,10 +230,6 @@ func isPKZero(pk core.PK) bool { return false } -func equalNoCase(s1, s2 string) bool { - return strings.ToLower(s1) == strings.ToLower(s2) -} - func indexNoCase(s, sep string) int { return strings.Index(strings.ToLower(s), strings.ToLower(sep)) } @@ -154,6 +275,19 @@ func structName(v reflect.Type) string { return v.Name() } +func col2NewCols(columns ...string) []string { + newColumns := make([]string, 0, len(columns)) + for _, col := range columns { + col = strings.Replace(col, "`", "", -1) + col = strings.Replace(col, `"`, "", -1) + ccols := strings.Split(col, ",") + for _, c := range ccols { + newColumns = append(newColumns, strings.TrimSpace(c)) + } + } + return newColumns +} + func sliceEq(left, right []string) bool { if len(left) != len(right) { return false @@ -188,7 +322,7 @@ func reflect2value(rawValue *reflect.Value) (str string, err error) { default: err = fmt.Errorf("Unsupported struct type %v", vv.Type().Name()) } - //时间类型 + // time type case reflect.Struct: if aa.ConvertibleTo(core.TimeType) { str = vv.Convert(core.TimeType).Interface().(time.Time).Format(time.RFC3339Nano) @@ -342,6 +476,21 @@ func query2(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []m return rows2Strings(rows) } +func setColumnInt(bean interface{}, col *core.Column, t int64) { + v, err := col.ValueOf(bean) + if err != nil { + return + } + if v.CanSet() { + switch v.Type().Kind() { + case reflect.Int, reflect.Int64, reflect.Int32: + v.SetInt(t) + case reflect.Uint, reflect.Uint64, reflect.Uint32: + v.SetUint(uint64(t)) + } + } +} + func setColumnTime(bean interface{}, col *core.Column, t time.Time) { v, err := col.ValueOf(bean) if err != nil { @@ -360,13 +509,12 @@ func setColumnTime(bean interface{}, col *core.Column, t time.Time) { } func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { - colNames := make([]string, 0) - args := make([]interface{}, 0) + colNames := make([]string, 0, len(table.ColumnsSeq())) + args := make([]interface{}, 0, len(table.ColumnsSeq())) for _, col := range table.Columns() { - lColName := strings.ToLower(col.Name) if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { - if _, ok := session.Statement.columnMap[lColName]; !ok { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok { continue } } @@ -376,8 +524,7 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - session.Engine.LogError(err) - continue + return nil, nil, err } fieldValue := *fieldValuePtr @@ -403,25 +550,26 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, } if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[lColName]; !ok { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok { continue } } if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[lColName]; ok { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); ok { continue } } // !evalphobia! set fieldValue as nil when column is nullable and zero-value - if _, ok := session.Statement.nullableMap[lColName]; ok { + if _, ok := getFlagForColumn(session.Statement.nullableMap, col); ok { if col.Nullable && isZero(fieldValue.Interface()) { var nilValue *int fieldValue = reflect.ValueOf(nilValue) } } - if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { + if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime /*&& isZero(fieldValue.Interface())*/ { + // if time is non-empty, then set to auto time val, t := session.Engine.NowTime2(col.SQLType.Name) args = append(args, val) @@ -448,3 +596,27 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, } return colNames, args, nil } + +func indexName(tableName, idxName string) string { + return fmt.Sprintf("IDX_%v_%v", tableName, idxName) +} + +func getFlagForColumn(m map[string]bool, col *core.Column) (val bool, has bool) { + + if len(m) == 0 { + return false, false + } + + n := len(col.Name) + + for mk := range m { + if len(mk) != n { + continue + } + if strings.EqualFold(mk, col.Name) { + return m[mk], true + } + } + + return false, false +} diff --git a/vendor/github.com/go-xorm/xorm/helpers_test.go b/vendor/github.com/go-xorm/xorm/helpers_test.go deleted file mode 100644 index 7d17383d337..00000000000 --- a/vendor/github.com/go-xorm/xorm/helpers_test.go +++ /dev/null @@ -1,22 +0,0 @@ -package xorm - -import "testing" - -func TestSplitTag(t *testing.T) { - var cases = []struct { - tag string - tags []string - }{ - {"not null default '2000-01-01 00:00:00' TIMESTAMP", []string{"not", "null", "default", "'2000-01-01 00:00:00'", "TIMESTAMP"}}, - {"TEXT", []string{"TEXT"}}, - {"default('2000-01-01 00:00:00')", []string{"default('2000-01-01 00:00:00')"}}, - {"json binary", []string{"json", "binary"}}, - } - - for _, kase := range cases { - tags := splitTag(kase.tag) - if !sliceEq(tags, kase.tags) { - t.Fatalf("[%d]%v is not equal [%d]%v", len(tags), tags, len(kase.tags), kase.tags) - } - } -} diff --git a/vendor/github.com/go-xorm/xorm/logger.go b/vendor/github.com/go-xorm/xorm/logger.go index 31787ba0e83..727d030a4cf 100644 --- a/vendor/github.com/go-xorm/xorm/logger.go +++ b/vendor/github.com/go-xorm/xorm/logger.go @@ -12,28 +12,81 @@ import ( "github.com/go-xorm/core" ) +// default log options const ( DEFAULT_LOG_PREFIX = "[xorm]" DEFAULT_LOG_FLAG = log.Ldate | log.Lmicroseconds DEFAULT_LOG_LEVEL = core.LOG_DEBUG ) -type SimpleLogger struct { - DEBUG *log.Logger - ERR *log.Logger - INFO *log.Logger - WARN *log.Logger - level core.LogLevel +var _ core.ILogger = DiscardLogger{} + +// DiscardLogger don't log implementation for core.ILogger +type DiscardLogger struct{} + +// Debug empty implementation +func (DiscardLogger) Debug(v ...interface{}) {} + +// Debugf empty implementation +func (DiscardLogger) Debugf(format string, v ...interface{}) {} + +// Error empty implementation +func (DiscardLogger) Error(v ...interface{}) {} + +// Errorf empty implementation +func (DiscardLogger) Errorf(format string, v ...interface{}) {} + +// Info empty implementation +func (DiscardLogger) Info(v ...interface{}) {} + +// Infof empty implementation +func (DiscardLogger) Infof(format string, v ...interface{}) {} + +// Warn empty implementation +func (DiscardLogger) Warn(v ...interface{}) {} + +// Warnf empty implementation +func (DiscardLogger) Warnf(format string, v ...interface{}) {} + +// Level empty implementation +func (DiscardLogger) Level() core.LogLevel { + return core.LOG_UNKNOWN } +// SetLevel empty implementation +func (DiscardLogger) SetLevel(l core.LogLevel) {} + +// ShowSQL empty implementation +func (DiscardLogger) ShowSQL(show ...bool) {} + +// IsShowSQL empty implementation +func (DiscardLogger) IsShowSQL() bool { + return false +} + +// SimpleLogger is the default implment of core.ILogger +type SimpleLogger struct { + DEBUG *log.Logger + ERR *log.Logger + INFO *log.Logger + WARN *log.Logger + level core.LogLevel + showSQL bool +} + +var _ core.ILogger = &SimpleLogger{} + +// NewSimpleLogger use a special io.Writer as logger output func NewSimpleLogger(out io.Writer) *SimpleLogger { return NewSimpleLogger2(out, DEFAULT_LOG_PREFIX, DEFAULT_LOG_FLAG) } +// NewSimpleLogger2 let you customrize your logger prefix and flag func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger { return NewSimpleLogger3(out, prefix, flag, DEFAULT_LOG_LEVEL) } +// NewSimpleLogger3 let you customrize your logger prefix and flag and logLevel func NewSimpleLogger3(out io.Writer, prefix string, flag int, l core.LogLevel) *SimpleLogger { return &SimpleLogger{ DEBUG: log.New(out, fmt.Sprintf("%s [debug] ", prefix), flag), @@ -44,67 +97,91 @@ func NewSimpleLogger3(out io.Writer, prefix string, flag int, l core.LogLevel) * } } -func (s *SimpleLogger) Err(v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level <= core.LOG_ERR { - s.ERR.Println(v...) +// Error implement core.ILogger +func (s *SimpleLogger) Error(v ...interface{}) { + if s.level <= core.LOG_ERR { + s.ERR.Output(2, fmt.Sprint(v...)) } return } -func (s *SimpleLogger) Errf(format string, v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level <= core.LOG_ERR { - s.ERR.Printf(format, v...) +// Errorf implement core.ILogger +func (s *SimpleLogger) Errorf(format string, v ...interface{}) { + if s.level <= core.LOG_ERR { + s.ERR.Output(2, fmt.Sprintf(format, v...)) } return } -func (s *SimpleLogger) Debug(v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level <= core.LOG_DEBUG { - s.DEBUG.Println(v...) +// Debug implement core.ILogger +func (s *SimpleLogger) Debug(v ...interface{}) { + if s.level <= core.LOG_DEBUG { + s.DEBUG.Output(2, fmt.Sprint(v...)) } return } -func (s *SimpleLogger) Debugf(format string, v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level >= core.LOG_DEBUG { - s.DEBUG.Printf(format, v...) +// Debugf implement core.ILogger +func (s *SimpleLogger) Debugf(format string, v ...interface{}) { + if s.level <= core.LOG_DEBUG { + s.DEBUG.Output(2, fmt.Sprintf(format, v...)) } return } -func (s *SimpleLogger) Info(v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level >= core.LOG_INFO { - s.INFO.Println(v...) +// Info implement core.ILogger +func (s *SimpleLogger) Info(v ...interface{}) { + if s.level <= core.LOG_INFO { + s.INFO.Output(2, fmt.Sprint(v...)) } return } -func (s *SimpleLogger) Infof(format string, v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level >= core.LOG_INFO { - s.INFO.Printf(format, v...) +// Infof implement core.ILogger +func (s *SimpleLogger) Infof(format string, v ...interface{}) { + if s.level <= core.LOG_INFO { + s.INFO.Output(2, fmt.Sprintf(format, v...)) } return } -func (s *SimpleLogger) Warning(v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level >= core.LOG_WARNING { - s.WARN.Println(v...) +// Warn implement core.ILogger +func (s *SimpleLogger) Warn(v ...interface{}) { + if s.level <= core.LOG_WARNING { + s.WARN.Output(2, fmt.Sprint(v...)) } return } -func (s *SimpleLogger) Warningf(format string, v ...interface{}) (err error) { - if s.level > core.LOG_OFF && s.level >= core.LOG_WARNING { - s.WARN.Printf(format, v...) +// Warnf implement core.ILogger +func (s *SimpleLogger) Warnf(format string, v ...interface{}) { + if s.level <= core.LOG_WARNING { + s.WARN.Output(2, fmt.Sprintf(format, v...)) } return } +// Level implement core.ILogger func (s *SimpleLogger) Level() core.LogLevel { return s.level } -func (s *SimpleLogger) SetLevel(l core.LogLevel) (err error) { +// SetLevel implement core.ILogger +func (s *SimpleLogger) SetLevel(l core.LogLevel) { s.level = l return } + +// ShowSQL implement core.ILogger +func (s *SimpleLogger) ShowSQL(show ...bool) { + if len(show) == 0 { + s.showSQL = true + return + } + s.showSQL = show[0] +} + +// IsShowSQL implement core.ILogger +func (s *SimpleLogger) IsShowSQL() bool { + return s.showSQL +} diff --git a/vendor/github.com/go-xorm/xorm/lru_cacher.go b/vendor/github.com/go-xorm/xorm/lru_cacher.go index 855cce2ee40..4a74504351f 100644 --- a/vendor/github.com/go-xorm/xorm/lru_cacher.go +++ b/vendor/github.com/go-xorm/xorm/lru_cacher.go @@ -13,6 +13,7 @@ import ( "github.com/go-xorm/core" ) +// LRUCacher implments cache object facilities type LRUCacher struct { idList *list.List sqlList *list.List @@ -26,10 +27,12 @@ type LRUCacher struct { GcInterval time.Duration } +// NewLRUCacher creates a cacher func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher { return NewLRUCacher2(store, 3600*time.Second, maxElementSize) } +// NewLRUCacher2 creates a cache include different params func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher { cacher := &LRUCacher{store: store, idList: list.New(), sqlList: list.New(), Expired: expired, @@ -41,10 +44,6 @@ func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize return cacher } -//func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher { -// return newLRUCacher(store, expired, maxSize, 0) -//} - // RunGC run once every m.GcInterval func (m *LRUCacher) RunGC() { time.AfterFunc(m.GcInterval, func() { @@ -92,7 +91,7 @@ func (m *LRUCacher) GC() { } } -// Get all bean's ids according to sql and parameter from cache +// GetIds returns all bean's ids according to sql and parameter from cache func (m *LRUCacher) GetIds(tableName, sql string) interface{} { m.mutex.Lock() defer m.mutex.Unlock() @@ -101,7 +100,7 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { } if v, err := m.store.Get(sql); err == nil { if el, ok := m.sqlIndex[tableName][sql]; !ok { - el = m.sqlList.PushBack(newSqlNode(tableName, sql)) + el = m.sqlList.PushBack(newSQLNode(tableName, sql)) m.sqlIndex[tableName][sql] = el } else { lastTime := el.Value.(*sqlNode).lastVisit @@ -114,21 +113,21 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { el.Value.(*sqlNode).lastVisit = time.Now() } return v - } else { - m.delIds(tableName, sql) } + m.delIds(tableName, sql) + return nil } -// Get bean according tableName and id from cache +// GetBean returns bean according tableName and id from cache func (m *LRUCacher) GetBean(tableName string, id string) interface{} { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.idIndex[tableName]; !ok { m.idIndex[tableName] = make(map[string]*list.Element) } - tid := genId(tableName, id) + tid := genID(tableName, id) if v, err := m.store.Get(tid); err == nil { if el, ok := m.idIndex[tableName][id]; ok { lastTime := el.Value.(*idNode).lastVisit @@ -141,19 +140,19 @@ func (m *LRUCacher) GetBean(tableName string, id string) interface{} { m.idList.MoveToBack(el) el.Value.(*idNode).lastVisit = time.Now() } else { - el = m.idList.PushBack(newIdNode(tableName, id)) + el = m.idList.PushBack(newIDNode(tableName, id)) m.idIndex[tableName][id] = el } return v - } else { - // store bean is not exist, then remove memory's index - m.delBean(tableName, id) - //m.clearIds(tableName) - return nil } + + // store bean is not exist, then remove memory's index + m.delBean(tableName, id) + //m.clearIds(tableName) + return nil } -// Clear all sql-ids mapping on table tableName from cache +// clearIds clears all sql-ids mapping on table tableName from cache func (m *LRUCacher) clearIds(tableName string) { if tis, ok := m.sqlIndex[tableName]; ok { for sql, v := range tis { @@ -164,6 +163,7 @@ func (m *LRUCacher) clearIds(tableName string) { m.sqlIndex[tableName] = make(map[string]*list.Element) } +// ClearIds clears all sql-ids mapping on table tableName from cache func (m *LRUCacher) ClearIds(tableName string) { m.mutex.Lock() defer m.mutex.Unlock() @@ -174,19 +174,21 @@ func (m *LRUCacher) clearBeans(tableName string) { if tis, ok := m.idIndex[tableName]; ok { for id, v := range tis { m.idList.Remove(v) - tid := genId(tableName, id) + tid := genID(tableName, id) m.store.Del(tid) } } m.idIndex[tableName] = make(map[string]*list.Element) } +// ClearBeans clears all beans in some table func (m *LRUCacher) ClearBeans(tableName string) { m.mutex.Lock() defer m.mutex.Unlock() m.clearBeans(tableName) } +// PutIds pus ids into table func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { m.mutex.Lock() defer m.mutex.Unlock() @@ -194,7 +196,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { m.sqlIndex[tableName] = make(map[string]*list.Element) } if el, ok := m.sqlIndex[tableName][sql]; !ok { - el = m.sqlList.PushBack(newSqlNode(tableName, sql)) + el = m.sqlList.PushBack(newSQLNode(tableName, sql)) m.sqlIndex[tableName][sql] = el } else { el.Value.(*sqlNode).lastVisit = time.Now() @@ -207,6 +209,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { } } +// PutBean puts beans into table func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { m.mutex.Lock() defer m.mutex.Unlock() @@ -214,13 +217,13 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { var ok bool if el, ok = m.idIndex[tableName][id]; !ok { - el = m.idList.PushBack(newIdNode(tableName, id)) + el = m.idList.PushBack(newIDNode(tableName, id)) m.idIndex[tableName][id] = el } else { el.Value.(*idNode).lastVisit = time.Now() } - m.store.Put(genId(tableName, id), obj) + m.store.Put(genID(tableName, id), obj) if m.idList.Len() > m.MaxElementSize { e := m.idList.Front() node := e.Value.(*idNode) @@ -238,6 +241,7 @@ func (m *LRUCacher) delIds(tableName, sql string) { m.store.Del(sql) } +// DelIds deletes ids func (m *LRUCacher) DelIds(tableName, sql string) { m.mutex.Lock() defer m.mutex.Unlock() @@ -245,7 +249,7 @@ func (m *LRUCacher) DelIds(tableName, sql string) { } func (m *LRUCacher) delBean(tableName string, id string) { - tid := genId(tableName, id) + tid := genID(tableName, id) if el, ok := m.idIndex[tableName][id]; ok { delete(m.idIndex[tableName], id) m.idList.Remove(el) @@ -254,6 +258,7 @@ func (m *LRUCacher) delBean(tableName string, id string) { m.store.Del(tid) } +// DelBean deletes beans in some table func (m *LRUCacher) DelBean(tableName string, id string) { m.mutex.Lock() defer m.mutex.Unlock() @@ -272,18 +277,18 @@ type sqlNode struct { lastVisit time.Time } -func genSqlKey(sql string, args interface{}) string { +func genSQLKey(sql string, args interface{}) string { return fmt.Sprintf("%v-%v", sql, args) } -func genId(prefix string, id string) string { +func genID(prefix string, id string) string { return fmt.Sprintf("%v-%v", prefix, id) } -func newIdNode(tbName string, id string) *idNode { +func newIDNode(tbName string, id string) *idNode { return &idNode{tbName, id, time.Now()} } -func newSqlNode(tbName, sql string) *sqlNode { +func newSQLNode(tbName, sql string) *sqlNode { return &sqlNode{tbName, sql, time.Now()} } diff --git a/vendor/github.com/go-xorm/xorm/memory_store.go b/vendor/github.com/go-xorm/xorm/memory_store.go index 60330995707..36853b19e36 100644 --- a/vendor/github.com/go-xorm/xorm/memory_store.go +++ b/vendor/github.com/go-xorm/xorm/memory_store.go @@ -12,16 +12,18 @@ import ( var _ core.CacheStore = NewMemoryStore() -// memory store +// MemoryStore represents in-memory store type MemoryStore struct { store map[interface{}]interface{} mutex sync.RWMutex } +// NewMemoryStore creates a new store in memory func NewMemoryStore() *MemoryStore { return &MemoryStore{store: make(map[interface{}]interface{})} } +// Put puts object into store func (s *MemoryStore) Put(key string, value interface{}) error { s.mutex.Lock() defer s.mutex.Unlock() @@ -29,6 +31,7 @@ func (s *MemoryStore) Put(key string, value interface{}) error { return nil } +// Get gets object from store func (s *MemoryStore) Get(key string) (interface{}, error) { s.mutex.RLock() defer s.mutex.RUnlock() @@ -39,6 +42,7 @@ func (s *MemoryStore) Get(key string) (interface{}, error) { return nil, ErrNotExist } +// Del deletes object func (s *MemoryStore) Del(key string) error { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/vendor/github.com/go-xorm/xorm/mymysql_driver.go b/vendor/github.com/go-xorm/xorm/mymysql_driver.go deleted file mode 100644 index 53094de9a7c..00000000000 --- a/vendor/github.com/go-xorm/xorm/mymysql_driver.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "strings" - "time" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("mymysql", &mymysqlDriver{}) -// } - -type mymysqlDriver struct { -} - -func (p *mymysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - db := &core.Uri{DbType: core.MYSQL} - - pd := strings.SplitN(dataSourceName, "*", 2) - if len(pd) == 2 { - // Parse protocol part of URI - p := strings.SplitN(pd[0], ":", 2) - if len(p) != 2 { - return nil, errors.New("Wrong protocol part of URI") - } - db.Proto = p[0] - options := strings.Split(p[1], ",") - db.Raddr = options[0] - for _, o := range options[1:] { - kv := strings.SplitN(o, "=", 2) - var k, v string - if len(kv) == 2 { - k, v = kv[0], kv[1] - } else { - k, v = o, "true" - } - switch k { - case "laddr": - db.Laddr = v - case "timeout": - to, err := time.ParseDuration(v) - if err != nil { - return nil, err - } - db.Timeout = to - default: - return nil, errors.New("Unknown option: " + k) - } - } - // Remove protocol part - pd = pd[1:] - } - // Parse database part of URI - dup := strings.SplitN(pd[0], "/", 3) - if len(dup) != 3 { - return nil, errors.New("Wrong database part of URI") - } - db.DbName = dup[0] - db.User = dup[1] - db.Passwd = dup[2] - - return db, nil -} diff --git a/vendor/github.com/go-xorm/xorm/mysql_driver.go b/vendor/github.com/go-xorm/xorm/mysql_driver.go deleted file mode 100644 index 872d854d14b..00000000000 --- a/vendor/github.com/go-xorm/xorm/mysql_driver.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "regexp" - "strings" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("mysql", &mysqlDriver{}) -// } - -type mysqlDriver struct { -} - -func (p *mysqlDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - dsnPattern := regexp.MustCompile( - `^(?:(?P.*?)(?::(?P.*))?@)?` + // [user[:password]@] - `(?:(?P[^\(]*)(?:\((?P[^\)]*)\))?)?` + // [net[(addr)]] - `\/(?P.*?)` + // /dbname - `(?:\?(?P[^\?]*))?$`) // [?param1=value1¶mN=valueN] - matches := dsnPattern.FindStringSubmatch(dataSourceName) - //tlsConfigRegister := make(map[string]*tls.Config) - names := dsnPattern.SubexpNames() - - uri := &core.Uri{DbType: core.MYSQL} - - for i, match := range matches { - switch names[i] { - case "dbname": - uri.DbName = match - case "params": - if len(match) > 0 { - kvs := strings.Split(match, "&") - for _, kv := range kvs { - splits := strings.Split(kv, "=") - if len(splits) == 2 { - switch splits[0] { - case "charset": - uri.Charset = splits[1] - } - } - } - } - - } - } - return uri, nil -} diff --git a/vendor/github.com/go-xorm/xorm/oci8_driver.go b/vendor/github.com/go-xorm/xorm/oci8_driver.go deleted file mode 100644 index f16a2a37d7c..00000000000 --- a/vendor/github.com/go-xorm/xorm/oci8_driver.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "regexp" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("oci8", &oci8Driver{}) -// } - -type oci8Driver struct { -} - -//dataSourceName=user/password@ipv4:port/dbname -//dataSourceName=user/password@[ipv6]:port/dbname -func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - db := &core.Uri{DbType: core.ORACLE} - dsnPattern := regexp.MustCompile( - `^(?P.*)\/(?P.*)@` + // user:password@ - `(?P.*)` + // ip:port - `\/(?P.*)`) // dbname - matches := dsnPattern.FindStringSubmatch(dataSourceName) - names := dsnPattern.SubexpNames() - for i, match := range matches { - switch names[i] { - case "dbname": - db.DbName = match - } - } - if db.DbName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} diff --git a/vendor/github.com/go-xorm/xorm/odbc_driver.go b/vendor/github.com/go-xorm/xorm/odbc_driver.go deleted file mode 100644 index 81b87fde8a8..00000000000 --- a/vendor/github.com/go-xorm/xorm/odbc_driver.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "strings" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("odbc", &odbcDriver{}) -// } - -type odbcDriver struct { -} - -func (p *odbcDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - kv := strings.Split(dataSourceName, ";") - var dbName string - - for _, c := range kv { - vv := strings.Split(strings.TrimSpace(c), "=") - if len(vv) == 2 { - switch strings.ToLower(vv[0]) { - case "database": - dbName = vv[1] - } - } - } - if dbName == "" { - return nil, errors.New("no db name provided") - } - return &core.Uri{DbName: dbName, DbType: core.MSSQL}, nil -} diff --git a/vendor/github.com/go-xorm/xorm/pq_driver.go b/vendor/github.com/go-xorm/xorm/pq_driver.go deleted file mode 100644 index a4e269756b6..00000000000 --- a/vendor/github.com/go-xorm/xorm/pq_driver.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "errors" - "fmt" - "net/url" - "sort" - "strings" - - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("postgres", &pqDriver{}) -// } - -type pqDriver struct { -} - -type values map[string]string - -func (vs values) Set(k, v string) { - vs[k] = v -} - -func (vs values) Get(k string) (v string) { - return vs[k] -} - -func errorf(s string, args ...interface{}) { - panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) -} - -func parseURL(connstr string) (string, error) { - u, err := url.Parse(connstr) - if err != nil { - return "", err - } - - if u.Scheme != "postgresql" && u.Scheme != "postgres" { - return "", fmt.Errorf("invalid connection protocol: %s", u.Scheme) - } - - var kvs []string - escaper := strings.NewReplacer(` `, `\ `, `'`, `\'`, `\`, `\\`) - accrue := func(k, v string) { - if v != "" { - kvs = append(kvs, k+"="+escaper.Replace(v)) - } - } - - if u.User != nil { - v := u.User.Username() - accrue("user", v) - - v, _ = u.User.Password() - accrue("password", v) - } - - i := strings.Index(u.Host, ":") - if i < 0 { - accrue("host", u.Host) - } else { - accrue("host", u.Host[:i]) - accrue("port", u.Host[i+1:]) - } - - if u.Path != "" { - accrue("dbname", u.Path[1:]) - } - - q := u.Query() - for k := range q { - accrue(k, q.Get(k)) - } - - sort.Strings(kvs) // Makes testing easier (not a performance concern) - return strings.Join(kvs, " "), nil -} - -func parseOpts(name string, o values) { - if len(name) == 0 { - return - } - - name = strings.TrimSpace(name) - - ps := strings.Split(name, " ") - for _, p := range ps { - kv := strings.Split(p, "=") - if len(kv) < 2 { - errorf("invalid option: %q", p) - } - o.Set(kv[0], kv[1]) - } -} - -func (p *pqDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - db := &core.Uri{DbType: core.POSTGRES} - o := make(values) - var err error - if strings.HasPrefix(dataSourceName, "postgresql://") || strings.HasPrefix(dataSourceName, "postgres://") { - dataSourceName, err = parseURL(dataSourceName) - if err != nil { - return nil, err - } - } - parseOpts(dataSourceName, o) - - db.DbName = o.Get("dbname") - if db.DbName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} diff --git a/vendor/github.com/go-xorm/xorm/processors.go b/vendor/github.com/go-xorm/xorm/processors.go index 8f95ae3be73..77dd30e5785 100644 --- a/vendor/github.com/go-xorm/xorm/processors.go +++ b/vendor/github.com/go-xorm/xorm/processors.go @@ -4,25 +4,27 @@ package xorm -// Executed before an object is initially persisted to the database +// BeforeInsertProcessor executed before an object is initially persisted to the database type BeforeInsertProcessor interface { BeforeInsert() } -// Executed before an object is updated +// BeforeUpdateProcessor executed before an object is updated type BeforeUpdateProcessor interface { BeforeUpdate() } -// Executed before an object is deleted +// BeforeDeleteProcessor executed before an object is deleted type BeforeDeleteProcessor interface { BeforeDelete() } +// BeforeSetProcessor executed before data set to the struct fields type BeforeSetProcessor interface { BeforeSet(string, Cell) } +// AfterSetProcessor executed after data set to the struct fields type AfterSetProcessor interface { AfterSet(string, Cell) } @@ -34,17 +36,17 @@ type AfterSetProcessor interface { //} // -- -// Executed after an object is persisted to the database +// AfterInsertProcessor executed after an object is persisted to the database type AfterInsertProcessor interface { AfterInsert() } -// Executed after an object has been updated +// AfterUpdateProcessor executed after an object has been updated type AfterUpdateProcessor interface { AfterUpdate() } -// Executed after an object has been deleted +// AfterDeleteProcessor executed after an object has been deleted type AfterDeleteProcessor interface { AfterDelete() } diff --git a/vendor/github.com/go-xorm/xorm/rows.go b/vendor/github.com/go-xorm/xorm/rows.go index fb18454d1dc..a91d08b7791 100644 --- a/vendor/github.com/go-xorm/xorm/rows.go +++ b/vendor/github.com/go-xorm/xorm/rows.go @@ -12,16 +12,16 @@ import ( "github.com/go-xorm/core" ) +// Rows rows wrapper a rows to type Rows struct { NoTypeCheck bool - session *Session - stmt *core.Stmt - rows *core.Rows - fields []string - fieldsCount int - beanType reflect.Type - lastError error + session *Session + stmt *core.Stmt + rows *core.Rows + fields []string + beanType reflect.Type + lastError error } func newRows(session *Session, bean interface{}) (*Rows, error) { @@ -29,13 +29,18 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { rows.session = session rows.beanType = reflect.Indirect(reflect.ValueOf(bean)).Type() - defer rows.session.Statement.Init() + defer rows.session.resetStatement() var sqlStr string var args []interface{} - rows.session.Statement.RefTable = rows.session.Engine.TableInfo(bean) + + rows.session.Statement.setRefValue(rValue(bean)) + if len(session.Statement.TableName()) <= 0 { + return nil, ErrTableNotFound + } + if rows.session.Statement.RawSQL == "" { - sqlStr, args = rows.session.Statement.genGetSql(bean) + sqlStr, args = rows.session.Statement.genGetSQL(bean) } else { sqlStr = rows.session.Statement.RawSQL args = rows.session.Statement.RawParams @@ -45,34 +50,42 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable) } - rows.session.saveLastSQL(sqlStr, args) + rows.session.saveLastSQL(sqlStr, args...) var err error - rows.stmt, err = rows.session.DB().Prepare(sqlStr) - if err != nil { - rows.lastError = err - defer rows.Close() - return nil, err - } + if rows.session.prepareStmt { + rows.stmt, err = rows.session.DB().Prepare(sqlStr) + if err != nil { + rows.lastError = err + rows.Close() + return nil, err + } - rows.rows, err = rows.stmt.Query(args...) - if err != nil { - rows.lastError = err - defer rows.Close() - return nil, err + rows.rows, err = rows.stmt.Query(args...) + if err != nil { + rows.lastError = err + rows.Close() + return nil, err + } + } else { + rows.rows, err = rows.session.DB().Query(sqlStr, args...) + if err != nil { + rows.lastError = err + rows.Close() + return nil, err + } } rows.fields, err = rows.rows.Columns() if err != nil { rows.lastError = err - defer rows.Close() + rows.Close() return nil, err } - rows.fieldsCount = len(rows.fields) return rows, nil } -// move cursor to next record, return false if end has reached +// Next move cursor to next record, return false if end has reached func (rows *Rows) Next() bool { if rows.lastError == nil && rows.rows != nil { hasNext := rows.rows.Next() @@ -89,7 +102,7 @@ func (rows *Rows) Err() error { return rows.lastError } -// scan row record to bean properties +// Scan row record to bean properties func (rows *Rows) Scan(bean interface{}) error { if rows.lastError != nil { return rows.lastError @@ -99,24 +112,14 @@ func (rows *Rows) Scan(bean interface{}) error { return fmt.Errorf("scan arg is incompatible type to [%v]", rows.beanType) } - return rows.session.row2Bean(rows.rows, rows.fields, rows.fieldsCount, bean) + dataStruct := rValue(bean) + rows.session.Statement.setRefValue(dataStruct) + _, err := rows.session.row2Bean(rows.rows, rows.fields, len(rows.fields), bean, &dataStruct, rows.session.Statement.RefTable) - // result, err := row2map(rows.rows, rows.fields) // !nashtsai! TODO remove row2map then scanMapIntoStruct conversation for better performance - // if err == nil { - // err = rows.session.scanMapIntoStruct(bean, result) - // } - // return err + return err } -// // Columns returns the column names. Columns returns an error if the rows are closed, or if the rows are from QueryRow and there was a deferred error. -// func (rows *Rows) Columns() ([]string, error) { -// if rows.lastError == nil && rows.rows != nil { -// return rows.rows.Columns() -// } -// return nil, rows.lastError -// } - -// close session if session.IsAutoClose is true, and claimed any opened resources +// Close session if session.IsAutoClose is true, and claimed any opened resources func (rows *Rows) Close() error { if rows.session.IsAutoClose { defer rows.session.Close() diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go index b7ce7ff6498..0e4cfcf1574 100644 --- a/vendor/github.com/go-xorm/xorm/session.go +++ b/vendor/github.com/go-xorm/xorm/session.go @@ -6,7 +6,6 @@ package xorm import ( "database/sql" - "database/sql/driver" "encoding/json" "errors" "fmt" @@ -19,7 +18,7 @@ import ( "github.com/go-xorm/core" ) -// Struct Session keep a pointer to sql.DB and provides all execution of all +// Session keep a pointer to sql.DB and provides all execution of all // kind of database operations. type Session struct { db *core.DB @@ -54,7 +53,13 @@ type Session struct { lastSQLArgs []interface{} } -// Method Init reset the session as the init status. +// Clone copy all the session's content and return a new session +func (session *Session) Clone() *Session { + var sess = *session + return &sess +} + +// Init reset the session as the init status. func (session *Session) Init() { session.Statement.Init() session.Statement.Engine = session.Engine @@ -75,7 +80,7 @@ func (session *Session) Init() { session.lastSQLArgs = []interface{}{} } -// Method Close release the connection from pool +// Close release the connection from pool func (session *Session) Close() { for _, v := range session.stmtCache { v.Close() @@ -100,44 +105,13 @@ func (session *Session) resetStatement() { } } -// Prepare +// Prepare set a flag to session that should be prepare statement before execute query func (session *Session) Prepare() *Session { session.prepareStmt = true return session } -// Method Sql provides raw sql input parameter. When you have a complex SQL statement -// and cannot use Where, Id, In and etc. Methods to describe, you can use Sql. -func (session *Session) Sql(querystring string, args ...interface{}) *Session { - session.Statement.Sql(querystring, args...) - return session -} - -// Method Where provides custom query condition. -func (session *Session) Where(querystring string, args ...interface{}) *Session { - session.Statement.Where(querystring, args...) - return session -} - -// Method Where provides custom query condition. -func (session *Session) And(querystring string, args ...interface{}) *Session { - session.Statement.And(querystring, args...) - return session -} - -// Method Where provides custom query condition. -func (session *Session) Or(querystring string, args ...interface{}) *Session { - session.Statement.Or(querystring, args...) - return session -} - -// Method Id provides converting id as a query condition -func (session *Session) Id(id interface{}) *Session { - session.Statement.Id(id) - return session -} - -// Apply before Processor, affected bean is passed to closure arg +// Before Apply before Processor, affected bean is passed to closure arg func (session *Session) Before(closures func(interface{})) *Session { if closures != nil { session.beforeClosures = append(session.beforeClosures, closures) @@ -145,7 +119,7 @@ func (session *Session) Before(closures func(interface{})) *Session { return session } -// Apply after Processor, affected bean is passed to closure arg +// After Apply after Processor, affected bean is passed to closure arg func (session *Session) After(closures func(interface{})) *Session { if closures != nil { session.afterClosures = append(session.afterClosures, closures) @@ -153,155 +127,74 @@ func (session *Session) After(closures func(interface{})) *Session { return session } -// Method core.Table can input a string or pointer to struct for special a table to operate. +// Table can input a string or pointer to struct for special a table to operate. func (session *Session) Table(tableNameOrBean interface{}) *Session { session.Statement.Table(tableNameOrBean) return session } -// set the table alias +// Alias set the table alias func (session *Session) Alias(alias string) *Session { session.Statement.Alias(alias) return session } -// Method In provides a query string like "id in (1, 2, 3)" -func (session *Session) In(column string, args ...interface{}) *Session { - session.Statement.In(column, args...) - return session -} - -// Method In provides a query string like "count = count + 1" -func (session *Session) Incr(column string, arg ...interface{}) *Session { - session.Statement.Incr(column, arg...) - return session -} - -// Method Decr provides a query string like "count = count - 1" -func (session *Session) Decr(column string, arg ...interface{}) *Session { - session.Statement.Decr(column, arg...) - return session -} - -// Method SetExpr provides a query string like "column = {expression}" -func (session *Session) SetExpr(column string, expression string) *Session { - session.Statement.SetExpr(column, expression) - return session -} - -// Method Cols provides some columns to special -func (session *Session) Select(str string) *Session { - session.Statement.Select(str) - return session -} - -// Method Cols provides some columns to special -func (session *Session) Cols(columns ...string) *Session { - session.Statement.Cols(columns...) - return session -} - -func (session *Session) AllCols() *Session { - session.Statement.AllCols() - return session -} - -func (session *Session) MustCols(columns ...string) *Session { - session.Statement.MustCols(columns...) - return session -} - +// NoCascade indicate that no cascade load child object func (session *Session) NoCascade() *Session { session.Statement.UseCascade = false return session } -// Xorm automatically retrieve condition according struct, but -// if struct has bool field, it will ignore them. So use UseBool -// to tell system to do not ignore them. -// If no paramters, it will use all the bool field of struct, or -// it will use paramters's columns -func (session *Session) UseBool(columns ...string) *Session { - session.Statement.UseBool(columns...) - return session -} - -// use for distinct columns. Caution: when you are using cache, -// distinct will not be cached because cache system need id, -// but distinct will not provide id -func (session *Session) Distinct(columns ...string) *Session { - session.Statement.Distinct(columns...) - return session -} - -// Set Read/Write locking for UPDATE +// ForUpdate Set Read/Write locking for UPDATE func (session *Session) ForUpdate() *Session { session.Statement.IsForUpdate = true return session } -// Only not use the paramters as select or update columns -func (session *Session) Omit(columns ...string) *Session { - session.Statement.Omit(columns...) - return session -} - -// Set null when column is zero-value and nullable for update -func (session *Session) Nullable(columns ...string) *Session { - session.Statement.Nullable(columns...) - return session -} - -// Method NoAutoTime means do not automatically give created field and updated field -// the current time on the current session temporarily -func (session *Session) NoAutoTime() *Session { - session.Statement.UseAutoTime = false - return session -} - +// NoAutoCondition disable generate SQL condition from beans func (session *Session) NoAutoCondition(no ...bool) *Session { session.Statement.NoAutoCondition(no...) return session } -// Method Limit provide limit and offset query condition +// Limit provide limit and offset query condition func (session *Session) Limit(limit int, start ...int) *Session { session.Statement.Limit(limit, start...) return session } -// Method OrderBy provide order by query condition, the input parameter is the content +// OrderBy provide order by query condition, the input parameter is the content // after order by on a sql statement. func (session *Session) OrderBy(order string) *Session { session.Statement.OrderBy(order) return session } -// Method Desc provide desc order by query condition, the input parameters are columns. +// Desc provide desc order by query condition, the input parameters are columns. func (session *Session) Desc(colNames ...string) *Session { session.Statement.Desc(colNames...) return session } -// Method Asc provide asc order by query condition, the input parameters are columns. +// Asc provide asc order by query condition, the input parameters are columns. func (session *Session) Asc(colNames ...string) *Session { session.Statement.Asc(colNames...) return session } -// Method StoreEngine is only avialble mysql dialect currently +// StoreEngine is only avialble mysql dialect currently func (session *Session) StoreEngine(storeEngine string) *Session { session.Statement.StoreEngine = storeEngine return session } -// Method Charset is only avialble mysql dialect currently +// Charset is only avialble mysql dialect currently func (session *Session) Charset(charset string) *Session { session.Statement.Charset = charset return session } -// Method Cascade indicates if loading sub Struct +// Cascade indicates if loading sub Struct func (session *Session) Cascade(trueOrFalse ...bool) *Session { if len(trueOrFalse) >= 1 { session.Statement.UseCascade = trueOrFalse[0] @@ -309,31 +202,32 @@ func (session *Session) Cascade(trueOrFalse ...bool) *Session { return session } -// Method NoCache ask this session do not retrieve data from cache system and +// NoCache ask this session do not retrieve data from cache system and // get data from database directly. func (session *Session) NoCache() *Session { session.Statement.UseCache = false return session } -//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (session *Session) Join(join_operator string, tablename interface{}, condition string) *Session { - session.Statement.Join(join_operator, tablename, condition) +// Join join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (session *Session) Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *Session { + session.Statement.Join(joinOperator, tablename, condition, args...) return session } -// Generate Group By statement +// GroupBy Generate Group By statement func (session *Session) GroupBy(keys string) *Session { session.Statement.GroupBy(keys) return session } -// Generate Having statement +// Having Generate Having statement func (session *Session) Having(conditions string) *Session { session.Statement.Having(conditions) return session } +// DB db return the wrapper of sql.DB func (session *Session) DB() *core.DB { if session.db == nil { session.db = session.Engine.db @@ -342,338 +236,18 @@ func (session *Session) DB() *core.DB { return session.db } -// Begin a transaction -func (session *Session) Begin() error { - if session.IsAutoCommit { - tx, err := session.DB().Begin() - if err != nil { - return err - } - session.IsAutoCommit = false - session.IsCommitedOrRollbacked = false - session.Tx = tx - session.saveLastSQL("BEGIN TRANSACTION") - } - return nil -} - -// When using transaction, you can rollback if any error -func (session *Session) Rollback() error { - if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.saveLastSQL(session.Engine.dialect.RollBackStr()) - session.IsCommitedOrRollbacked = true - return session.Tx.Rollback() - } - return nil -} - -// When using transaction, Commit will commit all operations. -func (session *Session) Commit() error { - if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.saveLastSQL("COMMIT") - session.IsCommitedOrRollbacked = true - var err error - if err = session.Tx.Commit(); err == nil { - // handle processors after tx committed - - closureCallFunc := func(closuresPtr *[]func(interface{}), bean interface{}) { - - if closuresPtr != nil { - for _, closure := range *closuresPtr { - closure(bean) - } - } - } - - for bean, closuresPtr := range session.afterInsertBeans { - closureCallFunc(closuresPtr, bean) - - if processor, ok := interface{}(bean).(AfterInsertProcessor); ok { - processor.AfterInsert() - } - } - for bean, closuresPtr := range session.afterUpdateBeans { - closureCallFunc(closuresPtr, bean) - - if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { - processor.AfterUpdate() - } - } - for bean, closuresPtr := range session.afterDeleteBeans { - closureCallFunc(closuresPtr, bean) - - if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { - processor.AfterDelete() - } - } - cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) { - if len(*slices) > 0 { - *slices = make(map[interface{}]*[]func(interface{}), 0) - } - } - cleanUpFunc(&session.afterInsertBeans) - cleanUpFunc(&session.afterUpdateBeans) - cleanUpFunc(&session.afterDeleteBeans) - } - return err - } - return nil -} - func cleanupProcessorsClosures(slices *[]func(interface{})) { if len(*slices) > 0 { *slices = make([]func(interface{}), 0) } } -func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]byte) error { - dataStruct := rValue(obj) - if dataStruct.Kind() != reflect.Struct { - return errors.New("Expected a pointer to a struct") - } - - var col *core.Column - table := session.Engine.autoMapType(dataStruct) - - for key, data := range objMap { - if col = table.GetColumn(key); col == nil { - session.Engine.LogWarn(fmt.Sprintf("struct %v's has not field %v. %v", - table.Type.Name(), key, table.ColumnsSeq())) - continue - } - - fieldName := col.FieldName - fieldPath := strings.Split(fieldName, ".") - var fieldValue reflect.Value - if len(fieldPath) > 2 { - session.Engine.LogError("Unsupported mutliderive", fieldName) - continue - } else if len(fieldPath) == 2 { - parentField := dataStruct.FieldByName(fieldPath[0]) - if parentField.IsValid() { - fieldValue = parentField.FieldByName(fieldPath[1]) - } - } else { - fieldValue = dataStruct.FieldByName(fieldName) - } - if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.Engine.LogWarn("table %v's column %v is not valid or cannot set", - table.Name, key) - continue - } - - err := session.bytes2Value(col, &fieldValue, data) - if err != nil { - return err - } - } - - return nil -} - -//Execute sql -func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) { - if session.prepareStmt { - stmt, err := session.doPrepare(sqlStr) - if err != nil { - return nil, err - } - - res, err := stmt.Exec(args...) - if err != nil { - return nil, err - } - return res, nil - } - - return session.DB().Exec(sqlStr, args...) -} - -func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { - for _, filter := range session.Engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) - } - - session.saveLastSQL(sqlStr, args...) - - return session.Engine.LogSQLExecutionTime(sqlStr, args, func() (sql.Result, error) { - if session.IsAutoCommit { - //oci8 can not auto commit (github.com/mattn/go-oci8) - if session.Engine.dialect.DBType() == core.ORACLE { - session.Begin() - r, err := session.Tx.Exec(sqlStr, args...) - session.Commit() - return r, err - } - return session.innerExec(sqlStr, args...) - } - return session.Tx.Exec(sqlStr, args...) - }) -} - -// Exec raw sql -func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.exec(sqlStr, args...) -} - -// this function create a table according a bean -func (session *Session) CreateTable(bean interface{}) error { - session.Statement.RefTable = session.Engine.TableInfo(bean) - - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.createOneTable() -} - -// create indexes -func (session *Session) CreateIndexes(bean interface{}) error { - session.Statement.RefTable = session.Engine.TableInfo(bean) - - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - sqls := session.Statement.genIndexSQL() - for _, sqlStr := range sqls { - _, err := session.exec(sqlStr) - if err != nil { - return err - } - } - return nil -} - -// create uniques -func (session *Session) CreateUniques(bean interface{}) error { - session.Statement.RefTable = session.Engine.TableInfo(bean) - - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - sqls := session.Statement.genUniqueSQL() - for _, sqlStr := range sqls { - _, err := session.exec(sqlStr) - if err != nil { - return err - } - } - return nil -} - -func (session *Session) createOneTable() error { - sqlStr := session.Statement.genCreateTableSQL() - session.Engine.LogDebug("create table sql: [", sqlStr, "]") - _, err := session.exec(sqlStr) - return err -} - -// to be deleted -func (session *Session) createAll() error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - for _, table := range session.Engine.Tables { - session.Statement.RefTable = table - err := session.createOneTable() - if err != nil { - return err - } - } - return nil -} - -// drop indexes -func (session *Session) DropIndexes(bean interface{}) error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - sqls := session.Statement.genDelIndexSQL() - for _, sqlStr := range sqls { - _, err := session.exec(sqlStr) - if err != nil { - return err - } - } - return nil -} - -// drop table will drop table if exist, if drop failed, it will return error -func (session *Session) DropTable(beanOrTableName interface{}) error { - tableName, err := session.Engine.tableName(beanOrTableName) - if err != nil { - return err - } - - var needDrop = true - if !session.Engine.dialect.SupportDropIfExists() { - sqlStr, args := session.Engine.dialect.TableCheckSql(tableName) - results, err := session.query(sqlStr, args...) - if err != nil { - return err - } - needDrop = len(results) > 0 - } - - if needDrop { - sqlStr := session.Engine.Dialect().DropTableSql(tableName) - _, err = session.exec(sqlStr) - return err - } - return nil -} - -func (statement *Statement) JoinColumns(cols []*core.Column, includeTableName bool) string { - var colnames = make([]string, len(cols)) - for i, col := range cols { - if includeTableName { - colnames[i] = statement.Engine.Quote(statement.TableName()) + - "." + statement.Engine.Quote(col.Name) - } else { - colnames[i] = statement.Engine.Quote(col.Name) - } - } - return strings.Join(colnames, ", ") -} - -func (statement *Statement) convertIdSql(sqlStr string) string { - if statement.RefTable != nil { - cols := statement.RefTable.PKColumns() - if len(cols) == 0 { - return "" - } - - colstrs := statement.JoinColumns(cols, false) - sqls := splitNNoCase(sqlStr, " from ", 2) - if len(sqls) != 2 { - return "" - } - if statement.Engine.dialect.DBType() == "ql" { - return fmt.Sprintf("SELECT id() FROM %v", sqls[1]) - } - return fmt.Sprintf("SELECT %s FROM %v", colstrs, sqls[1]) - } - return "" -} - func (session *Session) canCache() bool { if session.Statement.RefTable == nil || session.Statement.JoinStr != "" || session.Statement.RawSQL != "" || + !session.Statement.UseCache || + session.Statement.IsForUpdate || session.Tx != nil || len(session.Statement.selectStr) > 0 { return false @@ -681,330 +255,6 @@ func (session *Session) canCache() bool { return true } -func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { - // if has no reftable, then don't use cache currently - if !session.canCache() { - return false, ErrCacheFailed - } - - for _, filter := range session.Engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) - } - newsql := session.Statement.convertIdSql(sqlStr) - if newsql == "" { - return false, ErrCacheFailed - } - - cacher := session.Engine.getCacher2(session.Statement.RefTable) - tableName := session.Statement.TableName() - session.Engine.LogDebug("[cacheGet] find sql:", newsql, args) - ids, err := core.GetCacheSql(cacher, tableName, newsql, args) - table := session.Statement.RefTable - if err != nil { - var res = make([]string, len(table.PrimaryKeys)) - rows, err := session.DB().Query(newsql, args...) - if err != nil { - return false, err - } - defer rows.Close() - - if rows.Next() { - err = rows.ScanSlice(&res) - if err != nil { - return false, err - } - } else { - return false, ErrCacheFailed - } - - var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) - for i, col := range table.PKColumns() { - if col.SQLType.IsText() { - pk[i] = res[i] - } else if col.SQLType.IsNumeric() { - n, err := strconv.ParseInt(res[i], 10, 64) - if err != nil { - return false, err - } - pk[i] = n - } else { - return false, errors.New("unsupported") - } - } - - ids = []core.PK{pk} - session.Engine.LogDebug("[cacheGet] cache ids:", newsql, ids) - err = core.PutCacheSql(cacher, ids, tableName, newsql, args) - if err != nil { - return false, err - } - } else { - session.Engine.LogDebug("[cacheGet] cache hit sql:", newsql) - } - - if len(ids) > 0 { - structValue := reflect.Indirect(reflect.ValueOf(bean)) - id := ids[0] - session.Engine.LogDebug("[cacheGet] get bean:", tableName, id) - sid, err := id.ToString() - if err != nil { - return false, err - } - cacheBean := cacher.GetBean(tableName, sid) - if cacheBean == nil { - newSession := session.Engine.NewSession() - defer newSession.Close() - cacheBean = reflect.New(structValue.Type()).Interface() - newSession.Id(id).NoCache() - if session.Statement.AltTableName != "" { - newSession.Table(session.Statement.AltTableName) - } - if !session.Statement.UseCascade { - newSession.NoCascade() - } - has, err = newSession.Get(cacheBean) - if err != nil || !has { - return has, err - } - - session.Engine.LogDebug("[cacheGet] cache bean:", tableName, id, cacheBean) - cacher.PutBean(tableName, sid, cacheBean) - } else { - session.Engine.LogDebug("[cacheGet] cache hit bean:", tableName, id, cacheBean) - has = true - } - structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean))) - - return has, nil - } - return false, nil -} - -func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) { - if !session.canCache() || - indexNoCase(sqlStr, "having") != -1 || - indexNoCase(sqlStr, "group by") != -1 { - return ErrCacheFailed - } - - for _, filter := range session.Engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) - } - - newsql := session.Statement.convertIdSql(sqlStr) - if newsql == "" { - return ErrCacheFailed - } - - table := session.Statement.RefTable - cacher := session.Engine.getCacher2(table) - ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args) - if err != nil { - rows, err := session.DB().Query(newsql, args...) - if err != nil { - return err - } - defer rows.Close() - - var i int - ids = make([]core.PK, 0) - for rows.Next() { - i++ - if i > 500 { - session.Engine.LogDebug("[cacheFind] ids length > 500, no cache") - return ErrCacheFailed - } - var res = make([]string, len(table.PrimaryKeys)) - err = rows.ScanSlice(&res) - if err != nil { - return err - } - - var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) - for i, col := range table.PKColumns() { - if col.SQLType.IsNumeric() { - n, err := strconv.ParseInt(res[i], 10, 64) - if err != nil { - return err - } - pk[i] = n - } else if col.SQLType.IsText() { - pk[i] = res[i] - } else { - return errors.New("not supported") - } - } - - ids = append(ids, pk) - } - - tableName := session.Statement.TableName() - - session.Engine.LogDebug("[cacheFind] cache sql:", ids, tableName, newsql, args) - err = core.PutCacheSql(cacher, ids, tableName, newsql, args) - if err != nil { - return err - } - } else { - session.Engine.LogDebug("[cacheFind] cache hit sql:", newsql, args) - } - - sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - - ididxes := make(map[string]int) - var ides []core.PK = make([]core.PK, 0) - var temps []interface{} = make([]interface{}, len(ids)) - tableName := session.Statement.TableName() - for idx, id := range ids { - sid, err := id.ToString() - if err != nil { - return err - } - bean := cacher.GetBean(tableName, sid) - if bean == nil { - ides = append(ides, id) - ididxes[sid] = idx - } else { - session.Engine.LogDebug("[cacheFind] cache hit bean:", tableName, id, bean) - - pk := session.Engine.IdOf(bean) - xid, err := pk.ToString() - if err != nil { - return err - } - - if sid != xid { - session.Engine.LogError("[cacheFind] error cache", xid, sid, bean) - return ErrCacheFailed - } - temps[idx] = bean - } - } - - if len(ides) > 0 { - newSession := session.Engine.NewSession() - defer newSession.Close() - - slices := reflect.New(reflect.SliceOf(t)) - beans := slices.Interface() - - if len(table.PrimaryKeys) == 1 { - ff := make([]interface{}, 0) - for _, ie := range ides { - ff = append(ff, ie[0]) - } - - newSession.In(table.PrimaryKeys[0], ff...) - } else { - var kn = make([]string, 0) - for _, name := range table.PrimaryKeys { - kn = append(kn, name+" = ?") - } - condi := "(" + strings.Join(kn, " AND ") + ")" - for _, ie := range ides { - newSession.Or(condi, ie...) - } - } - - err = newSession.NoCache().Find(beans) - if err != nil { - return err - } - - vs := reflect.Indirect(reflect.ValueOf(beans)) - for i := 0; i < vs.Len(); i++ { - rv := vs.Index(i) - if rv.Kind() != reflect.Ptr { - rv = rv.Addr() - } - bean := rv.Interface() - id := session.Engine.IdOf(bean) - sid, err := id.ToString() - if err != nil { - return err - } - - temps[ididxes[sid]] = bean - session.Engine.LogDebug("[cacheFind] cache bean:", tableName, id, bean, temps) - cacher.PutBean(tableName, sid, bean) - } - } - - for j := 0; j < len(temps); j++ { - bean := temps[j] - if bean == nil { - session.Engine.LogWarn("[cacheFind] cache no hit:", tableName, ids[j], temps) - // return errors.New("cache error") // !nashtsai! no need to return error, but continue instead - continue - } - if sliceValue.Kind() == reflect.Slice { - if t.Kind() == reflect.Ptr { - sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(bean))) - } else { - sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean)))) - } - } else if sliceValue.Kind() == reflect.Map { - var key core.PK = ids[j] - keyType := sliceValue.Type().Key() - var ikey interface{} - if len(key) == 1 { - ikey, err = Atot(fmt.Sprintf("%v", key[0]), keyType) - if err != nil { - return err - } - } else { - if keyType.Kind() != reflect.Slice { - return errors.New("table have multiple primary keys, key is not core.PK or slice") - } - ikey = key - } - - if t.Kind() == reflect.Ptr { - sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean)) - } else { - sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean))) - } - } - } - - return nil -} - -// IterFunc only use by Iterate -type IterFunc func(idx int, bean interface{}) error - -// Return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields -// are conditions. -func (session *Session) Rows(bean interface{}) (*Rows, error) { - return newRows(session, bean) -} - -// Iterate record by record handle records from table, condiBeans's non-empty fields -// are conditions. beans could be []Struct, []*Struct, map[int64]Struct -// map[int64]*Struct -func (session *Session) Iterate(bean interface{}, fun IterFunc) error { - rows, err := session.Rows(bean) - if err != nil { - return err - } - defer rows.Close() - //b := reflect.New(iterator.beanType).Interface() - i := 0 - for rows.Next() { - b := reflect.New(rows.beanType).Interface() - err = rows.Scan(b) - if err != nil { - return err - } - err = fun(i, b) - if err != nil { - return err - } - i++ - } - return err -} - func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { crc := crc32.ChecksumIEEE([]byte(sqlStr)) // TODO try hash(sqlStr+len(sqlStr)) @@ -1020,637 +270,57 @@ func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { return } -// get retrieve one record from database, bean's non-empty fields -// will be as conditions -func (session *Session) Get(bean interface{}) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - session.Statement.Limit(1) - var sqlStr string - var args []interface{} - - if session.Statement.RefTable == nil { - session.Statement.RefTable = session.Engine.TableInfo(bean) - } - - if session.Statement.RawSQL == "" { - sqlStr, args = session.Statement.genGetSql(bean) - } else { - sqlStr = session.Statement.RawSQL - args = session.Statement.RawParams - } - - if session.Statement.JoinStr == "" { - if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && - session.Statement.UseCache && - !session.Statement.unscoped { - has, err := session.cacheGet(bean, sqlStr, args...) - if err != ErrCacheFailed { - return has, err - } - } - } - - var rawRows *core.Rows - var err error - session.queryPreprocess(&sqlStr, args...) - if session.IsAutoCommit { - if session.prepareStmt { - stmt, errPrepare := session.doPrepare(sqlStr) - if errPrepare != nil { - return false, errPrepare - } - // defer stmt.Close() // !nashtsai! don't close due to stmt is cached and bounded to this session - rawRows, err = stmt.Query(args...) - } else { - rawRows, err = session.DB().Query(sqlStr, args...) - } - } else { - rawRows, err = session.Tx.Query(sqlStr, args...) - } - if err != nil { - return false, err - } - - defer rawRows.Close() - - if rawRows.Next() { - if fields, err := rawRows.Columns(); err == nil { - err = session.row2Bean(rawRows, fields, len(fields), bean) - } - return true, err - } - return false, nil -} - -// Count counts the records. bean's non-empty fields -// are conditions. -func (session *Session) Count(bean interface{}) (int64, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - var sqlStr string - var args []interface{} - if session.Statement.RawSQL == "" { - sqlStr, args = session.Statement.genCountSql(bean) - } else { - sqlStr = session.Statement.RawSQL - args = session.Statement.RawParams - } - - resultsSlice, err := session.query(sqlStr, args...) - if err != nil { - return 0, err - } - - var total int64 = 0 - if len(resultsSlice) > 0 { - results := resultsSlice[0] - for _, value := range results { - total, err = strconv.ParseInt(string(value), 10, 64) - break - } - } - - return int64(total), err -} - -func Atot(s string, tp reflect.Type) (interface{}, error) { - var err error - var result interface{} - switch tp.Kind() { - case reflect.Int: - result, err = strconv.Atoi(s) - if err != nil { - return nil, errors.New("convert " + s + " as int: " + err.Error()) - } - case reflect.Int8: - x, err := strconv.Atoi(s) - if err != nil { - return nil, errors.New("convert " + s + " as int16: " + err.Error()) - } - result = int8(x) - case reflect.Int16: - x, err := strconv.Atoi(s) - if err != nil { - return nil, errors.New("convert " + s + " as int16: " + err.Error()) - } - result = int16(x) - case reflect.Int32: - x, err := strconv.Atoi(s) - if err != nil { - return nil, errors.New("convert " + s + " as int32: " + err.Error()) - } - result = int32(x) - case reflect.Int64: - result, err = strconv.ParseInt(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as int64: " + err.Error()) - } - case reflect.Uint: - x, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as uint: " + err.Error()) - } - result = uint(x) - case reflect.Uint8: - x, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as uint8: " + err.Error()) - } - result = uint8(x) - case reflect.Uint16: - x, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as uint16: " + err.Error()) - } - result = uint16(x) - case reflect.Uint32: - x, err := strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as uint32: " + err.Error()) - } - result = uint32(x) - case reflect.Uint64: - result, err = strconv.ParseUint(s, 10, 64) - if err != nil { - return nil, errors.New("convert " + s + " as uint64: " + err.Error()) - } - case reflect.String: - result = s - default: - panic("unsupported convert type") - } - return result, nil -} - -// Find retrieve records from table, condiBeans's non-empty fields -// are conditions. beans could be []Struct, []*Struct, map[int64]Struct -// map[int64]*Struct -func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { - return errors.New("needs a pointer to a slice or a map") - } - - sliceElementType := sliceValue.Type().Elem() - var table *core.Table - if session.Statement.RefTable == nil { - if sliceElementType.Kind() == reflect.Ptr { - if sliceElementType.Elem().Kind() == reflect.Struct { - pv := reflect.New(sliceElementType.Elem()) - table = session.Engine.autoMapType(pv.Elem()) - } else { - return errors.New("slice type") - } - } else if sliceElementType.Kind() == reflect.Struct { - pv := reflect.New(sliceElementType) - table = session.Engine.autoMapType(pv.Elem()) - } else { - return errors.New("slice type") - } - session.Statement.RefTable = table - } else { - table = session.Statement.RefTable - } - - var addedTableName = (len(session.Statement.JoinStr) > 0) - if !session.Statement.noAutoCondition && len(condiBean) > 0 { - colNames, args := session.Statement.buildConditions(table, condiBean[0], true, true, false, true, addedTableName) - session.Statement.ConditionStr = strings.Join(colNames, " AND ") - session.Statement.BeanArgs = args - } else { - // !oinume! Add " IS NULL" to WHERE whatever condiBean is given. - // See https://github.com/go-xorm/xorm/issues/179 - if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled - var colName string = session.Engine.Quote(col.Name) - if addedTableName { - var nm = session.Statement.TableName() - if len(session.Statement.TableAlias) > 0 { - nm = session.Statement.TableAlias - } - colName = session.Engine.Quote(nm) + "." + colName - } - session.Statement.ConditionStr = fmt.Sprintf("%v IS NULL OR %v = '0001-01-01 00:00:00'", - colName, colName) - } - } - - var sqlStr string - var args []interface{} - if session.Statement.RawSQL == "" { - var columnStr string = session.Statement.ColumnStr - if len(session.Statement.selectStr) > 0 { - columnStr = session.Statement.selectStr - } else { - if session.Statement.JoinStr == "" { - if columnStr == "" { - if session.Statement.GroupByStr != "" { - columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) - } else { - columnStr = session.Statement.genColumnStr() - } - } - } else { - if columnStr == "" { - if session.Statement.GroupByStr != "" { - columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) - } else { - columnStr = "*" - } - } - } - } - - session.Statement.attachInSql() - - sqlStr = session.Statement.genSelectSql(columnStr) - args = append(session.Statement.Params, session.Statement.BeanArgs...) - // for mssql and use limit - qs := strings.Count(sqlStr, "?") - if len(args)*2 == qs { - args = append(args, args...) - } - } else { - sqlStr = session.Statement.RawSQL - args = session.Statement.RawParams - } - - var err error - if session.Statement.JoinStr == "" { - if cacher := session.Engine.getCacher2(table); cacher != nil && - session.Statement.UseCache && - !session.Statement.IsDistinct && - !session.Statement.unscoped { - err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) - if err != ErrCacheFailed { - return err - } - err = nil // !nashtsai! reset err to nil for ErrCacheFailed - session.Engine.LogWarn("Cache Find Failed") - } - } - - if sliceValue.Kind() != reflect.Map { - var rawRows *core.Rows - var stmt *core.Stmt - - session.queryPreprocess(&sqlStr, args...) - - if session.IsAutoCommit { - if session.prepareStmt { - stmt, err = session.doPrepare(sqlStr) - if err != nil { - return err - } - rawRows, err = stmt.Query(args...) - } else { - rawRows, err = session.DB().Query(sqlStr, args...) - } - } else { - rawRows, err = session.Tx.Query(sqlStr, args...) - } - if err != nil { - return err - } - defer rawRows.Close() - - fields, err := rawRows.Columns() - if err != nil { - return err - } - - fieldsCount := len(fields) - - var newElemFunc func() reflect.Value - if sliceElementType.Kind() == reflect.Ptr { - newElemFunc = func() reflect.Value { - return reflect.New(sliceElementType.Elem()) - } - } else { - newElemFunc = func() reflect.Value { - return reflect.New(sliceElementType) - } - } - - var sliceValueSetFunc func(*reflect.Value) - - if sliceValue.Kind() == reflect.Slice { - if sliceElementType.Kind() == reflect.Ptr { - sliceValueSetFunc = func(newValue *reflect.Value) { - sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface()))) - } - } else { - sliceValueSetFunc = func(newValue *reflect.Value) { - sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) - } - } - } - - var newValue reflect.Value = newElemFunc() - dataStruct := rValue(newValue.Interface()) - if dataStruct.Kind() != reflect.Struct { - return errors.New("Expected a pointer to a struct") - } - - table := session.Engine.autoMapType(dataStruct) - return session.rows2Beans(rawRows, fields, fieldsCount, table, newElemFunc, sliceValueSetFunc) - } else { - resultsSlice, err := session.query(sqlStr, args...) - if err != nil { - return err - } - - keyType := sliceValue.Type().Key() - - for _, results := range resultsSlice { - var newValue reflect.Value - if sliceElementType.Kind() == reflect.Ptr { - newValue = reflect.New(sliceElementType.Elem()) - } else { - newValue = reflect.New(sliceElementType) - } - err := session.scanMapIntoStruct(newValue.Interface(), results) - if err != nil { - return err - } - var key interface{} - // if there is only one pk, we can put the id as map key. - if len(table.PrimaryKeys) == 1 { - key, err = Atot(string(results[table.PrimaryKeys[0]]), keyType) - if err != nil { - return err - } - } else { - if keyType.Kind() != reflect.Slice { - panic("don't support multiple primary key's map has non-slice key type") - } else { - keys := core.PK{} - for _, pk := range table.PrimaryKeys { - skey, err := Atot(string(results[pk]), keyType) - if err != nil { - return err - } - keys = append(keys, skey) - } - key = keys - } - } - - if sliceElementType.Kind() == reflect.Ptr { - sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(newValue.Interface())) - } else { - sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.Indirect(reflect.ValueOf(newValue.Interface()))) - } - } - } - return nil -} - -// Test if database is ok -func (session *Session) Ping() error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.DB().Ping() -} - -/* -func (session *Session) isColumnExist(tableName string, col *core.Column) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - return session.Engine.dialect.IsColumnExist(tableName, col) - //sqlStr, args := session.Engine.dialect.ColumnCheckSql(tableName, colName) - //results, err := session.query(sqlStr, args...) - //return len(results) > 0, err -}*/ - -func (engine *Engine) tableName(beanOrTableName interface{}) (string, error) { - v := rValue(beanOrTableName) - if v.Type().Kind() == reflect.String { - return beanOrTableName.(string), nil - } else if v.Type().Kind() == reflect.Struct { - table := engine.autoMapType(v) - return table.Name, nil - } - return "", errors.New("bean should be a struct or struct's point") -} - -func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) { - tableName, err := session.Engine.tableName(beanOrTableName) - if err != nil { - return false, err - } - - return session.isTableExist(tableName) -} - -func (session *Session) isTableExist(tableName string) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - sqlStr, args := session.Engine.dialect.TableCheckSql(tableName) - results, err := session.query(sqlStr, args...) - return len(results) > 0, err -} - -func (session *Session) IsTableEmpty(bean interface{}) (bool, error) { - v := rValue(bean) - t := v.Type() - - if t.Kind() == reflect.String { - return session.isTableEmpty(bean.(string)) - } else if t.Kind() == reflect.Struct { - session.Engine.autoMapType(v) - rows, err := session.Count(bean) - return rows == 0, err - } - return false, errors.New("bean should be a struct or struct's point") -} - -func (session *Session) isTableEmpty(tableName string) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - var total int64 - sql := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName)) - err := session.DB().QueryRow(sql).Scan(&total) - session.saveLastSQL(sql) - if err != nil { - return true, err - } - - return total == 0, nil -} - -func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - var idx string - if unique { - idx = uniqueName(tableName, idxName) - } else { - idx = indexName(tableName, idxName) - } - sqlStr, args := session.Engine.dialect.IndexCheckSql(tableName, idx) - results, err := session.query(sqlStr, args...) - return len(results) > 0, err -} - -// find if index is exist according cols -func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - indexes, err := session.Engine.dialect.GetIndexes(tableName) - if err != nil { - return false, err - } - - for _, index := range indexes { - if sliceEq(index.Cols, cols) { - if unique { - return index.Type == core.UniqueType, nil - } else { - return index.Type == core.IndexType, nil - } - } - } - return false, nil -} - -func (session *Session) addColumn(colName string) error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - col := session.Statement.RefTable.GetColumn(colName) - sql, args := session.Statement.genAddColumnStr(col) - _, err := session.exec(sql, args...) - return err -} - -func (session *Session) addIndex(tableName, idxName string) error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - index := session.Statement.RefTable.Indexes[idxName] - sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) - - _, err := session.exec(sqlStr) - return err -} - -func (session *Session) addUnique(tableName, uqeName string) error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - index := session.Statement.RefTable.Indexes[uqeName] - sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) - _, err := session.exec(sqlStr) - return err -} - -// To be deleted -func (session *Session) dropAll() error { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - for _, table := range session.Engine.Tables { - session.Statement.Init() - session.Statement.RefTable = table - sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName()) - _, err := session.exec(sqlStr) - if err != nil { - return err - } - } - return nil -} - func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value { var col *core.Column if col = table.GetColumnIdx(key, idx); col == nil { - session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.Columns())) + //session.Engine.logger.Warnf("table %v has no column %v. %v", table.Name, key, table.ColumnsSeq()) return nil } fieldValue, err := col.ValueOfV(dataStruct) if err != nil { - session.Engine.LogError(err) + session.Engine.logger.Error(err) return nil } if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.Engine.LogWarn("table %v's column %v is not valid or cannot set", - table.Name, key) + session.Engine.logger.Warnf("table %v's column %v is not valid or cannot set", table.Name, key) return nil } return fieldValue } +// Cell cell is a result of one column field type Cell *interface{} func (session *Session) rows2Beans(rows *core.Rows, fields []string, fieldsCount int, - table *core.Table, newElemFunc func() reflect.Value, - sliceValueSetFunc func(*reflect.Value)) error { + table *core.Table, newElemFunc func([]string) reflect.Value, + sliceValueSetFunc func(*reflect.Value, core.PK) error) error { for rows.Next() { - var newValue reflect.Value = newElemFunc() + var newValue = newElemFunc(fields) bean := newValue.Interface() dataStruct := rValue(bean) - err := session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table) + pk, err := session.row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table) + if err != nil { + return err + } + + err = sliceValueSetFunc(&newValue, pk) if err != nil { return err } - sliceValueSetFunc(&newValue) } return nil } -func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error { - dataStruct := rValue(bean) - if dataStruct.Kind() != reflect.Struct { - return errors.New("Expected a pointer to a struct") - } - - table := session.Engine.autoMapType(dataStruct) - return session._row2Bean(rows, fields, fieldsCount, bean, &dataStruct, table) -} - -func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) error { +func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}, dataStruct *reflect.Value, table *core.Table) (core.PK, error) { scanResults := make([]interface{}, fieldsCount) for i := 0; i < len(fields); i++ { var cell interface{} scanResults[i] = &cell } if err := rows.Scan(scanResults...); err != nil { - return err + return nil, err } if b, hasBeforeSet := bean.(BeforeSetProcessor); hasBeforeSet { @@ -1668,20 +338,22 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount }() var tempMap = make(map[string]int) + var pk core.PK for ii, key := range fields { var idx int var ok bool - if idx, ok = tempMap[strings.ToLower(key)]; !ok { + var lKey = strings.ToLower(key) + if idx, ok = tempMap[lKey]; !ok { idx = 0 } else { idx = idx + 1 } - tempMap[strings.ToLower(key)] = idx + tempMap[lKey] = idx if fieldValue := session.getField(dataStruct, key, table, idx); fieldValue != nil { rawValue := reflect.Indirect(reflect.ValueOf(scanResults[ii])) - //if row is null then ignore + // if row is null then ignore if rawValue.Interface() == nil { continue } @@ -1691,7 +363,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount if data, err := value2Bytes(&rawValue); err == nil { structConvert.FromDB(data) } else { - session.Engine.LogError(err) + session.Engine.logger.Error(err) } continue } @@ -1704,37 +376,80 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount } fieldValue.Interface().(core.Conversion).FromDB(data) } else { - session.Engine.LogError(err) + session.Engine.logger.Error(err) } continue } rawValueType := reflect.TypeOf(rawValue.Interface()) vv := reflect.ValueOf(rawValue.Interface()) - + col := table.GetColumnIdx(key, idx) + if col.IsPrimaryKey { + pk = append(pk, rawValue.Interface()) + } fieldType := fieldValue.Type() hasAssigned := false + if col.SQLType.IsJson() { + var bs []byte + if rawValueType.Kind() == reflect.String { + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } else { + return nil, fmt.Errorf("unsupported database data type: %s %v", key, rawValueType.Kind()) + } + + hasAssigned = true + + if len(bs) > 0 { + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + session.Engine.logger.Error(key, err) + return nil, err + } + } else { + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + session.Engine.logger.Error(key, err) + return nil, err + } + fieldValue.Set(x.Elem()) + } + } + + continue + } + switch fieldType.Kind() { case reflect.Complex64, reflect.Complex128: + // TODO: reimplement this + var bs []byte if rawValueType.Kind() == reflect.String { - hasAssigned = true - x := reflect.New(fieldType) - err := json.Unmarshal([]byte(vv.String()), x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err + bs = []byte(vv.String()) + } else if rawValueType.ConvertibleTo(core.BytesType) { + bs = vv.Bytes() + } + + hasAssigned = true + if len(bs) > 0 { + if fieldValue.CanAddr() { + err := json.Unmarshal(bs, fieldValue.Addr().Interface()) + if err != nil { + session.Engine.logger.Error(err) + return nil, err + } + } else { + x := reflect.New(fieldType) + err := json.Unmarshal(bs, x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return nil, err + } + fieldValue.Set(x.Elem()) } - fieldValue.Set(x.Elem()) - } else if rawValueType.Kind() == reflect.Slice { - hasAssigned = true - x := reflect.New(fieldType) - err := json.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(x.Elem()) } case reflect.Slice, reflect.Array: switch rawValueType.Kind() { @@ -1743,7 +458,21 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount case reflect.Uint8: if fieldType.Elem().Kind() == reflect.Uint8 { hasAssigned = true - fieldValue.Set(vv) + if col.SQLType.IsText() { + x := reflect.New(fieldType) + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return nil, err + } + fieldValue.Set(x.Elem()) + } else { + for i := 0; i < fieldValue.Len(); i++ { + if i < vv.Len() { + fieldValue.Index(i).Set(vv.Index(i)) + } + } + } } } } @@ -1779,18 +508,29 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount fieldValue.SetUint(uint64(vv.Int())) } case reflect.Struct: - col := table.GetColumn(key) if fieldType.ConvertibleTo(core.TimeType) { if rawValueType == core.TimeType { hasAssigned = true t := vv.Convert(core.TimeType).Interface().(time.Time) + z, _ := t.Zone() - if len(z) == 0 || t.Year() == 0 { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location - session.Engine.LogDebugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) - t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), - t.Minute(), t.Second(), t.Nanosecond(), time.Local) + dbTZ := session.Engine.DatabaseTZ + if dbTZ == nil { + if session.Engine.dialect.DBType() == core.SQLITE { + dbTZ = time.UTC + } else { + dbTZ = time.Local + } } + + // set new location if database don't save timezone or give an incorrect timezone + if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location + session.Engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t.Minute(), t.Second(), t.Nanosecond(), dbTZ) + } + // !nashtsai! convert to engine location if col.TimeZone == nil { t = t.In(session.Engine.TZLocation) @@ -1818,9 +558,17 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount if d, ok := vv.Interface().([]uint8); ok { hasAssigned = true t, err := session.byte2Time(col, d) - //fmt.Println(string(d), t, err) if err != nil { - session.Engine.LogError("byte2Time error:", err.Error()) + session.Engine.logger.Error("byte2Time error:", err.Error()) + hasAssigned = false + } else { + fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) + } + } else if d, ok := vv.Interface().(string); ok { + hasAssigned = true + t, err := session.str2Time(col, d) + if err != nil { + session.Engine.logger.Error("byte2Time error:", err.Error()) hasAssigned = false } else { fieldValue.Set(reflect.ValueOf(t).Convert(fieldType)) @@ -1833,33 +581,37 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount // !! 增加支持sql.Scanner接口的结构,如sql.NullString hasAssigned = true if err := nulVal.Scan(vv.Interface()); err != nil { - //fmt.Println("sql.Sanner error:", err.Error()) - session.Engine.LogError("sql.Sanner error:", err.Error()) + session.Engine.logger.Error("sql.Sanner error:", err.Error()) hasAssigned = false } } else if col.SQLType.IsJson() { if rawValueType.Kind() == reflect.String { hasAssigned = true x := reflect.New(fieldType) - err := json.Unmarshal([]byte(vv.String()), x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return nil, err + } + fieldValue.Set(x.Elem()) } - fieldValue.Set(x.Elem()) } else if rawValueType.Kind() == reflect.Slice { hasAssigned = true x := reflect.New(fieldType) - err := json.Unmarshal(vv.Bytes(), x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err + if len(vv.Bytes()) > 0 { + err := json.Unmarshal(vv.Bytes(), x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return nil, err + } + fieldValue.Set(x.Elem()) } - fieldValue.Set(x.Elem()) } } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) if table != nil { + hasAssigned = true if len(table.PrimaryKeys) != 1 { panic("unsupported non or composited primary key cascade") } @@ -1903,17 +655,18 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount defer newsession.Close() has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) if err != nil { - return err + return nil, err } if has { - v := structInter.Elem().Interface() - fieldValue.Set(reflect.ValueOf(v)) + //v := structInter.Elem().Interface() + //fieldValue.Set(reflect.ValueOf(v)) + fieldValue.Set(structInter.Elem()) } else { - return errors.New("cascade obj is not exist!") + return nil, errors.New("cascade obj is not exist") } } } else { - session.Engine.LogError("unsupported struct type in Scan: ", fieldValue.Type().String()) + session.Engine.logger.Error("unsupported struct type in Scan: ", fieldValue.Type().String()) } } case reflect.Ptr: @@ -1936,7 +689,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount case core.PtrTimeType: if rawValueType == core.PtrTimeType { hasAssigned = true - var x time.Time = rawValue.Interface().(time.Time) + var x = rawValue.Interface().(time.Time) fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrFloat64Type: @@ -1947,7 +700,7 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount } case core.PtrUint64Type: if rawValueType.Kind() == reflect.Int64 { - var x uint64 = uint64(vv.Int()) + var x = uint64(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } @@ -1959,74 +712,78 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount } case core.PtrFloat32Type: if rawValueType.Kind() == reflect.Float64 { - var x float32 = float32(vv.Float()) + var x = float32(vv.Float()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrIntType: if rawValueType.Kind() == reflect.Int64 { - var x int = int(vv.Int()) + var x = int(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrInt32Type: if rawValueType.Kind() == reflect.Int64 { - var x int32 = int32(vv.Int()) + var x = int32(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrInt8Type: if rawValueType.Kind() == reflect.Int64 { - var x int8 = int8(vv.Int()) + var x = int8(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrInt16Type: if rawValueType.Kind() == reflect.Int64 { - var x int16 = int16(vv.Int()) + var x = int16(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrUintType: if rawValueType.Kind() == reflect.Int64 { - var x uint = uint(vv.Int()) + var x = uint(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.PtrUint32Type: if rawValueType.Kind() == reflect.Int64 { - var x uint32 = uint32(vv.Int()) + var x = uint32(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.Uint8Type: if rawValueType.Kind() == reflect.Int64 { - var x uint8 = uint8(vv.Int()) + var x = uint8(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.Uint16Type: if rawValueType.Kind() == reflect.Int64 { - var x uint16 = uint16(vv.Int()) + var x = uint16(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } case core.Complex64Type: var x complex64 - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - session.Engine.LogError(err) - } else { - fieldValue.Set(reflect.ValueOf(&x)) + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + session.Engine.logger.Error(err) + } else { + fieldValue.Set(reflect.ValueOf(&x)) + } } hasAssigned = true case core.Complex128Type: var x complex128 - err := json.Unmarshal([]byte(vv.String()), &x) - if err != nil { - session.Engine.LogError(err) - } else { - fieldValue.Set(reflect.ValueOf(&x)) + if len([]byte(vv.String())) > 0 { + err := json.Unmarshal([]byte(vv.String()), &x) + if err != nil { + session.Engine.logger.Error(err) + } else { + fieldValue.Set(reflect.ValueOf(&x)) + } } hasAssigned = true } // switch fieldType @@ -2038,15 +795,14 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount if !hasAssigned { data, err := value2Bytes(&rawValue) if err == nil { - session.bytes2Value(table.GetColumn(key), fieldValue, data) + session.bytes2Value(col, fieldValue, data) } else { - session.Engine.LogError(err.Error()) + session.Engine.logger.Error(err.Error()) } } } } - return nil - + return pk, nil } func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { @@ -2057,1990 +813,6 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) session.saveLastSQL(*sqlStr, paramStr...) } -func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - - session.queryPreprocess(&sqlStr, paramStr...) - - if session.IsAutoCommit { - return session.innerQuery(sqlStr, paramStr...) - } - return session.txQuery(session.Tx, sqlStr, paramStr...) -} - -func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { - rows, err := tx.Query(sqlStr, params...) - if err != nil { - return nil, err - } - defer rows.Close() - - return rows2maps(rows) -} - -func (session *Session) innerQuery(sqlStr string, params ...interface{}) ([]map[string][]byte, error) { - var callback func() (*core.Stmt, *core.Rows, error) - if session.prepareStmt { - callback = func() (*core.Stmt, *core.Rows, error) { - stmt, err := session.doPrepare(sqlStr) - if err != nil { - return nil, nil, err - } - rows, err := stmt.Query(params...) - if err != nil { - return nil, nil, err - } - return stmt, rows, nil - } - } else { - callback = func() (*core.Stmt, *core.Rows, error) { - rows, err := session.DB().Query(sqlStr, params...) - if err != nil { - return nil, nil, err - } - return nil, rows, err - } - } - _, rows, err := session.Engine.LogSQLQueryTime(sqlStr, params, callback) - if rows != nil { - defer rows.Close() - } - if err != nil { - return nil, err - } - return rows2maps(rows) -} - -// Exec a raw sql and return records as []map[string][]byte -func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.query(sqlStr, paramStr...) -} - -// ============================= -// for string -// ============================= -func (session *Session) query2(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) { - session.queryPreprocess(&sqlStr, paramStr...) - - if session.IsAutoCommit { - return query2(session.DB(), sqlStr, paramStr...) - } - return txQuery2(session.Tx, sqlStr, paramStr...) -} - -// insert one or more beans -func (session *Session) Insert(beans ...interface{}) (int64, error) { - var affected int64 = 0 - var err error - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - for _, bean := range beans { - sliceValue := reflect.Indirect(reflect.ValueOf(bean)) - if sliceValue.Kind() == reflect.Slice { - size := sliceValue.Len() - if size > 0 { - if session.Engine.SupportInsertMany() { - cnt, err := session.innerInsertMulti(bean) - if err != nil { - return affected, err - } - affected += cnt - } else { - for i := 0; i < size; i++ { - cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) - if err != nil { - return affected, err - } - affected += cnt - } - } - } - } else { - cnt, err := session.innerInsert(bean) - if err != nil { - return affected, err - } - affected += cnt - } - } - - return affected, err -} - -func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) { - sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - if sliceValue.Kind() != reflect.Slice { - return 0, errors.New("needs a pointer to a slice") - } - - bean := sliceValue.Index(0).Interface() - elementValue := rValue(bean) - //sliceElementType := elementValue.Type() - - table := session.Engine.autoMapType(elementValue) - session.Statement.RefTable = table - - size := sliceValue.Len() - - colNames := make([]string, 0) - colMultiPlaces := make([]string, 0) - var args = make([]interface{}, 0) - cols := make([]*core.Column, 0) - - for i := 0; i < size; i++ { - v := sliceValue.Index(i) - vv := reflect.Indirect(v) - elemValue := v.Interface() - colPlaces := make([]string, 0) - - // handle BeforeInsertProcessor - // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? - for _, closure := range session.beforeClosures { - closure(elemValue) - } - - if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok { - processor.BeforeInsert() - } - // -- - - if i == 0 { - for _, col := range table.Columns() { - ptrFieldValue, err := col.ValueOfV(&vv) - if err != nil { - return 0, err - } - fieldValue := *ptrFieldValue - if col.IsAutoIncrement && isZero(fieldValue.Interface()) { - continue - } - if col.MapType == core.ONLYFROMDB { - continue - } - if col.IsDeleted { - continue - } - if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; !ok { - continue - } - } - if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; ok { - continue - } - } - if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - val, t := session.Engine.NowTime2(col.SQLType.Name) - args = append(args, val) - - var colName = col.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return 0, err - } - args = append(args, arg) - } - - colNames = append(colNames, col.Name) - cols = append(cols, col) - colPlaces = append(colPlaces, "?") - } - } else { - for _, col := range cols { - ptrFieldValue, err := col.ValueOfV(&vv) - if err != nil { - return 0, err - } - fieldValue := *ptrFieldValue - - if col.IsAutoIncrement && isZero(fieldValue.Interface()) { - continue - } - if col.MapType == core.ONLYFROMDB { - continue - } - if col.IsDeleted { - continue - } - if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; !ok { - continue - } - } - if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; ok { - continue - } - } - if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - val, t := session.Engine.NowTime2(col.SQLType.Name) - args = append(args, val) - - var colName = col.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return 0, err - } - args = append(args, arg) - } - - colPlaces = append(colPlaces, "?") - } - } - colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", ")) - } - cleanupProcessorsClosures(&session.beforeClosures) - - statement := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)", - session.Engine.QuoteStr(), - session.Statement.TableName(), - session.Engine.QuoteStr(), - session.Engine.QuoteStr(), - strings.Join(colNames, session.Engine.QuoteStr()+", "+session.Engine.QuoteStr()), - session.Engine.QuoteStr(), - strings.Join(colMultiPlaces, "),(")) - - res, err := session.exec(statement, args...) - if err != nil { - return 0, err - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - session.cacheInsert(session.Statement.TableName()) - } - - lenAfterClosures := len(session.afterClosures) - for i := 0; i < size; i++ { - elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() - - // handle AfterInsertProcessor - if session.IsAutoCommit { - // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? - for _, closure := range session.afterClosures { - closure(elemValue) - } - if processor, ok := interface{}(elemValue).(AfterInsertProcessor); ok { - processor.AfterInsert() - } - } else { - if lenAfterClosures > 0 { - if value, has := session.afterInsertBeans[elemValue]; has && value != nil { - *value = append(*value, session.afterClosures...) - } else { - afterClosures := make([]func(interface{}), lenAfterClosures) - copy(afterClosures, session.afterClosures) - session.afterInsertBeans[elemValue] = &afterClosures - } - } else { - if _, ok := interface{}(elemValue).(AfterInsertProcessor); ok { - session.afterInsertBeans[elemValue] = nil - } - } - } - } - cleanupProcessorsClosures(&session.afterClosures) - return res.RowsAffected() -} - -// Insert multiple records -func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { - sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - if sliceValue.Kind() == reflect.Slice { - if sliceValue.Len() > 0 { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - return session.innerInsertMulti(rowsSlicePtr) - } else { - return 0, nil - } - } else { - return 0, ErrParamsType - } -} - -func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) { - sdata := strings.TrimSpace(string(data)) - var x time.Time - var err error - - if sdata == "0000-00-00 00:00:00" || - sdata == "0001-01-01 00:00:00" { - } else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column - // time stamp - sd, err := strconv.ParseInt(sdata, 10, 64) - if err == nil { - x = time.Unix(sd, 0) - // !nashtsai! HACK mymysql driver is casuing Local location being change to CHAT and cause wrong time conversion - //fmt.Println(x.In(session.Engine.TZLocation), "===") - if col.TimeZone == nil { - x = x.In(session.Engine.TZLocation) - } else { - x = x.In(col.TimeZone) - } - //fmt.Println(x, "=====") - session.Engine.LogDebugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } else { - session.Engine.LogDebugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } - } else if len(sdata) > 19 && strings.Contains(sdata, "-") { - x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) - session.Engine.LogDebugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - if err != nil { - x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation) - session.Engine.LogDebugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } - if err != nil { - x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation) - session.Engine.LogDebugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } - - } else if len(sdata) == 19 && strings.Contains(sdata, "-") { - x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) - session.Engine.LogDebugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { - x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation) - session.Engine.LogDebugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } else if col.SQLType.Name == core.Time { - if strings.Contains(sdata, " ") { - ssd := strings.Split(sdata, " ") - sdata = ssd[1] - } - - sdata = strings.TrimSpace(sdata) - if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 { - sdata = sdata[len(sdata)-8:] - } - - st := fmt.Sprintf("2006-01-02 %v", sdata) - x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) - session.Engine.LogDebugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) - } else { - outErr = errors.New(fmt.Sprintf("unsupported time format %v", sdata)) - return - } - if err != nil { - outErr = errors.New(fmt.Sprintf("unsupported time format %v: %v", sdata, err)) - return - } - outTime = x - return -} - -// convert a db data([]byte) to a field value -func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error { - if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { - return structConvert.FromDB(data) - } - - if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { - return structConvert.FromDB(data) - } - - var v interface{} - key := col.Name - fieldType := fieldValue.Type() - - switch fieldType.Kind() { - case reflect.Complex64, reflect.Complex128: - x := reflect.New(fieldType) - - err := json.Unmarshal(data, x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(x.Elem()) - case reflect.Slice, reflect.Array, reflect.Map: - v = data - t := fieldType.Elem() - k := t.Kind() - if col.SQLType.IsText() { - x := reflect.New(fieldType) - err := json.Unmarshal(data, x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(x.Elem()) - } else if col.SQLType.IsBlob() { - if k == reflect.Uint8 { - fieldValue.Set(reflect.ValueOf(v)) - } else { - x := reflect.New(fieldType) - err := json.Unmarshal(data, x.Interface()) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(x.Elem()) - } - } else { - return ErrUnSupportedType - } - case reflect.String: - fieldValue.SetString(string(data)) - case reflect.Bool: - d := string(data) - v, err := strconv.ParseBool(d) - if err != nil { - return fmt.Errorf("arg %v as bool: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(v)) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - sdata := string(data) - var x int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API - if len(data) == 1 { - x = int64(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x, err = strconv.ParseInt(sdata, 16, 64) - } else if strings.HasPrefix(sdata, "0") { - x, err = strconv.ParseInt(sdata, 8, 64) - } else if strings.ToLower(sdata) == "true" { - x = 1 - } else if strings.ToLower(sdata) == "false" { - x = 0 - } else { - x, err = strconv.ParseInt(sdata, 10, 64) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.SetInt(x) - case reflect.Float32, reflect.Float64: - x, err := strconv.ParseFloat(string(data), 64) - if err != nil { - return fmt.Errorf("arg %v as float64: %s", key, err.Error()) - } - fieldValue.SetFloat(x) - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.SetUint(x) - //Currently only support Time type - case reflect.Struct: - // !! 增加支持sql.Scanner接口的结构,如sql.NullString - if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { - if err := nulVal.Scan(data); err != nil { - return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error()) - } - } else { - if fieldType.ConvertibleTo(core.TimeType) { - x, err := session.byte2Time(col, data) - if err != nil { - return err - } - v = x - fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) - } else if session.Statement.UseCascade { - table := session.Engine.autoMapType(*fieldValue) - if table != nil { - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - switch rawValueType.Kind() { - case reflect.Int64: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = x - case reflect.Int: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int(x) - case reflect.Int32: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int32(x) - case reflect.Int16: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int16(x) - case reflect.Int8: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = int8(x) - case reflect.Uint64: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = x - case reflect.Uint: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint(x) - case reflect.Uint32: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint32(x) - case reflect.Uint16: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint16(x) - case reflect.Uint8: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - pk[0] = uint8(x) - case reflect.String: - pk[0] = string(data) - default: - panic("unsupported primary key type cascade") - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - structInter := reflect.New(fieldValue.Type()) - newsession := session.Engine.NewSession() - defer newsession.Close() - has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Elem().Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist!") - } - } - } else { - return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) - } - } - } - case reflect.Ptr: - // !nashtsai! TODO merge duplicated codes above - //typeStr := fieldType.String() - switch fieldType { - // case "*string": - case core.PtrStringType: - x := string(data) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*bool": - case core.PtrBoolType: - d := string(data) - v, err := strconv.ParseBool(d) - if err != nil { - return fmt.Errorf("arg %v as bool: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&v)) - // case "*complex64": - case core.PtrComplex64Type: - var x complex64 - err := json.Unmarshal(data, &x) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*complex128": - case core.PtrComplex128Type: - var x complex128 - err := json.Unmarshal(data, &x) - if err != nil { - session.Engine.LogError(err) - return err - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*float64": - case core.PtrFloat64Type: - x, err := strconv.ParseFloat(string(data), 64) - if err != nil { - return fmt.Errorf("arg %v as float64: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*float32": - case core.PtrFloat32Type: - var x float32 - x1, err := strconv.ParseFloat(string(data), 32) - if err != nil { - return fmt.Errorf("arg %v as float32: %s", key, err.Error()) - } - x = float32(x1) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*time.Time": - case core.PtrTimeType: - x, err := session.byte2Time(col, data) - if err != nil { - return err - } - v = x - fieldValue.Set(reflect.ValueOf(&x)) - // case "*uint64": - case core.PtrUint64Type: - var x uint64 - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*uint": - case core.PtrUintType: - var x uint - x1, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - x = uint(x1) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*uint32": - case core.PtrUint32Type: - var x uint32 - x1, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - x = uint32(x1) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*uint8": - case core.PtrUint8Type: - var x uint8 - x1, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - x = uint8(x1) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*uint16": - case core.PtrUint16Type: - var x uint16 - x1, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - x = uint16(x1) - fieldValue.Set(reflect.ValueOf(&x)) - // case "*int64": - case core.PtrInt64Type: - sdata := string(data) - var x int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName(), "mysql") { - if len(data) == 1 { - x = int64(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x, err = strconv.ParseInt(sdata, 16, 64) - } else if strings.HasPrefix(sdata, "0") { - x, err = strconv.ParseInt(sdata, 8, 64) - } else { - x, err = strconv.ParseInt(sdata, 10, 64) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*int": - case core.PtrIntType: - sdata := string(data) - var x int - var x1 int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName(), "mysql") { - if len(data) == 1 { - x = int(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x1, err = strconv.ParseInt(sdata, 16, 64) - x = int(x1) - } else if strings.HasPrefix(sdata, "0") { - x1, err = strconv.ParseInt(sdata, 8, 64) - x = int(x1) - } else { - x1, err = strconv.ParseInt(sdata, 10, 64) - x = int(x1) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*int32": - case core.PtrInt32Type: - sdata := string(data) - var x int32 - var x1 int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - session.Engine.dialect.DBType() == core.MYSQL { - if len(data) == 1 { - x = int32(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x1, err = strconv.ParseInt(sdata, 16, 64) - x = int32(x1) - } else if strings.HasPrefix(sdata, "0") { - x1, err = strconv.ParseInt(sdata, 8, 64) - x = int32(x1) - } else { - x1, err = strconv.ParseInt(sdata, 10, 64) - x = int32(x1) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*int8": - case core.PtrInt8Type: - sdata := string(data) - var x int8 - var x1 int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName(), "mysql") { - if len(data) == 1 { - x = int8(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x1, err = strconv.ParseInt(sdata, 16, 64) - x = int8(x1) - } else if strings.HasPrefix(sdata, "0") { - x1, err = strconv.ParseInt(sdata, 8, 64) - x = int8(x1) - } else { - x1, err = strconv.ParseInt(sdata, 10, 64) - x = int8(x1) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - // case "*int16": - case core.PtrInt16Type: - sdata := string(data) - var x int16 - var x1 int64 - var err error - // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName(), "mysql") { - if len(data) == 1 { - x = int16(data[0]) - } else { - x = 0 - } - } else if strings.HasPrefix(sdata, "0x") { - x1, err = strconv.ParseInt(sdata, 16, 64) - x = int16(x1) - } else if strings.HasPrefix(sdata, "0") { - x1, err = strconv.ParseInt(sdata, 8, 64) - x = int16(x1) - } else { - x1, err = strconv.ParseInt(sdata, 10, 64) - x = int16(x1) - } - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - fieldValue.Set(reflect.ValueOf(&x)) - default: - if fieldType.Elem().Kind() == reflect.Struct { - if session.Statement.UseCascade { - structInter := reflect.New(fieldType.Elem()) - table := session.Engine.autoMapType(structInter.Elem()) - if table != nil { - if len(table.PrimaryKeys) > 1 { - panic("unsupported composited primary key cascade") - } - var pk = make(core.PK, len(table.PrimaryKeys)) - rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) - switch rawValueType.Kind() { - case reflect.Int64: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = x - case reflect.Int: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = int(x) - case reflect.Int32: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = int32(x) - case reflect.Int16: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = int16(x) - case reflect.Int8: - x, err := strconv.ParseInt(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = x - case reflect.Uint64: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = x - case reflect.Uint: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = uint(x) - case reflect.Uint32: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = uint32(x) - case reflect.Uint16: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = uint16(x) - case reflect.Uint8: - x, err := strconv.ParseUint(string(data), 10, 64) - if err != nil { - return fmt.Errorf("arg %v as int: %s", key, err.Error()) - } - - pk[0] = uint8(x) - case reflect.String: - pk[0] = string(data) - default: - panic("unsupported primary key type cascade") - } - - if !isPKZero(pk) { - // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch - // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne - // property to be fetched lazily - newsession := session.Engine.NewSession() - defer newsession.Close() - has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) - if err != nil { - return err - } - if has { - v = structInter.Interface() - fieldValue.Set(reflect.ValueOf(v)) - } else { - return errors.New("cascade obj is not exist!") - } - } - } - } else { - return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) - } - } - return fmt.Errorf("unsupported type in Scan: %s", reflect.TypeOf(v).String()) - } - default: - return fmt.Errorf("unsupported type in Scan: %s", reflect.TypeOf(v).String()) - } - - return nil -} - -// convert a field value of a struct to interface for put into db -func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) { - if fieldValue.CanAddr() { - if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { - data, err := fieldConvert.ToDB() - if err != nil { - return 0, err - } else { - return string(data), nil - } - } - } - - if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok { - data, err := fieldConvert.ToDB() - if err != nil { - return 0, err - } else { - return string(data), nil - } - } - - fieldType := fieldValue.Type() - k := fieldType.Kind() - if k == reflect.Ptr { - if fieldValue.IsNil() { - return nil, nil - } else if !fieldValue.IsValid() { - session.Engine.LogWarn("the field[", col.FieldName, "] is invalid") - return nil, nil - } else { - // !nashtsai! deference pointer type to instance type - fieldValue = fieldValue.Elem() - fieldType = fieldValue.Type() - k = fieldType.Kind() - } - } - - switch k { - case reflect.Bool: - return fieldValue.Bool(), nil - case reflect.String: - return fieldValue.String(), nil - case reflect.Struct: - if fieldType.ConvertibleTo(core.TimeType) { - t := fieldValue.Convert(core.TimeType).Interface().(time.Time) - if session.Engine.dialect.DBType() == core.MSSQL { - if t.IsZero() { - return nil, nil - } - } - tf := session.Engine.FormatTime(col.SQLType.Name, t) - return tf, nil - } - - if !col.SQLType.IsJson() { - // !! 增加支持driver.Valuer接口的结构,如sql.NullString - if v, ok := fieldValue.Interface().(driver.Valuer); ok { - return v.Value() - } - - fieldTable := session.Engine.autoMapType(fieldValue) - if len(fieldTable.PrimaryKeys) == 1 { - pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) - return pkField.Interface(), nil - } - return 0, fmt.Errorf("no primary key for col %v", col.Name) - } - - if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) - if err != nil { - session.Engine.LogError(err) - return 0, err - } - return string(bytes), nil - } else if col.SQLType.IsBlob() { - bytes, err := json.Marshal(fieldValue.Interface()) - if err != nil { - session.Engine.LogError(err) - return 0, err - } - return bytes, nil - } - return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) - case reflect.Complex64, reflect.Complex128: - bytes, err := json.Marshal(fieldValue.Interface()) - if err != nil { - session.Engine.LogError(err) - return 0, err - } - return string(bytes), nil - case reflect.Array, reflect.Slice, reflect.Map: - if !fieldValue.IsValid() { - return fieldValue.Interface(), nil - } - - if col.SQLType.IsText() { - bytes, err := json.Marshal(fieldValue.Interface()) - if err != nil { - session.Engine.LogError(err) - return 0, err - } - return string(bytes), nil - } else if col.SQLType.IsBlob() { - var bytes []byte - var err error - if (k == reflect.Array || k == reflect.Slice) && - (fieldValue.Type().Elem().Kind() == reflect.Uint8) { - bytes = fieldValue.Bytes() - } else { - bytes, err = json.Marshal(fieldValue.Interface()) - if err != nil { - session.Engine.LogError(err) - return 0, err - } - } - return bytes, nil - } - return nil, ErrUnSupportedType - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - return int64(fieldValue.Uint()), nil - default: - return fieldValue.Interface(), nil - } -} - -func (session *Session) innerInsert(bean interface{}) (int64, error) { - table := session.Engine.TableInfo(bean) - session.Statement.RefTable = table - - // handle BeforeInsertProcessor - for _, closure := range session.beforeClosures { - closure(bean) - } - cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used - - if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok { - processor.BeforeInsert() - } - // -- - colNames, args, err := genCols(table, session, bean, false, false) - if err != nil { - return 0, err - } - // insert expr columns, override if exists - exprColumns := session.Statement.getExpr() - exprColVals := make([]string, 0, len(exprColumns)) - for _, v := range exprColumns { - // remove the expr columns - for i, colName := range colNames { - if colName == v.colName { - colNames = append(colNames[:i], colNames[i+1:]...) - args = append(args[:i], args[i+1:]...) - } - } - - // append expr column to the end - colNames = append(colNames, v.colName) - exprColVals = append(exprColVals, v.expr) - } - - colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns)) - if len(exprColVals) > 0 { - colPlaces = colPlaces + strings.Join(exprColVals, ", ") - } else { - colPlaces = colPlaces[0 : len(colPlaces)-2] - } - - sqlStr := fmt.Sprintf("INSERT INTO %v%v%v (%v%v%v) VALUES (%v)", - session.Engine.QuoteStr(), - session.Statement.TableName(), - session.Engine.QuoteStr(), - session.Engine.QuoteStr(), - strings.Join(colNames, session.Engine.Quote(", ")), - session.Engine.QuoteStr(), - colPlaces) - - handleAfterInsertProcessorFunc := func(bean interface{}) { - - if session.IsAutoCommit { - for _, closure := range session.afterClosures { - closure(bean) - } - if processor, ok := interface{}(bean).(AfterInsertProcessor); ok { - processor.AfterInsert() - } - } else { - lenAfterClosures := len(session.afterClosures) - if lenAfterClosures > 0 { - if value, has := session.afterInsertBeans[bean]; has && value != nil { - *value = append(*value, session.afterClosures...) - } else { - afterClosures := make([]func(interface{}), lenAfterClosures) - copy(afterClosures, session.afterClosures) - session.afterInsertBeans[bean] = &afterClosures - } - - } else { - if _, ok := interface{}(bean).(AfterInsertProcessor); ok { - session.afterInsertBeans[bean] = nil - } - } - } - cleanupProcessorsClosures(&session.afterClosures) // cleanup after used - } - - // for postgres, many of them didn't implement lastInsertId, so we should - // implemented it ourself. - if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 { - //assert table.AutoIncrement != "" - res, err := session.query("select seq_atable.currval from dual", args...) - - if err != nil { - return 0, err - } else { - handleAfterInsertProcessorFunc(bean) - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - session.cacheInsert(session.Statement.TableName()) - } - - if table.Version != "" && session.Statement.checkVersion { - verValue, err := table.VersionColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) - } - } - - if len(res) < 1 { - return 0, errors.New("insert no error but not returned id") - } - - idByte := res[0][table.AutoIncrement] - id, err := strconv.ParseInt(string(idByte), 10, 64) - if err != nil { - return 1, err - } - - aiValue, err := table.AutoIncrColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } - - if aiValue == nil || !aiValue.IsValid() /*|| aiValue. != 0*/ || !aiValue.CanSet() { - return 1, nil - } - - v := int64ToInt(id, aiValue.Type().Kind()) - aiValue.Set(reflect.ValueOf(v)) - - return 1, nil - } else if session.Engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 { - //assert table.AutoIncrement != "" - sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement) - res, err := session.query(sqlStr, args...) - - if err != nil { - return 0, err - } else { - handleAfterInsertProcessorFunc(bean) - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - session.cacheInsert(session.Statement.TableName()) - } - - if table.Version != "" && session.Statement.checkVersion { - verValue, err := table.VersionColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) - } - } - - if len(res) < 1 { - return 0, errors.New("insert no error but not returned id") - } - - idByte := res[0][table.AutoIncrement] - id, err := strconv.ParseInt(string(idByte), 10, 64) - if err != nil { - return 1, err - } - - aiValue, err := table.AutoIncrColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } - - if aiValue == nil || !aiValue.IsValid() /*|| aiValue. != 0*/ || !aiValue.CanSet() { - return 1, nil - } - - v := int64ToInt(id, aiValue.Type().Kind()) - aiValue.Set(reflect.ValueOf(v)) - - return 1, nil - } else { - res, err := session.exec(sqlStr, args...) - if err != nil { - return 0, err - } else { - handleAfterInsertProcessorFunc(bean) - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - session.cacheInsert(session.Statement.TableName()) - } - - if table.Version != "" && session.Statement.checkVersion { - verValue, err := table.VersionColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } else if verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(1) - } - } - - if table.AutoIncrement == "" { - return res.RowsAffected() - } - - var id int64 = 0 - id, err = res.LastInsertId() - if err != nil || id <= 0 { - return res.RowsAffected() - } - - aiValue, err := table.AutoIncrColumn().ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } - - if aiValue == nil || !aiValue.IsValid() /*|| aiValue.Int() != 0*/ || !aiValue.CanSet() { - return res.RowsAffected() - } - - v := int64ToInt(id, aiValue.Type().Kind()) - aiValue.Set(reflect.ValueOf(v)) - - return res.RowsAffected() - } -} - -// Method InsertOne insert only one struct into database as a record. -// The in parameter bean must a struct or a point to struct. The return -// parameter is inserted and error -func (session *Session) InsertOne(bean interface{}) (int64, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - return session.innerInsert(bean) -} - -func (statement *Statement) convertUpdateSql(sqlStr string) (string, string) { - if statement.RefTable == nil || len(statement.RefTable.PrimaryKeys) != 1 { - return "", "" - } - - colstrs := statement.JoinColumns(statement.RefTable.PKColumns(), true) - sqls := splitNNoCase(sqlStr, "where", 2) - if len(sqls) != 2 { - if len(sqls) == 1 { - return sqls[0], fmt.Sprintf("SELECT %v FROM %v", - colstrs, statement.Engine.Quote(statement.TableName())) - } - return "", "" - } - - var whereStr = sqls[1] - - //TODO: for postgres only, if any other database? - var paraStr string - if statement.Engine.dialect.DBType() == core.POSTGRES { - paraStr = "$" - } else if statement.Engine.dialect.DBType() == core.MSSQL { - paraStr = ":" - } - - if paraStr != "" { - if strings.Contains(sqls[1], paraStr) { - dollers := strings.Split(sqls[1], paraStr) - whereStr = dollers[0] - for i, c := range dollers[1:] { - ccs := strings.SplitN(c, " ", 2) - whereStr += fmt.Sprintf(paraStr+"%v %v", i+1, ccs[1]) - } - } - } - - return sqls[0], fmt.Sprintf("SELECT %v FROM %v WHERE %v", - colstrs, statement.Engine.Quote(statement.TableName()), - whereStr) -} - -func (session *Session) cacheInsert(tables ...string) error { - if session.Statement.RefTable == nil { - return ErrCacheFailed - } - - table := session.Statement.RefTable - cacher := session.Engine.getCacher2(table) - - for _, t := range tables { - session.Engine.LogDebug("[cache] clear sql:", t) - cacher.ClearIds(t) - } - - return nil -} - -func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { - if session.Statement.RefTable == nil || - session.Tx != nil { - return ErrCacheFailed - } - - oldhead, newsql := session.Statement.convertUpdateSql(sqlStr) - if newsql == "" { - return ErrCacheFailed - } - for _, filter := range session.Engine.dialect.Filters() { - newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable) - } - session.Engine.LogDebug("[cacheUpdate] new sql", oldhead, newsql) - - var nStart int - if len(args) > 0 { - if strings.Index(sqlStr, "?") > -1 { - nStart = strings.Count(oldhead, "?") - } else { - // only for pq, TODO: if any other databse? - nStart = strings.Count(oldhead, "$") - } - } - table := session.Statement.RefTable - cacher := session.Engine.getCacher2(table) - tableName := session.Statement.TableName() - session.Engine.LogDebug("[cacheUpdate] get cache sql", newsql, args[nStart:]) - ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) - if err != nil { - rows, err := session.DB().Query(newsql, args[nStart:]...) - if err != nil { - return err - } - defer rows.Close() - - ids = make([]core.PK, 0) - for rows.Next() { - var res = make([]string, len(table.PrimaryKeys)) - err = rows.ScanSlice(&res) - if err != nil { - return err - } - var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) - for i, col := range table.PKColumns() { - if col.SQLType.IsNumeric() { - n, err := strconv.ParseInt(res[i], 10, 64) - if err != nil { - return err - } - pk[i] = n - } else if col.SQLType.IsText() { - pk[i] = res[i] - } else { - return errors.New("not supported") - } - } - - ids = append(ids, pk) - } - session.Engine.LogDebug("[cacheUpdate] find updated id", ids) - } /*else { - session.Engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args) - cacher.DelIds(tableName, genSqlKey(newsql, args)) - }*/ - - for _, id := range ids { - sid, err := id.ToString() - if err != nil { - return err - } - if bean := cacher.GetBean(tableName, sid); bean != nil { - sqls := splitNNoCase(sqlStr, "where", 2) - if len(sqls) == 0 || len(sqls) > 2 { - return ErrCacheFailed - } - - sqls = splitNNoCase(sqls[0], "set", 2) - if len(sqls) != 2 { - return ErrCacheFailed - } - kvs := strings.Split(strings.TrimSpace(sqls[1]), ",") - for idx, kv := range kvs { - sps := strings.SplitN(kv, "=", 2) - sps2 := strings.Split(sps[0], ".") - colName := sps2[len(sps2)-1] - if strings.Contains(colName, "`") { - colName = strings.TrimSpace(strings.Replace(colName, "`", "", -1)) - } else if strings.Contains(colName, session.Engine.QuoteStr()) { - colName = strings.TrimSpace(strings.Replace(colName, session.Engine.QuoteStr(), "", -1)) - } else { - session.Engine.LogDebug("[cacheUpdate] cannot find column", tableName, colName) - return ErrCacheFailed - } - - if col := table.GetColumn(colName); col != nil { - fieldValue, err := col.ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - } else { - session.Engine.LogDebug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) - if col.IsVersion && session.Statement.checkVersion { - fieldValue.SetInt(fieldValue.Int() + 1) - } else { - fieldValue.Set(reflect.ValueOf(args[idx])) - } - } - } else { - session.Engine.LogErrorf("[cacheUpdate] ERROR: column %v is not table %v's", - colName, table.Name) - } - } - - session.Engine.LogDebug("[cacheUpdate] update cache", tableName, id, bean) - cacher.PutBean(tableName, sid, bean) - } - } - session.Engine.LogDebug("[cacheUpdate] clear cached table sql:", tableName) - cacher.ClearIds(tableName) - return nil -} - -// Update records, bean's non-empty fields are updated contents, -// condiBean' non-empty filds are conditions -// CAUTION: -// 1.bool will defaultly be updated content nor conditions -// You should call UseBool if you have bool to use. -// 2.float32 & float64 may be not inexact as conditions -func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - t := rType(bean) - - var colNames []string - var args []interface{} - var table *core.Table - - // handle before update processors - for _, closure := range session.beforeClosures { - closure(bean) - } - cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used - if processor, ok := interface{}(bean).(BeforeUpdateProcessor); ok { - processor.BeforeUpdate() - } - // -- - - var err error - var isMap = t.Kind() == reflect.Map - var isStruct = t.Kind() == reflect.Struct - if isStruct { - table = session.Engine.TableInfo(bean) - session.Statement.RefTable = table - - if session.Statement.ColumnStr == "" { - colNames, args = buildUpdates(session.Engine, table, bean, false, false, - false, false, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap, session.Statement.nullableMap, - session.Statement.columnMap, true, session.Statement.unscoped) - } else { - colNames, args, err = genCols(table, session, bean, true, true) - if err != nil { - return 0, err - } - } - } else if isMap { - if session.Statement.RefTable == nil { - return 0, ErrTableNotFound - } - table = session.Statement.RefTable - colNames = make([]string, 0) - args = make([]interface{}, 0) - bValue := reflect.Indirect(reflect.ValueOf(bean)) - - for _, v := range bValue.MapKeys() { - colNames = append(colNames, session.Engine.Quote(v.String())+" = ?") - args = append(args, bValue.MapIndex(v).Interface()) - } - } else { - return 0, ErrParamsType - } - - if session.Statement.UseAutoTime && table.Updated != "" { - colNames = append(colNames, session.Engine.Quote(table.Updated)+" = ?") - col := table.UpdatedColumn() - val, t := session.Engine.NowTime2(col.SQLType.Name) - args = append(args, val) - - var colName = col.Name - if isStruct { - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } - } - - //for update action to like "column = column + ?" - incColumns := session.Statement.getInc() - for _, v := range incColumns { - colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" + ?") - args = append(args, v.arg) - } - //for update action to like "column = column - ?" - decColumns := session.Statement.getDec() - for _, v := range decColumns { - colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" - ?") - args = append(args, v.arg) - } - //for update action to like "column = expression" - exprColumns := session.Statement.getExpr() - for _, v := range exprColumns { - colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr) - } - - var condiColNames []string - var condiArgs []interface{} - - if !session.Statement.noAutoCondition && len(condiBean) > 0 { - condiColNames, condiArgs = session.Statement.buildConditions(session.Statement.RefTable, condiBean[0], true, true, false, true, false) - } - - var condition = "" - session.Statement.processIdParam() - st := session.Statement - defer session.resetStatement() - if st.WhereStr != "" { - condition = fmt.Sprintf("%v", st.WhereStr) - } - - if condition == "" { - if len(condiColNames) > 0 { - condition = fmt.Sprintf("%v", strings.Join(condiColNames, " "+session.Engine.Dialect().AndStr()+" ")) - } - } else { - if len(condiColNames) > 0 { - condition = fmt.Sprintf("(%v) %v (%v)", condition, - session.Engine.Dialect().AndStr(), strings.Join(condiColNames, " "+session.Engine.Dialect().AndStr()+" ")) - } - } - - var sqlStr, inSql string - var inArgs []interface{} - doIncVer := false - var verValue *reflect.Value - if table.Version != "" && session.Statement.checkVersion { - if condition != "" { - condition = fmt.Sprintf("WHERE (%v) %v %v = ?", condition, session.Engine.Dialect().AndStr(), - session.Engine.Quote(table.Version)) - } else { - condition = fmt.Sprintf("WHERE %v = ?", session.Engine.Quote(table.Version)) - } - inSql, inArgs = session.Statement.genInSql() - if len(inSql) > 0 { - if condition != "" { - condition += " " + session.Engine.Dialect().AndStr() + " " + inSql - } else { - condition = "WHERE " + inSql - } - } - - if st.LimitN > 0 { - condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) - } - - sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v", - session.Engine.Quote(session.Statement.TableName()), - strings.Join(colNames, ", "), - session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1", - condition) - - verValue, err = table.VersionColumn().ValueOf(bean) - if err != nil { - return 0, err - } - - condiArgs = append(condiArgs, verValue.Interface()) - doIncVer = true - } else { - if condition != "" { - condition = "WHERE " + condition - } - inSql, inArgs = session.Statement.genInSql() - if len(inSql) > 0 { - if condition != "" { - condition += " " + session.Engine.Dialect().AndStr() + " " + inSql - } else { - condition = "WHERE " + inSql - } - } - - if st.LimitN > 0 { - condition = condition + fmt.Sprintf(" LIMIT %d", st.LimitN) - } - - sqlStr = fmt.Sprintf("UPDATE %v SET %v %v", - session.Engine.Quote(session.Statement.TableName()), - strings.Join(colNames, ", "), - condition) - } - - args = append(args, st.Params...) - args = append(args, inArgs...) - args = append(args, condiArgs...) - - res, err := session.exec(sqlStr, args...) - if err != nil { - return 0, err - } else if doIncVer { - if verValue != nil && verValue.IsValid() && verValue.CanSet() { - verValue.SetInt(verValue.Int() + 1) - } - } - - if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { - cacher.ClearIds(session.Statement.TableName()) - cacher.ClearBeans(session.Statement.TableName()) - } - - // handle after update processors - if session.IsAutoCommit { - for _, closure := range session.afterClosures { - closure(bean) - } - if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { - session.Engine.LogDebug("[event]", session.Statement.TableName(), " has after update processor") - processor.AfterUpdate() - } - } else { - lenAfterClosures := len(session.afterClosures) - if lenAfterClosures > 0 { - if value, has := session.afterUpdateBeans[bean]; has && value != nil { - *value = append(*value, session.afterClosures...) - } else { - afterClosures := make([]func(interface{}), lenAfterClosures) - copy(afterClosures, session.afterClosures) - // FIXME: if bean is a map type, it will panic because map cannot be as map key - session.afterUpdateBeans[bean] = &afterClosures - } - - } else { - if _, ok := interface{}(bean).(AfterInsertProcessor); ok { - session.afterUpdateBeans[bean] = nil - } - } - } - cleanupProcessorsClosures(&session.afterClosures) // cleanup after used - // -- - - return res.RowsAffected() -} - -func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { - if session.Statement.RefTable == nil || - session.Tx != nil { - return ErrCacheFailed - } - - for _, filter := range session.Engine.dialect.Filters() { - sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) - } - - newsql := session.Statement.convertIdSql(sqlStr) - if newsql == "" { - return ErrCacheFailed - } - - cacher := session.Engine.getCacher2(session.Statement.RefTable) - tableName := session.Statement.TableName() - ids, err := core.GetCacheSql(cacher, tableName, newsql, args) - if err != nil { - resultsSlice, err := session.query(newsql, args...) - if err != nil { - return err - } - ids = make([]core.PK, 0) - if len(resultsSlice) > 0 { - for _, data := range resultsSlice { - var id int64 - var pk core.PK = make([]interface{}, 0) - for _, col := range session.Statement.RefTable.PKColumns() { - if v, ok := data[col.Name]; !ok { - return errors.New("no id") - } else { - if col.SQLType.IsText() { - pk = append(pk, string(v)) - } else if col.SQLType.IsNumeric() { - id, err = strconv.ParseInt(string(v), 10, 64) - if err != nil { - return err - } - pk = append(pk, id) - } else { - return errors.New("not supported primary key type") - } - } - } - ids = append(ids, pk) - } - } - } /*else { - session.Engine.LogDebug("delete cache sql %v", newsql) - cacher.DelIds(tableName, genSqlKey(newsql, args)) - }*/ - - for _, id := range ids { - session.Engine.LogDebug("[cacheDelete] delete cache obj", tableName, id) - sid, err := id.ToString() - if err != nil { - return err - } - cacher.DelBean(tableName, sid) - } - session.Engine.LogDebug("[cacheDelete] clear cache sql", tableName) - cacher.ClearIds(tableName) - return nil -} - -// Delete records, bean's non-empty fields are conditions -func (session *Session) Delete(bean interface{}) (int64, error) { - defer session.resetStatement() - if session.IsAutoClose { - defer session.Close() - } - - // handle before delete processors - for _, closure := range session.beforeClosures { - closure(bean) - } - cleanupProcessorsClosures(&session.beforeClosures) - - if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok { - processor.BeforeDelete() - } - // -- - - table := session.Engine.TableInfo(bean) - session.Statement.RefTable = table - var colNames []string - var args []interface{} - - if !session.Statement.noAutoCondition { - colNames, args = session.Statement.buildConditions(table, bean, true, true, false, true, false) - } - var condition = "" - var andStr = session.Engine.dialect.AndStr() - - session.Statement.processIdParam() - if session.Statement.WhereStr != "" { - condition = session.Statement.WhereStr - if len(colNames) > 0 { - condition += " " + andStr + " " + strings.Join(colNames, " "+andStr+" ") - } - } else { - condition = strings.Join(colNames, " "+andStr+" ") - } - inSql, inArgs := session.Statement.genInSql() - if len(inSql) > 0 { - if len(condition) > 0 { - condition += " " + andStr + " " - } - condition += inSql - args = append(args, inArgs...) - } - if len(condition) == 0 && session.Statement.LimitN == 0 { - return 0, ErrNeedDeletedCond - } - - var deleteSql, realSql string - var tableName = session.Engine.Quote(session.Statement.TableName()) - - if len(condition) > 0 { - deleteSql = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condition) - } else { - deleteSql = fmt.Sprintf("DELETE FROM %v", tableName) - } - - var orderSql string - if len(session.Statement.OrderStr) > 0 { - orderSql += fmt.Sprintf(" ORDER BY %s", session.Statement.OrderStr) - } - if session.Statement.LimitN > 0 { - orderSql += fmt.Sprintf(" LIMIT %d", session.Statement.LimitN) - } - - if len(orderSql) > 0 { - switch session.Engine.dialect.DBType() { - case core.POSTGRES: - inSql := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSql) - if len(condition) > 0 { - deleteSql += " AND " + inSql - } else { - deleteSql += " WHERE " + inSql - } - case core.SQLITE: - inSql := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSql) - if len(condition) > 0 { - deleteSql += " AND " + inSql - } else { - deleteSql += " WHERE " + inSql - } - // TODO: how to handle delete limit on mssql? - case core.MSSQL: - return 0, ErrNotImplemented - default: - deleteSql += orderSql - } - } - - argsForCache := make([]interface{}, 0, len(args)*2) - if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled - realSql = deleteSql - copy(argsForCache, args) - argsForCache = append(session.Statement.Params, argsForCache...) - } else { - // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache. - copy(argsForCache, args) - argsForCache = append(session.Statement.Params, argsForCache...) - - deletedColumn := table.DeletedColumn() - realSql = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", - session.Engine.Quote(session.Statement.TableName()), - session.Engine.Quote(deletedColumn.Name), - condition) - - if len(orderSql) > 0 { - switch session.Engine.dialect.DBType() { - case core.POSTGRES: - inSql := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSql) - if len(condition) > 0 { - realSql += " AND " + inSql - } else { - realSql += " WHERE " + inSql - } - case core.SQLITE: - inSql := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSql) - if len(condition) > 0 { - realSql += " AND " + inSql - } else { - realSql += " WHERE " + inSql - } - // TODO: how to handle delete limit on mssql? - case core.MSSQL: - return 0, ErrNotImplemented - default: - realSql += orderSql - } - } - - // !oinume! Insert NowTime to the head of session.Statement.Params - session.Statement.Params = append(session.Statement.Params, "") - paramsLen := len(session.Statement.Params) - copy(session.Statement.Params[1:paramsLen], session.Statement.Params[0:paramsLen-1]) - - val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name) - session.Statement.Params[0] = val - - var colName = deletedColumn.Name - session.afterClosures = append(session.afterClosures, func(bean interface{}) { - col := table.GetColumn(colName) - setColumnTime(bean, col, t) - }) - } - - args = append(session.Statement.Params, args...) - - if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache { - session.cacheDelete(deleteSql, argsForCache...) - } - - res, err := session.exec(realSql, args...) - if err != nil { - return 0, err - } - - // handle after delete processors - if session.IsAutoCommit { - for _, closure := range session.afterClosures { - closure(bean) - } - if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { - processor.AfterDelete() - } - } else { - lenAfterClosures := len(session.afterClosures) - if lenAfterClosures > 0 { - if value, has := session.afterDeleteBeans[bean]; has && value != nil { - *value = append(*value, session.afterClosures...) - } else { - afterClosures := make([]func(interface{}), lenAfterClosures) - copy(afterClosures, session.afterClosures) - session.afterDeleteBeans[bean] = &afterClosures - } - } else { - if _, ok := interface{}(bean).(AfterInsertProcessor); ok { - session.afterDeleteBeans[bean] = nil - } - } - } - cleanupProcessorsClosures(&session.afterClosures) - // -- - - return res.RowsAffected() -} - // saveLastSQL stores executed query information func (session *Session) saveLastSQL(sql string, args ...interface{}) { session.lastSQL = sql @@ -4053,192 +825,16 @@ func (session *Session) LastSQL() (string, []interface{}) { return session.lastSQL, session.lastSQLArgs } -func (s *Session) Sync2(beans ...interface{}) error { - engine := s.Engine - - tables, err := engine.DBMetas() - if err != nil { - return err +// tbName get some table's table name +func (session *Session) tbNameNoSchema(table *core.Table) string { + if len(session.Statement.AltTableName) > 0 { + return session.Statement.AltTableName } - structTables := make([]*core.Table, 0) - - for _, bean := range beans { - table := engine.TableInfo(bean) - structTables = append(structTables, table) - - var oriTable *core.Table - for _, tb := range tables { - if equalNoCase(tb.Name, table.Name) { - oriTable = tb - break - } - } - - if oriTable == nil { - err = engine.StoreEngine(s.Statement.StoreEngine).CreateTable(bean) - if err != nil { - return err - } - - err = engine.CreateUniques(bean) - if err != nil { - return err - } - - err = engine.CreateIndexes(bean) - if err != nil { - return err - } - } else { - for _, col := range table.Columns() { - var oriCol *core.Column - for _, col2 := range oriTable.Columns() { - if equalNoCase(col.Name, col2.Name) { - oriCol = col2 - break - } - } - - if oriCol != nil { - expectedType := engine.dialect.SqlType(col) - curType := engine.dialect.SqlType(oriCol) - if expectedType != curType { - if expectedType == core.Text && - strings.HasPrefix(curType, core.Varchar) { - // currently only support mysql & postgres - if engine.dialect.DBType() == core.MYSQL || - engine.dialect.DBType() == core.POSTGRES { - engine.LogInfof("Table %s column %s change type from %s to %s\n", - table.Name, col.Name, curType, expectedType) - _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) - } else { - engine.LogWarnf("Table %s column %s db type is %s, struct type is %s\n", - table.Name, col.Name, curType, expectedType) - } - } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { - if engine.dialect.DBType() == core.MYSQL { - if oriCol.Length < col.Length { - engine.LogInfof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - table.Name, col.Name, oriCol.Length, col.Length) - _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) - } - } - } else { - engine.LogWarnf("Table %s column %s db type is %s, struct type is %s", - table.Name, col.Name, curType, expectedType) - } - } else if expectedType == core.Varchar { - if engine.dialect.DBType() == core.MYSQL { - if oriCol.Length < col.Length { - engine.LogInfof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", - table.Name, col.Name, oriCol.Length, col.Length) - _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) - } - } - } - if col.Default != oriCol.Default { - engine.LogWarnf("Table %s Column %s db default is %s, struct default is %s", - table.Name, col.Name, oriCol.Default, col.Default) - } - if col.Nullable != oriCol.Nullable { - engine.LogWarnf("Table %s Column %s db nullable is %v, struct nullable is %v", - table.Name, col.Name, oriCol.Nullable, col.Nullable) - } - } else { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - err = session.addColumn(col.Name) - } - if err != nil { - return err - } - } - - var foundIndexNames = make(map[string]bool) - var addedNames = make(map[string]*core.Index) - - for name, index := range table.Indexes { - var oriIndex *core.Index - for name2, index2 := range oriTable.Indexes { - if index.Equal(index2) { - oriIndex = index2 - foundIndexNames[name2] = true - break - } - } - - if oriIndex != nil { - if oriIndex.Type != index.Type { - sql := engine.dialect.DropIndexSql(table.Name, oriIndex) - _, err = engine.Exec(sql) - if err != nil { - return err - } - oriIndex = nil - } - } - - if oriIndex == nil { - addedNames[name] = index - } - } - - for name2, index2 := range oriTable.Indexes { - if _, ok := foundIndexNames[name2]; !ok { - sql := engine.dialect.DropIndexSql(table.Name, index2) - _, err = engine.Exec(sql) - if err != nil { - return err - } - } - } - - for name, index := range addedNames { - if index.Type == core.UniqueType { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - err = session.addUnique(table.Name, name) - } else if index.Type == core.IndexType { - session := engine.NewSession() - session.Statement.RefTable = table - defer session.Close() - err = session.addIndex(table.Name, name) - } - if err != nil { - return err - } - } - } - } - - for _, table := range tables { - var oriTable *core.Table - for _, structTable := range structTables { - if equalNoCase(table.Name, structTable.Name) { - oriTable = structTable - break - } - } - - if oriTable == nil { - engine.LogWarnf("Table %s has no struct to mapping it", table.Name) - continue - } - - for _, colName := range table.ColumnsSeq() { - if oriTable.GetColumn(colName) == nil { - engine.LogWarnf("Table %s has column %s but struct has not related field", - table.Name, colName) - } - } - } - return nil + return table.Name } -// Always disable struct tag "deleted" +// Unscoped always disable struct tag "deleted" func (session *Session) Unscoped() *Session { session.Statement.Unscoped() return session diff --git a/vendor/github.com/go-xorm/xorm/session_cols.go b/vendor/github.com/go-xorm/xorm/session_cols.go new file mode 100644 index 00000000000..91185defc8c --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_cols.go @@ -0,0 +1,84 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// Incr provides a query string like "count = count + 1" +func (session *Session) Incr(column string, arg ...interface{}) *Session { + session.Statement.Incr(column, arg...) + return session +} + +// Decr provides a query string like "count = count - 1" +func (session *Session) Decr(column string, arg ...interface{}) *Session { + session.Statement.Decr(column, arg...) + return session +} + +// SetExpr provides a query string like "column = {expression}" +func (session *Session) SetExpr(column string, expression string) *Session { + session.Statement.SetExpr(column, expression) + return session +} + +// Select provides some columns to special +func (session *Session) Select(str string) *Session { + session.Statement.Select(str) + return session +} + +// Cols provides some columns to special +func (session *Session) Cols(columns ...string) *Session { + session.Statement.Cols(columns...) + return session +} + +// AllCols ask all columns +func (session *Session) AllCols() *Session { + session.Statement.AllCols() + return session +} + +// MustCols specify some columns must use even if they are empty +func (session *Session) MustCols(columns ...string) *Session { + session.Statement.MustCols(columns...) + return session +} + +// UseBool automatically retrieve condition according struct, but +// if struct has bool field, it will ignore them. So use UseBool +// to tell system to do not ignore them. +// If no parameters, it will use all the bool field of struct, or +// it will use parameters's columns +func (session *Session) UseBool(columns ...string) *Session { + session.Statement.UseBool(columns...) + return session +} + +// Distinct use for distinct columns. Caution: when you are using cache, +// distinct will not be cached because cache system need id, +// but distinct will not provide id +func (session *Session) Distinct(columns ...string) *Session { + session.Statement.Distinct(columns...) + return session +} + +// Omit Only not use the parameters as select or update columns +func (session *Session) Omit(columns ...string) *Session { + session.Statement.Omit(columns...) + return session +} + +// Nullable Set null when column is zero-value and nullable for update +func (session *Session) Nullable(columns ...string) *Session { + session.Statement.Nullable(columns...) + return session +} + +// NoAutoTime means do not automatically give created field and updated field +// the current time on the current session temporarily +func (session *Session) NoAutoTime() *Session { + session.Statement.UseAutoTime = false + return session +} diff --git a/vendor/github.com/go-xorm/xorm/session_cond.go b/vendor/github.com/go-xorm/xorm/session_cond.go new file mode 100644 index 00000000000..948a90bc1fc --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_cond.go @@ -0,0 +1,70 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import "github.com/go-xorm/builder" + +// Sql provides raw sql input parameter. When you have a complex SQL statement +// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. +// +// Deprecated: use SQL instead. +func (session *Session) Sql(query string, args ...interface{}) *Session { + return session.SQL(query, args...) +} + +// SQL provides raw sql input parameter. When you have a complex SQL statement +// and cannot use Where, Id, In and etc. Methods to describe, you can use SQL. +func (session *Session) SQL(query interface{}, args ...interface{}) *Session { + session.Statement.SQL(query, args...) + return session +} + +// Where provides custom query condition. +func (session *Session) Where(query interface{}, args ...interface{}) *Session { + session.Statement.Where(query, args...) + return session +} + +// And provides custom query condition. +func (session *Session) And(query interface{}, args ...interface{}) *Session { + session.Statement.And(query, args...) + return session +} + +// Or provides custom query condition. +func (session *Session) Or(query interface{}, args ...interface{}) *Session { + session.Statement.Or(query, args...) + return session +} + +// Id provides converting id as a query condition +// +// Deprecated: use ID instead +func (session *Session) Id(id interface{}) *Session { + return session.ID(id) +} + +// ID provides converting id as a query condition +func (session *Session) ID(id interface{}) *Session { + session.Statement.ID(id) + return session +} + +// In provides a query string like "id in (1, 2, 3)" +func (session *Session) In(column string, args ...interface{}) *Session { + session.Statement.In(column, args...) + return session +} + +// NotIn provides a query string like "id in (1, 2, 3)" +func (session *Session) NotIn(column string, args ...interface{}) *Session { + session.Statement.NotIn(column, args...) + return session +} + +// Conds returns session query conditions +func (session *Session) Conds() builder.Cond { + return session.Statement.cond +} diff --git a/vendor/github.com/go-xorm/xorm/session_convert.go b/vendor/github.com/go-xorm/xorm/session_convert.go new file mode 100644 index 00000000000..36ab465f5ee --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_convert.go @@ -0,0 +1,670 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "database/sql" + "database/sql/driver" + "encoding/json" + "errors" + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/go-xorm/core" +) + +func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) { + sdata := strings.TrimSpace(data) + var x time.Time + var err error + + if sdata == "0000-00-00 00:00:00" || + sdata == "0001-01-01 00:00:00" { + } else if !strings.ContainsAny(sdata, "- :") { // !nashtsai! has only found that mymysql driver is using this for time type column + // time stamp + sd, err := strconv.ParseInt(sdata, 10, 64) + if err == nil { + x = time.Unix(sd, 0) + // !nashtsai! HACK mymysql driver is causing Local location being change to CHAT and cause wrong time conversion + if col.TimeZone == nil { + x = x.In(session.Engine.TZLocation) + } else { + x = x.In(col.TimeZone) + } + session.Engine.logger.Debugf("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } else { + session.Engine.logger.Debugf("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } + } else if len(sdata) > 19 && strings.Contains(sdata, "-") { + x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(1) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + if err != nil { + x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(2) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } + if err != nil { + x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } + + } else if len(sdata) == 19 && strings.Contains(sdata, "-") { + x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(4) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { + x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(5) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } else if col.SQLType.Name == core.Time { + if strings.Contains(sdata, " ") { + ssd := strings.Split(sdata, " ") + sdata = ssd[1] + } + + sdata = strings.TrimSpace(sdata) + if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 { + sdata = sdata[len(sdata)-8:] + } + + st := fmt.Sprintf("2006-01-02 %v", sdata) + x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) + session.Engine.logger.Debugf("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } else { + outErr = fmt.Errorf("unsupported time format %v", sdata) + return + } + if err != nil { + outErr = fmt.Errorf("unsupported time format %v: %v", sdata, err) + return + } + outTime = x + return +} + +func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.Time, outErr error) { + return session.str2Time(col, string(data)) +} + +// convert a db data([]byte) to a field value +func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error { + if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + return structConvert.FromDB(data) + } + + if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { + return structConvert.FromDB(data) + } + + var v interface{} + key := col.Name + fieldType := fieldValue.Type() + + switch fieldType.Kind() { + case reflect.Complex64, reflect.Complex128: + x := reflect.New(fieldType) + if len(data) > 0 { + err := json.Unmarshal(data, x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return err + } + fieldValue.Set(x.Elem()) + } + case reflect.Slice, reflect.Array, reflect.Map: + v = data + t := fieldType.Elem() + k := t.Kind() + if col.SQLType.IsText() { + x := reflect.New(fieldType) + if len(data) > 0 { + err := json.Unmarshal(data, x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return err + } + fieldValue.Set(x.Elem()) + } + } else if col.SQLType.IsBlob() { + if k == reflect.Uint8 { + fieldValue.Set(reflect.ValueOf(v)) + } else { + x := reflect.New(fieldType) + if len(data) > 0 { + err := json.Unmarshal(data, x.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return err + } + fieldValue.Set(x.Elem()) + } + } + } else { + return ErrUnSupportedType + } + case reflect.String: + fieldValue.SetString(string(data)) + case reflect.Bool: + d := string(data) + v, err := strconv.ParseBool(d) + if err != nil { + return fmt.Errorf("arg %v as bool: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(v)) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + sdata := string(data) + var x int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API + if len(data) == 1 { + x = int64(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x, err = strconv.ParseInt(sdata, 16, 64) + } else if strings.HasPrefix(sdata, "0") { + x, err = strconv.ParseInt(sdata, 8, 64) + } else if strings.EqualFold(sdata, "true") { + x = 1 + } else if strings.EqualFold(sdata, "false") { + x = 0 + } else { + x, err = strconv.ParseInt(sdata, 10, 64) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.SetInt(x) + case reflect.Float32, reflect.Float64: + x, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return fmt.Errorf("arg %v as float64: %s", key, err.Error()) + } + fieldValue.SetFloat(x) + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.SetUint(x) + //Currently only support Time type + case reflect.Struct: + // !! 增加支持sql.Scanner接口的结构,如sql.NullString + if nulVal, ok := fieldValue.Addr().Interface().(sql.Scanner); ok { + if err := nulVal.Scan(data); err != nil { + return fmt.Errorf("sql.Scan(%v) failed: %s ", data, err.Error()) + } + } else { + if fieldType.ConvertibleTo(core.TimeType) { + x, err := session.byte2Time(col, data) + if err != nil { + return err + } + v = x + fieldValue.Set(reflect.ValueOf(v).Convert(fieldType)) + } else if session.Statement.UseCascade { + table := session.Engine.autoMapType(*fieldValue) + if table != nil { + // TODO: current only support 1 primary key + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + var err error + pk[0], err = str2PK(string(data), rawValueType) + if err != nil { + return err + } + + if !isPKZero(pk) { + // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch + // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne + // property to be fetched lazily + structInter := reflect.New(fieldValue.Type()) + newsession := session.Engine.NewSession() + defer newsession.Close() + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) + if err != nil { + return err + } + if has { + v = structInter.Elem().Interface() + fieldValue.Set(reflect.ValueOf(v)) + } else { + return errors.New("cascade obj is not exist") + } + } + } else { + return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) + } + } + } + case reflect.Ptr: + // !nashtsai! TODO merge duplicated codes above + //typeStr := fieldType.String() + switch fieldType.Elem().Kind() { + // case "*string": + case core.StringType.Kind(): + x := string(data) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*bool": + case core.BoolType.Kind(): + d := string(data) + v, err := strconv.ParseBool(d) + if err != nil { + return fmt.Errorf("arg %v as bool: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&v).Convert(fieldType)) + // case "*complex64": + case core.Complex64Type.Kind(): + var x complex64 + if len(data) > 0 { + err := json.Unmarshal(data, &x) + if err != nil { + session.Engine.logger.Error(err) + return err + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + } + // case "*complex128": + case core.Complex128Type.Kind(): + var x complex128 + if len(data) > 0 { + err := json.Unmarshal(data, &x) + if err != nil { + session.Engine.logger.Error(err) + return err + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + } + // case "*float64": + case core.Float64Type.Kind(): + x, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return fmt.Errorf("arg %v as float64: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*float32": + case core.Float32Type.Kind(): + var x float32 + x1, err := strconv.ParseFloat(string(data), 32) + if err != nil { + return fmt.Errorf("arg %v as float32: %s", key, err.Error()) + } + x = float32(x1) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*uint64": + case core.Uint64Type.Kind(): + var x uint64 + x, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*uint": + case core.UintType.Kind(): + var x uint + x1, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + x = uint(x1) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*uint32": + case core.Uint32Type.Kind(): + var x uint32 + x1, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + x = uint32(x1) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*uint8": + case core.Uint8Type.Kind(): + var x uint8 + x1, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + x = uint8(x1) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*uint16": + case core.Uint16Type.Kind(): + var x uint16 + x1, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + x = uint16(x1) + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*int64": + case core.Int64Type.Kind(): + sdata := string(data) + var x int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + strings.Contains(session.Engine.DriverName(), "mysql") { + if len(data) == 1 { + x = int64(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x, err = strconv.ParseInt(sdata, 16, 64) + } else if strings.HasPrefix(sdata, "0") { + x, err = strconv.ParseInt(sdata, 8, 64) + } else { + x, err = strconv.ParseInt(sdata, 10, 64) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*int": + case core.IntType.Kind(): + sdata := string(data) + var x int + var x1 int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + strings.Contains(session.Engine.DriverName(), "mysql") { + if len(data) == 1 { + x = int(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x1, err = strconv.ParseInt(sdata, 16, 64) + x = int(x1) + } else if strings.HasPrefix(sdata, "0") { + x1, err = strconv.ParseInt(sdata, 8, 64) + x = int(x1) + } else { + x1, err = strconv.ParseInt(sdata, 10, 64) + x = int(x1) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*int32": + case core.Int32Type.Kind(): + sdata := string(data) + var x int32 + var x1 int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + session.Engine.dialect.DBType() == core.MYSQL { + if len(data) == 1 { + x = int32(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x1, err = strconv.ParseInt(sdata, 16, 64) + x = int32(x1) + } else if strings.HasPrefix(sdata, "0") { + x1, err = strconv.ParseInt(sdata, 8, 64) + x = int32(x1) + } else { + x1, err = strconv.ParseInt(sdata, 10, 64) + x = int32(x1) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*int8": + case core.Int8Type.Kind(): + sdata := string(data) + var x int8 + var x1 int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + strings.Contains(session.Engine.DriverName(), "mysql") { + if len(data) == 1 { + x = int8(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x1, err = strconv.ParseInt(sdata, 16, 64) + x = int8(x1) + } else if strings.HasPrefix(sdata, "0") { + x1, err = strconv.ParseInt(sdata, 8, 64) + x = int8(x1) + } else { + x1, err = strconv.ParseInt(sdata, 10, 64) + x = int8(x1) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*int16": + case core.Int16Type.Kind(): + sdata := string(data) + var x int16 + var x1 int64 + var err error + // for mysql, when use bit, it returned \x01 + if col.SQLType.Name == core.Bit && + strings.Contains(session.Engine.DriverName(), "mysql") { + if len(data) == 1 { + x = int16(data[0]) + } else { + x = 0 + } + } else if strings.HasPrefix(sdata, "0x") { + x1, err = strconv.ParseInt(sdata, 16, 64) + x = int16(x1) + } else if strings.HasPrefix(sdata, "0") { + x1, err = strconv.ParseInt(sdata, 8, 64) + x = int16(x1) + } else { + x1, err = strconv.ParseInt(sdata, 10, 64) + x = int16(x1) + } + if err != nil { + return fmt.Errorf("arg %v as int: %s", key, err.Error()) + } + fieldValue.Set(reflect.ValueOf(&x).Convert(fieldType)) + // case "*SomeStruct": + case reflect.Struct: + switch fieldType { + // case "*.time.Time": + case core.PtrTimeType: + x, err := session.byte2Time(col, data) + if err != nil { + return err + } + v = x + fieldValue.Set(reflect.ValueOf(&x)) + default: + if session.Statement.UseCascade { + structInter := reflect.New(fieldType.Elem()) + table := session.Engine.autoMapType(structInter.Elem()) + if table != nil { + if len(table.PrimaryKeys) > 1 { + panic("unsupported composited primary key cascade") + } + var pk = make(core.PK, len(table.PrimaryKeys)) + var err error + rawValueType := table.ColumnType(table.PKColumns()[0].FieldName) + pk[0], err = str2PK(string(data), rawValueType) + if err != nil { + return err + } + + if !isPKZero(pk) { + // !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch + // however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne + // property to be fetched lazily + newsession := session.Engine.NewSession() + defer newsession.Close() + has, err := newsession.Id(pk).NoCascade().Get(structInter.Interface()) + if err != nil { + return err + } + if has { + v = structInter.Interface() + fieldValue.Set(reflect.ValueOf(v)) + } else { + return errors.New("cascade obj is not exist") + } + } + } + } else { + return fmt.Errorf("unsupported struct type in Scan: %s", fieldValue.Type().String()) + } + } + default: + return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) + } + default: + return fmt.Errorf("unsupported type in Scan: %s", fieldValue.Type().String()) + } + + return nil +} + +// convert a field value of a struct to interface for put into db +func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) { + if fieldValue.CanAddr() { + if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { + data, err := fieldConvert.ToDB() + if err != nil { + return 0, err + } + if col.SQLType.IsBlob() { + return data, nil + } + return string(data), nil + } + } + + if fieldConvert, ok := fieldValue.Interface().(core.Conversion); ok { + data, err := fieldConvert.ToDB() + if err != nil { + return 0, err + } + if col.SQLType.IsBlob() { + return data, nil + } + return string(data), nil + } + + fieldType := fieldValue.Type() + k := fieldType.Kind() + if k == reflect.Ptr { + if fieldValue.IsNil() { + return nil, nil + } else if !fieldValue.IsValid() { + session.Engine.logger.Warn("the field[", col.FieldName, "] is invalid") + return nil, nil + } else { + // !nashtsai! deference pointer type to instance type + fieldValue = fieldValue.Elem() + fieldType = fieldValue.Type() + k = fieldType.Kind() + } + } + + switch k { + case reflect.Bool: + return fieldValue.Bool(), nil + case reflect.String: + return fieldValue.String(), nil + case reflect.Struct: + if fieldType.ConvertibleTo(core.TimeType) { + t := fieldValue.Convert(core.TimeType).Interface().(time.Time) + if session.Engine.dialect.DBType() == core.MSSQL { + if t.IsZero() { + return nil, nil + } + } + tf := session.Engine.FormatTime(col.SQLType.Name, t) + return tf, nil + } + + if !col.SQLType.IsJson() { + // !! 增加支持driver.Valuer接口的结构,如sql.NullString + if v, ok := fieldValue.Interface().(driver.Valuer); ok { + return v.Value() + } + + fieldTable := session.Engine.autoMapType(fieldValue) + if len(fieldTable.PrimaryKeys) == 1 { + pkField := reflect.Indirect(fieldValue).FieldByName(fieldTable.PKColumns()[0].FieldName) + return pkField.Interface(), nil + } + return 0, fmt.Errorf("no primary key for col %v", col.Name) + } + + if col.SQLType.IsText() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return 0, err + } + return string(bytes), nil + } else if col.SQLType.IsBlob() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return 0, err + } + return bytes, nil + } + return nil, fmt.Errorf("Unsupported type %v", fieldValue.Type()) + case reflect.Complex64, reflect.Complex128: + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return 0, err + } + return string(bytes), nil + case reflect.Array, reflect.Slice, reflect.Map: + if !fieldValue.IsValid() { + return fieldValue.Interface(), nil + } + + if col.SQLType.IsText() { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return 0, err + } + return string(bytes), nil + } else if col.SQLType.IsBlob() { + var bytes []byte + var err error + if (k == reflect.Array || k == reflect.Slice) && + (fieldValue.Type().Elem().Kind() == reflect.Uint8) { + bytes = fieldValue.Bytes() + } else { + bytes, err = json.Marshal(fieldValue.Interface()) + if err != nil { + session.Engine.logger.Error(err) + return 0, err + } + } + return bytes, nil + } + return nil, ErrUnSupportedType + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: + return int64(fieldValue.Uint()), nil + default: + return fieldValue.Interface(), nil + } +} diff --git a/vendor/github.com/go-xorm/xorm/session_delete.go b/vendor/github.com/go-xorm/xorm/session_delete.go new file mode 100644 index 00000000000..1c458fe1ea4 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_delete.go @@ -0,0 +1,238 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "fmt" + "strconv" + + "github.com/go-xorm/core" +) + +func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { + if session.Statement.RefTable == nil || + session.Tx != nil { + return ErrCacheFailed + } + + for _, filter := range session.Engine.dialect.Filters() { + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) + } + + newsql := session.Statement.convertIDSQL(sqlStr) + if newsql == "" { + return ErrCacheFailed + } + + cacher := session.Engine.getCacher2(session.Statement.RefTable) + tableName := session.Statement.TableName() + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) + if err != nil { + resultsSlice, err := session.query(newsql, args...) + if err != nil { + return err + } + ids = make([]core.PK, 0) + if len(resultsSlice) > 0 { + for _, data := range resultsSlice { + var id int64 + var pk core.PK = make([]interface{}, 0) + for _, col := range session.Statement.RefTable.PKColumns() { + if v, ok := data[col.Name]; !ok { + return errors.New("no id") + } else if col.SQLType.IsText() { + pk = append(pk, string(v)) + } else if col.SQLType.IsNumeric() { + id, err = strconv.ParseInt(string(v), 10, 64) + if err != nil { + return err + } + pk = append(pk, id) + } else { + return errors.New("not supported primary key type") + } + } + ids = append(ids, pk) + } + } + } /*else { + session.Engine.LogDebug("delete cache sql %v", newsql) + cacher.DelIds(tableName, genSqlKey(newsql, args)) + }*/ + + for _, id := range ids { + session.Engine.logger.Debug("[cacheDelete] delete cache obj", tableName, id) + sid, err := id.ToString() + if err != nil { + return err + } + cacher.DelBean(tableName, sid) + } + session.Engine.logger.Debug("[cacheDelete] clear cache sql", tableName) + cacher.ClearIds(tableName) + return nil +} + +// Delete records, bean's non-empty fields are conditions +func (session *Session) Delete(bean interface{}) (int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + session.Statement.setRefValue(rValue(bean)) + var table = session.Statement.RefTable + + // handle before delete processors + for _, closure := range session.beforeClosures { + closure(bean) + } + cleanupProcessorsClosures(&session.beforeClosures) + + if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok { + processor.BeforeDelete() + } + + // -- + condSQL, condArgs, _ := session.Statement.genConds(bean) + if len(condSQL) == 0 && session.Statement.LimitN == 0 { + return 0, ErrNeedDeletedCond + } + + var tableName = session.Engine.Quote(session.Statement.TableName()) + var deleteSQL string + if len(condSQL) > 0 { + deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL) + } else { + deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName) + } + + var orderSQL string + if len(session.Statement.OrderStr) > 0 { + orderSQL += fmt.Sprintf(" ORDER BY %s", session.Statement.OrderStr) + } + if session.Statement.LimitN > 0 { + orderSQL += fmt.Sprintf(" LIMIT %d", session.Statement.LimitN) + } + + if len(orderSQL) > 0 { + switch session.Engine.dialect.DBType() { + case core.POSTGRES: + inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) + if len(condSQL) > 0 { + deleteSQL += " AND " + inSQL + } else { + deleteSQL += " WHERE " + inSQL + } + case core.SQLITE: + inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) + if len(condSQL) > 0 { + deleteSQL += " AND " + inSQL + } else { + deleteSQL += " WHERE " + inSQL + } + // TODO: how to handle delete limit on mssql? + case core.MSSQL: + return 0, ErrNotImplemented + default: + deleteSQL += orderSQL + } + } + + var realSQL string + argsForCache := make([]interface{}, 0, len(condArgs)*2) + if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled + realSQL = deleteSQL + copy(argsForCache, condArgs) + argsForCache = append(condArgs, argsForCache...) + } else { + // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache. + copy(argsForCache, condArgs) + argsForCache = append(condArgs, argsForCache...) + + deletedColumn := table.DeletedColumn() + realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", + session.Engine.Quote(session.Statement.TableName()), + session.Engine.Quote(deletedColumn.Name), + condSQL) + + if len(orderSQL) > 0 { + switch session.Engine.dialect.DBType() { + case core.POSTGRES: + inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL) + if len(condSQL) > 0 { + realSQL += " AND " + inSQL + } else { + realSQL += " WHERE " + inSQL + } + case core.SQLITE: + inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL) + if len(condSQL) > 0 { + realSQL += " AND " + inSQL + } else { + realSQL += " WHERE " + inSQL + } + // TODO: how to handle delete limit on mssql? + case core.MSSQL: + return 0, ErrNotImplemented + default: + realSQL += orderSQL + } + } + + // !oinume! Insert NowTime to the head of session.Statement.Params + condArgs = append(condArgs, "") + paramsLen := len(condArgs) + copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1]) + + val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name) + condArgs[0] = val + + var colName = deletedColumn.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } + + if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache { + session.cacheDelete(deleteSQL, argsForCache...) + } + + res, err := session.exec(realSQL, condArgs...) + if err != nil { + return 0, err + } + + // handle after delete processors + if session.IsAutoCommit { + for _, closure := range session.afterClosures { + closure(bean) + } + if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { + processor.AfterDelete() + } + } else { + lenAfterClosures := len(session.afterClosures) + if lenAfterClosures > 0 { + if value, has := session.afterDeleteBeans[bean]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + session.afterDeleteBeans[bean] = &afterClosures + } + } else { + if _, ok := interface{}(bean).(AfterDeleteProcessor); ok { + session.afterDeleteBeans[bean] = nil + } + } + } + cleanupProcessorsClosures(&session.afterClosures) + // -- + + return res.RowsAffected() +} diff --git a/vendor/github.com/go-xorm/xorm/session_find.go b/vendor/github.com/go-xorm/xorm/session_find.go new file mode 100644 index 00000000000..748e319f793 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_find.go @@ -0,0 +1,461 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-xorm/builder" + "github.com/go-xorm/core" +) + +const ( + tpStruct = iota + tpNonStruct +) + +// Find retrieve records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct +func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() != reflect.Slice && sliceValue.Kind() != reflect.Map { + return errors.New("needs a pointer to a slice or a map") + } + + sliceElementType := sliceValue.Type().Elem() + + var tp = tpStruct + if session.Statement.RefTable == nil { + if sliceElementType.Kind() == reflect.Ptr { + if sliceElementType.Elem().Kind() == reflect.Struct { + pv := reflect.New(sliceElementType.Elem()) + session.Statement.setRefValue(pv.Elem()) + } else { + tp = tpNonStruct + } + } else if sliceElementType.Kind() == reflect.Struct { + pv := reflect.New(sliceElementType) + session.Statement.setRefValue(pv.Elem()) + } else { + tp = tpNonStruct + } + } + + var table = session.Statement.RefTable + + var addedTableName = (len(session.Statement.JoinStr) > 0) + var autoCond builder.Cond + if tp == tpStruct { + if !session.Statement.noAutoCondition && len(condiBean) > 0 { + var err error + autoCond, err = session.Statement.buildConds(table, condiBean[0], true, true, false, true, addedTableName) + if err != nil { + panic(err) + } + } else { + // !oinume! Add " IS NULL" to WHERE whatever condiBean is given. + // See https://github.com/go-xorm/xorm/issues/179 + if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled + var colName = session.Engine.Quote(col.Name) + if addedTableName { + var nm = session.Statement.TableName() + if len(session.Statement.TableAlias) > 0 { + nm = session.Statement.TableAlias + } + colName = session.Engine.Quote(nm) + "." + colName + } + if session.Engine.dialect.DBType() == core.MSSQL { + autoCond = builder.IsNull{colName} + } else { + autoCond = builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"}) + } + } + } + } + + var sqlStr string + var args []interface{} + if session.Statement.RawSQL == "" { + if len(session.Statement.TableName()) <= 0 { + return ErrTableNotFound + } + + var columnStr = session.Statement.ColumnStr + if len(session.Statement.selectStr) > 0 { + columnStr = session.Statement.selectStr + } else { + if session.Statement.JoinStr == "" { + if columnStr == "" { + if session.Statement.GroupByStr != "" { + columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) + } else { + columnStr = session.Statement.genColumnStr() + } + } + } else { + if columnStr == "" { + if session.Statement.GroupByStr != "" { + columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1)) + } else { + columnStr = "*" + } + } + } + if columnStr == "" { + columnStr = "*" + } + } + + condSQL, condArgs, _ := builder.ToSQL(session.Statement.cond.And(autoCond)) + + args = append(session.Statement.joinArgs, condArgs...) + sqlStr = session.Statement.genSelectSQL(columnStr, condSQL) + // for mssql and use limit + qs := strings.Count(sqlStr, "?") + if len(args)*2 == qs { + args = append(args, args...) + } + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + var err error + if session.canCache() { + if cacher := session.Engine.getCacher2(table); cacher != nil && + !session.Statement.IsDistinct && + !session.Statement.unscoped { + err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) + if err != ErrCacheFailed { + return err + } + err = nil // !nashtsai! reset err to nil for ErrCacheFailed + session.Engine.logger.Warn("Cache Find Failed") + } + } + + return session.noCacheFind(table, sliceValue, sqlStr, args...) +} + +func (session *Session) noCacheFind(table *core.Table, containerValue reflect.Value, sqlStr string, args ...interface{}) error { + var rawRows *core.Rows + var err error + + session.queryPreprocess(&sqlStr, args...) + if session.IsAutoCommit { + _, rawRows, err = session.innerQuery(sqlStr, args...) + } else { + rawRows, err = session.Tx.Query(sqlStr, args...) + } + if err != nil { + return err + } + defer rawRows.Close() + + fields, err := rawRows.Columns() + if err != nil { + return err + } + + var newElemFunc func(fields []string) reflect.Value + elemType := containerValue.Type().Elem() + var isPointer bool + if elemType.Kind() == reflect.Ptr { + isPointer = true + elemType = elemType.Elem() + } + if elemType.Kind() == reflect.Ptr { + return errors.New("pointer to pointer is not supported") + } + + newElemFunc = func(fields []string) reflect.Value { + switch elemType.Kind() { + case reflect.Slice: + slice := reflect.MakeSlice(elemType, len(fields), len(fields)) + x := reflect.New(slice.Type()) + x.Elem().Set(slice) + return x + case reflect.Map: + mp := reflect.MakeMap(elemType) + x := reflect.New(mp.Type()) + x.Elem().Set(mp) + return x + } + return reflect.New(elemType) + } + + var containerValueSetFunc func(*reflect.Value, core.PK) error + + if containerValue.Kind() == reflect.Slice { + containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error { + if isPointer { + containerValue.Set(reflect.Append(containerValue, newValue.Elem().Addr())) + } else { + containerValue.Set(reflect.Append(containerValue, newValue.Elem())) + } + return nil + } + } else { + keyType := containerValue.Type().Key() + if len(table.PrimaryKeys) == 0 { + return errors.New("don't support multiple primary key's map has non-slice key type") + } + if len(table.PrimaryKeys) > 1 && keyType.Kind() != reflect.Slice { + return errors.New("don't support multiple primary key's map has non-slice key type") + } + + containerValueSetFunc = func(newValue *reflect.Value, pk core.PK) error { + keyValue := reflect.New(keyType) + err := convertPKToValue(table, keyValue.Interface(), pk) + if err != nil { + return err + } + if isPointer { + containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem().Addr()) + } else { + containerValue.SetMapIndex(keyValue.Elem(), newValue.Elem()) + } + return nil + } + } + + if elemType.Kind() == reflect.Struct { + var newValue = newElemFunc(fields) + dataStruct := rValue(newValue.Interface()) + return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, containerValueSetFunc) + } + + for rawRows.Next() { + var newValue = newElemFunc(fields) + bean := newValue.Interface() + + switch elemType.Kind() { + case reflect.Slice: + err = rawRows.ScanSlice(bean) + case reflect.Map: + err = rawRows.ScanMap(bean) + default: + err = rawRows.Scan(bean) + } + + if err != nil { + return err + } + + if err := containerValueSetFunc(&newValue, nil); err != nil { + return err + } + } + return nil +} + +func convertPKToValue(table *core.Table, dst interface{}, pk core.PK) error { + cols := table.PKColumns() + if len(cols) == 1 { + return convertAssign(dst, pk[0]) + } + + dst = pk + return nil +} + +func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) { + if !session.canCache() || + indexNoCase(sqlStr, "having") != -1 || + indexNoCase(sqlStr, "group by") != -1 { + return ErrCacheFailed + } + + for _, filter := range session.Engine.dialect.Filters() { + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) + } + + newsql := session.Statement.convertIDSQL(sqlStr) + if newsql == "" { + return ErrCacheFailed + } + + tableName := session.Statement.TableName() + + table := session.Statement.RefTable + cacher := session.Engine.getCacher2(table) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) + if err != nil { + rows, err := session.DB().Query(newsql, args...) + if err != nil { + return err + } + defer rows.Close() + + var i int + ids = make([]core.PK, 0) + for rows.Next() { + i++ + if i > 500 { + session.Engine.logger.Debug("[cacheFind] ids length > 500, no cache") + return ErrCacheFailed + } + var res = make([]string, len(table.PrimaryKeys)) + err = rows.ScanSlice(&res) + if err != nil { + return err + } + + var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) + for i, col := range table.PKColumns() { + if col.SQLType.IsNumeric() { + n, err := strconv.ParseInt(res[i], 10, 64) + if err != nil { + return err + } + pk[i] = n + } else if col.SQLType.IsText() { + pk[i] = res[i] + } else { + return errors.New("not supported") + } + } + + ids = append(ids, pk) + } + + session.Engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, newsql, args) + err = core.PutCacheSql(cacher, ids, tableName, newsql, args) + if err != nil { + return err + } + } else { + session.Engine.logger.Debug("[cacheFind] cache hit sql:", newsql, args) + } + + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + + ididxes := make(map[string]int) + var ides []core.PK + var temps = make([]interface{}, len(ids)) + + for idx, id := range ids { + sid, err := id.ToString() + if err != nil { + return err + } + bean := cacher.GetBean(tableName, sid) + if bean == nil { + ides = append(ides, id) + ididxes[sid] = idx + } else { + session.Engine.logger.Debug("[cacheFind] cache hit bean:", tableName, id, bean) + + pk := session.Engine.IdOf(bean) + xid, err := pk.ToString() + if err != nil { + return err + } + + if sid != xid { + session.Engine.logger.Error("[cacheFind] error cache", xid, sid, bean) + return ErrCacheFailed + } + temps[idx] = bean + } + } + + if len(ides) > 0 { + newSession := session.Engine.NewSession() + defer newSession.Close() + + slices := reflect.New(reflect.SliceOf(t)) + beans := slices.Interface() + + if len(table.PrimaryKeys) == 1 { + ff := make([]interface{}, 0, len(ides)) + for _, ie := range ides { + ff = append(ff, ie[0]) + } + + newSession.In("`"+table.PrimaryKeys[0]+"`", ff...) + } else { + for _, ie := range ides { + cond := builder.NewCond() + for i, name := range table.PrimaryKeys { + cond = cond.And(builder.Eq{"`" + name + "`": ie[i]}) + } + newSession.Or(cond) + } + } + + err = newSession.NoCache().Find(beans) + if err != nil { + return err + } + + vs := reflect.Indirect(reflect.ValueOf(beans)) + for i := 0; i < vs.Len(); i++ { + rv := vs.Index(i) + if rv.Kind() != reflect.Ptr { + rv = rv.Addr() + } + id := session.Engine.IdOfV(rv) + sid, err := id.ToString() + if err != nil { + return err + } + + bean := rv.Interface() + temps[ididxes[sid]] = bean + session.Engine.logger.Debug("[cacheFind] cache bean:", tableName, id, bean, temps) + cacher.PutBean(tableName, sid, bean) + } + } + + for j := 0; j < len(temps); j++ { + bean := temps[j] + if bean == nil { + session.Engine.logger.Warn("[cacheFind] cache no hit:", tableName, ids[j], temps) + // return errors.New("cache error") // !nashtsai! no need to return error, but continue instead + continue + } + if sliceValue.Kind() == reflect.Slice { + if t.Kind() == reflect.Ptr { + sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(bean))) + } else { + sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean)))) + } + } else if sliceValue.Kind() == reflect.Map { + var key = ids[j] + keyType := sliceValue.Type().Key() + var ikey interface{} + if len(key) == 1 { + ikey, err = str2PK(fmt.Sprintf("%v", key[0]), keyType) + if err != nil { + return err + } + } else { + if keyType.Kind() != reflect.Slice { + return errors.New("table have multiple primary keys, key is not core.PK or slice") + } + ikey = key + } + + if t.Kind() == reflect.Ptr { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean)) + } else { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean))) + } + } + } + + return nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_get.go b/vendor/github.com/go-xorm/xorm/session_get.go new file mode 100644 index 00000000000..0c78ed94835 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_get.go @@ -0,0 +1,192 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "reflect" + "strconv" + + "github.com/go-xorm/core" +) + +// Get retrieve one record from database, bean's non-empty fields +// will be as conditions +func (session *Session) Get(bean interface{}) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + beanValue := reflect.ValueOf(bean) + if beanValue.Kind() != reflect.Ptr { + return false, errors.New("needs a pointer to a struct") + } + + // FIXME: remove this after support non-struct Get + if beanValue.Elem().Kind() != reflect.Struct { + return false, errors.New("needs a pointer to a struct") + } + + if beanValue.Elem().Kind() == reflect.Struct { + session.Statement.setRefValue(beanValue.Elem()) + } + + var sqlStr string + var args []interface{} + + if session.Statement.RawSQL == "" { + if len(session.Statement.TableName()) <= 0 { + return false, ErrTableNotFound + } + session.Statement.Limit(1) + sqlStr, args = session.Statement.genGetSQL(bean) + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + if session.canCache() { + if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && + !session.Statement.unscoped { + has, err := session.cacheGet(bean, sqlStr, args...) + if err != ErrCacheFailed { + return has, err + } + } + } + + return session.nocacheGet(beanValue.Elem().Kind(), bean, sqlStr, args...) +} + +func (session *Session) nocacheGet(beanKind reflect.Kind, bean interface{}, sqlStr string, args ...interface{}) (bool, error) { + var rawRows *core.Rows + var err error + session.queryPreprocess(&sqlStr, args...) + if session.IsAutoCommit { + _, rawRows, err = session.innerQuery(sqlStr, args...) + } else { + rawRows, err = session.Tx.Query(sqlStr, args...) + } + if err != nil { + return false, err + } + + defer rawRows.Close() + + if rawRows.Next() { + fields, err := rawRows.Columns() + if err != nil { + // WARN: Alougth rawRows return true, but get fields failed + return true, err + } + + switch beanKind { + case reflect.Struct: + dataStruct := rValue(bean) + session.Statement.setRefValue(dataStruct) + _, err = session.row2Bean(rawRows, fields, len(fields), bean, &dataStruct, session.Statement.RefTable) + case reflect.Slice: + err = rawRows.ScanSlice(bean) + case reflect.Map: + err = rawRows.ScanMap(bean) + default: + err = rawRows.Scan(bean) + } + + return true, err + } + return false, nil +} + +func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) { + // if has no reftable, then don't use cache currently + if !session.canCache() { + return false, ErrCacheFailed + } + + for _, filter := range session.Engine.dialect.Filters() { + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) + } + newsql := session.Statement.convertIDSQL(sqlStr) + if newsql == "" { + return false, ErrCacheFailed + } + + cacher := session.Engine.getCacher2(session.Statement.RefTable) + tableName := session.Statement.TableName() + session.Engine.logger.Debug("[cacheGet] find sql:", newsql, args) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) + table := session.Statement.RefTable + if err != nil { + var res = make([]string, len(table.PrimaryKeys)) + rows, err := session.DB().Query(newsql, args...) + if err != nil { + return false, err + } + defer rows.Close() + + if rows.Next() { + err = rows.ScanSlice(&res) + if err != nil { + return false, err + } + } else { + return false, ErrCacheFailed + } + + var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) + for i, col := range table.PKColumns() { + if col.SQLType.IsText() { + pk[i] = res[i] + } else if col.SQLType.IsNumeric() { + n, err := strconv.ParseInt(res[i], 10, 64) + if err != nil { + return false, err + } + pk[i] = n + } else { + return false, errors.New("unsupported") + } + } + + ids = []core.PK{pk} + session.Engine.logger.Debug("[cacheGet] cache ids:", newsql, ids) + err = core.PutCacheSql(cacher, ids, tableName, newsql, args) + if err != nil { + return false, err + } + } else { + session.Engine.logger.Debug("[cacheGet] cache hit sql:", newsql) + } + + if len(ids) > 0 { + structValue := reflect.Indirect(reflect.ValueOf(bean)) + id := ids[0] + session.Engine.logger.Debug("[cacheGet] get bean:", tableName, id) + sid, err := id.ToString() + if err != nil { + return false, err + } + cacheBean := cacher.GetBean(tableName, sid) + if cacheBean == nil { + cacheBean = bean + has, err = session.nocacheGet(reflect.Struct, cacheBean, sqlStr, args...) + if err != nil || !has { + return has, err + } + + session.Engine.logger.Debug("[cacheGet] cache bean:", tableName, id, cacheBean) + cacher.PutBean(tableName, sid, cacheBean) + } else { + session.Engine.logger.Debug("[cacheGet] cache hit bean:", tableName, id, cacheBean) + has = true + } + structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean))) + + return has, nil + } + return false, nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_insert.go b/vendor/github.com/go-xorm/xorm/session_insert.go new file mode 100644 index 00000000000..5b607b1fecb --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_insert.go @@ -0,0 +1,543 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-xorm/core" +) + +// Insert insert one or more beans +func (session *Session) Insert(beans ...interface{}) (int64, error) { + var affected int64 + var err error + + if session.IsAutoClose { + defer session.Close() + } + defer session.resetStatement() + + for _, bean := range beans { + sliceValue := reflect.Indirect(reflect.ValueOf(bean)) + if sliceValue.Kind() == reflect.Slice { + size := sliceValue.Len() + if size > 0 { + if session.Engine.SupportInsertMany() { + cnt, err := session.innerInsertMulti(bean) + if err != nil { + return affected, err + } + affected += cnt + } else { + for i := 0; i < size; i++ { + cnt, err := session.innerInsert(sliceValue.Index(i).Interface()) + if err != nil { + return affected, err + } + affected += cnt + } + } + } + } else { + cnt, err := session.innerInsert(bean) + if err != nil { + return affected, err + } + affected += cnt + } + } + + return affected, err +} + +func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) { + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() != reflect.Slice { + return 0, errors.New("needs a pointer to a slice") + } + + if sliceValue.Len() <= 0 { + return 0, errors.New("could not insert a empty slice") + } + + session.Statement.setRefValue(sliceValue.Index(0)) + + if len(session.Statement.TableName()) <= 0 { + return 0, ErrTableNotFound + } + + table := session.Statement.RefTable + size := sliceValue.Len() + + var colNames []string + var colMultiPlaces []string + var args []interface{} + var cols []*core.Column + + for i := 0; i < size; i++ { + v := sliceValue.Index(i) + vv := reflect.Indirect(v) + elemValue := v.Interface() + var colPlaces []string + + // handle BeforeInsertProcessor + // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? + for _, closure := range session.beforeClosures { + closure(elemValue) + } + + if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok { + processor.BeforeInsert() + } + // -- + + if i == 0 { + for _, col := range table.Columns() { + ptrFieldValue, err := col.ValueOfV(&vv) + if err != nil { + return 0, err + } + fieldValue := *ptrFieldValue + if col.IsAutoIncrement && isZero(fieldValue.Interface()) { + continue + } + if col.MapType == core.ONLYFROMDB { + continue + } + if col.IsDeleted { + continue + } + if session.Statement.ColumnStr != "" { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok { + continue + } + } + if session.Statement.OmitStr != "" { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); ok { + continue + } + } + if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.Statement.checkVersion { + args = append(args, 1) + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnInt(bean, col, 1) + }) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return 0, err + } + args = append(args, arg) + } + + colNames = append(colNames, col.Name) + cols = append(cols, col) + colPlaces = append(colPlaces, "?") + } + } else { + for _, col := range cols { + ptrFieldValue, err := col.ValueOfV(&vv) + if err != nil { + return 0, err + } + fieldValue := *ptrFieldValue + + if col.IsAutoIncrement && isZero(fieldValue.Interface()) { + continue + } + if col.MapType == core.ONLYFROMDB { + continue + } + if col.IsDeleted { + continue + } + if session.Statement.ColumnStr != "" { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); !ok { + continue + } + } + if session.Statement.OmitStr != "" { + if _, ok := getFlagForColumn(session.Statement.columnMap, col); ok { + continue + } + } + if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } else if col.IsVersion && session.Statement.checkVersion { + args = append(args, 1) + var colName = col.Name + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnInt(bean, col, 1) + }) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return 0, err + } + args = append(args, arg) + } + + colPlaces = append(colPlaces, "?") + } + } + colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", ")) + } + cleanupProcessorsClosures(&session.beforeClosures) + + var sql = "INSERT INTO %s (%v%v%v) VALUES (%v)" + var statement string + if session.Engine.dialect.DBType() == core.ORACLE { + sql = "INSERT ALL INTO %s (%v%v%v) VALUES (%v) SELECT 1 FROM DUAL" + temp := fmt.Sprintf(") INTO %s (%v%v%v) VALUES (", + session.Engine.Quote(session.Statement.TableName()), + session.Engine.QuoteStr(), + strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()), + session.Engine.QuoteStr()) + statement = fmt.Sprintf(sql, + session.Engine.Quote(session.Statement.TableName()), + session.Engine.QuoteStr(), + strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()), + session.Engine.QuoteStr(), + strings.Join(colMultiPlaces, temp)) + } else { + statement = fmt.Sprintf(sql, + session.Engine.Quote(session.Statement.TableName()), + session.Engine.QuoteStr(), + strings.Join(colNames, session.Engine.QuoteStr() + ", " + session.Engine.QuoteStr()), + session.Engine.QuoteStr(), + strings.Join(colMultiPlaces, "),(")) + } + res, err := session.exec(statement, args...) + if err != nil { + return 0, err + } + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + lenAfterClosures := len(session.afterClosures) + for i := 0; i < size; i++ { + elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().Interface() + + // handle AfterInsertProcessor + if session.IsAutoCommit { + // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi?? + for _, closure := range session.afterClosures { + closure(elemValue) + } + if processor, ok := interface{}(elemValue).(AfterInsertProcessor); ok { + processor.AfterInsert() + } + } else { + if lenAfterClosures > 0 { + if value, has := session.afterInsertBeans[elemValue]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + session.afterInsertBeans[elemValue] = &afterClosures + } + } else { + if _, ok := interface{}(elemValue).(AfterInsertProcessor); ok { + session.afterInsertBeans[elemValue] = nil + } + } + } + } + + cleanupProcessorsClosures(&session.afterClosures) + return res.RowsAffected() +} + +// InsertMulti insert multiple records +func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) + if sliceValue.Kind() != reflect.Slice { + return 0, ErrParamsType + + } + + if sliceValue.Len() <= 0 { + return 0, nil + } + + return session.innerInsertMulti(rowsSlicePtr) +} + +func (session *Session) innerInsert(bean interface{}) (int64, error) { + session.Statement.setRefValue(rValue(bean)) + if len(session.Statement.TableName()) <= 0 { + return 0, ErrTableNotFound + } + + table := session.Statement.RefTable + + // handle BeforeInsertProcessor + for _, closure := range session.beforeClosures { + closure(bean) + } + cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used + + if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok { + processor.BeforeInsert() + } + // -- + colNames, args, err := genCols(session.Statement.RefTable, session, bean, false, false) + if err != nil { + return 0, err + } + // insert expr columns, override if exists + exprColumns := session.Statement.getExpr() + exprColVals := make([]string, 0, len(exprColumns)) + for _, v := range exprColumns { + // remove the expr columns + for i, colName := range colNames { + if colName == v.colName { + colNames = append(colNames[:i], colNames[i + 1:]...) + args = append(args[:i], args[i + 1:]...) + } + } + + // append expr column to the end + colNames = append(colNames, v.colName) + exprColVals = append(exprColVals, v.expr) + } + + colPlaces := strings.Repeat("?, ", len(colNames) - len(exprColumns)) + if len(exprColVals) > 0 { + colPlaces = colPlaces + strings.Join(exprColVals, ", ") + } else { + colPlaces = colPlaces[0 : len(colPlaces) - 2] + } + + sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)", + session.Engine.Quote(session.Statement.TableName()), + session.Engine.QuoteStr(), + strings.Join(colNames, session.Engine.Quote(", ")), + session.Engine.QuoteStr(), + colPlaces) + + handleAfterInsertProcessorFunc := func(bean interface{}) { + if session.IsAutoCommit { + for _, closure := range session.afterClosures { + closure(bean) + } + if processor, ok := interface{}(bean).(AfterInsertProcessor); ok { + processor.AfterInsert() + } + } else { + lenAfterClosures := len(session.afterClosures) + if lenAfterClosures > 0 { + if value, has := session.afterInsertBeans[bean]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + session.afterInsertBeans[bean] = &afterClosures + } + + } else { + if _, ok := interface{}(bean).(AfterInsertProcessor); ok { + session.afterInsertBeans[bean] = nil + } + } + } + cleanupProcessorsClosures(&session.afterClosures) // cleanup after used + } + + // for postgres, many of them didn't implement lastInsertId, so we should + // implemented it ourself. + if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 { + //assert table.AutoIncrement != "" + res, err := session.query("select seq_atable.currval from dual", args...) + if err != nil { + return 0, err + } + + handleAfterInsertProcessorFunc(bean) + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + if table.Version != "" && session.Statement.checkVersion { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } else if verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(1) + } + } + + if len(res) < 1 { + return 0, errors.New("insert no error but not returned id") + } + + idByte := res[0][table.AutoIncrement] + id, err := strconv.ParseInt(string(idByte), 10, 64) + if err != nil || id <= 0 { + return 1, err + } + + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } + + if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { + return 1, nil + } + + aiValue.Set(int64ToIntValue(id, aiValue.Type())) + + return 1, nil + } else if session.Engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 { + //assert table.AutoIncrement != "" + sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement) + res, err := session.query(sqlStr, args...) + + if err != nil { + return 0, err + } + handleAfterInsertProcessorFunc(bean) + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + if table.Version != "" && session.Statement.checkVersion { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } else if verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(1) + } + } + + if len(res) < 1 { + return 0, errors.New("insert no error but not returned id") + } + + idByte := res[0][table.AutoIncrement] + id, err := strconv.ParseInt(string(idByte), 10, 64) + if err != nil || id <= 0 { + return 1, err + } + + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } + + if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { + return 1, nil + } + + aiValue.Set(int64ToIntValue(id, aiValue.Type())) + + return 1, nil + } else { + res, err := session.exec(sqlStr, args...) + if err != nil { + return 0, err + } + + defer handleAfterInsertProcessorFunc(bean) + + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + session.cacheInsert(session.Statement.TableName()) + } + + if table.Version != "" && session.Statement.checkVersion { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } else if verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(1) + } + } + + if table.AutoIncrement == "" { + return res.RowsAffected() + } + + var id int64 + id, err = res.LastInsertId() + if err != nil || id <= 0 { + return res.RowsAffected() + } + + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } + + if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() { + return res.RowsAffected() + } + + aiValue.Set(int64ToIntValue(id, aiValue.Type())) + + return res.RowsAffected() + } +} + +// InsertOne insert only one struct into database as a record. +// The in parameter bean must a struct or a point to struct. The return +// parameter is inserted and error +func (session *Session) InsertOne(bean interface{}) (int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + return session.innerInsert(bean) +} + +func (session *Session) cacheInsert(tables ...string) error { + if session.Statement.RefTable == nil { + return ErrCacheFailed + } + + table := session.Statement.RefTable + cacher := session.Engine.getCacher2(table) + + for _, t := range tables { + session.Engine.logger.Debug("[cache] clear sql:", t) + cacher.ClearIds(t) + } + + return nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_iterate.go b/vendor/github.com/go-xorm/xorm/session_iterate.go new file mode 100644 index 00000000000..7c148095902 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_iterate.go @@ -0,0 +1,42 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import "reflect" + +// IterFunc only use by Iterate +type IterFunc func(idx int, bean interface{}) error + +// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields +// are conditions. +func (session *Session) Rows(bean interface{}) (*Rows, error) { + return newRows(session, bean) +} + +// Iterate record by record handle records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct +func (session *Session) Iterate(bean interface{}, fun IterFunc) error { + rows, err := session.Rows(bean) + if err != nil { + return err + } + defer rows.Close() + + i := 0 + for rows.Next() { + b := reflect.New(rows.beanType).Interface() + err = rows.Scan(b) + if err != nil { + return err + } + err = fun(i, b) + if err != nil { + return err + } + i++ + } + return err +} diff --git a/vendor/github.com/go-xorm/xorm/session_raw.go b/vendor/github.com/go-xorm/xorm/session_raw.go new file mode 100644 index 00000000000..9351d5cf94b --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_raw.go @@ -0,0 +1,144 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "database/sql" + + "github.com/go-xorm/core" +) + +func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { + session.queryPreprocess(&sqlStr, paramStr...) + + if session.IsAutoCommit { + return session.innerQuery2(sqlStr, paramStr...) + } + return session.txQuery(session.Tx, sqlStr, paramStr...) +} + +func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { + rows, err := tx.Query(sqlStr, params...) + if err != nil { + return nil, err + } + defer rows.Close() + + return rows2maps(rows) +} + +func (session *Session) innerQuery(sqlStr string, params ...interface{}) (*core.Stmt, *core.Rows, error) { + var callback func() (*core.Stmt, *core.Rows, error) + if session.prepareStmt { + callback = func() (*core.Stmt, *core.Rows, error) { + stmt, err := session.doPrepare(sqlStr) + if err != nil { + return nil, nil, err + } + rows, err := stmt.Query(params...) + if err != nil { + return nil, nil, err + } + return stmt, rows, nil + } + } else { + callback = func() (*core.Stmt, *core.Rows, error) { + rows, err := session.DB().Query(sqlStr, params...) + if err != nil { + return nil, nil, err + } + return nil, rows, err + } + } + stmt, rows, err := session.Engine.logSQLQueryTime(sqlStr, params, callback) + if err != nil { + return nil, nil, err + } + return stmt, rows, nil +} + +func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map[string][]byte, error) { + _, rows, err := session.innerQuery(sqlStr, params...) + if rows != nil { + defer rows.Close() + } + if err != nil { + return nil, err + } + return rows2maps(rows) +} + +// Query a raw sql and return records as []map[string][]byte +func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + return session.query(sqlStr, paramStr...) +} + +// ============================= +// for string +// ============================= +func (session *Session) query2(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string]string, err error) { + session.queryPreprocess(&sqlStr, paramStr...) + + if session.IsAutoCommit { + return query2(session.DB(), sqlStr, paramStr...) + } + return txQuery2(session.Tx, sqlStr, paramStr...) +} + +// Execute sql +func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) { + if session.prepareStmt { + stmt, err := session.doPrepare(sqlStr) + if err != nil { + return nil, err + } + + res, err := stmt.Exec(args...) + if err != nil { + return nil, err + } + return res, nil + } + + return session.DB().Exec(sqlStr, args...) +} + +func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { + for _, filter := range session.Engine.dialect.Filters() { + // TODO: for table name, it's no need to RefTable + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) + } + + session.saveLastSQL(sqlStr, args...) + + return session.Engine.logSQLExecutionTime(sqlStr, args, func() (sql.Result, error) { + if session.IsAutoCommit { + // FIXME: oci8 can not auto commit (github.com/mattn/go-oci8) + if session.Engine.dialect.DBType() == core.ORACLE { + session.Begin() + r, err := session.Tx.Exec(sqlStr, args...) + session.Commit() + return r, err + } + return session.innerExec(sqlStr, args...) + } + return session.Tx.Exec(sqlStr, args...) + }) +} + +// Exec raw sql +func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + return session.exec(sqlStr, args...) +} diff --git a/vendor/github.com/go-xorm/xorm/session_schema.go b/vendor/github.com/go-xorm/xorm/session_schema.go new file mode 100644 index 00000000000..21fa2996149 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_schema.go @@ -0,0 +1,489 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "database/sql" + "errors" + "fmt" + "reflect" + "strings" + + "github.com/go-xorm/core" +) + +// Ping test if database is ok +func (session *Session) Ping() error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + return session.DB().Ping() +} + +// CreateTable create a table according a bean +func (session *Session) CreateTable(bean interface{}) error { + v := rValue(bean) + session.Statement.setRefValue(v) + + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + return session.createOneTable() +} + +// CreateIndexes create indexes +func (session *Session) CreateIndexes(bean interface{}) error { + v := rValue(bean) + session.Statement.setRefValue(v) + + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + sqls := session.Statement.genIndexSQL() + for _, sqlStr := range sqls { + _, err := session.exec(sqlStr) + if err != nil { + return err + } + } + return nil +} + +// CreateUniques create uniques +func (session *Session) CreateUniques(bean interface{}) error { + v := rValue(bean) + session.Statement.setRefValue(v) + + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + sqls := session.Statement.genUniqueSQL() + for _, sqlStr := range sqls { + _, err := session.exec(sqlStr) + if err != nil { + return err + } + } + return nil +} + +func (session *Session) createOneTable() error { + sqlStr := session.Statement.genCreateTableSQL() + _, err := session.exec(sqlStr) + return err +} + +// to be deleted +func (session *Session) createAll() error { + if session.IsAutoClose { + defer session.Close() + } + + for _, table := range session.Engine.Tables { + session.Statement.RefTable = table + session.Statement.tableName = table.Name + err := session.createOneTable() + session.resetStatement() + if err != nil { + return err + } + } + return nil +} + +// DropIndexes drop indexes +func (session *Session) DropIndexes(bean interface{}) error { + v := rValue(bean) + session.Statement.setRefValue(v) + + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + sqls := session.Statement.genDelIndexSQL() + for _, sqlStr := range sqls { + _, err := session.exec(sqlStr) + if err != nil { + return err + } + } + return nil +} + +// DropTable drop table will drop table if exist, if drop failed, it will return error +func (session *Session) DropTable(beanOrTableName interface{}) error { + tableName, err := session.Engine.tableName(beanOrTableName) + if err != nil { + return err + } + + var needDrop = true + if !session.Engine.dialect.SupportDropIfExists() { + sqlStr, args := session.Engine.dialect.TableCheckSql(tableName) + results, err := session.query(sqlStr, args...) + if err != nil { + return err + } + needDrop = len(results) > 0 + } + + if needDrop { + sqlStr := session.Engine.Dialect().DropTableSql(tableName) + _, err = session.exec(sqlStr) + return err + } + return nil +} + +// IsTableExist if a table is exist +func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) { + tableName, err := session.Engine.tableName(beanOrTableName) + if err != nil { + return false, err + } + + return session.isTableExist(tableName) +} + +func (session *Session) isTableExist(tableName string) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + sqlStr, args := session.Engine.dialect.TableCheckSql(tableName) + results, err := session.query(sqlStr, args...) + return len(results) > 0, err +} + +// IsTableEmpty if table have any records +func (session *Session) IsTableEmpty(bean interface{}) (bool, error) { + v := rValue(bean) + t := v.Type() + + if t.Kind() == reflect.String { + return session.isTableEmpty(bean.(string)) + } else if t.Kind() == reflect.Struct { + rows, err := session.Count(bean) + return rows == 0, err + } + return false, errors.New("bean should be a struct or struct's point") +} + +func (session *Session) isTableEmpty(tableName string) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var total int64 + sqlStr := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName)) + err := session.DB().QueryRow(sqlStr).Scan(&total) + session.saveLastSQL(sqlStr) + if err != nil { + if err == sql.ErrNoRows { + err = nil + } + return true, err + } + + return total == 0, nil +} + +func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + var idx string + if unique { + idx = uniqueName(tableName, idxName) + } else { + idx = indexName(tableName, idxName) + } + sqlStr, args := session.Engine.dialect.IndexCheckSql(tableName, idx) + results, err := session.query(sqlStr, args...) + return len(results) > 0, err +} + +// find if index is exist according cols +func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + indexes, err := session.Engine.dialect.GetIndexes(tableName) + if err != nil { + return false, err + } + + for _, index := range indexes { + if sliceEq(index.Cols, cols) { + if unique { + return index.Type == core.UniqueType, nil + } + return index.Type == core.IndexType, nil + } + } + return false, nil +} + +func (session *Session) addColumn(colName string) error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + col := session.Statement.RefTable.GetColumn(colName) + sql, args := session.Statement.genAddColumnStr(col) + _, err := session.exec(sql, args...) + return err +} + +func (session *Session) addIndex(tableName, idxName string) error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + index := session.Statement.RefTable.Indexes[idxName] + sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) + + _, err := session.exec(sqlStr) + return err +} + +func (session *Session) addUnique(tableName, uqeName string) error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + index := session.Statement.RefTable.Indexes[uqeName] + sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) + _, err := session.exec(sqlStr) + return err +} + +// To be deleted +func (session *Session) dropAll() error { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + for _, table := range session.Engine.Tables { + session.Statement.Init() + session.Statement.RefTable = table + sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName()) + _, err := session.exec(sqlStr) + if err != nil { + return err + } + } + return nil +} + +// Sync2 synchronize structs to database tables +func (session *Session) Sync2(beans ...interface{}) error { + engine := session.Engine + + tables, err := engine.DBMetas() + if err != nil { + return err + } + + var structTables []*core.Table + + for _, bean := range beans { + v := rValue(bean) + table, err := engine.mapType(v) + if err != nil { + return err + } + structTables = append(structTables, table) + var tbName = session.tbNameNoSchema(table) + + var oriTable *core.Table + for _, tb := range tables { + if strings.EqualFold(tb.Name, tbName) { + oriTable = tb + break + } + } + + if oriTable == nil { + err = session.StoreEngine(session.Statement.StoreEngine).CreateTable(bean) + if err != nil { + return err + } + + err = session.CreateUniques(bean) + if err != nil { + return err + } + + err = session.CreateIndexes(bean) + if err != nil { + return err + } + } else { + for _, col := range table.Columns() { + var oriCol *core.Column + for _, col2 := range oriTable.Columns() { + if strings.EqualFold(col.Name, col2.Name) { + oriCol = col2 + break + } + } + + if oriCol != nil { + expectedType := engine.dialect.SqlType(col) + curType := engine.dialect.SqlType(oriCol) + if expectedType != curType { + if expectedType == core.Text && + strings.HasPrefix(curType, core.Varchar) { + // currently only support mysql & postgres + if engine.dialect.DBType() == core.MYSQL || + engine.dialect.DBType() == core.POSTGRES { + engine.logger.Infof("Table %s column %s change type from %s to %s\n", + tbName, col.Name, curType, expectedType) + _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) + } else { + engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n", + tbName, col.Name, curType, expectedType) + } + } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) { + if engine.dialect.DBType() == core.MYSQL { + if oriCol.Length < col.Length { + engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + tbName, col.Name, oriCol.Length, col.Length) + _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) + } + } + } else { + if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') { + engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s", + tbName, col.Name, curType, expectedType) + } + } + } else if expectedType == core.Varchar { + if engine.dialect.DBType() == core.MYSQL { + if oriCol.Length < col.Length { + engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n", + tbName, col.Name, oriCol.Length, col.Length) + _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col)) + } + } + } + if col.Default != oriCol.Default { + engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s", + tbName, col.Name, oriCol.Default, col.Default) + } + if col.Nullable != oriCol.Nullable { + engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v", + tbName, col.Name, oriCol.Nullable, col.Nullable) + } + } else { + session := engine.NewSession() + session.Statement.RefTable = table + session.Statement.tableName = tbName + defer session.Close() + err = session.addColumn(col.Name) + } + if err != nil { + return err + } + } + + var foundIndexNames = make(map[string]bool) + var addedNames = make(map[string]*core.Index) + + for name, index := range table.Indexes { + var oriIndex *core.Index + for name2, index2 := range oriTable.Indexes { + if index.Equal(index2) { + oriIndex = index2 + foundIndexNames[name2] = true + break + } + } + + if oriIndex != nil { + if oriIndex.Type != index.Type { + sql := engine.dialect.DropIndexSql(tbName, oriIndex) + _, err = engine.Exec(sql) + if err != nil { + return err + } + oriIndex = nil + } + } + + if oriIndex == nil { + addedNames[name] = index + } + } + + for name2, index2 := range oriTable.Indexes { + if _, ok := foundIndexNames[name2]; !ok { + sql := engine.dialect.DropIndexSql(tbName, index2) + _, err = engine.Exec(sql) + if err != nil { + return err + } + } + } + + for name, index := range addedNames { + if index.Type == core.UniqueType { + session := engine.NewSession() + session.Statement.RefTable = table + session.Statement.tableName = tbName + defer session.Close() + err = session.addUnique(tbName, name) + } else if index.Type == core.IndexType { + session := engine.NewSession() + session.Statement.RefTable = table + session.Statement.tableName = tbName + defer session.Close() + err = session.addIndex(tbName, name) + } + if err != nil { + return err + } + } + } + } + + for _, table := range tables { + var oriTable *core.Table + for _, structTable := range structTables { + if strings.EqualFold(table.Name, session.tbNameNoSchema(structTable)) { + oriTable = structTable + break + } + } + + if oriTable == nil { + //engine.LogWarnf("Table %s has no struct to mapping it", table.Name) + continue + } + + for _, colName := range table.ColumnsSeq() { + if oriTable.GetColumn(colName) == nil { + engine.logger.Warnf("Table %s has column %s but struct has not related field", table.Name, colName) + } + } + } + return nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_sum.go b/vendor/github.com/go-xorm/xorm/session_sum.go new file mode 100644 index 00000000000..e1409c7ff43 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_sum.go @@ -0,0 +1,137 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import "database/sql" + +// Count counts the records. bean's non-empty fields +// are conditions. +func (session *Session) Count(bean interface{}) (int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var sqlStr string + var args []interface{} + if session.Statement.RawSQL == "" { + sqlStr, args = session.Statement.genCountSQL(bean) + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + session.queryPreprocess(&sqlStr, args...) + + var err error + var total int64 + if session.IsAutoCommit { + err = session.DB().QueryRow(sqlStr, args...).Scan(&total) + } else { + err = session.Tx.QueryRow(sqlStr, args...).Scan(&total) + } + + if err == sql.ErrNoRows || err == nil { + return total, nil + } + + return 0, err +} + +// Sum call sum some column. bean's non-empty fields are conditions. +func (session *Session) Sum(bean interface{}, columnName string) (float64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var sqlStr string + var args []interface{} + if len(session.Statement.RawSQL) == 0 { + sqlStr, args = session.Statement.genSumSQL(bean, columnName) + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + session.queryPreprocess(&sqlStr, args...) + + var err error + var res float64 + if session.IsAutoCommit { + err = session.DB().QueryRow(sqlStr, args...).Scan(&res) + } else { + err = session.Tx.QueryRow(sqlStr, args...).Scan(&res) + } + + if err == sql.ErrNoRows || err == nil { + return res, nil + } + return 0, err +} + +// Sums call sum some columns. bean's non-empty fields are conditions. +func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var sqlStr string + var args []interface{} + if len(session.Statement.RawSQL) == 0 { + sqlStr, args = session.Statement.genSumSQL(bean, columnNames...) + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + session.queryPreprocess(&sqlStr, args...) + + var err error + var res = make([]float64, len(columnNames), len(columnNames)) + if session.IsAutoCommit { + err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) + } else { + err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res) + } + + if err == sql.ErrNoRows || err == nil { + return res, nil + } + return nil, err +} + +// SumsInt sum specify columns and return as []int64 instead of []float64 +func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + var sqlStr string + var args []interface{} + if len(session.Statement.RawSQL) == 0 { + sqlStr, args = session.Statement.genSumSQL(bean, columnNames...) + } else { + sqlStr = session.Statement.RawSQL + args = session.Statement.RawParams + } + + session.queryPreprocess(&sqlStr, args...) + + var err error + var res = make([]int64, len(columnNames), len(columnNames)) + if session.IsAutoCommit { + err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res) + } else { + err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res) + } + + if err == sql.ErrNoRows || err == nil { + return res, nil + } + return nil, err +} diff --git a/vendor/github.com/go-xorm/xorm/session_tx.go b/vendor/github.com/go-xorm/xorm/session_tx.go new file mode 100644 index 00000000000..302bc104d54 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_tx.go @@ -0,0 +1,83 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +// Begin a transaction +func (session *Session) Begin() error { + if session.IsAutoCommit { + tx, err := session.DB().Begin() + if err != nil { + return err + } + session.IsAutoCommit = false + session.IsCommitedOrRollbacked = false + session.Tx = tx + session.saveLastSQL("BEGIN TRANSACTION") + } + return nil +} + +// Rollback When using transaction, you can rollback if any error +func (session *Session) Rollback() error { + if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { + session.saveLastSQL(session.Engine.dialect.RollBackStr()) + session.IsCommitedOrRollbacked = true + return session.Tx.Rollback() + } + return nil +} + +// Commit When using transaction, Commit will commit all operations. +func (session *Session) Commit() error { + if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { + session.saveLastSQL("COMMIT") + session.IsCommitedOrRollbacked = true + var err error + if err = session.Tx.Commit(); err == nil { + // handle processors after tx committed + + closureCallFunc := func(closuresPtr *[]func(interface{}), bean interface{}) { + + if closuresPtr != nil { + for _, closure := range *closuresPtr { + closure(bean) + } + } + } + + for bean, closuresPtr := range session.afterInsertBeans { + closureCallFunc(closuresPtr, bean) + + if processor, ok := interface{}(bean).(AfterInsertProcessor); ok { + processor.AfterInsert() + } + } + for bean, closuresPtr := range session.afterUpdateBeans { + closureCallFunc(closuresPtr, bean) + + if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { + processor.AfterUpdate() + } + } + for bean, closuresPtr := range session.afterDeleteBeans { + closureCallFunc(closuresPtr, bean) + + if processor, ok := interface{}(bean).(AfterDeleteProcessor); ok { + processor.AfterDelete() + } + } + cleanUpFunc := func(slices *map[interface{}]*[]func(interface{})) { + if len(*slices) > 0 { + *slices = make(map[interface{}]*[]func(interface{}), 0) + } + } + cleanUpFunc(&session.afterInsertBeans) + cleanUpFunc(&session.afterUpdateBeans) + cleanUpFunc(&session.afterDeleteBeans) + } + return err + } + return nil +} diff --git a/vendor/github.com/go-xorm/xorm/session_update.go b/vendor/github.com/go-xorm/xorm/session_update.go new file mode 100644 index 00000000000..0f2d1b5cefb --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/session_update.go @@ -0,0 +1,356 @@ +// Copyright 2016 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "errors" + "fmt" + "reflect" + "strconv" + "strings" + + "github.com/go-xorm/builder" + "github.com/go-xorm/core" +) + +func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { + if session.Statement.RefTable == nil || + session.Tx != nil { + return ErrCacheFailed + } + + oldhead, newsql := session.Statement.convertUpdateSQL(sqlStr) + if newsql == "" { + return ErrCacheFailed + } + for _, filter := range session.Engine.dialect.Filters() { + newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable) + } + session.Engine.logger.Debug("[cacheUpdate] new sql", oldhead, newsql) + + var nStart int + if len(args) > 0 { + if strings.Index(sqlStr, "?") > -1 { + nStart = strings.Count(oldhead, "?") + } else { + // only for pq, TODO: if any other databse? + nStart = strings.Count(oldhead, "$") + } + } + table := session.Statement.RefTable + cacher := session.Engine.getCacher2(table) + tableName := session.Statement.TableName() + session.Engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:]) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) + if err != nil { + rows, err := session.DB().Query(newsql, args[nStart:]...) + if err != nil { + return err + } + defer rows.Close() + + ids = make([]core.PK, 0) + for rows.Next() { + var res = make([]string, len(table.PrimaryKeys)) + err = rows.ScanSlice(&res) + if err != nil { + return err + } + var pk core.PK = make([]interface{}, len(table.PrimaryKeys)) + for i, col := range table.PKColumns() { + if col.SQLType.IsNumeric() { + n, err := strconv.ParseInt(res[i], 10, 64) + if err != nil { + return err + } + pk[i] = n + } else if col.SQLType.IsText() { + pk[i] = res[i] + } else { + return errors.New("not supported") + } + } + + ids = append(ids, pk) + } + session.Engine.logger.Debug("[cacheUpdate] find updated id", ids) + } /*else { + session.Engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args) + cacher.DelIds(tableName, genSqlKey(newsql, args)) + }*/ + + for _, id := range ids { + sid, err := id.ToString() + if err != nil { + return err + } + if bean := cacher.GetBean(tableName, sid); bean != nil { + sqls := splitNNoCase(sqlStr, "where", 2) + if len(sqls) == 0 || len(sqls) > 2 { + return ErrCacheFailed + } + + sqls = splitNNoCase(sqls[0], "set", 2) + if len(sqls) != 2 { + return ErrCacheFailed + } + kvs := strings.Split(strings.TrimSpace(sqls[1]), ",") + for idx, kv := range kvs { + sps := strings.SplitN(kv, "=", 2) + sps2 := strings.Split(sps[0], ".") + colName := sps2[len(sps2)-1] + if strings.Contains(colName, "`") { + colName = strings.TrimSpace(strings.Replace(colName, "`", "", -1)) + } else if strings.Contains(colName, session.Engine.QuoteStr()) { + colName = strings.TrimSpace(strings.Replace(colName, session.Engine.QuoteStr(), "", -1)) + } else { + session.Engine.logger.Debug("[cacheUpdate] cannot find column", tableName, colName) + return ErrCacheFailed + } + + if col := table.GetColumn(colName); col != nil { + fieldValue, err := col.ValueOf(bean) + if err != nil { + session.Engine.logger.Error(err) + } else { + session.Engine.logger.Debug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) + if col.IsVersion && session.Statement.checkVersion { + fieldValue.SetInt(fieldValue.Int() + 1) + } else { + fieldValue.Set(reflect.ValueOf(args[idx])) + } + } + } else { + session.Engine.logger.Errorf("[cacheUpdate] ERROR: column %v is not table %v's", + colName, table.Name) + } + } + + session.Engine.logger.Debug("[cacheUpdate] update cache", tableName, id, bean) + cacher.PutBean(tableName, sid, bean) + } + } + session.Engine.logger.Debug("[cacheUpdate] clear cached table sql:", tableName) + cacher.ClearIds(tableName) + return nil +} + +// Update records, bean's non-empty fields are updated contents, +// condiBean' non-empty filds are conditions +// CAUTION: +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions +func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) { + defer session.resetStatement() + if session.IsAutoClose { + defer session.Close() + } + + v := rValue(bean) + t := v.Type() + + var colNames []string + var args []interface{} + + // handle before update processors + for _, closure := range session.beforeClosures { + closure(bean) + } + cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used + if processor, ok := interface{}(bean).(BeforeUpdateProcessor); ok { + processor.BeforeUpdate() + } + // -- + + var err error + var isMap = t.Kind() == reflect.Map + var isStruct = t.Kind() == reflect.Struct + if isStruct { + session.Statement.setRefValue(v) + + if len(session.Statement.TableName()) <= 0 { + return 0, ErrTableNotFound + } + + if session.Statement.ColumnStr == "" { + colNames, args = buildUpdates(session.Engine, session.Statement.RefTable, bean, false, false, + false, false, session.Statement.allUseBool, session.Statement.useAllCols, + session.Statement.mustColumnMap, session.Statement.nullableMap, + session.Statement.columnMap, true, session.Statement.unscoped) + } else { + colNames, args, err = genCols(session.Statement.RefTable, session, bean, true, true) + if err != nil { + return 0, err + } + } + } else if isMap { + colNames = make([]string, 0) + args = make([]interface{}, 0) + bValue := reflect.Indirect(reflect.ValueOf(bean)) + + for _, v := range bValue.MapKeys() { + colNames = append(colNames, session.Engine.Quote(v.String())+" = ?") + args = append(args, bValue.MapIndex(v).Interface()) + } + } else { + return 0, ErrParamsType + } + + table := session.Statement.RefTable + + if session.Statement.UseAutoTime && table != nil && table.Updated != "" { + colNames = append(colNames, session.Engine.Quote(table.Updated)+" = ?") + col := table.UpdatedColumn() + val, t := session.Engine.NowTime2(col.SQLType.Name) + args = append(args, val) + + var colName = col.Name + if isStruct { + session.afterClosures = append(session.afterClosures, func(bean interface{}) { + col := table.GetColumn(colName) + setColumnTime(bean, col, t) + }) + } + } + + //for update action to like "column = column + ?" + incColumns := session.Statement.getInc() + for _, v := range incColumns { + colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" + ?") + args = append(args, v.arg) + } + //for update action to like "column = column - ?" + decColumns := session.Statement.getDec() + for _, v := range decColumns { + colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" - ?") + args = append(args, v.arg) + } + //for update action to like "column = expression" + exprColumns := session.Statement.getExpr() + for _, v := range exprColumns { + colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr) + } + + session.Statement.processIDParam() + + var autoCond builder.Cond + if !session.Statement.noAutoCondition && len(condiBean) > 0 { + var err error + autoCond, err = session.Statement.buildConds(session.Statement.RefTable, condiBean[0], true, true, false, true, false) + if err != nil { + return 0, err + } + } + + st := session.Statement + defer session.resetStatement() + + var sqlStr string + var condArgs []interface{} + var condSQL string + cond := session.Statement.cond.And(autoCond) + + var doIncVer = (table != nil && table.Version != "" && session.Statement.checkVersion) + var verValue *reflect.Value + if doIncVer { + verValue, err = table.VersionColumn().ValueOf(bean) + if err != nil { + return 0, err + } + + cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()}) + colNames = append(colNames, session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1") + } + + condSQL, condArgs, _ = builder.ToSQL(cond) + if len(condSQL) > 0 { + condSQL = "WHERE " + condSQL + } + + if st.OrderStr != "" { + condSQL = condSQL + fmt.Sprintf(" ORDER BY %v", st.OrderStr) + } + + // TODO: Oracle support needed + var top string + if st.LimitN > 0 { + if st.Engine.dialect.DBType() == core.MYSQL { + condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) + } else if st.Engine.dialect.DBType() == core.SQLITE { + tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) + cond = cond.And(builder.Expr(fmt.Sprintf("rowid IN (SELECT rowid FROM %v %v)", + session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...)) + condSQL, condArgs, _ = builder.ToSQL(cond) + if len(condSQL) > 0 { + condSQL = "WHERE " + condSQL + } + } else if st.Engine.dialect.DBType() == core.POSTGRES { + tempCondSQL := condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN) + cond = cond.And(builder.Expr(fmt.Sprintf("CTID IN (SELECT CTID FROM %v %v)", + session.Engine.Quote(session.Statement.TableName()), tempCondSQL), condArgs...)) + condSQL, condArgs, _ = builder.ToSQL(cond) + if len(condSQL) > 0 { + condSQL = "WHERE " + condSQL + } + } else if st.Engine.dialect.DBType() == core.MSSQL { + top = fmt.Sprintf("top (%d) ", st.LimitN) + } + } + + sqlStr = fmt.Sprintf("UPDATE %v%v SET %v %v", + top, + session.Engine.Quote(session.Statement.TableName()), + strings.Join(colNames, ", "), + condSQL) + + res, err := session.exec(sqlStr, append(args, condArgs...)...) + if err != nil { + return 0, err + } else if doIncVer { + if verValue != nil && verValue.IsValid() && verValue.CanSet() { + verValue.SetInt(verValue.Int() + 1) + } + } + + if table != nil { + if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { + cacher.ClearIds(session.Statement.TableName()) + cacher.ClearBeans(session.Statement.TableName()) + } + } + + // handle after update processors + if session.IsAutoCommit { + for _, closure := range session.afterClosures { + closure(bean) + } + if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok { + session.Engine.logger.Debug("[event]", session.Statement.TableName(), " has after update processor") + processor.AfterUpdate() + } + } else { + lenAfterClosures := len(session.afterClosures) + if lenAfterClosures > 0 { + if value, has := session.afterUpdateBeans[bean]; has && value != nil { + *value = append(*value, session.afterClosures...) + } else { + afterClosures := make([]func(interface{}), lenAfterClosures) + copy(afterClosures, session.afterClosures) + // FIXME: if bean is a map type, it will panic because map cannot be as map key + session.afterUpdateBeans[bean] = &afterClosures + } + + } else { + if _, ok := interface{}(bean).(AfterUpdateProcessor); ok { + session.afterUpdateBeans[bean] = nil + } + } + } + cleanupProcessorsClosures(&session.afterClosures) // cleanup after used + // -- + + return res.RowsAffected() +} diff --git a/vendor/github.com/go-xorm/xorm/sqlite3_driver.go b/vendor/github.com/go-xorm/xorm/sqlite3_driver.go deleted file mode 100644 index 6ae19569ef8..00000000000 --- a/vendor/github.com/go-xorm/xorm/sqlite3_driver.go +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright 2015 The Xorm Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package xorm - -import ( - "github.com/go-xorm/core" -) - -// func init() { -// core.RegisterDriver("sqlite3", &sqlite3Driver{}) -// } - -type sqlite3Driver struct { -} - -func (p *sqlite3Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { - return &core.Uri{DbType: core.SQLITE, DbName: dataSourceName}, nil -} diff --git a/vendor/github.com/go-xorm/xorm/statement.go b/vendor/github.com/go-xorm/xorm/statement.go index 9b9042fa9ad..82101ff20e9 100644 --- a/vendor/github.com/go-xorm/xorm/statement.go +++ b/vendor/github.com/go-xorm/xorm/statement.go @@ -14,14 +14,10 @@ import ( "strings" "time" + "github.com/go-xorm/builder" "github.com/go-xorm/core" ) -type inParam struct { - colName string - args []interface{} -} - type incrParam struct { colName string arg interface{} @@ -37,17 +33,16 @@ type exprParam struct { expr string } -// statement save all the sql info for executing SQL +// Statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table Engine *Engine Start int LimitN int - WhereStr string - IdParam *core.PK - Params []interface{} + idParam *core.PK OrderStr string JoinStr string + joinArgs []interface{} GroupByStr string HavingStr string ColumnStr string @@ -55,15 +50,14 @@ type Statement struct { columnMap map[string]bool useAllCols bool OmitStr string - ConditionStr string AltTableName string + tableName string RawSQL string RawParams []interface{} UseCascade bool UseAutoJoin bool StoreEngine string Charset string - BeanArgs []interface{} UseCache bool UseAutoTime bool noAutoCondition bool @@ -75,33 +69,31 @@ type Statement struct { unscoped bool mustColumnMap map[string]bool nullableMap map[string]bool - inColumns map[string]*inParam incrColumns map[string]incrParam decrColumns map[string]decrParam exprColumns map[string]exprParam + cond builder.Cond } -// init +// Init reset all the statement's fields func (statement *Statement) Init() { statement.RefTable = nil statement.Start = 0 statement.LimitN = 0 - statement.WhereStr = "" - statement.Params = make([]interface{}, 0) statement.OrderStr = "" statement.UseCascade = true statement.JoinStr = "" + statement.joinArgs = make([]interface{}, 0) statement.GroupByStr = "" statement.HavingStr = "" statement.ColumnStr = "" statement.OmitStr = "" statement.columnMap = make(map[string]bool) - statement.ConditionStr = "" statement.AltTableName = "" - statement.IdParam = nil + statement.tableName = "" + statement.idParam = nil statement.RawSQL = "" statement.RawParams = make([]interface{}, 0) - statement.BeanArgs = make([]interface{}, 0) statement.UseCache = true statement.UseAutoTime = true statement.noAutoCondition = false @@ -115,10 +107,10 @@ func (statement *Statement) Init() { statement.nullableMap = make(map[string]bool) statement.checkVersion = true statement.unscoped = false - statement.inColumns = make(map[string]*inParam) statement.incrColumns = make(map[string]incrParam) statement.decrColumns = make(map[string]decrParam) statement.exprColumns = make(map[string]exprParam) + statement.cond = builder.NewCond() } // NoAutoCondition if you do not want convert bean's field as query condition, then use this function @@ -130,57 +122,96 @@ func (statement *Statement) NoAutoCondition(no ...bool) *Statement { return statement } -// Sql add the raw sql statement -func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { - statement.RawSQL = querystring - statement.RawParams = args - return statement -} - // Alias set the table alias func (statement *Statement) Alias(alias string) *Statement { statement.TableAlias = alias return statement } -// Where add Where statment -func (statement *Statement) Where(querystring string, args ...interface{}) *Statement { - if !strings.Contains(querystring, statement.Engine.dialect.EqStr()) { - querystring = strings.Replace(querystring, "=", statement.Engine.dialect.EqStr(), -1) +// SQL adds raw sql statement +func (statement *Statement) SQL(query interface{}, args ...interface{}) *Statement { + switch query.(type) { + case (*builder.Builder): + var err error + statement.RawSQL, statement.RawParams, err = query.(*builder.Builder).ToSQL() + if err != nil { + statement.Engine.logger.Error(err) + } + case string: + statement.RawSQL = query.(string) + statement.RawParams = args + default: + statement.Engine.logger.Error("unsupported sql type") } - statement.WhereStr = querystring - statement.Params = args + return statement } -// And add Where & and statment -func (statement *Statement) And(querystring string, args ...interface{}) *Statement { - if len(statement.WhereStr) > 0 { - var buf bytes.Buffer - fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, - statement.Engine.dialect.AndStr(), querystring) - statement.WhereStr = buf.String() - } else { - statement.WhereStr = querystring +// Where add Where statement +func (statement *Statement) Where(query interface{}, args ...interface{}) *Statement { + return statement.And(query, args...) +} + +// And add Where & and statement +func (statement *Statement) And(query interface{}, args ...interface{}) *Statement { + switch query.(type) { + case string: + cond := builder.Expr(query.(string), args...) + statement.cond = statement.cond.And(cond) + case builder.Cond: + cond := query.(builder.Cond) + statement.cond = statement.cond.And(cond) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.And(vv) + } + } + default: + // TODO: not support condition type } - statement.Params = append(statement.Params, args...) + return statement } -// Or add Where & Or statment -func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { - if len(statement.WhereStr) > 0 { - var buf bytes.Buffer - fmt.Fprintf(&buf, "(%v) %s (%v)", statement.WhereStr, - statement.Engine.dialect.OrStr(), querystring) - statement.WhereStr = buf.String() - } else { - statement.WhereStr = querystring +// Or add Where & Or statement +func (statement *Statement) Or(query interface{}, args ...interface{}) *Statement { + switch query.(type) { + case string: + cond := builder.Expr(query.(string), args...) + statement.cond = statement.cond.Or(cond) + case builder.Cond: + cond := query.(builder.Cond) + statement.cond = statement.cond.Or(cond) + for _, v := range args { + if vv, ok := v.(builder.Cond); ok { + statement.cond = statement.cond.Or(vv) + } + } + default: + // TODO: not support condition type } - statement.Params = append(statement.Params, args...) return statement } +// In generate "Where column IN (?) " statement +func (statement *Statement) In(column string, args ...interface{}) *Statement { + in := builder.In(statement.Engine.Quote(column), args...) + statement.cond = statement.cond.And(in) + return statement +} + +// NotIn generate "Where column NOT IN (?) " statement +func (statement *Statement) NotIn(column string, args ...interface{}) *Statement { + notIn := builder.NotIn(statement.Engine.Quote(column), args...) + statement.cond = statement.cond.And(notIn) + return statement +} + +func (statement *Statement) setRefValue(v reflect.Value) { + statement.RefTable = statement.Engine.autoMapType(reflect.Indirect(v)) + statement.tableName = statement.Engine.tbName(v) +} + // Table tempororily set table name, the parameter could be a string or a pointer of struct func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { v := rValue(tableNameOrBean) @@ -189,6 +220,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { statement.AltTableName = tableNameOrBean.(string) } else if t.Kind() == reflect.Struct { statement.RefTable = statement.Engine.autoMapType(v) + statement.AltTableName = statement.Engine.tbName(v) } return statement } @@ -200,7 +232,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, mustColumnMap map[string]bool, nullableMap map[string]bool, columnMap map[string]bool, update, unscoped bool) ([]string, []interface{}) { - colNames := make([]string, 0) + var colNames = make([]string, 0) var args = make([]interface{}, 0) for _, col := range table.Columns() { if !includeVersion && col.IsVersion { @@ -218,13 +250,13 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if col.IsDeleted && !unscoped { continue } - if use, ok := columnMap[col.Name]; ok && !use { + if use, ok := columnMap[strings.ToLower(col.Name)]; ok && !use { continue } fieldValuePtr, err := col.ValueOf(bean) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } @@ -233,9 +265,8 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, requiredField := useAllCols includeNil := useAllCols - lColName := strings.ToLower(col.Name) - if b, ok := mustColumnMap[lColName]; ok { + if b, ok := getFlagForColumn(mustColumnMap, col); ok { if b { requiredField = true } else { @@ -244,7 +275,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, } // !evalphobia! set fieldValue as nil when column is nullable and zero-value - if b, ok := nullableMap[lColName]; ok { + if b, ok := getFlagForColumn(nullableMap, col); ok { if b && col.Nullable && isZero(fieldValue.Interface()) { var nilValue *int fieldValue = reflect.ValueOf(nilValue) @@ -259,7 +290,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { data, err := structConvert.ToDB() if err != nil { - engine.LogError(err) + engine.logger.Error(err) } else { val = data } @@ -270,7 +301,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if structConvert, ok := fieldValue.Interface().(core.Conversion); ok { data, err := structConvert.ToDB() if err != nil { - engine.LogError(err) + engine.logger.Error(err) } else { val = data } @@ -345,7 +376,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if len(table.PrimaryKeys) == 1 { pkField := reflect.Indirect(fieldValue).FieldByName(table.PKColumns()[0].FieldName) // fix non-int pk issues - if pkField.IsValid() && !isZero(pkField.Interface()) { + if pkField.IsValid() && (!requiredField && !isZero(pkField.Interface())) { val = pkField.Interface() } else { continue @@ -358,14 +389,19 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, val = fieldValue.Interface() } } else { - bytes, err := json.Marshal(fieldValue.Interface()) - if err != nil { - panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) - } - if col.SQLType.IsText() { - val = string(bytes) - } else if col.SQLType.IsBlob() { - val = bytes + // Blank struct could not be as update data + if requiredField || !isStructZero(fieldValue) { + bytes, err := json.Marshal(fieldValue.Interface()) + if err != nil { + panic(fmt.Sprintf("mashal %v failed", fieldValue.Interface())) + } + if col.SQLType.IsText() { + val = string(bytes) + } else if col.SQLType.IsBlob() { + val = bytes + } + } else { + continue } } } @@ -374,7 +410,11 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if fieldValue == reflect.Zero(fieldType) { continue } - if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { + if fieldType.Kind() == reflect.Array { + if isArrayValueZero(fieldValue) { + continue + } + } else if fieldValue.IsNil() || !fieldValue.IsValid() || fieldValue.Len() == 0 { continue } } @@ -382,24 +422,27 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = string(bytes) } else if col.SQLType.IsBlob() { var bytes []byte var err error - if (fieldType.Kind() == reflect.Array || fieldType.Kind() == reflect.Slice) && + if fieldType.Kind() == reflect.Slice && fieldType.Elem().Kind() == reflect.Uint8 { if fieldValue.Len() > 0 { val = fieldValue.Bytes() } else { continue } + } else if fieldType.Kind() == reflect.Array && + fieldType.Elem().Kind() == reflect.Uint8 { + val = fieldValue.Slice(0, 0).Interface() } else { bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = bytes @@ -412,7 +455,6 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, } APPEND: - //fmt.Println("==", col.Name, "==", fmt.Sprintf("%v", val)) args = append(args, val) if col.IsPrimaryKey && engine.dialect.DBType() == "ql" { continue @@ -423,13 +465,26 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, return colNames, args } -// Auto generating conditions according a struct -func buildConditions(engine *Engine, table *core.Table, bean interface{}, +func (statement *Statement) needTableName() bool { + return len(statement.JoinStr) > 0 +} + +func (statement *Statement) colName(col *core.Column, tableName string) string { + if statement.needTableName() { + var nm = tableName + if len(statement.TableAlias) > 0 { + nm = statement.TableAlias + } + return statement.Engine.Quote(nm) + "." + statement.Engine.Quote(col.Name) + } + return statement.Engine.Quote(col.Name) +} + +func buildConds(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, - mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) ([]string, []interface{}) { - colNames := make([]string, 0) - var args = make([]interface{}, 0) + mustColumnMap map[string]bool, tableName, aliasName string, addedTableName bool) (builder.Cond, error) { + var conds []builder.Cond for _, col := range table.Columns() { if !includeVersion && col.IsVersion { continue @@ -441,7 +496,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, continue } - if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { + if engine.dialect.DBType() == core.MSSQL && (col.SQLType.Name == core.Text || col.SQLType.IsBlob() || col.SQLType.Name == core.TimeStampz) { continue } if col.SQLType.IsJson() { @@ -461,13 +516,16 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, fieldValuePtr, err := col.ValueOf(bean) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } if col.IsDeleted && !unscoped { // tag "deleted" is enabled - colNames = append(colNames, fmt.Sprintf("%v IS NULL or %v = '0001-01-01 00:00:00'", - colName, colName)) + if engine.dialect.DBType() == core.MSSQL { + conds = append(conds, builder.IsNull{colName}) + } else { + conds = append(conds, builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"})) + } } fieldValue := *fieldValuePtr @@ -477,7 +535,8 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, fieldType := reflect.TypeOf(fieldValue.Interface()) requiredField := useAllCols - if b, ok := mustColumnMap[strings.ToLower(col.Name)]; ok { + + if b, ok := getFlagForColumn(mustColumnMap, col); ok { if b { requiredField = true } else { @@ -488,8 +547,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if fieldType.Kind() == reflect.Ptr { if fieldValue.IsNil() { if includeNil { - args = append(args, nil) - colNames = append(colNames, fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr())) + conds = append(conds, builder.Eq{colName: nil}) } continue } else if !fieldValue.IsValid() { @@ -557,7 +615,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = string(bytes) @@ -566,7 +624,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, var err error bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = bytes @@ -592,7 +650,9 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } } } - case reflect.Array, reflect.Slice, reflect.Map: + case reflect.Array: + continue + case reflect.Slice, reflect.Map: if fieldValue == reflect.Zero(fieldType) { continue } @@ -603,7 +663,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = string(bytes) @@ -620,7 +680,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } else { bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogError(err) + engine.logger.Error(err) continue } val = bytes @@ -632,59 +692,50 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, val = fieldValue.Interface() } - args = append(args, val) - var condi string - if col.IsPrimaryKey && engine.dialect.DBType() == "ql" { - condi = "id() == ?" - } else { - condi = fmt.Sprintf("%v %s ?", colName, engine.dialect.EqStr()) - } - colNames = append(colNames, condi) + conds = append(conds, builder.Eq{colName: val}) } - return colNames, args + return builder.And(conds...), nil } -// return current tableName +// TableName return current tableName func (statement *Statement) TableName() string { if statement.AltTableName != "" { return statement.AltTableName } - if statement.RefTable != nil { - return statement.RefTable.Name - } - return "" + return statement.tableName } -var ( - ptrPkType = reflect.TypeOf(&core.PK{}) - pkType = reflect.TypeOf(core.PK{}) -) - -// Generate "where id = ? " statment or for composite key "where key1 = ? and key2 = ?" -func (statement *Statement) Id(id interface{}) *Statement { +// ID generate "where id = ? " statement or for composite key "where key1 = ? and key2 = ?" +func (statement *Statement) ID(id interface{}) *Statement { idValue := reflect.ValueOf(id) idType := reflect.TypeOf(idValue.Interface()) switch idType { case ptrPkType: if pkPtr, ok := (id).(*core.PK); ok { - statement.IdParam = pkPtr + statement.idParam = pkPtr + return statement } case pkType: if pk, ok := (id).(core.PK); ok { - statement.IdParam = &pk + statement.idParam = &pk + return statement } - default: - // TODO: treat as int primitve for now, need to handle type check? - statement.IdParam = &core.PK{id} } + switch idType.Kind() { + case reflect.String: + statement.idParam = &core.PK{idValue.Convert(reflect.TypeOf("")).Interface()} + return statement + } + + statement.idParam = &core.PK{id} return statement } -// Incr Generate "Update ... Set column = column + arg" statment +// Incr Generate "Update ... Set column = column + arg" statement func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { k := strings.ToLower(column) if len(arg) > 0 { @@ -695,7 +746,7 @@ func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { return statement } -// Decr Generate "Update ... Set column = column - arg" statment +// Decr Generate "Update ... Set column = column - arg" statement func (statement *Statement) Decr(column string, arg ...interface{}) *Statement { k := strings.ToLower(column) if len(arg) > 0 { @@ -706,115 +757,28 @@ func (statement *Statement) Decr(column string, arg ...interface{}) *Statement { return statement } -// SetExpr Generate "Update ... Set column = {expression}" statment +// SetExpr Generate "Update ... Set column = {expression}" statement func (statement *Statement) SetExpr(column string, expression string) *Statement { k := strings.ToLower(column) statement.exprColumns[k] = exprParam{column, expression} return statement } -// Generate "Update ... Set column = column + arg" statment +// Generate "Update ... Set column = column + arg" statement func (statement *Statement) getInc() map[string]incrParam { return statement.incrColumns } -// Generate "Update ... Set column = column - arg" statment +// Generate "Update ... Set column = column - arg" statement func (statement *Statement) getDec() map[string]decrParam { return statement.decrColumns } -// Generate "Update ... Set column = {expression}" statment +// Generate "Update ... Set column = {expression}" statement func (statement *Statement) getExpr() map[string]exprParam { return statement.exprColumns } -// Generate "Where column IN (?) " statment -func (statement *Statement) In(column string, args ...interface{}) *Statement { - length := len(args) - if length == 0 { - return statement - } - - k := strings.ToLower(column) - var newargs []interface{} - if length == 1 && - reflect.TypeOf(args[0]).Kind() == reflect.Slice { - newargs = make([]interface{}, 0) - v := reflect.ValueOf(args[0]) - for i := 0; i < v.Len(); i++ { - newargs = append(newargs, v.Index(i).Interface()) - } - } else { - newargs = args - } - - if _, ok := statement.inColumns[k]; ok { - statement.inColumns[k].args = append(statement.inColumns[k].args, newargs...) - } else { - statement.inColumns[k] = &inParam{column, newargs} - } - return statement -} - -func (statement *Statement) genInSql() (string, []interface{}) { - if len(statement.inColumns) == 0 { - return "", []interface{}{} - } - - inStrs := make([]string, len(statement.inColumns), len(statement.inColumns)) - args := make([]interface{}, 0) - var buf bytes.Buffer - var i int - for _, params := range statement.inColumns { - buf.Reset() - fmt.Fprintf(&buf, "(%v IN (%v))", - statement.Engine.autoQuote(params.colName), - strings.Join(makeArray("?", len(params.args)), ",")) - inStrs[i] = buf.String() - i++ - args = append(args, params.args...) - } - - if len(statement.inColumns) == 1 { - return inStrs[0], args - } - return fmt.Sprintf("(%v)", strings.Join(inStrs, " "+statement.Engine.dialect.AndStr()+" ")), args -} - -func (statement *Statement) attachInSql() { - inSql, inArgs := statement.genInSql() - if len(inSql) > 0 { - if len(statement.ConditionStr) > 0 { - statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " " - } - statement.ConditionStr += inSql - statement.Params = append(statement.Params, inArgs...) - } -} - -func col2NewCols(columns ...string) []string { - newColumns := make([]string, 0) - for _, col := range columns { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, `"`, "", -1) - ccols := strings.Split(col, ",") - for _, c := range ccols { - newColumns = append(newColumns, strings.TrimSpace(c)) - } - } - return newColumns -} - -func (engine *Engine) autoQuote(col string) string { - col = strings.Replace(col, "`", "", -1) - col = strings.Replace(col, engine.QuoteStr(), "", -1) - fields := strings.Split(strings.TrimSpace(col), ".") - for i, field := range fields { - fields[i] = engine.Quote(field) - } - return strings.Join(fields, ".") -} - func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { newColumns := make([]string, 0) for _, col := range columns { @@ -824,10 +788,10 @@ func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { for _, c := range ccols { fields := strings.Split(strings.TrimSpace(c), ".") if len(fields) == 1 { - newColumns = append(newColumns, statement.Engine.Quote(fields[0])) + newColumns = append(newColumns, statement.Engine.quote(fields[0])) } else if len(fields) == 2 { - newColumns = append(newColumns, statement.Engine.Quote(fields[0])+"."+ - statement.Engine.Quote(fields[1])) + newColumns = append(newColumns, statement.Engine.quote(fields[0])+"."+ + statement.Engine.quote(fields[1])) } else { panic(errors.New("unwanted colnames")) } @@ -836,46 +800,45 @@ func (statement *Statement) col2NewColsWithQuote(columns ...string) []string { return newColumns } -// Generate "Distince col1, col2 " statment +// Distinct generates "DISTINCT col1, col2 " statement func (statement *Statement) Distinct(columns ...string) *Statement { statement.IsDistinct = true statement.Cols(columns...) return statement } -// Generate "SELECT ... FOR UPDATE" statment +// ForUpdate generates "SELECT ... FOR UPDATE" statement func (statement *Statement) ForUpdate() *Statement { statement.IsForUpdate = true return statement } -// replace select -func (s *Statement) Select(str string) *Statement { - s.selectStr = str - return s -} - -// Generate "col1, col2" statement -func (statement *Statement) Cols(columns ...string) *Statement { - newColumns := col2NewCols(columns...) - for _, nc := range newColumns { - statement.columnMap[strings.ToLower(nc)] = true - } - statement.ColumnStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) - if strings.Contains(statement.ColumnStr, ".") { - statement.ColumnStr = strings.Replace(statement.ColumnStr, ".", statement.Engine.Quote("."), -1) - } - statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.Quote("*"), "*", -1) +// Select replace select +func (statement *Statement) Select(str string) *Statement { + statement.selectStr = str return statement } -// Update use only: update all columns +// Cols generate "col1, col2" statement +func (statement *Statement) Cols(columns ...string) *Statement { + cols := col2NewCols(columns...) + for _, nc := range cols { + statement.columnMap[strings.ToLower(nc)] = true + } + + newColumns := statement.col2NewColsWithQuote(columns...) + statement.ColumnStr = strings.Join(newColumns, ", ") + statement.ColumnStr = strings.Replace(statement.ColumnStr, statement.Engine.quote("*"), "*", -1) + return statement +} + +// AllCols update use only: update all columns func (statement *Statement) AllCols() *Statement { statement.useAllCols = true return statement } -// Update use only: must update columns +// MustCols update use only: must update columns func (statement *Statement) MustCols(columns ...string) *Statement { newColumns := col2NewCols(columns...) for _, nc := range newColumns { @@ -884,7 +847,7 @@ func (statement *Statement) MustCols(columns ...string) *Statement { return statement } -// indicates that use bool fields as update contents and query contiditions +// UseBool indicates that use bool fields as update contents and query contiditions func (statement *Statement) UseBool(columns ...string) *Statement { if len(columns) > 0 { statement.MustCols(columns...) @@ -894,7 +857,7 @@ func (statement *Statement) UseBool(columns ...string) *Statement { return statement } -// do not use the columns +// Omit do not use the columns func (statement *Statement) Omit(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { @@ -903,7 +866,7 @@ func (statement *Statement) Omit(columns ...string) { statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) } -// Update use only: update columns to null when value is nullable and zero-value +// Nullable Update use only: update columns to null when value is nullable and zero-value func (statement *Statement) Nullable(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { @@ -911,13 +874,13 @@ func (statement *Statement) Nullable(columns ...string) { } } -// Generate LIMIT limit statement +// Top generate LIMIT limit statement func (statement *Statement) Top(limit int) *Statement { statement.Limit(limit) return statement } -// Generate LIMIT start, limit statement +// Limit generate LIMIT start, limit statement func (statement *Statement) Limit(limit int, start ...int) *Statement { statement.LimitN = limit if len(start) > 0 { @@ -926,7 +889,7 @@ func (statement *Statement) Limit(limit int, start ...int) *Statement { return statement } -// Generate "Order By order" statement +// OrderBy generate "Order By order" statement func (statement *Statement) OrderBy(order string) *Statement { if len(statement.OrderStr) > 0 { statement.OrderStr += ", " @@ -935,6 +898,7 @@ func (statement *Statement) OrderBy(order string) *Statement { return statement } +// Desc generate `ORDER BY xx DESC` func (statement *Statement) Desc(colNames ...string) *Statement { var buf bytes.Buffer fmt.Fprintf(&buf, statement.OrderStr) @@ -947,7 +911,7 @@ func (statement *Statement) Desc(colNames ...string) *Statement { return statement } -// Method Asc provide asc order by query condition, the input parameters are columns. +// Asc provide asc order by query condition, the input parameters are columns. func (statement *Statement) Asc(colNames ...string) *Statement { var buf bytes.Buffer fmt.Fprintf(&buf, statement.OrderStr) @@ -960,13 +924,13 @@ func (statement *Statement) Asc(colNames ...string) *Statement { return statement } -//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (statement *Statement) Join(join_operator string, tablename interface{}, condition string) *Statement { +// Join The joinOP should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +func (statement *Statement) Join(joinOP string, tablename interface{}, condition string, args ...interface{}) *Statement { var buf bytes.Buffer if len(statement.JoinStr) > 0 { - fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, join_operator) + fmt.Fprintf(&buf, "%v %v JOIN ", statement.JoinStr, joinOP) } else { - fmt.Fprintf(&buf, "%v JOIN ", join_operator) + fmt.Fprintf(&buf, "%v JOIN ", joinOP) } switch tablename.(type) { @@ -988,8 +952,7 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co if t.Kind() == reflect.String { table = f.(string) } else if t.Kind() == reflect.Struct { - r := statement.Engine.autoMapType(v) - table = r.Name + table = statement.Engine.tbName(v) } } if l > 1 { @@ -1004,79 +967,81 @@ func (statement *Statement) Join(join_operator string, tablename interface{}, co fmt.Fprintf(&buf, " ON %v", condition) statement.JoinStr = buf.String() + statement.joinArgs = append(statement.joinArgs, args...) return statement } -// Generate "Group By keys" statement +// GroupBy generate "Group By keys" statement func (statement *Statement) GroupBy(keys string) *Statement { statement.GroupByStr = keys return statement } -// Generate "Having conditions" statement +// Having generate "Having conditions" statement func (statement *Statement) Having(conditions string) *Statement { statement.HavingStr = fmt.Sprintf("HAVING %v", conditions) return statement } -// Always disable struct tag "deleted" +// Unscoped always disable struct tag "deleted" func (statement *Statement) Unscoped() *Statement { statement.unscoped = true return statement } func (statement *Statement) genColumnStr() string { - table := statement.RefTable - colNames := make([]string, 0) - for _, col := range table.Columns() { + var buf bytes.Buffer + if statement.RefTable == nil { + return "" + } + + columns := statement.RefTable.Columns() + + for _, col := range columns { if statement.OmitStr != "" { - if _, ok := statement.columnMap[strings.ToLower(col.Name)]; ok { + if _, ok := getFlagForColumn(statement.columnMap, col); ok { continue } } + if col.MapType == core.ONLYTODB { continue } - if statement.JoinStr != "" { - var name string - if statement.TableAlias != "" { - name = statement.Engine.Quote(statement.TableAlias) - } else { - name = statement.Engine.Quote(statement.TableName()) - } - name += "." + statement.Engine.Quote(col.Name) - if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" { - colNames = append(colNames, "id() AS "+name) - } else { - colNames = append(colNames, name) - } - } else { - name := statement.Engine.Quote(col.Name) - if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" { - colNames = append(colNames, "id() AS "+name) - } else { - colNames = append(colNames, name) - } + if buf.Len() != 0 { + buf.WriteString(", ") } + + if col.IsPrimaryKey && statement.Engine.Dialect().DBType() == "ql" { + buf.WriteString("id() AS ") + } + + if statement.JoinStr != "" { + if statement.TableAlias != "" { + buf.WriteString(statement.TableAlias) + } else { + buf.WriteString(statement.TableName()) + } + + buf.WriteString(".") + } + + statement.Engine.QuoteTo(&buf, col.Name) } - return strings.Join(colNames, ", ") + + return buf.String() } func (statement *Statement) genCreateTableSQL() string { - return statement.Engine.dialect.CreateTableSql(statement.RefTable, statement.AltTableName, + return statement.Engine.dialect.CreateTableSql(statement.RefTable, statement.TableName(), statement.StoreEngine, statement.Charset) } -func indexName(tableName, idxName string) string { - return fmt.Sprintf("IDX_%v_%v", tableName, idxName) -} - -func (s *Statement) genIndexSQL() []string { - var sqls []string = make([]string, 0) - tbName := s.TableName() - quote := s.Engine.Quote - for idxName, index := range s.RefTable.Indexes { +func (statement *Statement) genIndexSQL() []string { + var sqls []string + tbName := statement.TableName() + quote := statement.Engine.Quote + for idxName, index := range statement.RefTable.Indexes { if index.Type == core.IndexType { sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(indexName(tbName, idxName)), quote(tbName), quote(strings.Join(index.Cols, quote(",")))) @@ -1090,61 +1055,75 @@ func uniqueName(tableName, uqeName string) string { return fmt.Sprintf("UQE_%v_%v", tableName, uqeName) } -func (s *Statement) genUniqueSQL() []string { - var sqls []string = make([]string, 0) - tbName := s.TableName() - for _, index := range s.RefTable.Indexes { +func (statement *Statement) genUniqueSQL() []string { + var sqls []string + tbName := statement.TableName() + for _, index := range statement.RefTable.Indexes { if index.Type == core.UniqueType { - sql := s.Engine.dialect.CreateIndexSql(tbName, index) + sql := statement.Engine.dialect.CreateIndexSql(tbName, index) sqls = append(sqls, sql) } } return sqls } -func (s *Statement) genDelIndexSQL() []string { - var sqls []string = make([]string, 0) - for idxName, index := range s.RefTable.Indexes { +func (statement *Statement) genDelIndexSQL() []string { + var sqls []string + tbName := statement.TableName() + for idxName, index := range statement.RefTable.Indexes { var rIdxName string if index.Type == core.UniqueType { - rIdxName = uniqueName(s.TableName(), idxName) + rIdxName = uniqueName(tbName, idxName) } else if index.Type == core.IndexType { - rIdxName = indexName(s.TableName(), idxName) + rIdxName = indexName(tbName, idxName) } - sql := fmt.Sprintf("DROP INDEX %v", s.Engine.Quote(rIdxName)) - if s.Engine.dialect.IndexOnTable() { - sql += fmt.Sprintf(" ON %v", s.Engine.Quote(s.TableName())) + sql := fmt.Sprintf("DROP INDEX %v", statement.Engine.Quote(rIdxName)) + if statement.Engine.dialect.IndexOnTable() { + sql += fmt.Sprintf(" ON %v", statement.Engine.Quote(statement.TableName())) } sqls = append(sqls, sql) } return sqls } -func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) { - var table *core.Table - if statement.RefTable == nil { - table = statement.Engine.TableInfo(bean) - statement.RefTable = table - } else { - table = statement.RefTable - } +func (statement *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { + quote := statement.Engine.Quote + sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(statement.TableName()), + col.String(statement.Engine.dialect)) + return sql, []interface{}{} +} - var addedTableName = (len(statement.JoinStr) > 0) +func (statement *Statement) buildConds(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) (builder.Cond, error) { + return buildConds(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols, + statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName) +} +func (statement *Statement) genConds(bean interface{}) (string, []interface{}, error) { if !statement.noAutoCondition { - colNames, args := statement.buildConditions(table, bean, true, true, false, true, addedTableName) - - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") - statement.BeanArgs = args + var addedTableName = (len(statement.JoinStr) > 0) + autoCond, err := statement.buildConds(statement.RefTable, bean, true, true, false, true, addedTableName) + if err != nil { + return "", nil, err + } + statement.cond = statement.cond.And(autoCond) } - var columnStr string = statement.ColumnStr + statement.processIDParam() + + return builder.ToSQL(statement.cond) +} + +func (statement *Statement) genGetSQL(bean interface{}) (string, []interface{}) { + statement.setRefValue(rValue(bean)) + + var columnStr = statement.ColumnStr if len(statement.selectStr) > 0 { columnStr = statement.selectStr } else { + // TODO: always generate column names, not use * even if join if len(statement.JoinStr) == 0 { if len(columnStr) == 0 { - if statement.GroupByStr != "" { + if len(statement.GroupByStr) > 0 { columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) } else { columnStr = statement.genColumnStr() @@ -1152,7 +1131,7 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) } } else { if len(columnStr) == 0 { - if statement.GroupByStr != "" { + if len(statement.GroupByStr) > 0 { columnStr = statement.Engine.Quote(strings.Replace(statement.GroupByStr, ",", statement.Engine.Quote(","), -1)) } else { columnStr = "*" @@ -1161,61 +1140,43 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) } } - statement.attachInSql() // !admpub! fix bug:Iterate func missing "... IN (...)" - return statement.genSelectSql(columnStr), append(statement.Params, statement.BeanArgs...) + condSQL, condArgs, _ := statement.genConds(bean) + + return statement.genSelectSQL(columnStr, condSQL), append(statement.joinArgs, condArgs...) } -func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { - quote := s.Engine.Quote - sql := fmt.Sprintf("ALTER TABLE %v ADD %v;", quote(s.TableName()), - col.String(s.Engine.dialect)) - return sql, []interface{}{} +func (statement *Statement) genCountSQL(bean interface{}) (string, []interface{}) { + statement.setRefValue(rValue(bean)) + + condSQL, condArgs, _ := statement.genConds(bean) + + var selectSQL = statement.selectStr + if len(selectSQL) <= 0 { + if statement.IsDistinct { + selectSQL = fmt.Sprintf("count(DISTINCT %s)", statement.ColumnStr) + } else { + selectSQL = "count(*)" + } + } + return statement.genSelectSQL(selectSQL, condSQL), append(statement.joinArgs, condArgs...) } -/*func (s *Statement) genAddIndexStr(idxName string, cols []string) (string, []interface{}) { - quote := s.Engine.Quote - colstr := quote(strings.Join(cols, quote(", "))) - sql := fmt.Sprintf("CREATE INDEX %v ON %v (%v);", quote(idxName), quote(s.TableName()), colstr) - return sql, []interface{}{} -} +func (statement *Statement) genSumSQL(bean interface{}, columns ...string) (string, []interface{}) { + statement.setRefValue(rValue(bean)) -func (s *Statement) genAddUniqueStr(uqeName string, cols []string) (string, []interface{}) { - quote := s.Engine.Quote - colstr := quote(strings.Join(cols, quote(", "))) - sql := fmt.Sprintf("CREATE UNIQUE INDEX %v ON %v (%v);", quote(uqeName), quote(s.TableName()), colstr) - return sql, []interface{}{} -}*/ - -func (statement *Statement) buildConditions(table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, addedTableName bool) ([]string, []interface{}) { - return buildConditions(statement.Engine, table, bean, includeVersion, includeUpdated, includeNil, includeAutoIncr, statement.allUseBool, statement.useAllCols, - statement.unscoped, statement.mustColumnMap, statement.TableName(), statement.TableAlias, addedTableName) -} - -func (statement *Statement) genCountSql(bean interface{}) (string, []interface{}) { - table := statement.Engine.TableInfo(bean) - statement.RefTable = table - - var addedTableName = (len(statement.JoinStr) > 0) - - if !statement.noAutoCondition { - colNames, args := statement.buildConditions(table, bean, true, true, false, true, addedTableName) - - statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") - statement.BeanArgs = args + var sumStrs = make([]string, 0, len(columns)) + for _, colName := range columns { + sumStrs = append(sumStrs, fmt.Sprintf("COALESCE(sum(%s),0)", statement.Engine.Quote(colName))) } - // count(index fieldname) > count(0) > count(*) - var id string = "*" - if statement.Engine.Dialect().DBType() == "ql" { - id = "" - } - statement.attachInSql() - return statement.genSelectSql(fmt.Sprintf("count(%v)", id)), append(statement.Params, statement.BeanArgs...) + condSQL, condArgs, _ := statement.genConds(bean) + + return statement.genSelectSQL(strings.Join(sumStrs, ", "), condSQL), append(statement.joinArgs, condArgs...) } -func (statement *Statement) genSelectSql(columnStr string) (a string) { +func (statement *Statement) genSelectSQL(columnStr, condSQL string) (a string) { var distinct string - if statement.IsDistinct { + if statement.IsDistinct && !strings.HasPrefix(columnStr, "count") { distinct = "DISTINCT " } @@ -1224,24 +1185,15 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { var top string var mssqlCondi string - statement.processIdParam() + statement.processIDParam() var buf bytes.Buffer - if len(statement.WhereStr) > 0 { - if len(statement.ConditionStr) > 0 { - fmt.Fprintf(&buf, " WHERE (%v)", statement.WhereStr) - } else { - fmt.Fprintf(&buf, " WHERE %v", statement.WhereStr) - } - if statement.ConditionStr != "" { - fmt.Fprintf(&buf, " %s (%v)", dialect.AndStr(), statement.ConditionStr) - } - } else if len(statement.ConditionStr) > 0 { - fmt.Fprintf(&buf, " WHERE %v", statement.ConditionStr) + if len(condSQL) > 0 { + fmt.Fprintf(&buf, " WHERE %v", condSQL) } var whereStr = buf.String() - var fromStr string = " FROM " + quote(statement.TableName()) + var fromStr = " FROM " + quote(statement.TableName()) if statement.TableAlias != "" { if dialect.DBType() == core.ORACLE { fromStr += " " + quote(statement.TableAlias) @@ -1258,7 +1210,7 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { top = fmt.Sprintf(" TOP %d ", statement.LimitN) } if statement.Start > 0 { - var column string = "(id)" + var column string if len(statement.RefTable.PKColumns()) == 0 { for _, index := range statement.RefTable.Indexes { if len(index.Cols) == 1 { @@ -1269,7 +1221,17 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { if len(column) == 0 { column = statement.RefTable.ColumnsSeq()[0] } + } else { + column = statement.RefTable.PKColumns()[0].Name } + if statement.needTableName() { + if len(statement.TableAlias) > 0 { + column = statement.TableAlias + "." + column + } else { + column = statement.TableName() + "." + column + } + } + var orderStr string if len(statement.OrderStr) > 0 { orderStr = " ORDER BY " + statement.OrderStr @@ -1320,22 +1282,94 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { return } -func (statement *Statement) processIdParam() { - if statement.IdParam != nil { - if statement.Engine.dialect.DBType() != "ql" { - for i, col := range statement.RefTable.PKColumns() { - if i < len(*(statement.IdParam)) { - statement.And(fmt.Sprintf("%v %s ?", statement.Engine.Quote(col.Name), - statement.Engine.dialect.EqStr()), (*(statement.IdParam))[i]) - } else { - statement.And(fmt.Sprintf("%v %s ?", statement.Engine.Quote(col.Name), - statement.Engine.dialect.EqStr()), "") - } - } +func (statement *Statement) processIDParam() { + if statement.idParam == nil { + return + } + + for i, col := range statement.RefTable.PKColumns() { + var colName = statement.colName(col, statement.TableName()) + if i < len(*(statement.idParam)) { + statement.cond = statement.cond.And(builder.Eq{colName: (*(statement.idParam))[i]}) } else { - if len(*(statement.IdParam)) <= 1 { - statement.And("id() == ?", (*(statement.IdParam))[0]) - } + statement.cond = statement.cond.And(builder.Eq{colName: ""}) } } } + +func (statement *Statement) joinColumns(cols []*core.Column, includeTableName bool) string { + var colnames = make([]string, len(cols)) + for i, col := range cols { + if includeTableName { + colnames[i] = statement.Engine.Quote(statement.TableName()) + + "." + statement.Engine.Quote(col.Name) + } else { + colnames[i] = statement.Engine.Quote(col.Name) + } + } + return strings.Join(colnames, ", ") +} + +func (statement *Statement) convertIDSQL(sqlStr string) string { + if statement.RefTable != nil { + cols := statement.RefTable.PKColumns() + if len(cols) == 0 { + return "" + } + + colstrs := statement.joinColumns(cols, false) + sqls := splitNNoCase(sqlStr, " from ", 2) + if len(sqls) != 2 { + return "" + } + + var top string + if statement.LimitN > 0 && statement.Engine.dialect.DBType() == core.MSSQL { + top = fmt.Sprintf("TOP %d ", statement.LimitN) + } + + return fmt.Sprintf("SELECT %s%s FROM %v", top, colstrs, sqls[1]) + } + return "" +} + +func (statement *Statement) convertUpdateSQL(sqlStr string) (string, string) { + if statement.RefTable == nil || len(statement.RefTable.PrimaryKeys) != 1 { + return "", "" + } + + colstrs := statement.joinColumns(statement.RefTable.PKColumns(), true) + sqls := splitNNoCase(sqlStr, "where", 2) + if len(sqls) != 2 { + if len(sqls) == 1 { + return sqls[0], fmt.Sprintf("SELECT %v FROM %v", + colstrs, statement.Engine.Quote(statement.TableName())) + } + return "", "" + } + + var whereStr = sqls[1] + + //TODO: for postgres only, if any other database? + var paraStr string + if statement.Engine.dialect.DBType() == core.POSTGRES { + paraStr = "$" + } else if statement.Engine.dialect.DBType() == core.MSSQL { + paraStr = ":" + } + + if paraStr != "" { + if strings.Contains(sqls[1], paraStr) { + dollers := strings.Split(sqls[1], paraStr) + whereStr = dollers[0] + for i, c := range dollers[1:] { + ccs := strings.SplitN(c, " ", 2) + whereStr += fmt.Sprintf(paraStr+"%v %v", i+1, ccs[1]) + } + } + } + + return sqls[0], fmt.Sprintf("SELECT %v FROM %v WHERE %v", + colstrs, statement.Engine.Quote(statement.TableName()), + whereStr) +} diff --git a/vendor/github.com/go-xorm/xorm/syslogger.go b/vendor/github.com/go-xorm/xorm/syslogger.go index eff69d11194..8840635d4c9 100644 --- a/vendor/github.com/go-xorm/xorm/syslogger.go +++ b/vendor/github.com/go-xorm/xorm/syslogger.go @@ -13,51 +13,77 @@ import ( "github.com/go-xorm/core" ) +var _ core.ILogger = &SyslogLogger{} + +// SyslogLogger will be depricated type SyslogLogger struct { - w *syslog.Writer + w *syslog.Writer + showSQL bool } +// NewSyslogLogger implements core.ILogger func NewSyslogLogger(w *syslog.Writer) *SyslogLogger { return &SyslogLogger{w: w} } -func (s *SyslogLogger) Debug(v ...interface{}) (err error) { - return s.w.Debug(fmt.Sprint(v...)) +// Debug log content as Debug +func (s *SyslogLogger) Debug(v ...interface{}) { + s.w.Debug(fmt.Sprint(v...)) } -func (s *SyslogLogger) Debugf(format string, v ...interface{}) (err error) { - return s.w.Debug(fmt.Sprintf(format, v...)) +// Debugf log content as Debug and format +func (s *SyslogLogger) Debugf(format string, v ...interface{}) { + s.w.Debug(fmt.Sprintf(format, v...)) } -func (s *SyslogLogger) Err(v ...interface{}) (err error) { - return s.w.Err(fmt.Sprint(v...)) +// Error log content as Error +func (s *SyslogLogger) Error(v ...interface{}) { + s.w.Err(fmt.Sprint(v...)) } -func (s *SyslogLogger) Errf(format string, v ...interface{}) (err error) { - return s.w.Err(fmt.Sprintf(format, v...)) +// Errorf log content as Errorf and format +func (s *SyslogLogger) Errorf(format string, v ...interface{}) { + s.w.Err(fmt.Sprintf(format, v...)) } -func (s *SyslogLogger) Info(v ...interface{}) (err error) { - return s.w.Info(fmt.Sprint(v...)) +// Info log content as Info +func (s *SyslogLogger) Info(v ...interface{}) { + s.w.Info(fmt.Sprint(v...)) } -func (s *SyslogLogger) Infof(format string, v ...interface{}) (err error) { - return s.w.Info(fmt.Sprintf(format, v...)) +// Infof log content as Infof and format +func (s *SyslogLogger) Infof(format string, v ...interface{}) { + s.w.Info(fmt.Sprintf(format, v...)) } -func (s *SyslogLogger) Warning(v ...interface{}) (err error) { - return s.w.Warning(fmt.Sprint(v...)) +// Warn log content as Warn +func (s *SyslogLogger) Warn(v ...interface{}) { + s.w.Warning(fmt.Sprint(v...)) } -func (s *SyslogLogger) Warningf(format string, v ...interface{}) (err error) { - return s.w.Warning(fmt.Sprintf(format, v...)) +// Warnf log content as Warnf and format +func (s *SyslogLogger) Warnf(format string, v ...interface{}) { + s.w.Warning(fmt.Sprintf(format, v...)) } +// Level shows log level func (s *SyslogLogger) Level() core.LogLevel { return core.LOG_UNKNOWN } // SetLevel always return error, as current log/syslog package doesn't allow to set priority level after syslog.Writer created -func (s *SyslogLogger) SetLevel(l core.LogLevel) (err error) { - return fmt.Errorf("unable to set syslog level") +func (s *SyslogLogger) SetLevel(l core.LogLevel) {} + +// ShowSQL set if logging SQL +func (s *SyslogLogger) ShowSQL(show ...bool) { + if len(show) == 0 { + s.showSQL = true + return + } + s.showSQL = show[0] +} + +// IsShowSQL if logging SQL +func (s *SyslogLogger) IsShowSQL() bool { + return s.showSQL } diff --git a/vendor/github.com/go-xorm/xorm/tag.go b/vendor/github.com/go-xorm/xorm/tag.go new file mode 100644 index 00000000000..4b0e3f54a57 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/tag.go @@ -0,0 +1,281 @@ +// Copyright 2017 The Xorm Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package xorm + +import ( + "fmt" + "reflect" + "strconv" + "strings" + "time" + + "github.com/go-xorm/core" +) + +type tagContext struct { + tagName string + params []string + preTag, nextTag string + table *core.Table + col *core.Column + fieldValue reflect.Value + isIndex bool + isUnique bool + indexNames map[string]int + engine *Engine + hasCacheTag bool + hasNoCacheTag bool + ignoreNext bool +} + +// tagHandler describes tag handler for XORM +type tagHandler func(ctx *tagContext) error + +var ( + // defaultTagHandlers enumerates all the default tag handler + defaultTagHandlers = map[string]tagHandler{ + "<-": OnlyFromDBTagHandler, + "->": OnlyToDBTagHandler, + "PK": PKTagHandler, + "NULL": NULLTagHandler, + "NOT": IgnoreTagHandler, + "AUTOINCR": AutoIncrTagHandler, + "DEFAULT": DefaultTagHandler, + "CREATED": CreatedTagHandler, + "UPDATED": UpdatedTagHandler, + "DELETED": DeletedTagHandler, + "VERSION": VersionTagHandler, + "UTC": UTCTagHandler, + "LOCAL": LocalTagHandler, + "NOTNULL": NotNullTagHandler, + "INDEX": IndexTagHandler, + "UNIQUE": UniqueTagHandler, + "CACHE": CacheTagHandler, + "NOCACHE": NoCacheTagHandler, + } +) + +func init() { + for k := range core.SqlTypes { + defaultTagHandlers[k] = SQLTypeTagHandler + } +} + +// IgnoreTagHandler describes ignored tag handler +func IgnoreTagHandler(ctx *tagContext) error { + return nil +} + +// OnlyFromDBTagHandler describes mapping direction tag handler +func OnlyFromDBTagHandler(ctx *tagContext) error { + ctx.col.MapType = core.ONLYFROMDB + return nil +} + +// OnlyToDBTagHandler describes mapping direction tag handler +func OnlyToDBTagHandler(ctx *tagContext) error { + ctx.col.MapType = core.ONLYTODB + return nil +} + +// PKTagHandler decribes primary key tag handler +func PKTagHandler(ctx *tagContext) error { + ctx.col.IsPrimaryKey = true + ctx.col.Nullable = false + return nil +} + +// NULLTagHandler describes null tag handler +func NULLTagHandler(ctx *tagContext) error { + ctx.col.Nullable = (strings.ToUpper(ctx.preTag) != "NOT") + return nil +} + +// NotNullTagHandler describes notnull tag handler +func NotNullTagHandler(ctx *tagContext) error { + ctx.col.Nullable = false + return nil +} + +// AutoIncrTagHandler describes autoincr tag handler +func AutoIncrTagHandler(ctx *tagContext) error { + ctx.col.IsAutoIncrement = true + /* + if len(ctx.params) > 0 { + autoStartInt, err := strconv.Atoi(ctx.params[0]) + if err != nil { + return err + } + ctx.col.AutoIncrStart = autoStartInt + } else { + ctx.col.AutoIncrStart = 1 + } + */ + return nil +} + +// DefaultTagHandler describes default tag handler +func DefaultTagHandler(ctx *tagContext) error { + if len(ctx.params) > 0 { + ctx.col.Default = ctx.params[0] + } else { + ctx.col.Default = ctx.nextTag + ctx.ignoreNext = true + } + return nil +} + +// CreatedTagHandler describes created tag handler +func CreatedTagHandler(ctx *tagContext) error { + ctx.col.IsCreated = true + return nil +} + +// VersionTagHandler describes version tag handler +func VersionTagHandler(ctx *tagContext) error { + ctx.col.IsVersion = true + ctx.col.Default = "1" + return nil +} + +// UTCTagHandler describes utc tag handler +func UTCTagHandler(ctx *tagContext) error { + ctx.col.TimeZone = time.UTC + return nil +} + +// LocalTagHandler describes local tag handler +func LocalTagHandler(ctx *tagContext) error { + if len(ctx.params) == 0 { + ctx.col.TimeZone = time.Local + } else { + var err error + ctx.col.TimeZone, err = time.LoadLocation(ctx.params[0]) + if err != nil { + return err + } + } + return nil +} + +// UpdatedTagHandler describes updated tag handler +func UpdatedTagHandler(ctx *tagContext) error { + ctx.col.IsUpdated = true + return nil +} + +// DeletedTagHandler describes deleted tag handler +func DeletedTagHandler(ctx *tagContext) error { + ctx.col.IsDeleted = true + return nil +} + +// IndexTagHandler describes index tag handler +func IndexTagHandler(ctx *tagContext) error { + if len(ctx.params) > 0 { + ctx.indexNames[ctx.params[0]] = core.IndexType + } else { + ctx.isIndex = true + } + return nil +} + +// UniqueTagHandler describes unique tag handler +func UniqueTagHandler(ctx *tagContext) error { + if len(ctx.params) > 0 { + ctx.indexNames[ctx.params[0]] = core.UniqueType + } else { + ctx.isUnique = true + } + return nil +} + +// SQLTypeTagHandler describes SQL Type tag handler +func SQLTypeTagHandler(ctx *tagContext) error { + ctx.col.SQLType = core.SQLType{Name: ctx.tagName} + if len(ctx.params) > 0 { + if ctx.tagName == core.Enum { + ctx.col.EnumOptions = make(map[string]int) + for k, v := range ctx.params { + v = strings.TrimSpace(v) + v = strings.Trim(v, "'") + ctx.col.EnumOptions[v] = k + } + } else if ctx.tagName == core.Set { + ctx.col.SetOptions = make(map[string]int) + for k, v := range ctx.params { + v = strings.TrimSpace(v) + v = strings.Trim(v, "'") + ctx.col.SetOptions[v] = k + } + } else { + var err error + if len(ctx.params) == 2 { + ctx.col.Length, err = strconv.Atoi(ctx.params[0]) + if err != nil { + return err + } + ctx.col.Length2, err = strconv.Atoi(ctx.params[1]) + if err != nil { + return err + } + } else if len(ctx.params) == 1 { + ctx.col.Length, err = strconv.Atoi(ctx.params[0]) + if err != nil { + return err + } + } + } + } + return nil +} + +// ExtendsTagHandler describes extends tag handler +func ExtendsTagHandler(ctx *tagContext) error { + var fieldValue = ctx.fieldValue + switch fieldValue.Kind() { + case reflect.Ptr: + f := fieldValue.Type().Elem() + if f.Kind() == reflect.Struct { + fieldPtr := fieldValue + fieldValue = fieldValue.Elem() + if !fieldValue.IsValid() || fieldPtr.IsNil() { + fieldValue = reflect.New(f).Elem() + } + } + fallthrough + case reflect.Struct: + parentTable, err := ctx.engine.mapType(fieldValue) + if err != nil { + return err + } + for _, col := range parentTable.Columns() { + col.FieldName = fmt.Sprintf("%v.%v", ctx.col.FieldName, col.FieldName) + ctx.table.AddColumn(col) + for indexName, indexType := range col.Indexes { + addIndex(indexName, ctx.table, col, indexType) + } + } + default: + //TODO: warning + } + return nil +} + +// CacheTagHandler describes cache tag handler +func CacheTagHandler(ctx *tagContext) error { + if !ctx.hasCacheTag { + ctx.hasCacheTag = true + } + return nil +} + +// NoCacheTagHandler describes nocache tag handler +func NoCacheTagHandler(ctx *tagContext) error { + if !ctx.hasNoCacheTag { + ctx.hasNoCacheTag = true + } + return nil +} diff --git a/vendor/github.com/go-xorm/xorm/types.go b/vendor/github.com/go-xorm/xorm/types.go new file mode 100644 index 00000000000..99d761c2789 --- /dev/null +++ b/vendor/github.com/go-xorm/xorm/types.go @@ -0,0 +1,12 @@ +package xorm + +import ( + "reflect" + + "github.com/go-xorm/core" +) + +var ( + ptrPkType = reflect.TypeOf(&core.PK{}) + pkType = reflect.TypeOf(core.PK{}) +) diff --git a/vendor/github.com/go-xorm/xorm/xorm.go b/vendor/github.com/go-xorm/xorm/xorm.go index 2da9949ebc2..2cfbe9ecd31 100644 --- a/vendor/github.com/go-xorm/xorm/xorm.go +++ b/vendor/github.com/go-xorm/xorm/xorm.go @@ -5,7 +5,6 @@ package xorm import ( - "errors" "fmt" "os" "reflect" @@ -17,7 +16,8 @@ import ( ) const ( - Version string = "0.4.5.0204" + // Version show the xorm's version + Version string = "0.6.2.0326" ) func regDrvsNDialects() bool { @@ -31,6 +31,7 @@ func regDrvsNDialects() bool { "mysql": {"mysql", func() core.Driver { return &mysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "mymysql": {"mysql", func() core.Driver { return &mymysqlDriver{} }, func() core.Dialect { return &mysql{} }}, "postgres": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, + "pgx": {"postgres", func() core.Driver { return &pqDriver{} }, func() core.Dialect { return &postgres{} }}, "sqlite3": {"sqlite3", func() core.Driver { return &sqlite3Driver{} }, func() core.Dialect { return &sqlite3{} }}, "oci8": {"oracle", func() core.Driver { return &oci8Driver{} }, func() core.Dialect { return &oracle{} }}, "goracle": {"oracle", func() core.Driver { return &goracleDriver{} }, func() core.Dialect { return &oracle{} }}, @@ -49,13 +50,13 @@ func close(engine *Engine) { engine.Close() } -// new a db manager according to the parameter. Currently support four +// NewEngine new a db manager according to the parameter. Currently support four // drivers func NewEngine(driverName string, dataSourceName string) (*Engine, error) { regDrvsNDialects() driver := core.QueryDriver(driverName) if driver == nil { - return nil, errors.New(fmt.Sprintf("Unsupported driver name: %v", driverName)) + return nil, fmt.Errorf("Unsupported driver name: %v", driverName) } uri, err := driver.Parse(driverName, dataSourceName) @@ -65,7 +66,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { dialect := core.QueryDialect(uri.DbType) if dialect == nil { - return nil, errors.New(fmt.Sprintf("Unsupported dialect type: %v", uri.DbType)) + return nil, fmt.Errorf("Unsupported dialect type: %v", uri.DbType) } db, err := core.Open(driverName, dataSourceName) @@ -84,12 +85,13 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { Tables: make(map[reflect.Type]*core.Table), mutex: &sync.RWMutex{}, TagIdentifier: "xorm", - Logger: NewSimpleLogger(os.Stdout), TZLocation: time.Local, + tagHandlers: defaultTagHandlers, } - engine.dialect.SetLogger(engine.Logger) - + logger := NewSimpleLogger(os.Stdout) + logger.SetLevel(core.LOG_INFO) + engine.SetLogger(logger) engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) runtime.SetFinalizer(engine, close) @@ -97,7 +99,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return engine, nil } -// clone an engine +// Clone clone an engine func (engine *Engine) Clone() (*Engine, error) { return NewEngine(engine.DriverName(), engine.DataSourceName()) } diff --git a/vendor/vendor.json b/vendor/vendor.json index 1260057f5fd..32df62ae334 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -332,6 +332,28 @@ "revision": "9dee4ca50b83acdf57a35fb9e6fb4be640afa2f3", "revisionTime": "2017-03-27T11:30:21Z" }, + { + "checksumSHA1": "HHB+Jna1wv0cXLxtCyOnQqFwvn4=", + "path": "github.com/go-xorm/builder", + "revision": "c6e604e9c7b7461715091e14ad0c242ec44c26e4", + "revisionTime": "2017-02-24T04:30:50Z" + }, + { + "checksumSHA1": "vt2CGANHLNXPAZ01ve3UlsgQ0uU=", + "path": "github.com/go-xorm/core", + "revision": "e8409d73255791843585964791443dbad877058c", + "revisionTime": "2017-03-17T12:25:07Z" + }, + { + "path": "github.com/go-xorm/xor,m", + "revision": "" + }, + { + "checksumSHA1": "xkwhf97yNV6tFwrOHCPeWtKW39E=", + "path": "github.com/go-xorm/xorm", + "revision": "6687a2b4e824f4d87f2d65060ec5cb0d896dff1e", + "revisionTime": "2017-03-30T09:59:41Z" + }, { "checksumSHA1": "B4bk7vdV9aD7AhrtATXfgEacdqE=", "path": "github.com/gorilla/websocket",