From 4df43ffa0ff8578ee55560b92c04cb6d831d5548 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 7 Jan 2014 17:33:27 +0800 Subject: [PATCH 01/55] new architecture --- base_test.go | 8 +- core/column.go | 86 + core/converstion.go | 8 + core/db.go | 44 + core/db_test.go | 53 + core/dialect.go | 144 + core/driver.go | 23 + core/filter.go | 42 + core/index.go | 25 + core/table.go | 94 + core/type.go | 235 ++ mssql.go => dialects/mssql.go | 196 +- dialects/mysql.go | 276 ++ oracle.go => dialects/oracle.go | 186 +- postgres.go => dialects/postgres.go | 242 +- sqlite3.go => dialects/sqlite3.go | 81 +- docs/AutoMap.md | 128 +- docs/COLUMNTYPE.md | 874 ++--- docs/ChangelogCN.md | 62 +- docs/cache_design.graffle | 4590 +++++++++++++-------------- drivers/goracle.go | 38 + mymysql.go => drivers/mymysql.go | 32 +- drivers/mysql.go | 50 + drivers/oci.go | 37 + drivers/odbc.go | 34 + drivers/pq.go | 59 + drivers/sqlite3.go | 16 + engine.go | 177 +- examples/cache.go | 164 +- examples/cachegoroutine.go | 158 +- examples/conversion.go | 100 +- examples/derive.go | 88 +- examples/goroutine.go | 160 +- examples/maxconnect.go | 160 +- examples/pool.go | 62 +- examples/singlemapping.go | 76 +- examples/sync.go | 124 +- mysql.go | 344 -- rows.go | 2 +- session.go | 131 +- statement.go | 94 +- table.go | 387 +-- tests/mysql_ddl.sql | 8 +- xorm.go | 51 +- xorm/.gopmfile | 2 +- xorm/c++.go | 102 +- xorm/cmd.go | 94 +- xorm/go.go | 420 +-- xorm/lang.go | 68 +- xorm/reverse.go | 400 +-- xorm/shell.go | 216 +- xorm/xorm.go | 158 +- 52 files changed, 5946 insertions(+), 5463 deletions(-) create mode 100644 core/column.go create mode 100644 core/converstion.go create mode 100644 core/db.go create mode 100644 core/db_test.go create mode 100644 core/dialect.go create mode 100644 core/driver.go create mode 100644 core/filter.go create mode 100644 core/index.go create mode 100644 core/table.go create mode 100644 core/type.go rename mssql.go => dialects/mssql.go (63%) create mode 100644 dialects/mysql.go rename oracle.go => dialects/oracle.go (56%) rename postgres.go => dialects/postgres.go (54%) rename sqlite3.go => dialects/sqlite3.go (80%) create mode 100644 drivers/goracle.go rename mymysql.go => drivers/mymysql.go (67%) create mode 100644 drivers/mysql.go create mode 100644 drivers/oci.go create mode 100644 drivers/odbc.go create mode 100644 drivers/pq.go create mode 100644 drivers/sqlite3.go delete mode 100644 mysql.go diff --git a/base_test.go b/base_test.go index 66631160..98905f84 100644 --- a/base_test.go +++ b/base_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" "time" + + "github.com/lunny/xorm/core" ) /* @@ -1850,7 +1852,7 @@ func testMetaInfo(engine *Engine, t *testing.T) { for _, table := range tables { fmt.Println(table.Name) - for _, col := range table.Columns { + for _, col := range table.Columns() { fmt.Println(col.String(engine.dialect)) } @@ -3525,8 +3527,8 @@ func testNullValue(engine *Engine, t *testing.T) { // skipped postgres test due to postgres driver doesn't read time.Time's timzezone info when stored in the db // mysql and sqlite3 seem have done this correctly by storing datatime in UTC timezone, I think postgres driver // prefer using timestamp with timezone to sovle the issue - if engine.DriverName != POSTGRES && engine.DriverName != MYMYSQL && - engine.DriverName != MYSQL { + if engine.DriverName != core.POSTGRES && engine.DriverName != "mymysql" && + engine.DriverName != core.MYSQL { if (*nullDataGet.TimePtr).Unix() != (*nullDataUpdate.TimePtr).Unix() { t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]:[%v]", *nullDataGet.TimePtr, *nullDataUpdate.TimePtr))) } else { diff --git a/core/column.go b/core/column.go new file mode 100644 index 00000000..ac90bc39 --- /dev/null +++ b/core/column.go @@ -0,0 +1,86 @@ +package core + +import ( + "reflect" + "strings" +) + +// database column +type Column struct { + Name string + FieldName string + SQLType SQLType + Length int + Length2 int + Nullable bool + Default string + Indexes map[string]bool + IsPrimaryKey bool + IsAutoIncrement bool + MapType int + IsCreated bool + IsUpdated bool + IsCascade bool + IsVersion bool +} + +// generate column description string according dialect +func (col *Column) String(d Dialect) string { + sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " + + sql += d.SqlType(col) + " " + + if col.IsPrimaryKey { + sql += "PRIMARY KEY " + if col.IsAutoIncrement { + sql += d.AutoIncrStr() + " " + } + } + + if col.Nullable { + sql += "NULL " + } else { + sql += "NOT NULL " + } + + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + + return sql +} + +func (col *Column) StringNoPk(d Dialect) string { + sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " + + sql += d.SqlType(col) + " " + + if col.Nullable { + sql += "NULL " + } else { + sql += "NOT NULL " + } + + if col.Default != "" { + sql += "DEFAULT " + col.Default + " " + } + + return sql +} + +// return col's filed of struct's value +func (col *Column) ValueOf(bean interface{}) reflect.Value { + var fieldValue reflect.Value + if strings.Contains(col.FieldName, ".") { + fields := strings.Split(col.FieldName, ".") + if len(fields) > 2 { + return reflect.ValueOf(nil) + } + + fieldValue = reflect.Indirect(reflect.ValueOf(bean)).FieldByName(fields[0]) + fieldValue = fieldValue.FieldByName(fields[1]) + } else { + fieldValue = reflect.Indirect(reflect.ValueOf(bean)).FieldByName(col.FieldName) + } + return fieldValue +} diff --git a/core/converstion.go b/core/converstion.go new file mode 100644 index 00000000..18522fbe --- /dev/null +++ b/core/converstion.go @@ -0,0 +1,8 @@ +package core + +// Conversion is an interface. A type implements Conversion will according +// the custom method to fill into database and retrieve from database. +type Conversion interface { + FromDB([]byte) error + ToDB() ([]byte, error) +} diff --git a/core/db.go b/core/db.go new file mode 100644 index 00000000..9249c2a8 --- /dev/null +++ b/core/db.go @@ -0,0 +1,44 @@ +package core + +import ( + "database/sql" + "reflect" +) + +type DB struct { + *sql.DB +} + +func Open(driverName, dataSourceName string) (*DB, error) { + db, err := sql.Open(driverName, dataSourceName) + return &DB{db}, err +} + +func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { + rows, err := db.DB.Query(query, args...) + return &Rows{rows}, err +} + +type Rows struct { + *sql.Rows +} + +func (rs *Rows) Scan(dest ...interface{}) error { + newDest := make([]interface{}, 0) + for _, s := range dest { + vv := reflect.ValueOf(s) + switch vv.Kind() { + case reflect.Ptr: + vvv := vv.Elem() + if vvv.Kind() == reflect.Struct { + for j := 0; j < vvv.NumField(); j++ { + newDest = append(newDest, vvv.FieldByIndex([]int{j}).Addr().Interface()) + } + } else { + newDest = append(newDest, s) + } + } + } + + return rs.Rows.Scan(newDest...) +} diff --git a/core/db_test.go b/core/db_test.go new file mode 100644 index 00000000..8894bd17 --- /dev/null +++ b/core/db_test.go @@ -0,0 +1,53 @@ +package core + +import ( + "fmt" + "testing" + + _ "github.com/mattn/go-sqlite3" +) + +var ( + createTableSqlite3 = "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);" +) + +type User struct { + Id int64 + Name string + Title string + Age float32 + Alias string + NickName string +} + +func TestQuery(t *testing.T) { + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + 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.Scan(&user) + if err != nil { + t.Error(err) + } + fmt.Println(user) + } +} diff --git a/core/dialect.go b/core/dialect.go new file mode 100644 index 00000000..fd7a483e --- /dev/null +++ b/core/dialect.go @@ -0,0 +1,144 @@ +package core + +import ( + "strings" + "time" +) + +type dbType string + +type Uri struct { + DbType dbType + Proto string + Host string + Port string + DbName string + User string + Passwd string + Charset string + Laddr string + Raddr string + Timeout time.Duration +} + +// a dialect is a driver's wrapper +type Dialect interface { + Init(*Uri, string, string) error + URI() *Uri + DBType() dbType + SqlType(t *Column) string + SupportInsertMany() bool + QuoteStr() string + AutoIncrStr() string + SupportEngine() bool + SupportCharset() bool + IndexOnTable() bool + + IndexCheckSql(tableName, idxName string) (string, []interface{}) + TableCheckSql(tableName string) (string, []interface{}) + ColumnCheckSql(tableName, colName string) (string, []interface{}) + + GetColumns(tableName string) ([]string, map[string]*Column, error) + GetTables() ([]*Table, error) + GetIndexes(tableName string) (map[string]*Index, error) + + CreateTableSql(table *Table, tableName, storeEngine, charset string) string + Filters() []Filter + + DriverName() string + DataSourceName() string +} + +type Base struct { + dialect Dialect + driverName string + dataSourceName string + *Uri +} + +func (b *Base) Init(dialect Dialect, uri *Uri, drivername, dataSourceName string) error { + b.dialect = dialect + b.driverName, b.dataSourceName = drivername, dataSourceName + b.Uri = uri + return nil +} + +func (b *Base) URI() *Uri { + return b.Uri +} + +func (b *Base) DBType() dbType { + return b.Uri.DbType +} + +func (b *Base) DriverName() string { + return b.driverName +} + +func (b *Base) DataSourceName() string { + return b.dataSourceName +} + +func (b *Base) Quote(c string) string { + return b.dialect.QuoteStr() + c + b.dialect.QuoteStr() +} + +func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset string) string { + var sql string + sql = "CREATE TABLE IF NOT EXISTS " + if tableName == "" { + tableName = table.Name + } + + sql += b.Quote(tableName) + " (" + + pkList := table.PrimaryKeys + + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.IsPrimaryKey && len(pkList) == 1 { + sql += col.String(b.dialect) + } else { + sql += col.StringNoPk(b.dialect) + } + sql = strings.TrimSpace(sql) + sql += ", " + } + + if len(pkList) > 1 { + sql += "PRIMARY KEY ( " + sql += strings.Join(pkList, ",") + sql += " ), " + } + + sql = sql[:len(sql)-2] + ")" + if b.dialect.SupportEngine() && storeEngine != "" { + sql += " ENGINE=" + storeEngine + } + if b.dialect.SupportCharset() { + if charset == "" { + charset = b.dialect.URI().Charset + } + sql += " DEFAULT CHARSET " + charset + } + sql += ";" + return sql +} + +var ( + dialects = map[dbType]Dialect{} +) + +func RegisterDialect(dbName dbType, dialect Dialect) { + if dialect == nil { + panic("core: Register dialect is nil") + } + if _, dup := dialects[dbName]; dup { + panic("core: Register called twice for dialect " + dbName) + } + dialects[dbName] = dialect +} + +func QueryDialect(dbName dbType) Dialect { + return dialects[dbName] +} diff --git a/core/driver.go b/core/driver.go new file mode 100644 index 00000000..b008a0ac --- /dev/null +++ b/core/driver.go @@ -0,0 +1,23 @@ +package core + +type driver interface { + Parse(string, string) (*Uri, error) +} + +var ( + drivers = map[string]driver{} +) + +func RegisterDriver(driverName string, driver driver) { + if driver == nil { + panic("core: Register driver is nil") + } + if _, dup := drivers[driverName]; dup { + panic("core: Register called twice for driver " + driverName) + } + drivers[driverName] = driver +} + +func QueryDriver(driverName string) driver { + return drivers[driverName] +} diff --git a/core/filter.go b/core/filter.go new file mode 100644 index 00000000..072bc589 --- /dev/null +++ b/core/filter.go @@ -0,0 +1,42 @@ +package core + +import "strings" + +// Filter is an interface to filter SQL +type Filter interface { + Do(sql string, dialect Dialect, table *Table) string +} + +// QuoteFilter filter SQL replace ` to database's own quote character +type QuoteFilter struct { +} + +func (s *QuoteFilter) Do(sql string, dialect Dialect, table *Table) string { + return strings.Replace(sql, "`", dialect.QuoteStr(), -1) +} + +// IdFilter filter SQL replace (id) to primary key column name +type IdFilter struct { +} + +type Quoter struct { + dialect Dialect +} + +func NewQuoter(dialect Dialect) *Quoter { + return &Quoter{dialect} +} + +func (q *Quoter) Quote(content string) string { + return q.dialect.QuoteStr() + content + q.dialect.QuoteStr() +} + +func (i *IdFilter) Do(sql string, dialect Dialect, table *Table) string { + quoter := NewQuoter(dialect) + if table != nil && len(table.PrimaryKeys) == 1 { + sql = strings.Replace(sql, "`(id)`", quoter.Quote(table.PrimaryKeys[0]), -1) + sql = strings.Replace(sql, quoter.Quote("(id)"), quoter.Quote(table.PrimaryKeys[0]), -1) + return strings.Replace(sql, "(id)", quoter.Quote(table.PrimaryKeys[0]), -1) + } + return sql +} diff --git a/core/index.go b/core/index.go new file mode 100644 index 00000000..eb088850 --- /dev/null +++ b/core/index.go @@ -0,0 +1,25 @@ +package core + +const ( + IndexType = iota + 1 + UniqueType +) + +// database index +type Index struct { + Name string + Type int + Cols []string +} + +// add columns which will be composite index +func (index *Index) AddColumn(cols ...string) { + for _, col := range cols { + index.Cols = append(index.Cols, col) + } +} + +// new an index +func NewIndex(name string, indexType int) *Index { + return &Index{name, indexType, make([]string, 0)} +} diff --git a/core/table.go b/core/table.go new file mode 100644 index 00000000..554d5a5c --- /dev/null +++ b/core/table.go @@ -0,0 +1,94 @@ +package core + +import ( + "reflect" + "strings" +) + +// database table +type Table struct { + Name string + Type reflect.Type + columnsSeq []string + columns map[string]*Column + Indexes map[string]*Index + PrimaryKeys []string + AutoIncrement string + Created map[string]bool + Updated string + Version string +} + +func (table *Table) Columns() map[string]*Column { + return table.columns +} + +func (table *Table) ColumnsSeq() []string { + return table.columnsSeq +} + +func NewEmptyTable() *Table { + return &Table{columnsSeq: make([]string, 0), + columns: make(map[string]*Column), + Indexes: make(map[string]*Index), + Created: make(map[string]bool), + PrimaryKeys: make([]string, 0), + } +} + +func NewTable(name string, t reflect.Type) *Table { + return &Table{Name: name, Type: t, + columnsSeq: make([]string, 0), + columns: make(map[string]*Column), + Indexes: make(map[string]*Index), + Created: make(map[string]bool), + PrimaryKeys: make([]string, 0), + } +} + +func (table *Table) GetColumn(name string) *Column { + return table.columns[strings.ToLower(name)] +} + +// if has primary key, return column +func (table *Table) PKColumns() []*Column { + columns := make([]*Column, 0) + for _, name := range table.PrimaryKeys { + columns = append(columns, table.GetColumn(name)) + } + return columns +} + +func (table *Table) AutoIncrColumn() *Column { + return table.GetColumn(table.AutoIncrement) +} + +func (table *Table) VersionColumn() *Column { + return table.GetColumn(table.Version) +} + +// add a column to table +func (table *Table) AddColumn(col *Column) { + table.columnsSeq = append(table.columnsSeq, col.Name) + table.columns[strings.ToLower(col.Name)] = col + if col.IsPrimaryKey { + table.PrimaryKeys = append(table.PrimaryKeys, col.Name) + } + if col.IsAutoIncrement { + table.AutoIncrement = col.Name + } + if col.IsCreated { + table.Created[col.Name] = true + } + if col.IsUpdated { + table.Updated = col.Name + } + if col.IsVersion { + table.Version = col.Name + } +} + +// add an index or an unique to table +func (table *Table) AddIndex(index *Index) { + table.Indexes[index.Name] = index +} diff --git a/core/type.go b/core/type.go new file mode 100644 index 00000000..84c4426c --- /dev/null +++ b/core/type.go @@ -0,0 +1,235 @@ +package core + +import ( + "reflect" + "sort" + "strings" + "time" +) + +const ( + POSTGRES = "postgres" + SQLITE = "sqlite3" + MYSQL = "mysql" + MSSQL = "mssql" + ORACLE = "oracle" +) + +// xorm SQL types +type SQLType struct { + Name string + DefaultLength int + DefaultLength2 int +} + +func (s *SQLType) IsText() bool { + return s.Name == Char || s.Name == Varchar || s.Name == TinyText || + s.Name == Text || s.Name == MediumText || s.Name == LongText +} + +func (s *SQLType) IsBlob() bool { + return (s.Name == TinyBlob) || (s.Name == Blob) || + s.Name == MediumBlob || s.Name == LongBlob || + s.Name == Binary || s.Name == VarBinary || s.Name == Bytea +} + +var ( + Bit = "BIT" + TinyInt = "TINYINT" + SmallInt = "SMALLINT" + MediumInt = "MEDIUMINT" + Int = "INT" + Integer = "INTEGER" + BigInt = "BIGINT" + + Char = "CHAR" + Varchar = "VARCHAR" + TinyText = "TINYTEXT" + Text = "TEXT" + MediumText = "MEDIUMTEXT" + LongText = "LONGTEXT" + + Date = "DATE" + DateTime = "DATETIME" + Time = "TIME" + TimeStamp = "TIMESTAMP" + TimeStampz = "TIMESTAMPZ" + + Decimal = "DECIMAL" + Numeric = "NUMERIC" + + Real = "REAL" + Float = "FLOAT" + Double = "DOUBLE" + + Binary = "BINARY" + VarBinary = "VARBINARY" + TinyBlob = "TINYBLOB" + Blob = "BLOB" + MediumBlob = "MEDIUMBLOB" + LongBlob = "LONGBLOB" + Bytea = "BYTEA" + + Bool = "BOOL" + + Serial = "SERIAL" + BigSerial = "BIGSERIAL" + + SqlTypes = map[string]bool{ + Bit: true, + TinyInt: true, + SmallInt: true, + MediumInt: true, + Int: true, + Integer: true, + BigInt: true, + + Char: true, + Varchar: true, + TinyText: true, + Text: true, + MediumText: true, + LongText: true, + + Date: true, + DateTime: true, + Time: true, + TimeStamp: true, + TimeStampz: true, + + Decimal: true, + Numeric: true, + + Binary: true, + VarBinary: true, + Real: true, + Float: true, + Double: true, + TinyBlob: true, + Blob: true, + MediumBlob: true, + LongBlob: true, + Bytea: true, + + Bool: true, + + Serial: true, + BigSerial: true, + } + + intTypes = sort.StringSlice{"*int", "*int16", "*int32", "*int8"} + uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"} +) + +// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparision +var ( + c_EMPTY_STRING string + c_BOOL_DEFAULT bool + c_BYTE_DEFAULT byte + c_COMPLEX64_DEFAULT complex64 + c_COMPLEX128_DEFAULT complex128 + c_FLOAT32_DEFAULT float32 + c_FLOAT64_DEFAULT float64 + c_INT64_DEFAULT int64 + c_UINT64_DEFAULT uint64 + c_INT32_DEFAULT int32 + c_UINT32_DEFAULT uint32 + c_INT16_DEFAULT int16 + c_UINT16_DEFAULT uint16 + c_INT8_DEFAULT int8 + c_UINT8_DEFAULT uint8 + c_INT_DEFAULT int + c_UINT_DEFAULT uint + c_TIME_DEFAULT time.Time +) + +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: + st = SQLType{Int, 0, 0} + case reflect.Int64, reflect.Uint64: + st = SQLType{BigInt, 0, 0} + case reflect.Float32: + st = SQLType{Float, 0, 0} + case reflect.Float64: + st = SQLType{Double, 0, 0} + case reflect.Complex64, reflect.Complex128: + st = SQLType{Varchar, 64, 0} + case reflect.Array, reflect.Slice, reflect.Map: + if t.Elem() == reflect.TypeOf(c_BYTE_DEFAULT) { + st = SQLType{Blob, 0, 0} + } else { + st = SQLType{Text, 0, 0} + } + case reflect.Bool: + st = SQLType{Bool, 0, 0} + case reflect.String: + st = SQLType{Varchar, 255, 0} + case reflect.Struct: + if t == reflect.TypeOf(c_TIME_DEFAULT) { + st = SQLType{DateTime, 0, 0} + } else { + // TODO need to handle association struct + st = SQLType{Text, 0, 0} + } + case reflect.Ptr: + st, _ = ptrType2SQLType(t) + 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) + switch name { + case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, Serial: + return reflect.TypeOf(1) + case BigInt, BigSerial: + return reflect.TypeOf(int64(1)) + case Float, Real: + return reflect.TypeOf(float32(1)) + case Double: + return reflect.TypeOf(float64(1)) + case Char, Varchar, TinyText, Text, MediumText, LongText: + return reflect.TypeOf("") + case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary: + return reflect.TypeOf([]byte{}) + case Bool: + return reflect.TypeOf(true) + case DateTime, Date, Time, TimeStamp, TimeStampz: + return reflect.TypeOf(c_TIME_DEFAULT) + case Decimal, Numeric: + return reflect.TypeOf("") + default: + return reflect.TypeOf("") + } +} diff --git a/mssql.go b/dialects/mssql.go similarity index 63% rename from mssql.go rename to dialects/mssql.go index 3606332f..a9c385e6 100644 --- a/mssql.go +++ b/dialects/mssql.go @@ -1,46 +1,28 @@ -package xorm +package dialects import ( //"crypto/tls" - "database/sql" + "errors" "fmt" //"regexp" "strconv" "strings" //"time" + + . "github.com/lunny/xorm/core" ) +func init() { + RegisterDialect("mssql", &mssql{}) +} + type mssql struct { - base - quoteFilter Filter + Base } -type odbcParser struct { -} - -func (p *odbcParser) parse(driverName, dataSourceName string) (*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 &uri{dbName: dbName, dbType: MSSQL}, nil -} - -func (db *mssql) Init(drivername, uri string) error { - db.quoteFilter = &QuoteFilter{} - return db.base.init(&odbcParser{}, drivername, uri) +func (db *mssql) Init(uri *Uri, drivername, dataSourceName string) error { + return db.Base.Init(db, uri, drivername, dataSourceName) } func (db *mssql) SqlType(c *Column) string { @@ -139,51 +121,48 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*Column, err 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 + `')` - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + + rows, err := cnn.Query(s, args...) if err != nil { return nil, nil, err } cols := make(map[string]*Column) colSeq := make([]string, 0) - for _, record := range res { + for rows.Next() { + var name, ctype, precision, scale string + var maxLen int + err = rows.Scan(&name, &ctype, &maxLen, &precision, &scale) + if err != nil { + return nil, nil, err + } + col := new(Column) col.Indexes = make(map[string]bool) - for name, content := range record { - switch name { - case "name": + col.Length = maxLen + col.Name = strings.Trim(name, "` ") - col.Name = strings.Trim(string(content), "` ") - case "ctype": - ct := strings.ToUpper(string(content)) - switch ct { - case "DATETIMEOFFSET": - col.SQLType = SQLType{TimeStampz, 0, 0} - case "NVARCHAR": - col.SQLType = SQLType{Varchar, 0, 0} - case "IMAGE": - col.SQLType = SQLType{VarBinary, 0, 0} - default: - if _, ok := sqlTypes[ct]; ok { - col.SQLType = SQLType{ct, 0, 0} - } else { - return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v", - ct, tableName, col.Name)) - } - } - - case "max_length": - len1, err := strconv.Atoi(strings.TrimSpace(string(content))) - if err != nil { - return nil, nil, err - } - col.Length = len1 + ct := strings.ToUpper(ctype) + switch ct { + case "DATETIMEOFFSET": + col.SQLType = SQLType{TimeStampz, 0, 0} + case "NVARCHAR": + col.SQLType = SQLType{Varchar, 0, 0} + case "IMAGE": + col.SQLType = SQLType{VarBinary, 0, 0} + default: + if _, ok := SqlTypes[ct]; ok { + col.SQLType = SQLType{ct, 0, 0} + } else { + return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v", + ct, tableName, col.Name)) } } + if col.SQLType.IsText() { if col.Default != "" { col.Default = "'" + col.Default + "'" @@ -198,25 +177,25 @@ where a.object_id=object_id('` + tableName + `')` func (db *mssql) GetTables() ([]*Table, error) { args := []interface{}{} s := `select name from sysobjects where xtype ='U'` - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "name": - table.Name = strings.Trim(string(content), "` ") - } + for rows.Next() { + table := NewEmptyTable() + var name string + err = rows.Scan(&name) + if err != nil { + return nil, err } + table.Name = strings.Trim(name, "` ") tables = append(tables, table) } return tables, nil @@ -238,40 +217,39 @@ 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) =? ` - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } indexes := make(map[string]*Index, 0) - for _, record := range res { + for rows.Next() { var indexType int - var indexName, colName string - for name, content := range record { - switch name { - case "IS_UNIQUE": - i, err := strconv.ParseBool(string(content)) - if err != nil { - return nil, err - } + var indexName, colName, isUnique string - if i { - indexType = UniqueType - } else { - indexType = IndexType - } - case "INDEX_NAME": - indexName = string(content) - case "COLUMN_NAME": - colName = strings.Trim(string(content), "` ") - } + err = rows.Scan(&indexName, &colName, &isUnique, nil) + if err != nil { + return nil, err } + i, err := strconv.ParseBool(isUnique) + if err != nil { + return nil, err + } + + if i { + indexType = UniqueType + } else { + indexType = IndexType + } + + colName = strings.Trim(colName, "` ") + if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { indexName = indexName[5+len(tableName) : len(indexName)] } @@ -288,3 +266,41 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? } return indexes, nil } + +func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset string) string { + var sql string + if tableName == "" { + tableName = table.Name + } + + sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + tableName + "' ) CREATE TABLE" + + sql += db.QuoteStr() + tableName + db.QuoteStr() + " (" + + pkList := table.PrimaryKeys + + for _, colName := range table.ColumnsSeq() { + col := table.GetColumn(colName) + if col.IsPrimaryKey && len(pkList) == 1 { + sql += col.String(db) + } else { + sql += col.StringNoPk(db) + } + sql = strings.TrimSpace(sql) + sql += ", " + } + + if len(pkList) > 1 { + sql += "PRIMARY KEY ( " + sql += strings.Join(pkList, ",") + sql += " ), " + } + + sql = sql[:len(sql)-2] + ")" + sql += ";" + return sql +} + +func (db *mssql) Filters() []Filter { + return []Filter{&IdFilter{}, &QuoteFilter{}} +} diff --git a/dialects/mysql.go b/dialects/mysql.go new file mode 100644 index 00000000..e3bca826 --- /dev/null +++ b/dialects/mysql.go @@ -0,0 +1,276 @@ +package dialects + +import ( + "crypto/tls" + "errors" + "fmt" + "strconv" + "strings" + "time" + + . "github.com/lunny/xorm/core" +) + +func init() { + RegisterDialect("mysql", &mysql{}) +} + +type mysql struct { + Base + net string + addr string + params map[string]string + loc *time.Location + timeout time.Duration + tls *tls.Config + allowAllFiles bool + allowOldPasswords bool + clientFoundRows bool +} + +func (db *mysql) Init(uri *Uri, drivername, dataSourceName string) error { + return db.Base.Init(db, uri, drivername, dataSourceName) +} + +func (db *mysql) SqlType(c *Column) string { + var res string + switch t := c.SQLType.Name; t { + case Bool: + res = TinyInt + case Serial: + c.IsAutoIncrement = true + c.IsPrimaryKey = true + c.Nullable = false + res = Int + case BigSerial: + c.IsAutoIncrement = true + c.IsPrimaryKey = true + c.Nullable = false + res = BigInt + case Bytea: + res = Blob + case TimeStampz: + res = Char + c.Length = 64 + default: + res = t + } + + var hasLen1 bool = (c.Length > 0) + var hasLen2 bool = (c.Length2 > 0) + if hasLen1 { + res += "(" + strconv.Itoa(c.Length) + ")" + } else if hasLen2 { + res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" + } + return res +} + +func (db *mysql) SupportInsertMany() bool { + return true +} + +func (db *mysql) QuoteStr() string { + return "`" +} + +func (db *mysql) SupportEngine() bool { + return true +} + +func (db *mysql) AutoIncrStr() string { + return "AUTO_INCREMENT" +} + +func (db *mysql) SupportCharset() bool { + return true +} + +func (db *mysql) IndexOnTable() bool { + return true +} + +func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) { + args := []interface{}{db.DbName, tableName, idxName} + sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`" + sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?" + return sql, args +} + +func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) { + args := []interface{}{db.DbName, tableName, colName} + sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?" + return sql, args +} + +func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) { + args := []interface{}{db.DbName, tableName} + sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?" + return sql, args +} + +func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, error) { + 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` = ?" + cnn, err := Open(db.DriverName(), db.DataSourceName()) + if err != nil { + return nil, nil, err + } + defer cnn.Close() + rows, err := cnn.Query(s, args...) + if err != nil { + return nil, nil, err + } + cols := make(map[string]*Column) + colSeq := make([]string, 0) + for rows.Next() { + col := new(Column) + col.Indexes = make(map[string]bool) + + var columnName, isNullable, colType, colKey, extra string + var colDefault *string + err = rows.Scan(&columnName, &isNullable, &colDefault, &colType, &colKey, &extra) + if err != nil { + return nil, nil, err + } + col.Name = strings.Trim(columnName, "` ") + if "YES" == isNullable { + col.Nullable = true + } + + if colDefault != nil { + col.Default = *colDefault + } + + cts := strings.Split(colType, "(") + var len1, len2 int + if len(cts) == 2 { + idx := strings.Index(cts[1], ")") + lens := strings.Split(cts[1][0:idx], ",") + len1, err = strconv.Atoi(strings.TrimSpace(lens[0])) + if err != nil { + return nil, nil, err + } + if len(lens) == 2 { + len2, err = strconv.Atoi(lens[1]) + if err != nil { + return nil, nil, err + } + } + } + colName := cts[0] + colType = strings.ToUpper(colName) + col.Length = len1 + col.Length2 = len2 + if _, ok := SqlTypes[colType]; ok { + col.SQLType = SQLType{colType, len1, len2} + } else { + return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType)) + } + + if colKey == "PRI" { + col.IsPrimaryKey = true + } + if colKey == "UNI" { + //col.is + } + + if extra == "auto_increment" { + col.IsAutoIncrement = true + } + + if col.SQLType.IsText() { + if col.Default != "" { + col.Default = "'" + col.Default + "'" + } + } + cols[col.Name] = col + colSeq = append(colSeq, col.Name) + } + return colSeq, cols, nil +} + +func (db *mysql) GetTables() ([]*Table, error) { + args := []interface{}{db.DbName} + s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=?" + cnn, err := Open(db.DriverName(), db.DataSourceName()) + if err != nil { + return nil, err + } + defer cnn.Close() + rows, err := cnn.Query(s, args...) + if err != nil { + return nil, err + } + + tables := make([]*Table, 0) + for rows.Next() { + table := NewEmptyTable() + var name, engine, tableRows string + var autoIncr *string + err = rows.Scan(&name, &engine, &tableRows, &autoIncr) + if err != nil { + return nil, err + } + + table.Name = name + tables = append(tables, table) + } + return tables, nil +} + +func (db *mysql) GetIndexes(tableName string) (map[string]*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` = ?" + cnn, err := Open(db.DriverName(), db.DataSourceName()) + if err != nil { + return nil, err + } + defer cnn.Close() + rows, err := cnn.Query(s, args...) + if err != nil { + return nil, err + } + + indexes := make(map[string]*Index, 0) + for rows.Next() { + var indexType int + var indexName, colName, nonUnique string + err = rows.Scan(&indexName, &nonUnique, &colName) + if err != nil { + return nil, err + } + + if indexName == "PRIMARY" { + continue + } + + if "YES" == nonUnique || nonUnique == "1" { + indexType = IndexType + } else { + indexType = UniqueType + } + + colName = strings.Trim(colName, "` ") + + if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { + indexName = indexName[5+len(tableName) : len(indexName)] + } + + var index *Index + var ok bool + if index, ok = indexes[indexName]; !ok { + index = new(Index) + index.Type = indexType + index.Name = indexName + indexes[indexName] = index + } + index.AddColumn(colName) + } + return indexes, nil +} + +func (db *mysql) Filters() []Filter { + return []Filter{&IdFilter{}} +} diff --git a/oracle.go b/dialects/oracle.go similarity index 56% rename from oracle.go rename to dialects/oracle.go index 4e3c6fb6..c3f98f1a 100644 --- a/oracle.go +++ b/dialects/oracle.go @@ -1,45 +1,24 @@ -package xorm +package dialects import ( - "database/sql" "errors" "fmt" - "regexp" "strconv" "strings" + + . "github.com/lunny/xorm/core" ) +func init() { + RegisterDialect("oracle", &oracle{}) +} + type oracle struct { - base + Base } -type oracleParser struct { -} - -//dataSourceName=user/password@ipv4:port/dbname -//dataSourceName=user/password@[ipv6]:port/dbname -func (p *oracleParser) parse(driverName, dataSourceName string) (*uri, error) { - db := &uri{dbType: ORACLE_OCI} - 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 -} - -func (db *oracle) Init(drivername, uri string) error { - return db.base.init(&oracleParser{}, drivername, uri) +func (db *oracle) Init(uri *Uri, drivername, dataSourceName string) error { + return db.Base.Init(db, uri, drivername, dataSourceName) } func (db *oracle) SqlType(c *Column) string { @@ -119,55 +98,55 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, nil, err } + defer rows.Close() + cols := make(map[string]*Column) colSeq := make([]string, 0) - for _, record := range res { + for rows.Next() { col := new(Column) col.Indexes = make(map[string]bool) - for name, content := range record { - switch name { - case "column_name": - col.Name = strings.Trim(string(content), `" `) - case "data_default": - col.Default = string(content) - case "nullable": - if string(content) == "Y" { - col.Nullable = true - } else { - col.Nullable = false - } - case "data_type": - ct := string(content) - switch ct { - case "VARCHAR2": - col.SQLType = SQLType{Varchar, 0, 0} - case "TIMESTAMP WITH TIME ZONE": - col.SQLType = SQLType{TimeStamp, 0, 0} - default: - col.SQLType = SQLType{strings.ToUpper(ct), 0, 0} - } - if _, ok := sqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct)) - } - case "data_length": - i, err := strconv.Atoi(string(content)) - if err != nil { - return nil, nil, errors.New("retrieve length error") - } - col.Length = i - case "data_precision": - case "data_scale": - } + + var colName, colDefault, nullable, dataType, dataPrecision, dataScale string + var dataLen int + + err = rows.Scan(&colName, &colDefault, &dataType, &dataLen, &dataPrecision, + &dataScale, &nullable) + if err != nil { + return nil, nil, err } + + col.Name = strings.Trim(colName, `" `) + col.Default = colDefault + + if nullable == "Y" { + col.Nullable = true + } else { + col.Nullable = false + } + + switch dataType { + case "VARCHAR2": + col.SQLType = SQLType{Varchar, 0, 0} + case "TIMESTAMP WITH TIME ZONE": + col.SQLType = SQLType{TimeStampz, 0, 0} + default: + col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0} + } + if _, ok := SqlTypes[col.SQLType.Name]; !ok { + return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) + } + + col.Length = dataLen + if col.SQLType.IsText() { if col.Default != "" { col.Default = "'" + col.Default + "'" @@ -183,25 +162,24 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er func (db *oracle) GetTables() ([]*Table, error) { args := []interface{}{} s := "SELECT table_name FROM user_tables" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "table_name": - table.Name = string(content) - } + for rows.Next() { + table := NewEmptyTable() + err = rows.Scan(&table.Name) + if err != nil { + return nil, err } + tables = append(tables, table) } return tables, nil @@ -209,39 +187,36 @@ func (db *oracle) GetTables() ([]*Table, error) { func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { args := []interface{}{tableName} - s := "SELECT t.column_name,i.table_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + + 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" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } + defer rows.Close() indexes := make(map[string]*Index, 0) - for _, record := range res { + for rows.Next() { var indexType int - var indexName string - var colName string + var indexName, colName, uniqueness string - for name, content := range record { - switch name { - case "index_name": - indexName = strings.Trim(string(content), `" `) - case "uniqueness": - c := string(content) - if c == "UNIQUE" { - indexType = UniqueType - } else { - indexType = IndexType - } - case "column_name": - colName = string(content) - } + err = rows.Scan(&colName, &uniqueness, &indexName) + if err != nil { + return nil, err + } + + indexName = strings.Trim(indexName, `" `) + + if uniqueness == "UNIQUE" { + indexType = UniqueType + } else { + indexType = IndexType } var index *Index @@ -256,3 +231,20 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { } return indexes, nil } + +// PgSeqFilter filter SQL replace ?, ? ... to :1, :2 ... +type OracleSeqFilter struct { +} + +func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string { + counts := strings.Count(sql, "?") + for i := 1; i <= counts; i++ { + newstr := ":" + fmt.Sprintf("%v", i) + sql = strings.Replace(sql, "?", newstr, 1) + } + return sql +} + +func (db *oracle) Filters() []Filter { + return []Filter{&QuoteFilter{}, &OracleSeqFilter{}, &IdFilter{}} +} diff --git a/postgres.go b/dialects/postgres.go similarity index 54% rename from postgres.go rename to dialects/postgres.go index 9e12b009..dbf9ca09 100644 --- a/postgres.go +++ b/dialects/postgres.go @@ -1,65 +1,24 @@ -package xorm +package dialects import ( - "database/sql" "errors" "fmt" "strconv" "strings" + + . "github.com/lunny/xorm/core" ) +func init() { + RegisterDialect("postgres", &postgres{}) +} + type postgres struct { - base + Base } -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 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]) - } -} - -type postgresParser struct { -} - -func (p *postgresParser) parse(driverName, dataSourceName string) (*uri, error) { - db := &uri{dbType: POSTGRES} - o := make(values) - parseOpts(dataSourceName, o) - - db.dbName = o.Get("dbname") - if db.dbName == "" { - return nil, errors.New("dbname is empty") - } - return db, nil -} - -func (db *postgres) Init(drivername, uri string) error { - return db.base.init(&postgresParser{}, drivername, uri) +func (db *postgres) Init(uri *Uri, drivername, dataSourceName string) error { + return db.Base.Init(db, uri, drivername, dataSourceName) } func (db *postgres) SqlType(c *Column) string { @@ -153,68 +112,74 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, args := []interface{}{tableName} s := "SELECT column_name, column_default, is_nullable, data_type, character_maximum_length" + ", numeric_precision, numeric_precision_radix FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" - - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, nil, err } cols := make(map[string]*Column) colSeq := make([]string, 0) - for _, record := range res { + + for rows.Next() { col := new(Column) col.Indexes = make(map[string]bool) - for name, content := range record { - switch name { - case "column_name": - col.Name = strings.Trim(string(content), `" `) - case "column_default": - if strings.HasPrefix(string(content), "nextval") { - col.IsPrimaryKey = true - } else { - col.Default = string(content) - } - case "is_nullable": - if string(content) == "YES" { - col.Nullable = true - } else { - col.Nullable = false - } - case "data_type": - ct := string(content) - switch ct { - case "character varying", "character": - col.SQLType = SQLType{Varchar, 0, 0} - case "timestamp without time zone": - col.SQLType = SQLType{DateTime, 0, 0} - case "timestamp with time zone": - col.SQLType = SQLType{TimeStampz, 0, 0} - case "double precision": - col.SQLType = SQLType{Double, 0, 0} - case "boolean": - col.SQLType = SQLType{Bool, 0, 0} - case "time without time zone": - col.SQLType = SQLType{Time, 0, 0} - default: - col.SQLType = SQLType{strings.ToUpper(ct), 0, 0} - } - if _, ok := sqlTypes[col.SQLType.Name]; !ok { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct)) - } - case "character_maximum_length": - i, err := strconv.Atoi(string(content)) - if err != nil { - return nil, nil, errors.New("retrieve length error") - } - col.Length = i - case "numeric_precision": - case "numeric_precision_radix": + var colName, isNullable, dataType string + var maxLenStr, colDefault, numPrecision, numRadix *string + err = rows.Scan(&colName, &colDefault, &isNullable, &dataType, &maxLenStr, &numPrecision, &numRadix) + if err != nil { + return nil, nil, err + } + + var maxLen int + if maxLenStr != nil { + maxLen, err = strconv.Atoi(*maxLenStr) + if err != nil { + return nil, nil, err } } + + col.Name = strings.Trim(colName, `" `) + + if colDefault != nil { + if strings.HasPrefix(*colDefault, "nextval") { + col.IsPrimaryKey = true + } else { + col.Default = *colDefault + } + } + + if isNullable == "YES" { + col.Nullable = true + } else { + col.Nullable = false + } + + switch dataType { + case "character varying", "character": + col.SQLType = SQLType{Varchar, 0, 0} + case "timestamp without time zone": + col.SQLType = SQLType{DateTime, 0, 0} + case "timestamp with time zone": + col.SQLType = SQLType{TimeStampz, 0, 0} + case "double precision": + col.SQLType = SQLType{Double, 0, 0} + case "boolean": + col.SQLType = SQLType{Bool, 0, 0} + case "time without time zone": + col.SQLType = SQLType{Time, 0, 0} + default: + col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0} + } + if _, ok := SqlTypes[col.SQLType.Name]; !ok { + return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) + } + + col.Length = maxLen + if col.SQLType.IsText() { if col.Default != "" { col.Default = "'" + col.Default + "'" @@ -230,25 +195,25 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, func (db *postgres) GetTables() ([]*Table, error) { args := []interface{}{} s := "SELECT tablename FROM pg_tables where schemaname = 'public'" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "tablename": - table.Name = string(content) - } + for rows.Next() { + table := NewEmptyTable() + var name string + err = rows.Scan(&name) + if err != nil { + return nil, err } + table.Name = name tables = append(tables, table) } return tables, nil @@ -256,39 +221,37 @@ func (db *postgres) GetTables() ([]*Table, error) { func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { args := []interface{}{tableName} - s := "SELECT tablename, indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' and tablename = $1" + s := "SELECT indexname, indexdef FROM pg_indexes WHERE schemaname = 'public' and tablename = $1" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } indexes := make(map[string]*Index, 0) - for _, record := range res { + for rows.Next() { var indexType int - var indexName string + var indexName, indexdef string var colNames []string - - for name, content := range record { - switch name { - case "indexname": - indexName = strings.Trim(string(content), `" `) - case "indexdef": - c := string(content) - if strings.HasPrefix(c, "CREATE UNIQUE INDEX") { - indexType = UniqueType - } else { - indexType = IndexType - } - cs := strings.Split(c, "(") - colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") - } + err = rows.Scan(&indexName, &indexdef) + if err != nil { + return nil, err } + indexName = strings.Trim(indexName, `" `) + + if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") { + indexType = UniqueType + } else { + indexType = IndexType + } + cs := strings.Split(indexdef, "(") + colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") + if strings.HasSuffix(indexName, "_pkey") { continue } @@ -307,3 +270,24 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { } return indexes, nil } + +// PgSeqFilter filter SQL replace ?, ? ... to $1, $2 ... +type PgSeqFilter struct { +} + +func (s *PgSeqFilter) Do(sql string, dialect Dialect, table *Table) string { + segs := strings.Split(sql, "?") + size := len(segs) + res := "" + for i, c := range segs { + if i < size-1 { + res += c + fmt.Sprintf("$%v", i+1) + } + } + res += segs[size-1] + return res +} + +func (db *postgres) Filters() []Filter { + return []Filter{&IdFilter{}, &QuoteFilter{}, &PgSeqFilter{}} +} diff --git a/sqlite3.go b/dialects/sqlite3.go similarity index 80% rename from sqlite3.go rename to dialects/sqlite3.go index d52b2966..1a5d968d 100644 --- a/sqlite3.go +++ b/dialects/sqlite3.go @@ -1,23 +1,21 @@ -package xorm +package dialects import ( - "database/sql" "strings" + + . "github.com/lunny/xorm/core" ) +func init() { + RegisterDialect("sqlite3", &sqlite3{}) +} + type sqlite3 struct { - base + Base } -type sqlite3Parser struct { -} - -func (p *sqlite3Parser) parse(driverName, dataSourceName string) (*uri, error) { - return &uri{dbType: SQLITE, dbName: dataSourceName}, nil -} - -func (db *sqlite3) Init(drivername, dataSourceName string) error { - return db.base.init(&sqlite3Parser{}, drivername, dataSourceName) +func (db *sqlite3) Init(uri *Uri, drivername, dataSourceName string) error { + return db.Base.Init(db, uri, drivername, dataSourceName) } func (db *sqlite3) SqlType(c *Column) string { @@ -89,28 +87,29 @@ func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interfac func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, error) { args := []interface{}{tableName} s := "SELECT sql FROM sqlite_master WHERE type='table' and name = ?" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + + rows, err := cnn.Query(s, args...) if err != nil { return nil, nil, err } + defer rows.Close() - var sql string - for _, record := range res { - for name, content := range record { - if name == "sql" { - sql = string(content) - } + var name string + for rows.Next() { + err = rows.Scan(&name) + if err != nil { + return nil, nil, err } } - nStart := strings.Index(sql, "(") - nEnd := strings.Index(sql, ")") - colCreates := strings.Split(sql[nStart+1:nEnd], ",") + nStart := strings.Index(name, "(") + nEnd := strings.Index(name, ")") + colCreates := strings.Split(name[nStart+1:nEnd], ",") cols := make(map[string]*Column) colSeq := make([]string, 0) for _, colStr := range colCreates { @@ -148,24 +147,23 @@ func (db *sqlite3) GetTables() ([]*Table, error) { args := []interface{}{} s := "SELECT name FROM sqlite_master WHERE type='table'" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } + defer rows.Close() tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "name": - table.Name = string(content) - } + for rows.Next() { + table := NewEmptyTable() + err = rows.Scan(&table.Name) + if err != nil { + return nil, err } if table.Name == "sqlite_sequence" { continue @@ -178,25 +176,30 @@ func (db *sqlite3) GetTables() ([]*Table, error) { func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { args := []interface{}{tableName} s := "SELECT sql FROM sqlite_master WHERE type='index' and tbl_name = ?" - cnn, err := sql.Open(db.driverName, db.dataSourceName) + cnn, err := Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } defer cnn.Close() - res, err := query(cnn, s, args...) + rows, err := cnn.Query(s, args...) if err != nil { return nil, err } + defer rows.Close() indexes := make(map[string]*Index, 0) - for _, record := range res { - index := new(Index) - sql := string(record["sql"]) + for rows.Next() { + var sql string + err = rows.Scan(&sql) + if err != nil { + return nil, err + } if sql == "" { continue } + index := new(Index) nNStart := strings.Index(sql, "INDEX") nNEnd := strings.Index(sql, "ON") if nNStart == -1 || nNEnd == -1 { @@ -230,3 +233,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { return indexes, nil } + +func (db *sqlite3) Filters() []Filter { + return []Filter{&IdFilter{}} +} diff --git a/docs/AutoMap.md b/docs/AutoMap.md index f5b2aa57..cae66209 100644 --- a/docs/AutoMap.md +++ b/docs/AutoMap.md @@ -1,65 +1,65 @@ -When a struct auto mapping to a database's table, the below table describes how they change to each other: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +When a struct auto mapping to a database's table, the below table describes how they change to each other: + +
go type's kind - value methodxorm type -
implemented ConversionConversion.ToDB / Conversion.FromDBText
int, int8, int16, int32, uint, uint8, uint16, uint32 Int
int64, uint64BigInt
float32Float
float64Double
complex64, complex128json.Marshal / json.UnMarshalVarchar(64)
[]uint8Blob
array, slice, map except []uint8json.Marshal / json.UnMarshalText
bool1 or 0Bool
stringVarchar(255)
time.TimeDateTime
cascade structprimary key field valueBigInt
structjson.Marshal / json.UnMarshalText
- Others - - Text -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
go type's kind + value methodxorm type +
implemented ConversionConversion.ToDB / Conversion.FromDBText
int, int8, int16, int32, uint, uint8, uint16, uint32 Int
int64, uint64BigInt
float32Float
float64Double
complex64, complex128json.Marshal / json.UnMarshalVarchar(64)
[]uint8Blob
array, slice, map except []uint8json.Marshal / json.UnMarshalText
bool1 or 0Bool
stringVarchar(255)
time.TimeDateTime
cascade structprimary key field valueBigInt
structjson.Marshal / json.UnMarshalText
+ Others + + Text +
\ No newline at end of file diff --git a/docs/COLUMNTYPE.md b/docs/COLUMNTYPE.md index eb8f2c8a..d5a7e85a 100644 --- a/docs/COLUMNTYPE.md +++ b/docs/COLUMNTYPE.md @@ -1,438 +1,438 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
xorm - mysql - sqlite3 - postgres - remark
BIT - BIT - INTEGER - BIT -
TINYINT - TINYINT - INTEGER - SMALLINT -
SMALLINT - SMALLINT - INTEGER - SMALLINT -
MEDIUMINT - MEDIUMINT - INTEGER - INTEGER -
INT - INT - INTEGER - INTEGER -
INTEGER - INTEGER - INTEGER - INTEGER -
BIGINT - BIGINT - INTEGER - BIGINT -
CHAR - CHAR - TEXT - CHAR -
VARCHAR - VARCHAR - TEXT - VARCHAR -
TINYTEXT - TINYTEXT - TEXT - TEXT -
TEXT - TEXT - TEXT - TEXT -
MEDIUMTEXT - MEDIUMTEXT - TEXT - TEXT -
LONGTEXT - LONGTEXT - TEXT - TEXT -
BINARY - BINARY - BLOB - BYTEA -
VARBINARY - VARBINARY - BLOB - BYTEA -
DATE - DATE - NUMERIC - DATE -
DATETIME - DATETIME - NUMERIC - TIMESTAMP -
TIME - TIME - NUMERIC - TIME -
TIMESTAMP - TIMESTAMP - NUMERIC - TIMESTAMP -
TIMESTAMPZ - TEXT - TEXT - TIMESTAMP with zone - timestamp with zone info
REAL - REAL - REAL - REAL -
FLOAT - FLOAT - REAL - REAL -
DOUBLE - DOUBLE - REAL - DOUBLE PRECISION -
DECIMAL - DECIMAL - NUMERIC - DECIMAL -
NUMERIC - NUMERIC - NUMERIC - NUMERIC -
TINYBLOB - TINYBLOB - BLOB - BYTEA -
BLOB - BLOB - BLOB - BYTEA -
MEDIUMBLOB - MEDIUMBLOB - BLOB - BYTEA -
LONGBLOB - LONGBLOB - BLOB - BYTEA -
BYTEA - BLOB - BLOB - BYTEA -
BOOL - TINYINT - INTEGER - BOOLEAN -
SERIAL - INT - INTEGER - SERIAL - auto increment
BIGSERIAL - BIGINT - INTEGER - BIGSERIAL - auto increment
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
xorm + mysql + sqlite3 + postgres + remark
BIT + BIT + INTEGER + BIT +
TINYINT + TINYINT + INTEGER + SMALLINT +
SMALLINT + SMALLINT + INTEGER + SMALLINT +
MEDIUMINT + MEDIUMINT + INTEGER + INTEGER +
INT + INT + INTEGER + INTEGER +
INTEGER + INTEGER + INTEGER + INTEGER +
BIGINT + BIGINT + INTEGER + BIGINT +
CHAR + CHAR + TEXT + CHAR +
VARCHAR + VARCHAR + TEXT + VARCHAR +
TINYTEXT + TINYTEXT + TEXT + TEXT +
TEXT + TEXT + TEXT + TEXT +
MEDIUMTEXT + MEDIUMTEXT + TEXT + TEXT +
LONGTEXT + LONGTEXT + TEXT + TEXT +
BINARY + BINARY + BLOB + BYTEA +
VARBINARY + VARBINARY + BLOB + BYTEA +
DATE + DATE + NUMERIC + DATE +
DATETIME + DATETIME + NUMERIC + TIMESTAMP +
TIME + TIME + NUMERIC + TIME +
TIMESTAMP + TIMESTAMP + NUMERIC + TIMESTAMP +
TIMESTAMPZ + TEXT + TEXT + TIMESTAMP with zone + timestamp with zone info
REAL + REAL + REAL + REAL +
FLOAT + FLOAT + REAL + REAL +
DOUBLE + DOUBLE + REAL + DOUBLE PRECISION +
DECIMAL + DECIMAL + NUMERIC + DECIMAL +
NUMERIC + NUMERIC + NUMERIC + NUMERIC +
TINYBLOB + TINYBLOB + BLOB + BYTEA +
BLOB + BLOB + BLOB + BYTEA +
MEDIUMBLOB + MEDIUMBLOB + BLOB + BYTEA +
LONGBLOB + LONGBLOB + BLOB + BYTEA +
BYTEA + BLOB + BLOB + BYTEA +
BOOL + TINYINT + INTEGER + BOOLEAN +
SERIAL + INT + INTEGER + SERIAL + auto increment
BIGSERIAL + BIGINT + INTEGER + BIGSERIAL + auto increment
\ No newline at end of file diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index 79140be4..3786eba9 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -1,31 +1,31 @@ -## 更新日志 - -* **v0.3.1** - - 新特性: - * 支持 MSSQL DB 通过 ODBC 驱动 ([github.com/lunny/godbc](https://github.com/lunny/godbc)); - * 通过多个pk标记支持联合主键; - * 新增 Rows() API 用来遍历查询结果,该函数提供了类似sql.Rows的相似用法,可作为 Iterate() API 的可选替代; - * ORM 结构体现在允许内建类型的指针作为成员,使得数据库为null成为可能; - * Before 和 After 支持 - - 改进: - * 允许 int/int32/int64/uint/uint32/uint64/string 作为主键类型 - * 查询函数 Get()/Find()/Iterate() 在性能上的改进 - -* **v0.2.3** : 改善了文档;提供了乐观锁支持;添加了带时区时间字段支持;Mapper现在分成表名Mapper和字段名Mapper,同时实现了表或字段的自定义前缀后缀;Insert方法的返回值含义从id, err更改为 affected, err,请大家注意;添加了UseBool 和 Distinct函数。 -* **v0.2.2** : Postgres驱动新增了对lib/pq的支持;新增了逐条遍历方法Iterate;新增了SetMaxConns(go1.2+)支持,修复了bug若干; -* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); 修复了一些bug. -* **v0.2.0** : 新增 [缓存](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构; -* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。 -* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)。 -* **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现,可以选择不使用连接池,使用系统连接池和使用自带连接池三种实现,默认为SysConnectPool,即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法,在系统退出时应调用此方法。 -* **v0.1.6** : 新增Conversion,支持自定义类型到数据库类型的转换;新增查询结构体自动检测匿名成员支持;新增单向映射支持; -* **v0.1.5** : 新增对多线程的支持;新增Sql()函数;支持任意sql语句的struct查询;Get函数返回值变动;MakeSession和Create函数被NewSession和NewEngine函数替代; -* **v0.1.4** : Get函数和Find函数新增简单的级联载入功能;对更多的数据库类型支持。 -* **v0.1.3** : Find函数现在支持传入Slice或者Map,当传入Map时,key为id;新增Table函数以为多表和临时表进行支持。 -* **v0.1.2** : Insert函数支持混合struct和slice指针传入,并根据数据库类型自动批量插入,同时自动添加事务 -* **v0.1.1** : 添加 Id, In 函数,改善 README 文档 -* **v0.1.0** : 初始化工程 - - +## 更新日志 + +* **v0.3.1** + + 新特性: + * 支持 MSSQL DB 通过 ODBC 驱动 ([github.com/lunny/godbc](https://github.com/lunny/godbc)); + * 通过多个pk标记支持联合主键; + * 新增 Rows() API 用来遍历查询结果,该函数提供了类似sql.Rows的相似用法,可作为 Iterate() API 的可选替代; + * ORM 结构体现在允许内建类型的指针作为成员,使得数据库为null成为可能; + * Before 和 After 支持 + + 改进: + * 允许 int/int32/int64/uint/uint32/uint64/string 作为主键类型 + * 查询函数 Get()/Find()/Iterate() 在性能上的改进 + +* **v0.2.3** : 改善了文档;提供了乐观锁支持;添加了带时区时间字段支持;Mapper现在分成表名Mapper和字段名Mapper,同时实现了表或字段的自定义前缀后缀;Insert方法的返回值含义从id, err更改为 affected, err,请大家注意;添加了UseBool 和 Distinct函数。 +* **v0.2.2** : Postgres驱动新增了对lib/pq的支持;新增了逐条遍历方法Iterate;新增了SetMaxConns(go1.2+)支持,修复了bug若干; +* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); 修复了一些bug. +* **v0.2.0** : 新增 [缓存](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构; +* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。 +* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)。 +* **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现,可以选择不使用连接池,使用系统连接池和使用自带连接池三种实现,默认为SysConnectPool,即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法,在系统退出时应调用此方法。 +* **v0.1.6** : 新增Conversion,支持自定义类型到数据库类型的转换;新增查询结构体自动检测匿名成员支持;新增单向映射支持; +* **v0.1.5** : 新增对多线程的支持;新增Sql()函数;支持任意sql语句的struct查询;Get函数返回值变动;MakeSession和Create函数被NewSession和NewEngine函数替代; +* **v0.1.4** : Get函数和Find函数新增简单的级联载入功能;对更多的数据库类型支持。 +* **v0.1.3** : Find函数现在支持传入Slice或者Map,当传入Map时,key为id;新增Table函数以为多表和临时表进行支持。 +* **v0.1.2** : Insert函数支持混合struct和slice指针传入,并根据数据库类型自动批量插入,同时自动添加事务 +* **v0.1.1** : 添加 Id, In 函数,改善 README 文档 +* **v0.1.0** : 初始化工程 + + diff --git a/docs/cache_design.graffle b/docs/cache_design.graffle index 5b7c487b..bfd31dc9 100644 --- a/docs/cache_design.graffle +++ b/docs/cache_design.graffle @@ -1,2295 +1,2295 @@ - - - - - ActiveLayerIndex - 0 - ApplicationVersion - - com.omnigroup.OmniGrafflePro - 139.16.0.171715 - - AutoAdjust - - BackgroundGraphic - - Bounds - {{0, 0}, {771, 554.18930041152259}} - Class - SolidGraphic - ID - 2 - Style - - fill - - Color - - b - 0.989303 - g - 0.907286 - r - 0.795377 - - FillType - 2 - GradientAngle - 78 - GradientColor - - b - 1 - g - 0.854588 - r - 0.623912 - - MiddleColor - - b - 1 - g - 0.856844 - r - 0.43695 - - TrippleBlend - YES - - shadow - - Draws - NO - - stroke - - Draws - NO - - - - BaseZoom - 0 - CanvasOrigin - {0, 0} - CanvasSize - {771, 554.18930041152259} - ColumnAlign - 1 - ColumnSpacing - 36 - CreationDate - 2013-09-29 07:57:57 +0000 - Creator - Lunny Xiao - DisplayScale - 1.000 cm = 1.000 cm - FileType - flat - GraphDocumentVersion - 8 - GraphicsList - - - Bounds - {{409.89504441572683, 415.64570506990464}, {104.42639923095703, 79.447883605957031}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 30 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 .\ -.\ -.\ -} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{276.44083898205287, 413.07252538018992}, {112.36092376708984, 79.447883605957031}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 29 - Shape - Rectangle - Style - - fill - - Color - - b - 0.776486 - g - 0.588495 - r - 0.670497 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.618021 - g - 0.412924 - r - 0.50312 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 .\ -.\ -.} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{409.89504441572689, 337.17180246145824}, {104.42639923095703, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 28 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 user-2:User\{\}} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{274.32251833907753, 322.94787397618234}, {112.36092376708984, 79.447883605957031}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 27 - Shape - Rectangle - Style - - fill - - Color - - b - 0.776486 - g - 0.588495 - r - 0.670497 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.618021 - g - 0.412924 - r - 0.50312 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 select id from tb3:[2,5]} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{406.08888702072045, 256.42244420026987}, {104.42639923095703, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 25 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 user-2:User\{\}} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{406.08888302813585, 187.47137690331695}, {104.42639923095703, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 24 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 table-1:Table\{\}} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{406.08887903555114, 118.52029512620169}, {104.42639923095703, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 23 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 user-1:User\{\}} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{556.54055354053583, 325.93280718390133}, {124.41892177446698, 51}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 22 - Shape - Rectangle - Style - - fill - - Color - - b - 0.793851 - g - 0.625208 - r - 0.562982 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.639673 - g - 0.450584 - r - 0.381079 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.511421 - g - 0.637255 - r - 0.120867 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 Del(k, v)} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{556.54055354053583, 240.81081643580876}, {124.41892177446698, 51}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 21 - Shape - Rectangle - Style - - fill - - Color - - b - 0.793851 - g - 0.625208 - r - 0.562982 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.639673 - g - 0.450584 - r - 0.381079 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.511421 - g - 0.637255 - r - 0.120867 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 Put(k, v)} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{556.54054526129187, 150.52220626433376}, {124.41893005371094, 51}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 20 - Shape - Rectangle - Style - - fill - - Color - - b - 0.793851 - g - 0.625208 - r - 0.562982 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.639673 - g - 0.450584 - r - 0.381079 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.511421 - g - 0.637255 - r - 0.120867 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 Get(k, v)} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{276.44083898205287, 214.72973288487913}, {112.36092376708984, 79.447883605957031}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 19 - Shape - Rectangle - Style - - fill - - Color - - b - 0.776486 - g - 0.588495 - r - 0.670497 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.618021 - g - 0.412924 - r - 0.50312 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 select id from tb2:[2,5]} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{274.32251963877655, 117.18245433711769}, {112.36092376708984, 68.777008056640625}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 18 - Shape - Rectangle - Style - - fill - - Color - - b - 0.776486 - g - 0.588495 - r - 0.670497 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.618021 - g - 0.412924 - r - 0.50312 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 select id from tb1:[1,2,3]} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{103.00000194954862, 30.108108889738791}, {80, 51}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 14 - Shape - Rectangle - Style - - fill - - Color - - b - 0.776486 - g - 0.588495 - r - 0.670497 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.618021 - g - 0.412924 - r - 0.50312 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 SQL} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{532.04631600645348, 25.096524339927051}, {166.93052673339844, 79.447883605957031}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - Size - 18 - - ID - 13 - Shape - Rectangle - Style - - fill - - Color - - b - 0.793851 - g - 0.625208 - r - 0.562982 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.639673 - g - 0.450584 - r - 0.381079 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.511421 - g - 0.637255 - r - 0.120867 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;\f1\fnil\fcharset134 STHeitiSC-Light;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf0 Cache\ - -\f1 Store} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{298.9845516069733, 37.412179328792348}, {173.03089904785156, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.8 - g - 0.8 - r - 0.8 - - Font - Verdana - Size - 18 - - ID - 17 - Shape - Rectangle - Style - - fill - - Color - - b - 0.6 - g - 0.6 - r - 0.6 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.4 - g - 0.4 - r - 0.4 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.590997 - g - 0.18677 - r - 0.567819 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 LRUCacher} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{99.822519035537013, 333.12161552787364}, {88, 51}} - Class - ShapedGraphic - FontInfo - - Color - - archive - - YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy - WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O - U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 - gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO - U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO - U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE - 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl - c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob - HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o - fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB - AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl - - b - 0 - g - 0 - r - 0 - - Font - Verdana - NSKern - 0.0 - Size - 15 - - ID - 12 - Magnets - - {1, 0} - {-1, 0} - - Shape - Rectangle - Style - - fill - - Color - - b - 0.806569 - g - 0.806569 - r - 0.806569 - - FillType - 2 - GradientAngle - 90 - GradientColor - - w - 0.653285 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.2 - g - 0.2 - r - 0.2 - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\fs30 \cf0 \expnd0\expndtw0\kerning0 -Delet\ -SQL} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{97.118322659032714, 226.09466078541047}, {88, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0 - g - 0 - r - 0.501961 - - Font - Verdana - NSKern - 0.0 - Size - 15 - - ID - 15 - Magnets - - {1, 0} - {-1, 0} - - Shape - Rectangle - Style - - fill - - Color - - b - 0 - g - 0.389485 - r - 1 - - FillType - 3 - GradientCenter - {-0.34285700000000002, -0.114286} - GradientColor - - b - 0 - g - 0.495748 - r - 1 - - MiddleColor - - b - 0 - g - 0.887657 - r - 1 - - MiddleFraction - 0.6269841194152832 - TrippleBlend - YES - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.2 - g - 0.2 - r - 0.2 - - Draws - NO - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red128\green0\blue0;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc - -\f0\fs30 \cf2 \expnd0\expndtw0\kerning0 -Update\ -SQL} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - Bounds - {{103, 123.08108395608006}, {80, 51}} - Class - ShapedGraphic - FontInfo - - Color - - b - 0.821332 - g - 0.672602 - r - 0.928374 - - Font - Verdana - Size - 18 - - ID - 16 - Shape - Rectangle - Style - - fill - - Color - - b - 0.436973 - g - 0.155566 - r - 0.758999 - - FillType - 2 - GradientAngle - 90 - GradientColor - - b - 0.25098 - g - 0 - r - 0.501961 - - - shadow - - Beneath - YES - Color - - a - 0.15 - b - 0 - g - 0 - r - 0 - - Fuzziness - 0.0 - ShadowVector - {2, 2} - - stroke - - Color - - b - 0.511421 - g - 0.637255 - r - 0.120867 - - Draws - NO - Width - 2 - - - Text - - Text - {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 -\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;\f1\fnil\fcharset0 Verdana;} -{\colortbl;\red255\green255\blue255;\red237\green172\blue209;} -\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc - -\f0\fs36 \cf2 select -\f1 -\f0 SQL} - VerticalPad - 0 - - TextRelativeArea - {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} - - - GridInfo - - GuidesLocked - NO - GuidesVisible - YES - HPages - 2 - ImageCounter - 3 - KeepToScale - - Layers - - - Lock - NO - Name - 图层 1 - Print - YES - View - YES - - - LayoutInfo - - Animate - NO - AutoLayout - 2 - circoMinDist - 18 - circoSeparation - 0.0 - layoutEngine - neato - neatoLineLength - 0.92083334922790527 - neatoSeparation - 0.0 - twopiSeparation - 0.0 - - LinksVisible - NO - MagnetsVisible - NO - MasterSheets - - ModificationDate - 2013-09-29 08:24:57 +0000 - Modifier - Lunny Xiao - NotesVisible - NO - Orientation - 2 - OriginVisible - NO - OutlineStyle - Brainstorming/Clouds - PageBreaks - NO - PrintInfo - - NSBottomMargin - - float - 41 - - NSHorizonalPagination - - coded - BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG - - NSLeftMargin - - float - 18 - - NSPaperSize - - size - {595, 842} - - NSPrintReverseOrientation - - int - 0 - - NSRightMargin - - float - 18 - - NSTopMargin - - float - 18 - - - PrintOnePage - - ReadOnly - NO - RowAlign - 1 - RowSpacing - 36 - SheetTitle - 版面 1 - SmartAlignmentGuidesActive - YES - SmartDistanceGuidesActive - YES - UniqueID - 1 - UseEntirePage - - VPages - 1 - WindowInfo - - CurrentSheet - 0 - ExpandedCanvases - - FitInWindow - - Frame - {{138, 197}, {869, 617}} - ListView - - OutlineWidth - 142 - RightSidebar - - Sidebar - - SidebarWidth - 138 - VisibleRegion - {{1.0591603214876664, 1.0591603214876664}, {770.00955372153339, 553.94084813804955}} - Zoom - 0.94414412975311279 - ZoomValues - - - 版面 1 - 0.0 - 1 - - - - - + + + + + ActiveLayerIndex + 0 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 139.16.0.171715 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {771, 554.18930041152259}} + Class + SolidGraphic + ID + 2 + Style + + fill + + Color + + b + 0.989303 + g + 0.907286 + r + 0.795377 + + FillType + 2 + GradientAngle + 78 + GradientColor + + b + 1 + g + 0.854588 + r + 0.623912 + + MiddleColor + + b + 1 + g + 0.856844 + r + 0.43695 + + TrippleBlend + YES + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + BaseZoom + 0 + CanvasOrigin + {0, 0} + CanvasSize + {771, 554.18930041152259} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2013-09-29 07:57:57 +0000 + Creator + Lunny Xiao + DisplayScale + 1.000 cm = 1.000 cm + FileType + flat + GraphDocumentVersion + 8 + GraphicsList + + + Bounds + {{409.89504441572683, 415.64570506990464}, {104.42639923095703, 79.447883605957031}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 30 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 .\ +.\ +.\ +} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{276.44083898205287, 413.07252538018992}, {112.36092376708984, 79.447883605957031}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 29 + Shape + Rectangle + Style + + fill + + Color + + b + 0.776486 + g + 0.588495 + r + 0.670497 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.618021 + g + 0.412924 + r + 0.50312 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 .\ +.\ +.} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{409.89504441572689, 337.17180246145824}, {104.42639923095703, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 28 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 user-2:User\{\}} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{274.32251833907753, 322.94787397618234}, {112.36092376708984, 79.447883605957031}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 27 + Shape + Rectangle + Style + + fill + + Color + + b + 0.776486 + g + 0.588495 + r + 0.670497 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.618021 + g + 0.412924 + r + 0.50312 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 select id from tb3:[2,5]} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{406.08888702072045, 256.42244420026987}, {104.42639923095703, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 25 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 user-2:User\{\}} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{406.08888302813585, 187.47137690331695}, {104.42639923095703, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 24 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 table-1:Table\{\}} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{406.08887903555114, 118.52029512620169}, {104.42639923095703, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 23 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 user-1:User\{\}} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{556.54055354053583, 325.93280718390133}, {124.41892177446698, 51}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 22 + Shape + Rectangle + Style + + fill + + Color + + b + 0.793851 + g + 0.625208 + r + 0.562982 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.639673 + g + 0.450584 + r + 0.381079 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.511421 + g + 0.637255 + r + 0.120867 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Del(k, v)} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{556.54055354053583, 240.81081643580876}, {124.41892177446698, 51}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 21 + Shape + Rectangle + Style + + fill + + Color + + b + 0.793851 + g + 0.625208 + r + 0.562982 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.639673 + g + 0.450584 + r + 0.381079 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.511421 + g + 0.637255 + r + 0.120867 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Put(k, v)} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{556.54054526129187, 150.52220626433376}, {124.41893005371094, 51}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 20 + Shape + Rectangle + Style + + fill + + Color + + b + 0.793851 + g + 0.625208 + r + 0.562982 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.639673 + g + 0.450584 + r + 0.381079 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.511421 + g + 0.637255 + r + 0.120867 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Get(k, v)} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{276.44083898205287, 214.72973288487913}, {112.36092376708984, 79.447883605957031}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 19 + Shape + Rectangle + Style + + fill + + Color + + b + 0.776486 + g + 0.588495 + r + 0.670497 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.618021 + g + 0.412924 + r + 0.50312 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 select id from tb2:[2,5]} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{274.32251963877655, 117.18245433711769}, {112.36092376708984, 68.777008056640625}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 18 + Shape + Rectangle + Style + + fill + + Color + + b + 0.776486 + g + 0.588495 + r + 0.670497 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.618021 + g + 0.412924 + r + 0.50312 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 select id from tb1:[1,2,3]} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{103.00000194954862, 30.108108889738791}, {80, 51}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 14 + Shape + Rectangle + Style + + fill + + Color + + b + 0.776486 + g + 0.588495 + r + 0.670497 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.618021 + g + 0.412924 + r + 0.50312 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 SQL} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{532.04631600645348, 25.096524339927051}, {166.93052673339844, 79.447883605957031}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + Size + 18 + + ID + 13 + Shape + Rectangle + Style + + fill + + Color + + b + 0.793851 + g + 0.625208 + r + 0.562982 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.639673 + g + 0.450584 + r + 0.381079 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.511421 + g + 0.637255 + r + 0.120867 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;\f1\fnil\fcharset134 STHeitiSC-Light;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf0 Cache\ + +\f1 Store} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{298.9845516069733, 37.412179328792348}, {173.03089904785156, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.8 + g + 0.8 + r + 0.8 + + Font + Verdana + Size + 18 + + ID + 17 + Shape + Rectangle + Style + + fill + + Color + + b + 0.6 + g + 0.6 + r + 0.6 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.4 + g + 0.4 + r + 0.4 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.590997 + g + 0.18677 + r + 0.567819 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red204\green204\blue204;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 LRUCacher} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{99.822519035537013, 333.12161552787364}, {88, 51}} + Class + ShapedGraphic + FontInfo + + Color + + archive + + YnBsaXN0MDDUAQIDBAUGBwpZJGFyY2hpdmVy + WCR2ZXJzaW9uVCR0b3BYJG9iamVjdHNfEA9O + U0tleWVkQXJjaGl2ZXISAAGGoNEICVRyb290 + gAGlCwwVGR5VJG51bGzUDQ4PEBESExRfEBJO + U0N1c3RvbUNvbG9yU3BhY2VXTlNXaGl0ZVxO + U0NvbG9yU3BhY2VWJGNsYXNzgAJCMAAQA4AE + 0hYQFxhUTlNJRBACgAPSGhscD1gkY2xhc3Nl + c1okY2xhc3NuYW1log8dWE5TT2JqZWN00hob + HyCiIB1XTlNDb2xvcggRGyQpMkRJTFFTWV9o + fYWSmZueoKKnrK6wtb7JzNXa3QAAAAAAAAEB + AAAAAAAAACEAAAAAAAAAAAAAAAAAAADl + + b + 0 + g + 0 + r + 0 + + Font + Verdana + NSKern + 0.0 + Size + 15 + + ID + 12 + Magnets + + {1, 0} + {-1, 0} + + Shape + Rectangle + Style + + fill + + Color + + b + 0.806569 + g + 0.806569 + r + 0.806569 + + FillType + 2 + GradientAngle + 90 + GradientColor + + w + 0.653285 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.2 + g + 0.2 + r + 0.2 + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs30 \cf0 \expnd0\expndtw0\kerning0 +Delet\ +SQL} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{97.118322659032714, 226.09466078541047}, {88, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0 + r + 0.501961 + + Font + Verdana + NSKern + 0.0 + Size + 15 + + ID + 15 + Magnets + + {1, 0} + {-1, 0} + + Shape + Rectangle + Style + + fill + + Color + + b + 0 + g + 0.389485 + r + 1 + + FillType + 3 + GradientCenter + {-0.34285700000000002, -0.114286} + GradientColor + + b + 0 + g + 0.495748 + r + 1 + + MiddleColor + + b + 0 + g + 0.887657 + r + 1 + + MiddleFraction + 0.6269841194152832 + TrippleBlend + YES + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.2 + g + 0.2 + r + 0.2 + + Draws + NO + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red128\green0\blue0;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc + +\f0\fs30 \cf2 \expnd0\expndtw0\kerning0 +Update\ +SQL} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + Bounds + {{103, 123.08108395608006}, {80, 51}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0.821332 + g + 0.672602 + r + 0.928374 + + Font + Verdana + Size + 18 + + ID + 16 + Shape + Rectangle + Style + + fill + + Color + + b + 0.436973 + g + 0.155566 + r + 0.758999 + + FillType + 2 + GradientAngle + 90 + GradientColor + + b + 0.25098 + g + 0 + r + 0.501961 + + + shadow + + Beneath + YES + Color + + a + 0.15 + b + 0 + g + 0 + r + 0 + + Fuzziness + 0.0 + ShadowVector + {2, 2} + + stroke + + Color + + b + 0.511421 + g + 0.637255 + r + 0.120867 + + Draws + NO + Width + 2 + + + Text + + Text + {\rtf1\ansi\ansicpg936\cocoartf1187\cocoasubrtf390 +\cocoascreenfonts1{\fonttbl\f0\fnil\fcharset134 STHeitiSC-Light;\f1\fnil\fcharset0 Verdana;} +{\colortbl;\red255\green255\blue255;\red237\green172\blue209;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural\qc + +\f0\fs36 \cf2 select +\f1 +\f0 SQL} + VerticalPad + 0 + + TextRelativeArea + {{0.10000000000000001, 0.14999999999999999}, {0.80000000000000004, 0.69999999999999996}} + + + GridInfo + + GuidesLocked + NO + GuidesVisible + YES + HPages + 2 + ImageCounter + 3 + KeepToScale + + Layers + + + Lock + NO + Name + 图层 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + AutoLayout + 2 + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + neato + neatoLineLength + 0.92083334922790527 + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2013-09-29 08:24:57 +0000 + Modifier + Lunny Xiao + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + OutlineStyle + Brainstorming/Clouds + PageBreaks + NO + PrintInfo + + NSBottomMargin + + float + 41 + + NSHorizonalPagination + + coded + BAtzdHJlYW10eXBlZIHoA4QBQISEhAhOU051bWJlcgCEhAdOU1ZhbHVlAISECE5TT2JqZWN0AIWEASqEhAFxlwCG + + NSLeftMargin + + float + 18 + + NSPaperSize + + size + {595, 842} + + NSPrintReverseOrientation + + int + 0 + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + 版面 1 + SmartAlignmentGuidesActive + YES + SmartDistanceGuidesActive + YES + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + FitInWindow + + Frame + {{138, 197}, {869, 617}} + ListView + + OutlineWidth + 142 + RightSidebar + + Sidebar + + SidebarWidth + 138 + VisibleRegion + {{1.0591603214876664, 1.0591603214876664}, {770.00955372153339, 553.94084813804955}} + Zoom + 0.94414412975311279 + ZoomValues + + + 版面 1 + 0.0 + 1 + + + + + diff --git a/drivers/goracle.go b/drivers/goracle.go new file mode 100644 index 00000000..ce93793d --- /dev/null +++ b/drivers/goracle.go @@ -0,0 +1,38 @@ +package drivers + +import ( + "errors" + "regexp" + + "github.com/lunny/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/mymysql.go b/drivers/mymysql.go similarity index 67% rename from mymysql.go rename to drivers/mymysql.go index 8101d4c0..bd8a058f 100644 --- a/mymysql.go +++ b/drivers/mymysql.go @@ -1,20 +1,22 @@ -package xorm +package drivers import ( "errors" "strings" "time" + + "github.com/lunny/xorm/core" ) -type mymysql struct { - mysql +func init() { + core.RegisterDriver("mymysql", &mymysqlDriver{}) } -type mymysqlParser struct { +type mymysqlDriver struct { } -func (p *mymysqlParser) parse(driverName, dataSourceName string) (*uri, error) { - db := &uri{dbType: MYSQL} +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 { @@ -23,9 +25,9 @@ func (p *mymysqlParser) parse(driverName, dataSourceName string) (*uri, error) { if len(p) != 2 { return nil, errors.New("Wrong protocol part of URI") } - db.proto = p[0] + db.Proto = p[0] options := strings.Split(p[1], ",") - db.raddr = options[0] + db.Raddr = options[0] for _, o := range options[1:] { kv := strings.SplitN(o, "=", 2) var k, v string @@ -36,13 +38,13 @@ func (p *mymysqlParser) parse(driverName, dataSourceName string) (*uri, error) { } switch k { case "laddr": - db.laddr = v + db.Laddr = v case "timeout": to, err := time.ParseDuration(v) if err != nil { return nil, err } - db.timeout = to + db.Timeout = to default: return nil, errors.New("Unknown option: " + k) } @@ -55,13 +57,9 @@ func (p *mymysqlParser) parse(driverName, dataSourceName string) (*uri, error) { 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] + db.DbName = dup[0] + db.User = dup[1] + db.Passwd = dup[2] return db, nil } - -func (db *mymysql) Init(drivername, uri string) error { - return db.mysql.base.init(&mymysqlParser{}, drivername, uri) -} diff --git a/drivers/mysql.go b/drivers/mysql.go new file mode 100644 index 00000000..a2fe0e2f --- /dev/null +++ b/drivers/mysql.go @@ -0,0 +1,50 @@ +package drivers + +import ( + "regexp" + "strings" + + "github.com/lunny/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/drivers/oci.go b/drivers/oci.go new file mode 100644 index 00000000..04f8cde6 --- /dev/null +++ b/drivers/oci.go @@ -0,0 +1,37 @@ +package drivers + +import ( + "errors" + "regexp" + + "github.com/lunny/xorm/core" +) + +func init() { + core.RegisterDriver("oci", &ociDriver{}) +} + +type ociDriver struct { +} + +//dataSourceName=user/password@ipv4:port/dbname +//dataSourceName=user/password@[ipv6]:port/dbname +func (p *ociDriver) 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/drivers/odbc.go b/drivers/odbc.go new file mode 100644 index 00000000..a83b71fb --- /dev/null +++ b/drivers/odbc.go @@ -0,0 +1,34 @@ +package drivers + +import ( + "errors" + "strings" + + "github.com/lunny/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/drivers/pq.go b/drivers/pq.go new file mode 100644 index 00000000..5e564fb7 --- /dev/null +++ b/drivers/pq.go @@ -0,0 +1,59 @@ +package drivers + +import ( + "errors" + "fmt" + "strings" + + "github.com/lunny/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 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) + parseOpts(dataSourceName, o) + + db.DbName = o.Get("dbname") + if db.DbName == "" { + return nil, errors.New("dbname is empty") + } + return db, nil +} diff --git a/drivers/sqlite3.go b/drivers/sqlite3.go new file mode 100644 index 00000000..587c91c7 --- /dev/null +++ b/drivers/sqlite3.go @@ -0,0 +1,16 @@ +package drivers + +import ( + "github.com/lunny/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/engine.go b/engine.go index b5fd317f..cc68d75d 100644 --- a/engine.go +++ b/engine.go @@ -12,9 +12,11 @@ import ( "strconv" "strings" "sync" + + "github.com/lunny/xorm/core" ) -const ( +/*const ( POSTGRES = "postgres" SQLITE = "sqlite3" MYSQL = "mysql" @@ -23,28 +25,7 @@ const ( MSSQL = "mssql" ORACLE_OCI = "oci8" -) - -// a dialect is a driver's wrapper -type dialect interface { - Init(DriverName, DataSourceName string) error - URI() *uri - DBType() string - SqlType(t *Column) string - SupportInsertMany() bool - QuoteStr() string - AutoIncrStr() string - SupportEngine() bool - SupportCharset() bool - IndexOnTable() bool - IndexCheckSql(tableName, idxName string) (string, []interface{}) - TableCheckSql(tableName string) (string, []interface{}) - ColumnCheckSql(tableName, colName string) (string, []interface{}) - - GetColumns(tableName string) ([]string, map[string]*Column, error) - GetTables() ([]*Table, error) - GetIndexes(tableName string) (map[string]*Index, error) -} +)*/ type PK []interface{} @@ -56,18 +37,19 @@ type Engine struct { TagIdentifier string DriverName string DataSourceName string - dialect dialect - Tables map[reflect.Type]*Table - mutex *sync.Mutex - ShowSQL bool - ShowErr bool - ShowDebug bool - ShowWarn bool - Pool IConnectPool - Filters []Filter - Logger io.Writer - Cacher Cacher - UseCache bool + dialect core.Dialect + Tables map[reflect.Type]*core.Table + + mutex *sync.Mutex + ShowSQL bool + ShowErr bool + ShowDebug bool + ShowWarn bool + Pool IConnectPool + Filters []core.Filter + Logger io.Writer + Cacher Cacher + tableCachers map[reflect.Type]Cacher } func (engine *Engine) SetMapper(mapper IMapper) { @@ -102,8 +84,8 @@ func (engine *Engine) Quote(sql string) string { return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr() } -// A simple wrapper to dialect's SqlType method -func (engine *Engine) SqlType(c *Column) string { +// A simple wrapper to dialect's core.SqlType method +func (engine *Engine) SqlType(c *core.Column) string { return engine.dialect.SqlType(c) } @@ -130,12 +112,7 @@ func (engine *Engine) SetMaxIdleConns(conns int) { // SetDefaltCacher set the default cacher. Xorm's default not enable cacher. func (engine *Engine) SetDefaultCacher(cacher Cacher) { - if cacher == nil { - engine.UseCache = false - } else { - engine.UseCache = true - engine.Cacher = cacher - } + engine.Cacher = cacher } // If you has set default cacher, and you want temporilly stop use cache, @@ -156,7 +133,7 @@ func (engine *Engine) NoCascade() *Session { func (engine *Engine) MapCacher(bean interface{}, cacher Cacher) { t := rType(bean) engine.autoMapType(t) - engine.Tables[t].Cacher = cacher + engine.tableCachers[t] = cacher } // OpenDB provides a interface to operate database directly. @@ -235,7 +212,7 @@ func (engine *Engine) NoAutoTime() *Session { } // Retrieve all tables, columns, indexes' informations from database. -func (engine *Engine) DBMetas() ([]*Table, error) { +func (engine *Engine) DBMetas() ([]*core.Table, error) { tables, err := engine.dialect.GetTables() if err != nil { return nil, err @@ -246,8 +223,11 @@ func (engine *Engine) DBMetas() ([]*Table, error) { if err != nil { return nil, err } - table.Columns = cols - table.ColumnsSeq = colSeq + for _, name := range colSeq { + table.AddColumn(cols[name]) + } + //table.Columns = cols + //table.ColumnsSeq = colSeq indexes, err := engine.dialect.GetIndexes(table.Name) if err != nil { @@ -257,10 +237,10 @@ func (engine *Engine) DBMetas() ([]*Table, error) { for _, index := range indexes { for _, name := range index.Cols { - if col, ok := table.Columns[name]; ok { + if col := table.GetColumn(name); col != nil { col.Indexes[index.Name] = true } else { - return nil, fmt.Errorf("Unknown col "+name+" in indexes %v", table.Columns) + return nil, fmt.Errorf("Unknown col "+name+" in indexes %v", index) } } } @@ -420,7 +400,7 @@ func (engine *Engine) Having(conditions string) *Session { return session.Having(conditions) } -func (engine *Engine) autoMapType(t reflect.Type) *Table { +func (engine *Engine) autoMapType(t reflect.Type) *core.Table { engine.mutex.Lock() defer engine.mutex.Unlock() table, ok := engine.Tables[t] @@ -431,36 +411,30 @@ func (engine *Engine) autoMapType(t reflect.Type) *Table { return table } -func (engine *Engine) autoMap(bean interface{}) *Table { +func (engine *Engine) autoMap(bean interface{}) *core.Table { t := rType(bean) return engine.autoMapType(t) } -func (engine *Engine) newTable() *Table { - table := &Table{} - table.Indexes = make(map[string]*Index) - table.Columns = make(map[string]*Column) - table.ColumnsSeq = make([]string, 0) - table.Created = make(map[string]bool) - table.Cacher = engine.Cacher - return table +func (engine *Engine) mapType(t reflect.Type) *core.Table { + return mappingTable(t, engine.tableMapper, engine.columnMapper, engine.dialect, engine.TagIdentifier) } -func (engine *Engine) mapType(t reflect.Type) *Table { - table := engine.newTable() - table.Name = engine.tableMapper.Obj2Table(t.Name()) +func mappingTable(t reflect.Type, tableMapper IMapper, colMapper IMapper, dialect core.Dialect, tagId string) *core.Table { + table := core.NewEmptyTable() + table.Name = tableMapper.Obj2Table(t.Name()) table.Type = t var idFieldColName string for i := 0; i < t.NumField(); i++ { tag := t.Field(i).Tag - ormTagStr := tag.Get(engine.TagIdentifier) - var col *Column + ormTagStr := tag.Get(tagId) + var col *core.Column fieldType := t.Field(i).Type if ormTagStr != "" { - col = &Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, + col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, IsAutoIncrement: false, MapType: TWOSIDES, Indexes: make(map[string]bool)} tags := strings.Split(ormTagStr, " ") @@ -470,16 +444,16 @@ func (engine *Engine) mapType(t reflect.Type) *Table { } if (strings.ToUpper(tags[0]) == "EXTENDS") && (fieldType.Kind() == reflect.Struct) { - parentTable := engine.mapType(fieldType) - for name, col := range parentTable.Columns { + parentTable := mappingTable(fieldType, tableMapper, colMapper, dialect, tagId) + for _, col := range parentTable.Columns() { col.FieldName = fmt.Sprintf("%v.%v", fieldType.Name(), col.FieldName) - table.Columns[strings.ToLower(name)] = col - table.ColumnsSeq = append(table.ColumnsSeq, name) + table.AddColumn(col) } - table.PrimaryKeys = parentTable.PrimaryKeys + //table.PrimaryKeys = parentTable.PrimaryKeys continue } + var indexType int var indexName string for j, key := range tags { @@ -506,15 +480,15 @@ func (engine *Engine) mapType(t reflect.Type) *Table { case k == "UPDATED": col.IsUpdated = true case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): - indexType = IndexType + indexType = core.IndexType indexName = k[len("INDEX")+1 : len(k)-1] case k == "INDEX": - indexType = IndexType + indexType = core.IndexType case strings.HasPrefix(k, "UNIQUE(") && strings.HasSuffix(k, ")"): indexName = k[len("UNIQUE")+1 : len(k)-1] - indexType = UniqueType + indexType = core.UniqueType case k == "UNIQUE": - indexType = UniqueType + indexType = core.UniqueType case k == "NOTNULL": col.Nullable = false case k == "NOT": @@ -525,10 +499,10 @@ func (engine *Engine) mapType(t reflect.Type) *Table { } } else if strings.Contains(k, "(") && strings.HasSuffix(k, ")") { fs := strings.Split(k, "(") - if _, ok := sqlTypes[fs[0]]; !ok { + if _, ok := core.SqlTypes[fs[0]]; !ok { continue } - col.SQLType = SQLType{fs[0], 0, 0} + col.SQLType = core.SQLType{fs[0], 0, 0} fs2 := strings.Split(fs[1][0:len(fs[1])-1], ",") if len(fs2) == 2 { col.Length, _ = strconv.Atoi(fs2[0]) @@ -537,17 +511,17 @@ func (engine *Engine) mapType(t reflect.Type) *Table { col.Length, _ = strconv.Atoi(fs2[0]) } } else { - if _, ok := sqlTypes[k]; ok { - col.SQLType = SQLType{k, 0, 0} + if _, ok := core.SqlTypes[k]; ok { + col.SQLType = core.SQLType{k, 0, 0} } else if key != col.Default { col.Name = key } } - engine.SqlType(col) + dialect.SqlType(col) } } if col.SQLType.Name == "" { - col.SQLType = Type2SQLType(fieldType) + col.SQLType = core.Type2SQLType(fieldType) } if col.Length == 0 { col.Length = col.SQLType.DefaultLength @@ -556,9 +530,9 @@ func (engine *Engine) mapType(t reflect.Type) *Table { col.Length2 = col.SQLType.DefaultLength2 } if col.Name == "" { - col.Name = engine.columnMapper.Obj2Table(t.Field(i).Name) + col.Name = colMapper.Obj2Table(t.Field(i).Name) } - if indexType == IndexType { + if indexType == core.IndexType { if indexName == "" { indexName = col.Name } @@ -566,12 +540,12 @@ func (engine *Engine) mapType(t reflect.Type) *Table { index.AddColumn(col.Name) col.Indexes[index.Name] = true } else { - index := NewIndex(indexName, IndexType) + index := core.NewIndex(indexName, core.IndexType) index.AddColumn(col.Name) table.AddIndex(index) col.Indexes[index.Name] = true } - } else if indexType == UniqueType { + } else if indexType == core.UniqueType { if indexName == "" { indexName = col.Name } @@ -579,7 +553,7 @@ func (engine *Engine) mapType(t reflect.Type) *Table { index.AddColumn(col.Name) col.Indexes[index.Name] = true } else { - index := NewIndex(indexName, UniqueType) + index := core.NewIndex(indexName, core.UniqueType) index.AddColumn(col.Name) table.AddIndex(index) col.Indexes[index.Name] = true @@ -587,8 +561,8 @@ func (engine *Engine) mapType(t reflect.Type) *Table { } } } else { - sqlType := Type2SQLType(fieldType) - col = &Column{engine.columnMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, + sqlType := core.Type2SQLType(fieldType) + col = &core.Column{colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, sqlType.DefaultLength, sqlType.DefaultLength2, true, "", make(map[string]bool), false, false, TWOSIDES, false, false, false, false} } @@ -604,7 +578,7 @@ func (engine *Engine) mapType(t reflect.Type) *Table { } if idFieldColName != "" && len(table.PrimaryKeys) == 0 { - col := table.Columns[strings.ToLower(idFieldColName)] + col := table.GetColumn(idFieldColName) col.IsPrimaryKey = true col.IsAutoIncrement = true col.Nullable = false @@ -666,6 +640,13 @@ func (engine *Engine) CreateUniques(bean interface{}) error { return session.CreateUniques(bean) } +func (engine *Engine) getCacher(t reflect.Type) Cacher { + if cacher, ok := engine.tableCachers[t]; ok { + return cacher + } + return engine.Cacher +} + // If enabled cache, clear the cache bean func (engine *Engine) ClearCacheBean(bean interface{}, id int64) error { t := rType(bean) @@ -673,9 +654,10 @@ func (engine *Engine) ClearCacheBean(bean interface{}, id int64) error { return errors.New("error params") } table := engine.autoMap(bean) - if table.Cacher != nil { - table.Cacher.ClearIds(table.Name) - table.Cacher.DelBean(table.Name, id) + cacher := engine.getCacher(t) + if cacher != nil { + cacher.ClearIds(table.Name) + cacher.DelBean(table.Name, id) } return nil } @@ -688,9 +670,10 @@ func (engine *Engine) ClearCache(beans ...interface{}) error { return errors.New("error params") } table := engine.autoMap(bean) - if table.Cacher != nil { - table.Cacher.ClearIds(table.Name) - table.Cacher.ClearBeans(table.Name) + cacher := engine.getCacher(t) + if cacher != nil { + cacher.ClearIds(table.Name) + cacher.ClearBeans(table.Name) } } return nil @@ -730,7 +713,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { return err } } else { - for _, col := range table.Columns { + for _, col := range table.Columns() { session := engine.NewSession() session.Statement.RefTable = table defer session.Close() @@ -753,7 +736,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { session := engine.NewSession() session.Statement.RefTable = table defer session.Close() - if index.Type == UniqueType { + if index.Type == core.UniqueType { //isExist, err := session.isIndexExist(table.Name, name, true) isExist, err := session.isIndexExist2(table.Name, index.Cols, true) if err != nil { @@ -768,7 +751,7 @@ func (engine *Engine) Sync(beans ...interface{}) error { return err } } - } else if index.Type == IndexType { + } else if index.Type == core.IndexType { isExist, err := session.isIndexExist2(table.Name, index.Cols, false) if err != nil { return err diff --git a/examples/cache.go b/examples/cache.go index 9e47e77e..d543cad2 100644 --- a/examples/cache.go +++ b/examples/cache.go @@ -1,109 +1,109 @@ package main import ( - "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "fmt" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type User struct { - Id int64 - Name string + Id int64 + Name string } func main() { - f := "cache.db" - os.Remove(f) + f := "cache.db" + os.Remove(f) - Orm, err := xorm.NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - Orm.ShowSQL = true - cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) - Orm.SetDefaultCacher(cacher) + Orm, err := xorm.NewEngine("sqlite3", f) + if err != nil { + fmt.Println(err) + return + } + Orm.ShowSQL = true + cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + Orm.SetDefaultCacher(cacher) - err = Orm.CreateTables(&User{}) - if err != nil { - fmt.Println(err) - return - } + err = Orm.CreateTables(&User{}) + if err != nil { + fmt.Println(err) + return + } - _, err = Orm.Insert(&User{Name: "xlw"}) - if err != nil { - fmt.Println(err) - return - } + _, err = Orm.Insert(&User{Name: "xlw"}) + if err != nil { + fmt.Println(err) + return + } - users := make([]User, 0) - err = Orm.Find(&users) - if err != nil { - fmt.Println(err) - return - } + users := make([]User, 0) + err = Orm.Find(&users) + if err != nil { + fmt.Println(err) + return + } - fmt.Println("users:", users) + fmt.Println("users:", users) - users2 := make([]User, 0) + users2 := make([]User, 0) - err = Orm.Find(&users2) - if err != nil { - fmt.Println(err) - return - } + err = Orm.Find(&users2) + if err != nil { + fmt.Println(err) + return + } - fmt.Println("users2:", users2) + fmt.Println("users2:", users2) - users3 := make([]User, 0) + users3 := make([]User, 0) - err = Orm.Find(&users3) - if err != nil { - fmt.Println(err) - return - } + err = Orm.Find(&users3) + if err != nil { + fmt.Println(err) + return + } - fmt.Println("users3:", users3) + fmt.Println("users3:", users3) - user4 := new(User) - has, err := Orm.Id(1).Get(user4) - if err != nil { - fmt.Println(err) - return - } + user4 := new(User) + has, err := Orm.Id(1).Get(user4) + if err != nil { + fmt.Println(err) + return + } - fmt.Println("user4:", has, user4) + fmt.Println("user4:", has, user4) - user4.Name = "xiaolunwen" - _, err = Orm.Id(1).Update(user4) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("user4:", user4) + user4.Name = "xiaolunwen" + _, err = Orm.Id(1).Update(user4) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("user4:", user4) - user5 := new(User) - has, err = Orm.Id(1).Get(user5) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("user5:", has, user5) + user5 := new(User) + has, err = Orm.Id(1).Get(user5) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("user5:", has, user5) - _, err = Orm.Id(1).Delete(new(User)) - if err != nil { - fmt.Println(err) - return - } + _, err = Orm.Id(1).Delete(new(User)) + if err != nil { + fmt.Println(err) + return + } - for { - user6 := new(User) - has, err = Orm.Id(1).Get(user6) - if err != nil { - fmt.Println(err) - return - } - fmt.Println("user6:", has, user6) - } + for { + user6 := new(User) + has, err = Orm.Id(1).Get(user6) + if err != nil { + fmt.Println(err) + return + } + fmt.Println("user6:", has, user6) + } } diff --git a/examples/cachegoroutine.go b/examples/cachegoroutine.go index 1a5feb44..117490d6 100644 --- a/examples/cachegoroutine.go +++ b/examples/cachegoroutine.go @@ -1,105 +1,105 @@ package main import ( - "fmt" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "fmt" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type User struct { - Id int64 - Name string + Id int64 + Name string } func sqliteEngine() (*xorm.Engine, error) { - os.Remove("./test.db") - return xorm.NewEngine("sqlite3", "./goroutine.db") + os.Remove("./test.db") + return xorm.NewEngine("sqlite3", "./goroutine.db") } func mysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mysql", "root:@/test?charset=utf8") + return xorm.NewEngine("mysql", "root:@/test?charset=utf8") } var u *User = &User{} func test(engine *xorm.Engine) { - err := engine.CreateTables(u) - if err != nil { - fmt.Println(err) - return - } + err := engine.CreateTables(u) + if err != nil { + fmt.Println(err) + return + } - size := 500 - queue := make(chan int, size) + size := 500 + queue := make(chan int, size) - for i := 0; i < size; i++ { - go func(x int) { - //x := i - err := engine.Ping() - if err != nil { - fmt.Println(err) - } else { - for j := 0; j < 10; j++ { - if x+j < 2 { - _, err = engine.Get(u) - } else if x+j < 4 { - users := make([]User, 0) - err = engine.Find(&users) - } else if x+j < 8 { - _, err = engine.Count(u) - } else if x+j < 16 { - _, err = engine.Insert(&User{Name: "xlw"}) - } else if x+j < 32 { - //_, err = engine.Id(1).Delete(u) - _, err = engine.Delete(u) - } - if err != nil { - fmt.Println(err) - queue <- x - return - } - } - fmt.Printf("%v success!\n", x) - } - queue <- x - }(i) - } + for i := 0; i < size; i++ { + go func(x int) { + //x := i + err := engine.Ping() + if err != nil { + fmt.Println(err) + } else { + for j := 0; j < 10; j++ { + if x+j < 2 { + _, err = engine.Get(u) + } else if x+j < 4 { + users := make([]User, 0) + err = engine.Find(&users) + } else if x+j < 8 { + _, err = engine.Count(u) + } else if x+j < 16 { + _, err = engine.Insert(&User{Name: "xlw"}) + } else if x+j < 32 { + //_, err = engine.Id(1).Delete(u) + _, err = engine.Delete(u) + } + if err != nil { + fmt.Println(err) + queue <- x + return + } + } + fmt.Printf("%v success!\n", x) + } + queue <- x + }(i) + } - for i := 0; i < size; i++ { - <-queue - } + for i := 0; i < size; i++ { + <-queue + } - //conns := atomic.LoadInt32(&xorm.ConnectionNum) - //fmt.Println("connection number:", conns) - fmt.Println("end") + //conns := atomic.LoadInt32(&xorm.ConnectionNum) + //fmt.Println("connection number:", conns) + fmt.Println("end") } func main() { - fmt.Println("-----start sqlite go routines-----") - engine, err := sqliteEngine() - if err != nil { - fmt.Println(err) - return - } - engine.ShowSQL = true - cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) - engine.SetDefaultCacher(cacher) - fmt.Println(engine) - test(engine) - fmt.Println("test end") - engine.Close() + fmt.Println("-----start sqlite go routines-----") + engine, err := sqliteEngine() + if err != nil { + fmt.Println(err) + return + } + engine.ShowSQL = true + cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + engine.SetDefaultCacher(cacher) + fmt.Println(engine) + test(engine) + fmt.Println("test end") + engine.Close() - fmt.Println("-----start mysql go routines-----") - engine, err = mysqlEngine() - engine.ShowSQL = true - cacher = xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) - engine.SetDefaultCacher(cacher) - if err != nil { - fmt.Println(err) - return - } - defer engine.Close() - test(engine) + fmt.Println("-----start mysql go routines-----") + engine, err = mysqlEngine() + engine.ShowSQL = true + cacher = xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + engine.SetDefaultCacher(cacher) + if err != nil { + fmt.Println(err) + return + } + defer engine.Close() + test(engine) } diff --git a/examples/conversion.go b/examples/conversion.go index 1d1b36a9..b74207ba 100644 --- a/examples/conversion.go +++ b/examples/conversion.go @@ -1,76 +1,76 @@ package main import ( - "errors" - "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "errors" + "fmt" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type Status struct { - Name string - Color string + Name string + Color string } var ( - Registed Status = Status{"Registed", "white"} - Approved Status = Status{"Approved", "green"} - Removed Status = Status{"Removed", "red"} - Statuses map[string]Status = map[string]Status{ - Registed.Name: Registed, - Approved.Name: Approved, - Removed.Name: Removed, - } + Registed Status = Status{"Registed", "white"} + Approved Status = Status{"Approved", "green"} + Removed Status = Status{"Removed", "red"} + Statuses map[string]Status = map[string]Status{ + Registed.Name: Registed, + Approved.Name: Approved, + Removed.Name: Removed, + } ) func (s *Status) FromDB(bytes []byte) error { - if r, ok := Statuses[string(bytes)]; ok { - *s = r - return nil - } else { - return errors.New("no this data") - } + if r, ok := Statuses[string(bytes)]; ok { + *s = r + return nil + } else { + return errors.New("no this data") + } } func (s *Status) ToDB() ([]byte, error) { - return []byte(s.Name), nil + return []byte(s.Name), nil } type User struct { - Id int64 - Name string - Status Status `xorm:"varchar(40)"` + Id int64 + Name string + Status Status `xorm:"varchar(40)"` } func main() { - f := "conversion.db" - os.Remove(f) + f := "conversion.db" + os.Remove(f) - Orm, err := NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}) - if err != nil { - fmt.Println(err) - return - } + Orm, err := NewEngine("sqlite3", f) + if err != nil { + fmt.Println(err) + return + } + Orm.ShowSQL = true + err = Orm.CreateTables(&User{}) + if err != nil { + fmt.Println(err) + return + } - _, err = Orm.Insert(&User{1, "xlw", Registed}) - if err != nil { - fmt.Println(err) - return - } + _, err = Orm.Insert(&User{1, "xlw", Registed}) + if err != nil { + fmt.Println(err) + return + } - users := make([]User, 0) - err = Orm.Find(&users) - if err != nil { - fmt.Println(err) - return - } + users := make([]User, 0) + err = Orm.Find(&users) + if err != nil { + fmt.Println(err) + return + } - fmt.Println(users) + fmt.Println(users) } diff --git a/examples/derive.go b/examples/derive.go index 72a9b41a..f3480521 100644 --- a/examples/derive.go +++ b/examples/derive.go @@ -1,66 +1,66 @@ package main import ( - "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "fmt" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type User struct { - Id int64 - Name string + Id int64 + Name string } type LoginInfo struct { - Id int64 - IP string - UserId int64 + Id int64 + IP string + UserId int64 } type LoginInfo1 struct { - LoginInfo `xorm:"extends"` - UserName string + LoginInfo `xorm:"extends"` + UserName string } func main() { - f := "derive.db" - os.Remove(f) + f := "derive.db" + os.Remove(f) - Orm, err := NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - defer Orm.Close() - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}, &LoginInfo{}) - if err != nil { - fmt.Println(err) - return - } + Orm, err := NewEngine("sqlite3", f) + if err != nil { + fmt.Println(err) + return + } + defer Orm.Close() + Orm.ShowSQL = true + err = Orm.CreateTables(&User{}, &LoginInfo{}) + if err != nil { + fmt.Println(err) + return + } - _, err = Orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1}) - if err != nil { - fmt.Println(err) - return - } + _, err = Orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1}) + if err != nil { + fmt.Println(err) + return + } - info := LoginInfo{} - _, err = Orm.Id(1).Get(&info) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(info) + info := LoginInfo{} + _, err = Orm.Id(1).Get(&info) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(info) - infos := make([]LoginInfo1, 0) - err = Orm.Sql(`select *, (select name from user where id = login_info.user_id) as user_name from + infos := make([]LoginInfo1, 0) + err = Orm.Sql(`select *, (select name from user where id = login_info.user_id) as user_name from login_info limit 10`).Find(&infos) - if err != nil { - fmt.Println(err) - return - } + if err != nil { + fmt.Println(err) + return + } - fmt.Println(infos) + fmt.Println(infos) } diff --git a/examples/goroutine.go b/examples/goroutine.go index 9cf0a7d5..16c0b242 100644 --- a/examples/goroutine.go +++ b/examples/goroutine.go @@ -1,106 +1,106 @@ package main import ( - "fmt" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" - "runtime" + "fmt" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" + "runtime" ) type User struct { - Id int64 - Name string + Id int64 + Name string } func sqliteEngine() (*xorm.Engine, error) { - os.Remove("./test.db") - return xorm.NewEngine("sqlite3", "./goroutine.db") + os.Remove("./test.db") + return xorm.NewEngine("sqlite3", "./goroutine.db") } func mysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mysql", "root:@/test?charset=utf8") + return xorm.NewEngine("mysql", "root:@/test?charset=utf8") } var u *User = &User{} func test(engine *xorm.Engine) { - err := engine.CreateTables(u) - if err != nil { - fmt.Println(err) - return - } + err := engine.CreateTables(u) + if err != nil { + fmt.Println(err) + return + } - size := 500 - queue := make(chan int, size) + size := 500 + queue := make(chan int, size) - for i := 0; i < size; i++ { - go func(x int) { - //x := i - err := engine.Test() - if err != nil { - fmt.Println(err) - } else { - err = engine.Map(u) - if err != nil { - fmt.Println("Map user failed") - } else { - for j := 0; j < 10; j++ { - if x+j < 2 { - _, err = engine.Get(u) - } else if x+j < 4 { - users := make([]User, 0) - err = engine.Find(&users) - } else if x+j < 8 { - _, err = engine.Count(u) - } else if x+j < 16 { - _, err = engine.Insert(&User{Name: "xlw"}) - } else if x+j < 32 { - _, err = engine.Id(1).Delete(u) - } - if err != nil { - fmt.Println(err) - queue <- x - return - } - } - fmt.Printf("%v success!\n", x) - } - } - queue <- x - }(i) - } + for i := 0; i < size; i++ { + go func(x int) { + //x := i + err := engine.Test() + if err != nil { + fmt.Println(err) + } else { + err = engine.Map(u) + if err != nil { + fmt.Println("Map user failed") + } else { + for j := 0; j < 10; j++ { + if x+j < 2 { + _, err = engine.Get(u) + } else if x+j < 4 { + users := make([]User, 0) + err = engine.Find(&users) + } else if x+j < 8 { + _, err = engine.Count(u) + } else if x+j < 16 { + _, err = engine.Insert(&User{Name: "xlw"}) + } else if x+j < 32 { + _, err = engine.Id(1).Delete(u) + } + if err != nil { + fmt.Println(err) + queue <- x + return + } + } + fmt.Printf("%v success!\n", x) + } + } + queue <- x + }(i) + } - for i := 0; i < size; i++ { - <-queue - } + for i := 0; i < size; i++ { + <-queue + } - //conns := atomic.LoadInt32(&xorm.ConnectionNum) - //fmt.Println("connection number:", conns) - fmt.Println("end") + //conns := atomic.LoadInt32(&xorm.ConnectionNum) + //fmt.Println("connection number:", conns) + fmt.Println("end") } func main() { - runtime.GOMAXPROCS(2) - fmt.Println("-----start sqlite go routines-----") - engine, err := sqliteEngine() - if err != nil { - fmt.Println(err) - return - } - engine.ShowSQL = true - fmt.Println(engine) - test(engine) - fmt.Println("test end") - engine.Close() + runtime.GOMAXPROCS(2) + fmt.Println("-----start sqlite go routines-----") + engine, err := sqliteEngine() + if err != nil { + fmt.Println(err) + return + } + engine.ShowSQL = true + fmt.Println(engine) + test(engine) + fmt.Println("test end") + engine.Close() - fmt.Println("-----start mysql go routines-----") - engine, err = mysqlEngine() - if err != nil { - fmt.Println(err) - return - } - defer engine.Close() - test(engine) + fmt.Println("-----start mysql go routines-----") + engine, err = mysqlEngine() + if err != nil { + fmt.Println(err) + return + } + defer engine.Close() + test(engine) } diff --git a/examples/maxconnect.go b/examples/maxconnect.go index 86e5beb8..e3fbfea7 100644 --- a/examples/maxconnect.go +++ b/examples/maxconnect.go @@ -1,106 +1,106 @@ package main import ( - "fmt" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - xorm "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" - "runtime" + "fmt" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + xorm "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" + "runtime" ) type User struct { - Id int64 - Name string + Id int64 + Name string } func sqliteEngine() (*xorm.Engine, error) { - os.Remove("./test.db") - return xorm.NewEngine("sqlite3", "./goroutine.db") + os.Remove("./test.db") + return xorm.NewEngine("sqlite3", "./goroutine.db") } func mysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mysql", "root:@/test?charset=utf8") + return xorm.NewEngine("mysql", "root:@/test?charset=utf8") } var u *User = &User{} func test(engine *xorm.Engine) { - err := engine.CreateTables(u) - if err != nil { - fmt.Println(err) - return - } + err := engine.CreateTables(u) + if err != nil { + fmt.Println(err) + return + } - engine.ShowSQL = true - engine.Pool.SetMaxConns(5) - size := 1000 - queue := make(chan int, size) + engine.ShowSQL = true + engine.Pool.SetMaxConns(5) + size := 1000 + queue := make(chan int, size) - for i := 0; i < size; i++ { - go func(x int) { - //x := i - err := engine.Test() - if err != nil { - fmt.Println(err) - } else { - err = engine.Map(u) - if err != nil { - fmt.Println("Map user failed") - } else { - for j := 0; j < 10; j++ { - if x+j < 2 { - _, err = engine.Get(u) - } else if x+j < 4 { - users := make([]User, 0) - err = engine.Find(&users) - } else if x+j < 8 { - _, err = engine.Count(u) - } else if x+j < 16 { - _, err = engine.Insert(&User{Name: "xlw"}) - } else if x+j < 32 { - _, err = engine.Id(1).Delete(u) - } - if err != nil { - fmt.Println(err) - queue <- x - return - } - } - fmt.Printf("%v success!\n", x) - } - } - queue <- x - }(i) - } + for i := 0; i < size; i++ { + go func(x int) { + //x := i + err := engine.Test() + if err != nil { + fmt.Println(err) + } else { + err = engine.Map(u) + if err != nil { + fmt.Println("Map user failed") + } else { + for j := 0; j < 10; j++ { + if x+j < 2 { + _, err = engine.Get(u) + } else if x+j < 4 { + users := make([]User, 0) + err = engine.Find(&users) + } else if x+j < 8 { + _, err = engine.Count(u) + } else if x+j < 16 { + _, err = engine.Insert(&User{Name: "xlw"}) + } else if x+j < 32 { + _, err = engine.Id(1).Delete(u) + } + if err != nil { + fmt.Println(err) + queue <- x + return + } + } + fmt.Printf("%v success!\n", x) + } + } + queue <- x + }(i) + } - for i := 0; i < size; i++ { - <-queue - } + for i := 0; i < size; i++ { + <-queue + } - fmt.Println("end") + fmt.Println("end") } func main() { - runtime.GOMAXPROCS(2) - fmt.Println("create engine") - engine, err := sqliteEngine() - if err != nil { - fmt.Println(err) - return - } - engine.ShowSQL = true - fmt.Println(engine) - test(engine) - fmt.Println("------------------------") - engine.Close() + runtime.GOMAXPROCS(2) + fmt.Println("create engine") + engine, err := sqliteEngine() + if err != nil { + fmt.Println(err) + return + } + engine.ShowSQL = true + fmt.Println(engine) + test(engine) + fmt.Println("------------------------") + engine.Close() - engine, err = mysqlEngine() - if err != nil { - fmt.Println(err) - return - } - defer engine.Close() - test(engine) + engine, err = mysqlEngine() + if err != nil { + fmt.Println(err) + return + } + defer engine.Close() + test(engine) } diff --git a/examples/pool.go b/examples/pool.go index 16fe0d08..d6ec0de7 100644 --- a/examples/pool.go +++ b/examples/pool.go @@ -1,45 +1,45 @@ package main import ( - "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "fmt" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type User struct { - Id int64 - Name string + Id int64 + Name string } func main() { - f := "pool.db" - os.Remove(f) + f := "pool.db" + os.Remove(f) - Orm, err := NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - err = Orm.SetPool(NewSimpleConnectPool()) - if err != nil { - fmt.Println(err) - return - } + Orm, err := NewEngine("sqlite3", f) + if err != nil { + fmt.Println(err) + return + } + err = Orm.SetPool(NewSimpleConnectPool()) + if err != nil { + fmt.Println(err) + return + } - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}) - if err != nil { - fmt.Println(err) - return - } + Orm.ShowSQL = true + err = Orm.CreateTables(&User{}) + if err != nil { + fmt.Println(err) + return + } - for i := 0; i < 10; i++ { - _, err = Orm.Get(&User{}) - if err != nil { - fmt.Println(err) - break - } + for i := 0; i < 10; i++ { + _, err = Orm.Get(&User{}) + if err != nil { + fmt.Println(err) + break + } - } + } } diff --git a/examples/singlemapping.go b/examples/singlemapping.go index e6aff445..932a0589 100644 --- a/examples/singlemapping.go +++ b/examples/singlemapping.go @@ -1,54 +1,54 @@ package main import ( - "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - "os" + "fmt" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + "os" ) type User struct { - Id int64 - Name string + Id int64 + Name string } type LoginInfo struct { - Id int64 - IP string - UserId int64 - // timestamp should be updated by database, so only allow get from db - TimeStamp string `xorm:"<-"` - // assume - Nonuse int `xorm:"->"` + Id int64 + IP string + UserId int64 + // timestamp should be updated by database, so only allow get from db + TimeStamp string `xorm:"<-"` + // assume + Nonuse int `xorm:"->"` } func main() { - f := "singleMapping.db" - os.Remove(f) + f := "singleMapping.db" + os.Remove(f) - Orm, err := NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}, &LoginInfo{}) - if err != nil { - fmt.Println(err) - return - } + Orm, err := NewEngine("sqlite3", f) + if err != nil { + fmt.Println(err) + return + } + Orm.ShowSQL = true + err = Orm.CreateTables(&User{}, &LoginInfo{}) + if err != nil { + fmt.Println(err) + return + } - _, err = Orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1, "", 23}) - if err != nil { - fmt.Println(err) - return - } + _, err = Orm.Insert(&User{1, "xlw"}, &LoginInfo{1, "127.0.0.1", 1, "", 23}) + if err != nil { + fmt.Println(err) + return + } - info := LoginInfo{} - _, err = Orm.Id(1).Get(&info) - if err != nil { - fmt.Println(err) - return - } - fmt.Println(info) + info := LoginInfo{} + _, err = Orm.Id(1).Get(&info) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(info) } diff --git a/examples/sync.go b/examples/sync.go index ba41bafe..043bffa8 100644 --- a/examples/sync.go +++ b/examples/sync.go @@ -1,92 +1,92 @@ package main import ( - "fmt" - _ "github.com/bylevel/pq" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" + "fmt" + _ "github.com/bylevel/pq" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" ) type SyncUser2 struct { - Id int64 - Name string `xorm:"unique"` - Age int `xorm:"index"` - Title string - Address string - Genre string - Area string - Date int + Id int64 + Name string `xorm:"unique"` + Age int `xorm:"index"` + Title string + Address string + Genre string + Area string + Date int } type SyncLoginInfo2 struct { - Id int64 - IP string `xorm:"index"` - UserId int64 - AddedCol int - // timestamp should be updated by database, so only allow get from db - TimeStamp string - // assume - Nonuse int `xorm:"unique"` - Newa string `xorm:"index"` + Id int64 + IP string `xorm:"index"` + UserId int64 + AddedCol int + // timestamp should be updated by database, so only allow get from db + TimeStamp string + // assume + Nonuse int `xorm:"unique"` + Newa string `xorm:"index"` } func sync(engine *xorm.Engine) error { - return engine.Sync(&SyncLoginInfo2{}, &SyncUser2{}) + return engine.Sync(&SyncLoginInfo2{}, &SyncUser2{}) } func sqliteEngine() (*xorm.Engine, error) { - f := "sync.db" - //os.Remove(f) + f := "sync.db" + //os.Remove(f) - return xorm.NewEngine("sqlite3", f) + return xorm.NewEngine("sqlite3", f) } func mysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mysql", "root:@/test?charset=utf8") + return xorm.NewEngine("mysql", "root:@/test?charset=utf8") } func postgresEngine() (*xorm.Engine, error) { - return xorm.NewEngine("postgres", "dbname=xorm_test sslmode=disable") + return xorm.NewEngine("postgres", "dbname=xorm_test sslmode=disable") } type engineFunc func() (*xorm.Engine, error) func main() { - //engines := []engineFunc{sqliteEngine, mysqlEngine, postgresEngine} - //engines := []engineFunc{sqliteEngine} - //engines := []engineFunc{mysqlEngine} - engines := []engineFunc{postgresEngine} - for _, enginefunc := range engines { - Orm, err := enginefunc() - fmt.Println("--------", Orm.DriverName, "----------") - if err != nil { - fmt.Println(err) - return - } - Orm.ShowSQL = true - err = sync(Orm) - if err != nil { - fmt.Println(err) - } + //engines := []engineFunc{sqliteEngine, mysqlEngine, postgresEngine} + //engines := []engineFunc{sqliteEngine} + //engines := []engineFunc{mysqlEngine} + engines := []engineFunc{postgresEngine} + for _, enginefunc := range engines { + Orm, err := enginefunc() + fmt.Println("--------", Orm.DriverName, "----------") + if err != nil { + fmt.Println(err) + return + } + Orm.ShowSQL = true + err = sync(Orm) + if err != nil { + fmt.Println(err) + } - _, err = Orm.Where("id > 0").Delete(&SyncUser2{}) - if err != nil { - fmt.Println(err) - } + _, err = Orm.Where("id > 0").Delete(&SyncUser2{}) + if err != nil { + fmt.Println(err) + } - user := &SyncUser2{ - Name: "testsdf", - Age: 15, - Title: "newsfds", - Address: "fasfdsafdsaf", - Genre: "fsafd", - Area: "fafdsafd", - Date: 1000, - } - _, err = Orm.Insert(user) - if err != nil { - fmt.Println(err) - } - } + user := &SyncUser2{ + Name: "testsdf", + Age: 15, + Title: "newsfds", + Address: "fasfdsafdsaf", + Genre: "fsafd", + Area: "fafdsafd", + Date: 1000, + } + _, err = Orm.Insert(user) + if err != nil { + fmt.Println(err) + } + } } diff --git a/mysql.go b/mysql.go deleted file mode 100644 index 4dcde839..00000000 --- a/mysql.go +++ /dev/null @@ -1,344 +0,0 @@ -package xorm - -import ( - "crypto/tls" - "database/sql" - "errors" - "fmt" - "regexp" - "strconv" - "strings" - "time" -) - -type uri struct { - dbType string - proto string - host string - port string - dbName string - user string - passwd string - charset string - laddr string - raddr string - timeout time.Duration -} - -type parser interface { - parse(driverName, dataSourceName string) (*uri, error) -} - -type mysqlParser struct { -} - -func (p *mysqlParser) parse(driverName, dataSourceName string) (*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 := &uri{dbType: 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 -} - -type base struct { - parser parser - driverName string - dataSourceName string - *uri -} - -func (b *base) init(parser parser, drivername, dataSourceName string) (err error) { - b.parser = parser - b.driverName, b.dataSourceName = drivername, dataSourceName - b.uri, err = b.parser.parse(b.driverName, b.dataSourceName) - return -} - -func (b *base) URI() *uri { - return b.uri -} - -func (b *base) DBType() string { - return b.uri.dbType -} - -type mysql struct { - base - net string - addr string - params map[string]string - loc *time.Location - timeout time.Duration - tls *tls.Config - allowAllFiles bool - allowOldPasswords bool - clientFoundRows bool -} - -func (db *mysql) Init(drivername, uri string) error { - return db.base.init(&mysqlParser{}, drivername, uri) -} - -func (db *mysql) SqlType(c *Column) string { - var res string - switch t := c.SQLType.Name; t { - case Bool: - res = TinyInt - case Serial: - c.IsAutoIncrement = true - c.IsPrimaryKey = true - c.Nullable = false - res = Int - case BigSerial: - c.IsAutoIncrement = true - c.IsPrimaryKey = true - c.Nullable = false - res = BigInt - case Bytea: - res = Blob - case TimeStampz: - res = Char - c.Length = 64 - default: - res = t - } - - var hasLen1 bool = (c.Length > 0) - var hasLen2 bool = (c.Length2 > 0) - if hasLen1 { - res += "(" + strconv.Itoa(c.Length) + ")" - } else if hasLen2 { - res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" - } - return res -} - -func (db *mysql) SupportInsertMany() bool { - return true -} - -func (db *mysql) QuoteStr() string { - return "`" -} - -func (db *mysql) SupportEngine() bool { - return true -} - -func (db *mysql) AutoIncrStr() string { - return "AUTO_INCREMENT" -} - -func (db *mysql) SupportCharset() bool { - return true -} - -func (db *mysql) IndexOnTable() bool { - return true -} - -func (db *mysql) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{db.dbName, tableName, idxName} - sql := "SELECT `INDEX_NAME` FROM `INFORMATION_SCHEMA`.`STATISTICS`" - sql += " WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `INDEX_NAME`=?" - return sql, args -} - -func (db *mysql) ColumnCheckSql(tableName, colName string) (string, []interface{}) { - args := []interface{}{db.dbName, tableName, colName} - sql := "SELECT `COLUMN_NAME` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = ? AND `TABLE_NAME` = ? AND `COLUMN_NAME` = ?" - return sql, args -} - -func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{db.dbName, tableName} - sql := "SELECT `TABLE_NAME` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=? and `TABLE_NAME`=?" - return sql, args -} - -func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, error) { - 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` = ?" - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, nil, err - } - cols := make(map[string]*Column) - colSeq := make([]string, 0) - for _, record := range res { - col := new(Column) - col.Indexes = make(map[string]bool) - for name, content := range record { - switch name { - case "COLUMN_NAME": - col.Name = strings.Trim(string(content), "` ") - case "IS_NULLABLE": - if "YES" == string(content) { - col.Nullable = true - } - case "COLUMN_DEFAULT": - // add '' - col.Default = string(content) - case "COLUMN_TYPE": - cts := strings.Split(string(content), "(") - var len1, len2 int - if len(cts) == 2 { - idx := strings.Index(cts[1], ")") - lens := strings.Split(cts[1][0:idx], ",") - len1, err = strconv.Atoi(strings.TrimSpace(lens[0])) - if err != nil { - return nil, nil, err - } - if len(lens) == 2 { - len2, err = strconv.Atoi(lens[1]) - if err != nil { - return nil, nil, err - } - } - } - colName := cts[0] - colType := strings.ToUpper(colName) - col.Length = len1 - col.Length2 = len2 - if _, ok := sqlTypes[colType]; ok { - col.SQLType = SQLType{colType, len1, len2} - } else { - return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType)) - } - case "COLUMN_KEY": - key := string(content) - if key == "PRI" { - col.IsPrimaryKey = true - } - if key == "UNI" { - //col.is - } - case "EXTRA": - extra := string(content) - if extra == "auto_increment" { - col.IsAutoIncrement = true - } - } - } - if col.SQLType.IsText() { - if col.Default != "" { - col.Default = "'" + col.Default + "'" - } - } - cols[col.Name] = col - colSeq = append(colSeq, col.Name) - } - return colSeq, cols, nil -} - -func (db *mysql) GetTables() ([]*Table, error) { - args := []interface{}{db.dbName} - s := "SELECT `TABLE_NAME`, `ENGINE`, `TABLE_ROWS`, `AUTO_INCREMENT` from `INFORMATION_SCHEMA`.`TABLES` WHERE `TABLE_SCHEMA`=?" - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, err - } - - tables := make([]*Table, 0) - for _, record := range res { - table := new(Table) - for name, content := range record { - switch name { - case "TABLE_NAME": - table.Name = strings.Trim(string(content), "` ") - case "ENGINE": - } - } - tables = append(tables, table) - } - return tables, nil -} - -func (db *mysql) GetIndexes(tableName string) (map[string]*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` = ?" - cnn, err := sql.Open(db.driverName, db.dataSourceName) - if err != nil { - return nil, err - } - defer cnn.Close() - res, err := query(cnn, s, args...) - if err != nil { - return nil, err - } - - indexes := make(map[string]*Index, 0) - for _, record := range res { - var indexType int - var indexName, colName string - for name, content := range record { - switch name { - case "NON_UNIQUE": - if "YES" == string(content) || string(content) == "1" { - indexType = IndexType - } else { - indexType = UniqueType - } - case "INDEX_NAME": - indexName = string(content) - case "COLUMN_NAME": - colName = strings.Trim(string(content), "` ") - } - } - if indexName == "PRIMARY" { - continue - } - if strings.HasPrefix(indexName, "IDX_"+tableName) || strings.HasPrefix(indexName, "UQE_"+tableName) { - indexName = indexName[5+len(tableName) : len(indexName)] - } - - var index *Index - var ok bool - if index, ok = indexes[indexName]; !ok { - index = new(Index) - index.Type = indexType - index.Name = indexName - indexes[indexName] = index - } - index.AddColumn(colName) - } - return indexes, nil -} diff --git a/rows.go b/rows.go index 0ac6c956..ef04af75 100644 --- a/rows.go +++ b/rows.go @@ -41,7 +41,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { } for _, filter := range rows.session.Engine.Filters { - sql = filter.Do(sql, session) + sql = filter.Do(sql, session.Engine.dialect, rows.session.Statement.RefTable) } rows.session.Engine.LogSQL(sql) diff --git a/session.go b/session.go index 7623009b..7bdc814e 100644 --- a/session.go +++ b/session.go @@ -9,6 +9,8 @@ import ( "strconv" "strings" "time" + + "github.com/lunny/xorm/core" ) // Struct Session keep a pointer to sql.DB and provides all execution of all @@ -108,7 +110,7 @@ func (session *Session) After(closures func(interface{})) *Session { return session } -// Method Table can input a string or pointer to struct for special a table to operate. +// Method core.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 @@ -354,13 +356,10 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b } table := session.Engine.autoMapType(rType(obj)) - + var col *core.Column for key, data := range objMap { - key = strings.ToLower(key) - var col *Column - var ok bool - if col, ok = table.Columns[key]; !ok { - session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.ColumnsSeq)) + if col = table.GetColumn(key); col == nil { + session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.Columns())) continue } @@ -410,7 +409,7 @@ func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Resul func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { for _, filter := range session.Engine.Filters { - sqlStr = filter.Do(sqlStr, session) + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } session.Engine.LogSQL(sqlStr) @@ -596,14 +595,14 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, ErrCacheFailed } for _, filter := range session.Engine.Filters { - sqlStr = filter.Do(sqlStr, session) + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } newsql := session.Statement.convertIdSql(sqlStr) if newsql == "" { return false, ErrCacheFailed } - cacher := session.Statement.RefTable.Cacher + cacher := session.Engine.getCacher(session.Statement.RefTable.Type) tableName := session.Statement.TableName() session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args) ids, err := getCacheSql(cacher, tableName, newsql, args) @@ -679,7 +678,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } for _, filter := range session.Engine.Filters { - sqlStr = filter.Do(sqlStr, session) + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } newsql := session.Statement.convertIdSql(sqlStr) @@ -688,7 +687,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } table := session.Statement.RefTable - cacher := table.Cacher + cacher := session.Engine.getCacher(t) ids, err := getCacheSql(cacher, session.Statement.TableName(), newsql, args) if err != nil { //session.Engine.LogError(err) @@ -892,7 +891,7 @@ func (session *Session) Get(bean interface{}) (bool, error) { args = session.Statement.RawParams } - if session.Statement.RefTable.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache { has, err := session.cacheGet(bean, sqlStr, args...) if err != ErrCacheFailed { return has, err @@ -1003,7 +1002,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } sliceElementType := sliceValue.Type().Elem() - var table *Table + var table *core.Table if session.Statement.RefTable == nil { if sliceElementType.Kind() == reflect.Ptr { if sliceElementType.Elem().Kind() == reflect.Struct { @@ -1045,7 +1044,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) args = session.Statement.RawParams } - if table.Cacher != nil && + if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache && !session.Statement.IsDistinct { err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...) @@ -1236,9 +1235,9 @@ func (session *Session) isIndexExist2(tableName string, cols []string, unique bo for _, index := range indexes { if sliceEq(index.Cols, cols) { if unique { - return index.Type == UniqueType, nil + return index.Type == core.UniqueType, nil } else { - return index.Type == IndexType, nil + return index.Type == core.IndexType, nil } } } @@ -1256,7 +1255,7 @@ func (session *Session) addColumn(colName string) error { } //fmt.Println(session.Statement.RefTable) - col := session.Statement.RefTable.Columns[strings.ToLower(colName)] + col := session.Statement.RefTable.GetColumn(colName) sql, args := session.Statement.genAddColumnStr(col) _, err = session.exec(sql, args...) return err @@ -1345,14 +1344,14 @@ func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err return result, nil } -func (session *Session) getField(dataStruct *reflect.Value, key string, table *Table) *reflect.Value { +func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table) *reflect.Value { - key = strings.ToLower(key) - if _, ok := table.Columns[key]; !ok { - session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.ColumnsSeq)) + var col *core.Column + if col = table.GetColumn(key); col == nil { + session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.Columns())) return nil } - col := table.Columns[key] + fieldName := col.FieldName fieldPath := strings.Split(fieldName, ".") var fieldValue reflect.Value @@ -1404,7 +1403,7 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in continue } - if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { if data, err := value2Bytes(&rawValue); err == nil { structConvert.FromDB(data) } else { @@ -1634,7 +1633,7 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in if !hasAssigned { data, err := value2Bytes(&rawValue) if err == nil { - session.bytes2Value(table.Columns[key], fieldValue, data) + session.bytes2Value(table.GetColumn(key), fieldValue, data) } else { session.Engine.LogError(err.Error()) } @@ -1647,7 +1646,7 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { for _, filter := range session.Engine.Filters { - *sqlStr = filter.Do(*sqlStr, session) + *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable) } session.Engine.LogSQL(*sqlStr) @@ -1763,7 +1762,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error colNames := make([]string, 0) colMultiPlaces := make([]string, 0) var args = make([]interface{}, 0) - cols := make([]*Column, 0) + cols := make([]*core.Column, 0) for i := 0; i < size; i++ { elemValue := sliceValue.Index(i).Interface() @@ -1781,7 +1780,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error // -- if i == 0 { - for _, col := range table.Columns { + for _, col := range table.Columns() { fieldValue := reflect.Indirect(reflect.ValueOf(elemValue)).FieldByName(col.FieldName) if col.IsAutoIncrement && fieldValue.Int() == 0 { continue @@ -1853,7 +1852,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return 0, err } - if table.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache { session.cacheInsert(session.Statement.TableName()) } @@ -1904,7 +1903,7 @@ func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { return session.innerInsertMulti(rowsSlicePtr) } -func (session *Session) byte2Time(col *Column, data []byte) (outTime time.Time, outErr error) { +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 @@ -1929,7 +1928,7 @@ func (session *Session) byte2Time(col *Column, data []byte) (outTime time.Time, x, err = time.Parse("2006-01-02 15:04:05", sdata) } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { x, err = time.Parse("2006-01-02", sdata) - } else if col.SQLType.Name == Time { + } else if col.SQLType.Name == core.Time { if strings.Contains(sdata, " ") { ssd := strings.Split(sdata, " ") sdata = ssd[1] @@ -1937,7 +1936,7 @@ func (session *Session) byte2Time(col *Column, data []byte) (outTime time.Time, sdata = strings.TrimSpace(sdata) //fmt.Println(sdata) - if session.Engine.dialect.DBType() == MYSQL && len(sdata) > 8 { + if session.Engine.dialect.DBType() == core.MYSQL && len(sdata) > 8 { sdata = sdata[len(sdata)-8:] } //fmt.Println(sdata) @@ -1957,8 +1956,8 @@ func (session *Session) byte2Time(col *Column, data []byte) (outTime time.Time, } // convert a db data([]byte) to a field value -func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data []byte) error { - if structConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { +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) } @@ -2018,8 +2017,8 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && - session.Engine.dialect.DBType() == MYSQL { + if col.SQLType.Name == core.Bit && + session.Engine.dialect.DBType() == core.MYSQL { if len(data) == 1 { x = int64(data[0]) } else { @@ -2199,7 +2198,7 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && + if col.SQLType.Name == core.Bit && strings.Contains(session.Engine.DriverName, "mysql") { if len(data) == 1 { x = int64(data[0]) @@ -2225,7 +2224,7 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x1 int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && + if col.SQLType.Name == core.Bit && strings.Contains(session.Engine.DriverName, "mysql") { if len(data) == 1 { x = int(data[0]) @@ -2254,7 +2253,7 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x1 int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && + if col.SQLType.Name == core.Bit && strings.Contains(session.Engine.DriverName, "mysql") { if len(data) == 1 { x = int32(data[0]) @@ -2283,7 +2282,7 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x1 int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && + if col.SQLType.Name == core.Bit && strings.Contains(session.Engine.DriverName, "mysql") { if len(data) == 1 { x = int8(data[0]) @@ -2312,7 +2311,7 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data var x1 int64 var err error // for mysql, when use bit, it returned \x01 - if col.SQLType.Name == Bit && + if col.SQLType.Name == core.Bit && strings.Contains(session.Engine.DriverName, "mysql") { if len(data) == 1 { x = int16(data[0]) @@ -2345,9 +2344,9 @@ func (session *Session) bytes2Value(col *Column, fieldValue *reflect.Value, data } // convert a field value of a struct to interface for put into db -func (session *Session) value2Interface(col *Column, fieldValue reflect.Value) (interface{}, error) { +func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Value) (interface{}, error) { if fieldValue.CanAddr() { - if fieldConvert, ok := fieldValue.Addr().Interface().(Conversion); ok { + if fieldConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok { data, err := fieldConvert.ToDB() if err != nil { return 0, err @@ -2384,19 +2383,19 @@ func (session *Session) value2Interface(col *Column, fieldValue reflect.Value) ( case reflect.Struct: if fieldType == reflect.TypeOf(c_TIME_DEFAULT) { t := fieldValue.Interface().(time.Time) - if session.Engine.dialect.DBType() == MSSQL { + if session.Engine.dialect.DBType() == core.MSSQL { if t.IsZero() { return nil, nil } } - if col.SQLType.Name == Time { + if col.SQLType.Name == core.Time { //s := fieldValue.Interface().(time.Time).Format("2006-01-02 15:04:05 -0700") s := fieldValue.Interface().(time.Time).Format(time.RFC3339) return s[11:19], nil - } else if col.SQLType.Name == Date { + } else if col.SQLType.Name == core.Date { return fieldValue.Interface().(time.Time).Format("2006-01-02"), nil - } else if col.SQLType.Name == TimeStampz { - if session.Engine.dialect.DBType() == MSSQL { + } else if col.SQLType.Name == core.TimeStampz { + if session.Engine.dialect.DBType() == core.MSSQL { tf := t.Format("2006-01-02T15:04:05.9999999Z07:00") return tf, nil } @@ -2470,7 +2469,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { } // -- - colNames, args, err := table.genCols(session, bean, false, false) + colNames, args, err := genCols(table, session, bean, false, false) if err != nil { return 0, err } @@ -2519,7 +2518,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { // for postgres, many of them didn't implement lastInsertId, so we should // implemented it ourself. - if session.Engine.DriverName != POSTGRES || table.AutoIncrement == "" { + if session.Engine.DriverName != core.POSTGRES || table.AutoIncrement == "" { res, err := session.exec(sqlStr, args...) if err != nil { return 0, err @@ -2527,7 +2526,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { handleAfterInsertProcessorFunc(bean) } - if table.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache { session.cacheInsert(session.Statement.TableName()) } @@ -2580,7 +2579,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { handleAfterInsertProcessorFunc(bean) } - if table.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(table.Type); cacher != nil && session.Statement.UseCache { session.cacheInsert(session.Statement.TableName()) } @@ -2659,9 +2658,9 @@ func (statement *Statement) convertUpdateSql(sqlStr string) (string, string) { //TODO: for postgres only, if any other database? var paraStr string - if statement.Engine.dialect.DBType() == POSTGRES { + if statement.Engine.dialect.DBType() == core.POSTGRES { paraStr = "$" - } else if statement.Engine.dialect.DBType() == MSSQL { + } else if statement.Engine.dialect.DBType() == core.MSSQL { paraStr = ":" } @@ -2687,7 +2686,7 @@ func (session *Session) cacheInsert(tables ...string) error { } table := session.Statement.RefTable - cacher := table.Cacher + cacher := session.Engine.getCacher(table.Type) for _, t := range tables { session.Engine.LogDebug("cache clear:", t) @@ -2707,7 +2706,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { return ErrCacheFailed } for _, filter := range session.Engine.Filters { - newsql = filter.Do(newsql, session) + newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable) } session.Engine.LogDebug("[xorm:cacheUpdate] new sql", oldhead, newsql) @@ -2721,7 +2720,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { } } table := session.Statement.RefTable - cacher := table.Cacher + cacher := session.Engine.getCacher(table.Type) tableName := session.Statement.TableName() session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:]) ids, err := getCacheSql(cacher, tableName, newsql, args[nStart:]) @@ -2777,7 +2776,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { return ErrCacheFailed } - if col, ok := table.Columns[strings.ToLower(colName)]; ok { + if col := table.GetColumn(colName); col != nil { fieldValue := col.ValueOf(bean) session.Engine.LogDebug("[xorm:cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) if col.IsVersion && session.Statement.checkVersion { @@ -2820,7 +2819,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 var colNames []string var args []interface{} - var table *Table + var table *core.Table // handle before update processors for _, closure := range session.beforeClosures { @@ -2840,7 +2839,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 colNames, args = buildConditions(session.Engine, table, bean, false, false, false, false, session.Statement.allUseBool, session.Statement.boolColumnMap) } else { - colNames, args, err = table.genCols(session, bean, true, true) + colNames, args, err = genCols(table, session, bean, true, true) if err != nil { return 0, err } @@ -2946,10 +2945,10 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 return 0, err } - if table.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(t); cacher != nil && session.Statement.UseCache { //session.cacheUpdate(sqlStr, args...) - table.Cacher.ClearIds(session.Statement.TableName()) - table.Cacher.ClearBeans(session.Statement.TableName()) + cacher.ClearIds(session.Statement.TableName()) + cacher.ClearBeans(session.Statement.TableName()) } // handle after update processors @@ -2990,7 +2989,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { } for _, filter := range session.Engine.Filters { - sqlStr = filter.Do(sqlStr, session) + sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } newsql := session.Statement.convertIdSql(sqlStr) @@ -2998,7 +2997,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { return ErrCacheFailed } - cacher := session.Statement.RefTable.Cacher + cacher := session.Engine.getCacher(session.Statement.RefTable.Type) tableName := session.Statement.TableName() ids, err := getCacheSql(cacher, tableName, newsql, args) if err != nil { @@ -3090,7 +3089,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { args = append(session.Statement.Params, args...) - if table.Cacher != nil && session.Statement.UseCache { + if cacher := session.Engine.getCacher(session.Statement.RefTable.Type); cacher != nil && session.Statement.UseCache { session.cacheDelete(sqlStr, args...) } diff --git a/statement.go b/statement.go index ab6ecd62..e3eec380 100644 --- a/statement.go +++ b/statement.go @@ -8,11 +8,13 @@ import ( "encoding/json" "strings" "time" + + "github.com/lunny/xorm/core" ) // statement save all the sql info for executing SQL type Statement struct { - RefTable *Table + RefTable *core.Table Engine *Engine Start int LimitN int @@ -64,7 +66,7 @@ func (statement *Statement) Init() { statement.RawSQL = "" statement.RawParams = make([]interface{}, 0) statement.BeanArgs = make([]interface{}, 0) - statement.UseCache = statement.Engine.UseCache + statement.UseCache = true statement.UseAutoTime = true statement.IsDistinct = false statement.allUseBool = false @@ -237,13 +239,13 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { }*/ // Auto generating conditions according a struct -func buildConditions(engine *Engine, table *Table, bean interface{}, +func buildConditions(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, includeAutoIncr bool, allUseBool bool, boolColumnMap map[string]bool) ([]string, []interface{}) { colNames := make([]string, 0) var args = make([]interface{}, 0) - for _, col := range table.Columns { + for _, col := range table.Columns() { if !includeVersion && col.IsVersion { continue } @@ -255,7 +257,7 @@ func buildConditions(engine *Engine, table *Table, bean interface{}, } // //fmt.Println(engine.dialect.DBType(), Text) - if engine.dialect.DBType() == MSSQL && col.SQLType.Name == Text { + if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { continue } fieldValue := col.ValueOf(bean) @@ -323,10 +325,10 @@ func buildConditions(engine *Engine, table *Table, bean interface{}, continue } var str string - if col.SQLType.Name == Time { + if col.SQLType.Name == core.Time { s := t.UTC().Format("2006-01-02 15:04:05") val = s[11:19] - } else if col.SQLType.Name == Date { + } else if col.SQLType.Name == core.Date { str = t.Format("2006-01-02") val = str } else { @@ -584,7 +586,7 @@ func (statement *Statement) Having(conditions string) *Statement { func (statement *Statement) genColumnStr() string { table := statement.RefTable colNames := make([]string, 0) - for _, col := range table.Columns { + for _, col := range table.Columns() { if statement.OmitStr != "" { if _, ok := statement.columnMap[col.Name]; ok { continue @@ -599,54 +601,8 @@ func (statement *Statement) genColumnStr() string { } func (statement *Statement) genCreateTableSQL() string { - var sql string - if statement.Engine.dialect.DBType() == MSSQL { - sql = "IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = '" + statement.TableName() + "' ) CREATE TABLE" - } else { - sql = "CREATE TABLE IF NOT EXISTS " - } - sql += statement.Engine.Quote(statement.TableName()) + " (" - - pkList := []string{} - - for _, colName := range statement.RefTable.ColumnsSeq { - col := statement.RefTable.Columns[strings.ToLower(colName)] - if col.IsPrimaryKey { - pkList = append(pkList, col.Name) - } - } - - statement.Engine.LogDebug("len:", len(pkList)) - for _, colName := range statement.RefTable.ColumnsSeq { - col := statement.RefTable.Columns[strings.ToLower(colName)] - if col.IsPrimaryKey && len(pkList) == 1 { - sql += col.String(statement.Engine.dialect) - } else { - sql += col.stringNoPk(statement.Engine.dialect) - } - sql = strings.TrimSpace(sql) - sql += ", " - } - - if len(pkList) > 1 { - sql += "PRIMARY KEY ( " - sql += strings.Join(pkList, ",") - sql += " ), " - } - - sql = sql[:len(sql)-2] + ")" - if statement.Engine.dialect.SupportEngine() && statement.StoreEngine != "" { - sql += " ENGINE=" + statement.StoreEngine - } - if statement.Engine.dialect.SupportCharset() { - if statement.Charset != "" { - sql += " DEFAULT CHARSET " + statement.Charset - } else if statement.Engine.dialect.URI().charset != "" { - sql += " DEFAULT CHARSET " + statement.Engine.dialect.URI().charset - } - } - sql += ";" - return sql + return statement.Engine.dialect.CreateTableSql(statement.RefTable, statement.AltTableName, + statement.StoreEngine, statement.Charset) } func indexName(tableName, idxName string) string { @@ -658,7 +614,7 @@ func (s *Statement) genIndexSQL() []string { tbName := s.TableName() quote := s.Engine.Quote for idxName, index := range s.RefTable.Indexes { - if index.Type == IndexType { + 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(",")))) sqls = append(sqls, sql) @@ -676,7 +632,7 @@ func (s *Statement) genUniqueSQL() []string { tbName := s.TableName() quote := s.Engine.Quote for idxName, unique := range s.RefTable.Indexes { - if unique.Type == UniqueType { + if unique.Type == core.UniqueType { sql := fmt.Sprintf("CREATE UNIQUE INDEX %v ON %v (%v);", quote(uniqueName(tbName, idxName)), quote(tbName), quote(strings.Join(unique.Cols, quote(",")))) sqls = append(sqls, sql) @@ -689,9 +645,9 @@ func (s *Statement) genDelIndexSQL() []string { var sqls []string = make([]string, 0) for idxName, index := range s.RefTable.Indexes { var rIdxName string - if index.Type == UniqueType { + if index.Type == core.UniqueType { rIdxName = uniqueName(s.TableName(), idxName) - } else if index.Type == IndexType { + } else if index.Type == core.IndexType { rIdxName = indexName(s.TableName(), idxName) } sql := fmt.Sprintf("DROP INDEX %v", s.Engine.Quote(rIdxName)) @@ -704,7 +660,7 @@ func (s *Statement) genDelIndexSQL() []string { } func (s *Statement) genDropSQL() string { - if s.Engine.dialect.DBType() == MSSQL { + if s.Engine.dialect.DBType() == core.MSSQL { return "IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'" + s.TableName() + "') and OBJECTPROPERTY(id, N'IsUserTable') = 1) " + "DROP TABLE " + s.Engine.Quote(s.TableName()) + ";" @@ -731,7 +687,7 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) return statement.genSelectSql(columnStr), append(statement.Params, statement.BeanArgs...) } -func (s *Statement) genAddColumnStr(col *Column) (string, []interface{}) { +func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { quote := s.Engine.Quote sql := fmt.Sprintf("ALTER TABLE %v ADD COLUMN %v;", quote(s.TableName()), col.String(s.Engine.dialect)) @@ -804,13 +760,14 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { if statement.OrderStr != "" { a = fmt.Sprintf("%v ORDER BY %v", a, statement.OrderStr) } - if statement.Engine.dialect.DBType() != MSSQL { + if statement.Engine.dialect.DBType() != core.MSSQL { if statement.Start > 0 { a = fmt.Sprintf("%v LIMIT %v OFFSET %v", a, statement.LimitN, statement.Start) } else if statement.LimitN > 0 { a = fmt.Sprintf("%v LIMIT %v", a, statement.LimitN) } } else { + //TODO: for mssql, should handler limit. /*SELECT * FROM ( SELECT *, ROW_NUMBER() OVER (ORDER BY id desc) as row FROM "userinfo" ) a WHERE row > [start] and row <= [start+limit] order by id desc*/ @@ -823,11 +780,12 @@ func (statement *Statement) processIdParam() { if statement.IdParam != nil { i := 0 - colCnt := len(statement.RefTable.ColumnsSeq) + columns := statement.RefTable.ColumnsSeq() + colCnt := len(columns) for _, elem := range *(statement.IdParam) { for ; i < colCnt; i++ { - colName := statement.RefTable.ColumnsSeq[i] - col := statement.RefTable.Columns[strings.ToLower(colName)] + colName := columns[i] + col := statement.RefTable.GetColumn(colName) if col.IsPrimaryKey { statement.And(fmt.Sprintf("%v=?", col.Name), elem) i++ @@ -840,8 +798,8 @@ func (statement *Statement) processIdParam() { // as empty string for now, so this will result sql exec failed instead of unexpected // false update/delete for ; i < colCnt; i++ { - colName := statement.RefTable.ColumnsSeq[i] - col := statement.RefTable.Columns[strings.ToLower(colName)] + colName := columns[i] + col := statement.RefTable.GetColumn(colName) if col.IsPrimaryKey { statement.And(fmt.Sprintf("%v=?", col.Name), "") } diff --git a/table.go b/table.go index a1ae7cdc..c7aca369 100644 --- a/table.go +++ b/table.go @@ -2,120 +2,11 @@ package xorm import ( "reflect" - "sort" - "strings" "time" + + "github.com/lunny/xorm/core" ) -// xorm SQL types -type SQLType struct { - Name string - DefaultLength int - DefaultLength2 int -} - -func (s *SQLType) IsText() bool { - return s.Name == Char || s.Name == Varchar || s.Name == TinyText || - s.Name == Text || s.Name == MediumText || s.Name == LongText -} - -func (s *SQLType) IsBlob() bool { - return (s.Name == TinyBlob) || (s.Name == Blob) || - s.Name == MediumBlob || s.Name == LongBlob || - s.Name == Binary || s.Name == VarBinary || s.Name == Bytea -} - -const () - -var ( - Bit = "BIT" - TinyInt = "TINYINT" - SmallInt = "SMALLINT" - MediumInt = "MEDIUMINT" - Int = "INT" - Integer = "INTEGER" - BigInt = "BIGINT" - - Char = "CHAR" - Varchar = "VARCHAR" - TinyText = "TINYTEXT" - Text = "TEXT" - MediumText = "MEDIUMTEXT" - LongText = "LONGTEXT" - - Date = "DATE" - DateTime = "DATETIME" - Time = "TIME" - TimeStamp = "TIMESTAMP" - TimeStampz = "TIMESTAMPZ" - - Decimal = "DECIMAL" - Numeric = "NUMERIC" - - Real = "REAL" - Float = "FLOAT" - Double = "DOUBLE" - - Binary = "BINARY" - VarBinary = "VARBINARY" - TinyBlob = "TINYBLOB" - Blob = "BLOB" - MediumBlob = "MEDIUMBLOB" - LongBlob = "LONGBLOB" - Bytea = "BYTEA" - - Bool = "BOOL" - - Serial = "SERIAL" - BigSerial = "BIGSERIAL" - - sqlTypes = map[string]bool{ - Bit: true, - TinyInt: true, - SmallInt: true, - MediumInt: true, - Int: true, - Integer: true, - BigInt: true, - - Char: true, - Varchar: true, - TinyText: true, - Text: true, - MediumText: true, - LongText: true, - - Date: true, - DateTime: true, - Time: true, - TimeStamp: true, - TimeStampz: true, - - Decimal: true, - Numeric: true, - - Binary: true, - VarBinary: true, - Real: true, - Float: true, - Double: true, - TinyBlob: true, - Blob: true, - MediumBlob: true, - LongBlob: true, - Bytea: true, - - Bool: true, - - Serial: true, - BigSerial: true, - } - - intTypes = sort.StringSlice{"*int", "*int16", "*int32", "*int8"} - uintTypes = sort.StringSlice{"*uint", "*uint16", "*uint32", "*uint8"} -) - -// !nashtsai! treat following var as interal const values, these are used for reflect.TypeOf comparision var ( c_EMPTY_STRING string c_BOOL_DEFAULT bool @@ -137,280 +28,17 @@ var ( c_TIME_DEFAULT time.Time ) -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: - st = SQLType{Int, 0, 0} - case reflect.Int64, reflect.Uint64: - st = SQLType{BigInt, 0, 0} - case reflect.Float32: - st = SQLType{Float, 0, 0} - case reflect.Float64: - st = SQLType{Double, 0, 0} - case reflect.Complex64, reflect.Complex128: - st = SQLType{Varchar, 64, 0} - case reflect.Array, reflect.Slice, reflect.Map: - if t.Elem() == reflect.TypeOf(c_BYTE_DEFAULT) { - st = SQLType{Blob, 0, 0} - } else { - st = SQLType{Text, 0, 0} - } - case reflect.Bool: - st = SQLType{Bool, 0, 0} - case reflect.String: - st = SQLType{Varchar, 255, 0} - case reflect.Struct: - if t == reflect.TypeOf(c_TIME_DEFAULT) { - st = SQLType{DateTime, 0, 0} - } else { - // TODO need to handle association struct - st = SQLType{Text, 0, 0} - } - case reflect.Ptr: - st, _ = ptrType2SQLType(t) - 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) - switch name { - case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, Serial: - return reflect.TypeOf(1) - case BigInt, BigSerial: - return reflect.TypeOf(int64(1)) - case Float, Real: - return reflect.TypeOf(float32(1)) - case Double: - return reflect.TypeOf(float64(1)) - case Char, Varchar, TinyText, Text, MediumText, LongText: - return reflect.TypeOf("") - case TinyBlob, Blob, LongBlob, Bytea, Binary, MediumBlob, VarBinary: - return reflect.TypeOf([]byte{}) - case Bool: - return reflect.TypeOf(true) - case DateTime, Date, Time, TimeStamp, TimeStampz: - return reflect.TypeOf(c_TIME_DEFAULT) - case Decimal, Numeric: - return reflect.TypeOf("") - default: - return reflect.TypeOf("") - } -} - -const ( - IndexType = iota + 1 - UniqueType -) - -// database index -type Index struct { - Name string - Type int - Cols []string -} - -// add columns which will be composite index -func (index *Index) AddColumn(cols ...string) { - for _, col := range cols { - index.Cols = append(index.Cols, col) - } -} - -// new an index -func NewIndex(name string, indexType int) *Index { - return &Index{name, indexType, make([]string, 0)} -} - const ( TWOSIDES = iota + 1 ONLYTODB ONLYFROMDB ) -// database column -type Column struct { - Name string - FieldName string - SQLType SQLType - Length int - Length2 int - Nullable bool - Default string - Indexes map[string]bool - IsPrimaryKey bool - IsAutoIncrement bool - MapType int - IsCreated bool - IsUpdated bool - IsCascade bool - IsVersion bool -} - -// generate column description string according dialect -func (col *Column) String(d dialect) string { - sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " - - sql += d.SqlType(col) + " " - - if col.IsPrimaryKey { - sql += "PRIMARY KEY " - if col.IsAutoIncrement { - sql += d.AutoIncrStr() + " " - } - } - - if col.Nullable { - sql += "NULL " - } else { - sql += "NOT NULL " - } - - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - - return sql -} - -func (col *Column) stringNoPk(d dialect) string { - sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " - - sql += d.SqlType(col) + " " - - if col.Nullable { - sql += "NULL " - } else { - sql += "NOT NULL " - } - - if col.Default != "" { - sql += "DEFAULT " + col.Default + " " - } - - return sql -} - -// return col's filed of struct's value -func (col *Column) ValueOf(bean interface{}) reflect.Value { - var fieldValue reflect.Value - if strings.Contains(col.FieldName, ".") { - fields := strings.Split(col.FieldName, ".") - if len(fields) > 2 { - return reflect.ValueOf(nil) - } - - fieldValue = reflect.Indirect(reflect.ValueOf(bean)).FieldByName(fields[0]) - fieldValue = fieldValue.FieldByName(fields[1]) - } else { - fieldValue = reflect.Indirect(reflect.ValueOf(bean)).FieldByName(col.FieldName) - } - return fieldValue -} - -// database table -type Table struct { - Name string - Type reflect.Type - ColumnsSeq []string - Columns map[string]*Column - Indexes map[string]*Index - PrimaryKeys []string - AutoIncrement string - Created map[string]bool - Updated string - Version string - Cacher Cacher -} - -/* -func NewTable(name string, t reflect.Type) *Table { - return &Table{Name: name, Type: t, - ColumnsSeq: make([]string, 0), - Columns: make(map[string]*Column), - Indexes: make(map[string]*Index), - Created: make(map[string]bool), - } -}*/ - -// if has primary key, return column -func (table *Table) PKColumns() []*Column { - columns := make([]*Column, 0) - for _, name := range table.PrimaryKeys { - columns = append(columns, table.Columns[strings.ToLower(name)]) - } - return columns -} - -func (table *Table) AutoIncrColumn() *Column { - return table.Columns[strings.ToLower(table.AutoIncrement)] -} - -func (table *Table) VersionColumn() *Column { - return table.Columns[strings.ToLower(table.Version)] -} - -// add a column to table -func (table *Table) AddColumn(col *Column) { - table.ColumnsSeq = append(table.ColumnsSeq, col.Name) - table.Columns[strings.ToLower(col.Name)] = col - if col.IsPrimaryKey { - table.PrimaryKeys = append(table.PrimaryKeys, col.Name) - } - if col.IsAutoIncrement { - table.AutoIncrement = col.Name - } - if col.IsCreated { - table.Created[col.Name] = true - } - if col.IsUpdated { - table.Updated = col.Name - } - if col.IsVersion { - table.Version = col.Name - } -} - -// add an index or an unique to table -func (table *Table) AddIndex(index *Index) { - table.Indexes[index.Name] = index -} - -func (table *Table) genCols(session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { +func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { colNames := make([]string, 0) args := make([]interface{}, 0) - for _, col := range table.Columns { + for _, col := range table.Columns() { if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { if _, ok := session.Statement.columnMap[col.Name]; !ok { continue @@ -469,10 +97,3 @@ func (table *Table) genCols(session *Session, bean interface{}, useCol bool, inc } return colNames, args, nil } - -// Conversion is an interface. A type implements Conversion will according -// the custom method to fill into database and retrieve from database. -type Conversion interface { - FromDB([]byte) error - ToDB() ([]byte, error) -} diff --git a/tests/mysql_ddl.sql b/tests/mysql_ddl.sql index 53c9f316..92c95829 100644 --- a/tests/mysql_ddl.sql +++ b/tests/mysql_ddl.sql @@ -1,4 +1,4 @@ ---DROP DATABASE xorm_test; ---DROP DATABASE xorm_test2; -CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci; -CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci; +--DROP DATABASE xorm_test; +--DROP DATABASE xorm_test2; +CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci; +CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci; diff --git a/xorm.go b/xorm.go index bff3f1bc..5826db60 100644 --- a/xorm.go +++ b/xorm.go @@ -7,6 +7,10 @@ import ( "reflect" "runtime" "sync" + + "github.com/lunny/xorm/core" + _ "github.com/lunny/xorm/dialects" + _ "github.com/lunny/xorm/drivers" ) const ( @@ -20,39 +24,38 @@ func close(engine *Engine) { // new a db manager according to the parameter. Currently support four // drivers func NewEngine(driverName string, dataSourceName string) (*Engine, error) { - engine := &Engine{DriverName: driverName, - DataSourceName: dataSourceName, Filters: make([]Filter, 0)} - engine.SetMapper(SnakeMapper{}) - - if driverName == SQLITE { - engine.dialect = &sqlite3{} - } else if driverName == MYSQL { - engine.dialect = &mysql{} - } else if driverName == POSTGRES { - engine.dialect = &postgres{} - engine.Filters = append(engine.Filters, &PgSeqFilter{}) - engine.Filters = append(engine.Filters, &QuoteFilter{}) - } else if driverName == MYMYSQL { - engine.dialect = &mymysql{} - } else if driverName == "odbc" { - engine.dialect = &mssql{quoteFilter: &QuoteFilter{}} - engine.Filters = append(engine.Filters, &QuoteFilter{}) - } else if driverName == ORACLE_OCI { - engine.dialect = &oracle{} - engine.Filters = append(engine.Filters, &QuoteFilter{}) - } else { + driver := core.QueryDriver(driverName) + if driver == nil { return nil, errors.New(fmt.Sprintf("Unsupported driver name: %v", driverName)) } - err := engine.dialect.Init(driverName, dataSourceName) + + uri, err := driver.Parse(driverName, dataSourceName) if err != nil { return nil, err } - engine.Tables = make(map[reflect.Type]*Table) + dialect := core.QueryDialect(uri.DbType) + if dialect == nil { + return nil, errors.New(fmt.Sprintf("Unsupported dialect type: %v", uri.DbType)) + } + + err = dialect.Init(uri, driverName, dataSourceName) + if err != nil { + return nil, err + } + + engine := &Engine{DriverName: driverName, + DataSourceName: dataSourceName, dialect: dialect, + tableCachers: make(map[reflect.Type]Cacher)} + + engine.SetMapper(SnakeMapper{}) + + engine.Filters = dialect.Filters() + + engine.Tables = make(map[reflect.Type]*core.Table) engine.mutex = &sync.Mutex{} engine.TagIdentifier = "xorm" - engine.Filters = append(engine.Filters, &IdFilter{}) engine.Logger = os.Stdout //engine.Pool = NewSimpleConnectPool() diff --git a/xorm/.gopmfile b/xorm/.gopmfile index f5cdbb0c..a77947be 100644 --- a/xorm/.gopmfile +++ b/xorm/.gopmfile @@ -1,2 +1,2 @@ -[deps] +[deps] github.com/lunny/xorm=../ \ No newline at end of file diff --git a/xorm/c++.go b/xorm/c++.go index 59f84b8f..ed094060 100644 --- a/xorm/c++.go +++ b/xorm/c++.go @@ -1,65 +1,65 @@ package main import ( - //"fmt" - "github.com/lunny/xorm" - "strings" - "text/template" + //"fmt" + "github.com/lunny/xorm" + "strings" + "text/template" ) var ( - CPlusTmpl LangTmpl = LangTmpl{ - template.FuncMap{"Mapper": mapper.Table2Obj, - "Type": cPlusTypeStr, - "UnTitle": unTitle, - }, - nil, - genCPlusImports, - } + CPlusTmpl LangTmpl = LangTmpl{ + template.FuncMap{"Mapper": mapper.Table2Obj, + "Type": cPlusTypeStr, + "UnTitle": unTitle, + }, + nil, + genCPlusImports, + } ) func cPlusTypeStr(col *xorm.Column) string { - tp := col.SQLType - name := strings.ToUpper(tp.Name) - switch name { - case xorm.Bit, xorm.TinyInt, xorm.SmallInt, xorm.MediumInt, xorm.Int, xorm.Integer, xorm.Serial: - return "int" - case xorm.BigInt, xorm.BigSerial: - return "__int64" - case xorm.Char, xorm.Varchar, xorm.TinyText, xorm.Text, xorm.MediumText, xorm.LongText: - return "tstring" - case xorm.Date, xorm.DateTime, xorm.Time, xorm.TimeStamp: - return "time_t" - case xorm.Decimal, xorm.Numeric: - return "tstring" - case xorm.Real, xorm.Float: - return "float" - case xorm.Double: - return "double" - case xorm.TinyBlob, xorm.Blob, xorm.MediumBlob, xorm.LongBlob, xorm.Bytea: - return "tstring" - case xorm.Bool: - return "bool" - default: - return "tstring" - } - return "" + tp := col.SQLType + name := strings.ToUpper(tp.Name) + switch name { + case xorm.Bit, xorm.TinyInt, xorm.SmallInt, xorm.MediumInt, xorm.Int, xorm.Integer, xorm.Serial: + return "int" + case xorm.BigInt, xorm.BigSerial: + return "__int64" + case xorm.Char, xorm.Varchar, xorm.TinyText, xorm.Text, xorm.MediumText, xorm.LongText: + return "tstring" + case xorm.Date, xorm.DateTime, xorm.Time, xorm.TimeStamp: + return "time_t" + case xorm.Decimal, xorm.Numeric: + return "tstring" + case xorm.Real, xorm.Float: + return "float" + case xorm.Double: + return "double" + case xorm.TinyBlob, xorm.Blob, xorm.MediumBlob, xorm.LongBlob, xorm.Bytea: + return "tstring" + case xorm.Bool: + return "bool" + default: + return "tstring" + } + return "" } func genCPlusImports(tables []*xorm.Table) map[string]string { - imports := make(map[string]string) + imports := make(map[string]string) - for _, table := range tables { - for _, col := range table.Columns { - switch cPlusTypeStr(col) { - case "time_t": - imports[``] = `` - case "tstring": - imports[""] = "" - //case "__int64": - // imports[""] = "" - } - } - } - return imports + for _, table := range tables { + for _, col := range table.Columns { + switch cPlusTypeStr(col) { + case "time_t": + imports[``] = `` + case "tstring": + imports[""] = "" + //case "__int64": + // imports[""] = "" + } + } + } + return imports } diff --git a/xorm/cmd.go b/xorm/cmd.go index eec39e59..cb326f15 100644 --- a/xorm/cmd.go +++ b/xorm/cmd.go @@ -1,78 +1,78 @@ package main import ( - "fmt" - "os" - "strings" + "fmt" + "os" + "strings" ) // A Command is an implementation of a go command // like go build or go fix. type Command struct { - // Run runs the command. - // The args are the arguments after the command name. - Run func(cmd *Command, args []string) + // Run runs the command. + // The args are the arguments after the command name. + Run func(cmd *Command, args []string) - // UsageLine is the one-line usage message. - // The first word in the line is taken to be the command name. - UsageLine string + // UsageLine is the one-line usage message. + // The first word in the line is taken to be the command name. + UsageLine string - // Short is the short description shown in the 'go help' output. - Short string + // Short is the short description shown in the 'go help' output. + Short string - // Long is the long message shown in the 'go help ' output. - Long string + // Long is the long message shown in the 'go help ' output. + Long string - // Flag is a set of flags specific to this command. - Flags map[string]bool + // Flag is a set of flags specific to this command. + Flags map[string]bool } // Name returns the command's name: the first word in the usage line. func (c *Command) Name() string { - name := c.UsageLine - i := strings.Index(name, " ") - if i >= 0 { - name = name[:i] - } - return name + name := c.UsageLine + i := strings.Index(name, " ") + if i >= 0 { + name = name[:i] + } + return name } func (c *Command) Usage() { - fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) - fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) - os.Exit(2) + fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) + fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) + os.Exit(2) } // Runnable reports whether the command can be run; otherwise // it is a documentation pseudo-command such as importpath. func (c *Command) Runnable() bool { - return c.Run != nil + return c.Run != nil } // checkFlags checks if the flag exists with correct format. func checkFlags(flags map[string]bool, args []string, print func(string)) int { - num := 0 // Number of valid flags, use to cut out. - for i, f := range args { - // Check flag prefix '-'. - if !strings.HasPrefix(f, "-") { - // Not a flag, finish check process. - break - } + num := 0 // Number of valid flags, use to cut out. + for i, f := range args { + // Check flag prefix '-'. + if !strings.HasPrefix(f, "-") { + // Not a flag, finish check process. + break + } - // Check if it a valid flag. - if v, ok := flags[f]; ok { - flags[f] = !v - if !v { - print(f) - } else { - fmt.Println("DISABLE: " + f) - } - } else { - fmt.Printf("[ERRO] Unknown flag: %s.\n", f) - return -1 - } - num = i + 1 - } + // Check if it a valid flag. + if v, ok := flags[f]; ok { + flags[f] = !v + if !v { + print(f) + } else { + fmt.Println("DISABLE: " + f) + } + } else { + fmt.Printf("[ERRO] Unknown flag: %s.\n", f) + return -1 + } + num = i + 1 + } - return num + return num } diff --git a/xorm/go.go b/xorm/go.go index 682f6b0b..3f46322d 100644 --- a/xorm/go.go +++ b/xorm/go.go @@ -1,263 +1,263 @@ package main import ( - "errors" - "fmt" - "github.com/lunny/xorm" - "go/format" - "reflect" - "strings" - "text/template" + "errors" + "fmt" + "github.com/lunny/xorm" + "go/format" + "reflect" + "strings" + "text/template" ) var ( - GoLangTmpl LangTmpl = LangTmpl{ - template.FuncMap{"Mapper": mapper.Table2Obj, - "Type": typestring, - "Tag": tag, - "UnTitle": unTitle, - "gt": gt, - "getCol": getCol, - }, - formatGo, - genGoImports, - } + GoLangTmpl LangTmpl = LangTmpl{ + template.FuncMap{"Mapper": mapper.Table2Obj, + "Type": typestring, + "Tag": tag, + "UnTitle": unTitle, + "gt": gt, + "getCol": getCol, + }, + formatGo, + genGoImports, + } ) var ( - errBadComparisonType = errors.New("invalid type for comparison") - errBadComparison = errors.New("incompatible types for comparison") - errNoComparison = errors.New("missing argument for comparison") + errBadComparisonType = errors.New("invalid type for comparison") + errBadComparison = errors.New("incompatible types for comparison") + errNoComparison = errors.New("missing argument for comparison") ) type kind int const ( - invalidKind kind = iota - boolKind - complexKind - intKind - floatKind - integerKind - stringKind - uintKind + invalidKind kind = iota + boolKind + complexKind + intKind + floatKind + integerKind + stringKind + uintKind ) func basicKind(v reflect.Value) (kind, error) { - switch v.Kind() { - case reflect.Bool: - return boolKind, nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intKind, nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return uintKind, nil - case reflect.Float32, reflect.Float64: - return floatKind, nil - case reflect.Complex64, reflect.Complex128: - return complexKind, nil - case reflect.String: - return stringKind, nil - } - return invalidKind, errBadComparisonType + switch v.Kind() { + case reflect.Bool: + return boolKind, nil + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return intKind, nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return uintKind, nil + case reflect.Float32, reflect.Float64: + return floatKind, nil + case reflect.Complex64, reflect.Complex128: + return complexKind, nil + case reflect.String: + return stringKind, nil + } + return invalidKind, errBadComparisonType } // eq evaluates the comparison a == b || a == c || ... func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - if len(arg2) == 0 { - return false, errNoComparison - } - for _, arg := range arg2 { - v2 := reflect.ValueOf(arg) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind: - truth = v1.Bool() == v2.Bool() - case complexKind: - truth = v1.Complex() == v2.Complex() - case floatKind: - truth = v1.Float() == v2.Float() - case intKind: - truth = v1.Int() == v2.Int() - case stringKind: - truth = v1.String() == v2.String() - case uintKind: - truth = v1.Uint() == v2.Uint() - default: - panic("invalid kind") - } - if truth { - return true, nil - } - } - return false, nil + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + if len(arg2) == 0 { + return false, errNoComparison + } + for _, arg := range arg2 { + v2 := reflect.ValueOf(arg) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind: + truth = v1.Bool() == v2.Bool() + case complexKind: + truth = v1.Complex() == v2.Complex() + case floatKind: + truth = v1.Float() == v2.Float() + case intKind: + truth = v1.Int() == v2.Int() + case stringKind: + truth = v1.String() == v2.String() + case uintKind: + truth = v1.Uint() == v2.Uint() + default: + panic("invalid kind") + } + if truth { + return true, nil + } + } + return false, nil } // lt evaluates the comparison a < b. func lt(arg1, arg2 interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - v2 := reflect.ValueOf(arg2) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") - } - return truth, nil + v1 := reflect.ValueOf(arg1) + k1, err := basicKind(v1) + if err != nil { + return false, err + } + v2 := reflect.ValueOf(arg2) + k2, err := basicKind(v2) + if err != nil { + return false, err + } + if k1 != k2 { + return false, errBadComparison + } + truth := false + switch k1 { + case boolKind, complexKind: + return false, errBadComparisonType + case floatKind: + truth = v1.Float() < v2.Float() + case intKind: + truth = v1.Int() < v2.Int() + case stringKind: + truth = v1.String() < v2.String() + case uintKind: + truth = v1.Uint() < v2.Uint() + default: + panic("invalid kind") + } + return truth, nil } // le evaluates the comparison <= b. func le(arg1, arg2 interface{}) (bool, error) { - // <= is < or ==. - lessThan, err := lt(arg1, arg2) - if lessThan || err != nil { - return lessThan, err - } - return eq(arg1, arg2) + // <= is < or ==. + lessThan, err := lt(arg1, arg2) + if lessThan || err != nil { + return lessThan, err + } + return eq(arg1, arg2) } // gt evaluates the comparison a > b. func gt(arg1, arg2 interface{}) (bool, error) { - // > is the inverse of <=. - lessOrEqual, err := le(arg1, arg2) - if err != nil { - return false, err - } - return !lessOrEqual, nil + // > is the inverse of <=. + lessOrEqual, err := le(arg1, arg2) + if err != nil { + return false, err + } + return !lessOrEqual, nil } func getCol(cols map[string]*xorm.Column, name string) *xorm.Column { - return cols[name] + return cols[name] } func formatGo(src string) (string, error) { - source, err := format.Source([]byte(src)) - if err != nil { - return "", err - } - return string(source), nil + source, err := format.Source([]byte(src)) + if err != nil { + return "", err + } + return string(source), nil } func genGoImports(tables []*xorm.Table) map[string]string { - imports := make(map[string]string) + imports := make(map[string]string) - for _, table := range tables { - for _, col := range table.Columns { - if typestring(col) == "time.Time" { - imports["time"] = "time" - } - } - } - return imports + for _, table := range tables { + for _, col := range table.Columns { + if typestring(col) == "time.Time" { + imports["time"] = "time" + } + } + } + return imports } func typestring(col *xorm.Column) string { - st := col.SQLType - /*if col.IsPrimaryKey { - return "int64" - }*/ - t := xorm.SQLType2Type(st) - s := t.String() - if s == "[]uint8" { - return "[]byte" - } - return s + st := col.SQLType + /*if col.IsPrimaryKey { + return "int64" + }*/ + t := xorm.SQLType2Type(st) + s := t.String() + if s == "[]uint8" { + return "[]byte" + } + return s } func tag(table *xorm.Table, col *xorm.Column) string { - isNameId := (mapper.Table2Obj(col.Name) == "Id") - isIdPk := isNameId && typestring(col) == "int64" + isNameId := (mapper.Table2Obj(col.Name) == "Id") + isIdPk := isNameId && typestring(col) == "int64" - res := make([]string, 0) - if !col.Nullable { - if !isIdPk { - res = append(res, "not null") - } - } - if col.IsPrimaryKey { - if !isIdPk { - res = append(res, "pk") - } - } - if col.Default != "" { - res = append(res, "default "+col.Default) - } - if col.IsAutoIncrement { - if !isIdPk { - res = append(res, "autoincr") - } - } - if col.IsCreated { - res = append(res, "created") - } - if col.IsUpdated { - res = append(res, "updated") - } - for name, _ := range col.Indexes { - index := table.Indexes[name] - var uistr string - if index.Type == xorm.UniqueType { - uistr = "unique" - } else if index.Type == xorm.IndexType { - uistr = "index" - } - if len(index.Cols) > 1 { - uistr += "(" + index.Name + ")" - } - res = append(res, uistr) - } + res := make([]string, 0) + if !col.Nullable { + if !isIdPk { + res = append(res, "not null") + } + } + if col.IsPrimaryKey { + if !isIdPk { + res = append(res, "pk") + } + } + if col.Default != "" { + res = append(res, "default "+col.Default) + } + if col.IsAutoIncrement { + if !isIdPk { + res = append(res, "autoincr") + } + } + if col.IsCreated { + res = append(res, "created") + } + if col.IsUpdated { + res = append(res, "updated") + } + for name, _ := range col.Indexes { + index := table.Indexes[name] + var uistr string + if index.Type == xorm.UniqueType { + uistr = "unique" + } else if index.Type == xorm.IndexType { + uistr = "index" + } + if len(index.Cols) > 1 { + uistr += "(" + index.Name + ")" + } + res = append(res, uistr) + } - nstr := col.SQLType.Name - if col.Length != 0 { - if col.Length2 != 0 { - nstr += fmt.Sprintf("(%v, %v)", col.Length, col.Length2) - } else { - nstr += fmt.Sprintf("(%v)", col.Length) - } - } - res = append(res, nstr) + nstr := col.SQLType.Name + if col.Length != 0 { + if col.Length2 != 0 { + nstr += fmt.Sprintf("(%v, %v)", col.Length, col.Length2) + } else { + nstr += fmt.Sprintf("(%v)", col.Length) + } + } + res = append(res, nstr) - var tags []string - if genJson { - tags = append(tags, "json:\""+col.Name+"\"") - } - if len(res) > 0 { - tags = append(tags, "xorm:\""+strings.Join(res, " ")+"\"") - } - if len(tags) > 0 { - return "`" + strings.Join(tags, " ") + "`" - } else { - return "" - } + var tags []string + if genJson { + tags = append(tags, "json:\""+col.Name+"\"") + } + if len(res) > 0 { + tags = append(tags, "xorm:\""+strings.Join(res, " ")+"\"") + } + if len(tags) > 0 { + return "`" + strings.Join(tags, " ") + "`" + } else { + return "" + } } diff --git a/xorm/lang.go b/xorm/lang.go index 94076b48..17d09a98 100644 --- a/xorm/lang.go +++ b/xorm/lang.go @@ -1,51 +1,51 @@ package main import ( - "github.com/lunny/xorm" - "io/ioutil" - "strings" - "text/template" + "github.com/lunny/xorm" + "io/ioutil" + "strings" + "text/template" ) type LangTmpl struct { - Funcs template.FuncMap - Formater func(string) (string, error) - GenImports func([]*xorm.Table) map[string]string + Funcs template.FuncMap + Formater func(string) (string, error) + GenImports func([]*xorm.Table) map[string]string } var ( - mapper = &xorm.SnakeMapper{} - langTmpls = map[string]LangTmpl{ - "go": GoLangTmpl, - "c++": CPlusTmpl, - } + mapper = &xorm.SnakeMapper{} + langTmpls = map[string]LangTmpl{ + "go": GoLangTmpl, + "c++": CPlusTmpl, + } ) func loadConfig(f string) map[string]string { - bts, err := ioutil.ReadFile(f) - if err != nil { - return nil - } - configs := make(map[string]string) - lines := strings.Split(string(bts), "\n") - for _, line := range lines { - line = strings.TrimRight(line, "\r") - vs := strings.Split(line, "=") - if len(vs) == 2 { - configs[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1]) - } - } - return configs + bts, err := ioutil.ReadFile(f) + if err != nil { + return nil + } + configs := make(map[string]string) + lines := strings.Split(string(bts), "\n") + for _, line := range lines { + line = strings.TrimRight(line, "\r") + vs := strings.Split(line, "=") + if len(vs) == 2 { + configs[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1]) + } + } + return configs } func unTitle(src string) string { - if src == "" { - return "" - } + if src == "" { + return "" + } - if len(src) == 1 { - return strings.ToLower(string(src[0])) - } else { - return strings.ToLower(string(src[0])) + src[1:] - } + if len(src) == 1 { + return strings.ToLower(string(src[0])) + } else { + return strings.ToLower(string(src[0])) + src[1:] + } } diff --git a/xorm/reverse.go b/xorm/reverse.go index 17accfe5..9eae48d2 100644 --- a/xorm/reverse.go +++ b/xorm/reverse.go @@ -1,26 +1,26 @@ package main import ( - "bytes" - "fmt" - _ "github.com/bylevel/pq" - "github.com/dvirsky/go-pylog/logging" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - _ "github.com/ziutek/mymysql/godrv" - "io/ioutil" - "os" - "path" - "path/filepath" - "strconv" - "text/template" + "bytes" + "fmt" + _ "github.com/bylevel/pq" + "github.com/dvirsky/go-pylog/logging" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" + _ "github.com/ziutek/mymysql/godrv" + "io/ioutil" + "os" + "path" + "path/filepath" + "strconv" + "text/template" ) var CmdReverse = &Command{ - UsageLine: "reverse [-m] driverName datasourceName tmplPath [generatedPath]", - Short: "reverse a db to codes", - Long: ` + UsageLine: "reverse [-m] driverName datasourceName tmplPath [generatedPath]", + Short: "reverse a db to codes", + Long: ` according database's tables and columns to generate codes for Go, C++ and etc. -m Generated one go file for every table @@ -33,236 +33,236 @@ according database's tables and columns to generate codes for Go, C++ and etc. } func init() { - CmdReverse.Run = runReverse - CmdReverse.Flags = map[string]bool{ - "-s": false, - "-l": false, - } + CmdReverse.Run = runReverse + CmdReverse.Flags = map[string]bool{ + "-s": false, + "-l": false, + } } var ( - genJson bool = false + genJson bool = false ) func printReversePrompt(flag string) { } type Tmpl struct { - Tables []*xorm.Table - Imports map[string]string - Model string + Tables []*xorm.Table + Imports map[string]string + Model string } func dirExists(dir string) bool { - d, e := os.Stat(dir) - switch { - case e != nil: - return false - case !d.IsDir(): - return false - } + d, e := os.Stat(dir) + switch { + case e != nil: + return false + case !d.IsDir(): + return false + } - return true + return true } func runReverse(cmd *Command, args []string) { - num := checkFlags(cmd.Flags, args, printReversePrompt) - if num == -1 { - return - } - args = args[num:] + num := checkFlags(cmd.Flags, args, printReversePrompt) + if num == -1 { + return + } + args = args[num:] - if len(args) < 3 { - fmt.Println("params error, please see xorm help reverse") - return - } + if len(args) < 3 { + fmt.Println("params error, please see xorm help reverse") + return + } - var isMultiFile bool = true - if use, ok := cmd.Flags["-s"]; ok { - isMultiFile = !use - } + var isMultiFile bool = true + if use, ok := cmd.Flags["-s"]; ok { + isMultiFile = !use + } - curPath, err := os.Getwd() - if err != nil { - fmt.Println(curPath) - return - } + curPath, err := os.Getwd() + if err != nil { + fmt.Println(curPath) + return + } - var genDir string - var model string - if len(args) == 4 { + var genDir string + var model string + if len(args) == 4 { - genDir, err = filepath.Abs(args[3]) - if err != nil { - fmt.Println(err) - return - } - model = path.Base(genDir) - } else { - model = "model" - genDir = path.Join(curPath, model) - } + genDir, err = filepath.Abs(args[3]) + if err != nil { + fmt.Println(err) + return + } + model = path.Base(genDir) + } else { + model = "model" + genDir = path.Join(curPath, model) + } - dir, err := filepath.Abs(args[2]) - if err != nil { - logging.Error("%v", err) - return - } + dir, err := filepath.Abs(args[2]) + if err != nil { + logging.Error("%v", err) + return + } - if !dirExists(dir) { - logging.Error("Template %v path is not exist", dir) - return - } + if !dirExists(dir) { + logging.Error("Template %v path is not exist", dir) + return + } - var langTmpl LangTmpl - var ok bool - var lang string = "go" + var langTmpl LangTmpl + var ok bool + var lang string = "go" - cfgPath := path.Join(dir, "config") - info, err := os.Stat(cfgPath) - var configs map[string]string - if err == nil && !info.IsDir() { - configs = loadConfig(cfgPath) - if l, ok := configs["lang"]; ok { - lang = l - } - if j, ok := configs["genJson"]; ok { - genJson, err = strconv.ParseBool(j) - } - } + cfgPath := path.Join(dir, "config") + info, err := os.Stat(cfgPath) + var configs map[string]string + if err == nil && !info.IsDir() { + configs = loadConfig(cfgPath) + if l, ok := configs["lang"]; ok { + lang = l + } + if j, ok := configs["genJson"]; ok { + genJson, err = strconv.ParseBool(j) + } + } - if langTmpl, ok = langTmpls[lang]; !ok { - fmt.Println("Unsupported programing language", lang) - return - } + if langTmpl, ok = langTmpls[lang]; !ok { + fmt.Println("Unsupported programing language", lang) + return + } - os.MkdirAll(genDir, os.ModePerm) + os.MkdirAll(genDir, os.ModePerm) - Orm, err := xorm.NewEngine(args[0], args[1]) - if err != nil { - logging.Error("%v", err) - return - } + Orm, err := xorm.NewEngine(args[0], args[1]) + if err != nil { + logging.Error("%v", err) + return + } - tables, err := Orm.DBMetas() - if err != nil { - logging.Error("%v", err) - return - } + tables, err := Orm.DBMetas() + if err != nil { + logging.Error("%v", err) + return + } - filepath.Walk(dir, func(f string, info os.FileInfo, err error) error { - if info.IsDir() { - return nil - } + filepath.Walk(dir, func(f string, info os.FileInfo, err error) error { + if info.IsDir() { + return nil + } - if info.Name() == "config" { - return nil - } + if info.Name() == "config" { + return nil + } - bs, err := ioutil.ReadFile(f) - if err != nil { - logging.Error("%v", err) - return err - } + bs, err := ioutil.ReadFile(f) + if err != nil { + logging.Error("%v", err) + return err + } - t := template.New(f) - t.Funcs(langTmpl.Funcs) + t := template.New(f) + t.Funcs(langTmpl.Funcs) - tmpl, err := t.Parse(string(bs)) - if err != nil { - logging.Error("%v", err) - return err - } + tmpl, err := t.Parse(string(bs)) + if err != nil { + logging.Error("%v", err) + return err + } - var w *os.File - fileName := info.Name() - newFileName := fileName[:len(fileName)-4] - ext := path.Ext(newFileName) + var w *os.File + fileName := info.Name() + newFileName := fileName[:len(fileName)-4] + ext := path.Ext(newFileName) - if !isMultiFile { - w, err = os.OpenFile(path.Join(genDir, newFileName), os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - logging.Error("%v", err) - return err - } + if !isMultiFile { + w, err = os.OpenFile(path.Join(genDir, newFileName), os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + logging.Error("%v", err) + return err + } - imports := langTmpl.GenImports(tables) + imports := langTmpl.GenImports(tables) - tbls := make([]*xorm.Table, 0) - for _, table := range tables { - tbls = append(tbls, table) - } + tbls := make([]*xorm.Table, 0) + for _, table := range tables { + tbls = append(tbls, table) + } - newbytes := bytes.NewBufferString("") + newbytes := bytes.NewBufferString("") - t := &Tmpl{Tables: tbls, Imports: imports, Model: model} - err = tmpl.Execute(newbytes, t) - if err != nil { - logging.Error("%v", err) - return err - } + t := &Tmpl{Tables: tbls, Imports: imports, Model: model} + err = tmpl.Execute(newbytes, t) + if err != nil { + logging.Error("%v", err) + return err + } - tplcontent, err := ioutil.ReadAll(newbytes) - if err != nil { - logging.Error("%v", err) - return err - } - var source string - if langTmpl.Formater != nil { - source, err = langTmpl.Formater(string(tplcontent)) - if err != nil { - logging.Error("%v", err) - return err - } - } else { - source = string(tplcontent) - } + tplcontent, err := ioutil.ReadAll(newbytes) + if err != nil { + logging.Error("%v", err) + return err + } + var source string + if langTmpl.Formater != nil { + source, err = langTmpl.Formater(string(tplcontent)) + if err != nil { + logging.Error("%v", err) + return err + } + } else { + source = string(tplcontent) + } - w.WriteString(source) - w.Close() - } else { - for _, table := range tables { - // imports - tbs := []*xorm.Table{table} - imports := langTmpl.GenImports(tbs) + w.WriteString(source) + w.Close() + } else { + for _, table := range tables { + // imports + tbs := []*xorm.Table{table} + imports := langTmpl.GenImports(tbs) - w, err := os.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - logging.Error("%v", err) - return err - } + w, err := os.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600) + if err != nil { + logging.Error("%v", err) + return err + } - newbytes := bytes.NewBufferString("") + newbytes := bytes.NewBufferString("") - t := &Tmpl{Tables: tbs, Imports: imports, Model: model} - err = tmpl.Execute(newbytes, t) - if err != nil { - logging.Error("%v", err) - return err - } + t := &Tmpl{Tables: tbs, Imports: imports, Model: model} + err = tmpl.Execute(newbytes, t) + if err != nil { + logging.Error("%v", err) + return err + } - tplcontent, err := ioutil.ReadAll(newbytes) - if err != nil { - logging.Error("%v", err) - return err - } - var source string - if langTmpl.Formater != nil { - source, err = langTmpl.Formater(string(tplcontent)) - if err != nil { - logging.Error("%v-%v", err, string(tplcontent)) - return err - } - } else { - source = string(tplcontent) - } + tplcontent, err := ioutil.ReadAll(newbytes) + if err != nil { + logging.Error("%v", err) + return err + } + var source string + if langTmpl.Formater != nil { + source, err = langTmpl.Formater(string(tplcontent)) + if err != nil { + logging.Error("%v-%v", err, string(tplcontent)) + return err + } + } else { + source = string(tplcontent) + } - w.WriteString(source) - w.Close() - } - } + w.WriteString(source) + w.Close() + } + } - return nil - }) + return nil + }) } diff --git a/xorm/shell.go b/xorm/shell.go index 75a103c6..6fd0db6a 100644 --- a/xorm/shell.go +++ b/xorm/shell.go @@ -1,15 +1,15 @@ package main import ( - "fmt" - "github.com/lunny/xorm" - "strings" + "fmt" + "github.com/lunny/xorm" + "strings" ) var CmdShell = &Command{ - UsageLine: "shell driverName datasourceName", - Short: "a general shell to operate all kinds of database", - Long: ` + UsageLine: "shell driverName datasourceName", + Short: "a general shell to operate all kinds of database", + Long: ` general database's shell for sqlite3, mysql, postgres. driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres @@ -18,14 +18,14 @@ general database's shell for sqlite3, mysql, postgres. } func init() { - CmdShell.Run = runShell - CmdShell.Flags = map[string]bool{} + CmdShell.Run = runShell + CmdShell.Flags = map[string]bool{} } var engine *xorm.Engine func shellHelp() { - fmt.Println(` + fmt.Println(` show tables show all tables columns show table's column info indexes show table's index info @@ -38,110 +38,110 @@ func shellHelp() { } func runShell(cmd *Command, args []string) { - if len(args) != 2 { - fmt.Println("params error, please see xorm help shell") - return - } + if len(args) != 2 { + fmt.Println("params error, please see xorm help shell") + return + } - var err error - engine, err = xorm.NewEngine(args[0], args[1]) - if err != nil { - fmt.Println(err) - return - } + var err error + engine, err = xorm.NewEngine(args[0], args[1]) + if err != nil { + fmt.Println(err) + return + } - err = engine.Ping() - if err != nil { - fmt.Println(err) - return - } + err = engine.Ping() + if err != nil { + fmt.Println(err) + return + } - var scmd string - fmt.Print("xorm$ ") - for { - var input string - _, err := fmt.Scan(&input) - if err != nil { - fmt.Println(err) - continue - } - if strings.ToLower(input) == "exit" { - fmt.Println("bye") - return - } - if !strings.HasSuffix(input, ";") { - scmd = scmd + " " + input - continue - } - scmd = scmd + " " + input - lcmd := strings.TrimSpace(strings.ToLower(scmd)) - if strings.HasPrefix(lcmd, "select") { - res, err := engine.Query(scmd + "\n") - if err != nil { - fmt.Println(err) - } else { - if len(res) <= 0 { - fmt.Println("no records") - } else { - columns := make(map[string]int) - for k, _ := range res[0] { - columns[k] = len(k) - } + var scmd string + fmt.Print("xorm$ ") + for { + var input string + _, err := fmt.Scan(&input) + if err != nil { + fmt.Println(err) + continue + } + if strings.ToLower(input) == "exit" { + fmt.Println("bye") + return + } + if !strings.HasSuffix(input, ";") { + scmd = scmd + " " + input + continue + } + scmd = scmd + " " + input + lcmd := strings.TrimSpace(strings.ToLower(scmd)) + if strings.HasPrefix(lcmd, "select") { + res, err := engine.Query(scmd + "\n") + if err != nil { + fmt.Println(err) + } else { + if len(res) <= 0 { + fmt.Println("no records") + } else { + columns := make(map[string]int) + for k, _ := range res[0] { + columns[k] = len(k) + } - for _, m := range res { - for k, s := range m { - l := len(string(s)) - if l > columns[k] { - columns[k] = l - } - } - } + for _, m := range res { + for k, s := range m { + l := len(string(s)) + if l > columns[k] { + columns[k] = l + } + } + } - var maxlen = 0 - for _, l := range columns { - maxlen = maxlen + l + 3 - } - maxlen = maxlen + 1 + var maxlen = 0 + for _, l := range columns { + maxlen = maxlen + l + 3 + } + maxlen = maxlen + 1 - fmt.Println(strings.Repeat("-", maxlen)) - fmt.Print("|") - slice := make([]string, 0) - for k, l := range columns { - fmt.Print(" " + k + " ") - fmt.Print(strings.Repeat(" ", l-len(k))) - fmt.Print("|") - slice = append(slice, k) - } - fmt.Print("\n") - for _, r := range res { - fmt.Print("|") - for _, k := range slice { - fmt.Print(" " + string(r[k]) + " ") - fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k])))) - fmt.Print("|") - } - fmt.Print("\n") - } - fmt.Println(strings.Repeat("-", maxlen)) - //fmt.Println(res) - } - } - } else if lcmd == "show tables;" { - /*tables, err := engine.DBMetas() - if err != nil { - fmt.Println(err) - } else { + fmt.Println(strings.Repeat("-", maxlen)) + fmt.Print("|") + slice := make([]string, 0) + for k, l := range columns { + fmt.Print(" " + k + " ") + fmt.Print(strings.Repeat(" ", l-len(k))) + fmt.Print("|") + slice = append(slice, k) + } + fmt.Print("\n") + for _, r := range res { + fmt.Print("|") + for _, k := range slice { + fmt.Print(" " + string(r[k]) + " ") + fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k])))) + fmt.Print("|") + } + fmt.Print("\n") + } + fmt.Println(strings.Repeat("-", maxlen)) + //fmt.Println(res) + } + } + } else if lcmd == "show tables;" { + /*tables, err := engine.DBMetas() + if err != nil { + fmt.Println(err) + } else { - }*/ - } else { - cnt, err := engine.Exec(scmd) - if err != nil { - fmt.Println(err) - } else { - fmt.Printf("%d records changed.\n", cnt) - } - } - scmd = "" - fmt.Print("xorm$ ") - } + }*/ + } else { + cnt, err := engine.Exec(scmd) + if err != nil { + fmt.Println(err) + } else { + fmt.Printf("%d records changed.\n", cnt) + } + } + scmd = "" + fmt.Print("xorm$ ") + } } diff --git a/xorm/xorm.go b/xorm/xorm.go index a027f85f..11235e88 100644 --- a/xorm/xorm.go +++ b/xorm/xorm.go @@ -1,16 +1,16 @@ package main import ( - "fmt" - "github.com/dvirsky/go-pylog/logging" - "io" - "os" - "runtime" - "strings" - "sync" - "text/template" - "unicode" - "unicode/utf8" + "fmt" + "github.com/dvirsky/go-pylog/logging" + "io" + "os" + "runtime" + "strings" + "sync" + "text/template" + "unicode" + "unicode/utf8" ) // +build go1.1 @@ -23,52 +23,52 @@ const version = "0.1" // Commands lists the available commands and help topics. // The order here is the order in which they are printed by 'gopm help'. var commands = []*Command{ - CmdReverse, - CmdShell, + CmdReverse, + CmdShell, } func init() { - runtime.GOMAXPROCS(runtime.NumCPU()) + runtime.GOMAXPROCS(runtime.NumCPU()) } func main() { - logging.SetLevel(logging.ALL) - // Check length of arguments. - args := os.Args[1:] - if len(args) < 1 { - usage() - return - } + logging.SetLevel(logging.ALL) + // Check length of arguments. + args := os.Args[1:] + if len(args) < 1 { + usage() + return + } - // Show help documentation. - if args[0] == "help" { - help(args[1:]) - return - } + // Show help documentation. + if args[0] == "help" { + help(args[1:]) + return + } - // Check commands and run. - for _, comm := range commands { - if comm.Name() == args[0] && comm.Run != nil { - comm.Run(comm, args[1:]) - exit() - return - } - } + // Check commands and run. + for _, comm := range commands { + if comm.Name() == args[0] && comm.Run != nil { + comm.Run(comm, args[1:]) + exit() + return + } + } - fmt.Fprintf(os.Stderr, "xorm: unknown subcommand %q\nRun 'xorm help' for usage.\n", args[0]) - setExitStatus(2) - exit() + fmt.Fprintf(os.Stderr, "xorm: unknown subcommand %q\nRun 'xorm help' for usage.\n", args[0]) + setExitStatus(2) + exit() } var exitStatus = 0 var exitMu sync.Mutex func setExitStatus(n int) { - exitMu.Lock() - if exitStatus < n { - exitStatus = n - } - exitMu.Unlock() + exitMu.Lock() + if exitStatus < n { + exitStatus = n + } + exitMu.Unlock() } var usageTemplate = `xorm is a database tool based xorm package. @@ -97,66 +97,66 @@ var helpTemplate = `{{if .Runnable}}usage: xorm {{.UsageLine}} // tmpl executes the given template text on data, writing the result to w. func tmpl(w io.Writer, text string, data interface{}) { - t := template.New("top") - t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) - template.Must(t.Parse(text)) - if err := t.Execute(w, data); err != nil { - panic(err) - } + t := template.New("top") + t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) + template.Must(t.Parse(text)) + if err := t.Execute(w, data); err != nil { + panic(err) + } } func capitalize(s string) string { - if s == "" { - return s - } - r, n := utf8.DecodeRuneInString(s) - return string(unicode.ToTitle(r)) + s[n:] + if s == "" { + return s + } + r, n := utf8.DecodeRuneInString(s) + return string(unicode.ToTitle(r)) + s[n:] } func printUsage(w io.Writer) { - tmpl(w, usageTemplate, commands) + tmpl(w, usageTemplate, commands) } func usage() { - printUsage(os.Stderr) - os.Exit(2) + printUsage(os.Stderr) + os.Exit(2) } // help implements the 'help' command. func help(args []string) { - if len(args) == 0 { - printUsage(os.Stdout) - // not exit 2: succeeded at 'gopm help'. - return - } - if len(args) != 1 { - fmt.Fprintf(os.Stderr, "usage: xorm help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'gopm help' - } + if len(args) == 0 { + printUsage(os.Stdout) + // not exit 2: succeeded at 'gopm help'. + return + } + if len(args) != 1 { + fmt.Fprintf(os.Stderr, "usage: xorm help command\n\nToo many arguments given.\n") + os.Exit(2) // failed at 'gopm help' + } - arg := args[0] + arg := args[0] - for _, cmd := range commands { - if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'gopm help cmd'. - return - } - } + for _, cmd := range commands { + if cmd.Name() == arg { + tmpl(os.Stdout, helpTemplate, cmd) + // not exit 2: succeeded at 'gopm help cmd'. + return + } + } - fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'xorm help'.\n", arg) - os.Exit(2) // failed at 'gopm help cmd' + fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'xorm help'.\n", arg) + os.Exit(2) // failed at 'gopm help cmd' } var atexitFuncs []func() func atexit(f func()) { - atexitFuncs = append(atexitFuncs, f) + atexitFuncs = append(atexitFuncs, f) } func exit() { - for _, f := range atexitFuncs { - f() - } - os.Exit(exitStatus) + for _, f := range atexitFuncs { + f() + } + os.Exit(exitStatus) } From 07b2d15a2676a32b4368bc8069a7f65fe7ef08b0 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Wed, 8 Jan 2014 18:37:22 +0800 Subject: [PATCH 02/55] code tidy up, minor performance improvement --- engine.go | 2 +- session.go | 102 ++++++++++++++++++++++++++++++++++------------------- table.go | 1 + 3 files changed, 68 insertions(+), 37 deletions(-) diff --git a/engine.go b/engine.go index b5fd317f..1e303696 100644 --- a/engine.go +++ b/engine.go @@ -590,7 +590,7 @@ func (engine *Engine) mapType(t reflect.Type) *Table { sqlType := Type2SQLType(fieldType) col = &Column{engine.columnMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, sqlType.DefaultLength, sqlType.DefaultLength2, true, "", make(map[string]bool), false, false, - TWOSIDES, false, false, false, false} + TWOSIDES, false, false, false, false, nil} } if col.IsAutoIncrement { col.Nullable = false diff --git a/session.go b/session.go index 7623009b..7cb7fc05 100644 --- a/session.go +++ b/session.go @@ -1092,25 +1092,53 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) fieldsCount := len(fields) - for rawRows.Next() { - var newValue reflect.Value + 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 { - newValue = reflect.New(sliceElementType.Elem()) - } else { - newValue = reflect.New(sliceElementType) - } - err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) - if err != nil { - return err - } - 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 { + } + } else { + sliceValueSetFunc = func(newValue *reflect.Value) { sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) } } } + + for rawRows.Next() { + var newValue reflect.Value = newElemFunc() + // if sliceElementType.Kind() == reflect.Ptr { + // newValue = reflect.New(sliceElementType.Elem()) + // } else { + // newValue = reflect.New(sliceElementType) + // } + if sliceValueSetFunc != nil { + err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) + if err != nil { + return err + } + sliceValueSetFunc(&newValue) + } + // // if sliceValue.Kind() == reflect.Slice { + // // if sliceElementType.Kind() == reflect.Ptr { + // // sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface()))) + // // } else { + // // sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) + // // } + // // } + } } else { resultsSlice, err := session.query(sqlStr, args...) if err != nil { @@ -1347,32 +1375,35 @@ func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err func (session *Session) getField(dataStruct *reflect.Value, key string, table *Table) *reflect.Value { - key = strings.ToLower(key) - if _, ok := table.Columns[key]; !ok { + //key = strings.ToLower(key) + if col, ok := table.Columns[key]; !ok { session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.ColumnsSeq)) return nil - } - col := table.Columns[key] - fieldName := col.FieldName - fieldPath := strings.Split(fieldName, ".") - var fieldValue reflect.Value - if len(fieldPath) > 2 { - session.Engine.LogError("Unsupported mutliderive", fieldName) - return nil - } else if len(fieldPath) == 2 { - parentField := dataStruct.FieldByName(fieldPath[0]) - if parentField.IsValid() { - fieldValue = parentField.FieldByName(fieldPath[1]) - } } else { - fieldValue = dataStruct.FieldByName(fieldName) + fieldName := col.FieldName + if col.fieldPath == nil { + col.fieldPath = strings.Split(fieldName, ".") + } + var fieldValue reflect.Value + fieldPathLen := len(col.fieldPath) + if fieldPathLen > 2 { + session.Engine.LogError("Unsupported mutliderive", fieldName) + return nil + } else if fieldPathLen == 2 { + parentField := dataStruct.FieldByName(col.fieldPath[0]) + if parentField.IsValid() { + fieldValue = parentField.FieldByName(col.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) + return nil + } + return &fieldValue } - if !fieldValue.IsValid() || !fieldValue.CanSet() { - session.Engine.LogWarn("table %v's column %v is not valid or cannot set", - table.Name, key) - return nil - } - return &fieldValue } func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error { @@ -1395,7 +1426,6 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in for ii, key := range fields { if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil { - rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) //if row is null then ignore diff --git a/table.go b/table.go index a1ae7cdc..0667a12e 100644 --- a/table.go +++ b/table.go @@ -275,6 +275,7 @@ type Column struct { IsUpdated bool IsCascade bool IsVersion bool + fieldPath []string } // generate column description string according dialect From 97714332668117ef9483e452d7840a8ffde7fc83 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Wed, 8 Jan 2014 18:42:47 +0800 Subject: [PATCH 03/55] remove commented codes --- session.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/session.go b/session.go index 7cb7fc05..7d70c16e 100644 --- a/session.go +++ b/session.go @@ -1119,11 +1119,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) for rawRows.Next() { var newValue reflect.Value = newElemFunc() - // if sliceElementType.Kind() == reflect.Ptr { - // newValue = reflect.New(sliceElementType.Elem()) - // } else { - // newValue = reflect.New(sliceElementType) - // } if sliceValueSetFunc != nil { err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) if err != nil { @@ -1131,13 +1126,6 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } sliceValueSetFunc(&newValue) } - // // if sliceValue.Kind() == reflect.Slice { - // // if sliceElementType.Kind() == reflect.Ptr { - // // sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface()))) - // // } else { - // // sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface())))) - // // } - // // } } } else { resultsSlice, err := session.query(sqlStr, args...) From 004b30f2fb6987d8ec9d6a31c385a592d73b7621 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 9 Jan 2014 17:55:33 +0800 Subject: [PATCH 04/55] samemapper support --- base_test.go | 71 +++++++++++++--------- mysql_test.go | 28 ++++++++- postgres_test.go | 150 +++++++++++++++++------------------------------ sqlite3_test.go | 36 ++++++++++++ statement.go | 24 +++++--- 5 files changed, 175 insertions(+), 134 deletions(-) diff --git a/base_test.go b/base_test.go index 9b66890c..cc53fe1c 100644 --- a/base_test.go +++ b/base_test.go @@ -150,7 +150,7 @@ func exec(engine *Engine, t *testing.T) { fmt.Println(res) } -func querySameMapper(engine *Engine, t *testing.T) { +func testQuerySameMapper(engine *Engine, t *testing.T) { sql := "select * from `Userinfo`" results, err := engine.Query(sql) if err != nil { @@ -274,7 +274,7 @@ type Condi map[string]interface{} func update(engine *Engine, t *testing.T) { // update by id user := Userinfo{Username: "xxx", Height: 1.2} - cnt, err := engine.Id(1).Update(&user) + cnt, err := engine.Id(4).Update(&user) if err != nil { t.Error(err) panic(err) @@ -286,8 +286,8 @@ func update(engine *Engine, t *testing.T) { return } - condi := Condi{"username": "zzz", "height": 0.0, "departname": ""} - cnt, err = engine.Table(&user).Id(1).Update(&condi) + condi := Condi{"username": "zzz", "departname": ""} + cnt, err = engine.Table(&user).Id(4).Update(&condi) if err != nil { t.Error(err) panic(err) @@ -321,7 +321,7 @@ func update(engine *Engine, t *testing.T) { func updateSameMapper(engine *Engine, t *testing.T) { // update by id user := Userinfo{Username: "xxx", Height: 1.2} - cnt, err := engine.Id(1).Update(&user) + cnt, err := engine.Id(4).Update(&user) if err != nil { t.Error(err) panic(err) @@ -333,15 +333,15 @@ func updateSameMapper(engine *Engine, t *testing.T) { return } - condi := Condi{"Username": "zzz", "Height": 0.0, "Departname": ""} - cnt, err = engine.Table(&user).Id(1).Update(&condi) + condi := Condi{"Username": "zzz", "Departname": ""} + cnt, err = engine.Table(&user).Id(4).Update(&condi) if err != nil { t.Error(err) panic(err) } if cnt != 1 { - err = errors.New("insert not returned 1") + err = errors.New("update not returned 1") t.Error(err) panic(err) return @@ -354,7 +354,7 @@ func updateSameMapper(engine *Engine, t *testing.T) { } if cnt != 1 { - err = errors.New("insert not returned 1") + err = errors.New("update not returned 1") t.Error(err) panic(err) return @@ -376,6 +376,7 @@ func testDelete(engine *Engine, t *testing.T) { } user.Uid = 0 + user.IsMan = true has, err := engine.Id(3).Get(&user) if err != nil { t.Error(err) @@ -423,7 +424,9 @@ func get(engine *Engine, t *testing.T) { panic(err) } - _, err = engine.Where("`user` = ?", "xlw").Delete(&NoIdUser{}) + userCol := engine.columnMapper.Obj2Table("User") + + _, err = engine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) if err != nil { t.Error(err) panic(err) @@ -442,7 +445,7 @@ func get(engine *Engine, t *testing.T) { } noIdUser := new(NoIdUser) - has, err = engine.Where("`user` = ?", "xlw").Get(noIdUser) + has, err = engine.Where("`"+userCol+"` = ?", "xlw").Get(noIdUser) if err != nil { t.Error(err) panic(err) @@ -484,7 +487,8 @@ func find(engine *Engine, t *testing.T) { } users2 := make([]Userinfo, 0) - err = engine.Sql("select * from userinfo").Find(&users2) + userinfo := engine.tableMapper.Obj2Table("Userinfo") + err = engine.Sql("select * from " + engine.Quote(userinfo)).Find(&users2) if err != nil { t.Error(err) panic(err) @@ -574,7 +578,10 @@ func in(engine *Engine, t *testing.T) { } fmt.Println(users) - err = engine.In("(id)", 1).In("(id)", 2).In("departname", "dev").Find(&users) + department := engine.columnMapper.Obj2Table("Departname") + dev := engine.columnMapper.Obj2Table("Dev") + + err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) if err != nil { t.Error(err) panic(err) @@ -3782,18 +3789,12 @@ func testAll(engine *Engine, t *testing.T) { directCreateTable(engine, t) fmt.Println("-------------- insert --------------") insert(engine, t) - fmt.Println("-------------- query --------------") - testQuery(engine, t) - fmt.Println("-------------- exec --------------") - exec(engine, t) fmt.Println("-------------- insertAutoIncr --------------") insertAutoIncr(engine, t) fmt.Println("-------------- insertMulti --------------") insertMulti(engine, t) fmt.Println("-------------- insertTwoTable --------------") insertTwoTable(engine, t) - fmt.Println("-------------- update --------------") - update(engine, t) fmt.Println("-------------- testDelete --------------") testDelete(engine, t) fmt.Println("-------------- get --------------") @@ -3816,12 +3817,6 @@ func testAll(engine *Engine, t *testing.T) { in(engine, t) fmt.Println("-------------- limit --------------") limit(engine, t) - fmt.Println("-------------- order --------------") - order(engine, t) - fmt.Println("-------------- join --------------") - join(engine, t) - fmt.Println("-------------- having --------------") - having(engine, t) } func testAll2(engine *Engine, t *testing.T) { @@ -3904,9 +3899,31 @@ func testAll3(engine *Engine, t *testing.T) { } func testAllSnakeMapper(engine *Engine, t *testing.T) { - + fmt.Println("-------------- query --------------") + testQuery(engine, t) + fmt.Println("-------------- exec --------------") + exec(engine, t) + fmt.Println("-------------- update --------------") + update(engine, t) + fmt.Println("-------------- order --------------") + order(engine, t) + fmt.Println("-------------- join --------------") + join(engine, t) + fmt.Println("-------------- having --------------") + having(engine, t) } func testAllSameMapper(engine *Engine, t *testing.T) { - + fmt.Println("-------------- query --------------") + testQuerySameMapper(engine, t) + fmt.Println("-------------- exec --------------") + execSameMapper(engine, t) + fmt.Println("-------------- update --------------") + updateSameMapper(engine, t) + fmt.Println("-------------- order --------------") + orderSameMapper(engine, t) + fmt.Println("-------------- join --------------") + joinSameMapper(engine, t) + fmt.Println("-------------- having --------------") + havingSameMapper(engine, t) } diff --git a/mysql_test.go b/mysql_test.go index e0c3deac..f26095cb 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -42,7 +42,7 @@ func TestMysqlSameMapper(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test3?charset=utf8") + engine, err := NewEngine("mysql", "root:@/xorm_test1?charset=utf8") defer engine.Close() if err != nil { t.Error(err) @@ -66,7 +66,7 @@ func TestMysqlWithCache(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test?charset=utf8") + engine, err := NewEngine("mysql", "root:@/xorm_test2?charset=utf8") defer engine.Close() if err != nil { t.Error(err) @@ -82,6 +82,30 @@ func TestMysqlWithCache(t *testing.T) { testAll2(engine, t) } +func TestMysqlWithCacheSameMapper(t *testing.T) { + err := mysqlDdlImport() + if err != nil { + t.Error(err) + return + } + + engine, err := NewEngine("mysql", "root:@/xorm_test3?charset=utf8") + defer engine.Close() + if err != nil { + t.Error(err) + return + } + engine.SetMapper(SameMapper{}) + engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql + + testAll(engine, t) + testAll2(engine, t) +} + func newMysqlEngine() (*Engine, error) { return NewEngine("mysql", "root:@/xorm_test?charset=utf8") } diff --git a/postgres_test.go b/postgres_test.go index 9657cffd..32fb8b17 100644 --- a/postgres_test.go +++ b/postgres_test.go @@ -12,7 +12,22 @@ import ( var connStr string = "dbname=xorm_test sslmode=disable" func newPostgresEngine() (*Engine, error) { - return NewEngine("postgres", connStr) + orm, err := NewEngine("postgres", connStr) + if err != nil { + return nil, err + } + tables, err := orm.DBMetas() + if err != nil { + return nil, err + } + for _, table := range tables { + _, err = orm.Exec("drop table \"" + table.Name + "\"") + if err != nil { + return nil, err + } + } + + return orm, err } func newPostgresDriverDB() (*sql.DB, error) { @@ -32,6 +47,7 @@ func TestPostgres(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) testAll3(engine, t) } @@ -50,105 +66,47 @@ func TestPostgresWithCache(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) } -/* -func TestPostgres2(t *testing.T) { - engine, err := NewEngine("postgres", "dbname=xorm_test sslmode=disable") - if err != nil { - t.Error(err) - return - } - defer engine.Close() - engine.ShowSQL = showTestSql - engine.Mapper = SameMapper{} +func TestPostgresSameMapper(t *testing.T) { + engine, err := newPostgresEngine() + if err != nil { + t.Error(err) + return + } + defer engine.Close() + engine.SetMapper(SameMapper{}) + engine.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql - fmt.Println("-------------- directCreateTable --------------") - directCreateTable(engine, t) - fmt.Println("-------------- mapper --------------") - mapper(engine, t) - fmt.Println("-------------- insert --------------") - insert(engine, t) - fmt.Println("-------------- querySameMapper --------------") - querySameMapper(engine, t) - fmt.Println("-------------- execSameMapper --------------") - execSameMapper(engine, t) - fmt.Println("-------------- insertAutoIncr --------------") - insertAutoIncr(engine, t) - fmt.Println("-------------- insertMulti --------------") - insertMulti(engine, t) - fmt.Println("-------------- insertTwoTable --------------") - insertTwoTable(engine, t) - fmt.Println("-------------- updateSameMapper --------------") - updateSameMapper(engine, t) - fmt.Println("-------------- testdelete --------------") - testdelete(engine, t) - fmt.Println("-------------- get --------------") - get(engine, t) - fmt.Println("-------------- cascadeGet --------------") - cascadeGet(engine, t) - fmt.Println("-------------- find --------------") - find(engine, t) - fmt.Println("-------------- find2 --------------") - find2(engine, t) - fmt.Println("-------------- findMap --------------") - findMap(engine, t) - fmt.Println("-------------- findMap2 --------------") - findMap2(engine, t) - fmt.Println("-------------- count --------------") - count(engine, t) - fmt.Println("-------------- where --------------") - where(engine, t) - fmt.Println("-------------- in --------------") - in(engine, t) - fmt.Println("-------------- limit --------------") - limit(engine, t) - fmt.Println("-------------- orderSameMapper --------------") - orderSameMapper(engine, t) - fmt.Println("-------------- joinSameMapper --------------") - joinSameMapper(engine, t) - fmt.Println("-------------- havingSameMapper --------------") - havingSameMapper(engine, t) - fmt.Println("-------------- combineTransactionSameMapper --------------") - combineTransactionSameMapper(engine, t) - fmt.Println("-------------- table --------------") - table(engine, t) - fmt.Println("-------------- createMultiTables --------------") - createMultiTables(engine, t) - fmt.Println("-------------- tableOp --------------") - tableOp(engine, t) - fmt.Println("-------------- testColsSameMapper --------------") - testColsSameMapper(engine, t) - fmt.Println("-------------- testCharst --------------") - testCharst(engine, t) - fmt.Println("-------------- testStoreEngine --------------") - testStoreEngine(engine, t) - fmt.Println("-------------- testExtends --------------") - testExtends(engine, t) - fmt.Println("-------------- testColTypes --------------") - testColTypes(engine, t) - fmt.Println("-------------- testCustomType --------------") - testCustomType(engine, t) - fmt.Println("-------------- testCreatedAndUpdated --------------") - testCreatedAndUpdated(engine, t) - fmt.Println("-------------- testIndexAndUnique --------------") - testIndexAndUnique(engine, t) - fmt.Println("-------------- testMetaInfo --------------") - testMetaInfo(engine, t) - fmt.Println("-------------- testIterate --------------") - testIterate(engine, t) - fmt.Println("-------------- testStrangeName --------------") - testStrangeName(engine, t) - fmt.Println("-------------- testVersion --------------") - testVersion(engine, t) - fmt.Println("-------------- testDistinct --------------") - testDistinct(engine, t) - fmt.Println("-------------- testUseBool --------------") - testUseBool(engine, t) - fmt.Println("-------------- transaction --------------") - transaction(engine, t) -}*/ + testAll(engine, t) + testAllSameMapper(engine, t) + testAll2(engine, t) + testAll3(engine, t) +} + +func TestPostgresWithCacheSameMapper(t *testing.T) { + engine, err := newPostgresEngine() + if err != nil { + t.Error(err) + return + } + engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + defer engine.Close() + engine.SetMapper(SameMapper{}) + engine.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql + + testAll(engine, t) + testAllSameMapper(engine, t) + testAll2(engine, t) +} const ( createTablePostgres = `CREATE TABLE IF NOT EXISTS "big_struct" ("id" SERIAL PRIMARY KEY NOT NULL, "name" VARCHAR(255) NULL, "title" VARCHAR(255) NULL, "age" VARCHAR(255) NULL, "alias" VARCHAR(255) NULL, "nick_name" VARCHAR(255) NULL);` diff --git a/sqlite3_test.go b/sqlite3_test.go index b55702b0..62922462 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -52,6 +52,42 @@ func TestSqlite3WithCache(t *testing.T) { testAll2(engine, t) } +func TestSqlite3SameMapper(t *testing.T) { + engine, err := newSqlite3Engine() + defer engine.Close() + if err != nil { + t.Error(err) + return + } + engine.SetMapper(SameMapper{}) + engine.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql + + testAll(engine, t) + testAll2(engine, t) + testAll3(engine, t) +} + +func TestSqlite3WithCacheSameMapper(t *testing.T) { + engine, err := newSqlite3Engine() + defer engine.Close() + if err != nil { + t.Error(err) + return + } + engine.SetMapper(SameMapper{}) + engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql + + testAll(engine, t) + testAll2(engine, t) +} + const ( createTableSqlite3 = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, `title` TEXT NULL, `age` TEXT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL);" dropTableSqlite3 = "DROP TABLE IF EXISTS `big_struct`;" diff --git a/statement.go b/statement.go index 49811110..8b5ff430 100644 --- a/statement.go +++ b/statement.go @@ -12,6 +12,11 @@ import ( "github.com/lunny/xorm/core" ) +type inParam struct { + colName string + args []interface{} +} + // statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table @@ -43,7 +48,7 @@ type Statement struct { allUseBool bool checkVersion bool boolColumnMap map[string]bool - inColumns map[string][]interface{} + inColumns map[string]*inParam } // init @@ -72,7 +77,7 @@ func (statement *Statement) Init() { statement.allUseBool = false statement.boolColumnMap = make(map[string]bool) statement.checkVersion = true - statement.inColumns = make(map[string][]interface{}) + statement.inColumns = make(map[string]*inParam) } // add the raw sql statement @@ -456,10 +461,10 @@ func (statement *Statement) Id(id interface{}) *Statement { // Generate "Where column IN (?) " statment func (statement *Statement) In(column string, args ...interface{}) *Statement { k := strings.ToLower(column) - if params, ok := statement.inColumns[k]; ok { - statement.inColumns[k] = append(params, args...) + if _, ok := statement.inColumns[k]; ok { + statement.inColumns[k].args = append(statement.inColumns[k].args, args...) } else { - statement.inColumns[k] = args + statement.inColumns[k] = &inParam{column, args} } return statement } @@ -471,10 +476,11 @@ func (statement *Statement) genInSql() (string, []interface{}) { inStrs := make([]string, 0, len(statement.inColumns)) args := make([]interface{}, 0) - for column, params := range statement.inColumns { - inStrs = append(inStrs, fmt.Sprintf("(%v IN (%v))", statement.Engine.Quote(column), - strings.Join(makeArray("?", len(params)), ","))) - args = append(args, params...) + for _, params := range statement.inColumns { + inStrs = append(inStrs, fmt.Sprintf("(%v IN (%v))", + statement.Engine.Quote(params.colName), + strings.Join(makeArray("?", len(params.args)), ","))) + args = append(args, params.args...) } if len(statement.inColumns) == 1 { From 52eef13832f561099890d297749c13e58fc6d64b Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Tue, 14 Jan 2014 18:22:21 +0800 Subject: [PATCH 05/55] add create mysql xorm_test3 db stmt to ddl --- tests/mysql_ddl.sql | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/mysql_ddl.sql b/tests/mysql_ddl.sql index 92c95829..db20aa33 100644 --- a/tests/mysql_ddl.sql +++ b/tests/mysql_ddl.sql @@ -2,3 +2,4 @@ --DROP DATABASE xorm_test2; CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci; +CREATE DATABASE IF NOT EXISTS xorm_test3 CHARACTER SET utf8 COLLATE utf8_general_ci; From 60db5981147782e3605878a1b9642fd0a38a3cdf Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Tue, 14 Jan 2014 18:53:00 +0800 Subject: [PATCH 06/55] caching session bounded sql.Stmt to increase performance --- session.go | 77 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 28 deletions(-) diff --git a/session.go b/session.go index e8165a41..a983237b 100644 --- a/session.go +++ b/session.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "hash/crc32" "reflect" "strconv" "strings" @@ -33,6 +34,8 @@ type Session struct { beforeClosures []func(interface{}) afterClosures []func(interface{}) + + stmtCache map[uint32]*sql.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) } // Method Init reset the session as the init status. @@ -53,14 +56,17 @@ func (session *Session) Init() { // Method Close release the connection from pool func (session *Session) Close() { - defer func() { - if session.Db != nil { - session.Engine.Pool.ReleaseDB(session.Engine, session.Db) - session.Db = nil - session.Tx = nil - session.Init() - } - }() + for _, v := range session.stmtCache { + v.Close() + } + + if session.Db != nil { + session.Engine.Pool.ReleaseDB(session.Engine, session.Db) + session.Db = nil + session.Tx = nil + session.stmtCache = nil + session.Init() + } } // Method Sql provides raw sql input parameter. When you have a complex SQL statement @@ -256,6 +262,7 @@ func (session *Session) newDb() error { return err } session.Db = db + session.stmtCache = make(map[uint32]*sql.Stmt, 0) } return nil } @@ -394,13 +401,13 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b //Execute sql func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) { - rs, err := session.Db.Prepare(sqlStr) + stmt, err := session.doPrepare(sqlStr) if err != nil { return nil, err } - defer rs.Close() + //defer stmt.Close() - res, err := rs.Exec(args...) + res, err := stmt.Exec(args...) if err != nil { return nil, err } @@ -866,6 +873,21 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error { return nil } +func (session *Session) doPrepare(sqlStr string) (stmt *sql.Stmt, err error) { + crc := crc32.ChecksumIEEE([]byte(sqlStr)) + // TODO try hash(sqlStr+len(sqlStr)) + var has bool + stmt, has = session.stmtCache[crc] + if !has { + stmt, err = session.Db.Prepare(sqlStr) + if err != nil { + return nil, err + } + session.stmtCache[crc] = stmt + } + return +} + // get retrieve one record from database, bean's non-empty fields // will be as conditions func (session *Session) Get(bean interface{}) (bool, error) { @@ -901,11 +923,11 @@ func (session *Session) Get(bean interface{}) (bool, error) { var rawRows *sql.Rows session.queryPreprocess(&sqlStr, args...) if session.IsAutoCommit { - stmt, err := session.Db.Prepare(sqlStr) + stmt, err := session.doPrepare(sqlStr) if err != nil { return false, err } - defer stmt.Close() + // 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.Tx.Query(sqlStr, args...) @@ -1070,11 +1092,10 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) // defer rawRows.Close() if session.IsAutoCommit { - stmt, err = session.Db.Prepare(sqlStr) + stmt, err = session.doPrepare(sqlStr) if err != nil { return err } - defer stmt.Close() rawRows, err = stmt.Query(args...) } else { rawRows, err = session.Tx.Query(sqlStr, args...) @@ -1165,19 +1186,19 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return nil } -func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error { - var err error - if session.IsAutoCommit { - *rawStmt, err = session.Db.Prepare(sqlStr) - if err != nil { - return err - } - *rawRows, err = (*rawStmt).Query(args...) - } else { - *rawRows, err = session.Tx.Query(sqlStr, args...) - } - return err -} +// func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error { +// var err error +// if session.IsAutoCommit { +// *rawStmt, err = session.doPrepare(sqlStr) +// if err != nil { +// return err +// } +// *rawRows, err = (*rawStmt).Query(args...) +// } else { +// *rawRows, err = session.Tx.Query(sqlStr, args...) +// } +// return err +// } // Test if database is ok func (session *Session) Ping() error { From e456f596008aa9e69b08eb35c5e8dbeaf700edad Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Wed, 15 Jan 2014 10:29:56 +0800 Subject: [PATCH 07/55] improved docs --- docs/QuickStart.md | 64 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 719269e5..d77ee672 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -63,7 +63,7 @@ defer engine.Close() 一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 -xorm当前支持四种驱动如下: +xorm当前支持五种驱动四个数据库如下: * Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) @@ -110,7 +110,7 @@ xorm支持将一个struct映射为数据库中对应的一张表。映射规则 ### 2.1.名称映射规则 -名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持相同的命名。 +名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名。 当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用 @@ -121,7 +121,8 @@ engine.SetMapper(SameMapper{}) 同时需要注意的是: * 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: +* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: + ```Go engine.SetTableMapper(SameMapper{}) engine.SetColumnMapper(SnakeMapper{}) @@ -130,8 +131,9 @@ engine.SetColumnMapper(SnakeMapper{}) ### 2.2.前缀映射规则和后缀映射规则 -* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀; -* 通过`engine.NewSufffixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的后缀。 +* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 +* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 +* ### 2.3.使用Table和Tag改变名称映射 @@ -160,7 +162,7 @@ type User struct { name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名,如与其它关键字冲突,请使用单引号括起来。 - pk是否是Primary Key,如果在一个struct中有两个字段都使用了此标记,则这两个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7中Go的数据类型。 + pk是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。 当前支持30多种字段类型,详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)字段类型 @@ -205,7 +207,7 @@ type User struct { 另外有如下几条自动映射的规则: -- 1.如果field名称为`Id`而且类型为`int64`的话,会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字或非int64类型做为主键名,必须在对应的Tag上加上`xorm:"pk"`来定义主键,加上`xorm:"autoincr"`作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性。 +- 1.如果field名称为`Id`而且类型为`int64`并且没有定义tag,则会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字或非int64类型做为主键名,必须在对应的Tag上加上`xorm:"pk"`来定义主键,加上`xorm:"autoincr"`作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性。 - 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义 @@ -235,41 +237,50 @@ xorm提供了一些动态获取和修改表结构的方法。对于一般的应 ## 3.1 获取数据库信息 * DBMetas() + xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表,字段,索引的信息。 ## 3.2.表操作 * CreateTables() + 创建表使用`engine.CreateTables()`,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 * IsTableEmpty() + 判断表是否为空,参数和CreateTables相同 * IsTableExist() + 判断表是否存在 * DropTables() + 删除表使用`engine.DropTables()`,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。 ## 3.3.创建索引和唯一索引 * CreateIndexes + 根据struct中的tag来创建索引 * CreateUniques + 根据struct中的tag来创建唯一索引 ## 3.4.同步数据库结构 同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现: -1) 自动检测和创建表,这个检测是根据表的名字 -2)自动检测和新增表中的字段,这个检测是根据字段名 -3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 + +* 1) 自动检测和创建表,这个检测是根据表的名字 +* 2)自动检测和新增表中的字段,这个检测是根据字段名 +* 3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 调用方法如下: + ```Go err := engine.Sync(new(User)) ``` @@ -281,6 +292,7 @@ err := engine.Sync(new(User)) 如果传入的是Slice并且当数据库支持批量插入时,Insert会使用批量插入的方式进行插入。 * 插入一条数据 + ```Go user := new(User) user.Name = "myname" @@ -288,11 +300,13 @@ affected, err := engine.Insert(user) ``` 在插入单条数据成功后,如果该结构体有自增字段,则自增字段会被自动赋值为数据库中的id + ```Go fmt.Println(user.Id) ``` * 插入同一个表的多条数据 + ```Go users := make([]User, 0) users[0].Name = "name0" @@ -301,6 +315,7 @@ affected, err := engine.Insert(&users) ``` * 使用指针Slice插入多条记录 + ```Go users := make([]*User, 0) users[0] = new(User) @@ -310,6 +325,7 @@ affected, err := engine.Insert(&users) ``` * 插入不同表的一条记录 + ```Go user := new(User) user.Name = "myname" @@ -319,6 +335,7 @@ affected, err := engine.Insert(user, question) ``` * 插入不同表的多条记录 + ```Go users := make([]User, 0) users[0].Name = "name0" @@ -355,10 +372,10 @@ affected, err := engine.Insert(user, &questions) * Id(interface{}) 传入一个PK字段的值,作为查询条件,如果是复合主键,则 `Id(xorm.PK{1, 2})` -传入的两个参数按照struct中字段出现的顺序赋值。 +传入的两个参数按照struct中pk标记字段出现的顺序赋值。 * Where(string, …interface{}) -和Where语句中的条件基本相同,作为条件 +和SQL中Where语句中的条件基本相同,作为条件 * And(string, …interface{}) 和Where函数中的条件基本相同,作为条件 @@ -379,7 +396,7 @@ affected, err := engine.Insert(user, &questions) 按照指定的顺序进行排序 * In(string, …interface{}) -某字段在一些值中 +某字段在一些值中,这里需要注意必须是[]interface{}才可以展开,由于Go语言的限制,[]int64等均不可以展开。 * Cols(…string) 只查询或更新某些指定的字段,默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如: @@ -448,20 +465,28 @@ Having的参数字符串 如: 1) 根据Id来获得单条数据: + ```Go user := new(User) has, err := engine.Id(id).Get(user) +// 复合主键的获取方法 +// has, errr := engine.Id(xorm.PK{1,2}).Get(user) ``` + 2) 根据Where来获得单条数据: + ```Go user := new(User) has, err := engine.Where("name=?", "xlw").Get(user) ``` + 3) 根据user结构体中已有的非空数据来获得单条数据: + ```Go user := &User{Id:1} has, err := engine.Get(user) ``` + 或者其它条件 ```Go @@ -477,6 +502,7 @@ has, err := engine.Get(user) 查询多条数据使用`Find`方法,Find方法的第一个参数为`slice`的指针或`Map`指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针。 1) 传入Slice用于返回数据 + ```Go everyone := make([]Userinfo, 0) err := engine.Find(&everyone) @@ -485,7 +511,8 @@ pEveryOne := make([]*Userinfo, 0) err := engine.Find(&pEveryOne) ``` -2) 传入Map用户返回数据,map必须为`map[int64]Userinfo`的形式,map的key为id +2) 传入Map用户返回数据,map必须为`map[int64]Userinfo`的形式,map的key为id,因此对于复合主键无法使用这种方式。 + ```Go users := make(map[int64]Userinfo) err := engine.Find(&users) @@ -495,6 +522,7 @@ err := engine.Find(&pUsers) ``` 3) 也可以加入各种条件 + ```Go users := make([]Userinfo, 0) err := engine.Where("age > ? or name = ?", 30, "xlw").Limit(20, 10).Find(&users) @@ -504,6 +532,7 @@ err := engine.Where("age > ? or name = ?", 30, "xlw").Limit(20, 10).Find(&users) ### 5.5.Iterate方法 Iterate方法提供逐条执行查询到的记录的方法,他所能使用的条件和Find方法完全相同 + ```Go err := engine.Where("age > ? or name=?)", 30, "xlw").Iterate(new(Userinfo), func(i int, bean interface{})error{ user := bean.(*Userinfo) @@ -550,11 +579,13 @@ affected, err := engine.Id(id).Update(user) 这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择: 1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 + ```Go affected, err := engine.Id(id).Cols("age").Update(&user) ``` 2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 + ```Go affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) ``` @@ -583,6 +614,7 @@ engine.Id(1).Update(&user) ## 7.删除数据 删除数据`Delete`方法,参数为struct的指针并且成为查询条件。 + ```Go user := new(User) affected, err := engine.Id(id).Delete(user) @@ -659,14 +691,17 @@ xorm内置了一致性缓存支持,不过默认并没有开启。要开启缓 cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) engine.SetDefaultCacher(cacher) ``` + 上述代码采用了LRU算法的一个缓存,缓存方式是存放到内存中,缓存struct的记录数为1000条,缓存针对的范围是所有具有主键的表,没有主键的表中的数据将不会被缓存。 如果只想针对部分表,则: + ```Go cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) engine.MapCacher(&user, cacher) ``` 如果要禁用某个表的缓存,则: + ```Go engine.MapCacher(&user, nil) ``` @@ -682,6 +717,7 @@ engine.MapCacher(&user, nil) 2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。 3. 在使用Exec方法执行了方法之后,可能会导致缓存与数据库不一致的地方。因此如果启用缓存,尽量避免使用Exec。如果必须使用,则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如: + ```Go engine.Exec("update user set name = ? where id = ?", "xlw", 1) engine.ClearCache(new(User)) From 84ebac71f394b5f701313080cf99f3b35e9f32e7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 16 Jan 2014 11:26:24 +0800 Subject: [PATCH 08/55] improved tests --- base_test.go | 54 ++++++++++++++++++++++++++++++++++++++++++++++--- mysql_test.go | 4 ++++ sqlite3_test.go | 4 ++++ 3 files changed, 59 insertions(+), 3 deletions(-) diff --git a/base_test.go b/base_test.go index cc53fe1c..5c60500d 100644 --- a/base_test.go +++ b/base_test.go @@ -269,6 +269,16 @@ func insertTwoTable(engine *Engine, t *testing.T) { } } +type Article struct { + Id int32 `xorm:"pk INT autoincr` + Name string `xorm:"VARCHAR(45)"` + Img string `xorm:"VARCHAR(100)"` + Aside string `xorm:"VARCHAR(200)"` + Desc string `xorm:"VARCHAR(200)"` + Content string `xorm:"TEXT"` + Status int8 `xorm:"TINYINT(4)"` +} + type Condi map[string]interface{} func update(engine *Engine, t *testing.T) { @@ -316,6 +326,38 @@ func update(engine *Engine, t *testing.T) { panic(err) return } + + err = engine.Sync(&Article{}) + if err != nil { + t.Error(err) + panic(err) + } + + cnt, err = engine.Insert(&Article{0, "1", "2", "3", "4", "5", 2}) + if err != nil { + t.Error(err) + panic(err) + } + + if cnt != 1 { + err = errors.New("insert not returned 1") + t.Error(err) + panic(err) + return + } + + cnt, err = engine.Id(1).Update(&Article{Name: "6"}) + if err != nil { + t.Error(err) + panic(err) + } + + if cnt != 1 { + err = errors.New("update not returned 1") + t.Error(err) + panic(err) + return + } } func updateSameMapper(engine *Engine, t *testing.T) { @@ -353,8 +395,14 @@ func updateSameMapper(engine *Engine, t *testing.T) { panic(err) } - if cnt != 1 { - err = errors.New("update not returned 1") + total, err := engine.Count(&user) + if err != nil { + t.Error(err) + panic(err) + } + + if cnt != total { + err = errors.New("insert not returned 1") t.Error(err) panic(err) return @@ -706,7 +754,7 @@ func orderSameMapper(engine *Engine, t *testing.T) { func joinSameMapper(engine *Engine, t *testing.T) { users := make([]Userinfo, 0) - err := engine.Join("LEFT", `"Userdetail"`, `"Userinfo"."id"="Userdetail"."Id"`).Find(&users) + err := engine.Join("LEFT", "`Userdetail`", "`Userinfo`.`(id)`=`Userdetail`.`(id)`").Find(&users) if err != nil { t.Error(err) panic(err) diff --git a/mysql_test.go b/mysql_test.go index f26095cb..6c356afe 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -31,6 +31,7 @@ func TestMysql(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) testAll3(engine, t) } @@ -55,6 +56,7 @@ func TestMysqlSameMapper(t *testing.T) { engine.SetMapper(SameMapper{}) testAll(engine, t) + testAllSameMapper(engine, t) testAll2(engine, t) testAll3(engine, t) } @@ -79,6 +81,7 @@ func TestMysqlWithCache(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) } @@ -103,6 +106,7 @@ func TestMysqlWithCacheSameMapper(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSameMapper(engine, t) testAll2(engine, t) } diff --git a/sqlite3_test.go b/sqlite3_test.go index 62922462..aa66cb4e 100644 --- a/sqlite3_test.go +++ b/sqlite3_test.go @@ -31,6 +31,7 @@ func TestSqlite3(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) testAll3(engine, t) } @@ -49,6 +50,7 @@ func TestSqlite3WithCache(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSnakeMapper(engine, t) testAll2(engine, t) } @@ -66,6 +68,7 @@ func TestSqlite3SameMapper(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSameMapper(engine, t) testAll2(engine, t) testAll3(engine, t) } @@ -85,6 +88,7 @@ func TestSqlite3WithCacheSameMapper(t *testing.T) { engine.ShowDebug = showTestSql testAll(engine, t) + testAllSameMapper(engine, t) testAll2(engine, t) } From 21c08ea09c487819bfa808b0b818c32ff100dd24 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 23 Jan 2014 11:09:48 +0800 Subject: [PATCH 09/55] move test files to tests/ subdir --- engine.go | 10 +- base_test.go => tests/base_test.go | 184 +++++++++--------- benchmark.bat => tests/benchmark.bat | 0 benchmark.sh => tests/benchmark.sh | 0 .../benchmark_base_test.go | 8 +- mssql_test.go => tests/mssql_test.go | 13 +- mymysql_test.go => tests/mymysql_test.go | 21 +- mysql_test.go => tests/mysql_test.go | 31 +-- postgres_test.go => tests/postgres_test.go | 19 +- sqlite3_test.go => tests/sqlite3_test.go | 19 +- tests/{ => testdata}/mysql_ddl.sql | 0 11 files changed, 157 insertions(+), 148 deletions(-) rename base_test.go => tests/base_test.go (94%) rename benchmark.bat => tests/benchmark.bat (100%) rename benchmark.sh => tests/benchmark.sh (100%) rename benchmark_base_test.go => tests/benchmark_base_test.go (93%) rename mssql_test.go => tests/mssql_test.go (87%) rename mymysql_test.go => tests/mymysql_test.go (80%) rename mysql_test.go => tests/mysql_test.go (79%) rename postgres_test.go => tests/postgres_test.go (87%) rename sqlite3_test.go => tests/sqlite3_test.go (86%) rename tests/{ => testdata}/mysql_ddl.sql (100%) diff --git a/engine.go b/engine.go index 99e62ee0..a6509828 100644 --- a/engine.go +++ b/engine.go @@ -21,8 +21,8 @@ type PK []interface{} // Engine is the major struct of xorm, it means a database manager. // Commonly, an application only need one engine type Engine struct { - columnMapper IMapper - tableMapper IMapper + ColumnMapper IMapper + TableMapper IMapper TagIdentifier string DriverName string DataSourceName string @@ -47,11 +47,11 @@ func (engine *Engine) SetMapper(mapper IMapper) { } func (engine *Engine) SetTableMapper(mapper IMapper) { - engine.tableMapper = mapper + engine.TableMapper = mapper } func (engine *Engine) SetColumnMapper(mapper IMapper) { - engine.columnMapper = mapper + engine.ColumnMapper = mapper } // If engine's database support batch insert records like @@ -406,7 +406,7 @@ func (engine *Engine) autoMap(bean interface{}) *core.Table { } func (engine *Engine) mapType(t reflect.Type) *core.Table { - return mappingTable(t, engine.tableMapper, engine.columnMapper, engine.dialect, engine.TagIdentifier) + return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier) } func mappingTable(t reflect.Type, tableMapper IMapper, colMapper IMapper, dialect core.Dialect, tagId string) *core.Table { diff --git a/base_test.go b/tests/base_test.go similarity index 94% rename from base_test.go rename to tests/base_test.go index 5c60500d..cf12066a 100644 --- a/base_test.go +++ b/tests/base_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/lunny/xorm" "github.com/lunny/xorm/core" ) @@ -44,7 +45,7 @@ type Userdetail struct { Profile string `xorm:"varchar(2000)"` } -func directCreateTable(engine *Engine, t *testing.T) { +func directCreateTable(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&Userinfo{}, &Userdetail{}) if err != nil { t.Error(err) @@ -94,7 +95,7 @@ func directCreateTable(engine *Engine, t *testing.T) { } } -func insert(engine *Engine, t *testing.T) { +func insert(engine *xorm.Engine, t *testing.T) { user := Userinfo{0, "xiaolunwen", "dev", "lunny", time.Now(), Userdetail{Id: 1}, 1.78, []byte{1, 2, 3}, true} cnt, err := engine.Insert(&user) @@ -130,7 +131,7 @@ func insert(engine *Engine, t *testing.T) { } } -func testQuery(engine *Engine, t *testing.T) { +func testQuery(engine *xorm.Engine, t *testing.T) { sql := "select * from userinfo" results, err := engine.Query(sql) if err != nil { @@ -140,7 +141,7 @@ func testQuery(engine *Engine, t *testing.T) { fmt.Println(results) } -func exec(engine *Engine, t *testing.T) { +func exec(engine *xorm.Engine, t *testing.T) { sql := "update userinfo set username=? where id=?" res, err := engine.Exec(sql, "xiaolun", 1) if err != nil { @@ -150,7 +151,7 @@ func exec(engine *Engine, t *testing.T) { fmt.Println(res) } -func testQuerySameMapper(engine *Engine, t *testing.T) { +func testQuerySameMapper(engine *xorm.Engine, t *testing.T) { sql := "select * from `Userinfo`" results, err := engine.Query(sql) if err != nil { @@ -160,7 +161,7 @@ func testQuerySameMapper(engine *Engine, t *testing.T) { fmt.Println(results) } -func execSameMapper(engine *Engine, t *testing.T) { +func execSameMapper(engine *xorm.Engine, t *testing.T) { sql := "update `Userinfo` set `Username`=? where (id)=?" res, err := engine.Exec(sql, "xiaolun", 1) if err != nil { @@ -170,7 +171,7 @@ func execSameMapper(engine *Engine, t *testing.T) { fmt.Println(res) } -func insertAutoIncr(engine *Engine, t *testing.T) { +func insertAutoIncr(engine *xorm.Engine, t *testing.T) { // auto increment insert user := Userinfo{Username: "xiaolunwen2", Departname: "dev", Alias: "lunny", Created: time.Now(), Detail: Userdetail{Id: 1}, Height: 1.78, Avatar: []byte{1, 2, 3}, IsMan: true} @@ -194,11 +195,11 @@ func insertAutoIncr(engine *Engine, t *testing.T) { type BigInsert struct { } -func insertDefault(engine *Engine, t *testing.T) { +func insertDefault(engine *xorm.Engine, t *testing.T) { } -func insertMulti(engine *Engine, t *testing.T) { +func insertMulti(engine *xorm.Engine, t *testing.T) { //engine.InsertMany = true users := []Userinfo{ {Username: "xlw", Departname: "dev", Alias: "lunny2", Created: time.Now()}, @@ -239,7 +240,7 @@ func insertMulti(engine *Engine, t *testing.T) { } } -func insertTwoTable(engine *Engine, t *testing.T) { +func insertTwoTable(engine *xorm.Engine, t *testing.T) { userdetail := Userdetail{ /*Id: 1, */ Intro: "I'm a very beautiful women.", Profile: "sfsaf"} userinfo := Userinfo{Username: "xlw3", Departname: "dev", Alias: "lunny4", Created: time.Now(), Detail: userdetail} @@ -281,7 +282,7 @@ type Article struct { type Condi map[string]interface{} -func update(engine *Engine, t *testing.T) { +func update(engine *xorm.Engine, t *testing.T) { // update by id user := Userinfo{Username: "xxx", Height: 1.2} cnt, err := engine.Id(4).Update(&user) @@ -360,7 +361,7 @@ func update(engine *Engine, t *testing.T) { } } -func updateSameMapper(engine *Engine, t *testing.T) { +func updateSameMapper(engine *xorm.Engine, t *testing.T) { // update by id user := Userinfo{Username: "xxx", Height: 1.2} cnt, err := engine.Id(4).Update(&user) @@ -409,7 +410,7 @@ func updateSameMapper(engine *Engine, t *testing.T) { } } -func testDelete(engine *Engine, t *testing.T) { +func testDelete(engine *xorm.Engine, t *testing.T) { user := Userinfo{Uid: 1} cnt, err := engine.Delete(&user) if err != nil { @@ -452,7 +453,7 @@ type NoIdUser struct { Total int64 } -func get(engine *Engine, t *testing.T) { +func get(engine *xorm.Engine, t *testing.T) { user := Userinfo{Uid: 2} has, err := engine.Get(&user) @@ -472,7 +473,7 @@ func get(engine *Engine, t *testing.T) { panic(err) } - userCol := engine.columnMapper.Obj2Table("User") + userCol := engine.ColumnMapper.Obj2Table("User") _, err = engine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) if err != nil { @@ -507,7 +508,7 @@ func get(engine *Engine, t *testing.T) { fmt.Println(noIdUser) } -func cascadeGet(engine *Engine, t *testing.T) { +func cascadeGet(engine *xorm.Engine, t *testing.T) { user := Userinfo{Uid: 11} has, err := engine.Get(&user) @@ -522,7 +523,7 @@ func cascadeGet(engine *Engine, t *testing.T) { } } -func find(engine *Engine, t *testing.T) { +func find(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Find(&users) @@ -535,7 +536,7 @@ func find(engine *Engine, t *testing.T) { } users2 := make([]Userinfo, 0) - userinfo := engine.tableMapper.Obj2Table("Userinfo") + userinfo := engine.TableMapper.Obj2Table("Userinfo") err = engine.Sql("select * from " + engine.Quote(userinfo)).Find(&users2) if err != nil { t.Error(err) @@ -543,7 +544,7 @@ func find(engine *Engine, t *testing.T) { } } -func find2(engine *Engine, t *testing.T) { +func find2(engine *xorm.Engine, t *testing.T) { users := make([]*Userinfo, 0) err := engine.Find(&users) @@ -556,7 +557,7 @@ func find2(engine *Engine, t *testing.T) { } } -func findMap(engine *Engine, t *testing.T) { +func findMap(engine *xorm.Engine, t *testing.T) { users := make(map[int64]Userinfo) err := engine.Find(&users) @@ -569,7 +570,7 @@ func findMap(engine *Engine, t *testing.T) { } } -func findMap2(engine *Engine, t *testing.T) { +func findMap2(engine *xorm.Engine, t *testing.T) { users := make(map[int64]*Userinfo) err := engine.Find(&users) @@ -582,7 +583,7 @@ func findMap2(engine *Engine, t *testing.T) { } } -func count(engine *Engine, t *testing.T) { +func count(engine *xorm.Engine, t *testing.T) { user := Userinfo{Departname: "dev"} total, err := engine.Count(&user) if err != nil { @@ -592,7 +593,7 @@ func count(engine *Engine, t *testing.T) { fmt.Printf("Total %d records!!!\n", total) } -func where(engine *Engine, t *testing.T) { +func where(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Where("(id) > ?", 2).Find(&users) if err != nil { @@ -609,7 +610,7 @@ func where(engine *Engine, t *testing.T) { fmt.Println(users) } -func in(engine *Engine, t *testing.T) { +func in(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.In("(id)", 1, 2, 3).Find(&users) if err != nil { @@ -626,8 +627,8 @@ func in(engine *Engine, t *testing.T) { } fmt.Println(users) - department := engine.columnMapper.Obj2Table("Departname") - dev := engine.columnMapper.Obj2Table("Dev") + department := engine.ColumnMapper.Obj2Table("Departname") + dev := engine.ColumnMapper.Obj2Table("Dev") err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) if err != nil { @@ -687,7 +688,7 @@ func in(engine *Engine, t *testing.T) { } } -func limit(engine *Engine, t *testing.T) { +func limit(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Limit(2, 1).Find(&users) if err != nil { @@ -697,7 +698,7 @@ func limit(engine *Engine, t *testing.T) { fmt.Println(users) } -func order(engine *Engine, t *testing.T) { +func order(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.OrderBy("id desc").Find(&users) if err != nil { @@ -715,7 +716,7 @@ func order(engine *Engine, t *testing.T) { fmt.Println(users2) } -func join(engine *Engine, t *testing.T) { +func join(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Join("LEFT", "userdetail", "userinfo.id=userdetail.id").Find(&users) if err != nil { @@ -724,7 +725,7 @@ func join(engine *Engine, t *testing.T) { } } -func having(engine *Engine, t *testing.T) { +func having(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.GroupBy("username").Having("username='xlw'").Find(&users) if err != nil { @@ -734,7 +735,7 @@ func having(engine *Engine, t *testing.T) { fmt.Println(users) } -func orderSameMapper(engine *Engine, t *testing.T) { +func orderSameMapper(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.OrderBy("(id) desc").Find(&users) if err != nil { @@ -752,7 +753,7 @@ func orderSameMapper(engine *Engine, t *testing.T) { fmt.Println(users2) } -func joinSameMapper(engine *Engine, t *testing.T) { +func joinSameMapper(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Join("LEFT", "`Userdetail`", "`Userinfo`.`(id)`=`Userdetail`.`(id)`").Find(&users) if err != nil { @@ -761,7 +762,7 @@ func joinSameMapper(engine *Engine, t *testing.T) { } } -func havingSameMapper(engine *Engine, t *testing.T) { +func havingSameMapper(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.GroupBy("Username").Having(`"Username"='xlw'`).Find(&users) if err != nil { @@ -771,7 +772,7 @@ func havingSameMapper(engine *Engine, t *testing.T) { fmt.Println(users) } -func transaction(engine *Engine, t *testing.T) { +func transaction(engine *xorm.Engine, t *testing.T) { counter := func() { total, err := engine.Count(&Userinfo{}) if err != nil { @@ -823,7 +824,7 @@ func transaction(engine *Engine, t *testing.T) { // panic(err) !nashtsai! should remove this } -func combineTransaction(engine *Engine, t *testing.T) { +func combineTransaction(engine *xorm.Engine, t *testing.T) { counter := func() { total, err := engine.Count(&Userinfo{}) if err != nil { @@ -872,7 +873,7 @@ func combineTransaction(engine *Engine, t *testing.T) { } } -func combineTransactionSameMapper(engine *Engine, t *testing.T) { +func combineTransactionSameMapper(engine *xorm.Engine, t *testing.T) { counter := func() { total, err := engine.Count(&Userinfo{}) if err != nil { @@ -921,7 +922,7 @@ func combineTransactionSameMapper(engine *Engine, t *testing.T) { } } -func table(engine *Engine, t *testing.T) { +func table(engine *xorm.Engine, t *testing.T) { err := engine.DropTables("user_user") if err != nil { t.Error(err) @@ -935,7 +936,7 @@ func table(engine *Engine, t *testing.T) { } } -func createMultiTables(engine *Engine, t *testing.T) { +func createMultiTables(engine *xorm.Engine, t *testing.T) { session := engine.NewSession() defer session.Close() @@ -969,7 +970,7 @@ func createMultiTables(engine *Engine, t *testing.T) { } } -func tableOp(engine *Engine, t *testing.T) { +func tableOp(engine *xorm.Engine, t *testing.T) { user := Userinfo{Username: "tablexiao", Departname: "dev", Alias: "lunny", Created: time.Now()} tableName := fmt.Sprintf("user_%v", len(user.Username)) cnt, err := engine.Table(tableName).Insert(&user) @@ -1017,7 +1018,7 @@ func tableOp(engine *Engine, t *testing.T) { } } -func testCharst(engine *Engine, t *testing.T) { +func testCharst(engine *xorm.Engine, t *testing.T) { err := engine.DropTables("user_charset") if err != nil { t.Error(err) @@ -1031,7 +1032,7 @@ func testCharst(engine *Engine, t *testing.T) { } } -func testStoreEngine(engine *Engine, t *testing.T) { +func testStoreEngine(engine *xorm.Engine, t *testing.T) { err := engine.DropTables("user_store_engine") if err != nil { t.Error(err) @@ -1050,7 +1051,7 @@ type tempUser struct { Username string } -func testCols(engine *Engine, t *testing.T) { +func testCols(engine *xorm.Engine, t *testing.T) { users := []Userinfo{} err := engine.Cols("id, username").Find(&users) if err != nil { @@ -1077,7 +1078,7 @@ func testCols(engine *Engine, t *testing.T) { fmt.Println("===================", user, affected) } -func testColsSameMapper(engine *Engine, t *testing.T) { +func testColsSameMapper(engine *xorm.Engine, t *testing.T) { users := []Userinfo{} err := engine.Cols("(id), Username").Find(&users) if err != nil { @@ -1109,7 +1110,7 @@ type tempUser2 struct { Departname string } -func testExtends(engine *Engine, t *testing.T) { +func testExtends(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&tempUser2{}) if err != nil { t.Error(err) @@ -1186,7 +1187,7 @@ type allCols struct { //BigSerial int64 `xorm:"BIGSERIAL"` } -func testColTypes(engine *Engine, t *testing.T) { +func testColTypes(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&allCols{}) if err != nil { t.Error(err) @@ -1319,7 +1320,7 @@ type MyStruct struct { MSS map[string]string } -func testCustomType(engine *Engine, t *testing.T) { +func testCustomType(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&MyStruct{}) if err != nil { t.Error(err) @@ -1405,7 +1406,7 @@ type UserCU struct { Updated time.Time `xorm:"updated"` } -func testCreatedAndUpdated(engine *Engine, t *testing.T) { +func testCreatedAndUpdated(engine *xorm.Engine, t *testing.T) { u := new(UserCU) err := engine.DropTables(u) if err != nil { @@ -1472,7 +1473,7 @@ type IndexOrUnique struct { UniGroup2 int `xorm:"unique(lll)"` } -func testIndexAndUnique(engine *Engine, t *testing.T) { +func testIndexAndUnique(engine *xorm.Engine, t *testing.T) { err := engine.CreateTables(&IndexOrUnique{}) if err != nil { t.Error(err) @@ -1534,7 +1535,7 @@ type StringPK struct { Name string } -func testIntId(engine *Engine, t *testing.T) { +func testIntId(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&IntId{}) if err != nil { t.Error(err) @@ -1594,7 +1595,7 @@ func testIntId(engine *Engine, t *testing.T) { } } -func testInt32Id(engine *Engine, t *testing.T) { +func testInt32Id(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&Int32Id{}) if err != nil { t.Error(err) @@ -1655,7 +1656,7 @@ func testInt32Id(engine *Engine, t *testing.T) { } } -func testUintId(engine *Engine, t *testing.T) { +func testUintId(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&UintId{}) if err != nil { t.Error(err) @@ -1715,7 +1716,7 @@ func testUintId(engine *Engine, t *testing.T) { } } -func testUint32Id(engine *Engine, t *testing.T) { +func testUint32Id(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&Uint32Id{}) if err != nil { t.Error(err) @@ -1776,7 +1777,7 @@ func testUint32Id(engine *Engine, t *testing.T) { } } -func testUint64Id(engine *Engine, t *testing.T) { +func testUint64Id(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&Uint64Id{}) if err != nil { t.Error(err) @@ -1837,7 +1838,7 @@ func testUint64Id(engine *Engine, t *testing.T) { } } -func testStringPK(engine *Engine, t *testing.T) { +func testStringPK(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&StringPK{}) if err != nil { t.Error(err) @@ -1898,7 +1899,7 @@ func testStringPK(engine *Engine, t *testing.T) { } } -func testMetaInfo(engine *Engine, t *testing.T) { +func testMetaInfo(engine *xorm.Engine, t *testing.T) { tables, err := engine.DBMetas() if err != nil { t.Error(err) @@ -1907,9 +1908,10 @@ func testMetaInfo(engine *Engine, t *testing.T) { for _, table := range tables { fmt.Println(table.Name) - for _, col := range table.Columns() { - fmt.Println(col.String(engine.dialect)) - } + //for _, col := range table.Columns() { + //TODO: engine.dialect show exported + //fmt.Println(col.String(engine.dia)) + //} for _, index := range table.Indexes { fmt.Println(index.Name, index.Type, strings.Join(index.Cols, ",")) @@ -1917,7 +1919,7 @@ func testMetaInfo(engine *Engine, t *testing.T) { } } -func testIterate(engine *Engine, t *testing.T) { +func testIterate(engine *xorm.Engine, t *testing.T) { err := engine.Omit("is_man").Iterate(new(Userinfo), func(idx int, bean interface{}) error { user := bean.(*Userinfo) fmt.Println(idx, "--", user) @@ -1930,7 +1932,7 @@ func testIterate(engine *Engine, t *testing.T) { } } -func testRows(engine *Engine, t *testing.T) { +func testRows(engine *xorm.Engine, t *testing.T) { rows, err := engine.Omit("is_man").Rows(new(Userinfo)) if err != nil { t.Error(err) @@ -1956,7 +1958,7 @@ type StrangeName struct { Name string } -func testStrangeName(engine *Engine, t *testing.T) { +func testStrangeName(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(new(StrangeName)) if err != nil { t.Error(err) @@ -1985,7 +1987,7 @@ type VersionS struct { Ver int `xorm:"version"` } -func testVersion(engine *Engine, t *testing.T) { +func testVersion(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(new(VersionS)) if err != nil { t.Error(err) @@ -2058,7 +2060,7 @@ func testVersion(engine *Engine, t *testing.T) { }*/ } -func testDistinct(engine *Engine, t *testing.T) { +func testDistinct(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) err := engine.Distinct("departname").Find(&users) if err != nil { @@ -2089,7 +2091,7 @@ func testDistinct(engine *Engine, t *testing.T) { fmt.Println(users2) } -func testUseBool(engine *Engine, t *testing.T) { +func testUseBool(engine *xorm.Engine, t *testing.T) { cnt1, err := engine.Count(&Userinfo{}) if err != nil { t.Error(err) @@ -2129,7 +2131,7 @@ func testUseBool(engine *Engine, t *testing.T) { } } -func testBool(engine *Engine, t *testing.T) { +func testBool(engine *xorm.Engine, t *testing.T) { _, err := engine.UseBool().Update(&Userinfo{IsMan: true}) if err != nil { t.Error(err) @@ -2175,7 +2177,7 @@ type TTime struct { Tz time.Time `xorm:"timestampz"` } -func testTime(engine *Engine, t *testing.T) { +func testTime(engine *xorm.Engine, t *testing.T) { err := engine.Sync(&TTime{}) if err != nil { t.Error(err) @@ -2217,14 +2219,14 @@ func testTime(engine *Engine, t *testing.T) { fmt.Println("=======\n", tt4s, "=======\n") } -func testPrefixTableName(engine *Engine, t *testing.T) { - tempEngine, err := NewEngine(engine.DriverName, engine.DataSourceName) +func testPrefixTableName(engine *xorm.Engine, t *testing.T) { + tempEngine, err := xorm.NewEngine(engine.DriverName, engine.DataSourceName) if err != nil { t.Error(err) panic(err) } tempEngine.ShowSQL = true - mapper := NewPrefixMapper(SnakeMapper{}, "xlw_") + mapper := xorm.NewPrefixMapper(xorm.SnakeMapper{}, "xlw_") //tempEngine.SetMapper(mapper) tempEngine.SetTableMapper(mapper) exist, err := tempEngine.IsTableExist(&Userinfo{}) @@ -2255,7 +2257,7 @@ type CreatedUpdated struct { Updated time.Time `xorm:"updated"` } -func testCreatedUpdated(engine *Engine, t *testing.T) { +func testCreatedUpdated(engine *xorm.Engine, t *testing.T) { err := engine.Sync(&CreatedUpdated{}) if err != nil { t.Error(err) @@ -2330,7 +2332,7 @@ func (p *ProcessorsStruct) AfterDelete() { p.AfterDeletedFlag = 1 } -func testProcessors(engine *Engine, t *testing.T) { +func testProcessors(engine *xorm.Engine, t *testing.T) { // tempEngine, err := NewEngine(engine.DriverName, engine.DataSourceName) // if err != nil { // t.Error(err) @@ -2556,7 +2558,7 @@ func testProcessors(engine *Engine, t *testing.T) { // -- } -func testProcessorsTx(engine *Engine, t *testing.T) { +func testProcessorsTx(engine *xorm.Engine, t *testing.T) { // tempEngine, err := NewEngine(engine.DriverName, engine.DataSourceName) // if err != nil { // t.Error(err) @@ -3062,7 +3064,7 @@ type NullData3 struct { StringPtr *string } -func testPointerData(engine *Engine, t *testing.T) { +func testPointerData(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&NullData{}) if err != nil { @@ -3232,7 +3234,7 @@ func testPointerData(engine *Engine, t *testing.T) { // using instance type should just work too nullData2Get := NullData2{} - tableName := engine.tableMapper.Obj2Table("NullData") + tableName := engine.TableMapper.Obj2Table("NullData") has, err = engine.Table(tableName).Id(nullData.Id).Get(&nullData2Get) if err != nil { @@ -3324,7 +3326,7 @@ func testPointerData(engine *Engine, t *testing.T) { // -- } -func testNullValue(engine *Engine, t *testing.T) { +func testNullValue(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&NullData{}) if err != nil { @@ -3599,7 +3601,7 @@ func testNullValue(engine *Engine, t *testing.T) { // update to null values nullDataUpdate = NullData{} - string_ptr := engine.columnMapper.Obj2Table("StringPtr") + string_ptr := engine.ColumnMapper.Obj2Table("StringPtr") cnt, err = engine.Id(nullData.Id).Cols(string_ptr).Update(&nullDataUpdate) if err != nil { @@ -3709,7 +3711,7 @@ type CompositeKey struct { UpdateStr string } -func testCompositeKey(engine *Engine, t *testing.T) { +func testCompositeKey(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&CompositeKey{}) if err != nil { @@ -3736,7 +3738,7 @@ func testCompositeKey(engine *Engine, t *testing.T) { } var compositeKeyVal CompositeKey - has, err := engine.Id(PK{11, 22}).Get(&compositeKeyVal) + has, err := engine.Id(xorm.PK{11, 22}).Get(&compositeKeyVal) if err != nil { t.Error(err) } else if !has { @@ -3744,7 +3746,7 @@ func testCompositeKey(engine *Engine, t *testing.T) { } // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&PK{11, 22}).Get(&compositeKeyVal) + has, err = engine.Id(&xorm.PK{11, 22}).Get(&compositeKeyVal) if err != nil { t.Error(err) } else if !has { @@ -3752,14 +3754,14 @@ func testCompositeKey(engine *Engine, t *testing.T) { } compositeKeyVal = CompositeKey{UpdateStr: "test1"} - cnt, err = engine.Id(PK{11, 22}).Update(&compositeKeyVal) + cnt, err = engine.Id(xorm.PK{11, 22}).Update(&compositeKeyVal) if err != nil { t.Error(err) } else if cnt != 1 { t.Error(errors.New("can't update CompositeKey{11, 22}")) } - cnt, err = engine.Id(PK{11, 22}).Delete(&CompositeKey{}) + cnt, err = engine.Id(xorm.PK{11, 22}).Delete(&CompositeKey{}) if err != nil { t.Error(err) } else if cnt != 1 { @@ -3774,7 +3776,7 @@ type User struct { Score int32 `xorm:"integer"` } -func testCompositeKey2(engine *Engine, t *testing.T) { +func testCompositeKey2(engine *xorm.Engine, t *testing.T) { err := engine.DropTables(&User{}) if err != nil { @@ -3801,7 +3803,7 @@ func testCompositeKey2(engine *Engine, t *testing.T) { } var user User - has, err := engine.Id(PK{"11", 22}).Get(&user) + has, err := engine.Id(xorm.PK{"11", 22}).Get(&user) if err != nil { t.Error(err) } else if !has { @@ -3809,7 +3811,7 @@ func testCompositeKey2(engine *Engine, t *testing.T) { } // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&PK{"11", 22}).Get(&user) + has, err = engine.Id(&xorm.PK{"11", 22}).Get(&user) if err != nil { t.Error(err) } else if !has { @@ -3817,14 +3819,14 @@ func testCompositeKey2(engine *Engine, t *testing.T) { } user = User{NickName: "test1"} - cnt, err = engine.Id(PK{"11", 22}).Update(&user) + cnt, err = engine.Id(xorm.PK{"11", 22}).Update(&user) if err != nil { t.Error(err) } else if cnt != 1 { t.Error(errors.New("can't update User{11, 22}")) } - cnt, err = engine.Id(PK{"11", 22}).Delete(&User{}) + cnt, err = engine.Id(xorm.PK{"11", 22}).Delete(&User{}) if err != nil { t.Error(err) } else if cnt != 1 { @@ -3832,7 +3834,7 @@ func testCompositeKey2(engine *Engine, t *testing.T) { } } -func testAll(engine *Engine, t *testing.T) { +func testAll(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- directCreateTable --------------") directCreateTable(engine, t) fmt.Println("-------------- insert --------------") @@ -3867,7 +3869,7 @@ func testAll(engine *Engine, t *testing.T) { limit(engine, t) } -func testAll2(engine *Engine, t *testing.T) { +func testAll2(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- combineTransaction --------------") combineTransaction(engine, t) fmt.Println("-------------- table --------------") @@ -3931,7 +3933,7 @@ func testAll2(engine *Engine, t *testing.T) { } // !nash! the 3rd set of the test is intended for non-cache enabled engine -func testAll3(engine *Engine, t *testing.T) { +func testAll3(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- processors TX --------------") testProcessorsTx(engine, t) fmt.Println("-------------- insert pointer data --------------") @@ -3946,7 +3948,7 @@ func testAll3(engine *Engine, t *testing.T) { testStringPK(engine, t) } -func testAllSnakeMapper(engine *Engine, t *testing.T) { +func testAllSnakeMapper(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- query --------------") testQuery(engine, t) fmt.Println("-------------- exec --------------") @@ -3961,7 +3963,7 @@ func testAllSnakeMapper(engine *Engine, t *testing.T) { having(engine, t) } -func testAllSameMapper(engine *Engine, t *testing.T) { +func testAllSameMapper(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- query --------------") testQuerySameMapper(engine, t) fmt.Println("-------------- exec --------------") diff --git a/benchmark.bat b/tests/benchmark.bat similarity index 100% rename from benchmark.bat rename to tests/benchmark.bat diff --git a/benchmark.sh b/tests/benchmark.sh similarity index 100% rename from benchmark.sh rename to tests/benchmark.sh diff --git a/benchmark_base_test.go b/tests/benchmark_base_test.go similarity index 93% rename from benchmark_base_test.go rename to tests/benchmark_base_test.go index d263da06..5ea598e5 100644 --- a/benchmark_base_test.go +++ b/tests/benchmark_base_test.go @@ -3,6 +3,8 @@ package xorm import ( "database/sql" "testing" + + "github.com/lunny/xorm" ) type BigStruct struct { @@ -77,7 +79,7 @@ func doBenchDriver(newdriver func() (*sql.DB, error), createTableSql, } } -func doBenchInsert(engine *Engine, b *testing.B) { +func doBenchInsert(engine *xorm.Engine, b *testing.B) { b.StopTimer() bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} err := engine.CreateTables(bs) @@ -103,7 +105,7 @@ func doBenchInsert(engine *Engine, b *testing.B) { } } -func doBenchFind(engine *Engine, b *testing.B) { +func doBenchFind(engine *xorm.Engine, b *testing.B) { b.StopTimer() bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} err := engine.CreateTables(bs) @@ -138,7 +140,7 @@ func doBenchFind(engine *Engine, b *testing.B) { } } -func doBenchFindPtr(engine *Engine, b *testing.B) { +func doBenchFindPtr(engine *xorm.Engine, b *testing.B) { b.StopTimer() bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} err := engine.CreateTables(bs) diff --git a/mssql_test.go b/tests/mssql_test.go similarity index 87% rename from mssql_test.go rename to tests/mssql_test.go index a9093866..101d07ae 100644 --- a/mssql_test.go +++ b/tests/mssql_test.go @@ -8,12 +8,13 @@ import ( "testing" _ "github.com/lunny/godbc" + "github.com/lunny/xorm" ) const mssqlConnStr = "driver={SQL Server};Server=192.168.20.135;Database=xorm_test; uid=sa; pwd=1234;" -func newMssqlEngine() (*Engine, error) { - return NewEngine("odbc", mssqlConnStr) +func newMssqlEngine() (*xorm.Engine, error) { + return xorm.NewEngine("odbc", mssqlConnStr) } func TestMssql(t *testing.T) { @@ -39,7 +40,7 @@ func TestMssqlWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -112,7 +113,7 @@ func BenchmarkMssqlCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -124,7 +125,7 @@ func BenchmarkMssqlCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -136,7 +137,7 @@ func BenchmarkMssqlCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/mymysql_test.go b/tests/mymysql_test.go similarity index 80% rename from mymysql_test.go rename to tests/mymysql_test.go index 0ad8aace..c64261dc 100644 --- a/mymysql_test.go +++ b/tests/mymysql_test.go @@ -4,6 +4,7 @@ import ( "database/sql" "testing" + "github.com/lunny/xorm" _ "github.com/ziutek/mymysql/godrv" ) @@ -20,7 +21,7 @@ func TestMyMysql(t *testing.T) { t.Error(err) return } - engine, err := NewEngine("mymysql", "xorm_test/root/") + engine, err := xorm.NewEngine("mymysql", "xorm_test/root/") defer engine.Close() if err != nil { t.Error(err) @@ -42,13 +43,13 @@ func TestMyMysqlWithCache(t *testing.T) { t.Error(err) return } - engine, err := NewEngine("mymysql", "xorm_test2/root/") + engine, err := xorm.NewEngine("mymysql", "xorm_test2/root/") defer engine.Close() if err != nil { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -58,8 +59,8 @@ func TestMyMysqlWithCache(t *testing.T) { testAll2(engine, t) } -func newMyMysqlEngine() (*Engine, error) { - return NewEngine("mymysql", "xorm_test2/root/") +func newMyMysqlEngine() (*xorm.Engine, error) { + return xorm.NewEngine("mymysql", "xorm_test2/root/") } func newMyMysqlDriverDB() (*sql.DB, error) { @@ -77,7 +78,7 @@ func BenchmarkMyMysqlDriverFind(t *testing.B) { } func mymysqlDdlImport() error { - engine, err := NewEngine("mymysql", "/root/") + engine, err := xorm.NewEngine("mymysql", "/root/") if err != nil { return err } @@ -86,7 +87,7 @@ func mymysqlDdlImport() error { engine.ShowWarn = showTestSql engine.ShowDebug = showTestSql - sqlResults, _ := engine.Import("tests/mysql_ddl.sql") + sqlResults, _ := engine.Import("testdata/mysql_ddl.sql") engine.LogDebug("sql results: %v", sqlResults) engine.Close() return nil @@ -135,7 +136,7 @@ func BenchmarkMyMysqlCacheInsert(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -148,7 +149,7 @@ func BenchmarkMyMysqlCacheFind(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -161,7 +162,7 @@ func BenchmarkMyMysqlCacheFindPtr(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/mysql_test.go b/tests/mysql_test.go similarity index 79% rename from mysql_test.go rename to tests/mysql_test.go index 6c356afe..f42be512 100644 --- a/mysql_test.go +++ b/tests/mysql_test.go @@ -5,6 +5,7 @@ import ( "testing" _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" ) /* @@ -19,7 +20,7 @@ func TestMysql(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test?charset=utf8") + engine, err := xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8") defer engine.Close() if err != nil { t.Error(err) @@ -43,7 +44,7 @@ func TestMysqlSameMapper(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test1?charset=utf8") + engine, err := xorm.NewEngine("mysql", "root:@/xorm_test1?charset=utf8") defer engine.Close() if err != nil { t.Error(err) @@ -53,7 +54,7 @@ func TestMysqlSameMapper(t *testing.T) { engine.ShowErr = showTestSql engine.ShowWarn = showTestSql engine.ShowDebug = showTestSql - engine.SetMapper(SameMapper{}) + engine.SetMapper(xorm.SameMapper{}) testAll(engine, t) testAllSameMapper(engine, t) @@ -68,13 +69,13 @@ func TestMysqlWithCache(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test2?charset=utf8") + engine, err := xorm.NewEngine("mysql", "root:@/xorm_test2?charset=utf8") defer engine.Close() if err != nil { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -92,14 +93,14 @@ func TestMysqlWithCacheSameMapper(t *testing.T) { return } - engine, err := NewEngine("mysql", "root:@/xorm_test3?charset=utf8") + engine, err := xorm.NewEngine("mysql", "root:@/xorm_test3?charset=utf8") defer engine.Close() if err != nil { t.Error(err) return } - engine.SetMapper(SameMapper{}) - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetMapper(xorm.SameMapper{}) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -110,12 +111,12 @@ func TestMysqlWithCacheSameMapper(t *testing.T) { testAll2(engine, t) } -func newMysqlEngine() (*Engine, error) { - return NewEngine("mysql", "root:@/xorm_test?charset=utf8") +func newMysqlEngine() (*xorm.Engine, error) { + return xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8") } func mysqlDdlImport() error { - engine, err := NewEngine("mysql", "root:@/?charset=utf8") + engine, err := xorm.NewEngine("mysql", "root:@/?charset=utf8") if err != nil { return err } @@ -124,7 +125,7 @@ func mysqlDdlImport() error { engine.ShowWarn = showTestSql engine.ShowDebug = showTestSql - sqlResults, _ := engine.Import("tests/mysql_ddl.sql") + sqlResults, _ := engine.Import("testdata/mysql_ddl.sql") engine.LogDebug("sql results: %v", sqlResults) engine.Close() return nil @@ -189,7 +190,7 @@ func BenchmarkMysqlCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -201,7 +202,7 @@ func BenchmarkMysqlCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -213,7 +214,7 @@ func BenchmarkMysqlCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/postgres_test.go b/tests/postgres_test.go similarity index 87% rename from postgres_test.go rename to tests/postgres_test.go index 32fb8b17..9c2ee2d8 100644 --- a/postgres_test.go +++ b/tests/postgres_test.go @@ -5,14 +5,15 @@ import ( "testing" _ "github.com/lib/pq" + "github.com/lunny/xorm" ) //var connStr string = "dbname=xorm_test user=lunny password=1234 sslmode=disable" var connStr string = "dbname=xorm_test sslmode=disable" -func newPostgresEngine() (*Engine, error) { - orm, err := NewEngine("postgres", connStr) +func newPostgresEngine() (*xorm.Engine, error) { + orm, err := xorm.NewEngine("postgres", connStr) if err != nil { return nil, err } @@ -58,7 +59,7 @@ func TestPostgresWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) defer engine.Close() engine.ShowSQL = showTestSql engine.ShowErr = showTestSql @@ -77,7 +78,7 @@ func TestPostgresSameMapper(t *testing.T) { return } defer engine.Close() - engine.SetMapper(SameMapper{}) + engine.SetMapper(xorm.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -95,9 +96,9 @@ func TestPostgresWithCacheSameMapper(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) defer engine.Close() - engine.SetMapper(SameMapper{}) + engine.SetMapper(xorm.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -167,7 +168,7 @@ func BenchmarkPostgresCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -180,7 +181,7 @@ func BenchmarkPostgresCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -193,7 +194,7 @@ func BenchmarkPostgresCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/sqlite3_test.go b/tests/sqlite3_test.go similarity index 86% rename from sqlite3_test.go rename to tests/sqlite3_test.go index aa66cb4e..aa10af64 100644 --- a/sqlite3_test.go +++ b/tests/sqlite3_test.go @@ -5,12 +5,13 @@ import ( "os" "testing" + "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" ) -func newSqlite3Engine() (*Engine, error) { +func newSqlite3Engine() (*xorm.Engine, error) { os.Remove("./test.db") - return NewEngine("sqlite3", "./test.db") + return xorm.NewEngine("sqlite3", "./test.db") } func newSqlite3DriverDB() (*sql.DB, error) { @@ -43,7 +44,7 @@ func TestSqlite3WithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -61,7 +62,7 @@ func TestSqlite3SameMapper(t *testing.T) { t.Error(err) return } - engine.SetMapper(SameMapper{}) + engine.SetMapper(xorm.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -80,8 +81,8 @@ func TestSqlite3WithCacheSameMapper(t *testing.T) { t.Error(err) return } - engine.SetMapper(SameMapper{}) - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetMapper(xorm.SameMapper{}) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -151,7 +152,7 @@ func BenchmarkSqlite3CacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -163,7 +164,7 @@ func BenchmarkSqlite3CacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -175,6 +176,6 @@ func BenchmarkSqlite3CacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(NewLRUCacher(NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/mysql_ddl.sql b/tests/testdata/mysql_ddl.sql similarity index 100% rename from tests/mysql_ddl.sql rename to tests/testdata/mysql_ddl.sql From e77fca31ae8dfc1471bd99faffb1fc067aced9c5 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jan 2014 10:07:11 +0800 Subject: [PATCH 10/55] move tests to tests subdir & refactoring --- cache.go => caches/lruCacher.go | 193 +++++++------------------------- caches/memoryStore.go | 49 ++++++++ core/cache.go | 77 +++++++++++++ core/db.go | 127 ++++++++++++++++++--- core/db_test.go | 189 ++++++++++++++++++++++++++++++- core/dialect.go | 2 +- mapper.go => core/mapper.go | 2 +- core/pk.go | 25 +++++ core/pk_test.go | 22 ++++ dialects/postgres.go | 2 +- engine.go | 45 +++++--- session.go | 126 +++++++++++++-------- statement.go | 47 +++----- tests/base_test.go | 44 ++++---- tests/benchmark_base_test.go | 2 +- tests/mssql_test.go | 11 +- tests/mymysql_test.go | 11 +- tests/mysql_test.go | 18 +-- tests/postgres_test.go | 18 +-- tests/sqlite3_test.go | 18 +-- tests/testdata/mysql_ddl.sql | 7 +- xorm.go | 14 ++- 22 files changed, 721 insertions(+), 328 deletions(-) rename cache.go => caches/lruCacher.go (59%) create mode 100644 caches/memoryStore.go create mode 100644 core/cache.go rename mapper.go => core/mapper.go (99%) create mode 100644 core/pk.go create mode 100644 core/pk_test.go diff --git a/cache.go b/caches/lruCacher.go similarity index 59% rename from cache.go rename to caches/lruCacher.go index e1ccc0d1..6f851922 100644 --- a/cache.go +++ b/caches/lruCacher.go @@ -1,107 +1,21 @@ -package xorm +//LRUCacher implements Cacher according to LRU algorithm +package caches import ( "container/list" - "errors" "fmt" - "strconv" - "strings" "sync" "time" + + "github.com/lunny/xorm/core" ) -const ( - // default cache expired time - CacheExpired = 60 * time.Minute - // not use now - CacheMaxMemory = 256 - // evey ten minutes to clear all expired nodes - CacheGcInterval = 10 * time.Minute - // each time when gc to removed max nodes - CacheGcMaxRemoved = 20 -) - -// CacheStore is a interface to store cache -type CacheStore interface { - Put(key, value interface{}) error - Get(key interface{}) (interface{}, error) - Del(key interface{}) error -} - -// MemoryStore implements CacheStore provide local machine -// memory store -type MemoryStore struct { - store map[interface{}]interface{} - mutex sync.RWMutex -} - -func NewMemoryStore() *MemoryStore { - return &MemoryStore{store: make(map[interface{}]interface{})} -} - -func (s *MemoryStore) Put(key, value interface{}) error { - s.mutex.Lock() - defer s.mutex.Unlock() - s.store[key] = value - return nil -} - -func (s *MemoryStore) Get(key interface{}) (interface{}, error) { - s.mutex.RLock() - defer s.mutex.RUnlock() - if v, ok := s.store[key]; ok { - return v, nil - } - - return nil, ErrNotExist -} - -func (s *MemoryStore) Del(key interface{}) error { - s.mutex.Lock() - defer s.mutex.Unlock() - delete(s.store, key) - return nil -} - -// Cacher is an interface to provide cache -type Cacher interface { - GetIds(tableName, sql string) interface{} - GetBean(tableName string, id int64) interface{} - PutIds(tableName, sql string, ids interface{}) - PutBean(tableName string, id int64, obj interface{}) - DelIds(tableName, sql string) - DelBean(tableName string, id int64) - ClearIds(tableName string) - ClearBeans(tableName string) -} - -type idNode struct { - tbName string - id int64 - lastVisit time.Time -} - -type sqlNode struct { - tbName string - sql string - lastVisit time.Time -} - -func newIdNode(tbName string, id int64) *idNode { - return &idNode{tbName, id, time.Now()} -} - -func newSqlNode(tbName, sql string) *sqlNode { - return &sqlNode{tbName, sql, time.Now()} -} - -// LRUCacher implements Cacher according to LRU algorithm type LRUCacher struct { idList *list.List sqlList *list.List - idIndex map[string]map[interface{}]*list.Element - sqlIndex map[string]map[interface{}]*list.Element - store CacheStore + idIndex map[string]map[string]*list.Element + sqlIndex map[string]map[string]*list.Element + store core.CacheStore Max int mutex sync.Mutex Expired time.Duration @@ -109,25 +23,17 @@ type LRUCacher struct { GcInterval time.Duration } -func newLRUCacher(store CacheStore, expired time.Duration, maxSize int, max int) *LRUCacher { +func NewLRUCacher(store core.CacheStore, expired time.Duration, maxSize int, max int) *LRUCacher { cacher := &LRUCacher{store: store, idList: list.New(), sqlList: list.New(), Expired: expired, maxSize: maxSize, - GcInterval: CacheGcInterval, Max: max, - sqlIndex: make(map[string]map[interface{}]*list.Element), - idIndex: make(map[string]map[interface{}]*list.Element), + GcInterval: core.CacheGcInterval, Max: max, + sqlIndex: make(map[string]map[string]*list.Element), + idIndex: make(map[string]map[string]*list.Element), } cacher.RunGC() return cacher } -func NewLRUCacher(store CacheStore, max int) *LRUCacher { - return newLRUCacher(store, CacheExpired, CacheMaxMemory, max) -} - -func NewLRUCacher2(store CacheStore, expired time.Duration, max int) *LRUCacher { - return newLRUCacher(store, expired, 0, max) -} - //func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher { // return newLRUCacher(store, expired, maxSize, 0) //} @@ -148,7 +54,7 @@ func (m *LRUCacher) GC() { defer m.mutex.Unlock() var removedNum int for e := m.idList.Front(); e != nil; { - if removedNum <= CacheGcMaxRemoved && + if removedNum <= core.CacheGcMaxRemoved && time.Now().Sub(e.Value.(*idNode).lastVisit) > m.Expired { removedNum++ next := e.Next() @@ -164,7 +70,7 @@ func (m *LRUCacher) GC() { removedNum = 0 for e := m.sqlList.Front(); e != nil; { - if removedNum <= CacheGcMaxRemoved && + if removedNum <= core.CacheGcMaxRemoved && time.Now().Sub(e.Value.(*sqlNode).lastVisit) > m.Expired { removedNum++ next := e.Next() @@ -184,7 +90,7 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.sqlIndex[tableName]; !ok { - m.sqlIndex[tableName] = make(map[interface{}]*list.Element) + m.sqlIndex[tableName] = make(map[string]*list.Element) } if v, err := m.store.Get(sql); err == nil { if el, ok := m.sqlIndex[tableName][sql]; !ok { @@ -209,11 +115,11 @@ func (m *LRUCacher) GetIds(tableName, sql string) interface{} { } // Get bean according tableName and id from cache -func (m *LRUCacher) GetBean(tableName string, id int64) interface{} { +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[interface{}]*list.Element) + m.idIndex[tableName] = make(map[string]*list.Element) } tid := genId(tableName, id) if v, err := m.store.Get(tid); err == nil { @@ -248,7 +154,7 @@ func (m *LRUCacher) clearIds(tableName string) { m.store.Del(sql) } } - m.sqlIndex[tableName] = make(map[interface{}]*list.Element) + m.sqlIndex[tableName] = make(map[string]*list.Element) } func (m *LRUCacher) ClearIds(tableName string) { @@ -261,11 +167,11 @@ 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.(int64)) + tid := genId(tableName, id) m.store.Del(tid) } } - m.idIndex[tableName] = make(map[interface{}]*list.Element) + m.idIndex[tableName] = make(map[string]*list.Element) } func (m *LRUCacher) ClearBeans(tableName string) { @@ -278,7 +184,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { m.mutex.Lock() defer m.mutex.Unlock() if _, ok := m.sqlIndex[tableName]; !ok { - m.sqlIndex[tableName] = make(map[interface{}]*list.Element) + m.sqlIndex[tableName] = make(map[string]*list.Element) } if el, ok := m.sqlIndex[tableName][sql]; !ok { el = m.sqlList.PushBack(newSqlNode(tableName, sql)) @@ -294,7 +200,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { } } -func (m *LRUCacher) PutBean(tableName string, id int64, obj interface{}) { +func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { m.mutex.Lock() defer m.mutex.Unlock() var el *list.Element @@ -331,7 +237,7 @@ func (m *LRUCacher) DelIds(tableName, sql string) { m.delIds(tableName, sql) } -func (m *LRUCacher) delBean(tableName string, id int64) { +func (m *LRUCacher) delBean(tableName string, id string) { tid := genId(tableName, id) if el, ok := m.idIndex[tableName][id]; ok { delete(m.idIndex[tableName], id) @@ -341,55 +247,36 @@ func (m *LRUCacher) delBean(tableName string, id int64) { m.store.Del(tid) } -func (m *LRUCacher) DelBean(tableName string, id int64) { +func (m *LRUCacher) DelBean(tableName string, id string) { m.mutex.Lock() defer m.mutex.Unlock() m.delBean(tableName, id) } -func encodeIds(ids []int64) (s string) { - s = "[" - for _, id := range ids { - s += fmt.Sprintf("%v,", id) - } - s = s[:len(s)-1] + "]" - return +type idNode struct { + tbName string + id string + lastVisit time.Time } -func decodeIds(s string) []int64 { - res := make([]int64, 0) - if len(s) >= 2 { - ss := strings.Split(s[1:len(s)-1], ",") - for _, s := range ss { - i, err := strconv.ParseInt(s, 10, 64) - if err != nil { - return res - } - res = append(res, i) - } - } - return res -} - -func getCacheSql(m Cacher, tableName, sql string, args interface{}) ([]int64, error) { - bytes := m.GetIds(tableName, genSqlKey(sql, args)) - if bytes == nil { - return nil, errors.New("Not Exist") - } - objs := decodeIds(bytes.(string)) - return objs, nil -} - -func putCacheSql(m Cacher, ids []int64, tableName, sql string, args interface{}) error { - bytes := encodeIds(ids) - m.PutIds(tableName, genSqlKey(sql, args), bytes) - return nil +type sqlNode struct { + tbName string + sql string + lastVisit time.Time } func genSqlKey(sql string, args interface{}) string { return fmt.Sprintf("%v-%v", sql, args) } -func genId(prefix string, id int64) string { +func genId(prefix string, id string) string { return fmt.Sprintf("%v-%v", prefix, id) } + +func newIdNode(tbName string, id string) *idNode { + return &idNode{tbName, id, time.Now()} +} + +func newSqlNode(tbName, sql string) *sqlNode { + return &sqlNode{tbName, sql, time.Now()} +} diff --git a/caches/memoryStore.go b/caches/memoryStore.go new file mode 100644 index 00000000..8bbb6b99 --- /dev/null +++ b/caches/memoryStore.go @@ -0,0 +1,49 @@ +// MemoryStore implements CacheStore provide local machine +package caches + +import ( + "errors" + "sync" + + "github.com/lunny/xorm/core" +) + +var ( + ErrNotExist = errors.New("key not exist") +) + +var _ core.CacheStore = NewMemoryStore() + +// memory store +type MemoryStore struct { + store map[interface{}]interface{} + mutex sync.RWMutex +} + +func NewMemoryStore() *MemoryStore { + return &MemoryStore{store: make(map[interface{}]interface{})} +} + +func (s *MemoryStore) Put(key string, value interface{}) error { + s.mutex.Lock() + defer s.mutex.Unlock() + s.store[key] = value + return nil +} + +func (s *MemoryStore) Get(key string) (interface{}, error) { + s.mutex.RLock() + defer s.mutex.RUnlock() + if v, ok := s.store[key]; ok { + return v, nil + } + + return nil, ErrNotExist +} + +func (s *MemoryStore) Del(key string) error { + s.mutex.Lock() + defer s.mutex.Unlock() + delete(s.store, key) + return nil +} diff --git a/core/cache.go b/core/cache.go new file mode 100644 index 00000000..bedd22a7 --- /dev/null +++ b/core/cache.go @@ -0,0 +1,77 @@ +package core + +import ( + "encoding/json" + "errors" + "fmt" + "time" +) + +const ( + // default cache expired time + CacheExpired = 60 * time.Minute + // not use now + CacheMaxMemory = 256 + // evey ten minutes to clear all expired nodes + CacheGcInterval = 10 * time.Minute + // each time when gc to removed max nodes + CacheGcMaxRemoved = 20 +) + +// CacheStore is a interface to store cache +type CacheStore interface { + // key is primary key or composite primary key or unique key's value + // value is struct's pointer + // key format : -p--... + Put(key string, value interface{}) error + Get(key string) (interface{}, error) + Del(key string) error +} + +// Cacher is an interface to provide cache +// id format : u--... +type Cacher interface { + GetIds(tableName, sql string) interface{} + GetBean(tableName string, id string) interface{} + PutIds(tableName, sql string, ids interface{}) + PutBean(tableName string, id string, obj interface{}) + DelIds(tableName, sql string) + DelBean(tableName string, id string) + ClearIds(tableName string) + ClearBeans(tableName string) +} + +func encodeIds(ids []PK) (string, error) { + b, err := json.Marshal(ids) + if err != nil { + return "", err + } + return string(b), nil +} + +func decodeIds(s string) ([]PK, error) { + pks := make([]PK, 0) + err := json.Unmarshal([]byte(s), &pks) + return pks, err +} + +func GetCacheSql(m Cacher, tableName, sql string, args interface{}) ([]PK, error) { + bytes := m.GetIds(tableName, GenSqlKey(sql, args)) + if bytes == nil { + return nil, errors.New("Not Exist") + } + return decodeIds(bytes.(string)) +} + +func PutCacheSql(m Cacher, ids []PK, tableName, sql string, args interface{}) error { + bytes, err := encodeIds(ids) + if err != nil { + return err + } + m.PutIds(tableName, GenSqlKey(sql, args), bytes) + return nil +} + +func GenSqlKey(sql string, args interface{}) string { + return fmt.Sprintf("%v-%v", sql, args) +} diff --git a/core/db.go b/core/db.go index 9249c2a8..3f96b343 100644 --- a/core/db.go +++ b/core/db.go @@ -2,43 +2,138 @@ package core import ( "database/sql" + "errors" "reflect" ) type DB struct { *sql.DB + Mapper IMapper } func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) - return &DB{db}, err + return &DB{db, &SnakeMapper{}}, err } func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { rows, err := db.DB.Query(query, args...) - return &Rows{rows}, err + return &Rows{rows, db.Mapper}, err } type Rows struct { *sql.Rows + Mapper IMapper } -func (rs *Rows) Scan(dest ...interface{}) error { - newDest := make([]interface{}, 0) - for _, s := range dest { - vv := reflect.ValueOf(s) - switch vv.Kind() { - case reflect.Ptr: - vvv := vv.Elem() - if vvv.Kind() == reflect.Struct { - for j := 0; j < vvv.NumField(); j++ { - newDest = append(newDest, vvv.FieldByIndex([]int{j}).Addr().Interface()) - } - } else { - newDest = append(newDest, s) - } +// scan data to a struct's pointer according field index +func (rs *Rows) ScanStruct(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") + } + + vvv := vv.Elem() + newDest := make([]interface{}, vvv.NumField()) + + for j := 0; j < vvv.NumField(); j++ { + newDest[j] = vvv.Field(j).Addr().Interface() + } + + return rs.Rows.Scan(newDest...) +} + +// scan data to a struct's pointer according field name +func (rs *Rows) ScanStruct2(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 + } + + vvv := vv.Elem() + newDest := make([]interface{}, len(cols)) + + for j, name := range cols { + f := vvv.FieldByName(rs.Mapper.Table2Obj(name)) + if f.IsValid() { + newDest[j] = f.Addr().Interface() + } else { + var v interface{} + newDest[j] = &v } } return rs.Rows.Scan(newDest...) } + +// 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 + } + + for i, _ := range cols { + 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 { + 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 +} diff --git a/core/db_test.go b/core/db_test.go index 8894bd17..26f2e04d 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -2,7 +2,9 @@ package core import ( "fmt" + "os" "testing" + "time" _ "github.com/mattn/go-sqlite3" ) @@ -20,7 +22,8 @@ type User struct { NickName string } -func TestQuery(t *testing.T) { +func TestOriQuery(t *testing.T) { + os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { t.Error(err) @@ -31,23 +34,197 @@ func TestQuery(t *testing.T) { t.Error(err) } - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + t.Error(err) + } + } + + rows, err := db.Query("select * from user") if err != nil { t.Error(err) } + defer rows.Close() + + start := time.Now() + + for rows.Next() { + var Id int64 + var Name, Title, Alias, NickName string + var Age float32 + err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName) + if err != nil { + t.Error(err) + } + fmt.Println(Id, Name, Title, Age, Alias, NickName) + } + + fmt.Println("ori ------", time.Now().Sub(start), "ns") +} + +func TestStructQuery(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + t.Error(err) + } + } + + rows, err := db.Query("select * from user") + if err != nil { + t.Error(err) + } + defer rows.Close() + start := time.Now() + + for rows.Next() { + var user User + err = rows.ScanStruct(&user) + if err != nil { + t.Error(err) + } + fmt.Println(user) + } + fmt.Println("struct ------", time.Now().Sub(start)) +} + +func TestStruct2Query(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + t.Error(err) + } + } + + db.Mapper = &SnakeMapper{} + + rows, err := db.Query("select * from user") + if err != nil { + t.Error(err) + } + defer rows.Close() + start := time.Now() + + for rows.Next() { + var user User + err = rows.ScanStruct2(&user) + if err != nil { + t.Error(err) + } + fmt.Println(user) + } + fmt.Println("struct2 ------", time.Now().Sub(start)) +} + +func TestSliceQuery(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + t.Error(err) + } + } rows, err := db.Query("select * from user") if err != nil { t.Error(err) } + defer rows.Close() + + cols, err := rows.Columns() + if err != nil { + t.Error(err) + } + + start := time.Now() + for rows.Next() { - var user User - err = rows.Scan(&user) + slice := make([]interface{}, len(cols)) + err = rows.ScanSlice(&slice) if err != nil { t.Error(err) } - fmt.Println(user) + fmt.Println(slice) } + + fmt.Println("slice ------", time.Now().Sub(start)) +} + +func TestMapQuery(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + t.Error(err) + } + } + + rows, err := db.Query("select * from user") + if err != nil { + t.Error(err) + } + + defer rows.Close() + + start := time.Now() + + for rows.Next() { + m := make(map[string]interface{}) + err = rows.ScanMap(&m) + if err != nil { + t.Error(err) + } + fmt.Println(m) + } + + fmt.Println("map ------", time.Now().Sub(start)) } diff --git a/core/dialect.go b/core/dialect.go index fd7a483e..ecba0844 100644 --- a/core/dialect.go +++ b/core/dialect.go @@ -107,7 +107,7 @@ func (b *Base) CreateTableSql(table *Table, tableName, storeEngine, charset stri if len(pkList) > 1 { sql += "PRIMARY KEY ( " - sql += strings.Join(pkList, ",") + sql += b.Quote(strings.Join(pkList, b.Quote(","))) sql += " ), " } diff --git a/mapper.go b/core/mapper.go similarity index 99% rename from mapper.go rename to core/mapper.go index 2e9c220a..0011dde5 100644 --- a/mapper.go +++ b/core/mapper.go @@ -1,4 +1,4 @@ -package xorm +package core import ( "strings" diff --git a/core/pk.go b/core/pk.go new file mode 100644 index 00000000..61d1371e --- /dev/null +++ b/core/pk.go @@ -0,0 +1,25 @@ +package core + +import ( + "encoding/json" +) + +type PK []interface{} + +func NewPK(pks ...interface{}) *PK { + p := PK(pks) + return &p +} + +func (p *PK) ToString() (string, error) { + bs, err := json.Marshal(*p) + if err != nil { + return "", nil + } + + return string(bs), nil +} + +func (p *PK) FromString(content string) error { + return json.Unmarshal([]byte(content), p) +} diff --git a/core/pk_test.go b/core/pk_test.go new file mode 100644 index 00000000..5245e574 --- /dev/null +++ b/core/pk_test.go @@ -0,0 +1,22 @@ +package core + +import ( + "fmt" + "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) +} diff --git a/dialects/postgres.go b/dialects/postgres.go index dbf9ca09..8a37d117 100644 --- a/dialects/postgres.go +++ b/dialects/postgres.go @@ -26,7 +26,7 @@ func (db *postgres) SqlType(c *Column) string { switch t := c.SQLType.Name; t { case TinyInt: res = SmallInt - + return res case MediumInt, Int, Integer: if c.IsAutoIncrement { return Serial diff --git a/engine.go b/engine.go index a6509828..dc3fd637 100644 --- a/engine.go +++ b/engine.go @@ -16,13 +16,11 @@ import ( "github.com/lunny/xorm/core" ) -type PK []interface{} - // Engine is the major struct of xorm, it means a database manager. // Commonly, an application only need one engine type Engine struct { - ColumnMapper IMapper - TableMapper IMapper + ColumnMapper core.IMapper + TableMapper core.IMapper TagIdentifier string DriverName string DataSourceName string @@ -37,20 +35,20 @@ type Engine struct { Pool IConnectPool Filters []core.Filter Logger io.Writer - Cacher Cacher - tableCachers map[reflect.Type]Cacher + Cacher core.Cacher + tableCachers map[reflect.Type]core.Cacher } -func (engine *Engine) SetMapper(mapper IMapper) { +func (engine *Engine) SetMapper(mapper core.IMapper) { engine.SetTableMapper(mapper) engine.SetColumnMapper(mapper) } -func (engine *Engine) SetTableMapper(mapper IMapper) { +func (engine *Engine) SetTableMapper(mapper core.IMapper) { engine.TableMapper = mapper } -func (engine *Engine) SetColumnMapper(mapper IMapper) { +func (engine *Engine) SetColumnMapper(mapper core.IMapper) { engine.ColumnMapper = mapper } @@ -100,7 +98,7 @@ func (engine *Engine) SetMaxIdleConns(conns int) { } // SetDefaltCacher set the default cacher. Xorm's default not enable cacher. -func (engine *Engine) SetDefaultCacher(cacher Cacher) { +func (engine *Engine) SetDefaultCacher(cacher core.Cacher) { engine.Cacher = cacher } @@ -119,7 +117,7 @@ func (engine *Engine) NoCascade() *Session { } // Set a table use a special cacher -func (engine *Engine) MapCacher(bean interface{}, cacher Cacher) { +func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { t := rType(bean) engine.autoMapType(t) engine.tableCachers[t] = cacher @@ -409,7 +407,7 @@ func (engine *Engine) mapType(t reflect.Type) *core.Table { return mappingTable(t, engine.TableMapper, engine.ColumnMapper, engine.dialect, engine.TagIdentifier) } -func mappingTable(t reflect.Type, tableMapper IMapper, colMapper IMapper, dialect core.Dialect, tagId string) *core.Table { +func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapper, dialect core.Dialect, tagId string) *core.Table { table := core.NewEmptyTable() table.Name = tableMapper.Obj2Table(t.Name()) table.Type = t @@ -517,6 +515,7 @@ func mappingTable(t reflect.Type, tableMapper IMapper, colMapper IMapper, dialec if col.Length2 == 0 { col.Length2 = col.SQLType.DefaultLength2 } + fmt.Println("======", col) if col.Name == "" { col.Name = colMapper.Obj2Table(t.Field(i).Name) } @@ -613,6 +612,24 @@ func (engine *Engine) IsTableExist(bean interface{}) (bool, error) { return has, err } +func (engine *Engine) IdOf(bean interface{}) core.PK { + table := engine.autoMap(bean) + v := reflect.Indirect(reflect.ValueOf(bean)) + pk := make([]interface{}, len(table.PrimaryKeys)) + for i, col := range table.PKColumns() { + pkField := v.FieldByName(col.FieldName) + switch pkField.Kind() { + case reflect.String: + pk[i] = pkField.String() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + pk[i] = pkField.Int() + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + pk[i] = pkField.Uint() + } + } + return core.PK(pk) +} + // create indexes func (engine *Engine) CreateIndexes(bean interface{}) error { session := engine.NewSession() @@ -627,7 +644,7 @@ func (engine *Engine) CreateUniques(bean interface{}) error { return session.CreateUniques(bean) } -func (engine *Engine) getCacher(t reflect.Type) Cacher { +func (engine *Engine) getCacher(t reflect.Type) core.Cacher { if cacher, ok := engine.tableCachers[t]; ok { return cacher } @@ -635,7 +652,7 @@ func (engine *Engine) getCacher(t reflect.Type) Cacher { } // If enabled cache, clear the cache bean -func (engine *Engine) ClearCacheBean(bean interface{}, id int64) error { +func (engine *Engine) ClearCacheBean(bean interface{}, id string) error { t := rType(bean) if t.Kind() != reflect.Struct { return errors.New("error params") diff --git a/session.go b/session.go index a983237b..382370c7 100644 --- a/session.go +++ b/session.go @@ -588,6 +588,7 @@ func (statement *Statement) convertIdSql(sqlStr string) string { if len(sqls) != 2 { return "" } + fmt.Println("-----", col) newsql := fmt.Sprintf("SELECT %v.%v FROM %v", statement.Engine.Quote(statement.TableName()), statement.Engine.Quote(col.Name), sqls[1]) return newsql @@ -612,14 +613,14 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf cacher := session.Engine.getCacher(session.Statement.RefTable.Type) tableName := session.Statement.TableName() session.Engine.LogDebug("[xorm:cacheGet] find sql:", newsql, args) - ids, err := getCacheSql(cacher, tableName, newsql, args) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { resultsSlice, err := session.query(newsql, args...) if err != nil { return false, err } session.Engine.LogDebug("[xorm:cacheGet] query ids:", resultsSlice) - ids = make([]int64, 0) + ids = make([]core.PK, 0) if len(resultsSlice) > 0 { data := resultsSlice[0] var id int64 @@ -631,10 +632,10 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf return false, err } } - ids = append(ids, id) + ids = append(ids, core.PK{id}) } session.Engine.LogDebug("[xorm:cacheGet] cache ids:", newsql, ids) - err = putCacheSql(cacher, ids, tableName, newsql, args) + err = core.PutCacheSql(cacher, ids, tableName, newsql, args) if err != nil { return false, err } @@ -646,7 +647,11 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf structValue := reflect.Indirect(reflect.ValueOf(bean)) id := ids[0] session.Engine.LogDebug("[xorm:cacheGet] get bean:", tableName, id) - cacheBean := cacher.GetBean(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() @@ -664,7 +669,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf } session.Engine.LogDebug("[xorm:cacheGet] cache bean:", tableName, id, cacheBean) - cacher.PutBean(tableName, id, cacheBean) + cacher.PutBean(tableName, sid, cacheBean) } else { session.Engine.LogDebug("[xorm:cacheGet] cached bean:", tableName, id, cacheBean) has = true @@ -695,7 +700,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in table := session.Statement.RefTable cacher := session.Engine.getCacher(t) - ids, err := getCacheSql(cacher, session.Statement.TableName(), newsql, args) + ids, err := core.GetCacheSql(cacher, session.Statement.TableName(), newsql, args) if err != nil { //session.Engine.LogError(err) resultsSlice, err := session.query(newsql, args...) @@ -709,7 +714,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } tableName := session.Statement.TableName() - ids = make([]int64, 0) + ids = make([]core.PK, 0) if len(resultsSlice) > 0 { for _, data := range resultsSlice { //fmt.Println(data) @@ -722,11 +727,11 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return err } } - ids = append(ids, id) + ids = append(ids, core.PK{id}) } } session.Engine.LogDebug("[xorm:cacheFind] cache ids:", ids, tableName, newsql, args) - err = putCacheSql(cacher, ids, tableName, newsql, args) + err = core.PutCacheSql(cacher, ids, tableName, newsql, args) if err != nil { return err } @@ -735,34 +740,32 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in } sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr)) - pkFieldName := session.Statement.RefTable.PKColumns()[0].FieldName + //pkFieldName := session.Statement.RefTable.PKColumns()[0].FieldName - ididxes := make(map[int64]int) - var ides []interface{} = make([]interface{}, 0) + 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 { - bean := cacher.GetBean(tableName, id) + sid, err := id.ToString() + if err != nil { + return err + } + bean := cacher.GetBean(tableName, sid) if bean == nil { ides = append(ides, id) - ididxes[id] = idx + ididxes[sid] = idx } else { session.Engine.LogDebug("[xorm:cacheFind] cached bean:", tableName, id, bean) - pkField := reflect.Indirect(reflect.ValueOf(bean)).FieldByName(pkFieldName) - - var sid int64 - switch pkField.Type().Kind() { - case reflect.Int32, reflect.Int, reflect.Int64: - sid = pkField.Int() - case reflect.Uint, reflect.Uint32, reflect.Uint64: - sid = int64(pkField.Uint()) - default: - return ErrCacheFailed + pk := session.Engine.IdOf(bean) + xid, err := pk.ToString() + if err != nil { + return err } - if sid != id { - session.Engine.LogError("[xorm:cacheFind] error cache", id, sid, bean) + if sid != xid { + session.Engine.LogError("[xorm:cacheFind] error cache", xid, sid, bean) return ErrCacheFailed } temps[idx] = bean @@ -777,7 +780,19 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in beans := slices.Interface() //beans := reflect.New(sliceValue.Type()).Interface() //err = newSession.In("(id)", ides...).OrderBy(session.Statement.OrderStr).NoCache().Find(beans) - err = newSession.In("(id)", ides...).NoCache().Find(beans) + ff := make([][]interface{}, len(table.PrimaryKeys)) + for i, _ := range table.PrimaryKeys { + ff[i] = make([]interface{}, 0) + } + for _, ie := range ides { + for i, _ := range table.PrimaryKeys { + ff[i] = append(ff[i], ie[i]) + } + } + for i, name := range table.PrimaryKeys { + newSession.In(name, ff[i]...) + } + err = newSession.NoCache().Find(beans) if err != nil { return err } @@ -789,12 +804,16 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in rv = rv.Addr() } bean := rv.Interface() - id := reflect.Indirect(reflect.ValueOf(bean)).FieldByName(pkFieldName).Int() + id := session.Engine.IdOf(bean) + sid, err := id.ToString() + if err != nil { + return err + } //bean := vs.Index(i).Addr().Interface() - temps[ididxes[id]] = bean + temps[ididxes[sid]] = bean //temps[idxes[i]] = bean session.Engine.LogDebug("[xorm:cacheFind] cache bean:", tableName, id, bean) - cacher.PutBean(tableName, id, bean) + cacher.PutBean(tableName, sid, bean) } } @@ -811,16 +830,21 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(bean)))) } } else if sliceValue.Kind() == reflect.Map { - var key int64 + var key core.PK if table.PrimaryKeys[0] != "" { key = ids[j] - } else { - key = int64(j) } - if t.Kind() == reflect.Ptr { - sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.ValueOf(bean)) - } else { - sliceValue.SetMapIndex(reflect.ValueOf(key), reflect.Indirect(reflect.ValueOf(bean))) + + if len(key) == 1 { + ikey, err := strconv.ParseInt(fmt.Sprintf("%v", key[0]), 10, 64) + if err != nil { + return err + } + if t.Kind() == reflect.Ptr { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean)) + } else { + sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean))) + } } } /*} else { @@ -2762,7 +2786,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { cacher := session.Engine.getCacher(table.Type) tableName := session.Statement.TableName() session.Engine.LogDebug("[xorm:cacheUpdate] get cache sql", newsql, args[nStart:]) - ids, err := getCacheSql(cacher, tableName, newsql, args[nStart:]) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:]) if err != nil { resultsSlice, err := session.query(newsql, args[nStart:]...) if err != nil { @@ -2770,7 +2794,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { } session.Engine.LogDebug("[xorm:cacheUpdate] find updated id", resultsSlice) - ids = make([]int64, 0) + ids = make([]core.PK, 0) if len(resultsSlice) > 0 { for _, data := range resultsSlice { var id int64 @@ -2782,7 +2806,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { return err } } - ids = append(ids, id) + ids = append(ids, core.PK{id}) } } } /*else { @@ -2791,7 +2815,11 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { }*/ for _, id := range ids { - if bean := cacher.GetBean(tableName, id); bean != nil { + 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 @@ -2834,7 +2862,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { } session.Engine.LogDebug("[xorm:cacheUpdate] update cache", tableName, id, bean) - cacher.PutBean(tableName, id, bean) + cacher.PutBean(tableName, sid, bean) } } session.Engine.LogDebug("[xorm:cacheUpdate] clear cached table sql:", tableName) @@ -3047,13 +3075,13 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { cacher := session.Engine.getCacher(session.Statement.RefTable.Type) tableName := session.Statement.TableName() - ids, err := getCacheSql(cacher, tableName, newsql, args) + ids, err := core.GetCacheSql(cacher, tableName, newsql, args) if err != nil { resultsSlice, err := session.query(newsql, args...) if err != nil { return err } - ids = make([]int64, 0) + ids = make([]core.PK, 0) if len(resultsSlice) > 0 { for _, data := range resultsSlice { var id int64 @@ -3065,7 +3093,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { return err } } - ids = append(ids, id) + ids = append(ids, core.PK{id}) } } } /*else { @@ -3075,7 +3103,11 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { for _, id := range ids { session.Engine.LogDebug("[xorm:cacheDelete] delete cache obj", tableName, id) - cacher.DelBean(tableName, id) + sid, err := id.ToString() + if err != nil { + return err + } + cacher.DelBean(tableName, sid) } session.Engine.LogDebug("[xorm:cacheDelete] clear cache table", tableName) cacher.ClearIds(tableName) diff --git a/statement.go b/statement.go index 8b5ff430..23cb0591 100644 --- a/statement.go +++ b/statement.go @@ -24,7 +24,7 @@ type Statement struct { Start int LimitN int WhereStr string - IdParam *PK + IdParam *core.PK Params []interface{} OrderStr string JoinStr string @@ -421,24 +421,28 @@ func (statement *Statement) TableName() string { return "" } +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 { - idValue := reflect.ValueOf(id) idType := reflect.TypeOf(idValue.Interface()) switch idType { - case reflect.TypeOf(&PK{}): - if pkPtr, ok := (id).(*PK); ok { + case ptrPkType: + if pkPtr, ok := (id).(*core.PK); ok { statement.IdParam = pkPtr } - case reflect.TypeOf(PK{}): - if pk, ok := (id).(PK); ok { + case pkType: + if pk, ok := (id).(core.PK); ok { statement.IdParam = &pk } default: // TODO treat as int primitve for now, need to handle type check - statement.IdParam = &PK{id} + statement.IdParam = &core.PK{id} // !nashtsai! REVIEW although it will be user's mistake if called Id() twice with // different value and Id should be PK's field name, however, at this stage probably @@ -789,31 +793,12 @@ func (statement *Statement) genSelectSql(columnStr string) (a string) { } func (statement *Statement) processIdParam() { - if statement.IdParam != nil { - i := 0 - columns := statement.RefTable.ColumnsSeq() - colCnt := len(columns) - for _, elem := range *(statement.IdParam) { - for ; i < colCnt; i++ { - colName := columns[i] - col := statement.RefTable.GetColumn(colName) - if col.IsPrimaryKey { - statement.And(fmt.Sprintf("%v=?", col.Name), elem) - i++ - break - } - } - } - - // !nashtsai! REVIEW what if statement.IdParam has insufficient pk item? handle it - // as empty string for now, so this will result sql exec failed instead of unexpected - // false update/delete - for ; i < colCnt; i++ { - colName := columns[i] - col := statement.RefTable.GetColumn(colName) - if col.IsPrimaryKey { - statement.And(fmt.Sprintf("%v=?", col.Name), "") + for i, col := range statement.RefTable.PKColumns() { + if i < len(*(statement.IdParam)) { + statement.And(fmt.Sprintf("%v=?", statement.Engine.Quote(col.Name)), (*(statement.IdParam))[i]) + } else { + statement.And(fmt.Sprintf("%v=?", statement.Engine.Quote(col.Name)), "") } } } diff --git a/tests/base_test.go b/tests/base_test.go index cf12066a..26d6cf9c 100644 --- a/tests/base_test.go +++ b/tests/base_test.go @@ -1,4 +1,4 @@ -package xorm +package tests import ( "errors" @@ -755,7 +755,7 @@ func orderSameMapper(engine *xorm.Engine, t *testing.T) { func joinSameMapper(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) - err := engine.Join("LEFT", "`Userdetail`", "`Userinfo`.`(id)`=`Userdetail`.`(id)`").Find(&users) + err := engine.Join("LEFT", "`Userdetail`", "`Userinfo`.`(id)`=`Userdetail`.`Id`").Find(&users) if err != nil { t.Error(err) panic(err) @@ -1080,7 +1080,7 @@ func testCols(engine *xorm.Engine, t *testing.T) { func testColsSameMapper(engine *xorm.Engine, t *testing.T) { users := []Userinfo{} - err := engine.Cols("(id), Username").Find(&users) + err := engine.Cols("id, Username").Find(&users) if err != nil { t.Error(err) panic(err) @@ -1089,7 +1089,8 @@ func testColsSameMapper(engine *xorm.Engine, t *testing.T) { fmt.Println(users) tmpUsers := []tempUser{} - err = engine.Table("Userinfo").Cols("(id), Username").Find(&tmpUsers) + // TODO: should use cache + err = engine.NoCache().Table("Userinfo").Cols("id, Username").Find(&tmpUsers) if err != nil { t.Error(err) panic(err) @@ -2062,7 +2063,8 @@ func testVersion(engine *xorm.Engine, t *testing.T) { func testDistinct(engine *xorm.Engine, t *testing.T) { users := make([]Userinfo, 0) - err := engine.Distinct("departname").Find(&users) + departname := engine.TableMapper.Obj2Table("Departname") + err := engine.Distinct(departname).Find(&users) if err != nil { t.Error(err) panic(err) @@ -2079,7 +2081,7 @@ func testDistinct(engine *xorm.Engine, t *testing.T) { } users2 := make([]Depart, 0) - err = engine.Distinct("departname").Table(new(Userinfo)).Find(&users2) + err = engine.Distinct(departname).Table(new(Userinfo)).Find(&users2) if err != nil { t.Error(err) panic(err) @@ -2226,7 +2228,7 @@ func testPrefixTableName(engine *xorm.Engine, t *testing.T) { panic(err) } tempEngine.ShowSQL = true - mapper := xorm.NewPrefixMapper(xorm.SnakeMapper{}, "xlw_") + mapper := core.NewPrefixMapper(core.SnakeMapper{}, "xlw_") //tempEngine.SetMapper(mapper) tempEngine.SetTableMapper(mapper) exist, err := tempEngine.IsTableExist(&Userinfo{}) @@ -3738,7 +3740,7 @@ func testCompositeKey(engine *xorm.Engine, t *testing.T) { } var compositeKeyVal CompositeKey - has, err := engine.Id(xorm.PK{11, 22}).Get(&compositeKeyVal) + has, err := engine.Id(core.PK{11, 22}).Get(&compositeKeyVal) if err != nil { t.Error(err) } else if !has { @@ -3746,7 +3748,7 @@ func testCompositeKey(engine *xorm.Engine, t *testing.T) { } // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&xorm.PK{11, 22}).Get(&compositeKeyVal) + has, err = engine.Id(&core.PK{11, 22}).Get(&compositeKeyVal) if err != nil { t.Error(err) } else if !has { @@ -3754,14 +3756,14 @@ func testCompositeKey(engine *xorm.Engine, t *testing.T) { } compositeKeyVal = CompositeKey{UpdateStr: "test1"} - cnt, err = engine.Id(xorm.PK{11, 22}).Update(&compositeKeyVal) + cnt, err = engine.Id(core.PK{11, 22}).Update(&compositeKeyVal) if err != nil { t.Error(err) } else if cnt != 1 { t.Error(errors.New("can't update CompositeKey{11, 22}")) } - cnt, err = engine.Id(xorm.PK{11, 22}).Delete(&CompositeKey{}) + cnt, err = engine.Id(core.PK{11, 22}).Delete(&CompositeKey{}) if err != nil { t.Error(err) } else if cnt != 1 { @@ -3803,7 +3805,7 @@ func testCompositeKey2(engine *xorm.Engine, t *testing.T) { } var user User - has, err := engine.Id(xorm.PK{"11", 22}).Get(&user) + has, err := engine.Id(core.PK{"11", 22}).Get(&user) if err != nil { t.Error(err) } else if !has { @@ -3811,7 +3813,7 @@ func testCompositeKey2(engine *xorm.Engine, t *testing.T) { } // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&xorm.PK{"11", 22}).Get(&user) + has, err = engine.Id(&core.PK{"11", 22}).Get(&user) if err != nil { t.Error(err) } else if !has { @@ -3819,14 +3821,14 @@ func testCompositeKey2(engine *xorm.Engine, t *testing.T) { } user = User{NickName: "test1"} - cnt, err = engine.Id(xorm.PK{"11", 22}).Update(&user) + cnt, err = engine.Id(core.PK{"11", 22}).Update(&user) if err != nil { t.Error(err) } else if cnt != 1 { t.Error(errors.New("can't update User{11, 22}")) } - cnt, err = engine.Id(xorm.PK{"11", 22}).Delete(&User{}) + cnt, err = engine.Id(core.PK{"11", 22}).Delete(&User{}) if err != nil { t.Error(err) } else if cnt != 1 { @@ -3870,16 +3872,12 @@ func testAll(engine *xorm.Engine, t *testing.T) { } func testAll2(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- combineTransaction --------------") - combineTransaction(engine, t) fmt.Println("-------------- table --------------") table(engine, t) fmt.Println("-------------- createMultiTables --------------") createMultiTables(engine, t) fmt.Println("-------------- tableOp --------------") tableOp(engine, t) - fmt.Println("-------------- testCols --------------") - testCols(engine, t) fmt.Println("-------------- testCharst --------------") testCharst(engine, t) fmt.Println("-------------- testStoreEngine --------------") @@ -3961,6 +3959,10 @@ func testAllSnakeMapper(engine *xorm.Engine, t *testing.T) { join(engine, t) fmt.Println("-------------- having --------------") having(engine, t) + fmt.Println("-------------- combineTransaction --------------") + combineTransaction(engine, t) + fmt.Println("-------------- testCols --------------") + testCols(engine, t) } func testAllSameMapper(engine *xorm.Engine, t *testing.T) { @@ -3976,4 +3978,8 @@ func testAllSameMapper(engine *xorm.Engine, t *testing.T) { joinSameMapper(engine, t) fmt.Println("-------------- having --------------") havingSameMapper(engine, t) + fmt.Println("-------------- combineTransaction --------------") + combineTransactionSameMapper(engine, t) + fmt.Println("-------------- testCols --------------") + testColsSameMapper(engine, t) } diff --git a/tests/benchmark_base_test.go b/tests/benchmark_base_test.go index 5ea598e5..5570fa39 100644 --- a/tests/benchmark_base_test.go +++ b/tests/benchmark_base_test.go @@ -1,4 +1,4 @@ -package xorm +package tests import ( "database/sql" diff --git a/tests/mssql_test.go b/tests/mssql_test.go index 101d07ae..e814d855 100644 --- a/tests/mssql_test.go +++ b/tests/mssql_test.go @@ -1,4 +1,4 @@ -package xorm +package tests // // +build windows @@ -9,6 +9,7 @@ import ( _ "github.com/lunny/godbc" "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" ) const mssqlConnStr = "driver={SQL Server};Server=192.168.20.135;Database=xorm_test; uid=sa; pwd=1234;" @@ -40,7 +41,7 @@ func TestMssqlWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -113,7 +114,7 @@ func BenchmarkMssqlCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -125,7 +126,7 @@ func BenchmarkMssqlCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -137,7 +138,7 @@ func BenchmarkMssqlCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/mymysql_test.go b/tests/mymysql_test.go index c64261dc..5646f95c 100644 --- a/tests/mymysql_test.go +++ b/tests/mymysql_test.go @@ -1,10 +1,11 @@ -package xorm +package tests import ( "database/sql" "testing" "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" _ "github.com/ziutek/mymysql/godrv" ) @@ -49,7 +50,7 @@ func TestMyMysqlWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -136,7 +137,7 @@ func BenchmarkMyMysqlCacheInsert(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -149,7 +150,7 @@ func BenchmarkMyMysqlCacheFind(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -162,7 +163,7 @@ func BenchmarkMyMysqlCacheFindPtr(t *testing.B) { } defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/mysql_test.go b/tests/mysql_test.go index f42be512..38eebc55 100644 --- a/tests/mysql_test.go +++ b/tests/mysql_test.go @@ -1,4 +1,4 @@ -package xorm +package tests import ( "database/sql" @@ -6,6 +6,8 @@ import ( _ "github.com/go-sql-driver/mysql" "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" + "github.com/lunny/xorm/core" ) /* @@ -54,7 +56,7 @@ func TestMysqlSameMapper(t *testing.T) { engine.ShowErr = showTestSql engine.ShowWarn = showTestSql engine.ShowDebug = showTestSql - engine.SetMapper(xorm.SameMapper{}) + engine.SetMapper(core.SameMapper{}) testAll(engine, t) testAllSameMapper(engine, t) @@ -75,7 +77,7 @@ func TestMysqlWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -99,8 +101,8 @@ func TestMysqlWithCacheSameMapper(t *testing.T) { t.Error(err) return } - engine.SetMapper(xorm.SameMapper{}) - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetMapper(core.SameMapper{}) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -190,7 +192,7 @@ func BenchmarkMysqlCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -202,7 +204,7 @@ func BenchmarkMysqlCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -214,7 +216,7 @@ func BenchmarkMysqlCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/postgres_test.go b/tests/postgres_test.go index 9c2ee2d8..85e926cc 100644 --- a/tests/postgres_test.go +++ b/tests/postgres_test.go @@ -1,4 +1,4 @@ -package xorm +package tests import ( "database/sql" @@ -6,6 +6,8 @@ import ( _ "github.com/lib/pq" "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" + "github.com/lunny/xorm/core" ) //var connStr string = "dbname=xorm_test user=lunny password=1234 sslmode=disable" @@ -59,7 +61,7 @@ func TestPostgresWithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) defer engine.Close() engine.ShowSQL = showTestSql engine.ShowErr = showTestSql @@ -78,7 +80,7 @@ func TestPostgresSameMapper(t *testing.T) { return } defer engine.Close() - engine.SetMapper(xorm.SameMapper{}) + engine.SetMapper(core.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -96,9 +98,9 @@ func TestPostgresWithCacheSameMapper(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) defer engine.Close() - engine.SetMapper(xorm.SameMapper{}) + engine.SetMapper(core.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -168,7 +170,7 @@ func BenchmarkPostgresCacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -181,7 +183,7 @@ func BenchmarkPostgresCacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -194,7 +196,7 @@ func BenchmarkPostgresCacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/sqlite3_test.go b/tests/sqlite3_test.go index aa10af64..1f982e29 100644 --- a/tests/sqlite3_test.go +++ b/tests/sqlite3_test.go @@ -1,4 +1,4 @@ -package xorm +package tests import ( "database/sql" @@ -6,6 +6,8 @@ import ( "testing" "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" + "github.com/lunny/xorm/core" _ "github.com/mattn/go-sqlite3" ) @@ -44,7 +46,7 @@ func TestSqlite3WithCache(t *testing.T) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -62,7 +64,7 @@ func TestSqlite3SameMapper(t *testing.T) { t.Error(err) return } - engine.SetMapper(xorm.SameMapper{}) + engine.SetMapper(core.SameMapper{}) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -81,8 +83,8 @@ func TestSqlite3WithCacheSameMapper(t *testing.T) { t.Error(err) return } - engine.SetMapper(xorm.SameMapper{}) - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetMapper(core.SameMapper{}) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) engine.ShowSQL = showTestSql engine.ShowErr = showTestSql engine.ShowWarn = showTestSql @@ -152,7 +154,7 @@ func BenchmarkSqlite3CacheInsert(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchInsert(engine, t) } @@ -164,7 +166,7 @@ func BenchmarkSqlite3CacheFind(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFind(engine, t) } @@ -176,6 +178,6 @@ func BenchmarkSqlite3CacheFindPtr(t *testing.B) { t.Error(err) return } - engine.SetDefaultCacher(xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)) + engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) doBenchFindPtr(engine, t) } diff --git a/tests/testdata/mysql_ddl.sql b/tests/testdata/mysql_ddl.sql index db20aa33..b1fcca5b 100644 --- a/tests/testdata/mysql_ddl.sql +++ b/tests/testdata/mysql_ddl.sql @@ -1,5 +1,8 @@ ---DROP DATABASE xorm_test; ---DROP DATABASE xorm_test2; +DROP DATABASE xorm_test; +DROP DATABASE xorm_test1; +DROP DATABASE xorm_test2; +DROP DATABASE xorm_test3; CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci; +CREATE DATABASE IF NOT EXISTS xorm_test1 CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci; CREATE DATABASE IF NOT EXISTS xorm_test3 CHARACTER SET utf8 COLLATE utf8_general_ci; diff --git a/xorm.go b/xorm.go index 5826db60..7ea51473 100644 --- a/xorm.go +++ b/xorm.go @@ -7,7 +7,9 @@ import ( "reflect" "runtime" "sync" + "time" + "github.com/lunny/xorm/caches" "github.com/lunny/xorm/core" _ "github.com/lunny/xorm/dialects" _ "github.com/lunny/xorm/drivers" @@ -46,9 +48,9 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { engine := &Engine{DriverName: driverName, DataSourceName: dataSourceName, dialect: dialect, - tableCachers: make(map[reflect.Type]Cacher)} + tableCachers: make(map[reflect.Type]core.Cacher)} - engine.SetMapper(SnakeMapper{}) + engine.SetMapper(core.SnakeMapper{}) engine.Filters = dialect.Filters() @@ -65,3 +67,11 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { runtime.SetFinalizer(engine, close) return engine, err } + +func NewLRUCacher(store core.CacheStore, max int) *caches.LRUCacher { + return caches.NewLRUCacher(store, core.CacheExpired, core.CacheMaxMemory, max) +} + +func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *caches.LRUCacher { + return caches.NewLRUCacher(store, expired, 0, max) +} From 56700ee3369581ddee5eda20877ab0c3cce3aa5d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jan 2014 10:31:07 +0800 Subject: [PATCH 11/55] bug fixed --- engine.go | 2 +- examples/cache.go | 8 +++--- examples/conversion.go | 5 ++-- examples/derive.go | 5 ++-- examples/goroutine.go | 51 +++++++++++++++++++------------------- examples/maxconnect.go | 52 +++++++++++++++++++-------------------- examples/pool.go | 7 +++--- examples/singlemapping.go | 5 ++-- session.go | 2 +- xorm.go | 6 ++++- xorm/c++.go | 27 ++++++++++---------- xorm/go.go | 21 +++++++--------- xorm/lang.go | 6 ++--- xorm/reverse.go | 20 ++++++++------- 14 files changed, 114 insertions(+), 103 deletions(-) diff --git a/engine.go b/engine.go index dc3fd637..7639310d 100644 --- a/engine.go +++ b/engine.go @@ -515,7 +515,7 @@ func mappingTable(t reflect.Type, tableMapper core.IMapper, colMapper core.IMapp if col.Length2 == 0 { col.Length2 = col.SQLType.DefaultLength2 } - fmt.Println("======", col) + //fmt.Println("======", col) if col.Name == "" { col.Name = colMapper.Obj2Table(t.Field(i).Name) } diff --git a/examples/cache.go b/examples/cache.go index d543cad2..8fd8f4a8 100644 --- a/examples/cache.go +++ b/examples/cache.go @@ -2,9 +2,11 @@ package main import ( "fmt" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" "os" + + "github.com/lunny/xorm" + "github.com/lunny/xorm/caches" + _ "github.com/mattn/go-sqlite3" ) type User struct { @@ -22,7 +24,7 @@ func main() { return } Orm.ShowSQL = true - cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + cacher := xorm.NewLRUCacher(caches.NewMemoryStore(), 1000) Orm.SetDefaultCacher(cacher) err = Orm.CreateTables(&User{}) diff --git a/examples/conversion.go b/examples/conversion.go index b74207ba..f16869c4 100644 --- a/examples/conversion.go +++ b/examples/conversion.go @@ -3,9 +3,10 @@ package main import ( "errors" "fmt" + "os" + "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" - "os" ) type Status struct { @@ -47,7 +48,7 @@ func main() { f := "conversion.db" os.Remove(f) - Orm, err := NewEngine("sqlite3", f) + Orm, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) return diff --git a/examples/derive.go b/examples/derive.go index f3480521..826b4271 100644 --- a/examples/derive.go +++ b/examples/derive.go @@ -2,9 +2,10 @@ package main import ( "fmt" + "os" + "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" - "os" ) type User struct { @@ -27,7 +28,7 @@ func main() { f := "derive.db" os.Remove(f) - Orm, err := NewEngine("sqlite3", f) + Orm, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) return diff --git a/examples/goroutine.go b/examples/goroutine.go index 16c0b242..6d45c924 100644 --- a/examples/goroutine.go +++ b/examples/goroutine.go @@ -2,11 +2,12 @@ package main import ( "fmt" + "os" + "runtime" + _ "github.com/go-sql-driver/mysql" "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" - "os" - "runtime" ) type User struct { @@ -38,35 +39,35 @@ func test(engine *xorm.Engine) { for i := 0; i < size; i++ { go func(x int) { //x := i - err := engine.Test() + err := engine.Ping() if err != nil { fmt.Println(err) } else { - err = engine.Map(u) + /*err = engine.(u) if err != nil { fmt.Println("Map user failed") - } else { - for j := 0; j < 10; j++ { - if x+j < 2 { - _, err = engine.Get(u) - } else if x+j < 4 { - users := make([]User, 0) - err = engine.Find(&users) - } else if x+j < 8 { - _, err = engine.Count(u) - } else if x+j < 16 { - _, err = engine.Insert(&User{Name: "xlw"}) - } else if x+j < 32 { - _, err = engine.Id(1).Delete(u) - } - if err != nil { - fmt.Println(err) - queue <- x - return - } + } else {*/ + for j := 0; j < 10; j++ { + if x+j < 2 { + _, err = engine.Get(u) + } else if x+j < 4 { + users := make([]User, 0) + err = engine.Find(&users) + } else if x+j < 8 { + _, err = engine.Count(u) + } else if x+j < 16 { + _, err = engine.Insert(&User{Name: "xlw"}) + } else if x+j < 32 { + _, err = engine.Id(1).Delete(u) + } + if err != nil { + fmt.Println(err) + queue <- x + return } - fmt.Printf("%v success!\n", x) } + fmt.Printf("%v success!\n", x) + //} } queue <- x }(i) @@ -82,7 +83,7 @@ func test(engine *xorm.Engine) { } func main() { - runtime.GOMAXPROCS(2) + runtime.GOMAXPROCS(1) fmt.Println("-----start sqlite go routines-----") engine, err := sqliteEngine() if err != nil { diff --git a/examples/maxconnect.go b/examples/maxconnect.go index e3fbfea7..e383de13 100644 --- a/examples/maxconnect.go +++ b/examples/maxconnect.go @@ -2,12 +2,12 @@ package main import ( "fmt" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - xorm "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" "os" "runtime" + + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + _ "github.com/mattn/go-sqlite3" ) type User struct { @@ -41,35 +41,35 @@ func test(engine *xorm.Engine) { for i := 0; i < size; i++ { go func(x int) { //x := i - err := engine.Test() + err := engine.Ping() if err != nil { fmt.Println(err) } else { - err = engine.Map(u) + /*err = engine.Map(u) if err != nil { fmt.Println("Map user failed") - } else { - for j := 0; j < 10; j++ { - if x+j < 2 { - _, err = engine.Get(u) - } else if x+j < 4 { - users := make([]User, 0) - err = engine.Find(&users) - } else if x+j < 8 { - _, err = engine.Count(u) - } else if x+j < 16 { - _, err = engine.Insert(&User{Name: "xlw"}) - } else if x+j < 32 { - _, err = engine.Id(1).Delete(u) - } - if err != nil { - fmt.Println(err) - queue <- x - return - } + } else {*/ + for j := 0; j < 10; j++ { + if x+j < 2 { + _, err = engine.Get(u) + } else if x+j < 4 { + users := make([]User, 0) + err = engine.Find(&users) + } else if x+j < 8 { + _, err = engine.Count(u) + } else if x+j < 16 { + _, err = engine.Insert(&User{Name: "xlw"}) + } else if x+j < 32 { + _, err = engine.Id(1).Delete(u) + } + if err != nil { + fmt.Println(err) + queue <- x + return } - fmt.Printf("%v success!\n", x) } + fmt.Printf("%v success!\n", x) + //} } queue <- x }(i) diff --git a/examples/pool.go b/examples/pool.go index d6ec0de7..746cdf8d 100644 --- a/examples/pool.go +++ b/examples/pool.go @@ -2,9 +2,10 @@ package main import ( "fmt" + "os" + "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" - "os" ) type User struct { @@ -16,12 +17,12 @@ func main() { f := "pool.db" os.Remove(f) - Orm, err := NewEngine("sqlite3", f) + Orm, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) return } - err = Orm.SetPool(NewSimpleConnectPool()) + err = Orm.SetPool(xorm.NewSimpleConnectPool()) if err != nil { fmt.Println(err) return diff --git a/examples/singlemapping.go b/examples/singlemapping.go index 932a0589..6954d7ee 100644 --- a/examples/singlemapping.go +++ b/examples/singlemapping.go @@ -2,9 +2,10 @@ package main import ( "fmt" + "os" + "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" - "os" ) type User struct { @@ -26,7 +27,7 @@ func main() { f := "singleMapping.db" os.Remove(f) - Orm, err := NewEngine("sqlite3", f) + Orm, err := xorm.NewEngine("sqlite3", f) if err != nil { fmt.Println(err) return diff --git a/session.go b/session.go index 382370c7..dae0f935 100644 --- a/session.go +++ b/session.go @@ -588,7 +588,7 @@ func (statement *Statement) convertIdSql(sqlStr string) string { if len(sqls) != 2 { return "" } - fmt.Println("-----", col) + //fmt.Println("-----", col) newsql := fmt.Sprintf("SELECT %v.%v FROM %v", statement.Engine.Quote(statement.TableName()), statement.Engine.Quote(col.Name), sqls[1]) return newsql diff --git a/xorm.go b/xorm.go index 7ea51473..a1f6a5d8 100644 --- a/xorm.go +++ b/xorm.go @@ -16,7 +16,7 @@ import ( ) const ( - Version string = "0.3.1" + Version string = "0.4" ) func close(engine *Engine) { @@ -75,3 +75,7 @@ func NewLRUCacher(store core.CacheStore, max int) *caches.LRUCacher { func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *caches.LRUCacher { return caches.NewLRUCacher(store, expired, 0, max) } + +func NewMemoryStore() *caches.MemoryStore { + return caches.NewMemoryStore() +} diff --git a/xorm/c++.go b/xorm/c++.go index ed094060..bbfdd3b1 100644 --- a/xorm/c++.go +++ b/xorm/c++.go @@ -2,9 +2,10 @@ package main import ( //"fmt" - "github.com/lunny/xorm" "strings" "text/template" + + "github.com/lunny/xorm/core" ) var ( @@ -18,27 +19,27 @@ var ( } ) -func cPlusTypeStr(col *xorm.Column) string { +func cPlusTypeStr(col *core.Column) string { tp := col.SQLType name := strings.ToUpper(tp.Name) switch name { - case xorm.Bit, xorm.TinyInt, xorm.SmallInt, xorm.MediumInt, xorm.Int, xorm.Integer, xorm.Serial: + case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.Serial: return "int" - case xorm.BigInt, xorm.BigSerial: + case core.BigInt, core.BigSerial: return "__int64" - case xorm.Char, xorm.Varchar, xorm.TinyText, xorm.Text, xorm.MediumText, xorm.LongText: + case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText: return "tstring" - case xorm.Date, xorm.DateTime, xorm.Time, xorm.TimeStamp: + case core.Date, core.DateTime, core.Time, core.TimeStamp: return "time_t" - case xorm.Decimal, xorm.Numeric: + case core.Decimal, core.Numeric: return "tstring" - case xorm.Real, xorm.Float: + case core.Real, core.Float: return "float" - case xorm.Double: + case core.Double: return "double" - case xorm.TinyBlob, xorm.Blob, xorm.MediumBlob, xorm.LongBlob, xorm.Bytea: + case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea: return "tstring" - case xorm.Bool: + case core.Bool: return "bool" default: return "tstring" @@ -46,11 +47,11 @@ func cPlusTypeStr(col *xorm.Column) string { return "" } -func genCPlusImports(tables []*xorm.Table) map[string]string { +func genCPlusImports(tables []*core.Table) map[string]string { imports := make(map[string]string) for _, table := range tables { - for _, col := range table.Columns { + for _, col := range table.Columns() { switch cPlusTypeStr(col) { case "time_t": imports[``] = `` diff --git a/xorm/go.go b/xorm/go.go index 3f46322d..880f902e 100644 --- a/xorm/go.go +++ b/xorm/go.go @@ -3,11 +3,11 @@ package main import ( "errors" "fmt" - "github.com/lunny/xorm" "go/format" "reflect" "strings" "text/template" + "github.com/lunny/xorm/core" ) var ( @@ -157,7 +157,7 @@ func gt(arg1, arg2 interface{}) (bool, error) { return !lessOrEqual, nil } -func getCol(cols map[string]*xorm.Column, name string) *xorm.Column { +func getCol(cols map[string]*core.Column, name string) *core.Column { return cols[name] } @@ -169,11 +169,11 @@ func formatGo(src string) (string, error) { return string(source), nil } -func genGoImports(tables []*xorm.Table) map[string]string { +func genGoImports(tables []*core.Table) map[string]string { imports := make(map[string]string) for _, table := range tables { - for _, col := range table.Columns { + for _, col := range table.Columns() { if typestring(col) == "time.Time" { imports["time"] = "time" } @@ -182,12 +182,9 @@ func genGoImports(tables []*xorm.Table) map[string]string { return imports } -func typestring(col *xorm.Column) string { +func typestring(col *core.Column) string { st := col.SQLType - /*if col.IsPrimaryKey { - return "int64" - }*/ - t := xorm.SQLType2Type(st) + t := core.SQLType2Type(st) s := t.String() if s == "[]uint8" { return "[]byte" @@ -195,7 +192,7 @@ func typestring(col *xorm.Column) string { return s } -func tag(table *xorm.Table, col *xorm.Column) string { +func tag(table *core.Table, col *core.Column) string { isNameId := (mapper.Table2Obj(col.Name) == "Id") isIdPk := isNameId && typestring(col) == "int64" @@ -227,9 +224,9 @@ func tag(table *xorm.Table, col *xorm.Column) string { for name, _ := range col.Indexes { index := table.Indexes[name] var uistr string - if index.Type == xorm.UniqueType { + if index.Type == core.UniqueType { uistr = "unique" - } else if index.Type == xorm.IndexType { + } else if index.Type == core.IndexType { uistr = "index" } if len(index.Cols) > 1 { diff --git a/xorm/lang.go b/xorm/lang.go index 17d09a98..a4785647 100644 --- a/xorm/lang.go +++ b/xorm/lang.go @@ -1,20 +1,20 @@ package main import ( - "github.com/lunny/xorm" "io/ioutil" "strings" "text/template" + "github.com/lunny/xorm/core" ) type LangTmpl struct { Funcs template.FuncMap Formater func(string) (string, error) - GenImports func([]*xorm.Table) map[string]string + GenImports func([]*core.Table) map[string]string } var ( - mapper = &xorm.SnakeMapper{} + mapper = &core.SnakeMapper{} langTmpls = map[string]LangTmpl{ "go": GoLangTmpl, "c++": CPlusTmpl, diff --git a/xorm/reverse.go b/xorm/reverse.go index 9eae48d2..8c87ee27 100644 --- a/xorm/reverse.go +++ b/xorm/reverse.go @@ -3,18 +3,20 @@ package main import ( "bytes" "fmt" - _ "github.com/bylevel/pq" - "github.com/dvirsky/go-pylog/logging" - _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - _ "github.com/mattn/go-sqlite3" - _ "github.com/ziutek/mymysql/godrv" "io/ioutil" "os" "path" "path/filepath" "strconv" "text/template" + + _ "github.com/bylevel/pq" + "github.com/dvirsky/go-pylog/logging" + _ "github.com/go-sql-driver/mysql" + "github.com/lunny/xorm" + "github.com/lunny/xorm/core" + _ "github.com/mattn/go-sqlite3" + _ "github.com/ziutek/mymysql/godrv" ) var CmdReverse = &Command{ @@ -48,7 +50,7 @@ func printReversePrompt(flag string) { } type Tmpl struct { - Tables []*xorm.Table + Tables []*core.Table Imports map[string]string Model string } @@ -188,7 +190,7 @@ func runReverse(cmd *Command, args []string) { imports := langTmpl.GenImports(tables) - tbls := make([]*xorm.Table, 0) + tbls := make([]*core.Table, 0) for _, table := range tables { tbls = append(tbls, table) } @@ -223,7 +225,7 @@ func runReverse(cmd *Command, args []string) { } else { for _, table := range tables { // imports - tbs := []*xorm.Table{table} + tbs := []*core.Table{table} imports := langTmpl.GenImports(tbs) w, err := os.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600) From 2f86968e940af46306b9dae5ab7638f6768cbf12 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jan 2014 10:50:42 +0800 Subject: [PATCH 12/55] doc & version --- VERSION | 2 +- doc.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/VERSION b/VERSION index a67c0014..7f23df75 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.3.1 +xorm v0.4 diff --git a/doc.go b/doc.go index 765f2dfe..16d0b3a7 100644 --- a/doc.go +++ b/doc.go @@ -1,4 +1,4 @@ -// Copyright 2013 The XORM Authors. All rights reserved. +// Copyright 2013 - 2014 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. From 8c5763bb3af3e5e18737dcdfb9398d8b3f39b511 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 25 Jan 2014 22:29:40 +0800 Subject: [PATCH 13/55] add struct, slice, map scan for db --- core/db.go | 32 ++++-- core/db_test.go | 288 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 215 insertions(+), 105 deletions(-) diff --git a/core/db.go b/core/db.go index 3f96b343..f1e52783 100644 --- a/core/db.go +++ b/core/db.go @@ -27,17 +27,33 @@ type Rows struct { } // scan data to a struct's pointer according field index -func (rs *Rows) ScanStruct(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") +func (rs *Rows) ScanStruct(dest ...interface{}) error { + if len(dest) == 0 { + return errors.New("at least one struct") } - vvv := vv.Elem() - newDest := make([]interface{}, vvv.NumField()) + 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") + } - for j := 0; j < vvv.NumField(); j++ { - newDest[j] = vvv.Field(j).Addr().Interface() + 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...) diff --git a/core/db_test.go b/core/db_test.go index 26f2e04d..41a4cf90 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -1,10 +1,8 @@ package core import ( - "fmt" "os" "testing" - "time" _ "github.com/mattn/go-sqlite3" ) @@ -22,209 +20,305 @@ type User struct { NickName string } -func TestOriQuery(t *testing.T) { +func BenchmarkOriQuery(b *testing.B) { + b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { - t.Error(err) + b.Error(err) } _, err = db.Exec(createTableSqlite3) if err != nil { - t.Error(err) + b.Error(err) } for i := 0; i < 50; i++ { _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", "xlw", "tester", 1.2, "lunny", "lunny xiao") if err != nil { - t.Error(err) + b.Error(err) } } - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } - defer rows.Close() + b.StartTimer() - start := time.Now() - - for rows.Next() { - var Id int64 - var Name, Title, Alias, NickName string - var Age float32 - err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName) + for i := 0; i < b.N; i++ { + rows, err := db.Query("select * from user") if err != nil { - t.Error(err) + b.Error(err) } - fmt.Println(Id, Name, Title, Age, Alias, NickName) - } - fmt.Println("ori ------", time.Now().Sub(start), "ns") + for rows.Next() { + var Id int64 + var Name, Title, Alias, NickName string + var Age float32 + err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName) + if err != nil { + b.Error(err) + } + //fmt.Println(Id, Name, Title, Age, Alias, NickName) + } + rows.Close() + } } -func TestStructQuery(t *testing.T) { +func BenchmarkStructQuery(b *testing.B) { + b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { - t.Error(err) + b.Error(err) } _, err = db.Exec(createTableSqlite3) if err != nil { - t.Error(err) + b.Error(err) } for i := 0; i < 50; i++ { _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", "xlw", "tester", 1.2, "lunny", "lunny xiao") if err != nil { - t.Error(err) + b.Error(err) } } - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } - defer rows.Close() - start := time.Now() + b.StartTimer() - for rows.Next() { - var user User - err = rows.ScanStruct(&user) + for i := 0; i < b.N; i++ { + rows, err := db.Query("select * from user") if err != nil { - t.Error(err) + b.Error(err) } - fmt.Println(user) + + for rows.Next() { + var user User + err = rows.ScanStruct(&user) + if err != nil { + b.Error(err) + } + //fmt.Println(user) + } + rows.Close() } - fmt.Println("struct ------", time.Now().Sub(start)) } -func TestStruct2Query(t *testing.T) { +func BenchmarkStruct2Query(b *testing.B) { + b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { - t.Error(err) + b.Error(err) } _, err = db.Exec(createTableSqlite3) if err != nil { - t.Error(err) + b.Error(err) } for i := 0; i < 50; i++ { _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", "xlw", "tester", 1.2, "lunny", "lunny xiao") if err != nil { - t.Error(err) + b.Error(err) } } db.Mapper = &SnakeMapper{} + b.StartTimer() - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } - defer rows.Close() - start := time.Now() - - for rows.Next() { - var user User - err = rows.ScanStruct2(&user) + for i := 0; i < b.N; i++ { + rows, err := db.Query("select * from user") if err != nil { - t.Error(err) + b.Error(err) } - fmt.Println(user) + + for rows.Next() { + var user User + err = rows.ScanStruct2(&user) + if err != nil { + b.Error(err) + } + //fmt.Println(user) + } + rows.Close() } - fmt.Println("struct2 ------", time.Now().Sub(start)) } -func TestSliceQuery(t *testing.T) { +func BenchmarkSliceQuery(b *testing.B) { + b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { - t.Error(err) + b.Error(err) } _, err = db.Exec(createTableSqlite3) if err != nil { - t.Error(err) + b.Error(err) } for i := 0; i < 50; i++ { _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", "xlw", "tester", 1.2, "lunny", "lunny xiao") if err != nil { - t.Error(err) + b.Error(err) } } - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } + b.StartTimer() - defer rows.Close() - - cols, err := rows.Columns() - if err != nil { - t.Error(err) - } - - start := time.Now() - - for rows.Next() { - slice := make([]interface{}, len(cols)) - err = rows.ScanSlice(&slice) + for i := 0; i < b.N; i++ { + rows, err := db.Query("select * from user") if err != nil { - t.Error(err) + b.Error(err) } - fmt.Println(slice) - } - fmt.Println("slice ------", time.Now().Sub(start)) + 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) + } + + rows.Close() + } } -func TestMapQuery(t *testing.T) { +func BenchmarkMapInterfaceQuery(b *testing.B) { + b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") if err != nil { - t.Error(err) + b.Error(err) } _, err = db.Exec(createTableSqlite3) if err != nil { - t.Error(err) + b.Error(err) } for i := 0; i < 50; i++ { _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", "xlw", "tester", 1.2, "lunny", "lunny xiao") if err != nil { - t.Error(err) + b.Error(err) } } - rows, err := db.Query("select * from user") - if err != nil { - t.Error(err) - } + b.StartTimer() - defer rows.Close() - - start := time.Now() - - for rows.Next() { - m := make(map[string]interface{}) - err = rows.ScanMap(&m) + for i := 0; i < b.N; i++ { + rows, err := db.Query("select * from user") if err != nil { - t.Error(err) + b.Error(err) } - fmt.Println(m) + + for rows.Next() { + m := make(map[string]interface{}) + err = rows.ScanMap(&m) + if err != nil { + b.Error(err) + } + //fmt.Println(m) + } + + 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) } - fmt.Println("map ------", time.Now().Sub(start)) + _, err = db.Exec(createTableSqlite3) + if err != nil { + b.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + 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) + } + /*for k, v := range m { + fmt.Printf("%v - %v\n", k, string(v)) + }*/ + } + + 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) + } + + _, err = db.Exec(createTableSqlite3) + if err != nil { + b.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + 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) + } + /*for k, v := range m { + fmt.Printf("%v - %v\n", k, v) + }*/ + } + + rows.Close() + } } From f1a4636699848f99fc9a08ad365b214a7ad62221 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sun, 26 Jan 2014 00:05:37 +0800 Subject: [PATCH 14/55] performance improved for db.ScanStruct2 --- core/db.go | 38 ++++++++++++++++++++++++++++++++------ core/db_test.go | 2 +- core/mapper.go | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 7 deletions(-) diff --git a/core/db.go b/core/db.go index f1e52783..f3fbbd43 100644 --- a/core/db.go +++ b/core/db.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "reflect" + "sync" ) type DB struct { @@ -13,7 +14,7 @@ type DB struct { func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) - return &DB{db, &SnakeMapper{}}, err + return &DB{db, NewCacheMapper(&SnakeMapper{})}, err } func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { @@ -59,6 +60,18 @@ func (rs *Rows) ScanStruct(dest ...interface{}) error { return rs.Rows.Scan(newDest...) } +type EmptyScanner struct { +} + +func (EmptyScanner) Scan(src interface{}) error { + return nil +} + +var ( + fieldCache = make(map[reflect.Type]map[string]int) + fieldCacheMutex sync.RWMutex +) + // scan data to a struct's pointer according field name func (rs *Rows) ScanStruct2(dest interface{}) error { vv := reflect.ValueOf(dest) @@ -72,14 +85,27 @@ func (rs *Rows) ScanStruct2(dest interface{}) error { } vvv := vv.Elem() - newDest := make([]interface{}, len(cols)) + t := vvv.Type() + fieldCacheMutex.RLock() + cache, ok := fieldCache[t] + fieldCacheMutex.RUnlock() + if !ok { + cache = make(map[string]int) + for i := 0; i < vvv.NumField(); i++ { + cache[vvv.Type().Field(i).Name] = i + } + fieldCacheMutex.Lock() + fieldCache[t] = cache + fieldCacheMutex.Unlock() + } + + newDest := make([]interface{}, len(cols)) + var v EmptyScanner for j, name := range cols { - f := vvv.FieldByName(rs.Mapper.Table2Obj(name)) - if f.IsValid() { - newDest[j] = f.Addr().Interface() + if i, ok := cache[rs.Mapper.Table2Obj(name)]; ok { + newDest[j] = vvv.Field(i).Addr().Interface() } else { - var v interface{} newDest[j] = &v } } diff --git a/core/db_test.go b/core/db_test.go index 41a4cf90..1ea670a5 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -125,7 +125,7 @@ func BenchmarkStruct2Query(b *testing.B) { } } - db.Mapper = &SnakeMapper{} + db.Mapper = NewCacheMapper(&SnakeMapper{}) b.StartTimer() for i := 0; i < b.N; i++ { diff --git a/core/mapper.go b/core/mapper.go index 0011dde5..fb209a7c 100644 --- a/core/mapper.go +++ b/core/mapper.go @@ -2,6 +2,7 @@ package core import ( "strings" + "sync" ) // name translation between struct, fields names and table, column names @@ -10,6 +11,50 @@ type IMapper interface { Table2Obj(string) string } +type CacheMapper struct { + oriMapper IMapper + obj2tableCache map[string]string + obj2tableMutex sync.RWMutex + table2objCache map[string]string + table2objMutex sync.RWMutex +} + +func NewCacheMapper(mapper IMapper) *CacheMapper { + return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string), + table2objCache: make(map[string]string), + } +} + +func (m *CacheMapper) Obj2Table(o string) string { + m.obj2tableMutex.RLock() + t, ok := m.obj2tableCache[o] + m.obj2tableMutex.RUnlock() + if ok { + return t + } + + t = m.oriMapper.Obj2Table(o) + m.obj2tableMutex.Lock() + m.obj2tableCache[o] = t + m.obj2tableMutex.Unlock() + return t +} + +func (m *CacheMapper) Table2Obj(t string) string { + m.table2objMutex.RLock() + o, ok := m.table2objCache[t] + m.table2objMutex.RUnlock() + if ok { + return o + } + + o = m.oriMapper.Table2Obj(t) + m.table2objMutex.Lock() + m.table2objCache[t] = o + m.table2objMutex.Unlock() + return o +} + // SameMapper implements IMapper and provides same name between struct and // database table type SameMapper struct { From e28f65b0ce295f6f7f3d8c4a04c33aa4dfa9a9f8 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 27 Jan 2014 21:01:52 +0800 Subject: [PATCH 15/55] improved benchmark test for db.go --- core/benchmark.sh | 1 + core/db.go | 7 ++++--- core/db_test.go | 36 ++++++++++++++++++++++++++---------- 3 files changed, 31 insertions(+), 13 deletions(-) create mode 100755 core/benchmark.sh diff --git a/core/benchmark.sh b/core/benchmark.sh new file mode 100755 index 00000000..eab9e57e --- /dev/null +++ b/core/benchmark.sh @@ -0,0 +1 @@ +go test -v -bench=. -run=XXX diff --git a/core/db.go b/core/db.go index f3fbbd43..21d276f0 100644 --- a/core/db.go +++ b/core/db.go @@ -93,7 +93,7 @@ func (rs *Rows) ScanStruct2(dest interface{}) error { if !ok { cache = make(map[string]int) for i := 0; i < vvv.NumField(); i++ { - cache[vvv.Type().Field(i).Name] = i + cache[rs.Mapper.Obj2Table(vvv.Type().Field(i).Name)] = i } fieldCacheMutex.Lock() fieldCache[t] = cache @@ -103,7 +103,7 @@ func (rs *Rows) ScanStruct2(dest interface{}) error { newDest := make([]interface{}, len(cols)) var v EmptyScanner for j, name := range cols { - if i, ok := cache[rs.Mapper.Table2Obj(name)]; ok { + if i, ok := cache[name]; ok { newDest[j] = vvv.Field(i).Addr().Interface() } else { newDest[j] = &v @@ -141,7 +141,8 @@ func (rs *Rows) ScanSlice(dest interface{}) error { return err } - for i, _ := range cols { + srcLen := vvv.Len() + for i := srcLen; i < len(cols); i++ { vvv = reflect.Append(vvv, reflect.ValueOf(newDest[i]).Elem()) } return nil diff --git a/core/db_test.go b/core/db_test.go index 1ea670a5..2d0e85c5 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -1,6 +1,8 @@ package core import ( + "errors" + "fmt" "os" "testing" @@ -98,7 +100,10 @@ func BenchmarkStructQuery(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(user) + if user.Name != "xlw" { + fmt.Println(user) + b.Error(errors.New("name should be xlw")) + } } rows.Close() } @@ -140,7 +145,10 @@ func BenchmarkStruct2Query(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(user) + if user.Name != "xlw" { + fmt.Println(user) + b.Error(errors.New("name should be xlw")) + } } rows.Close() } @@ -186,7 +194,10 @@ func BenchmarkSliceQuery(b *testing.B) { 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() @@ -228,7 +239,10 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { if err != nil { b.Error(err) } - //fmt.Println(m) + if m["name"].(string) != "xlw" { + fmt.Println(m) + b.Error(errors.New("name should be xlw")) + } } rows.Close() @@ -270,9 +284,10 @@ func BenchmarkMapBytesQuery(b *testing.B) { if err != nil { b.Error(err) } - /*for k, v := range m { - fmt.Printf("%v - %v\n", k, string(v)) - }*/ + if string(m["name"]) != "xlw" { + fmt.Println(m) + b.Error(errors.New("name should be xlw")) + } } rows.Close() @@ -314,9 +329,10 @@ func BenchmarkMapStringQuery(b *testing.B) { if err != nil { b.Error(err) } - /*for k, v := range m { - fmt.Printf("%v - %v\n", k, v) - }*/ + if m["name"] != "xlw" { + fmt.Println(m) + b.Error(errors.New("name should be xlw")) + } } rows.Close() From e462de93e966842607d94631635f33e320bff942 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 8 Feb 2014 11:44:56 +0800 Subject: [PATCH 16/55] add Row, Stmt etc. for db.go --- core/db.go | 228 ++++++++++++++++++++++++++++++++++---- core/db_test.go | 287 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 494 insertions(+), 21 deletions(-) diff --git a/core/db.go b/core/db.go index 21d276f0..7fedca73 100644 --- a/core/db.go +++ b/core/db.go @@ -4,6 +4,7 @@ import ( "database/sql" "errors" "reflect" + "regexp" "sync" ) @@ -12,6 +13,12 @@ type DB struct { Mapper IMapper } +type Stmt struct { + *sql.Stmt + Mapper IMapper + names map[string]int +} + func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) return &DB{db, NewCacheMapper(&SnakeMapper{})}, err @@ -22,6 +29,122 @@ func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { return &Rows{rows, db.Mapper}, err } +func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) + return "?" + }) + return db.Query(query, args...) +} + +func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) + return "?" + }) + return db.Query(query, args...) +} + +type Row struct { + *sql.Row + Mapper IMapper +} + +func (db *DB) QueryRow(query string, args ...interface{}) *Row { + row := db.DB.QueryRow(query, args...) + return &Row{row, db.Mapper} +} + +func (db *DB) Prepare(query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := db.DB.Prepare(query) + if err != nil { + return nil, err + } + return &Stmt{stmt, db.Mapper, names}, nil +} + +func (s *Stmt) ExecMap(mp interface{}) (sql.Result, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + return s.Stmt.Exec(args...) +} + +func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + return s.Stmt.Exec(args...) +} + +var ( + re = regexp.MustCompile(`[?](\w+)`) +) + +// insert into (name) values (?) +// insert into (name) values (?name) +func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) + return "?" + }) + + return db.DB.Exec(query, args...) +} + +func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) + return "?" + }) + + return db.DB.Exec(query, args...) +} + type Rows struct { *sql.Rows Mapper IMapper @@ -72,6 +195,28 @@ var ( 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) ScanStruct2(dest interface{}) error { vv := reflect.ValueOf(dest) @@ -84,27 +229,12 @@ func (rs *Rows) ScanStruct2(dest interface{}) error { return err } - vvv := vv.Elem() - t := vvv.Type() - - fieldCacheMutex.RLock() - cache, ok := fieldCache[t] - fieldCacheMutex.RUnlock() - if !ok { - cache = make(map[string]int) - for i := 0; i < vvv.NumField(); i++ { - cache[rs.Mapper.Obj2Table(vvv.Type().Field(i).Name)] = i - } - fieldCacheMutex.Lock() - fieldCache[t] = cache - fieldCacheMutex.Unlock() - } - newDest := make([]interface{}, len(cols)) var v EmptyScanner for j, name := range cols { - if i, ok := cache[name]; ok { - newDest[j] = vvv.Field(i).Addr().Interface() + f := fieldByName(vv.Elem(), rs.Mapper.Table2Obj(name)) + if f.IsValid() { + newDest[j] = f.Addr().Interface() } else { newDest[j] = &v } @@ -113,6 +243,36 @@ func (rs *Rows) ScanStruct2(dest interface{}) error { 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) @@ -164,8 +324,9 @@ func (rs *Rows) ScanMap(dest interface{}) error { vvv := vv.Elem() for i, _ := range cols { - v := reflect.New(vvv.Type().Elem()) - newDest[i] = v.Interface() + newDest[i] = ReflectNew(vvv.Type().Elem()).Interface() + //v := reflect.New(vvv.Type().Elem()) + //newDest[i] = v.Interface() } err = rs.Rows.Scan(newDest...) @@ -180,3 +341,30 @@ func (rs *Rows) ScanMap(dest interface{}) error { 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 +}*/ diff --git a/core/db_test.go b/core/db_test.go index 2d0e85c5..8324b385 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -29,6 +29,7 @@ func BenchmarkOriQuery(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -72,6 +73,7 @@ func BenchmarkStructQuery(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -116,6 +118,7 @@ func BenchmarkStruct2Query(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -154,13 +157,14 @@ func BenchmarkStruct2Query(b *testing.B) { } } -func BenchmarkSliceQuery(b *testing.B) { +func BenchmarkSliceInterfaceQuery(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(createTableSqlite3) if err != nil { @@ -203,6 +207,107 @@ func BenchmarkSliceQuery(b *testing.B) { 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(createTableSqlite3) + if err != nil { + b.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + 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() + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + b.Error(err) + } + defer db.Close() + + _, err = db.Exec(createTableSqlite3) + if err != nil { + b.Error(err) + } + + for i := 0; i < 50; i++ { + _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + 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() @@ -211,6 +316,7 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -256,6 +362,7 @@ func BenchmarkMapBytesQuery(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -301,6 +408,7 @@ func BenchmarkMapStringQuery(b *testing.B) { if err != nil { b.Error(err) } + defer db.Close() _, err = db.Exec(createTableSqlite3) if err != nil { @@ -338,3 +446,180 @@ func BenchmarkMapStringQuery(b *testing.B) { rows.Close() } } + +func BenchmarkExec(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(createTableSqlite3) + 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) values (?,?,?,?,?)", + "xlw", "tester", 1.2, "lunny", "lunny xiao") + if err != nil { + b.Error(err) + } + } +} + +func BenchmarkExecMap(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(createTableSqlite3) + 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", + } + + for i := 0; i < b.N; i++ { + _, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name) + values (?name,?title,?age,?alias,?nick_name)`, + &mp) + if err != nil { + b.Error(err) + } + } +} + +func TestExecMap(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + defer db.Close() + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + mp := map[string]interface{}{ + "name": "xlw", + "title": "tester", + "age": 1.2, + "alias": "lunny", + "nick_name": "lunny xiao", + } + + _, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name) + values (?name,?title,?age,?alias,?nick_name)`, + &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.ScanStruct2(&user) + if err != nil { + t.Error(err) + } + fmt.Println("--", user) + } +} + +func TestExecStruct(t *testing.T) { + os.Remove("./test.db") + db, err := Open("sqlite3", "./test.db") + if err != nil { + t.Error(err) + } + defer db.Close() + + _, err = db.Exec(createTableSqlite3) + if err != nil { + t.Error(err) + } + + user := User{Name: "xlw", + Title: "tester", + Age: 1.2, + Alias: "lunny", + NickName: "lunny xiao", + } + + _, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name) + values (?Name,?Title,?Age,?Alias,?NickName)`, + &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.ScanStruct2(&user) + if err != nil { + t.Error(err) + } + fmt.Println("1--", user) + } +} + +func BenchmarkExecStruct(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(createTableSqlite3) + if err != nil { + b.Error(err) + } + + b.StartTimer() + + user := User{Name: "xlw", + Title: "tester", + Age: 1.2, + Alias: "lunny", + NickName: "lunny xiao", + } + + for i := 0; i < b.N; i++ { + _, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name) + values (?Name,?Title,?Age,?Alias,?NickName)`, + &user) + if err != nil { + b.Error(err) + } + } +} From c31c21b0514522d1675a64a6e57babde70b8a591 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 10 Feb 2014 10:03:13 +0800 Subject: [PATCH 17/55] add querymap & querystruct for tx --- core/db.go | 217 ++++++++++++++++++++++++++++++++++++++---------- core/db_test.go | 77 +++++++++-------- 2 files changed, 215 insertions(+), 79 deletions(-) diff --git a/core/db.go b/core/db.go index 7fedca73..97103039 100644 --- a/core/db.go +++ b/core/db.go @@ -8,17 +8,44 @@ import ( "sync" ) +var ( + ErrNoMapPointer = errors.New("mp should be a map's pointer") + ErrNoStructPointer = errors.New("mp should be a map's pointer") +) + +func MapToSlice(query string, mp interface{}) (string, []interface{}, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return "", []interface{}{}, ErrNoMapPointer + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) + return "?" + }) + return query, args, nil +} + +func StructToSlice(query string, st interface{}) (string, []interface{}, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return "", []interface{}{}, ErrNoStructPointer + } + + args := make([]interface{}, 0) + query = re.ReplaceAllStringFunc(query, func(src string) string { + args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) + return "?" + }) + return query, args, nil +} + type DB struct { *sql.DB Mapper IMapper } -type Stmt struct { - *sql.Stmt - Mapper IMapper - names map[string]int -} - func Open(driverName, dataSourceName string) (*DB, error) { db, err := sql.Open(driverName, dataSourceName) return &DB{db, NewCacheMapper(&SnakeMapper{})}, err @@ -30,41 +57,60 @@ func (db *DB) Query(query string, args ...interface{}) (*Rows, error) { } func (db *DB) QueryMap(query string, mp interface{}) (*Rows, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err } - - args := make([]interface{}, 0) - query = re.ReplaceAllStringFunc(query, func(src string) string { - args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) - return "?" - }) return db.Query(query, args...) } func (db *DB) QueryStruct(query string, st interface{}) (*Rows, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err } - - args := make([]interface{}, 0) - query = re.ReplaceAllStringFunc(query, func(src string) string { - args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) - return "?" - }) return db.Query(query, args...) } type Row struct { *sql.Row + // One of these two will be non-nil: + err error // deferred error for easy chaining Mapper IMapper } +func (row *Row) Scan(dest ...interface{}) error { + if row.err != nil { + return row.err + } + return row.Row.Scan(dest...) +} + func (db *DB) QueryRow(query string, args ...interface{}) *Row { row := db.DB.QueryRow(query, args...) - return &Row{row, db.Mapper} + return &Row{row, nil, db.Mapper} +} + +func (db *DB) QueryRowMap(query string, mp interface{}) *Row { + query, args, err := MapToSlice(query, mp) + if err != nil { + return &Row{nil, err, db.Mapper} + } + return db.QueryRow(query, args...) +} + +func (db *DB) QueryRowStruct(query string, st interface{}) *Row { + query, args, err := StructToSlice(query, st) + if err != nil { + return &Row{nil, err, db.Mapper} + } + return db.QueryRow(query, args...) +} + +type Stmt struct { + *sql.Stmt + Mapper IMapper + names map[string]int } func (db *DB) Prepare(query string) (*Stmt, error) { @@ -116,32 +162,18 @@ var ( // insert into (name) values (?) // insert into (name) values (?name) func (db *DB) ExecMap(query string, mp interface{}) (sql.Result, error) { - vv := reflect.ValueOf(mp) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { - return nil, errors.New("mp should be a map's pointer") + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err } - - args := make([]interface{}, 0) - query = re.ReplaceAllStringFunc(query, func(src string) string { - args = append(args, vv.Elem().MapIndex(reflect.ValueOf(src[1:])).Interface()) - return "?" - }) - return db.DB.Exec(query, args...) } func (db *DB) ExecStruct(query string, st interface{}) (sql.Result, error) { - vv := reflect.ValueOf(st) - if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { - return nil, errors.New("mp should be a map's pointer") + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err } - - args := make([]interface{}, 0) - query = re.ReplaceAllStringFunc(query, func(src string) string { - args = append(args, vv.Elem().FieldByName(src[1:]).Interface()) - return "?" - }) - return db.DB.Exec(query, args...) } @@ -368,3 +400,98 @@ func (rs *Rows) ScanMap(dest interface{}) error { return nil }*/ + +type Tx struct { + *sql.Tx + Mapper IMapper +} + +func (db *DB) Begin() (*Tx, error) { + tx, err := db.DB.Begin() + if err != nil { + return nil, err + } + return &Tx{tx, db.Mapper}, nil +} + +func (tx *Tx) Prepare(query string) (*Stmt, error) { + names := make(map[string]int) + var i int + query = re.ReplaceAllStringFunc(query, func(src string) string { + names[src[1:]] = i + i += 1 + return "?" + }) + + stmt, err := tx.Tx.Prepare(query) + if err != nil { + return nil, err + } + return &Stmt{stmt, tx.Mapper, names}, nil +} + +func (tx *Tx) Stmt(stmt *Stmt) *Stmt { + // TODO: + return stmt +} + +func (tx *Tx) ExecMap(query string, mp interface{}) (sql.Result, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.Tx.Exec(query, args...) +} + +func (tx *Tx) ExecStruct(query string, st interface{}) (sql.Result, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.Tx.Exec(query, args...) +} + +func (tx *Tx) Query(query string, args ...interface{}) (*Rows, error) { + rows, err := tx.Tx.Query(query, args...) + if err != nil { + return nil, err + } + return &Rows{rows, tx.Mapper}, nil +} + +func (tx *Tx) QueryMap(query string, mp interface{}) (*Rows, error) { + query, args, err := MapToSlice(query, mp) + if err != nil { + return nil, err + } + return tx.Query(query, args...) +} + +func (tx *Tx) QueryStruct(query string, st interface{}) (*Rows, error) { + query, args, err := StructToSlice(query, st) + if err != nil { + return nil, err + } + return tx.Query(query, args...) +} + +func (tx *Tx) QueryRow(query string, args ...interface{}) *Row { + row := tx.Tx.QueryRow(query, args...) + return &Row{row, nil, tx.Mapper} +} + +func (tx *Tx) QueryRowMap(query string, mp interface{}) *Row { + query, args, err := MapToSlice(query, mp) + if err != nil { + return &Row{nil, err, tx.Mapper} + } + return tx.QueryRow(query, args...) +} + +func (tx *Tx) QueryRowStruct(query string, st interface{}) *Row { + query, args, err := StructToSlice(query, st) + if err != nil { + return &Row{nil, err, tx.Mapper} + } + return tx.QueryRow(query, args...) +} diff --git a/core/db_test.go b/core/db_test.go index 8324b385..9836a589 100644 --- a/core/db_test.go +++ b/core/db_test.go @@ -5,12 +5,14 @@ import ( "fmt" "os" "testing" + "time" _ "github.com/mattn/go-sqlite3" ) var ( - createTableSqlite3 = "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);" + createTableSqlite3 = "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);" ) type User struct { @@ -20,6 +22,7 @@ type User struct { Age float32 Alias string NickName string + Created time.Time } func BenchmarkOriQuery(b *testing.B) { @@ -37,8 +40,8 @@ func BenchmarkOriQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -56,7 +59,8 @@ func BenchmarkOriQuery(b *testing.B) { var Id int64 var Name, Title, Alias, NickName string var Age float32 - err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName) + var Created time.Time + err = rows.Scan(&Id, &Name, &Title, &Age, &Alias, &NickName, &Created) if err != nil { b.Error(err) } @@ -81,8 +85,8 @@ func BenchmarkStructQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -126,8 +130,8 @@ func BenchmarkStruct2Query(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -172,8 +176,8 @@ func BenchmarkSliceInterfaceQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -207,7 +211,8 @@ func BenchmarkSliceInterfaceQuery(b *testing.B) { rows.Close() } } -func BenchmarkSliceBytesQuery(b *testing.B) { + +/*func BenchmarkSliceBytesQuery(b *testing.B) { b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") @@ -222,8 +227,8 @@ func BenchmarkSliceBytesQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -273,8 +278,8 @@ func BenchmarkSliceStringQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -307,7 +312,7 @@ func BenchmarkSliceStringQuery(b *testing.B) { rows.Close() } -} +}*/ func BenchmarkMapInterfaceQuery(b *testing.B) { b.StopTimer() @@ -324,8 +329,8 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -355,7 +360,7 @@ func BenchmarkMapInterfaceQuery(b *testing.B) { } } -func BenchmarkMapBytesQuery(b *testing.B) { +/*func BenchmarkMapBytesQuery(b *testing.B) { b.StopTimer() os.Remove("./test.db") db, err := Open("sqlite3", "./test.db") @@ -370,8 +375,8 @@ func BenchmarkMapBytesQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -416,8 +421,8 @@ func BenchmarkMapStringQuery(b *testing.B) { } for i := 0; i < 50; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -445,7 +450,7 @@ func BenchmarkMapStringQuery(b *testing.B) { rows.Close() } -} +}*/ func BenchmarkExec(b *testing.B) { b.StopTimer() @@ -464,8 +469,8 @@ func BenchmarkExec(b *testing.B) { b.StartTimer() for i := 0; i < b.N; i++ { - _, err = db.Exec("insert into user (name, title, age, alias, nick_name) values (?,?,?,?,?)", - "xlw", "tester", 1.2, "lunny", "lunny xiao") + _, 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) } @@ -494,11 +499,12 @@ func BenchmarkExecMap(b *testing.B) { "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) - values (?name,?title,?age,?alias,?nick_name)`, + _, 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) @@ -525,10 +531,11 @@ func TestExecMap(t *testing.T) { "age": 1.2, "alias": "lunny", "nick_name": "lunny xiao", + "created": time.Now(), } - _, err = db.ExecMap(`insert into user (name, title, age, alias, nick_name) - values (?name,?title,?age,?alias,?nick_name)`, + _, 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) @@ -567,10 +574,11 @@ func TestExecStruct(t *testing.T) { Age: 1.2, Alias: "lunny", NickName: "lunny xiao", + Created: time.Now(), } - _, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name) - values (?Name,?Title,?Age,?Alias,?NickName)`, + _, 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) @@ -612,11 +620,12 @@ func BenchmarkExecStruct(b *testing.B) { Age: 1.2, Alias: "lunny", NickName: "lunny xiao", + Created: time.Now(), } for i := 0; i < b.N; i++ { - _, err = db.ExecStruct(`insert into user (name, title, age, alias, nick_name) - values (?Name,?Title,?Age,?Alias,?NickName)`, + _, 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) From a76d74804f42f9d707b92880eaa0856581dee5a6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Mon, 10 Feb 2014 15:36:25 +0800 Subject: [PATCH 18/55] a little performance improved --- engine.go | 8 +++++--- xorm.go | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/engine.go b/engine.go index 7639310d..af910562 100644 --- a/engine.go +++ b/engine.go @@ -27,7 +27,7 @@ type Engine struct { dialect core.Dialect Tables map[reflect.Type]*core.Table - mutex *sync.Mutex + mutex *sync.RWMutex ShowSQL bool ShowErr bool ShowDebug bool @@ -388,12 +388,14 @@ func (engine *Engine) Having(conditions string) *Session { } func (engine *Engine) autoMapType(t reflect.Type) *core.Table { - engine.mutex.Lock() - defer engine.mutex.Unlock() + engine.mutex.RLock() table, ok := engine.Tables[t] + engine.mutex.RUnlock() if !ok { table = engine.mapType(t) + engine.mutex.Lock() engine.Tables[t] = table + engine.mutex.Unlock() } return table } diff --git a/xorm.go b/xorm.go index a1f6a5d8..40653eea 100644 --- a/xorm.go +++ b/xorm.go @@ -50,12 +50,12 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { DataSourceName: dataSourceName, dialect: dialect, tableCachers: make(map[reflect.Type]core.Cacher)} - engine.SetMapper(core.SnakeMapper{}) + engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) engine.Filters = dialect.Filters() engine.Tables = make(map[reflect.Type]*core.Table) - engine.mutex = &sync.Mutex{} + engine.mutex = &sync.RWMutex{} engine.TagIdentifier = "xorm" engine.Logger = os.Stdout From 245d1fafb5067b3839cb2fea10b3906144bd3efc Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 11 Feb 2014 14:16:14 +0800 Subject: [PATCH 19/55] move types to core subdir --- core/type.go | 53 +++++++++++++++++ helpers.go | 4 +- session.go | 161 +++++++++++++++++++++++++++++++++++++-------------- table.go | 101 -------------------------------- 4 files changed, 172 insertions(+), 147 deletions(-) delete mode 100644 table.go diff --git a/core/type.go b/core/type.go index 84c4426c..68e2a23c 100644 --- a/core/type.go +++ b/core/type.go @@ -143,7 +143,60 @@ var ( c_TIME_DEFAULT time.Time ) +var ( + IntType = reflect.TypeOf(c_INT_DEFAULT) + Int8Type = reflect.TypeOf(c_INT8_DEFAULT) + Int16Type = reflect.TypeOf(c_INT16_DEFAULT) + Int32Type = reflect.TypeOf(c_INT32_DEFAULT) + Int64Type = reflect.TypeOf(c_INT64_DEFAULT) + + UintType = reflect.TypeOf(c_UINT_DEFAULT) + Uint8Type = reflect.TypeOf(c_UINT8_DEFAULT) + Uint16Type = reflect.TypeOf(c_UINT16_DEFAULT) + Uint32Type = reflect.TypeOf(c_UINT32_DEFAULT) + Uint64Type = reflect.TypeOf(c_UINT64_DEFAULT) + + Float32Type = reflect.TypeOf(c_FLOAT32_DEFAULT) + Float64Type = reflect.TypeOf(c_FLOAT64_DEFAULT) + + Complex64Type = reflect.TypeOf(c_COMPLEX64_DEFAULT) + Complex128Type = reflect.TypeOf(c_COMPLEX128_DEFAULT) + + StringType = reflect.TypeOf(c_EMPTY_STRING) + BoolType = reflect.TypeOf(c_BOOL_DEFAULT) + ByteType = reflect.TypeOf(c_BYTE_DEFAULT) + + TimeType = reflect.TypeOf(c_TIME_DEFAULT) +) + +var ( + PtrIntType = reflect.PtrTo(IntType) + PtrInt8Type = reflect.PtrTo(Int8Type) + PtrInt16Type = reflect.PtrTo(Int16Type) + PtrInt32Type = reflect.PtrTo(Int32Type) + PtrInt64Type = reflect.PtrTo(Int64Type) + + PtrUintType = reflect.PtrTo(UintType) + PtrUint8Type = reflect.PtrTo(Uint8Type) + PtrUint16Type = reflect.PtrTo(Uint16Type) + PtrUint32Type = reflect.PtrTo(Uint32Type) + PtrUint64Type = reflect.PtrTo(Uint64Type) + + PtrFloat32Type = reflect.PtrTo(Float32Type) + PtrFloat64Type = reflect.PtrTo(Float64Type) + + PtrComplex64Type = reflect.PtrTo(Complex64Type) + PtrComplex128Type = reflect.PtrTo(Complex128Type) + + PtrStringType = reflect.PtrTo(StringType) + PtrBoolType = reflect.PtrTo(BoolType) + PtrByteType = reflect.PtrTo(ByteType) + + PtrTimeType = reflect.PtrTo(TimeType) +) + 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: st = SQLType{Int, 0, 0} diff --git a/helpers.go b/helpers.go index 96f118f2..8b327fa8 100644 --- a/helpers.go +++ b/helpers.go @@ -7,6 +7,8 @@ import ( "strconv" "strings" "time" + + "github.com/lunny/xorm/core" ) func indexNoCase(s, sep string) int { @@ -94,7 +96,7 @@ func value2Bytes(rawValue *reflect.Value) (data []byte, err error) { } //时间类型 case reflect.Struct: - if aa == reflect.TypeOf(c_TIME_DEFAULT) { + if aa == core.TimeType { str = rawValue.Interface().(time.Time).Format(time.RFC3339Nano) data = []byte(str) } else { diff --git a/session.go b/session.go index dae0f935..9dc60c14 100644 --- a/session.go +++ b/session.go @@ -1379,10 +1379,10 @@ func (session *Session) dropAll() error { func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err error) { result := make(map[string][]byte) - var scanResultContainers []interface{} + scanResultContainers := make([]interface{}, len(fields)) for i := 0; i < len(fields); i++ { var scanResultContainer interface{} - scanResultContainers = append(scanResultContainers, &scanResultContainer) + scanResultContainers[i] = &scanResultContainer } if err := rows.Scan(scanResultContainers...); err != nil { return nil, err @@ -1435,10 +1435,10 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in table := session.Engine.autoMapType(rType(bean)) - var scanResultContainers []interface{} - for i := 0; i < fieldsCount; i++ { + scanResultContainers := make([]interface{}, len(fields)) + for i := 0; i < len(fields); i++ { var scanResultContainer interface{} - scanResultContainers = append(scanResultContainers, &scanResultContainer) + scanResultContainers[i] = &scanResultContainer } if err := rows.Scan(scanResultContainers...); err != nil { return err @@ -1528,8 +1528,8 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in fieldValue.SetUint(uint64(vv.Int())) } case reflect.Struct: - if fieldType == reflect.TypeOf(c_TIME_DEFAULT) { - if rawValueType == reflect.TypeOf(c_TIME_DEFAULT) { + if fieldType == core.TimeType { + if rawValueType == core.TimeType { hasAssigned = true fieldValue.Set(vv) } @@ -1567,97 +1567,97 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in //typeStr := fieldType.String() switch fieldType { // following types case matching ptr's native type, therefore assign ptr directly - case reflect.TypeOf(&c_EMPTY_STRING): + case core.PtrStringType: if rawValueType.Kind() == reflect.String { x := vv.String() hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_BOOL_DEFAULT): + case core.PtrBoolType: if rawValueType.Kind() == reflect.Bool { x := vv.Bool() hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_TIME_DEFAULT): - if rawValueType == reflect.TypeOf(c_TIME_DEFAULT) { + case core.PtrTimeType: + if rawValueType == core.PtrTimeType { hasAssigned = true var x time.Time = rawValue.Interface().(time.Time) fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_FLOAT64_DEFAULT): + case core.PtrFloat64Type: if rawValueType.Kind() == reflect.Float64 { x := vv.Float() hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_UINT64_DEFAULT): + case core.PtrUint64Type: if rawValueType.Kind() == reflect.Int64 { var x uint64 = uint64(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_INT64_DEFAULT): + case core.PtrInt64Type: if rawValueType.Kind() == reflect.Int64 { x := vv.Int() hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_FLOAT32_DEFAULT): + case core.PtrFloat32Type: if rawValueType.Kind() == reflect.Float64 { var x float32 = float32(vv.Float()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_INT_DEFAULT): + case core.PtrIntType: if rawValueType.Kind() == reflect.Int64 { var x int = int(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_INT32_DEFAULT): + case core.PtrInt32Type: if rawValueType.Kind() == reflect.Int64 { var x int32 = int32(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_INT8_DEFAULT): + case core.PtrInt8Type: if rawValueType.Kind() == reflect.Int64 { var x int8 = int8(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_INT16_DEFAULT): + case core.PtrInt16Type: if rawValueType.Kind() == reflect.Int64 { var x int16 = int16(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_UINT_DEFAULT): + case core.PtrUintType: if rawValueType.Kind() == reflect.Int64 { var x uint = uint(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_UINT32_DEFAULT): + case core.PtrUint32Type: if rawValueType.Kind() == reflect.Int64 { var x uint32 = uint32(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_UINT8_DEFAULT): + case core.Uint8Type: if rawValueType.Kind() == reflect.Int64 { var x uint8 = uint8(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_UINT16_DEFAULT): + case core.Uint16Type: if rawValueType.Kind() == reflect.Int64 { var x uint16 = uint16(vv.Int()) hasAssigned = true fieldValue.Set(reflect.ValueOf(&x)) } - case reflect.TypeOf(&c_COMPLEX64_DEFAULT): + case core.Complex64Type: var x complex64 err := json.Unmarshal([]byte(vv.String()), &x) if err != nil { @@ -1666,7 +1666,7 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in fieldValue.Set(reflect.ValueOf(&x)) } hasAssigned = true - case reflect.TypeOf(&c_COMPLEX128_DEFAULT): + case core.Complex128Type: var x complex128 err := json.Unmarshal([]byte(vv.String()), &x) if err != nil { @@ -2105,7 +2105,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, fieldValue.SetUint(x) //Currently only support Time type case reflect.Struct: - if fieldType == reflect.TypeOf(c_TIME_DEFAULT) { + if fieldType == core.TimeType { x, err := session.byte2Time(col, data) if err != nil { return err @@ -2146,11 +2146,11 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, //typeStr := fieldType.String() switch fieldType { // case "*string": - case reflect.TypeOf(&c_EMPTY_STRING): + case core.PtrStringType: x := string(data) fieldValue.Set(reflect.ValueOf(&x)) // case "*bool": - case reflect.TypeOf(&c_BOOL_DEFAULT): + case core.PtrBoolType: d := string(data) v, err := strconv.ParseBool(d) if err != nil { @@ -2158,7 +2158,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&v)) // case "*complex64": - case reflect.TypeOf(&c_COMPLEX64_DEFAULT): + case core.PtrComplex64Type: var x complex64 err := json.Unmarshal(data, &x) if err != nil { @@ -2167,7 +2167,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*complex128": - case reflect.TypeOf(&c_COMPLEX128_DEFAULT): + case core.PtrComplex128Type: var x complex128 err := json.Unmarshal(data, &x) if err != nil { @@ -2176,14 +2176,14 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*float64": - case reflect.TypeOf(&c_FLOAT64_DEFAULT): + 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 reflect.TypeOf(&c_FLOAT32_DEFAULT): + case core.PtrFloat32Type: var x float32 x1, err := strconv.ParseFloat(string(data), 32) if err != nil { @@ -2192,7 +2192,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, x = float32(x1) fieldValue.Set(reflect.ValueOf(&x)) // case "*time.Time": - case reflect.TypeOf(&c_TIME_DEFAULT): + case core.PtrTimeType: x, err := session.byte2Time(col, data) if err != nil { return err @@ -2200,7 +2200,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, v = x fieldValue.Set(reflect.ValueOf(&x)) // case "*uint64": - case reflect.TypeOf(&c_UINT64_DEFAULT): + case core.PtrUint64Type: var x uint64 x, err := strconv.ParseUint(string(data), 10, 64) if err != nil { @@ -2208,7 +2208,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*uint": - case reflect.TypeOf(&c_UINT_DEFAULT): + case core.PtrUintType: var x uint x1, err := strconv.ParseUint(string(data), 10, 64) if err != nil { @@ -2217,7 +2217,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, x = uint(x1) fieldValue.Set(reflect.ValueOf(&x)) // case "*uint32": - case reflect.TypeOf(&c_UINT32_DEFAULT): + case core.PtrUint32Type: var x uint32 x1, err := strconv.ParseUint(string(data), 10, 64) if err != nil { @@ -2226,7 +2226,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, x = uint32(x1) fieldValue.Set(reflect.ValueOf(&x)) // case "*uint8": - case reflect.TypeOf(&c_UINT8_DEFAULT): + case core.PtrUint8Type: var x uint8 x1, err := strconv.ParseUint(string(data), 10, 64) if err != nil { @@ -2235,7 +2235,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, x = uint8(x1) fieldValue.Set(reflect.ValueOf(&x)) // case "*uint16": - case reflect.TypeOf(&c_UINT16_DEFAULT): + case core.PtrUint16Type: var x uint16 x1, err := strconv.ParseUint(string(data), 10, 64) if err != nil { @@ -2244,7 +2244,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, x = uint16(x1) fieldValue.Set(reflect.ValueOf(&x)) // case "*int64": - case reflect.TypeOf(&c_INT64_DEFAULT): + case core.PtrInt64Type: sdata := string(data) var x int64 var err error @@ -2269,7 +2269,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*int": - case reflect.TypeOf(&c_INT_DEFAULT): + case core.PtrIntType: sdata := string(data) var x int var x1 int64 @@ -2298,14 +2298,14 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*int32": - case reflect.TypeOf(&c_INT32_DEFAULT): + 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 && - strings.Contains(session.Engine.DriverName, "mysql") { + session.Engine.dialect.DBType() == core.MYSQL { if len(data) == 1 { x = int32(data[0]) } else { @@ -2327,7 +2327,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*int8": - case reflect.TypeOf(&c_INT8_DEFAULT): + case core.PtrInt8Type: sdata := string(data) var x int8 var x1 int64 @@ -2356,7 +2356,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, } fieldValue.Set(reflect.ValueOf(&x)) // case "*int16": - case reflect.TypeOf(&c_INT16_DEFAULT): + case core.PtrInt16Type: sdata := string(data) var x int16 var x1 int64 @@ -2432,7 +2432,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val case reflect.String: return fieldValue.String(), nil case reflect.Struct: - if fieldType == reflect.TypeOf(c_TIME_DEFAULT) { + if fieldType == core.TimeType { t := fieldValue.Interface().(time.Time) if session.Engine.dialect.DBType() == core.MSSQL { if t.IsZero() { @@ -3208,3 +3208,74 @@ func (session *Session) Delete(bean interface{}) (int64, error) { return res.RowsAffected() } + +func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { + colNames := make([]string, 0) + args := make([]interface{}, 0) + + 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 { + continue + } + } + if col.MapType == core.ONLYFROMDB { + continue + } + + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + continue + } + fieldValue := *fieldValuePtr + + if col.IsAutoIncrement { + switch fieldValue.Type().Kind() { + case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: + if fieldValue.Int() == 0 { + continue + } + case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: + if fieldValue.Uint() == 0 { + continue + } + case reflect.String: + if len(fieldValue.String()) == 0 { + continue + } + } + } + + if session.Statement.ColumnStr != "" { + if _, ok := session.Statement.columnMap[lColName]; !ok { + continue + } + } + if session.Statement.OmitStr != "" { + if _, ok := session.Statement.columnMap[lColName]; ok { + continue + } + } + + if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { + args = append(args, time.Now()) + } else if col.IsVersion && session.Statement.checkVersion { + args = append(args, 1) + } else { + arg, err := session.value2Interface(col, fieldValue) + if err != nil { + return colNames, args, err + } + args = append(args, arg) + } + + if includeQuote { + colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?") + } else { + colNames = append(colNames, col.Name) + } + } + return colNames, args, nil +} diff --git a/table.go b/table.go deleted file mode 100644 index 69a7d122..00000000 --- a/table.go +++ /dev/null @@ -1,101 +0,0 @@ -package xorm - -import ( - "reflect" - "strings" - "time" - - "github.com/lunny/xorm/core" -) - -var ( - c_EMPTY_STRING string - c_BOOL_DEFAULT bool - c_BYTE_DEFAULT byte - c_COMPLEX64_DEFAULT complex64 - c_COMPLEX128_DEFAULT complex128 - c_FLOAT32_DEFAULT float32 - c_FLOAT64_DEFAULT float64 - c_INT64_DEFAULT int64 - c_UINT64_DEFAULT uint64 - c_INT32_DEFAULT int32 - c_UINT32_DEFAULT uint32 - c_INT16_DEFAULT int16 - c_UINT16_DEFAULT uint16 - c_INT8_DEFAULT int8 - c_UINT8_DEFAULT uint8 - c_INT_DEFAULT int - c_UINT_DEFAULT uint - c_TIME_DEFAULT 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) - - 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 { - continue - } - } - if col.MapType == core.ONLYFROMDB { - continue - } - - fieldValuePtr, err := col.ValueOf(bean) - if err != nil { - session.Engine.LogError(err) - continue - } - fieldValue := *fieldValuePtr - - if col.IsAutoIncrement { - switch fieldValue.Type().Kind() { - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64: - if fieldValue.Int() == 0 { - continue - } - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: - if fieldValue.Uint() == 0 { - continue - } - case reflect.String: - if len(fieldValue.String()) == 0 { - continue - } - } - } - - if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[lColName]; !ok { - continue - } - } - if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[lColName]; ok { - continue - } - } - - if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime { - args = append(args, time.Now()) - } else if col.IsVersion && session.Statement.checkVersion { - args = append(args, 1) - } else { - arg, err := session.value2Interface(col, fieldValue) - if err != nil { - return colNames, args, err - } - args = append(args, arg) - } - - if includeQuote { - colNames = append(colNames, session.Engine.Quote(col.Name)+" = ?") - } else { - colNames = append(colNames, col.Name) - } - } - return colNames, args, nil -} From b66e7aa067c51e0dcfef9f07a7047ab1d2e31891 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 11 Feb 2014 14:59:04 +0800 Subject: [PATCH 20/55] use core.DB instead of sql.DB --- core/db.go | 13 +++++++++++++ engine.go | 4 ++-- helpers.go | 3 +-- pool.go | 30 +++++++++++++++--------------- rows.go | 6 ++++-- session.go | 24 ++++++++++++------------ 6 files changed, 47 insertions(+), 33 deletions(-) diff --git a/core/db.go b/core/db.go index 97103039..6dedbb86 100644 --- a/core/db.go +++ b/core/db.go @@ -155,6 +155,19 @@ func (s *Stmt) ExecStruct(st interface{}) (sql.Result, error) { return s.Stmt.Exec(args...) } +func (s *Stmt) Query(args ...interface{}) (*Rows, error) { + rows, err := s.Stmt.Query(args...) + if err != nil { + return nil, err + } + return &Rows{rows, s.Mapper}, nil +} + +func (s *Stmt) QueryRow(args ...interface{}) *Row { + row := s.Stmt.QueryRow(args...) + return &Row{row, nil, s.Mapper} +} + var ( re = regexp.MustCompile(`[?](\w+)`) ) diff --git a/engine.go b/engine.go index af910562..2ac88f33 100644 --- a/engine.go +++ b/engine.go @@ -124,8 +124,8 @@ func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { } // OpenDB provides a interface to operate database directly. -func (engine *Engine) OpenDB() (*sql.DB, error) { - return sql.Open(engine.DriverName, engine.DataSourceName) +func (engine *Engine) OpenDB() (*core.DB, error) { + return core.Open(engine.DriverName, engine.DataSourceName) } // New a session diff --git a/helpers.go b/helpers.go index 8b327fa8..12ed4a03 100644 --- a/helpers.go +++ b/helpers.go @@ -1,7 +1,6 @@ package xorm import ( - "database/sql" "fmt" "reflect" "strconv" @@ -121,7 +120,7 @@ func value2Bytes(rawValue *reflect.Value) (data []byte, err error) { return } -func rows2maps(rows *sql.Rows) (resultsSlice []map[string][]byte, err error) { +func rows2maps(rows *core.Rows) (resultsSlice []map[string][]byte, err error) { fields, err := rows.Columns() if err != nil { return nil, err diff --git a/pool.go b/pool.go index 7a7b173e..bdef3268 100644 --- a/pool.go +++ b/pool.go @@ -1,7 +1,7 @@ package xorm import ( - "database/sql" + "github.com/lunny/xorm/core" //"fmt" "sync" //"sync/atomic" @@ -18,8 +18,8 @@ import ( // Close for invoking when engine.Close type IConnectPool interface { Init(engine *Engine) error - RetrieveDB(engine *Engine) (*sql.DB, error) - ReleaseDB(engine *Engine, db *sql.DB) + RetrieveDB(engine *Engine) (*core.DB, error) + ReleaseDB(engine *Engine, db *core.DB) Close(engine *Engine) error SetMaxIdleConns(conns int) MaxIdleConns() int @@ -43,13 +43,13 @@ func (p *NoneConnectPool) Init(engine *Engine) error { } // RetrieveDB directly open a connection -func (p *NoneConnectPool) RetrieveDB(engine *Engine) (db *sql.DB, err error) { +func (p *NoneConnectPool) RetrieveDB(engine *Engine) (db *core.DB, err error) { db, err = engine.OpenDB() return } // ReleaseDB directly close a connection -func (p *NoneConnectPool) ReleaseDB(engine *Engine, db *sql.DB) { +func (p *NoneConnectPool) ReleaseDB(engine *Engine, db *core.DB) { db.Close() } @@ -78,7 +78,7 @@ func (p *NoneConnectPool) MaxConns() int { // About the system connection pool, you can review the code database/sql/sql.go // It's currently default Pool implments. type SysConnectPool struct { - db *sql.DB + db *core.DB maxIdleConns int maxConns int curConns int @@ -118,7 +118,7 @@ func newCondNode() *node { } // RetrieveDB just return the only db -func (s *SysConnectPool) RetrieveDB(engine *Engine) (db *sql.DB, err error) { +func (s *SysConnectPool) RetrieveDB(engine *Engine) (db *core.DB, err error) { /*if s.maxConns > 0 { fmt.Println("before retrieve") s.mutex.Lock() @@ -141,7 +141,7 @@ func (s *SysConnectPool) RetrieveDB(engine *Engine) (db *sql.DB, err error) { } // ReleaseDB do nothing -func (s *SysConnectPool) ReleaseDB(engine *Engine, db *sql.DB) { +func (s *SysConnectPool) ReleaseDB(engine *Engine, db *core.DB) { /*if s.maxConns > 0 { s.mutex.Lock() fmt.Println("before release", s.queue.Len()) @@ -191,8 +191,8 @@ func (p *SysConnectPool) MaxConns() int { // NewSimpleConnectPool new a SimpleConnectPool func NewSimpleConnectPool() IConnectPool { - return &SimpleConnectPool{releasedConnects: make([]*sql.DB, 10), - usingConnects: map[*sql.DB]time.Time{}, + return &SimpleConnectPool{releasedConnects: make([]*core.DB, 10), + usingConnects: map[*core.DB]time.Time{}, cur: -1, maxWaitTimeOut: 14400, maxIdleConns: 10, @@ -205,9 +205,9 @@ func NewSimpleConnectPool() IConnectPool { // Opening or Closing a database connection must be enter a lock. // This implements will be improved in furture. type SimpleConnectPool struct { - releasedConnects []*sql.DB + releasedConnects []*core.DB cur int - usingConnects map[*sql.DB]time.Time + usingConnects map[*core.DB]time.Time maxWaitTimeOut int mutex *sync.Mutex maxIdleConns int @@ -218,10 +218,10 @@ func (s *SimpleConnectPool) Init(engine *Engine) error { } // RetrieveDB get a connection from connection pool -func (p *SimpleConnectPool) RetrieveDB(engine *Engine) (*sql.DB, error) { +func (p *SimpleConnectPool) RetrieveDB(engine *Engine) (*core.DB, error) { p.mutex.Lock() defer p.mutex.Unlock() - var db *sql.DB = nil + var db *core.DB = nil var err error = nil //fmt.Printf("%x, rbegin - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) if p.cur < 0 { @@ -242,7 +242,7 @@ func (p *SimpleConnectPool) RetrieveDB(engine *Engine) (*sql.DB, error) { } // ReleaseDB release a db from connection pool -func (p *SimpleConnectPool) ReleaseDB(engine *Engine, db *sql.DB) { +func (p *SimpleConnectPool) ReleaseDB(engine *Engine, db *core.DB) { p.mutex.Lock() defer p.mutex.Unlock() //fmt.Printf("%x, lbegin - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) diff --git a/rows.go b/rows.go index ef04af75..ecbbce46 100644 --- a/rows.go +++ b/rows.go @@ -4,14 +4,16 @@ import ( "database/sql" "fmt" "reflect" + + "github.com/lunny/xorm/core" ) type Rows struct { NoTypeCheck bool session *Session - stmt *sql.Stmt - rows *sql.Rows + stmt *core.Stmt + rows *core.Rows fields []string fieldsCount int beanType reflect.Type diff --git a/session.go b/session.go index 9dc60c14..271175fc 100644 --- a/session.go +++ b/session.go @@ -17,9 +17,9 @@ import ( // Struct Session keep a pointer to sql.DB and provides all execution of all // kind of database operations. type Session struct { - Db *sql.DB + Db *core.DB Engine *Engine - Tx *sql.Tx + Tx *core.Tx Statement Statement IsAutoCommit bool IsCommitedOrRollbacked bool @@ -35,7 +35,7 @@ type Session struct { beforeClosures []func(interface{}) afterClosures []func(interface{}) - stmtCache map[uint32]*sql.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) + stmtCache map[uint32]*core.Stmt //key: hash.Hash32 of (queryStr, len(queryStr)) } // Method Init reset the session as the init status. @@ -262,7 +262,7 @@ func (session *Session) newDb() error { return err } session.Db = db - session.stmtCache = make(map[uint32]*sql.Stmt, 0) + session.stmtCache = make(map[uint32]*core.Stmt, 0) } return nil } @@ -897,7 +897,7 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error { return nil } -func (session *Session) doPrepare(sqlStr string) (stmt *sql.Stmt, err error) { +func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) { crc := crc32.ChecksumIEEE([]byte(sqlStr)) // TODO try hash(sqlStr+len(sqlStr)) var has bool @@ -944,7 +944,7 @@ func (session *Session) Get(bean interface{}) (bool, error) { } } - var rawRows *sql.Rows + var rawRows *core.Rows session.queryPreprocess(&sqlStr, args...) if session.IsAutoCommit { stmt, err := session.doPrepare(sqlStr) @@ -1102,8 +1102,8 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) } if sliceValue.Kind() != reflect.Map { - var rawRows *sql.Rows - var stmt *sql.Stmt + var rawRows *core.Rows + var stmt *core.Stmt session.queryPreprocess(&sqlStr, args...) // err = session.queryRows(&stmt, &rawRows, sqlStr, args...) @@ -1377,7 +1377,7 @@ func (session *Session) dropAll() error { return nil } -func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err error) { +func row2map(rows *core.Rows, fields []string) (resultsMap map[string][]byte, err error) { result := make(map[string][]byte) scanResultContainers := make([]interface{}, len(fields)) for i := 0; i < len(fields); i++ { @@ -1426,7 +1426,7 @@ func (session *Session) getField(dataStruct *reflect.Value, key string, table *c return fieldValue } -func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error { +func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount int, bean interface{}) error { dataStruct := reflect.Indirect(reflect.ValueOf(bean)) if dataStruct.Kind() != reflect.Struct { @@ -1713,7 +1713,7 @@ func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSl return txQuery(session.Tx, sqlStr, paramStr...) } -func txQuery(tx *sql.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { +func 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 @@ -1723,7 +1723,7 @@ func txQuery(tx *sql.Tx, sqlStr string, params ...interface{}) (resultsSlice []m return rows2maps(rows) } -func query(db *sql.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { +func query(db *core.DB, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) { s, err := db.Prepare(sqlStr) if err != nil { return nil, err From 2c79b617411f4f72d69354e85e097cd2bd4d8d87 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 11 Feb 2014 15:07:21 +0800 Subject: [PATCH 21/55] add map & struct methods for core.Stmt --- core/db.go | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/core/db.go b/core/db.go index 6dedbb86..ed97987a 100644 --- a/core/db.go +++ b/core/db.go @@ -163,11 +163,67 @@ func (s *Stmt) Query(args ...interface{}) (*Rows, error) { return &Rows{rows, s.Mapper}, nil } +func (s *Stmt) QueryMap(mp interface{}) (*Rows, error) { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.Query(args...) +} + +func (s *Stmt) QueryStruct(st interface{}) (*Rows, error) { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return nil, errors.New("mp should be a map's pointer") + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.Query(args...) +} + func (s *Stmt) QueryRow(args ...interface{}) *Row { row := s.Stmt.QueryRow(args...) return &Row{row, nil, s.Mapper} } +func (s *Stmt) QueryRowMap(mp interface{}) *Row { + vv := reflect.ValueOf(mp) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Map { + return &Row{nil, errors.New("mp should be a map's pointer"), s.Mapper} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().MapIndex(reflect.ValueOf(k)).Interface() + } + + return s.QueryRow(args...) +} + +func (s *Stmt) QueryRowStruct(st interface{}) *Row { + vv := reflect.ValueOf(st) + if vv.Kind() != reflect.Ptr || vv.Elem().Kind() != reflect.Struct { + return &Row{nil, errors.New("st should be a struct's pointer"), s.Mapper} + } + + args := make([]interface{}, len(s.names)) + for k, i := range s.names { + args[i] = vv.Elem().FieldByName(k).Interface() + } + + return s.QueryRow(args...) +} + var ( re = regexp.MustCompile(`[?](\w+)`) ) From 4f6106e168dfc6d3456d5541e7de1a4a017f848b Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Wed, 12 Feb 2014 01:35:26 +0800 Subject: [PATCH 22/55] private Engine.LogSQL and create ILogger interface that is compatible with log/syslog package. Also fixed use of LogError instead of LogSQL --- engine.go | 24 ++++++++++++++++-------- logger.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ rows.go | 13 ++++++------- session.go | 20 +++++++++----------- statement.go | 8 ++++---- xorm.go | 10 ++++++---- 6 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 logger.go diff --git a/engine.go b/engine.go index 2ac88f33..527e72c7 100644 --- a/engine.go +++ b/engine.go @@ -6,7 +6,6 @@ import ( "database/sql" "errors" "fmt" - "io" "os" "reflect" "strconv" @@ -34,7 +33,7 @@ type Engine struct { ShowWarn bool Pool IConnectPool Filters []core.Filter - Logger io.Writer + Logger ILogger // io.Writer Cacher core.Cacher tableCachers map[reflect.Type]core.Cacher } @@ -144,35 +143,44 @@ func (engine *Engine) Close() error { func (engine *Engine) Ping() error { session := engine.NewSession() defer session.Close() - engine.LogSQL("PING DATABASE", engine.DriverName) + engine.LogInfo("PING DATABASE", engine.DriverName) return session.Ping() } // logging sql -func (engine *Engine) LogSQL(contents ...interface{}) { +func (engine *Engine) logSQL(sqlStr string, sqlArgs ...interface{}) { if engine.ShowSQL { - io.WriteString(engine.Logger, fmt.Sprintln(contents...)) + if len(sqlArgs) > 0 { + engine.LogInfo("[sql]", sqlStr, "[args]", sqlArgs) + } else { + engine.LogInfo("[sql]", sqlStr) + } } } // logging error func (engine *Engine) LogError(contents ...interface{}) { if engine.ShowErr { - io.WriteString(engine.Logger, fmt.Sprintln(contents...)) + engine.Logger.Err(fmt.Sprintln(contents...)) } } +// logging error +func (engine *Engine) LogInfo(contents ...interface{}) { + engine.Logger.Info(fmt.Sprintln(contents...)) +} + // logging debug func (engine *Engine) LogDebug(contents ...interface{}) { if engine.ShowDebug { - io.WriteString(engine.Logger, fmt.Sprintln(contents...)) + engine.Logger.Debug(fmt.Sprintln(contents...)) } } // logging warn func (engine *Engine) LogWarn(contents ...interface{}) { if engine.ShowWarn { - io.WriteString(engine.Logger, fmt.Sprintln(contents...)) + engine.Logger.Warning(fmt.Sprintln(contents...)) } } diff --git a/logger.go b/logger.go new file mode 100644 index 00000000..c4c17bbb --- /dev/null +++ b/logger.go @@ -0,0 +1,48 @@ +package xorm + +import ( + "io" + "log" +) + +// logger interface, log/syslog conform with this interface +type ILogger interface { + Debug(m string) (err error) + Err(m string) (err error) + Info(m string) (err error) + Warning(m string) (err error) +} + +type SimpleLogger struct { + logger *log.Logger +} + +func NewSimpleLogger(out io.Writer) *SimpleLogger { + return &SimpleLogger{ + logger: log.New(out, "[xorm] ", log.Ldate|log.Lmicroseconds)} +} + +func NewSimpleLogger2(out io.Writer, prefix string, flag int) *SimpleLogger { + return &SimpleLogger{ + logger: log.New(out, prefix, flag)} +} + +func (s *SimpleLogger) Debug(m string) (err error) { + s.logger.Println("[debug]", m) + return +} + +func (s *SimpleLogger) Err(m string) (err error) { + s.logger.Println("[error]", m) + return +} + +func (s *SimpleLogger) Info(m string) (err error) { + s.logger.Println("[info]", m) + return +} + +func (s *SimpleLogger) Warning(m string) (err error) { + s.logger.Println("[warning]", m) + return +} diff --git a/rows.go b/rows.go index ecbbce46..2d30e58e 100644 --- a/rows.go +++ b/rows.go @@ -32,24 +32,23 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { defer rows.session.Statement.Init() - var sql string + var sqlStr string var args []interface{} rows.session.Statement.RefTable = rows.session.Engine.autoMap(bean) if rows.session.Statement.RawSQL == "" { - sql, args = rows.session.Statement.genGetSql(bean) + sqlStr, args = rows.session.Statement.genGetSql(bean) } else { - sql = rows.session.Statement.RawSQL + sqlStr = rows.session.Statement.RawSQL args = rows.session.Statement.RawParams } for _, filter := range rows.session.Engine.Filters { - sql = filter.Do(sql, session.Engine.dialect, rows.session.Statement.RefTable) + sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable) } - rows.session.Engine.LogSQL(sql) - rows.session.Engine.LogSQL(args) + rows.session.Engine.logSQL(sqlStr, args) - rows.stmt, err = rows.session.Db.Prepare(sql) + rows.stmt, err = rows.session.Db.Prepare(sqlStr) if err != nil { rows.lastError = err defer rows.Close() diff --git a/session.go b/session.go index 271175fc..d19b10d9 100644 --- a/session.go +++ b/session.go @@ -282,7 +282,7 @@ func (session *Session) Begin() error { session.IsCommitedOrRollbacked = false session.Tx = tx - session.Engine.LogSQL("BEGIN TRANSACTION") + session.Engine.logSQL("BEGIN TRANSACTION") } return nil } @@ -290,7 +290,7 @@ func (session *Session) Begin() error { // When using transaction, you can rollback if any error func (session *Session) Rollback() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.Engine.LogSQL("ROLL BACK") + session.Engine.logSQL("ROLL BACK") session.IsCommitedOrRollbacked = true return session.Tx.Rollback() } @@ -300,7 +300,7 @@ func (session *Session) Rollback() error { // When using transaction, Commit will commit all operations. func (session *Session) Commit() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { - session.Engine.LogSQL("COMMIT") + session.Engine.logSQL("COMMIT") session.IsCommitedOrRollbacked = true var err error if err = session.Tx.Commit(); err == nil { @@ -419,8 +419,7 @@ func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, er sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } - session.Engine.LogSQL(sqlStr) - session.Engine.LogSQL(args) + session.Engine.logSQL(sqlStr, args) if session.IsAutoCommit { return session.innerExec(sqlStr, args...) @@ -1480,7 +1479,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i x := reflect.New(fieldType) err := json.Unmarshal([]byte(vv.String()), x.Interface()) if err != nil { - session.Engine.LogSQL(err) + session.Engine.LogError(err) return err } fieldValue.Set(x.Elem()) @@ -1700,8 +1699,7 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable) } - session.Engine.LogSQL(*sqlStr) - session.Engine.LogSQL(paramStr) + session.Engine.logSQL(*sqlStr, paramStr) } func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { @@ -2467,7 +2465,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val case reflect.Complex64, reflect.Complex128: bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - session.Engine.LogSQL(err) + session.Engine.LogError(err) return 0, err } return string(bytes), nil @@ -2479,7 +2477,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - session.Engine.LogSQL(err) + session.Engine.LogError(err) return 0, err } return string(bytes), nil @@ -2492,7 +2490,7 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val } else { bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - session.Engine.LogSQL(err) + session.Engine.LogError(err) return 0, err } } diff --git a/statement.go b/statement.go index 23cb0591..94e2baa4 100644 --- a/statement.go +++ b/statement.go @@ -210,7 +210,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogSQL(err) + engine.LogError(err) continue } val = string(bytes) @@ -227,7 +227,7 @@ func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { } else { bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogSQL(err) + engine.LogError(err) continue } val = bytes @@ -373,7 +373,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, if col.SQLType.IsText() { bytes, err := json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogSQL(err) + engine.LogError(err) continue } val = string(bytes) @@ -390,7 +390,7 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, } else { bytes, err = json.Marshal(fieldValue.Interface()) if err != nil { - engine.LogSQL(err) + engine.LogError(err) continue } val = bytes diff --git a/xorm.go b/xorm.go index 40653eea..497cd005 100644 --- a/xorm.go +++ b/xorm.go @@ -46,9 +46,11 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return nil, err } - engine := &Engine{DriverName: driverName, - DataSourceName: dataSourceName, dialect: dialect, - tableCachers: make(map[reflect.Type]core.Cacher)} + engine := &Engine{ + DriverName: driverName, + DataSourceName: dataSourceName, + dialect: dialect, + tableCachers: make(map[reflect.Type]core.Cacher)} engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) @@ -58,7 +60,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { engine.mutex = &sync.RWMutex{} engine.TagIdentifier = "xorm" - engine.Logger = os.Stdout + engine.Logger = NewSimpleLogger(os.Stdout) //engine.Pool = NewSimpleConnectPool() //engine.Pool = NewNoneConnectPool() From 128db9c7922b7262600392fb5300995704071cd6 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 11 Apr 2014 17:04:39 +0800 Subject: [PATCH 23/55] bug fixed and tests passed --- engine.go | 3 +-- tests/base_test.go | 20 ++++++++++---------- tests/benchmark_base_test.go | 2 +- tests/mssql_test.go | 4 ++-- tests/mymysql_test.go | 4 ++-- tests/mysql_test.go | 6 +++--- tests/postgres_test.go | 6 +++--- tests/sqlite3_test.go | 6 +++--- 8 files changed, 25 insertions(+), 26 deletions(-) diff --git a/engine.go b/engine.go index ca1eb522..0f4caacf 100644 --- a/engine.go +++ b/engine.go @@ -589,8 +589,7 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { col.Name = key } } - panic("broken") - //engine.dialect.SqlType(col) + engine.dialect.SqlType(col) } preKey = k } diff --git a/tests/base_test.go b/tests/base_test.go index c0c37015..4f5704a8 100644 --- a/tests/base_test.go +++ b/tests/base_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/lunny/xorm" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" ) /* @@ -442,8 +442,8 @@ func update(engine *xorm.Engine, t *testing.T) { } col2 := &UpdateMustCols{col1.Id, true, ""} - boolStr := engine.columnMapper.Obj2Table("Bool") - stringStr := engine.columnMapper.Obj2Table("String") + boolStr := engine.ColumnMapper.Obj2Table("Bool") + stringStr := engine.ColumnMapper.Obj2Table("String") _, err = engine.Id(col2.Id).MustCols(boolStr, stringStr).Update(col2) if err != nil { t.Error(err) @@ -746,7 +746,8 @@ func in(engine *xorm.Engine, t *testing.T) { users = make([]Userinfo, 0) ids := []interface{}{7, 8, 9} - err = engine.Where("departname = ?", "dev").In("(id)", ids...).Find(&users) + department := engine.ColumnMapper.Obj2Table("Departname") + err = engine.Where("`"+department+"` = ?", "dev").In("(id)", ids...).Find(&users) if err != nil { t.Error(err) panic(err) @@ -767,7 +768,6 @@ func in(engine *xorm.Engine, t *testing.T) { } } - department := engine.ColumnMapper.Obj2Table("Departname") dev := engine.ColumnMapper.Obj2Table("Dev") err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) @@ -3918,9 +3918,9 @@ type Lowercase struct { ended int64 `xorm:"-"` } -func testLowerCase(engine *Engine, t *testing.T) { +func testLowerCase(engine *xorm.Engine, t *testing.T) { err := engine.Sync(&Lowercase{}) - _, err = engine.Where("id > 0").Delete(&Lowercase{}) + _, err = engine.Where("(id) > 0").Delete(&Lowercase{}) if err != nil { t.Error(err) panic(err) @@ -4019,7 +4019,7 @@ func (c *CustomTableName) TableName() string { return "customtablename" } -func testCustomTableName(engine *Engine, t *testing.T) { +func testCustomTableName(engine *xorm.Engine, t *testing.T) { c := new(CustomTableName) err := engine.DropTables(c) if err != nil { @@ -4032,7 +4032,7 @@ func testCustomTableName(engine *Engine, t *testing.T) { } } -func testAll(engine *Engine, t *testing.T) { +func testAll(engine *xorm.Engine, t *testing.T) { fmt.Println("-------------- directCreateTable --------------") directCreateTable(engine, t) fmt.Println("-------------- insert --------------") diff --git a/tests/benchmark_base_test.go b/tests/benchmark_base_test.go index 5570fa39..d970bd13 100644 --- a/tests/benchmark_base_test.go +++ b/tests/benchmark_base_test.go @@ -4,7 +4,7 @@ import ( "database/sql" "testing" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" ) type BigStruct struct { diff --git a/tests/mssql_test.go b/tests/mssql_test.go index e814d855..fac774e1 100644 --- a/tests/mssql_test.go +++ b/tests/mssql_test.go @@ -7,9 +7,9 @@ import ( "database/sql" "testing" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" _ "github.com/lunny/godbc" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" ) const mssqlConnStr = "driver={SQL Server};Server=192.168.20.135;Database=xorm_test; uid=sa; pwd=1234;" diff --git a/tests/mymysql_test.go b/tests/mymysql_test.go index 5646f95c..ef212a2c 100644 --- a/tests/mymysql_test.go +++ b/tests/mymysql_test.go @@ -4,8 +4,8 @@ import ( "database/sql" "testing" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" _ "github.com/ziutek/mymysql/godrv" ) diff --git a/tests/mysql_test.go b/tests/mysql_test.go index 38eebc55..02a70b39 100644 --- a/tests/mysql_test.go +++ b/tests/mysql_test.go @@ -5,9 +5,9 @@ import ( "testing" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" ) /* diff --git a/tests/postgres_test.go b/tests/postgres_test.go index 85e926cc..8abee32f 100644 --- a/tests/postgres_test.go +++ b/tests/postgres_test.go @@ -4,10 +4,10 @@ import ( "database/sql" "testing" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" _ "github.com/lib/pq" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" - "github.com/lunny/xorm/core" ) //var connStr string = "dbname=xorm_test user=lunny password=1234 sslmode=disable" diff --git a/tests/sqlite3_test.go b/tests/sqlite3_test.go index 1f982e29..18cc84a3 100644 --- a/tests/sqlite3_test.go +++ b/tests/sqlite3_test.go @@ -5,9 +5,9 @@ import ( "os" "testing" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" _ "github.com/mattn/go-sqlite3" ) From 1213bc14b598327c23ab49889a78999ca3c196f4 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Fri, 11 Apr 2014 17:16:43 +0800 Subject: [PATCH 24/55] renamed import path --- .gopmfile | 2 +- CONTRIBUTING.md | 6 +- README.md | 20 ++-- README_CN.md | 18 ++-- doc.go | 4 +- docs/Changelog.md | 24 ++--- docs/ChangelogCN.md | 8 +- docs/QuickStart.md | 22 ++-- docs/QuickStartEn.md | 204 ++++++++++++++++++------------------- examples/cache.go | 4 +- examples/cachegoroutine.go | 2 +- examples/conversion.go | 2 +- examples/derive.go | 2 +- examples/goroutine.go | 2 +- examples/maxconnect.go | 2 +- examples/pool.go | 2 +- examples/singlemapping.go | 2 +- examples/sync.go | 2 +- xorm/.gopmfile | 2 +- xorm/README.md | 4 +- xorm/c++.go | 2 +- xorm/go.go | 2 +- xorm/lang.go | 2 +- xorm/reverse.go | 4 +- xorm/shell.go | 2 +- 25 files changed, 173 insertions(+), 173 deletions(-) diff --git a/.gopmfile b/.gopmfile index 3590b536..ecd6e20e 100644 --- a/.gopmfile +++ b/.gopmfile @@ -1,2 +1,2 @@ [target] -path = github.com/lunny/xorm \ No newline at end of file +path = github.com/go-xorm/xorm \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6f65c2ae..fcbf9e31 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -21,11 +21,11 @@ We appreciate any bug reports, but especially ones with self-contained further) test cases. It's especially helpful if you can submit a pull request with just the failing test case (you'll probably want to pattern it after the tests in -[base_test.go](https://github.com/lunny/xorm/blob/master/base_test.go) AND -[benchmark_base_test.go](https://github.com/lunny/xorm/blob/master/benchmark_base_test.go). +[base_test.go](https://github.com/go-xorm/xorm/blob/master/base_test.go) AND +[benchmark_base_test.go](https://github.com/go-xorm/xorm/blob/master/benchmark_base_test.go). If you implements a new database interface, you maybe need to add a _test.go file. -For example, [mysql_test.go](https://github.com/lunny/xorm/blob/master/mysql_test.go) +For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/mysql_test.go) ### New functionality diff --git a/README.md b/README.md index 4f85368b..ec56f0b3 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -[中文](https://github.com/lunny/xorm/blob/master/README_CN.md) +[中文](https://github.com/go-xorm/xorm/blob/master/README_CN.md) Xorm is a simple and powerful ORM for Go. -[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") +[![Build Status](https://drone.io/github.com/go-xorm/xorm/status.png)](https://drone.io/github.com/go-xorm/xorm/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") # Features @@ -18,7 +18,7 @@ Xorm is a simple and powerful ORM for Go. * Query Cache speed up -* Database Reverse support, See [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md) +* Database Reverse support, See [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md) * Simple cascade loading support @@ -54,25 +54,25 @@ Drivers for Go's sql package which currently support database/sql includes: * Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type * Performance improvement for Get()/Find()/Iterate() -[More changelogs ...](https://github.com/lunny/xorm/blob/master/docs/Changelog.md) +[More changelogs ...](https://github.com/go-xorm/xorm/blob/master/docs/Changelog.md) # Installation If you have [gopm](https://github.com/gpmgo/gopm) installed, - gopm get github.com/lunny/xorm + gopm get github.com/go-xorm/xorm Or - go get github.com/lunny/xorm + go get github.com/go-xorm/xorm # Documents -* [GoDoc](http://godoc.org/github.com/lunny/xorm) +* [GoDoc](http://godoc.org/github.com/go-xorm/xorm) -* [GoWalker](http://gowalker.org/github.com/lunny/xorm) +* [GoWalker](http://gowalker.org/github.com/go-xorm/xorm) -* [Quick Start](https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md) +* [Quick Start](https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md) # Cases @@ -96,7 +96,7 @@ Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xor # Contributors -If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md) +If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md) * [Lunny](https://github.com/lunny) * [Nashtsai](https://github.com/nashtsai) diff --git a/README_CN.md b/README_CN.md index 35b7ed70..ca43aa86 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,10 +1,10 @@ # xorm -[English](https://github.com/lunny/xorm/blob/master/README.md) +[English](https://github.com/go-xorm/xorm/blob/master/README.md) xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 -[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) +[![Build Status](https://drone.io/github.com/go-xorm/xorm/status.png)](https://drone.io/github.com/go-xorm/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/go-xorm/xorm) ## 特性 @@ -56,25 +56,25 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * 查询函数 Get()/Find()/Iterate() 在性能上的改进 -[更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md) +[更多更新日志...](https://github.com/go-xorm/xorm/blob/master/docs/ChangelogCN.md) ## 安装 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: - gopm get github.com/lunny/xorm + gopm get github.com/go-xorm/xorm 或者您也可以使用go工具进行安装: - go get github.com/lunny/xorm + go get github.com/go-xorm/xorm ## 文档 -* [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md) +* [快速开始](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md) -* [GoWalker代码文档](http://gowalker.org/github.com/lunny/xorm) +* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm) -* [Godoc代码文档](http://godoc.org/github.com/lunny/xorm) +* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm) ## 案例 @@ -99,7 +99,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 # 贡献者 -如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md) +如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md) * [Lunny](https://github.com/lunny) * [Nashtsai](https://github.com/nashtsai) diff --git a/doc.go b/doc.go index 16d0b3a7..b183e8d2 100644 --- a/doc.go +++ b/doc.go @@ -9,7 +9,7 @@ Installation Make sure you have installed Go 1.1+ and then: - go get github.com/lunny/xorm + go get github.com/go-xorm/xorm Create Engine @@ -137,6 +137,6 @@ The above 7 methods could use with condition methods. engine.Join("LEFT", "userdetail", "user.id=userdetail.id").Find() //SELECT * FROM user LEFT JOIN userdetail ON user.id=userdetail.id -More usage, please visit https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md +More usage, please visit https://github.com/go-xorm/xorm/blob/master/docs/QuickStartEn.md */ package xorm diff --git a/docs/Changelog.md b/docs/Changelog.md index aeeccbbe..e3f5a05c 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -11,20 +11,20 @@ Improvements: * Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type - * Performance improvement for Get()/Find()/Iterate() + * Performance improvement for Get()/Find()/Iterate() * **v0.2.3** : Improved documents; Optimistic Locking support; Timestamp with time zone support; Mapper change to tableMapper and columnMapper & added PrefixMapper & SuffixMapper support custom table or column name's prefix and suffix;Insert now return affected, err instead of id, err; Added UseBool & Distinct; * **v0.2.2** : Postgres drivers now support lib/pq; Added method Iterate for record by record to handler;Added SetMaxConns(go1.2+) support; some bugs fixed. -* **v0.2.1** : Added database reverse tool, now support generate go & c++ codes, see [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); some bug fixed. -* **v0.2.0** : Added Cache supported, select is speeder up 3~5x; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes; -* **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping). -* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping). -* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit. -* **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support -* **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine. -* **v0.1.4** : Added simple cascade load support; added more data type supports. -* **v0.1.3** : Find function now supports both slice and map; Add Table function for multi tables and temperory tables support -* **v0.1.2** : Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction -* **v0.1.1** : Add Id, In functions and improved README +* **v0.2.1** : Added database reverse tool, now support generate go & c++ codes, see [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md); some bug fixed. +* **v0.2.0** : Added Cache supported, select is speeder up 3~5x; Added SameMapper for same name between struct and table; Added Sync method for auto added tables, columns, indexes; +* **v0.1.9** : Added postgres and mymysql supported; Added ` and ? supported on Raw SQL even if postgres; Added Cols, StoreEngine, Charset function, Added many column data type supported, please see [Mapping Rules](#mapping). +* **v0.1.8** : Added union index and union unique supported, please see [Mapping Rules](#mapping). +* **v0.1.7** : Added IConnectPool interface and NoneConnectPool, SysConnectPool, SimpleConnectPool the three implements. You can choose one of them and the default is SysConnectPool. You can customrize your own connection pool. struct Engine added Close method, It should be invoked before system exit. +* **v0.1.6** : Added conversion interface support; added struct derive support; added single mapping support +* **v0.1.5** : Added multi threads support; added Sql() function for struct query; Get function changed return inteface; MakeSession and Create are instead with NewSession and NewEngine. +* **v0.1.4** : Added simple cascade load support; added more data type supports. +* **v0.1.3** : Find function now supports both slice and map; Add Table function for multi tables and temperory tables support +* **v0.1.2** : Insert function now supports both struct and slice pointer parameters, batch inserting and auto transaction +* **v0.1.1** : Add Id, In functions and improved README * **v0.1.0** : Inital release. \ No newline at end of file diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index 3786eba9..fef0bca7 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -15,10 +15,10 @@ * **v0.2.3** : 改善了文档;提供了乐观锁支持;添加了带时区时间字段支持;Mapper现在分成表名Mapper和字段名Mapper,同时实现了表或字段的自定义前缀后缀;Insert方法的返回值含义从id, err更改为 affected, err,请大家注意;添加了UseBool 和 Distinct函数。 * **v0.2.2** : Postgres驱动新增了对lib/pq的支持;新增了逐条遍历方法Iterate;新增了SetMaxConns(go1.2+)支持,修复了bug若干; -* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/lunny/xorm/blob/master/xorm/README.md); 修复了一些bug. -* **v0.2.0** : 新增 [缓存](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构; -* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。 -* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md#21)。 +* **v0.2.1** : 新增数据库反转工具,当前支持go和c++代码的生成,详见 [Xorm Tool README](https://github.com/go-xorm/xorm/blob/master/xorm/README.md); 修复了一些bug. +* **v0.2.0** : 新增 [缓存](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#120)支持,查询速度提升3-5倍; 新增数据库表和Struct同名的映射方式; 新增Sync同步表结构; +* **v0.1.9** : 新增 postgres 和 mymysql 驱动支持; 在Postgres中支持原始SQL语句中使用 ` 和 ? 符号; 新增Cols, StoreEngine, Charset 函数;SQL语句打印支持io.Writer接口,默认打印到控制台;新增更多的字段类型支持,详见 [映射规则](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#21);删除废弃的MakeSession和Create函数。 +* **v0.1.8** : 新增联合index,联合unique支持,请查看 [映射规则](https://github.com/go-xorm/xorm/blob/master/docs/QuickStart.md#21)。 * **v0.1.7** : 新增IConnectPool接口以及NoneConnectPool, SysConnectPool, SimpleConnectPool三种实现,可以选择不使用连接池,使用系统连接池和使用自带连接池三种实现,默认为SysConnectPool,即系统自带的连接池。同时支持自定义连接池。Engine新增Close方法,在系统退出时应调用此方法。 * **v0.1.6** : 新增Conversion,支持自定义类型到数据库类型的转换;新增查询结构体自动检测匿名成员支持;新增单向映射支持; * **v0.1.5** : 新增对多线程的支持;新增Sql()函数;支持任意sql语句的struct查询;Get函数返回值变动;MakeSession和Create函数被NewSession和NewEngine函数替代; diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 12443a5f..2fc373a7 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -45,7 +45,7 @@ xorm 快速入门 ```Go import ( _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" ) engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8") defer engine.Close() @@ -56,7 +56,7 @@ or ```Go import ( _ "github.com/mattn/go-sqlite3" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" ) engine, err = xorm.NewEngine("sqlite3", "./test.db") defer engine.Close() @@ -125,9 +125,9 @@ engine.SetMapper(SameMapper{}) 同时需要注意的是: <<<<<<< HEAD -* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 +* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: - + ======= * 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: @@ -169,7 +169,7 @@ type User struct { } ``` -对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。 +对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。 具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: @@ -181,7 +181,7 @@ type User struct { pk是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。 - 当前支持30多种字段类型,详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)字段类型 + 当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型 autoincr是否是自增 @@ -227,7 +227,7 @@ type User struct { - 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义 -- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/lunny/xorm/blob/master/docs/AutoMap.md) +- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) - 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换。 ```Go @@ -242,7 +242,7 @@ type Conversion interface { 如果不使用tag来定义field对应的数据库字段类型,那么系统会自动给出一个默认的字段类型,对应表如下: -[go类型<->数据库类型对应表](https://github.com/lunny/xorm/blob/master/docs/AutoMap.md) +[go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) ## 3.表结构操作 @@ -755,7 +755,7 @@ engine.ClearCache(new(User)) 缓存的实现原理如下图所示: -![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) +![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png) ## 12.事件 @@ -788,12 +788,12 @@ xorm支持两种方式的事件,一种是在Struct中的特定方法来作为 xorm工具提供了xorm命令,能够帮助做很多事情。 ### 13.1.反转命令 -参见 [xorm工具](https://github.com/lunny/xorm/tree/master/xorm) +参见 [xorm工具](https://github.com/go-xorm/xorm/tree/master/xorm) ## 14.Examples -请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples) +请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples) ## 15.案例 diff --git a/docs/QuickStartEn.md b/docs/QuickStartEn.md index 7cc08e2e..a8dce7cc 100644 --- a/docs/QuickStartEn.md +++ b/docs/QuickStartEn.md @@ -41,8 +41,8 @@ When using xorm, you can create multiple orm engines, an engine means a databse. ```Go import ( _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" -) + "github.com/go-xorm/xorm" +) engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8") defer engine.Close() ``` @@ -52,18 +52,18 @@ or ```Go import ( _ "github.com/mattn/go-sqlite3" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" ) engine, err = xorm.NewEngine("sqlite3", "./test.db") -defer engine.Close() +defer engine.Close() ``` Generally, you can only create one engine. Engine supports run on go rutines. xorm supports four drivers now: -* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) - +* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) + * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) @@ -95,7 +95,7 @@ engine.Logger = f 3.Engine support connection pool. The default pool is database/sql's and also you can use custom pool. Xorm provides two connection pool `xorm.NonConnectionPool` & `xorm.SimpleConnectPool`. If you want to use yourself pool, you can use `engine.SetPool` to set it. * Use `engine.SetIdleConns()` to set idle connections. -* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+. +* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+. ## 2.Define struct @@ -150,7 +150,7 @@ type User struct { } ``` -对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)。 +对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。 具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: @@ -162,7 +162,7 @@ type User struct { pk是否是Primary Key,当前仅支持int64类型 - 当前支持30多种字段类型,详情参见 [字段类型](https://github.com/lunny/xorm/blob/master/docs/COLUMNTYPE.md)字段类型 + 当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型 autoincr是否是自增 @@ -216,7 +216,7 @@ type Conversion interface { FromDB([]byte) error ToDB() ([]byte, error) } -``` +``` ## 3.表结构操作 @@ -264,7 +264,7 @@ xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到 调用方法如下: ```Go err := engine.Sync(new(User)) -``` +``` ## 4.插入数据 @@ -425,59 +425,59 @@ Having的参数字符串 当从一个struct来生成查询条件或更新字段时,xorm会判断struct的field是否为0,"",nil,如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值,因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法,如果默认不传参数,则所有的bool字段都将会被使用,如果参数不为空,则参数中指定的为字段名,则这些字段对应的bool值将被使用。 * Cascade(bool) -是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。 +是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。 -### 5.3.Get one record -Fetch a single object by user - -```Go -var user = User{Id:27} -has, err := engine.Get(&user) -// or has, err := engine.Id(27).Get(&user) - -var user = User{Name:"xlw"} -has, err := engine.Get(&user) -``` +### 5.3.Get one record +Fetch a single object by user + +```Go +var user = User{Id:27} +has, err := engine.Get(&user) +// or has, err := engine.Id(27).Get(&user) + +var user = User{Name:"xlw"} +has, err := engine.Get(&user) +``` -### 5.4.Find -Fetch multipe objects into a slice or a map, use Find: - -```Go -var everyone []Userinfo -err := engine.Find(&everyone) - -users := make(map[int64]Userinfo) -err := engine.Find(&users) -``` - -* also you can use Where, Limit - -```Go -var allusers []Userinfo -err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20 -``` - -* or you can use a struct query - -```Go -var tenusers []Userinfo -err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0 -``` - -* or In function - -```Go -var tenusers []Userinfo -err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5) -``` - -* The default will query all columns of a table. Use Cols function if you want to select some columns - -```Go -var tenusers []Userinfo -err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name +### 5.4.Find +Fetch multipe objects into a slice or a map, use Find: + +```Go +var everyone []Userinfo +err := engine.Find(&everyone) + +users := make(map[int64]Userinfo) +err := engine.Find(&users) +``` + +* also you can use Where, Limit + +```Go +var allusers []Userinfo +err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20 +``` + +* or you can use a struct query + +```Go +var tenusers []Userinfo +err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0 +``` + +* or In function + +```Go +var tenusers []Userinfo +err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5) +``` + +* The default will query all columns of a table. Use Cols function if you want to select some columns + +```Go +var tenusers []Userinfo +err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name ``` @@ -542,44 +542,44 @@ engine.Id(1).Get(&user) engine.Id(1).Update(&user) // UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ? ``` - + -## 7.Delete one or more records -Delete one or more records +## 7.Delete one or more records +Delete one or more records * delete by id - -```Go -err := engine.Id(1).Delete(&User{}) + +```Go +err := engine.Id(1).Delete(&User{}) ``` - + * delete by other conditions ```Go err := engine.Delete(&User{Name:"xlw"}) -``` +``` - -## 8.Execute SQL query - -Of course, SQL execution is also provided. - -If select then use Query - -```Go -sql := "select * from userinfo" -results, err := engine.Query(sql) -``` + +## 8.Execute SQL query - -## 9.Execute SQL command -If insert, update or delete then use Exec - -```Go -sql = "update userinfo set username=? where id=?" -res, err := engine.Exec(sql, "xiaolun", 1) -``` +Of course, SQL execution is also provided. + +If select then use Query + +```Go +sql := "select * from userinfo" +results, err := engine.Query(sql) +``` + + +## 9.Execute SQL command +If insert, update or delete then use Exec + +```Go +sql = "update userinfo set username=? where id=?" +res, err := engine.Exec(sql, "xiaolun", 1) +``` ## 10.Transaction @@ -613,8 +613,8 @@ if err != nil { err = session.Commit() if err != nil { return -} -``` +} +``` ## 11.缓存 @@ -654,19 +654,19 @@ engine.ClearCache(new(User)) Cache implement theory below: -![cache design](https://raw.github.com/lunny/xorm/master/docs/cache_design.png) +![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png) ## 12.xorm tool xorm工具提供了xorm命令,能够帮助做很多事情。 ### 12.1.Reverse command -Please visit [xorm tool](https://github.com/lunny/xorm/tree/master/xorm) +Please visit [xorm tool](https://github.com/go-xorm/xorm/tree/master/xorm) ## 13.Examples -请访问[https://github.com/lunny/xorm/tree/master/examples](https://github.com/lunny/xorm/tree/master/examples) +请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples) ## 14.Cases @@ -680,14 +680,14 @@ Please visit [xorm tool](https://github.com/lunny/xorm/tree/master/xorm) * [VeryHour](http://veryhour.com) -## 15.FAQ - -1.How the xorm tag use both with json? - - Use space. - -```Go -type User struct { - Name string `json:"name" xorm:"name"` -} +## 15.FAQ + +1.How the xorm tag use both with json? + + Use space. + +```Go +type User struct { + Name string `json:"name" xorm:"name"` +} ``` diff --git a/examples/cache.go b/examples/cache.go index 8fd8f4a8..c5d50273 100644 --- a/examples/cache.go +++ b/examples/cache.go @@ -4,8 +4,8 @@ import ( "fmt" "os" - "github.com/lunny/xorm" - "github.com/lunny/xorm/caches" + "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm/caches" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/cachegoroutine.go b/examples/cachegoroutine.go index 117490d6..925a16ca 100644 --- a/examples/cachegoroutine.go +++ b/examples/cachegoroutine.go @@ -3,7 +3,7 @@ package main import ( "fmt" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" "os" ) diff --git a/examples/conversion.go b/examples/conversion.go index f16869c4..1a74dea8 100644 --- a/examples/conversion.go +++ b/examples/conversion.go @@ -5,7 +5,7 @@ import ( "fmt" "os" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/derive.go b/examples/derive.go index 826b4271..2a340060 100644 --- a/examples/derive.go +++ b/examples/derive.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/goroutine.go b/examples/goroutine.go index 6d45c924..b18fe4f8 100644 --- a/examples/goroutine.go +++ b/examples/goroutine.go @@ -6,7 +6,7 @@ import ( "runtime" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/maxconnect.go b/examples/maxconnect.go index e383de13..8ec82d2a 100644 --- a/examples/maxconnect.go +++ b/examples/maxconnect.go @@ -6,7 +6,7 @@ import ( "runtime" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/pool.go b/examples/pool.go index 746cdf8d..7511bb6c 100644 --- a/examples/pool.go +++ b/examples/pool.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/singlemapping.go b/examples/singlemapping.go index 6954d7ee..f2d675b8 100644 --- a/examples/singlemapping.go +++ b/examples/singlemapping.go @@ -4,7 +4,7 @@ import ( "fmt" "os" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/examples/sync.go b/examples/sync.go index 043bffa8..f26026aa 100644 --- a/examples/sync.go +++ b/examples/sync.go @@ -4,7 +4,7 @@ import ( "fmt" _ "github.com/bylevel/pq" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" ) diff --git a/xorm/.gopmfile b/xorm/.gopmfile index a77947be..6fcf08b9 100644 --- a/xorm/.gopmfile +++ b/xorm/.gopmfile @@ -1,2 +1,2 @@ [deps] -github.com/lunny/xorm=../ \ No newline at end of file +github.com/go-xorm/xorm=../ \ No newline at end of file diff --git a/xorm/README.md b/xorm/README.md index b0d39b86..f4dbc9e6 100644 --- a/xorm/README.md +++ b/xorm/README.md @@ -5,11 +5,11 @@ xorm tools is a set of tools for database operation. ## Install -`go get github.com/lunny/xorm/xorm` +`go get github.com/go-xorm/xorm/xorm` and you should install the depends below: -* github.com/lunny/xorm +* github.com/go-xorm/xorm * Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) diff --git a/xorm/c++.go b/xorm/c++.go index bbfdd3b1..f5a0907a 100644 --- a/xorm/c++.go +++ b/xorm/c++.go @@ -5,7 +5,7 @@ import ( "strings" "text/template" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" ) var ( diff --git a/xorm/go.go b/xorm/go.go index d599f074..382b5b18 100644 --- a/xorm/go.go +++ b/xorm/go.go @@ -8,7 +8,7 @@ import ( "strings" "text/template" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" ) var ( diff --git a/xorm/lang.go b/xorm/lang.go index a4785647..3af43feb 100644 --- a/xorm/lang.go +++ b/xorm/lang.go @@ -1,10 +1,10 @@ package main import ( + "github.com/go-xorm/core" "io/ioutil" "strings" "text/template" - "github.com/lunny/xorm/core" ) type LangTmpl struct { diff --git a/xorm/reverse.go b/xorm/reverse.go index 5bd5f33b..1db8f115 100644 --- a/xorm/reverse.go +++ b/xorm/reverse.go @@ -14,8 +14,8 @@ import ( _ "github.com/bylevel/pq" "github.com/dvirsky/go-pylog/logging" _ "github.com/go-sql-driver/mysql" - "github.com/lunny/xorm" - "github.com/lunny/xorm/core" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" _ "github.com/ziutek/mymysql/godrv" diff --git a/xorm/shell.go b/xorm/shell.go index 6fd0db6a..5258e4c4 100644 --- a/xorm/shell.go +++ b/xorm/shell.go @@ -2,7 +2,7 @@ package main import ( "fmt" - "github.com/lunny/xorm" + "github.com/go-xorm/xorm" "strings" ) From f30dadfd0220b7724644599cb5373564bf8bc655 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 11 Apr 2014 17:22:21 +0800 Subject: [PATCH 25/55] move tests to a sereprated repository --- tests/base_test.go | 4185 ---------------------------------- tests/benchmark.bat | 1 - tests/benchmark.sh | 1 - tests/benchmark_base_test.go | 176 -- tests/mssql_test.go | 144 -- tests/mymysql_test.go | 169 -- tests/mysql_test.go | 222 -- tests/postgres_test.go | 202 -- tests/sqlite3_test.go | 183 -- tests/testdata/mysql_ddl.sql | 8 - 10 files changed, 5291 deletions(-) delete mode 100644 tests/base_test.go delete mode 100644 tests/benchmark.bat delete mode 100755 tests/benchmark.sh delete mode 100644 tests/benchmark_base_test.go delete mode 100644 tests/mssql_test.go delete mode 100644 tests/mymysql_test.go delete mode 100644 tests/mysql_test.go delete mode 100644 tests/postgres_test.go delete mode 100644 tests/sqlite3_test.go delete mode 100644 tests/testdata/mysql_ddl.sql diff --git a/tests/base_test.go b/tests/base_test.go deleted file mode 100644 index 4f5704a8..00000000 --- a/tests/base_test.go +++ /dev/null @@ -1,4185 +0,0 @@ -package tests - -import ( - "errors" - "fmt" - "strings" - "testing" - "time" - - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" -) - -/* -CREATE TABLE `userinfo` ( - `id` INT(10) NULL AUTO_INCREMENT, - `username` VARCHAR(64) NULL, - `departname` VARCHAR(64) NULL, - `created` DATE NULL, - PRIMARY KEY (`uid`) -); -CREATE TABLE `userdeatail` ( - `id` INT(10) NULL, - `intro` TEXT NULL, - `profile` TEXT NULL, - PRIMARY KEY (`uid`) -); -*/ - -type Userinfo struct { - Uid int64 `xorm:"id pk not null autoincr"` - Username string `xorm:"unique"` - Departname string - Alias string `xorm:"-"` - Created time.Time - Detail Userdetail `xorm:"detail_id int(11)"` - Height float64 - Avatar []byte - IsMan bool -} - -type Userdetail struct { - Id int64 - Intro string `xorm:"text"` - Profile string `xorm:"varchar(2000)"` -} - -func directCreateTable(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&Userinfo{}, &Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.Sync(&Userinfo{}, &Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.DropTables(&Userinfo{}, &Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&Userinfo{}, &Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateIndexes(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateIndexes(&Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateUniques(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateUniques(&Userdetail{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -func insert(engine *xorm.Engine, t *testing.T) { - user := Userinfo{0, "xiaolunwen", "dev", "lunny", time.Now(), - Userdetail{Id: 1}, 1.78, []byte{1, 2, 3}, true} - cnt, err := engine.Insert(&user) - fmt.Println(user.Uid) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - if user.Uid <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } - - user.Uid = 0 - cnt, err = engine.Insert(&user) - if err == nil { - err = errors.New("insert failed but no return error") - t.Error(err) - panic(err) - } - if cnt != 0 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } -} - -func testQuery(engine *xorm.Engine, t *testing.T) { - sql := "select * from userinfo" - results, err := engine.Query(sql) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(results) -} - -func exec(engine *xorm.Engine, t *testing.T) { - sql := "update userinfo set username=? where id=?" - res, err := engine.Exec(sql, "xiaolun", 1) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(res) -} - -func testQuerySameMapper(engine *xorm.Engine, t *testing.T) { - sql := "select * from `Userinfo`" - results, err := engine.Query(sql) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(results) -} - -func execSameMapper(engine *xorm.Engine, t *testing.T) { - sql := "update `Userinfo` set `Username`=? where (id)=?" - res, err := engine.Exec(sql, "xiaolun", 1) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(res) -} - -func insertAutoIncr(engine *xorm.Engine, t *testing.T) { - // auto increment insert - user := Userinfo{Username: "xiaolunwen2", Departname: "dev", Alias: "lunny", Created: time.Now(), - Detail: Userdetail{Id: 1}, Height: 1.78, Avatar: []byte{1, 2, 3}, IsMan: true} - cnt, err := engine.Insert(&user) - fmt.Println(user.Uid) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - if user.Uid <= 0 { - t.Error(errors.New("not return id error")) - } -} - -type BigInsert struct { -} - -func insertDefault(engine *xorm.Engine, t *testing.T) { - -} - -func insertMulti(engine *xorm.Engine, t *testing.T) { - //engine.InsertMany = true - users := []Userinfo{ - {Username: "xlw", Departname: "dev", Alias: "lunny2", Created: time.Now()}, - {Username: "xlw2", Departname: "dev", Alias: "lunny3", Created: time.Now()}, - {Username: "xlw11", Departname: "dev", Alias: "lunny2", Created: time.Now()}, - {Username: "xlw22", Departname: "dev", Alias: "lunny3", Created: time.Now()}, - } - cnt, err := engine.Insert(&users) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != int64(len(users)) { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - - users2 := []*Userinfo{ - &Userinfo{Username: "1xlw", Departname: "dev", Alias: "lunny2", Created: time.Now()}, - &Userinfo{Username: "1xlw2", Departname: "dev", Alias: "lunny3", Created: time.Now()}, - &Userinfo{Username: "1xlw11", Departname: "dev", Alias: "lunny2", Created: time.Now()}, - &Userinfo{Username: "1xlw22", Departname: "dev", Alias: "lunny3", Created: time.Now()}, - } - - cnt, err = engine.Insert(&users2) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != int64(len(users2)) { - err = errors.New(fmt.Sprintf("insert not returned %v", len(users2))) - t.Error(err) - panic(err) - return - } -} - -func insertTwoTable(engine *xorm.Engine, t *testing.T) { - userdetail := Userdetail{ /*Id: 1, */ Intro: "I'm a very beautiful women.", Profile: "sfsaf"} - userinfo := Userinfo{Username: "xlw3", Departname: "dev", Alias: "lunny4", Created: time.Now(), Detail: userdetail} - - cnt, err := engine.Insert(&userinfo, &userdetail) - if err != nil { - t.Error(err) - panic(err) - } - - if userinfo.Uid <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } - - if userdetail.Id <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } - - if cnt != 2 { - err = errors.New("insert not returned 2") - t.Error(err) - panic(err) - return - } -} - -type Article struct { - Id int32 `xorm:"pk INT autoincr"` - Name string `xorm:"VARCHAR(45)"` - Img string `xorm:"VARCHAR(100)"` - Aside string `xorm:"VARCHAR(200)"` - Desc string `xorm:"VARCHAR(200)"` - Content string `xorm:"TEXT"` - Status int8 `xorm:"TINYINT(4)"` -} - -type Condi map[string]interface{} - -func update(engine *xorm.Engine, t *testing.T) { - // update by id - user := Userinfo{Username: "xxx", Height: 1.2} - cnt, err := engine.Id(4).Update(&user) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } - - condi := Condi{"username": "zzz", "departname": ""} - cnt, err = engine.Table(&user).Id(4).Update(&condi) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } - - cnt, err = engine.Update(&Userinfo{Username: "yyy"}, &user) - if err != nil { - t.Error(err) - panic(err) - } - total, err := engine.Count(&user) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != total { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - - err = engine.Sync(&Article{}) - if err != nil { - t.Error(err) - panic(err) - } - - defer func() { - err = engine.DropTables(&Article{}) - if err != nil { - t.Error(err) - panic(err) - } - }() - - a := &Article{0, "1", "2", "3", "4", "5", 2} - cnt, err = engine.Insert(a) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New(fmt.Sprintf("insert not returned 1 but %d", cnt)) - t.Error(err) - panic(err) - } - - if a.Id == 0 { - err = errors.New("insert returned id is 0") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(a.Id).Update(&Article{Name: "6"}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New(fmt.Sprintf("insert not returned 1 but %d", cnt)) - t.Error(err) - panic(err) - return - } - - type UpdateAllCols struct { - Id int64 - Bool bool - String string - } - - col1 := &UpdateAllCols{} - err = engine.Sync(col1) - if err != nil { - t.Error(err) - panic(err) - } - - _, err = engine.Insert(col1) - if err != nil { - t.Error(err) - panic(err) - } - - col2 := &UpdateAllCols{col1.Id, true, ""} - _, err = engine.Id(col2.Id).AllCols().Update(col2) - if err != nil { - t.Error(err) - panic(err) - } - - col3 := &UpdateAllCols{} - has, err := engine.Id(col2.Id).Get(col3) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - err = errors.New(fmt.Sprintf("cannot get id %d", col2.Id)) - t.Error(err) - panic(err) - return - } - - if *col2 != *col3 { - err = errors.New(fmt.Sprintf("col2 should eq col3")) - t.Error(err) - panic(err) - return - } - - { - type UpdateMustCols struct { - Id int64 - Bool bool - String string - } - - col1 := &UpdateMustCols{} - err = engine.Sync(col1) - if err != nil { - t.Error(err) - panic(err) - } - - _, err = engine.Insert(col1) - if err != nil { - t.Error(err) - panic(err) - } - - col2 := &UpdateMustCols{col1.Id, true, ""} - boolStr := engine.ColumnMapper.Obj2Table("Bool") - stringStr := engine.ColumnMapper.Obj2Table("String") - _, err = engine.Id(col2.Id).MustCols(boolStr, stringStr).Update(col2) - if err != nil { - t.Error(err) - panic(err) - } - - col3 := &UpdateMustCols{} - has, err := engine.Id(col2.Id).Get(col3) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - err = errors.New(fmt.Sprintf("cannot get id %d", col2.Id)) - t.Error(err) - panic(err) - return - } - - if *col2 != *col3 { - err = errors.New(fmt.Sprintf("col2 should eq col3")) - t.Error(err) - panic(err) - return - } - } -} - -func updateSameMapper(engine *xorm.Engine, t *testing.T) { - // update by id - user := Userinfo{Username: "xxx", Height: 1.2} - cnt, err := engine.Id(4).Update(&user) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } - - condi := Condi{"Username": "zzz", "Departname": ""} - cnt, err = engine.Table(&user).Id(4).Update(&condi) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } - - cnt, err = engine.Update(&Userinfo{Username: "yyy"}, &user) - if err != nil { - t.Error(err) - panic(err) - } - - total, err := engine.Count(&user) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != total { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } -} - -func testDelete(engine *xorm.Engine, t *testing.T) { - user := Userinfo{Uid: 1} - cnt, err := engine.Delete(&user) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("delete not returned 1") - t.Error(err) - panic(err) - return - } - - user.Uid = 0 - user.IsMan = true - has, err := engine.Id(3).Get(&user) - if err != nil { - t.Error(err) - panic(err) - } - - if has { - //var tt time.Time - //user.Created = tt - cnt, err := engine.UseBool().Delete(&user) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - t.Error(errors.New("delete failed")) - panic(err) - } - } -} - -type NoIdUser struct { - User string `xorm:"unique"` - Remain int64 - Total int64 -} - -func get(engine *xorm.Engine, t *testing.T) { - user := Userinfo{Uid: 2} - - has, err := engine.Get(&user) - if err != nil { - t.Error(err) - panic(err) - } - if has { - fmt.Println(user) - } else { - fmt.Println("no record id is 2") - } - - err = engine.Sync(&NoIdUser{}) - if err != nil { - t.Error(err) - panic(err) - } - - userCol := engine.ColumnMapper.Obj2Table("User") - - _, err = engine.Where("`"+userCol+"` = ?", "xlw").Delete(&NoIdUser{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&NoIdUser{"xlw", 20, 100}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - } - - noIdUser := new(NoIdUser) - has, err = engine.Where("`"+userCol+"` = ?", "xlw").Get(noIdUser) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - err = errors.New("get not returned 1") - t.Error(err) - panic(err) - } - fmt.Println(noIdUser) -} - -func cascadeGet(engine *xorm.Engine, t *testing.T) { - user := Userinfo{Uid: 11} - - has, err := engine.Get(&user) - if err != nil { - t.Error(err) - panic(err) - } - if has { - fmt.Println(user) - } else { - fmt.Println("no record id is 2") - } -} - -func find(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - - err := engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for _, user := range users { - fmt.Println(user) - } - - users2 := make([]Userinfo, 0) - userinfo := engine.TableMapper.Obj2Table("Userinfo") - err = engine.Sql("select * from " + engine.Quote(userinfo)).Find(&users2) - if err != nil { - t.Error(err) - panic(err) - } -} - -func find2(engine *xorm.Engine, t *testing.T) { - users := make([]*Userinfo, 0) - - err := engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for _, user := range users { - fmt.Println(user) - } -} - -func findMap(engine *xorm.Engine, t *testing.T) { - users := make(map[int64]Userinfo) - - err := engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for _, user := range users { - fmt.Println(user) - } -} - -func findMap2(engine *xorm.Engine, t *testing.T) { - users := make(map[int64]*Userinfo) - - err := engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for id, user := range users { - fmt.Println(id, user) - } -} - -func count(engine *xorm.Engine, t *testing.T) { - user := Userinfo{Departname: "dev"} - total, err := engine.Count(&user) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Printf("Total %d records!!!\n", total) -} - -func where(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.Where("(id) > ?", 2).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - - err = engine.Where("(id) > ?", 2).And("(id) < ?", 10).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) -} - -func in(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.In("(id)", 7, 8, 9).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - if len(users) != 3 { - err = errors.New("in uses should be 7,8,9 total 3") - t.Error(err) - panic(err) - } - - for _, user := range users { - if user.Uid != 7 && user.Uid != 8 && user.Uid != 9 { - err = errors.New("in uses should be 7,8,9 total 3") - t.Error(err) - panic(err) - } - } - - users = make([]Userinfo, 0) - ids := []interface{}{7, 8, 9} - department := engine.ColumnMapper.Obj2Table("Departname") - err = engine.Where("`"+department+"` = ?", "dev").In("(id)", ids...).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - - if len(users) != 3 { - err = errors.New("in uses should be 7,8,9 total 3") - t.Error(err) - panic(err) - } - - for _, user := range users { - if user.Uid != 7 && user.Uid != 8 && user.Uid != 9 { - err = errors.New("in uses should be 7,8,9 total 3") - t.Error(err) - panic(err) - } - } - - dev := engine.ColumnMapper.Obj2Table("Dev") - - err = engine.In("(id)", 1).In("(id)", 2).In(department, dev).Find(&users) - - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - - cnt, err := engine.In("(id)", 4).Update(&Userinfo{Departname: "dev-"}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update records not 1") - t.Error(err) - panic(err) - } - - user := new(Userinfo) - has, err := engine.Id(4).Get(user) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get record not 1") - t.Error(err) - panic(err) - } - if user.Departname != "dev-" { - err = errors.New("update not success") - t.Error(err) - panic(err) - } - - cnt, err = engine.In("(id)", 4).Update(&Userinfo{Departname: "dev"}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update records not 1") - t.Error(err) - panic(err) - } - - cnt, err = engine.In("(id)", 5).Delete(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("deleted records not 1") - t.Error(err) - panic(err) - } -} - -func limit(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.Limit(2, 1).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) -} - -func order(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.OrderBy("id desc").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - - users2 := make([]Userinfo, 0) - err = engine.Asc("id", "username").Desc("height").Find(&users2) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users2) -} - -func join(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.Join("LEFT", "userdetail", "userinfo.id=userdetail.id").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } -} - -func having(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.GroupBy("username").Having("username='xlw'").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) -} - -func orderSameMapper(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.OrderBy("(id) desc").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) - - users2 := make([]Userinfo, 0) - err = engine.Asc("(id)", "Username").Desc("Height").Find(&users2) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users2) -} - -func joinSameMapper(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.Join("LEFT", "`Userdetail`", "`Userinfo`.`(id)`=`Userdetail`.`Id`").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } -} - -func havingSameMapper(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - err := engine.GroupBy("Username").Having(`"Username"='xlw'`).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(users) -} - -func transaction(engine *xorm.Engine, t *testing.T) { - counter := func() { - total, err := engine.Count(&Userinfo{}) - if err != nil { - t.Error(err) - } - fmt.Printf("----now total %v records\n", total) - } - - counter() - defer counter() - session := engine.NewSession() - defer session.Close() - - err := session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - //session.IsAutoRollback = false - user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} - _, err = session.Insert(&user1) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - user2 := Userinfo{Username: "yyy"} - _, err = session.Where("(id) = ?", 0).Update(&user2) - if err != nil { - session.Rollback() - fmt.Println(err) - //t.Error(err) - return - } - - _, err = session.Delete(&user2) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } - // panic(err) !nashtsai! should remove this -} - -func combineTransaction(engine *xorm.Engine, t *testing.T) { - counter := func() { - total, err := engine.Count(&Userinfo{}) - if err != nil { - t.Error(err) - } - fmt.Printf("----now total %v records\n", total) - } - - counter() - defer counter() - session := engine.NewSession() - defer session.Close() - - err := session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()} - _, err = session.Insert(&user1) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - user2 := Userinfo{Username: "zzz"} - _, err = session.Where("id = ?", 0).Update(&user2) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - _, err = session.Exec("delete from userinfo where username = ?", user2.Username) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } -} - -func combineTransactionSameMapper(engine *xorm.Engine, t *testing.T) { - counter := func() { - total, err := engine.Count(&Userinfo{}) - if err != nil { - t.Error(err) - } - fmt.Printf("----now total %v records\n", total) - } - - counter() - defer counter() - session := engine.NewSession() - defer session.Close() - - err := session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - //session.IsAutoRollback = false - user1 := Userinfo{Username: "xiaoxiao2", Departname: "dev", Alias: "lunny", Created: time.Now()} - _, err = session.Insert(&user1) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - user2 := Userinfo{Username: "zzz"} - _, err = session.Where("(id) = ?", 0).Update(&user2) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - _, err = session.Exec("delete from `Userinfo` where `Username` = ?", user2.Username) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } -} - -func table(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables("user_user") - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.Table("user_user").CreateTable(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -func createMultiTables(engine *xorm.Engine, t *testing.T) { - session := engine.NewSession() - defer session.Close() - - user := &Userinfo{} - err := session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - for i := 0; i < 10; i++ { - tableName := fmt.Sprintf("user_%v", i) - - err = session.DropTable(tableName) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - - err = session.Table(tableName).CreateTable(user) - if err != nil { - session.Rollback() - t.Error(err) - panic(err) - } - } - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } -} - -func tableOp(engine *xorm.Engine, t *testing.T) { - user := Userinfo{Username: "tablexiao", Departname: "dev", Alias: "lunny", Created: time.Now()} - tableName := fmt.Sprintf("user_%v", len(user.Username)) - cnt, err := engine.Table(tableName).Insert(&user) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - - has, err := engine.Table(tableName).Get(&Userinfo{Username: "tablexiao"}) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("Get has return false") - t.Error(err) - panic(err) - return - } - - users := make([]Userinfo, 0) - err = engine.Table(tableName).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - - id := user.Uid - cnt, err = engine.Table(tableName).Id(id).Update(&Userinfo{Username: "tableda"}) - if err != nil { - t.Error(err) - panic(err) - } - - _, err = engine.Table(tableName).Id(id).Delete(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -func testCharst(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables("user_charset") - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.Charset("utf8").Table("user_charset").CreateTable(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -func testStoreEngine(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables("user_store_engine") - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.StoreEngine("InnoDB").Table("user_store_engine").CreateTable(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -type tempUser struct { - Id int64 - Username string -} - -func testCols(engine *xorm.Engine, t *testing.T) { - users := []Userinfo{} - err := engine.Cols("id, username").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - - fmt.Println(users) - - tmpUsers := []tempUser{} - err = engine.NoCache().Table("userinfo").Cols("id, username").Find(&tmpUsers) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(tmpUsers) - - user := &Userinfo{Uid: 1, Alias: "", Height: 0} - affected, err := engine.Cols("departname, height").Id(1).Update(user) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println("===================", user, affected) -} - -func testColsSameMapper(engine *xorm.Engine, t *testing.T) { - users := []Userinfo{} - err := engine.Cols("id, Username").Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - - fmt.Println(users) - - tmpUsers := []tempUser{} - // TODO: should use cache - err = engine.NoCache().Table("Userinfo").Cols("id, Username").Find(&tmpUsers) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(tmpUsers) - - user := &Userinfo{Uid: 1, Alias: "", Height: 0} - affected, err := engine.Cols("Departname, Height").Update(user) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println("===================", user, affected) -} - -type tempUser2 struct { - tempUser `xorm:"extends"` - Departname string -} - -func testExtends(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&tempUser2{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&tempUser2{}) - if err != nil { - t.Error(err) - panic(err) - } - - tu := &tempUser2{tempUser{0, "extends"}, "dev depart"} - _, err = engine.Insert(tu) - if err != nil { - t.Error(err) - panic(err) - } - - tu2 := &tempUser2{} - _, err = engine.Get(tu2) - if err != nil { - t.Error(err) - panic(err) - } - - tu3 := &tempUser2{tempUser{0, "extends update"}, ""} - _, err = engine.Id(tu2.Id).Update(tu3) - if err != nil { - t.Error(err) - panic(err) - } -} - -type allCols struct { - Bit int `xorm:"BIT"` - TinyInt int8 `xorm:"TINYINT"` - SmallInt int16 `xorm:"SMALLINT"` - MediumInt int32 `xorm:"MEDIUMINT"` - Int int `xorm:"INT"` - Integer int `xorm:"INTEGER"` - BigInt int64 `xorm:"BIGINT"` - - Char string `xorm:"CHAR(12)"` - Varchar string `xorm:"VARCHAR(54)"` - TinyText string `xorm:"TINYTEXT"` - Text string `xorm:"TEXT"` - MediumText string `xorm:"MEDIUMTEXT"` - LongText string `xorm:"LONGTEXT"` - Binary []byte `xorm:"BINARY(23)"` - VarBinary []byte `xorm:"VARBINARY(12)"` - - Date time.Time `xorm:"DATE"` - DateTime time.Time `xorm:"DATETIME"` - Time time.Time `xorm:"TIME"` - TimeStamp time.Time `xorm:"TIMESTAMP"` - - Decimal float64 `xorm:"DECIMAL"` - Numeric float64 `xorm:"NUMERIC"` - - Real float32 `xorm:"REAL"` - Float float32 `xorm:"FLOAT"` - Double float64 `xorm:"DOUBLE"` - - TinyBlob []byte `xorm:"TINYBLOB"` - Blob []byte `xorm:"BLOB"` - MediumBlob []byte `xorm:"MEDIUMBLOB"` - LongBlob []byte `xorm:"LONGBLOB"` - Bytea []byte `xorm:"BYTEA"` - - Bool bool `xorm:"BOOL"` - - Serial int `xorm:"SERIAL"` - //BigSerial int64 `xorm:"BIGSERIAL"` -} - -func testColTypes(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&allCols{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&allCols{}) - if err != nil { - t.Error(err) - panic(err) - } - - ac := &allCols{ - 1, - 4, - 8, - 16, - 32, - 64, - 128, - - "123", - "fafdafa", - "fafafafdsafdsafdaf", - "fdsafafdsafdsaf", - "fafdsafdsafdsfadasfsfafd", - "fadfdsafdsafasfdasfds", - []byte("fdafsafdasfdsafsa"), - []byte("fdsafsdafs"), - - time.Now(), - time.Now(), - time.Now(), - time.Now(), - - 1.34, - 2.44302346, - - 1.3344, - 2.59693523, - 3.2342523543, - - []byte("fafdasf"), - []byte("fafdfdsafdsafasf"), - []byte("faffadsfdsdasf"), - []byte("faffdasfdsadasf"), - []byte("fafasdfsadffdasf"), - - true, - - 0, - //21, - } - - cnt, err := engine.Insert(ac) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert return not 1") - t.Error(err) - panic(err) - } - newAc := &allCols{} - has, err := engine.Get(newAc) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("error no ideas") - t.Error(err) - panic(err) - } - - // don't use this type as query condition - newAc.Real = 0 - newAc.Float = 0 - newAc.Double = 0 - newAc.LongText = "" - newAc.TinyText = "" - newAc.MediumText = "" - newAc.Text = "" - cnt, err = engine.Delete(newAc) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New(fmt.Sprintf("delete error, deleted counts is %v", cnt)) - t.Error(err) - panic(err) - } -} - -type MyInt int -type MyUInt uint -type MyFloat float64 -type MyString string - -/*func (s *MyString) FromDB(data []byte) error { - reflect. - s MyString(data) - return nil -} - -func (s *MyString) ToDB() ([]byte, error) { - return []byte(string(*s)), nil -}*/ - -type MyStruct struct { - Type MyInt - U MyUInt - F MyFloat - S MyString - IA []MyInt - UA []MyUInt - FA []MyFloat - SA []MyString - NameArray []string - Name string - UIA []uint - UIA8 []uint8 - UIA16 []uint16 - UIA32 []uint32 - UIA64 []uint64 - UI uint - //C64 complex64 - MSS map[string]string -} - -func testCustomType(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&MyStruct{}) - if err != nil { - t.Error(err) - panic(err) - return - } - - err = engine.CreateTables(&MyStruct{}) - i := MyStruct{Name: "Test", Type: MyInt(1)} - i.U = 23 - i.F = 1.34 - i.S = "fafdsafdsaf" - i.UI = 2 - i.IA = []MyInt{1, 3, 5} - i.UIA = []uint{1, 3} - i.UIA16 = []uint16{2} - i.UIA32 = []uint32{4, 5} - i.UIA64 = []uint64{6, 7, 9} - i.UIA8 = []uint8{1, 2, 3, 4} - i.NameArray = []string{"ssss", "fsdf", "lllll, ss"} - i.MSS = map[string]string{"s": "sfds,ss", "x": "lfjljsl"} - cnt, err := engine.Insert(&i) - if err != nil { - t.Error(err) - panic(err) - return - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - - fmt.Println(i) - i.NameArray = []string{} - i.MSS = map[string]string{} - i.F = 0 - has, err := engine.Get(&i) - if err != nil { - t.Error(err) - panic(err) - } else if !has { - t.Error(errors.New("should get one record")) - panic(err) - } - - ss := []MyStruct{} - err = engine.Find(&ss) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(ss) - - sss := MyStruct{} - has, err = engine.Get(&sss) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(sss) - - if has { - sss.NameArray = []string{} - sss.MSS = map[string]string{} - cnt, err := engine.Delete(&sss) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - t.Error(errors.New("delete error")) - panic(err) - } - } -} - -type UserCU struct { - Id int64 - Name string - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` -} - -func testCreatedAndUpdated(engine *xorm.Engine, t *testing.T) { - u := new(UserCU) - err := engine.DropTables(u) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(u) - if err != nil { - t.Error(err) - panic(err) - } - - u.Name = "sss" - cnt, err := engine.Insert(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - - u.Name = "xxx" - cnt, err = engine.Id(u.Id).Update(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("update not returned 1") - t.Error(err) - panic(err) - return - } - - u.Id = 0 - u.Created = time.Now().Add(-time.Hour * 24 * 365) - u.Updated = u.Created - fmt.Println(u) - cnt, err = engine.NoAutoTime().Insert(u) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } -} - -type IndexOrUnique struct { - Id int64 - Index int `xorm:"index"` - Unique int `xorm:"unique"` - Group1 int `xorm:"index(ttt)"` - Group2 int `xorm:"index(ttt)"` - UniGroup1 int `xorm:"unique(lll)"` - UniGroup2 int `xorm:"unique(lll)"` -} - -func testIndexAndUnique(engine *xorm.Engine, t *testing.T) { - err := engine.CreateTables(&IndexOrUnique{}) - if err != nil { - t.Error(err) - //panic(err) - } - - err = engine.DropTables(&IndexOrUnique{}) - if err != nil { - t.Error(err) - //panic(err) - } - - err = engine.CreateTables(&IndexOrUnique{}) - if err != nil { - t.Error(err) - //panic(err) - } - - err = engine.CreateIndexes(&IndexOrUnique{}) - if err != nil { - t.Error(err) - //panic(err) - } - - err = engine.CreateUniques(&IndexOrUnique{}) - if err != nil { - t.Error(err) - //panic(err) - } -} - -type IntId struct { - Id int `xorm:"pk autoincr"` - Name string -} - -type Int32Id struct { - Id int32 `xorm:"pk autoincr"` - Name string -} - -type UintId struct { - Id uint `xorm:"pk autoincr"` - Name string -} - -type Uint32Id struct { - Id uint32 `xorm:"pk autoincr"` - Name string -} - -type Uint64Id struct { - Id uint64 `xorm:"pk autoincr"` - Name string -} - -type StringPK struct { - Id string `xorm:"pk notnull"` - Name string -} - -func testIntId(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&IntId{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&IntId{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&IntId{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(IntId) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]IntId, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&IntId{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testInt32Id(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&Int32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&Int32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&Int32Id{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(Int32Id) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]Int32Id, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&Int32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testUintId(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&UintId{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&UintId{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&UintId{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(UintId) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]UintId, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&UintId{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testUint32Id(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&Uint32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&Uint32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&Uint32Id{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(Uint32Id) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]Uint32Id, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&Uint32Id{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testUint64Id(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&Uint64Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&Uint64Id{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&Uint64Id{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(Uint64Id) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]Uint64Id, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&Uint64Id{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testStringPK(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&StringPK{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&StringPK{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&StringPK{Name: "test"}) - if err != nil { - t.Error(err) - panic(err) - } - - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } - - bean := new(StringPK) - has, err := engine.Get(bean) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - beans := make([]StringPK, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - panic(err) - } - if len(beans) != 1 { - err = errors.New("get count should be one") - t.Error(err) - panic(err) - } - - cnt, err = engine.Id(bean.Id).Delete(&StringPK{}) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert count should be one") - t.Error(err) - panic(err) - } -} - -func testMetaInfo(engine *xorm.Engine, t *testing.T) { - tables, err := engine.DBMetas() - if err != nil { - t.Error(err) - panic(err) - } - - for _, table := range tables { - fmt.Println(table.Name) - //for _, col := range table.Columns() { - //TODO: engine.dialect show exported - //fmt.Println(col.String(engine.dia)) - //} - - for _, index := range table.Indexes { - fmt.Println(index.Name, index.Type, strings.Join(index.Cols, ",")) - } - } -} - -func testIterate(engine *xorm.Engine, t *testing.T) { - err := engine.Omit("is_man").Iterate(new(Userinfo), func(idx int, bean interface{}) error { - user := bean.(*Userinfo) - fmt.Println(idx, "--", user) - return nil - }) - - if err != nil { - t.Error(err) - panic(err) - } -} - -func testRows(engine *xorm.Engine, t *testing.T) { - rows, err := engine.Omit("is_man").Rows(new(Userinfo)) - if err != nil { - t.Error(err) - panic(err) - } - defer rows.Close() - - idx := 0 - user := new(Userinfo) - for rows.Next() { - err = rows.Scan(user) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(idx, "--", user) - idx++ - } -} - -type StrangeName struct { - Id_t int64 `xorm:"pk autoincr"` - Name string -} - -func testStrangeName(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(new(StrangeName)) - if err != nil { - t.Error(err) - } - - err = engine.CreateTables(new(StrangeName)) - if err != nil { - t.Error(err) - } - - _, err = engine.Insert(&StrangeName{Name: "sfsfdsfds"}) - if err != nil { - t.Error(err) - } - - beans := make([]StrangeName, 0) - err = engine.Find(&beans) - if err != nil { - t.Error(err) - } -} - -type VersionS struct { - Id int64 - Name string - Ver int `xorm:"version"` -} - -func testVersion(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(new(VersionS)) - if err != nil { - t.Error(err) - panic(err) - } - - ver := &VersionS{Name: "sfsfdsfds"} - _, err = engine.Insert(ver) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(ver) - if ver.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer := new(VersionS) - has, err := engine.Id(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - t.Error(errors.New(fmt.Sprintf("no version id is %v", ver.Id))) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 1 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - newVer.Name = "-------" - _, err = engine.Id(ver.Id).Update(newVer) - if err != nil { - t.Error(err) - panic(err) - } - - newVer = new(VersionS) - has, err = engine.Id(ver.Id).Get(newVer) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println(newVer) - if newVer.Ver != 2 { - err = errors.New("insert error") - t.Error(err) - panic(err) - } - - /* - newVer.Name = "-------" - _, err = engine.Id(ver.Id).Update(newVer) - if err != nil { - t.Error(err) - return - }*/ -} - -func testDistinct(engine *xorm.Engine, t *testing.T) { - users := make([]Userinfo, 0) - departname := engine.TableMapper.Obj2Table("Departname") - err := engine.Distinct(departname).Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - if len(users) != 1 { - t.Error(err) - panic(errors.New("should be one record")) - } - - fmt.Println(users) - - type Depart struct { - Departname string - } - - users2 := make([]Depart, 0) - err = engine.Distinct(departname).Table(new(Userinfo)).Find(&users2) - if err != nil { - t.Error(err) - panic(err) - } - if len(users2) != 1 { - t.Error(err) - panic(errors.New("should be one record")) - } - fmt.Println(users2) -} - -func testUseBool(engine *xorm.Engine, t *testing.T) { - cnt1, err := engine.Count(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - - users := make([]Userinfo, 0) - err = engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - var fNumber int64 - for _, u := range users { - if u.IsMan == false { - fNumber += 1 - } - } - - cnt2, err := engine.UseBool().Update(&Userinfo{IsMan: true}) - if err != nil { - t.Error(err) - panic(err) - } - if fNumber != cnt2 { - fmt.Println("cnt1", cnt1, "fNumber", fNumber, "cnt2", cnt2) - /*err = errors.New("Updated number is not corrected.") - t.Error(err) - panic(err)*/ - } - - _, err = engine.Update(&Userinfo{IsMan: true}) - if err == nil { - err = errors.New("error condition") - t.Error(err) - panic(err) - } -} - -func testBool(engine *xorm.Engine, t *testing.T) { - _, err := engine.UseBool().Update(&Userinfo{IsMan: true}) - if err != nil { - t.Error(err) - panic(err) - } - users := make([]Userinfo, 0) - err = engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for _, user := range users { - if !user.IsMan { - err = errors.New("update bool or find bool error") - t.Error(err) - panic(err) - } - } - - _, err = engine.UseBool().Update(&Userinfo{IsMan: false}) - if err != nil { - t.Error(err) - panic(err) - } - users = make([]Userinfo, 0) - err = engine.Find(&users) - if err != nil { - t.Error(err) - panic(err) - } - for _, user := range users { - if user.IsMan { - err = errors.New("update bool or find bool error") - t.Error(err) - panic(err) - } - } -} - -type TTime struct { - Id int64 - T time.Time - Tz time.Time `xorm:"timestampz"` -} - -func testTime(engine *xorm.Engine, t *testing.T) { - err := engine.Sync(&TTime{}) - if err != nil { - t.Error(err) - panic(err) - } - - tt := &TTime{} - _, err = engine.Insert(tt) - if err != nil { - t.Error(err) - panic(err) - } - - tt2 := &TTime{Id: tt.Id} - has, err := engine.Get(tt2) - if err != nil { - t.Error(err) - panic(err) - } - if !has { - err = errors.New("no record error") - t.Error(err) - panic(err) - } - - tt3 := &TTime{T: time.Now(), Tz: time.Now()} - _, err = engine.Insert(tt3) - if err != nil { - t.Error(err) - panic(err) - } - - tt4s := make([]TTime, 0) - err = engine.Find(&tt4s) - if err != nil { - t.Error(err) - panic(err) - } - fmt.Println("=======\n", tt4s, "=======\n") -} - -func testPrefixTableName(engine *xorm.Engine, t *testing.T) { - tempEngine, err := xorm.NewEngine(engine.DriverName, engine.DataSourceName) - if err != nil { - t.Error(err) - panic(err) - } - tempEngine.ShowSQL = true - mapper := core.NewPrefixMapper(core.SnakeMapper{}, "xlw_") - //tempEngine.SetMapper(mapper) - tempEngine.SetTableMapper(mapper) - exist, err := tempEngine.IsTableExist(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - if exist { - err = tempEngine.DropTables(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } - } - err = tempEngine.CreateTables(&Userinfo{}) - if err != nil { - t.Error(err) - panic(err) - } -} - -type CreatedUpdated struct { - Id int64 - Name string - Value float64 `xorm:"numeric"` - Created time.Time `xorm:"created"` - Created2 time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` -} - -func testCreatedUpdated(engine *xorm.Engine, t *testing.T) { - err := engine.Sync(&CreatedUpdated{}) - if err != nil { - t.Error(err) - panic(err) - } - - c := &CreatedUpdated{Name: "test"} - _, err = engine.Insert(c) - if err != nil { - t.Error(err) - panic(err) - } - - c2 := new(CreatedUpdated) - has, err := engine.Id(c.Id).Get(c2) - if err != nil { - t.Error(err) - panic(err) - } - - if !has { - panic(errors.New("no id")) - } - - c2.Value -= 1 - _, err = engine.Id(c2.Id).Update(c2) - if err != nil { - t.Error(err) - panic(err) - } -} - -type ProcessorsStruct struct { - Id int64 - - B4InsertFlag int - AfterInsertedFlag int - B4UpdateFlag int - AfterUpdatedFlag int - B4DeleteFlag int `xorm:"-"` - AfterDeletedFlag int `xorm:"-"` - - B4InsertViaExt int - AfterInsertedViaExt int - B4UpdateViaExt int - AfterUpdatedViaExt int - B4DeleteViaExt int `xorm:"-"` - AfterDeletedViaExt int `xorm:"-"` -} - -func (p *ProcessorsStruct) BeforeInsert() { - p.B4InsertFlag = 1 -} - -func (p *ProcessorsStruct) BeforeUpdate() { - p.B4UpdateFlag = 1 -} - -func (p *ProcessorsStruct) BeforeDelete() { - p.B4DeleteFlag = 1 -} - -func (p *ProcessorsStruct) AfterInsert() { - p.AfterInsertedFlag = 1 -} - -func (p *ProcessorsStruct) AfterUpdate() { - p.AfterUpdatedFlag = 1 -} - -func (p *ProcessorsStruct) AfterDelete() { - p.AfterDeletedFlag = 1 -} - -func testProcessors(engine *xorm.Engine, t *testing.T) { - // tempEngine, err := NewEngine(engine.DriverName, engine.DataSourceName) - // if err != nil { - // t.Error(err) - // panic(err) - // } - - engine.ShowSQL = true - err := engine.DropTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } - p := &ProcessorsStruct{} - - err = engine.CreateTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } - - b4InsertFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4InsertViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterInsertFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterInsertedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - _, err = engine.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } - } - - p2 := &ProcessorsStruct{} - _, err = engine.Id(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - // -- - - // test update processors - b4UpdateFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4UpdateViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterUpdateFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterUpdatedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - p = p2 // reset - - _, err = engine.Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } - } - - p2 = &ProcessorsStruct{} - _, err = engine.Id(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p2.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set: " + string(p.AfterUpdatedFlag))) - } - if p2.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p2.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set: " + string(p.AfterUpdatedViaExt))) - } - } - // -- - - // test delete processors - b4DeleteFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4DeleteViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterDeleteFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterDeletedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - p = p2 // reset - _, err = engine.Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag == 0 { - t.Error(errors.New("AfterDeletedFlag not set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt == 0 { - t.Error(errors.New("AfterDeletedViaExt not set")) - } - } - // -- - - // test insert multi - pslice := make([]*ProcessorsStruct, 0) - pslice = append(pslice, &ProcessorsStruct{}) - pslice = append(pslice, &ProcessorsStruct{}) - cnt, err := engine.Before(b4InsertFunc).After(afterInsertFunc).Insert(&pslice) - if err != nil { - t.Error(err) - panic(err) - } else { - if cnt != 2 { - t.Error(errors.New("incorrect insert count")) - } - for _, elem := range pslice { - if elem.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.AfterInsertedFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if elem.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } - } - } - - for _, elem := range pslice { - p = &ProcessorsStruct{} - _, err = engine.Id(elem.Id).Get(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - } - // -- -} - -func testProcessorsTx(engine *xorm.Engine, t *testing.T) { - // tempEngine, err := NewEngine(engine.DriverName, engine.DataSourceName) - // if err != nil { - // t.Error(err) - // panic(err) - // } - - // tempEngine.ShowSQL = true - err := engine.DropTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&ProcessorsStruct{}) - if err != nil { - t.Error(err) - panic(err) - } - - // test insert processors with tx rollback - session := engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - p := &ProcessorsStruct{} - b4InsertFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4InsertViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterInsertFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterInsertedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("B4InsertFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - - err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("B4InsertFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - session.Close() - p2 := &ProcessorsStruct{} - _, err = engine.Id(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.Id > 0 { - err = errors.New("tx got committed upon insert!?") - t.Error(err) - panic(err) - } - } - // -- - - // test insert processors with tx commit - session = engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - p = &ProcessorsStruct{} - _, err = session.Before(b4InsertFunc).After(afterInsertFunc).Insert(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p.AfterInsertedFlag == 0 { - t.Error(errors.New("AfterInsertedFlag not set")) - } - if p.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p.AfterInsertedViaExt == 0 { - t.Error(errors.New("AfterInsertedViaExt not set")) - } - } - session.Close() - p2 = &ProcessorsStruct{} - _, err = engine.Id(p.Id).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4InsertFlag == 0 { - t.Error(errors.New("B4InsertFlag not set")) - } - if p2.AfterInsertedFlag != 0 { - t.Error(errors.New("AfterInsertedFlag is set")) - } - if p2.B4InsertViaExt == 0 { - t.Error(errors.New("B4InsertViaExt not set")) - } - if p2.AfterInsertedViaExt != 0 { - t.Error(errors.New("AfterInsertedViaExt is set")) - } - } - insertedId := p2.Id - // -- - - // test update processors with tx rollback - session = engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - b4UpdateFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4UpdateViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterUpdateFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterUpdatedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - p = p2 // reset - - _, err = session.Id(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } - } - err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } - } - - session.Close() - p2 = &ProcessorsStruct{} - _, err = engine.Id(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4UpdateFlag != 0 { - t.Error(errors.New("B4UpdateFlag is set")) - } - if p2.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p2.B4UpdateViaExt != 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p2.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } - } - // -- - - // test update processors with tx commit - session = engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - p = &ProcessorsStruct{} - - _, err = session.Id(insertedId).Before(b4UpdateFunc).After(afterUpdateFunc).Update(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag != 0 { - t.Error(errors.New("AfterUpdatedFlag is set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt != 0 { - t.Error(errors.New("AfterUpdatedViaExt is set")) - } - } - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } - } - session.Close() - p2 = &ProcessorsStruct{} - _, err = engine.Id(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4UpdateFlag == 0 { - t.Error(errors.New("B4UpdateFlag not set")) - } - if p.AfterUpdatedFlag == 0 { - t.Error(errors.New("AfterUpdatedFlag not set")) - } - if p.B4UpdateViaExt == 0 { - t.Error(errors.New("B4UpdateViaExt not set")) - } - if p.AfterUpdatedViaExt == 0 { - t.Error(errors.New("AfterUpdatedViaExt not set")) - } - } - // -- - - // test delete processors with tx rollback - session = engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - b4DeleteFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.B4DeleteViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - afterDeleteFunc := func(bean interface{}) { - if v, ok := (bean).(*ProcessorsStruct); ok { - v.AfterDeletedViaExt = 1 - } else { - t.Error(errors.New("cast to ProcessorsStruct failed, how can this be!?")) - } - } - - p = &ProcessorsStruct{} // reset - - _, err = session.Id(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } - } - err = session.Rollback() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } - } - session.Close() - - p2 = &ProcessorsStruct{} - _, err = engine.Id(insertedId).Get(p2) - if err != nil { - t.Error(err) - panic(err) - } else { - if p2.B4DeleteFlag != 0 { - t.Error(errors.New("B4DeleteFlag is set")) - } - if p2.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p2.B4DeleteViaExt != 0 { - t.Error(errors.New("B4DeleteViaExt is set")) - } - if p2.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } - } - // -- - - // test delete processors with tx commit - session = engine.NewSession() - err = session.Begin() - if err != nil { - t.Error(err) - panic(err) - } - - p = &ProcessorsStruct{} - - _, err = session.Id(insertedId).Before(b4DeleteFunc).After(afterDeleteFunc).Delete(p) - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag != 0 { - t.Error(errors.New("AfterDeletedFlag is set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt != 0 { - t.Error(errors.New("AfterDeletedViaExt is set")) - } - } - err = session.Commit() - if err != nil { - t.Error(err) - panic(err) - } else { - if p.B4DeleteFlag == 0 { - t.Error(errors.New("B4DeleteFlag not set")) - } - if p.AfterDeletedFlag == 0 { - t.Error(errors.New("AfterDeletedFlag not set")) - } - if p.B4DeleteViaExt == 0 { - t.Error(errors.New("B4DeleteViaExt not set")) - } - if p.AfterDeletedViaExt == 0 { - t.Error(errors.New("AfterDeletedViaExt not set")) - } - } - session.Close() - // -- -} - -type NullData struct { - Id int64 - StringPtr *string - StringPtr2 *string `xorm:"text"` - BoolPtr *bool - BytePtr *byte - UintPtr *uint - Uint8Ptr *uint8 - Uint16Ptr *uint16 - Uint32Ptr *uint32 - Uint64Ptr *uint64 - IntPtr *int - Int8Ptr *int8 - Int16Ptr *int16 - Int32Ptr *int32 - Int64Ptr *int64 - RunePtr *rune - Float32Ptr *float32 - Float64Ptr *float64 - // Complex64Ptr *complex64 // !nashtsai! XORM yet support complex128: 'json: unsupported type: complex128' - // Complex128Ptr *complex128 // !nashtsai! XORM yet support complex128: 'json: unsupported type: complex128' - TimePtr *time.Time -} - -type NullData2 struct { - Id int64 - StringPtr string - StringPtr2 string `xorm:"text"` - BoolPtr bool - BytePtr byte - UintPtr uint - Uint8Ptr uint8 - Uint16Ptr uint16 - Uint32Ptr uint32 - Uint64Ptr uint64 - IntPtr int - Int8Ptr int8 - Int16Ptr int16 - Int32Ptr int32 - Int64Ptr int64 - RunePtr rune - Float32Ptr float32 - Float64Ptr float64 - // Complex64Ptr complex64 // !nashtsai! XORM yet support complex128: 'json: unsupported type: complex128' - // Complex128Ptr complex128 // !nashtsai! XORM yet support complex128: 'json: unsupported type: complex128' - TimePtr time.Time -} - -type NullData3 struct { - Id int64 - StringPtr *string -} - -func testPointerData(engine *xorm.Engine, t *testing.T) { - - err := engine.DropTables(&NullData{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&NullData{}) - if err != nil { - t.Error(err) - panic(err) - } - - nullData := NullData{ - StringPtr: new(string), - StringPtr2: new(string), - BoolPtr: new(bool), - BytePtr: new(byte), - UintPtr: new(uint), - Uint8Ptr: new(uint8), - Uint16Ptr: new(uint16), - Uint32Ptr: new(uint32), - Uint64Ptr: new(uint64), - IntPtr: new(int), - Int8Ptr: new(int8), - Int16Ptr: new(int16), - Int32Ptr: new(int32), - Int64Ptr: new(int64), - RunePtr: new(rune), - Float32Ptr: new(float32), - Float64Ptr: new(float64), - // Complex64Ptr: new(complex64), - // Complex128Ptr: new(complex128), - TimePtr: new(time.Time), - } - - *nullData.StringPtr = "abc" - *nullData.StringPtr2 = "123" - *nullData.BoolPtr = true - *nullData.BytePtr = 1 - *nullData.UintPtr = 1 - *nullData.Uint8Ptr = 1 - *nullData.Uint16Ptr = 1 - *nullData.Uint32Ptr = 1 - *nullData.Uint64Ptr = 1 - *nullData.IntPtr = -1 - *nullData.Int8Ptr = -1 - *nullData.Int16Ptr = -1 - *nullData.Int32Ptr = -1 - *nullData.Int64Ptr = -1 - *nullData.RunePtr = 1 - *nullData.Float32Ptr = -1.2 - *nullData.Float64Ptr = -1.1 - // *nullData.Complex64Ptr = 123456789012345678901234567890 - // *nullData.Complex128Ptr = 123456789012345678901234567890123456789012345678901234567890 - *nullData.TimePtr = time.Now() - - cnt, err := engine.Insert(&nullData) - fmt.Println(nullData.Id) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - if nullData.Id <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } - - // verify get values - nullDataGet := NullData{} - has, err := engine.Id(nullData.Id).Get(&nullDataGet) - if err != nil { - t.Error(err) - panic(err) - } else if !has { - t.Error(errors.New("ID not found")) - } - - if *nullDataGet.StringPtr != *nullData.StringPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.StringPtr))) - } - - if *nullDataGet.StringPtr2 != *nullData.StringPtr2 { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.StringPtr2))) - } - - if *nullDataGet.BoolPtr != *nullData.BoolPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%t]", *nullDataGet.BoolPtr))) - } - - if *nullDataGet.UintPtr != *nullData.UintPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.UintPtr))) - } - - if *nullDataGet.Uint8Ptr != *nullData.Uint8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint8Ptr))) - } - - if *nullDataGet.Uint16Ptr != *nullData.Uint16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint16Ptr))) - } - - if *nullDataGet.Uint32Ptr != *nullData.Uint32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint32Ptr))) - } - - if *nullDataGet.Uint64Ptr != *nullData.Uint64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint64Ptr))) - } - - if *nullDataGet.IntPtr != *nullData.IntPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.IntPtr))) - } - - if *nullDataGet.Int8Ptr != *nullData.Int8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int8Ptr))) - } - - if *nullDataGet.Int16Ptr != *nullData.Int16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int16Ptr))) - } - - if *nullDataGet.Int32Ptr != *nullData.Int32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int32Ptr))) - } - - if *nullDataGet.Int64Ptr != *nullData.Int64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int64Ptr))) - } - - if *nullDataGet.RunePtr != *nullData.RunePtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.RunePtr))) - } - - if *nullDataGet.Float32Ptr != *nullData.Float32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Float32Ptr))) - } - - if *nullDataGet.Float64Ptr != *nullData.Float64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Float64Ptr))) - } - - // if *nullDataGet.Complex64Ptr != *nullData.Complex64Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Complex64Ptr))) - // } - - // if *nullDataGet.Complex128Ptr != *nullData.Complex128Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Complex128Ptr))) - // } - - /*if (*nullDataGet.TimePtr).Unix() != (*nullData.TimePtr).Unix() { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]:[%v]", *nullDataGet.TimePtr, *nullData.TimePtr))) - } else { - // !nashtsai! mymysql driver will failed this test case, due the time is roundup to nearest second, I would considered this is a bug in mymysql driver - fmt.Printf("time value: [%v]:[%v]", *nullDataGet.TimePtr, *nullData.TimePtr) - fmt.Println() - }*/ - // -- - - // using instance type should just work too - nullData2Get := NullData2{} - - tableName := engine.TableMapper.Obj2Table("NullData") - - has, err = engine.Table(tableName).Id(nullData.Id).Get(&nullData2Get) - if err != nil { - t.Error(err) - panic(err) - } else if !has { - t.Error(errors.New("ID not found")) - } - - if nullData2Get.StringPtr != *nullData.StringPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.StringPtr))) - } - - if nullData2Get.StringPtr2 != *nullData.StringPtr2 { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.StringPtr2))) - } - - if nullData2Get.BoolPtr != *nullData.BoolPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%t]", nullData2Get.BoolPtr))) - } - - if nullData2Get.UintPtr != *nullData.UintPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.UintPtr))) - } - - if nullData2Get.Uint8Ptr != *nullData.Uint8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Uint8Ptr))) - } - - if nullData2Get.Uint16Ptr != *nullData.Uint16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Uint16Ptr))) - } - - if nullData2Get.Uint32Ptr != *nullData.Uint32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Uint32Ptr))) - } - - if nullData2Get.Uint64Ptr != *nullData.Uint64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Uint64Ptr))) - } - - if nullData2Get.IntPtr != *nullData.IntPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.IntPtr))) - } - - if nullData2Get.Int8Ptr != *nullData.Int8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Int8Ptr))) - } - - if nullData2Get.Int16Ptr != *nullData.Int16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Int16Ptr))) - } - - if nullData2Get.Int32Ptr != *nullData.Int32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Int32Ptr))) - } - - if nullData2Get.Int64Ptr != *nullData.Int64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Int64Ptr))) - } - - if nullData2Get.RunePtr != *nullData.RunePtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.RunePtr))) - } - - if nullData2Get.Float32Ptr != *nullData.Float32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Float32Ptr))) - } - - if nullData2Get.Float64Ptr != *nullData.Float64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Float64Ptr))) - } - - // if nullData2Get.Complex64Ptr != *nullData.Complex64Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Complex64Ptr))) - // } - - // if nullData2Get.Complex128Ptr != *nullData.Complex128Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", nullData2Get.Complex128Ptr))) - // } - - /*if nullData2Get.TimePtr.Unix() != (*nullData.TimePtr).Unix() { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]:[%v]", nullData2Get.TimePtr, *nullData.TimePtr))) - } else { - // !nashtsai! mymysql driver will failed this test case, due the time is roundup to nearest second, I would considered this is a bug in mymysql driver - fmt.Printf("time value: [%v]:[%v]", nullData2Get.TimePtr, *nullData.TimePtr) - fmt.Println() - }*/ - // -- -} - -func testNullValue(engine *xorm.Engine, t *testing.T) { - - err := engine.DropTables(&NullData{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&NullData{}) - if err != nil { - t.Error(err) - panic(err) - } - - nullData := NullData{} - - cnt, err := engine.Insert(&nullData) - fmt.Println(nullData.Id) - if err != nil { - t.Error(err) - panic(err) - } - if cnt != 1 { - err = errors.New("insert not returned 1") - t.Error(err) - panic(err) - return - } - if nullData.Id <= 0 { - err = errors.New("not return id error") - t.Error(err) - panic(err) - } - - nullDataGet := NullData{} - - has, err := engine.Id(nullData.Id).Get(&nullDataGet) - if err != nil { - t.Error(err) - panic(err) - } else if !has { - t.Error(errors.New("ID not found")) - } - - if nullDataGet.StringPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.StringPtr))) - } - - if nullDataGet.StringPtr2 != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.StringPtr2))) - } - - if nullDataGet.BoolPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%t]", *nullDataGet.BoolPtr))) - } - - if nullDataGet.UintPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.UintPtr))) - } - - if nullDataGet.Uint8Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint8Ptr))) - } - - if nullDataGet.Uint16Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint16Ptr))) - } - - if nullDataGet.Uint32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint32Ptr))) - } - - if nullDataGet.Uint64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint64Ptr))) - } - - if nullDataGet.IntPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.IntPtr))) - } - - if nullDataGet.Int8Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int8Ptr))) - } - - if nullDataGet.Int16Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int16Ptr))) - } - - if nullDataGet.Int32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int32Ptr))) - } - - if nullDataGet.Int64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int64Ptr))) - } - - if nullDataGet.RunePtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.RunePtr))) - } - - if nullDataGet.Float32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float32Ptr))) - } - - if nullDataGet.Float64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float64Ptr))) - } - - // if nullDataGet.Complex64Ptr != nil { - // t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Complex64Ptr))) - // } - - // if nullDataGet.Complex128Ptr != nil { - // t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Complex128Ptr))) - // } - - if nullDataGet.TimePtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.TimePtr))) - } - - nullDataUpdate := NullData{ - StringPtr: new(string), - StringPtr2: new(string), - BoolPtr: new(bool), - BytePtr: new(byte), - UintPtr: new(uint), - Uint8Ptr: new(uint8), - Uint16Ptr: new(uint16), - Uint32Ptr: new(uint32), - Uint64Ptr: new(uint64), - IntPtr: new(int), - Int8Ptr: new(int8), - Int16Ptr: new(int16), - Int32Ptr: new(int32), - Int64Ptr: new(int64), - RunePtr: new(rune), - Float32Ptr: new(float32), - Float64Ptr: new(float64), - // Complex64Ptr: new(complex64), - // Complex128Ptr: new(complex128), - TimePtr: new(time.Time), - } - - *nullDataUpdate.StringPtr = "abc" - *nullDataUpdate.StringPtr2 = "123" - *nullDataUpdate.BoolPtr = true - *nullDataUpdate.BytePtr = 1 - *nullDataUpdate.UintPtr = 1 - *nullDataUpdate.Uint8Ptr = 1 - *nullDataUpdate.Uint16Ptr = 1 - *nullDataUpdate.Uint32Ptr = 1 - *nullDataUpdate.Uint64Ptr = 1 - *nullDataUpdate.IntPtr = -1 - *nullDataUpdate.Int8Ptr = -1 - *nullDataUpdate.Int16Ptr = -1 - *nullDataUpdate.Int32Ptr = -1 - *nullDataUpdate.Int64Ptr = -1 - *nullDataUpdate.RunePtr = 1 - *nullDataUpdate.Float32Ptr = -1.2 - *nullDataUpdate.Float64Ptr = -1.1 - // *nullDataUpdate.Complex64Ptr = 123456789012345678901234567890 - // *nullDataUpdate.Complex128Ptr = 123456789012345678901234567890123456789012345678901234567890 - *nullDataUpdate.TimePtr = time.Now() - - cnt, err = engine.Id(nullData.Id).Update(&nullDataUpdate) - if err != nil { - t.Error(err) - panic(err) - } else if cnt != 1 { - t.Error(errors.New("update count == 0, how can this happen!?")) - return - } - - // verify get values - nullDataGet = NullData{} - has, err = engine.Id(nullData.Id).Get(&nullDataGet) - if err != nil { - t.Error(err) - return - } else if !has { - t.Error(errors.New("ID not found")) - return - } - - if *nullDataGet.StringPtr != *nullDataUpdate.StringPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.StringPtr))) - } - - if *nullDataGet.StringPtr2 != *nullDataUpdate.StringPtr2 { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.StringPtr2))) - } - - if *nullDataGet.BoolPtr != *nullDataUpdate.BoolPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%t]", *nullDataGet.BoolPtr))) - } - - if *nullDataGet.UintPtr != *nullDataUpdate.UintPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.UintPtr))) - } - - if *nullDataGet.Uint8Ptr != *nullDataUpdate.Uint8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint8Ptr))) - } - - if *nullDataGet.Uint16Ptr != *nullDataUpdate.Uint16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint16Ptr))) - } - - if *nullDataGet.Uint32Ptr != *nullDataUpdate.Uint32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint32Ptr))) - } - - if *nullDataGet.Uint64Ptr != *nullDataUpdate.Uint64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Uint64Ptr))) - } - - if *nullDataGet.IntPtr != *nullDataUpdate.IntPtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.IntPtr))) - } - - if *nullDataGet.Int8Ptr != *nullDataUpdate.Int8Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int8Ptr))) - } - - if *nullDataGet.Int16Ptr != *nullDataUpdate.Int16Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int16Ptr))) - } - - if *nullDataGet.Int32Ptr != *nullDataUpdate.Int32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int32Ptr))) - } - - if *nullDataGet.Int64Ptr != *nullDataUpdate.Int64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Int64Ptr))) - } - - if *nullDataGet.RunePtr != *nullDataUpdate.RunePtr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.RunePtr))) - } - - if *nullDataGet.Float32Ptr != *nullDataUpdate.Float32Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Float32Ptr))) - } - - if *nullDataGet.Float64Ptr != *nullDataUpdate.Float64Ptr { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Float64Ptr))) - } - - // if *nullDataGet.Complex64Ptr != *nullDataUpdate.Complex64Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Complex64Ptr))) - // } - - // if *nullDataGet.Complex128Ptr != *nullDataUpdate.Complex128Ptr { - // t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]", *nullDataGet.Complex128Ptr))) - // } - - // !nashtsai! skipped mymysql test due to driver will round up time caused inaccuracy comparison - // skipped postgres test due to postgres driver doesn't read time.Time's timzezone info when stored in the db - // mysql and sqlite3 seem have done this correctly by storing datatime in UTC timezone, I think postgres driver - // prefer using timestamp with timezone to sovle the issue - if engine.DriverName != core.POSTGRES && engine.DriverName != "mymysql" && - engine.DriverName != core.MYSQL { - if (*nullDataGet.TimePtr).Unix() != (*nullDataUpdate.TimePtr).Unix() { - t.Error(errors.New(fmt.Sprintf("inserted value unmatch: [%v]:[%v]", *nullDataGet.TimePtr, *nullDataUpdate.TimePtr))) - } else { - // !nashtsai! mymysql driver will failed this test case, due the time is roundup to nearest second, I would considered this is a bug in mymysql driver - // inserted value unmatch: [2013-12-25 12:12:45 +0800 CST]:[2013-12-25 12:12:44.878903653 +0800 CST] - fmt.Printf("time value: [%v]:[%v]", *nullDataGet.TimePtr, *nullDataUpdate.TimePtr) - fmt.Println() - } - } - - // update to null values - nullDataUpdate = NullData{} - - string_ptr := engine.ColumnMapper.Obj2Table("StringPtr") - - cnt, err = engine.Id(nullData.Id).Cols(string_ptr).Update(&nullDataUpdate) - if err != nil { - t.Error(err) - panic(err) - } else if cnt != 1 { - t.Error(errors.New("update count == 0, how can this happen!?")) - return - } - - // verify get values - nullDataGet = NullData{} - has, err = engine.Id(nullData.Id).Get(&nullDataGet) - if err != nil { - t.Error(err) - return - } else if !has { - t.Error(errors.New("ID not found")) - return - } - - fmt.Printf("%+v", nullDataGet) - fmt.Println() - - if nullDataGet.StringPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.StringPtr))) - } - /* - if nullDataGet.StringPtr2 != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.StringPtr2))) - } - - if nullDataGet.BoolPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%t]", *nullDataGet.BoolPtr))) - } - - if nullDataGet.UintPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.UintPtr))) - } - - if nullDataGet.Uint8Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint8Ptr))) - } - - if nullDataGet.Uint16Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint16Ptr))) - } - - if nullDataGet.Uint32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint32Ptr))) - } - - if nullDataGet.Uint64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Uint64Ptr))) - } - - if nullDataGet.IntPtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.IntPtr))) - } - - if nullDataGet.Int8Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int8Ptr))) - } - - if nullDataGet.Int16Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int16Ptr))) - } - - if nullDataGet.Int32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int32Ptr))) - } - - if nullDataGet.Int64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Int64Ptr))) - } - - if nullDataGet.RunePtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.RunePtr))) - } - - if nullDataGet.Float32Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float32Ptr))) - } - - if nullDataGet.Float64Ptr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float64Ptr))) - } - - // if nullDataGet.Complex64Ptr != nil { - // t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float64Ptr))) - // } - - // if nullDataGet.Complex128Ptr != nil { - // t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.Float64Ptr))) - // } - - if nullDataGet.TimePtr != nil { - t.Error(errors.New(fmt.Sprintf("not null value: [%v]", *nullDataGet.TimePtr))) - }*/ - // -- - -} - -type CompositeKey struct { - Id1 int64 `xorm:"id1 pk"` - Id2 int64 `xorm:"id2 pk"` - UpdateStr string -} - -func testCompositeKey(engine *xorm.Engine, t *testing.T) { - - err := engine.DropTables(&CompositeKey{}) - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&CompositeKey{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&CompositeKey{11, 22, ""}) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("failed to insert CompositeKey{11, 22}")) - } - - cnt, err = engine.Insert(&CompositeKey{11, 22, ""}) - if err == nil || cnt == 1 { - t.Error(errors.New("inserted CompositeKey{11, 22}")) - } - - var compositeKeyVal CompositeKey - has, err := engine.Id(core.PK{11, 22}).Get(&compositeKeyVal) - if err != nil { - t.Error(err) - } else if !has { - t.Error(errors.New("can't get CompositeKey{11, 22}")) - } - - // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&core.PK{11, 22}).Get(&compositeKeyVal) - if err != nil { - t.Error(err) - } else if !has { - t.Error(errors.New("can't get CompositeKey{11, 22}")) - } - - compositeKeyVal = CompositeKey{UpdateStr: "test1"} - cnt, err = engine.Id(core.PK{11, 22}).Update(&compositeKeyVal) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("can't update CompositeKey{11, 22}")) - } - - cnt, err = engine.Id(core.PK{11, 22}).Delete(&CompositeKey{}) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("can't delete CompositeKey{11, 22}")) - } -} - -type Lowercase struct { - Id int64 - Name string - ended int64 `xorm:"-"` -} - -func testLowerCase(engine *xorm.Engine, t *testing.T) { - err := engine.Sync(&Lowercase{}) - _, err = engine.Where("(id) > 0").Delete(&Lowercase{}) - if err != nil { - t.Error(err) - panic(err) - } - _, err = engine.Insert(&Lowercase{ended: 1}) - if err != nil { - t.Error(err) - panic(err) - } - - ls := make([]Lowercase, 0) - err = engine.Find(&ls) - if err != nil { - t.Error(err) - panic(err) - } - - if len(ls) != 1 { - err = errors.New("should be 1") - t.Error(err) - panic(err) - } -} - -type User struct { - UserId string `xorm:"varchar(19) not null pk"` - NickName string `xorm:"varchar(19) not null"` - GameId uint32 `xorm:"integer pk"` - Score int32 `xorm:"integer"` -} - -func testCompositeKey2(engine *xorm.Engine, t *testing.T) { - err := engine.DropTables(&User{}) - - if err != nil { - t.Error(err) - panic(err) - } - - err = engine.CreateTables(&User{}) - if err != nil { - t.Error(err) - panic(err) - } - - cnt, err := engine.Insert(&User{"11", "nick", 22, 5}) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("failed to insert User{11, 22}")) - } - - cnt, err = engine.Insert(&User{"11", "nick", 22, 6}) - if err == nil || cnt == 1 { - t.Error(errors.New("inserted User{11, 22}")) - } - - var user User - has, err := engine.Id(core.PK{"11", 22}).Get(&user) - if err != nil { - t.Error(err) - } else if !has { - t.Error(errors.New("can't get User{11, 22}")) - } - - // test passing PK ptr, this test seem failed withCache - has, err = engine.Id(&core.PK{"11", 22}).Get(&user) - if err != nil { - t.Error(err) - } else if !has { - t.Error(errors.New("can't get User{11, 22}")) - } - - user = User{NickName: "test1"} - cnt, err = engine.Id(core.PK{"11", 22}).Update(&user) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("can't update User{11, 22}")) - } - - cnt, err = engine.Id(core.PK{"11", 22}).Delete(&User{}) - if err != nil { - t.Error(err) - } else if cnt != 1 { - t.Error(errors.New("can't delete CompositeKey{11, 22}")) - } -} - -type CustomTableName struct { - Id int64 - Name string -} - -func (c *CustomTableName) TableName() string { - return "customtablename" -} - -func testCustomTableName(engine *xorm.Engine, t *testing.T) { - c := new(CustomTableName) - err := engine.DropTables(c) - if err != nil { - t.Error(err) - } - - err = engine.CreateTables(c) - if err != nil { - t.Error(err) - } -} - -func testAll(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- directCreateTable --------------") - directCreateTable(engine, t) - fmt.Println("-------------- insert --------------") - insert(engine, t) - fmt.Println("-------------- insertAutoIncr --------------") - insertAutoIncr(engine, t) - fmt.Println("-------------- insertMulti --------------") - insertMulti(engine, t) - fmt.Println("-------------- insertTwoTable --------------") - insertTwoTable(engine, t) - fmt.Println("-------------- testDelete --------------") - testDelete(engine, t) - fmt.Println("-------------- get --------------") - get(engine, t) - fmt.Println("-------------- cascadeGet --------------") - cascadeGet(engine, t) - fmt.Println("-------------- find --------------") - find(engine, t) - fmt.Println("-------------- find2 --------------") - find2(engine, t) - fmt.Println("-------------- findMap --------------") - findMap(engine, t) - fmt.Println("-------------- findMap2 --------------") - findMap2(engine, t) - fmt.Println("-------------- count --------------") - count(engine, t) - fmt.Println("-------------- where --------------") - where(engine, t) - fmt.Println("-------------- in --------------") - in(engine, t) - fmt.Println("-------------- limit --------------") - limit(engine, t) -} - -func testAll2(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- table --------------") - table(engine, t) - fmt.Println("-------------- createMultiTables --------------") - createMultiTables(engine, t) - fmt.Println("-------------- tableOp --------------") - tableOp(engine, t) - fmt.Println("-------------- testCharst --------------") - testCharst(engine, t) - fmt.Println("-------------- testStoreEngine --------------") - testStoreEngine(engine, t) - fmt.Println("-------------- testExtends --------------") - testExtends(engine, t) - fmt.Println("-------------- testColTypes --------------") - testColTypes(engine, t) - fmt.Println("-------------- testCustomType --------------") - testCustomType(engine, t) - fmt.Println("-------------- testCreatedAndUpdated --------------") - testCreatedAndUpdated(engine, t) - fmt.Println("-------------- testIndexAndUnique --------------") - testIndexAndUnique(engine, t) - fmt.Println("-------------- testIntId --------------") - testIntId(engine, t) - fmt.Println("-------------- testInt32Id --------------") - testInt32Id(engine, t) - fmt.Println("-------------- testUintId --------------") - testUintId(engine, t) - fmt.Println("-------------- testUint32Id --------------") - testUint32Id(engine, t) - fmt.Println("-------------- testUint64Id --------------") - testUint64Id(engine, t) - fmt.Println("-------------- testMetaInfo --------------") - testMetaInfo(engine, t) - fmt.Println("-------------- testIterate --------------") - testIterate(engine, t) - fmt.Println("-------------- testRows --------------") - testRows(engine, t) - fmt.Println("-------------- testStrangeName --------------") - testStrangeName(engine, t) - fmt.Println("-------------- testVersion --------------") - testVersion(engine, t) - fmt.Println("-------------- testDistinct --------------") - testDistinct(engine, t) - fmt.Println("-------------- testUseBool --------------") - testUseBool(engine, t) - fmt.Println("-------------- testBool --------------") - testBool(engine, t) - fmt.Println("-------------- testTime --------------") - testTime(engine, t) - fmt.Println("-------------- testPrefixTableName --------------") - testPrefixTableName(engine, t) - fmt.Println("-------------- testCreatedUpdated --------------") - testCreatedUpdated(engine, t) - fmt.Println("-------------- testLowercase ---------------") - testLowerCase(engine, t) - fmt.Println("-------------- processors --------------") - testProcessors(engine, t) - fmt.Println("-------------- transaction --------------") - transaction(engine, t) - fmt.Println("-------------- testCustomTableName --------------") - testCustomTableName(engine, t) -} - -// !nash! the 3rd set of the test is intended for non-cache enabled engine -func testAll3(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- processors TX --------------") - testProcessorsTx(engine, t) - fmt.Println("-------------- insert pointer data --------------") - testPointerData(engine, t) - fmt.Println("-------------- insert null data --------------") - testNullValue(engine, t) - fmt.Println("-------------- testCompositeKey --------------") - testCompositeKey(engine, t) - fmt.Println("-------------- testCompositeKey2 --------------") - testCompositeKey2(engine, t) - fmt.Println("-------------- testStringPK --------------") - testStringPK(engine, t) -} - -func testAllSnakeMapper(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- query --------------") - testQuery(engine, t) - fmt.Println("-------------- exec --------------") - exec(engine, t) - fmt.Println("-------------- update --------------") - update(engine, t) - fmt.Println("-------------- order --------------") - order(engine, t) - fmt.Println("-------------- join --------------") - join(engine, t) - fmt.Println("-------------- having --------------") - having(engine, t) - fmt.Println("-------------- combineTransaction --------------") - combineTransaction(engine, t) - fmt.Println("-------------- testCols --------------") - testCols(engine, t) -} - -func testAllSameMapper(engine *xorm.Engine, t *testing.T) { - fmt.Println("-------------- query --------------") - testQuerySameMapper(engine, t) - fmt.Println("-------------- exec --------------") - execSameMapper(engine, t) - fmt.Println("-------------- update --------------") - updateSameMapper(engine, t) - fmt.Println("-------------- order --------------") - orderSameMapper(engine, t) - fmt.Println("-------------- join --------------") - joinSameMapper(engine, t) - fmt.Println("-------------- having --------------") - havingSameMapper(engine, t) - fmt.Println("-------------- combineTransaction --------------") - combineTransactionSameMapper(engine, t) - fmt.Println("-------------- testCols --------------") - testColsSameMapper(engine, t) -} diff --git a/tests/benchmark.bat b/tests/benchmark.bat deleted file mode 100644 index d35fe044..00000000 --- a/tests/benchmark.bat +++ /dev/null @@ -1 +0,0 @@ -go test -v -bench=. -run=XXX \ No newline at end of file diff --git a/tests/benchmark.sh b/tests/benchmark.sh deleted file mode 100755 index d35fe044..00000000 --- a/tests/benchmark.sh +++ /dev/null @@ -1 +0,0 @@ -go test -v -bench=. -run=XXX \ No newline at end of file diff --git a/tests/benchmark_base_test.go b/tests/benchmark_base_test.go deleted file mode 100644 index d970bd13..00000000 --- a/tests/benchmark_base_test.go +++ /dev/null @@ -1,176 +0,0 @@ -package tests - -import ( - "database/sql" - "testing" - - "github.com/go-xorm/xorm" -) - -type BigStruct struct { - Id int64 - Name string - Title string - Age string - Alias string - NickName string -} - -func doBenchDriverInsert(db *sql.DB, b *testing.B) { - b.StartTimer() - for i := 0; i < b.N; i++ { - _, err := db.Exec(`insert into big_struct (name, title, age, alias, nick_name) - values ('fafdasf', 'fadfa', 'afadfsaf', 'fadfafdsafd', 'fadfafdsaf')`) - if err != nil { - b.Error(err) - return - } - } - b.StopTimer() -} - -func doBenchDriverFind(db *sql.DB, b *testing.B) { - b.StopTimer() - for i := 0; i < 50; i++ { - _, err := db.Exec(`insert into big_struct (name, title, age, alias, nick_name) - values ('fafdasf', 'fadfa', 'afadfsaf', 'fadfafdsafd', 'fadfafdsaf')`) - if err != nil { - b.Error(err) - return - } - } - - b.StartTimer() - for i := 0; i < b.N/50; i++ { - rows, err := db.Query("select * from big_struct limit 50") - if err != nil { - b.Error(err) - return - } - for rows.Next() { - s := &BigStruct{} - rows.Scan(&s.Id, &s.Name, &s.Title, &s.Age, &s.Alias, &s.NickName) - } - } - b.StopTimer() -} - -func doBenchDriver(newdriver func() (*sql.DB, error), createTableSql, - dropTableSql string, opFunc func(*sql.DB, *testing.B), t *testing.B) { - db, err := newdriver() - if err != nil { - t.Error(err) - return - } - defer db.Close() - - _, err = db.Exec(createTableSql) - if err != nil { - t.Error(err) - return - } - - opFunc(db, t) - - _, err = db.Exec(dropTableSql) - if err != nil { - t.Error(err) - return - } -} - -func doBenchInsert(engine *xorm.Engine, b *testing.B) { - b.StopTimer() - bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} - err := engine.CreateTables(bs) - if err != nil { - b.Error(err) - return - } - - b.StartTimer() - for i := 0; i < b.N; i++ { - bs.Id = 0 - _, err = engine.Insert(bs) - if err != nil { - b.Error(err) - return - } - } - b.StopTimer() - err = engine.DropTables(bs) - if err != nil { - b.Error(err) - return - } -} - -func doBenchFind(engine *xorm.Engine, b *testing.B) { - b.StopTimer() - bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} - err := engine.CreateTables(bs) - if err != nil { - b.Error(err) - return - } - - for i := 0; i < 100; i++ { - bs.Id = 0 - _, err = engine.Insert(bs) - if err != nil { - b.Error(err) - return - } - } - - b.StartTimer() - for i := 0; i < b.N/50; i++ { - bss := new([]BigStruct) - err = engine.Limit(50).Find(bss) - if err != nil { - b.Error(err) - return - } - } - b.StopTimer() - err = engine.DropTables(bs) - if err != nil { - b.Error(err) - return - } -} - -func doBenchFindPtr(engine *xorm.Engine, b *testing.B) { - b.StopTimer() - bs := &BigStruct{0, "fafdasf", "fadfa", "afadfsaf", "fadfafdsafd", "fadfafdsaf"} - err := engine.CreateTables(bs) - if err != nil { - b.Error(err) - return - } - - for i := 0; i < 100; i++ { - bs.Id = 0 - _, err = engine.Insert(bs) - if err != nil { - b.Error(err) - return - } - } - - b.StartTimer() - for i := 0; i < b.N/50; i++ { - bss := new([]*BigStruct) - err = engine.Limit(50).Find(bss) - if err != nil { - b.Error(err) - return - } - } - b.StopTimer() - err = engine.DropTables(bs) - if err != nil { - b.Error(err) - return - } -} diff --git a/tests/mssql_test.go b/tests/mssql_test.go deleted file mode 100644 index fac774e1..00000000 --- a/tests/mssql_test.go +++ /dev/null @@ -1,144 +0,0 @@ -package tests - -// -// +build windows - -import ( - "database/sql" - "testing" - - "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" - _ "github.com/lunny/godbc" -) - -const mssqlConnStr = "driver={SQL Server};Server=192.168.20.135;Database=xorm_test; uid=sa; pwd=1234;" - -func newMssqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("odbc", mssqlConnStr) -} - -func TestMssql(t *testing.T) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAll2(engine, t) -} - -func TestMssqlWithCache(t *testing.T) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAll2(engine, t) -} - -func newMssqlDriverDB() (*sql.DB, error) { - return sql.Open("odbc", mssqlConnStr) -} - -const ( - createTableMssql = `IF NOT EXISTS (SELECT [name] FROM sys.tables WHERE [name] = 'big_struct' ) CREATE TABLE - "big_struct" ("id" BIGINT PRIMARY KEY IDENTITY NOT NULL, "name" VARCHAR(255) NULL, "title" VARCHAR(255) NULL, - "age" VARCHAR(255) NULL, "alias" VARCHAR(255) NULL, "nick_name" VARCHAR(255) NULL); - ` - - dropTableMssql = "IF EXISTS (SELECT * FROM sysobjects WHERE id = object_id(N'big_struct') and OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE IF EXISTS `big_struct`;" -) - -func BenchmarkMssqlDriverInsert(t *testing.B) { - doBenchDriver(newMssqlDriverDB, createTableMssql, dropTableMssql, - doBenchDriverInsert, t) -} - -func BenchmarkMssqlDriverFind(t *testing.B) { - doBenchDriver(newMssqlDriverDB, createTableMssql, dropTableMssql, - doBenchDriverFind, t) -} - -func BenchmarkMssqlNoCacheInsert(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchInsert(engine, t) -} - -func BenchmarkMssqlNoCacheFind(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFind(engine, t) -} - -func BenchmarkMssqlNoCacheFindPtr(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFindPtr(engine, t) -} - -func BenchmarkMssqlCacheInsert(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchInsert(engine, t) -} - -func BenchmarkMssqlCacheFind(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFind(engine, t) -} - -func BenchmarkMssqlCacheFindPtr(t *testing.B) { - engine, err := newMssqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFindPtr(engine, t) -} diff --git a/tests/mymysql_test.go b/tests/mymysql_test.go deleted file mode 100644 index ef212a2c..00000000 --- a/tests/mymysql_test.go +++ /dev/null @@ -1,169 +0,0 @@ -package tests - -import ( - "database/sql" - "testing" - - "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" - _ "github.com/ziutek/mymysql/godrv" -) - -/* -CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET -utf8 COLLATE utf8_general_ci; -*/ - -var showTestSql bool = true - -func TestMyMysql(t *testing.T) { - err := mymysqlDdlImport() - if err != nil { - t.Error(err) - return - } - engine, err := xorm.NewEngine("mymysql", "xorm_test/root/") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestMyMysqlWithCache(t *testing.T) { - err := mymysqlDdlImport() - if err != nil { - t.Error(err) - return - } - engine, err := xorm.NewEngine("mymysql", "xorm_test2/root/") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAll2(engine, t) -} - -func newMyMysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mymysql", "xorm_test2/root/") -} - -func newMyMysqlDriverDB() (*sql.DB, error) { - return sql.Open("mymysql", "xorm_test2/root/") -} - -func BenchmarkMyMysqlDriverInsert(t *testing.B) { - doBenchDriver(newMyMysqlDriverDB, createTableMySql, dropTableMySql, - doBenchDriverInsert, t) -} - -func BenchmarkMyMysqlDriverFind(t *testing.B) { - doBenchDriver(newMyMysqlDriverDB, createTableMySql, dropTableMySql, - doBenchDriverFind, t) -} - -func mymysqlDdlImport() error { - engine, err := xorm.NewEngine("mymysql", "/root/") - if err != nil { - return err - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - sqlResults, _ := engine.Import("testdata/mysql_ddl.sql") - engine.LogDebug("sql results: %v", sqlResults) - engine.Close() - return nil -} - -func BenchmarkMyMysqlNoCacheInsert(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - defer engine.Close() - - doBenchInsert(engine, t) -} - -func BenchmarkMyMysqlNoCacheFind(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - defer engine.Close() - - //engine.ShowSQL = true - doBenchFind(engine, t) -} - -func BenchmarkMyMysqlNoCacheFindPtr(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - defer engine.Close() - - //engine.ShowSQL = true - doBenchFindPtr(engine, t) -} - -func BenchmarkMyMysqlCacheInsert(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - - defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchInsert(engine, t) -} - -func BenchmarkMyMysqlCacheFind(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - - defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFind(engine, t) -} - -func BenchmarkMyMysqlCacheFindPtr(t *testing.B) { - engine, err := newMyMysqlEngine() - if err != nil { - t.Error(err) - return - } - - defer engine.Close() - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFindPtr(engine, t) -} diff --git a/tests/mysql_test.go b/tests/mysql_test.go deleted file mode 100644 index 02a70b39..00000000 --- a/tests/mysql_test.go +++ /dev/null @@ -1,222 +0,0 @@ -package tests - -import ( - "database/sql" - "testing" - - _ "github.com/go-sql-driver/mysql" - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" -) - -/* -CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET -utf8 COLLATE utf8_general_ci; -*/ - -func TestMysql(t *testing.T) { - err := mysqlDdlImport() - if err != nil { - t.Error(err) - return - } - - engine, err := xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestMysqlSameMapper(t *testing.T) { - err := mysqlDdlImport() - if err != nil { - t.Error(err) - return - } - - engine, err := xorm.NewEngine("mysql", "root:@/xorm_test1?charset=utf8") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - engine.SetMapper(core.SameMapper{}) - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestMysqlWithCache(t *testing.T) { - err := mysqlDdlImport() - if err != nil { - t.Error(err) - return - } - - engine, err := xorm.NewEngine("mysql", "root:@/xorm_test2?charset=utf8") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) -} - -func TestMysqlWithCacheSameMapper(t *testing.T) { - err := mysqlDdlImport() - if err != nil { - t.Error(err) - return - } - - engine, err := xorm.NewEngine("mysql", "root:@/xorm_test3?charset=utf8") - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetMapper(core.SameMapper{}) - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) -} - -func newMysqlEngine() (*xorm.Engine, error) { - return xorm.NewEngine("mysql", "root:@/xorm_test?charset=utf8") -} - -func mysqlDdlImport() error { - engine, err := xorm.NewEngine("mysql", "root:@/?charset=utf8") - if err != nil { - return err - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - sqlResults, _ := engine.Import("testdata/mysql_ddl.sql") - engine.LogDebug("sql results: %v", sqlResults) - engine.Close() - return nil -} - -func newMysqlDriverDB() (*sql.DB, error) { - return sql.Open("mysql", "root:@/xorm_test?charset=utf8") -} - -const ( - createTableMySql = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` BIGINT PRIMARY KEY AUTO_INCREMENT NOT NULL, `name` VARCHAR(255) NULL, `title` VARCHAR(255) NULL, `age` VARCHAR(255) NULL, `alias` VARCHAR(255) NULL, `nick_name` VARCHAR(255) NULL);" - dropTableMySql = "DROP TABLE IF EXISTS `big_struct`;" -) - -func BenchmarkMysqlDriverInsert(t *testing.B) { - doBenchDriver(newMysqlDriverDB, createTableMySql, dropTableMySql, - doBenchDriverInsert, t) -} - -func BenchmarkMysqlDriverFind(t *testing.B) { - doBenchDriver(newMysqlDriverDB, createTableMySql, dropTableMySql, - doBenchDriverFind, t) -} - -func BenchmarkMysqlNoCacheInsert(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchInsert(engine, t) -} - -func BenchmarkMysqlNoCacheFind(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFind(engine, t) -} - -func BenchmarkMysqlNoCacheFindPtr(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFindPtr(engine, t) -} - -func BenchmarkMysqlCacheInsert(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchInsert(engine, t) -} - -func BenchmarkMysqlCacheFind(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFind(engine, t) -} - -func BenchmarkMysqlCacheFindPtr(t *testing.B) { - engine, err := newMysqlEngine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFindPtr(engine, t) -} diff --git a/tests/postgres_test.go b/tests/postgres_test.go deleted file mode 100644 index 8abee32f..00000000 --- a/tests/postgres_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package tests - -import ( - "database/sql" - "testing" - - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" - _ "github.com/lib/pq" -) - -//var connStr string = "dbname=xorm_test user=lunny password=1234 sslmode=disable" - -var connStr string = "dbname=xorm_test sslmode=disable" - -func newPostgresEngine() (*xorm.Engine, error) { - orm, err := xorm.NewEngine("postgres", connStr) - if err != nil { - return nil, err - } - tables, err := orm.DBMetas() - if err != nil { - return nil, err - } - for _, table := range tables { - _, err = orm.Exec("drop table \"" + table.Name + "\"") - if err != nil { - return nil, err - } - } - - return orm, err -} - -func newPostgresDriverDB() (*sql.DB, error) { - return sql.Open("postgres", connStr) -} - -func TestPostgres(t *testing.T) { - engine, err := newPostgresEngine() - if err != nil { - t.Error(err) - return - } - defer engine.Close() - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestPostgresWithCache(t *testing.T) { - engine, err := newPostgresEngine() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - defer engine.Close() - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) -} - -func TestPostgresSameMapper(t *testing.T) { - engine, err := newPostgresEngine() - if err != nil { - t.Error(err) - return - } - defer engine.Close() - engine.SetMapper(core.SameMapper{}) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestPostgresWithCacheSameMapper(t *testing.T) { - engine, err := newPostgresEngine() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - defer engine.Close() - engine.SetMapper(core.SameMapper{}) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) -} - -const ( - createTablePostgres = `CREATE TABLE IF NOT EXISTS "big_struct" ("id" SERIAL PRIMARY KEY NOT NULL, "name" VARCHAR(255) NULL, "title" VARCHAR(255) NULL, "age" VARCHAR(255) NULL, "alias" VARCHAR(255) NULL, "nick_name" VARCHAR(255) NULL);` - dropTablePostgres = `DROP TABLE IF EXISTS "big_struct";` -) - -func BenchmarkPostgresDriverInsert(t *testing.B) { - doBenchDriver(newPostgresDriverDB, createTablePostgres, dropTablePostgres, - doBenchDriverInsert, t) -} - -func BenchmarkPostgresDriverFind(t *testing.B) { - doBenchDriver(newPostgresDriverDB, createTablePostgres, dropTablePostgres, - doBenchDriverFind, t) -} - -func BenchmarkPostgresNoCacheInsert(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchInsert(engine, t) -} - -func BenchmarkPostgresNoCacheFind(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFind(engine, t) -} - -func BenchmarkPostgresNoCacheFindPtr(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFindPtr(engine, t) -} - -func BenchmarkPostgresCacheInsert(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchInsert(engine, t) -} - -func BenchmarkPostgresCacheFind(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFind(engine, t) -} - -func BenchmarkPostgresCacheFindPtr(t *testing.B) { - engine, err := newPostgresEngine() - - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - - doBenchFindPtr(engine, t) -} diff --git a/tests/sqlite3_test.go b/tests/sqlite3_test.go deleted file mode 100644 index 18cc84a3..00000000 --- a/tests/sqlite3_test.go +++ /dev/null @@ -1,183 +0,0 @@ -package tests - -import ( - "database/sql" - "os" - "testing" - - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" - _ "github.com/mattn/go-sqlite3" -) - -func newSqlite3Engine() (*xorm.Engine, error) { - os.Remove("./test.db") - return xorm.NewEngine("sqlite3", "./test.db") -} - -func newSqlite3DriverDB() (*sql.DB, error) { - os.Remove("./test.db") - return sql.Open("sqlite3", "./test.db") -} - -func TestSqlite3(t *testing.T) { - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestSqlite3WithCache(t *testing.T) { - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSnakeMapper(engine, t) - testAll2(engine, t) -} - -func TestSqlite3SameMapper(t *testing.T) { - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetMapper(core.SameMapper{}) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) - testAll3(engine, t) -} - -func TestSqlite3WithCacheSameMapper(t *testing.T) { - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetMapper(core.SameMapper{}) - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - engine.ShowSQL = showTestSql - engine.ShowErr = showTestSql - engine.ShowWarn = showTestSql - engine.ShowDebug = showTestSql - - testAll(engine, t) - testAllSameMapper(engine, t) - testAll2(engine, t) -} - -const ( - createTableSqlite3 = "CREATE TABLE IF NOT EXISTS `big_struct` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT NULL, `title` TEXT NULL, `age` TEXT NULL, `alias` TEXT NULL, `nick_name` TEXT NULL);" - dropTableSqlite3 = "DROP TABLE IF EXISTS `big_struct`;" -) - -func BenchmarkSqlite3DriverInsert(t *testing.B) { - doBenchDriver(newSqlite3DriverDB, createTableSqlite3, dropTableSqlite3, - doBenchDriverInsert, t) -} - -func BenchmarkSqlite3DriverFind(t *testing.B) { - doBenchDriver(newSqlite3DriverDB, createTableSqlite3, dropTableSqlite3, - doBenchDriverFind, t) -} - -func BenchmarkSqlite3NoCacheInsert(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchInsert(engine, t) -} - -func BenchmarkSqlite3NoCacheFind(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFind(engine, t) -} - -func BenchmarkSqlite3NoCacheFindPtr(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - //engine.ShowSQL = true - doBenchFindPtr(engine, t) -} - -func BenchmarkSqlite3CacheInsert(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - doBenchInsert(engine, t) -} - -func BenchmarkSqlite3CacheFind(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - doBenchFind(engine, t) -} - -func BenchmarkSqlite3CacheFindPtr(t *testing.B) { - t.StopTimer() - engine, err := newSqlite3Engine() - defer engine.Close() - if err != nil { - t.Error(err) - return - } - engine.SetDefaultCacher(xorm.NewLRUCacher(caches.NewMemoryStore(), 1000)) - doBenchFindPtr(engine, t) -} diff --git a/tests/testdata/mysql_ddl.sql b/tests/testdata/mysql_ddl.sql deleted file mode 100644 index b1fcca5b..00000000 --- a/tests/testdata/mysql_ddl.sql +++ /dev/null @@ -1,8 +0,0 @@ -DROP DATABASE xorm_test; -DROP DATABASE xorm_test1; -DROP DATABASE xorm_test2; -DROP DATABASE xorm_test3; -CREATE DATABASE IF NOT EXISTS xorm_test CHARACTER SET utf8 COLLATE utf8_general_ci; -CREATE DATABASE IF NOT EXISTS xorm_test1 CHARACTER SET utf8 COLLATE utf8_general_ci; -CREATE DATABASE IF NOT EXISTS xorm_test2 CHARACTER SET utf8 COLLATE utf8_general_ci; -CREATE DATABASE IF NOT EXISTS xorm_test3 CHARACTER SET utf8 COLLATE utf8_general_ci; From a0919b5371bb2199d7ed07effef829c8c730e87b Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Fri, 11 Apr 2014 17:29:00 +0800 Subject: [PATCH 26/55] remove xorm dir as it's relocated to github.com/go-xorm/cmd --- xorm/.gopmfile | 2 - xorm/README.md | 62 ------ xorm/c++.go | 66 ------- xorm/cmd.go | 78 -------- xorm/go.go | 261 ------------------------- xorm/lang.go | 51 ----- xorm/reverse.go | 288 ---------------------------- xorm/shell.go | 147 -------------- xorm/templates/c++/class.h.tpl | 21 -- xorm/templates/c++/config | 1 - xorm/templates/go/config | 1 - xorm/templates/go/struct.go.tpl | 14 -- xorm/templates/goxorm/config | 3 - xorm/templates/goxorm/struct.go.tpl | 18 -- xorm/xorm.go | 162 ---------------- 15 files changed, 1175 deletions(-) delete mode 100644 xorm/.gopmfile delete mode 100644 xorm/README.md delete mode 100644 xorm/c++.go delete mode 100644 xorm/cmd.go delete mode 100644 xorm/go.go delete mode 100644 xorm/lang.go delete mode 100644 xorm/reverse.go delete mode 100644 xorm/shell.go delete mode 100644 xorm/templates/c++/class.h.tpl delete mode 100644 xorm/templates/c++/config delete mode 100644 xorm/templates/go/config delete mode 100644 xorm/templates/go/struct.go.tpl delete mode 100644 xorm/templates/goxorm/config delete mode 100644 xorm/templates/goxorm/struct.go.tpl delete mode 100644 xorm/xorm.go diff --git a/xorm/.gopmfile b/xorm/.gopmfile deleted file mode 100644 index 6fcf08b9..00000000 --- a/xorm/.gopmfile +++ /dev/null @@ -1,2 +0,0 @@ -[deps] -github.com/go-xorm/xorm=../ \ No newline at end of file diff --git a/xorm/README.md b/xorm/README.md deleted file mode 100644 index f4dbc9e6..00000000 --- a/xorm/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# xorm tools - - -xorm tools is a set of tools for database operation. - -## Install - -`go get github.com/go-xorm/xorm/xorm` - -and you should install the depends below: - -* github.com/go-xorm/xorm - -* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) - -* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) - -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - -* Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq) - - -## Reverse - -After you installed the tool, you can type - -`xorm help reverse` - -to get help - -example: - -sqlite: -`xorm reverse sqite3 test.db templates/goxorm` - -mysql: -`xorm reverse mysql root:@/xorm_test?charset=utf8 templates/goxorm` - -mymysql: -`xorm reverse mymysql xorm_test2/root/ templates/goxorm` - -postgres: -`xorm reverse postgres "dbname=xorm_test sslmode=disable" templates/goxorm` - -will generated go files in `./model` directory - -## Template and Config - -Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three of default templates. In template directory, we can put a config file to control how to generating. - -```` -lang=go -genJson=1 -``` - -lang must be go or c++ now. -genJson can be 1 or 0, if 1 then the struct will have json tag. - -## LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/xorm/c++.go b/xorm/c++.go deleted file mode 100644 index f5a0907a..00000000 --- a/xorm/c++.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -import ( - //"fmt" - "strings" - "text/template" - - "github.com/go-xorm/core" -) - -var ( - CPlusTmpl LangTmpl = LangTmpl{ - template.FuncMap{"Mapper": mapper.Table2Obj, - "Type": cPlusTypeStr, - "UnTitle": unTitle, - }, - nil, - genCPlusImports, - } -) - -func cPlusTypeStr(col *core.Column) string { - tp := col.SQLType - name := strings.ToUpper(tp.Name) - switch name { - case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.Serial: - return "int" - case core.BigInt, core.BigSerial: - return "__int64" - case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText: - return "tstring" - case core.Date, core.DateTime, core.Time, core.TimeStamp: - return "time_t" - case core.Decimal, core.Numeric: - return "tstring" - case core.Real, core.Float: - return "float" - case core.Double: - return "double" - case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea: - return "tstring" - case core.Bool: - return "bool" - default: - return "tstring" - } - return "" -} - -func genCPlusImports(tables []*core.Table) map[string]string { - imports := make(map[string]string) - - for _, table := range tables { - for _, col := range table.Columns() { - switch cPlusTypeStr(col) { - case "time_t": - imports[``] = `` - case "tstring": - imports[""] = "" - //case "__int64": - // imports[""] = "" - } - } - } - return imports -} diff --git a/xorm/cmd.go b/xorm/cmd.go deleted file mode 100644 index cb326f15..00000000 --- a/xorm/cmd.go +++ /dev/null @@ -1,78 +0,0 @@ -package main - -import ( - "fmt" - "os" - "strings" -) - -// A Command is an implementation of a go command -// like go build or go fix. -type Command struct { - // Run runs the command. - // The args are the arguments after the command name. - Run func(cmd *Command, args []string) - - // UsageLine is the one-line usage message. - // The first word in the line is taken to be the command name. - UsageLine string - - // Short is the short description shown in the 'go help' output. - Short string - - // Long is the long message shown in the 'go help ' output. - Long string - - // Flag is a set of flags specific to this command. - Flags map[string]bool -} - -// Name returns the command's name: the first word in the usage line. -func (c *Command) Name() string { - name := c.UsageLine - i := strings.Index(name, " ") - if i >= 0 { - name = name[:i] - } - return name -} - -func (c *Command) Usage() { - fmt.Fprintf(os.Stderr, "usage: %s\n\n", c.UsageLine) - fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long)) - os.Exit(2) -} - -// Runnable reports whether the command can be run; otherwise -// it is a documentation pseudo-command such as importpath. -func (c *Command) Runnable() bool { - return c.Run != nil -} - -// checkFlags checks if the flag exists with correct format. -func checkFlags(flags map[string]bool, args []string, print func(string)) int { - num := 0 // Number of valid flags, use to cut out. - for i, f := range args { - // Check flag prefix '-'. - if !strings.HasPrefix(f, "-") { - // Not a flag, finish check process. - break - } - - // Check if it a valid flag. - if v, ok := flags[f]; ok { - flags[f] = !v - if !v { - print(f) - } else { - fmt.Println("DISABLE: " + f) - } - } else { - fmt.Printf("[ERRO] Unknown flag: %s.\n", f) - return -1 - } - num = i + 1 - } - - return num -} diff --git a/xorm/go.go b/xorm/go.go deleted file mode 100644 index 382b5b18..00000000 --- a/xorm/go.go +++ /dev/null @@ -1,261 +0,0 @@ -package main - -import ( - "errors" - "fmt" - "go/format" - "reflect" - "strings" - "text/template" - - "github.com/go-xorm/core" -) - -var ( - GoLangTmpl LangTmpl = LangTmpl{ - template.FuncMap{"Mapper": mapper.Table2Obj, - "Type": typestring, - "Tag": tag, - "UnTitle": unTitle, - "gt": gt, - "getCol": getCol, - }, - formatGo, - genGoImports, - } -) - -var ( - errBadComparisonType = errors.New("invalid type for comparison") - errBadComparison = errors.New("incompatible types for comparison") - errNoComparison = errors.New("missing argument for comparison") -) - -type kind int - -const ( - invalidKind kind = iota - boolKind - complexKind - intKind - floatKind - integerKind - stringKind - uintKind -) - -func basicKind(v reflect.Value) (kind, error) { - switch v.Kind() { - case reflect.Bool: - return boolKind, nil - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return intKind, nil - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return uintKind, nil - case reflect.Float32, reflect.Float64: - return floatKind, nil - case reflect.Complex64, reflect.Complex128: - return complexKind, nil - case reflect.String: - return stringKind, nil - } - return invalidKind, errBadComparisonType -} - -// eq evaluates the comparison a == b || a == c || ... -func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - if len(arg2) == 0 { - return false, errNoComparison - } - for _, arg := range arg2 { - v2 := reflect.ValueOf(arg) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind: - truth = v1.Bool() == v2.Bool() - case complexKind: - truth = v1.Complex() == v2.Complex() - case floatKind: - truth = v1.Float() == v2.Float() - case intKind: - truth = v1.Int() == v2.Int() - case stringKind: - truth = v1.String() == v2.String() - case uintKind: - truth = v1.Uint() == v2.Uint() - default: - panic("invalid kind") - } - if truth { - return true, nil - } - } - return false, nil -} - -// lt evaluates the comparison a < b. -func lt(arg1, arg2 interface{}) (bool, error) { - v1 := reflect.ValueOf(arg1) - k1, err := basicKind(v1) - if err != nil { - return false, err - } - v2 := reflect.ValueOf(arg2) - k2, err := basicKind(v2) - if err != nil { - return false, err - } - if k1 != k2 { - return false, errBadComparison - } - truth := false - switch k1 { - case boolKind, complexKind: - return false, errBadComparisonType - case floatKind: - truth = v1.Float() < v2.Float() - case intKind: - truth = v1.Int() < v2.Int() - case stringKind: - truth = v1.String() < v2.String() - case uintKind: - truth = v1.Uint() < v2.Uint() - default: - panic("invalid kind") - } - return truth, nil -} - -// le evaluates the comparison <= b. -func le(arg1, arg2 interface{}) (bool, error) { - // <= is < or ==. - lessThan, err := lt(arg1, arg2) - if lessThan || err != nil { - return lessThan, err - } - return eq(arg1, arg2) -} - -// gt evaluates the comparison a > b. -func gt(arg1, arg2 interface{}) (bool, error) { - // > is the inverse of <=. - lessOrEqual, err := le(arg1, arg2) - if err != nil { - return false, err - } - return !lessOrEqual, nil -} - -func getCol(cols map[string]*core.Column, name string) *core.Column { - return cols[name] -} - -func formatGo(src string) (string, error) { - source, err := format.Source([]byte(src)) - if err != nil { - return "", err - } - return string(source), nil -} - -func genGoImports(tables []*core.Table) map[string]string { - imports := make(map[string]string) - - for _, table := range tables { - for _, col := range table.Columns() { - if typestring(col) == "time.Time" { - imports["time"] = "time" - } - } - } - return imports -} - -func typestring(col *core.Column) string { - st := col.SQLType - t := core.SQLType2Type(st) - s := t.String() - if s == "[]uint8" { - return "[]byte" - } - return s -} - -func tag(table *core.Table, col *core.Column) string { - isNameId := (mapper.Table2Obj(col.Name) == "Id") - isIdPk := isNameId && typestring(col) == "int64" - - res := make([]string, 0) - if !col.Nullable { - if !isIdPk { - res = append(res, "not null") - } - } - if col.IsPrimaryKey { - if !isIdPk { - res = append(res, "pk") - } - } - if col.Default != "" { - res = append(res, "default "+col.Default) - } - if col.IsAutoIncrement { - if !isIdPk { - res = append(res, "autoincr") - } - } - if col.IsCreated { - res = append(res, "created") - } - if col.IsUpdated { - res = append(res, "updated") - } - for name, _ := range col.Indexes { - index := table.Indexes[name] - var uistr string - if index.Type == core.UniqueType { - uistr = "unique" - } else if index.Type == core.IndexType { - uistr = "index" - } - if len(index.Cols) > 1 { - uistr += "(" + index.Name + ")" - } - res = append(res, uistr) - } - - nstr := col.SQLType.Name - if col.Length != 0 { - if col.Length2 != 0 { - nstr += fmt.Sprintf("(%v,%v)", col.Length, col.Length2) - } else { - nstr += fmt.Sprintf("(%v)", col.Length) - } - } - res = append(res, nstr) - - var tags []string - if genJson { - tags = append(tags, "json:\""+col.Name+"\"") - } - if len(res) > 0 { - tags = append(tags, "xorm:\""+strings.Join(res, " ")+"\"") - } - if len(tags) > 0 { - return "`" + strings.Join(tags, " ") + "`" - } else { - return "" - } -} diff --git a/xorm/lang.go b/xorm/lang.go deleted file mode 100644 index 3af43feb..00000000 --- a/xorm/lang.go +++ /dev/null @@ -1,51 +0,0 @@ -package main - -import ( - "github.com/go-xorm/core" - "io/ioutil" - "strings" - "text/template" -) - -type LangTmpl struct { - Funcs template.FuncMap - Formater func(string) (string, error) - GenImports func([]*core.Table) map[string]string -} - -var ( - mapper = &core.SnakeMapper{} - langTmpls = map[string]LangTmpl{ - "go": GoLangTmpl, - "c++": CPlusTmpl, - } -) - -func loadConfig(f string) map[string]string { - bts, err := ioutil.ReadFile(f) - if err != nil { - return nil - } - configs := make(map[string]string) - lines := strings.Split(string(bts), "\n") - for _, line := range lines { - line = strings.TrimRight(line, "\r") - vs := strings.Split(line, "=") - if len(vs) == 2 { - configs[strings.TrimSpace(vs[0])] = strings.TrimSpace(vs[1]) - } - } - return configs -} - -func unTitle(src string) string { - if src == "" { - return "" - } - - if len(src) == 1 { - return strings.ToLower(string(src[0])) - } else { - return strings.ToLower(string(src[0])) + src[1:] - } -} diff --git a/xorm/reverse.go b/xorm/reverse.go deleted file mode 100644 index 1db8f115..00000000 --- a/xorm/reverse.go +++ /dev/null @@ -1,288 +0,0 @@ -package main - -import ( - "bytes" - "fmt" - "io/ioutil" - "os" - "path" - "path/filepath" - "strconv" - "strings" //[SWH|+] - "text/template" - - _ "github.com/bylevel/pq" - "github.com/dvirsky/go-pylog/logging" - _ "github.com/go-sql-driver/mysql" - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" - - _ "github.com/mattn/go-sqlite3" - _ "github.com/ziutek/mymysql/godrv" -) - -var CmdReverse = &Command{ - UsageLine: "reverse [-m] driverName datasourceName tmplPath [generatedPath]", - Short: "reverse a db to codes", - Long: ` -according database's tables and columns to generate codes for Go, C++ and etc. - - -m Generated one go file for every table - driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres - datasourceName Database connection uri, for detail infomation please visit driver's project page - tmplPath Template dir for generated. the default templates dir has provide 1 template - generatedPath This parameter is optional, if blank, the default value is model, then will - generated all codes in model dir -`, -} - -func init() { - CmdReverse.Run = runReverse - CmdReverse.Flags = map[string]bool{ - "-s": false, - "-l": false, - } -} - -var ( - genJson bool = false -) - -func printReversePrompt(flag string) { -} - -type Tmpl struct { - Tables []*core.Table - Imports map[string]string - Model string -} - -func dirExists(dir string) bool { - d, e := os.Stat(dir) - switch { - case e != nil: - return false - case !d.IsDir(): - return false - } - - return true -} - -func runReverse(cmd *Command, args []string) { - num := checkFlags(cmd.Flags, args, printReversePrompt) - if num == -1 { - return - } - args = args[num:] - - if len(args) < 3 { - fmt.Println("params error, please see xorm help reverse") - return - } - - var isMultiFile bool = true - if use, ok := cmd.Flags["-s"]; ok { - isMultiFile = !use - } - - curPath, err := os.Getwd() - if err != nil { - fmt.Println(curPath) - return - } - - var genDir string - var model string - if len(args) == 4 { - genDir, err = filepath.Abs(args[3]) - if err != nil { - fmt.Println(err) - return - } - - //[SWH|+] 经测试,path.Base不能解析windows下的“\”,需要替换为“/” - genDir = strings.Replace(genDir, "\\", "/", -1) - model = path.Base(genDir) - } else { - model = "model" - genDir = path.Join(curPath, model) - } - - dir, err := filepath.Abs(args[2]) - if err != nil { - logging.Error("%v", err) - return - } - - if !dirExists(dir) { - logging.Error("Template %v path is not exist", dir) - return - } - - var langTmpl LangTmpl - var ok bool - var lang string = "go" - var prefix string = "" //[SWH|+] - - cfgPath := path.Join(dir, "config") - info, err := os.Stat(cfgPath) - var configs map[string]string - if err == nil && !info.IsDir() { - configs = loadConfig(cfgPath) - if l, ok := configs["lang"]; ok { - lang = l - } - if j, ok := configs["genJson"]; ok { - genJson, err = strconv.ParseBool(j) - } - - //[SWH|+] - if j, ok := configs["prefix"]; ok { - prefix = j - } - } - - if langTmpl, ok = langTmpls[lang]; !ok { - fmt.Println("Unsupported programing language", lang) - return - } - - os.MkdirAll(genDir, os.ModePerm) - - Orm, err := xorm.NewEngine(args[0], args[1]) - if err != nil { - logging.Error("%v", err) - return - } - - tables, err := Orm.DBMetas() - if err != nil { - logging.Error("%v", err) - return - } - - filepath.Walk(dir, func(f string, info os.FileInfo, err error) error { - if info.IsDir() { - return nil - } - - if info.Name() == "config" { - return nil - } - - bs, err := ioutil.ReadFile(f) - if err != nil { - logging.Error("%v", err) - return err - } - - t := template.New(f) - t.Funcs(langTmpl.Funcs) - - tmpl, err := t.Parse(string(bs)) - if err != nil { - logging.Error("%v", err) - return err - } - - var w *os.File - fileName := info.Name() - newFileName := fileName[:len(fileName)-4] - ext := path.Ext(newFileName) - - if !isMultiFile { - w, err = os.OpenFile(path.Join(genDir, newFileName), os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - logging.Error("%v", err) - return err - } - - imports := langTmpl.GenImports(tables) - - tbls := make([]*core.Table, 0) - for _, table := range tables { - //[SWH|+] - if prefix != "" { - table.Name = strings.TrimPrefix(table.Name, prefix) - } - tbls = append(tbls, table) - } - - newbytes := bytes.NewBufferString("") - - t := &Tmpl{Tables: tbls, Imports: imports, Model: model} - err = tmpl.Execute(newbytes, t) - if err != nil { - logging.Error("%v", err) - return err - } - - tplcontent, err := ioutil.ReadAll(newbytes) - if err != nil { - logging.Error("%v", err) - return err - } - var source string - if langTmpl.Formater != nil { - source, err = langTmpl.Formater(string(tplcontent)) - if err != nil { - logging.Error("%v", err) - return err - } - } else { - source = string(tplcontent) - } - - w.WriteString(source) - w.Close() - } else { - for _, table := range tables { - //[SWH|+] - if prefix != "" { - table.Name = strings.TrimPrefix(table.Name, prefix) - } - // imports - tbs := []*core.Table{table} - imports := langTmpl.GenImports(tbs) - - w, err := os.OpenFile(path.Join(genDir, unTitle(mapper.Table2Obj(table.Name))+ext), os.O_RDWR|os.O_CREATE, 0600) - if err != nil { - logging.Error("%v", err) - return err - } - - newbytes := bytes.NewBufferString("") - - t := &Tmpl{Tables: tbs, Imports: imports, Model: model} - err = tmpl.Execute(newbytes, t) - if err != nil { - logging.Error("%v", err) - return err - } - - tplcontent, err := ioutil.ReadAll(newbytes) - if err != nil { - logging.Error("%v", err) - return err - } - var source string - if langTmpl.Formater != nil { - source, err = langTmpl.Formater(string(tplcontent)) - if err != nil { - logging.Error("%v-%v", err, string(tplcontent)) - return err - } - } else { - source = string(tplcontent) - } - - w.WriteString(source) - w.Close() - } - } - - return nil - }) - -} diff --git a/xorm/shell.go b/xorm/shell.go deleted file mode 100644 index 5258e4c4..00000000 --- a/xorm/shell.go +++ /dev/null @@ -1,147 +0,0 @@ -package main - -import ( - "fmt" - "github.com/go-xorm/xorm" - "strings" -) - -var CmdShell = &Command{ - UsageLine: "shell driverName datasourceName", - Short: "a general shell to operate all kinds of database", - Long: ` -general database's shell for sqlite3, mysql, postgres. - - driverName Database driver name, now supported four: mysql mymysql sqlite3 postgres - datasourceName Database connection uri, for detail infomation please visit driver's project page -`, -} - -func init() { - CmdShell.Run = runShell - CmdShell.Flags = map[string]bool{} -} - -var engine *xorm.Engine - -func shellHelp() { - fmt.Println(` - show tables show all tables - columns show table's column info - indexes show table's index info - exit exit shell - source exec sql file to current database - dump [-nodata] dump structs or records to sql file - help show this document - SQL statement - `) -} - -func runShell(cmd *Command, args []string) { - if len(args) != 2 { - fmt.Println("params error, please see xorm help shell") - return - } - - var err error - engine, err = xorm.NewEngine(args[0], args[1]) - if err != nil { - fmt.Println(err) - return - } - - err = engine.Ping() - if err != nil { - fmt.Println(err) - return - } - - var scmd string - fmt.Print("xorm$ ") - for { - var input string - _, err := fmt.Scan(&input) - if err != nil { - fmt.Println(err) - continue - } - if strings.ToLower(input) == "exit" { - fmt.Println("bye") - return - } - if !strings.HasSuffix(input, ";") { - scmd = scmd + " " + input - continue - } - scmd = scmd + " " + input - lcmd := strings.TrimSpace(strings.ToLower(scmd)) - if strings.HasPrefix(lcmd, "select") { - res, err := engine.Query(scmd + "\n") - if err != nil { - fmt.Println(err) - } else { - if len(res) <= 0 { - fmt.Println("no records") - } else { - columns := make(map[string]int) - for k, _ := range res[0] { - columns[k] = len(k) - } - - for _, m := range res { - for k, s := range m { - l := len(string(s)) - if l > columns[k] { - columns[k] = l - } - } - } - - var maxlen = 0 - for _, l := range columns { - maxlen = maxlen + l + 3 - } - maxlen = maxlen + 1 - - fmt.Println(strings.Repeat("-", maxlen)) - fmt.Print("|") - slice := make([]string, 0) - for k, l := range columns { - fmt.Print(" " + k + " ") - fmt.Print(strings.Repeat(" ", l-len(k))) - fmt.Print("|") - slice = append(slice, k) - } - fmt.Print("\n") - for _, r := range res { - fmt.Print("|") - for _, k := range slice { - fmt.Print(" " + string(r[k]) + " ") - fmt.Print(strings.Repeat(" ", columns[k]-len(string(r[k])))) - fmt.Print("|") - } - fmt.Print("\n") - } - fmt.Println(strings.Repeat("-", maxlen)) - //fmt.Println(res) - } - } - } else if lcmd == "show tables;" { - /*tables, err := engine.DBMetas() - if err != nil { - fmt.Println(err) - } else { - - }*/ - } else { - cnt, err := engine.Exec(scmd) - if err != nil { - fmt.Println(err) - } else { - fmt.Printf("%d records changed.\n", cnt) - } - } - scmd = "" - fmt.Print("xorm$ ") - } -} diff --git a/xorm/templates/c++/class.h.tpl b/xorm/templates/c++/class.h.tpl deleted file mode 100644 index 50a32ff8..00000000 --- a/xorm/templates/c++/class.h.tpl +++ /dev/null @@ -1,21 +0,0 @@ -{{ range .Imports}} -#include {{.}} -{{ end }} - -{{range .Tables}}class {{Mapper .Name}} { -{{$table := .}} -public: -{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} Get{{Mapper .Name}}() { - return this->m_{{UnTitle $name}}; - } - - void Set{{$name}}({{Type .}} {{UnTitle $name}}) { - this->m_{{UnTitle $name}} = {{UnTitle $name}}; - } - -{{end}}private: -{{range .Columns}}{{$name := Mapper .Name}} {{Type .}} m_{{UnTitle $name}}; -{{end}} -} - -{{end}} \ No newline at end of file diff --git a/xorm/templates/c++/config b/xorm/templates/c++/config deleted file mode 100644 index 4965bae3..00000000 --- a/xorm/templates/c++/config +++ /dev/null @@ -1 +0,0 @@ -lang=c++ \ No newline at end of file diff --git a/xorm/templates/go/config b/xorm/templates/go/config deleted file mode 100644 index 6fdeea2b..00000000 --- a/xorm/templates/go/config +++ /dev/null @@ -1 +0,0 @@ -lang=go \ No newline at end of file diff --git a/xorm/templates/go/struct.go.tpl b/xorm/templates/go/struct.go.tpl deleted file mode 100644 index 8e59d688..00000000 --- a/xorm/templates/go/struct.go.tpl +++ /dev/null @@ -1,14 +0,0 @@ -package {{.Model}} - -import ( - {{range .Imports}}"{{.}}"{{end}} -) - -{{range .Tables}} -type {{Mapper .Name}} struct { -{{$table := .}} -{{range .Columns}} {{Mapper .Name}} {{Type .}} -{{end}} -} - -{{end}} \ No newline at end of file diff --git a/xorm/templates/goxorm/config b/xorm/templates/goxorm/config deleted file mode 100644 index 5d7bf321..00000000 --- a/xorm/templates/goxorm/config +++ /dev/null @@ -1,3 +0,0 @@ -lang=go -genJson=0 -prefix=cos_ diff --git a/xorm/templates/goxorm/struct.go.tpl b/xorm/templates/goxorm/struct.go.tpl deleted file mode 100644 index 91b00854..00000000 --- a/xorm/templates/goxorm/struct.go.tpl +++ /dev/null @@ -1,18 +0,0 @@ -package {{.Model}} - -{{$ilen := len .Imports}} -{{if gt $ilen 0}} -import ( - {{range .Imports}}"{{.}}"{{end}} -) -{{end}} - -{{range .Tables}} -type {{Mapper .Name}} struct { -{{$table := .}} -{{$columns := .Columns}} -{{range .ColumnsSeq}}{{$col := getCol $columns .}} {{Mapper $col.Name}} {{Type $col}} {{Tag $table $col}} -{{end}} -} - -{{end}} \ No newline at end of file diff --git a/xorm/xorm.go b/xorm/xorm.go deleted file mode 100644 index 11235e88..00000000 --- a/xorm/xorm.go +++ /dev/null @@ -1,162 +0,0 @@ -package main - -import ( - "fmt" - "github.com/dvirsky/go-pylog/logging" - "io" - "os" - "runtime" - "strings" - "sync" - "text/template" - "unicode" - "unicode/utf8" -) - -// +build go1.1 - -// Test that go1.1 tag above is included in builds. main.go refers to this definition. -const go11tag = true - -const version = "0.1" - -// Commands lists the available commands and help topics. -// The order here is the order in which they are printed by 'gopm help'. -var commands = []*Command{ - CmdReverse, - CmdShell, -} - -func init() { - runtime.GOMAXPROCS(runtime.NumCPU()) -} - -func main() { - logging.SetLevel(logging.ALL) - // Check length of arguments. - args := os.Args[1:] - if len(args) < 1 { - usage() - return - } - - // Show help documentation. - if args[0] == "help" { - help(args[1:]) - return - } - - // Check commands and run. - for _, comm := range commands { - if comm.Name() == args[0] && comm.Run != nil { - comm.Run(comm, args[1:]) - exit() - return - } - } - - fmt.Fprintf(os.Stderr, "xorm: unknown subcommand %q\nRun 'xorm help' for usage.\n", args[0]) - setExitStatus(2) - exit() -} - -var exitStatus = 0 -var exitMu sync.Mutex - -func setExitStatus(n int) { - exitMu.Lock() - if exitStatus < n { - exitStatus = n - } - exitMu.Unlock() -} - -var usageTemplate = `xorm is a database tool based xorm package. -Usage: - - xorm command [arguments] - -The commands are: -{{range .}}{{if .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} - -Use "xorm help [command]" for more information about a command. - -Additional help topics: -{{range .}}{{if not .Runnable}} - {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}} - -Use "xorm help [topic]" for more information about that topic. - -` - -var helpTemplate = `{{if .Runnable}}usage: xorm {{.UsageLine}} - -{{end}}{{.Long | trim}} -` - -// tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) { - t := template.New("top") - t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize}) - template.Must(t.Parse(text)) - if err := t.Execute(w, data); err != nil { - panic(err) - } -} - -func capitalize(s string) string { - if s == "" { - return s - } - r, n := utf8.DecodeRuneInString(s) - return string(unicode.ToTitle(r)) + s[n:] -} - -func printUsage(w io.Writer) { - tmpl(w, usageTemplate, commands) -} - -func usage() { - printUsage(os.Stderr) - os.Exit(2) -} - -// help implements the 'help' command. -func help(args []string) { - if len(args) == 0 { - printUsage(os.Stdout) - // not exit 2: succeeded at 'gopm help'. - return - } - if len(args) != 1 { - fmt.Fprintf(os.Stderr, "usage: xorm help command\n\nToo many arguments given.\n") - os.Exit(2) // failed at 'gopm help' - } - - arg := args[0] - - for _, cmd := range commands { - if cmd.Name() == arg { - tmpl(os.Stdout, helpTemplate, cmd) - // not exit 2: succeeded at 'gopm help cmd'. - return - } - } - - fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'xorm help'.\n", arg) - os.Exit(2) // failed at 'gopm help cmd' -} - -var atexitFuncs []func() - -func atexit(f func()) { - atexitFuncs = append(atexitFuncs, f) -} - -func exit() { - for _, f := range atexitFuncs { - f() - } - os.Exit(exitStatus) -} From 81c947b61b7290945679a5a33100297d35aeb514 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Fri, 11 Apr 2014 21:06:11 +0800 Subject: [PATCH 27/55] flattened dialects dir and register db dialect for assocaited registered driver --- dialects/mssql.go => mssql_dialect.go | 100 +++++++++---------- dialects/mysql.go => mysql_dialect.go | 74 +++++++------- dialects/oracle.go => oracle_dialect.go | 78 +++++++-------- dialects/postgres.go => postgres_dialect.go | 104 ++++++++++---------- dialects/sqlite3.go => sqlite3_dialect.go | 82 +++++++-------- xorm.go | 23 ++++- 6 files changed, 241 insertions(+), 220 deletions(-) rename dialects/mssql.go => mssql_dialect.go (76%) rename dialects/mysql.go => mysql_dialect.go (81%) rename dialects/oracle.go => oracle_dialect.go (71%) rename dialects/postgres.go => postgres_dialect.go (71%) rename dialects/sqlite3.go => sqlite3_dialect.go (71%) diff --git a/dialects/mssql.go b/mssql_dialect.go similarity index 76% rename from dialects/mssql.go rename to mssql_dialect.go index 10ec3ad7..0b85f6a9 100644 --- a/dialects/mssql.go +++ b/mssql_dialect.go @@ -1,4 +1,4 @@ -package dialects +package xorm import ( "errors" @@ -6,58 +6,58 @@ import ( "strconv" "strings" - . "github.com/go-xorm/core" + "github.com/go-xorm/core" ) -func init() { - RegisterDialect("mssql", &mssql{}) -} +// func init() { +// RegisterDialect("mssql", &mssql{}) +// } type mssql struct { - Base + core.Base } -func (db *mssql) Init(uri *Uri, drivername, dataSourceName string) error { +func (db *mssql) Init(uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(db, uri, drivername, dataSourceName) } -func (db *mssql) SqlType(c *Column) string { +func (db *mssql) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { - case Bool: - res = TinyInt - case Serial: + case core.Bool: + res = core.TinyInt + case core.Serial: c.IsAutoIncrement = true c.IsPrimaryKey = true c.Nullable = false - res = Int - case BigSerial: + res = core.Int + case core.BigSerial: c.IsAutoIncrement = true c.IsPrimaryKey = true c.Nullable = false - res = BigInt - case Bytea, Blob, Binary, TinyBlob, MediumBlob, LongBlob: - res = VarBinary + res = core.BigInt + case core.Bytea, core.Blob, core.Binary, core.TinyBlob, core.MediumBlob, core.LongBlob: + res = core.VarBinary if c.Length == 0 { c.Length = 50 } - case TimeStamp: - res = DateTime - case TimeStampz: + case core.TimeStamp: + res = core.DateTime + case core.TimeStampz: res = "DATETIMEOFFSET" c.Length = 7 - case MediumInt: - res = Int - case MediumText, TinyText, LongText: - res = Text - case Double: - res = Real + case core.MediumInt: + res = core.Int + case core.MediumText, core.TinyText, core.LongText: + res = core.Text + case core.Double: + res = core.Real default: res = t } - if res == Int { - return Int + if res == core.Int { + return core.Int } var hasLen1 bool = (c.Length > 0) @@ -118,12 +118,12 @@ func (db *mssql) TableCheckSql(tableName string) (string, []interface{}) { return sql, args } -func (db *mssql) GetColumns(tableName string) ([]string, map[string]*Column, error) { +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 + `')` - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } @@ -133,7 +133,7 @@ where a.object_id=object_id('` + tableName + `')` if err != nil { return nil, nil, err } - cols := make(map[string]*Column) + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { var name, ctype, precision, scale string @@ -143,7 +143,7 @@ where a.object_id=object_id('` + tableName + `')` return nil, nil, err } - col := new(Column) + col := new(core.Column) col.Indexes = make(map[string]bool) col.Length = maxLen col.Name = strings.Trim(name, "` ") @@ -151,14 +151,14 @@ where a.object_id=object_id('` + tableName + `')` ct := strings.ToUpper(ctype) switch ct { case "DATETIMEOFFSET": - col.SQLType = SQLType{TimeStampz, 0, 0} + col.SQLType = core.SQLType{core.TimeStampz, 0, 0} case "NVARCHAR": - col.SQLType = SQLType{Varchar, 0, 0} + col.SQLType = core.SQLType{core.Varchar, 0, 0} case "IMAGE": - col.SQLType = SQLType{VarBinary, 0, 0} + col.SQLType = core.SQLType{core.VarBinary, 0, 0} default: - if _, ok := SqlTypes[ct]; ok { - col.SQLType = SQLType{ct, 0, 0} + if _, ok := core.SqlTypes[ct]; ok { + col.SQLType = core.SQLType{ct, 0, 0} } else { return nil, nil, errors.New(fmt.Sprintf("unknow colType %v for %v - %v", ct, tableName, col.Name)) @@ -180,10 +180,10 @@ where a.object_id=object_id('` + tableName + `')` return colSeq, cols, nil } -func (db *mssql) GetTables() ([]*Table, error) { +func (db *mssql) GetTables() ([]*core.Table, error) { args := []interface{}{} s := `select name from sysobjects where xtype ='U'` - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -193,9 +193,9 @@ func (db *mssql) GetTables() ([]*Table, error) { return nil, err } - tables := make([]*Table, 0) + tables := make([]*core.Table, 0) for rows.Next() { - table := NewEmptyTable() + table := core.NewEmptyTable() var name string err = rows.Scan(&name) if err != nil { @@ -207,7 +207,7 @@ func (db *mssql) GetTables() ([]*Table, error) { return tables, nil } -func (db *mssql) GetIndexes(tableName string) (map[string]*Index, error) { +func (db *mssql) GetIndexes(tableName string) (map[string]*core.Index, error) { args := []interface{}{tableName} s := `SELECT IXS.NAME AS [INDEX_NAME], @@ -223,7 +223,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) =? ` - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -233,7 +233,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? return nil, err } - indexes := make(map[string]*Index, 0) + indexes := make(map[string]*core.Index, 0) for rows.Next() { var indexType int var indexName, colName, isUnique string @@ -249,9 +249,9 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? } if i { - indexType = UniqueType + indexType = core.UniqueType } else { - indexType = IndexType + indexType = core.IndexType } colName = strings.Trim(colName, "` ") @@ -260,10 +260,10 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? indexName = indexName[5+len(tableName) : len(indexName)] } - var index *Index + var index *core.Index var ok bool if index, ok = indexes[indexName]; !ok { - index = new(Index) + index = new(core.Index) index.Type = indexType index.Name = indexName indexes[indexName] = index @@ -273,7 +273,7 @@ WHERE IXS.TYPE_DESC='NONCLUSTERED' and OBJECT_NAME(IXS.OBJECT_ID) =? return indexes, nil } -func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset string) string { +func (db *mssql) CreateTablSql(table *core.Table, tableName, storeEngine, charset string) string { var sql string if tableName == "" { tableName = table.Name @@ -307,6 +307,6 @@ func (db *mssql) CreateTablSql(table *Table, tableName, storeEngine, charset str return sql } -func (db *mssql) Filters() []Filter { - return []Filter{&IdFilter{}, &QuoteFilter{}} +func (db *mssql) Filters() []core.Filter { + return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}} } diff --git a/dialects/mysql.go b/mysql_dialect.go similarity index 81% rename from dialects/mysql.go rename to mysql_dialect.go index ffba757c..52a8a15e 100644 --- a/dialects/mysql.go +++ b/mysql_dialect.go @@ -1,4 +1,4 @@ -package dialects +package xorm import ( "crypto/tls" @@ -8,15 +8,15 @@ import ( "strings" "time" - . "github.com/go-xorm/core" + "github.com/go-xorm/core" ) -func init() { - RegisterDialect("mysql", &mysql{}) -} +// func init() { +// RegisterDialect("mysql", &mysql{}) +// } type mysql struct { - Base + core.Base net string addr string params map[string]string @@ -28,30 +28,30 @@ type mysql struct { clientFoundRows bool } -func (db *mysql) Init(uri *Uri, drivername, dataSourceName string) error { +func (db *mysql) Init(uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(db, uri, drivername, dataSourceName) } -func (db *mysql) SqlType(c *Column) string { +func (db *mysql) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { - case Bool: - res = TinyInt + case core.Bool: + res = core.TinyInt c.Length = 1 - case Serial: + case core.Serial: c.IsAutoIncrement = true c.IsPrimaryKey = true c.Nullable = false - res = Int - case BigSerial: + res = core.Int + case core.BigSerial: c.IsAutoIncrement = true c.IsPrimaryKey = true c.Nullable = false - res = BigInt - case Bytea: - res = Blob - case TimeStampz: - res = Char + res = core.BigInt + case core.Bytea: + res = core.Blob + case core.TimeStampz: + res = core.Char c.Length = 64 default: res = t @@ -110,11 +110,11 @@ func (db *mysql) TableCheckSql(tableName string) (string, []interface{}) { return sql, args } -func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, error) { +func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { 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` = ?" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } @@ -123,10 +123,10 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, err if err != nil { return nil, nil, err } - cols := make(map[string]*Column) + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { - col := new(Column) + col := new(core.Column) col.Indexes = make(map[string]bool) var columnName, isNullable, colType, colKey, extra string @@ -164,8 +164,8 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, err colType = strings.ToUpper(colName) col.Length = len1 col.Length2 = len2 - if _, ok := SqlTypes[colType]; ok { - col.SQLType = SQLType{colType, len1, len2} + if _, ok := core.SqlTypes[colType]; ok { + col.SQLType = core.SQLType{colType, len1, len2} } else { return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", colType)) } @@ -192,10 +192,10 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*Column, err return colSeq, cols, nil } -func (db *mysql) GetTables() ([]*Table, error) { +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`=?" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -205,9 +205,9 @@ func (db *mysql) GetTables() ([]*Table, error) { return nil, err } - tables := make([]*Table, 0) + tables := make([]*core.Table, 0) for rows.Next() { - table := NewEmptyTable() + table := core.NewEmptyTable() var name, engine, tableRows string var autoIncr *string err = rows.Scan(&name, &engine, &tableRows, &autoIncr) @@ -221,10 +221,10 @@ func (db *mysql) GetTables() ([]*Table, error) { return tables, nil } -func (db *mysql) GetIndexes(tableName string) (map[string]*Index, 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` = ?" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -234,7 +234,7 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) { return nil, err } - indexes := make(map[string]*Index, 0) + indexes := make(map[string]*core.Index, 0) for rows.Next() { var indexType int var indexName, colName, nonUnique string @@ -248,9 +248,9 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) { } if "YES" == nonUnique || nonUnique == "1" { - indexType = IndexType + indexType = core.IndexType } else { - indexType = UniqueType + indexType = core.UniqueType } colName = strings.Trim(colName, "` ") @@ -259,10 +259,10 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) { indexName = indexName[5+len(tableName) : len(indexName)] } - var index *Index + var index *core.Index var ok bool if index, ok = indexes[indexName]; !ok { - index = new(Index) + index = new(core.Index) index.Type = indexType index.Name = indexName indexes[indexName] = index @@ -272,6 +272,6 @@ func (db *mysql) GetIndexes(tableName string) (map[string]*Index, error) { return indexes, nil } -func (db *mysql) Filters() []Filter { - return []Filter{&IdFilter{}} +func (db *mysql) Filters() []core.Filter { + return []core.Filter{&core.IdFilter{}} } diff --git a/dialects/oracle.go b/oracle_dialect.go similarity index 71% rename from dialects/oracle.go rename to oracle_dialect.go index b86a74fc..febd318e 100644 --- a/dialects/oracle.go +++ b/oracle_dialect.go @@ -1,4 +1,4 @@ -package dialects +package xorm import ( "errors" @@ -6,37 +6,37 @@ import ( "strconv" "strings" - . "github.com/go-xorm/core" + "github.com/go-xorm/core" ) -func init() { - RegisterDialect("oracle", &oracle{}) -} +// func init() { +// RegisterDialect("oracle", &oracle{}) +// } type oracle struct { - Base + core.Base } -func (db *oracle) Init(uri *Uri, drivername, dataSourceName string) error { +func (db *oracle) Init(uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(db, uri, drivername, dataSourceName) } -func (db *oracle) SqlType(c *Column) string { +func (db *oracle) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { - case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: + case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool, core.Serial, core.BigSerial: return "NUMBER" - case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: - return Blob - case Time, DateTime, TimeStamp: - res = TimeStamp - case TimeStampz: + case core.Binary, core.VarBinary, core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob, core.Bytea: + return core.Blob + case core.Time, core.DateTime, core.TimeStamp: + res = core.TimeStamp + case core.TimeStampz: res = "TIMESTAMP WITH TIME ZONE" - case Float, Double, Numeric, Decimal: + case core.Float, core.Double, core.Numeric, core.Decimal: res = "NUMBER" - case Text, MediumText, LongText: + case core.Text, core.MediumText, core.LongText: res = "CLOB" - case Char, Varchar, TinyText: + case core.Char, core.Varchar, core.TinyText: return "VARCHAR2" default: res = t @@ -93,12 +93,12 @@ func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface " AND column_name = ?", args } -func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) { +func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{strings.ToUpper(tableName)} s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } @@ -109,10 +109,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er } defer rows.Close() - cols := make(map[string]*Column) + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { - col := new(Column) + col := new(core.Column) col.Indexes = make(map[string]bool) var colName, colDefault, nullable, dataType, dataPrecision, dataScale string @@ -135,13 +135,13 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er switch dataType { case "VARCHAR2": - col.SQLType = SQLType{Varchar, 0, 0} + col.SQLType = core.SQLType{core.Varchar, 0, 0} case "TIMESTAMP WITH TIME ZONE": - col.SQLType = SQLType{TimeStampz, 0, 0} + col.SQLType = core.SQLType{core.TimeStampz, 0, 0} default: - col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0} + col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0} } - if _, ok := SqlTypes[col.SQLType.Name]; !ok { + if _, ok := core.SqlTypes[col.SQLType.Name]; !ok { return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) } @@ -163,10 +163,10 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, er return colSeq, cols, nil } -func (db *oracle) GetTables() ([]*Table, error) { +func (db *oracle) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT table_name FROM user_tables" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -176,9 +176,9 @@ func (db *oracle) GetTables() ([]*Table, error) { return nil, err } - tables := make([]*Table, 0) + tables := make([]*core.Table, 0) for rows.Next() { - table := NewEmptyTable() + table := core.NewEmptyTable() err = rows.Scan(&table.Name) if err != nil { return nil, err @@ -189,12 +189,12 @@ func (db *oracle) GetTables() ([]*Table, error) { return tables, nil } -func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { +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" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -205,7 +205,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { } defer rows.Close() - indexes := make(map[string]*Index, 0) + indexes := make(map[string]*core.Index, 0) for rows.Next() { var indexType int var indexName, colName, uniqueness string @@ -218,15 +218,15 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { indexName = strings.Trim(indexName, `" `) if uniqueness == "UNIQUE" { - indexType = UniqueType + indexType = core.UniqueType } else { - indexType = IndexType + indexType = core.IndexType } - var index *Index + var index *core.Index var ok bool if index, ok = indexes[indexName]; !ok { - index = new(Index) + index = new(core.Index) index.Type = indexType index.Name = indexName indexes[indexName] = index @@ -240,7 +240,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { type OracleSeqFilter struct { } -func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string { +func (s *OracleSeqFilter) Do(sql string, dialect core.Dialect, table *core.Table) string { counts := strings.Count(sql, "?") for i := 1; i <= counts; i++ { newstr := ":" + fmt.Sprintf("%v", i) @@ -249,6 +249,6 @@ func (s *OracleSeqFilter) Do(sql string, dialect Dialect, table *Table) string { return sql } -func (db *oracle) Filters() []Filter { - return []Filter{&QuoteFilter{}, &OracleSeqFilter{}, &IdFilter{}} +func (db *oracle) Filters() []core.Filter { + return []core.Filter{&core.QuoteFilter{}, &OracleSeqFilter{}, &core.IdFilter{}} } diff --git a/dialects/postgres.go b/postgres_dialect.go similarity index 71% rename from dialects/postgres.go rename to postgres_dialect.go index d365732e..58d39f9d 100644 --- a/dialects/postgres.go +++ b/postgres_dialect.go @@ -1,4 +1,4 @@ -package dialects +package xorm import ( "errors" @@ -6,53 +6,53 @@ import ( "strconv" "strings" - . "github.com/go-xorm/core" + "github.com/go-xorm/core" ) -func init() { - RegisterDialect("postgres", &postgres{}) -} +// func init() { +// RegisterDialect("postgres", &postgres{}) +// } type postgres struct { - Base + core.Base } -func (db *postgres) Init(uri *Uri, drivername, dataSourceName string) error { +func (db *postgres) Init(uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(db, uri, drivername, dataSourceName) } -func (db *postgres) SqlType(c *Column) string { +func (db *postgres) SqlType(c *core.Column) string { var res string switch t := c.SQLType.Name; t { - case TinyInt: - res = SmallInt + case core.TinyInt: + res = core.SmallInt return res - case MediumInt, Int, Integer: + case core.MediumInt, core.Int, core.Integer: if c.IsAutoIncrement { - return Serial + return core.Serial } - return Integer - case Serial, BigSerial: + return core.Integer + case core.Serial, core.BigSerial: c.IsAutoIncrement = true c.Nullable = false res = t - case Binary, VarBinary: - return Bytea - case DateTime: - res = TimeStamp - case TimeStampz: + case core.Binary, core.VarBinary: + return core.Bytea + case core.DateTime: + res = core.TimeStamp + case core.TimeStampz: return "timestamp with time zone" - case Float: - res = Real - case TinyText, MediumText, LongText: - res = Text - case Blob, TinyBlob, MediumBlob, LongBlob: - return Bytea - case Double: + case core.Float: + res = core.Real + case core.TinyText, core.MediumText, core.LongText: + res = core.Text + case core.Blob, core.TinyBlob, core.MediumBlob, core.LongBlob: + return core.Bytea + case core.Double: return "DOUBLE PRECISION" default: if c.IsAutoIncrement { - return Serial + return core.Serial } res = t } @@ -108,11 +108,11 @@ func (db *postgres) ColumnCheckSql(tableName, colName string) (string, []interfa " AND column_name = ?", args } -func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, error) { +func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Column, error) { args := []interface{}{tableName} s := "SELECT column_name, column_default, is_nullable, data_type, character_maximum_length" + ", numeric_precision, numeric_precision_radix FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } @@ -121,11 +121,11 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, if err != nil { return nil, nil, err } - cols := make(map[string]*Column) + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { - col := new(Column) + col := new(core.Column) col.Indexes = make(map[string]bool) var colName, isNullable, dataType string @@ -161,21 +161,21 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, switch dataType { case "character varying", "character": - col.SQLType = SQLType{Varchar, 0, 0} + col.SQLType = core.SQLType{core.Varchar, 0, 0} case "timestamp without time zone": - col.SQLType = SQLType{DateTime, 0, 0} + col.SQLType = core.SQLType{core.DateTime, 0, 0} case "timestamp with time zone": - col.SQLType = SQLType{TimeStampz, 0, 0} + col.SQLType = core.SQLType{core.TimeStampz, 0, 0} case "double precision": - col.SQLType = SQLType{Double, 0, 0} + col.SQLType = core.SQLType{core.Double, 0, 0} case "boolean": - col.SQLType = SQLType{Bool, 0, 0} + col.SQLType = core.SQLType{core.Bool, 0, 0} case "time without time zone": - col.SQLType = SQLType{Time, 0, 0} + col.SQLType = core.SQLType{core.Time, 0, 0} default: - col.SQLType = SQLType{strings.ToUpper(dataType), 0, 0} + col.SQLType = core.SQLType{strings.ToUpper(dataType), 0, 0} } - if _, ok := SqlTypes[col.SQLType.Name]; !ok { + if _, ok := core.SqlTypes[col.SQLType.Name]; !ok { return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", dataType)) } @@ -197,10 +197,10 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*Column, return colSeq, cols, nil } -func (db *postgres) GetTables() ([]*Table, error) { +func (db *postgres) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT tablename FROM pg_tables where schemaname = 'public'" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -210,9 +210,9 @@ func (db *postgres) GetTables() ([]*Table, error) { return nil, err } - tables := make([]*Table, 0) + tables := make([]*core.Table, 0) for rows.Next() { - table := NewEmptyTable() + table := core.NewEmptyTable() var name string err = rows.Scan(&name) if err != nil { @@ -224,11 +224,11 @@ func (db *postgres) GetTables() ([]*Table, error) { return tables, nil } -func (db *postgres) GetIndexes(tableName string) (map[string]*Index, 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" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -238,7 +238,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { return nil, err } - indexes := make(map[string]*Index, 0) + indexes := make(map[string]*core.Index, 0) for rows.Next() { var indexType int var indexName, indexdef string @@ -250,9 +250,9 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { indexName = strings.Trim(indexName, `" `) if strings.HasPrefix(indexdef, "CREATE UNIQUE INDEX") { - indexType = UniqueType + indexType = core.UniqueType } else { - indexType = IndexType + indexType = core.IndexType } cs := strings.Split(indexdef, "(") colNames = strings.Split(cs[1][0:len(cs[1])-1], ",") @@ -267,7 +267,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { } } - index := &Index{Name: indexName, Type: indexType, Cols: make([]string, 0)} + index := &core.Index{Name: indexName, Type: indexType, Cols: make([]string, 0)} for _, colName := range colNames { index.Cols = append(index.Cols, strings.Trim(colName, `" `)) } @@ -280,7 +280,7 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*Index, error) { type PgSeqFilter struct { } -func (s *PgSeqFilter) Do(sql string, dialect Dialect, table *Table) string { +func (s *PgSeqFilter) Do(sql string, dialect core.Dialect, table *core.Table) string { segs := strings.Split(sql, "?") size := len(segs) res := "" @@ -293,6 +293,6 @@ func (s *PgSeqFilter) Do(sql string, dialect Dialect, table *Table) string { return res } -func (db *postgres) Filters() []Filter { - return []Filter{&IdFilter{}, &QuoteFilter{}, &PgSeqFilter{}} +func (db *postgres) Filters() []core.Filter { + return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &PgSeqFilter{}} } diff --git a/dialects/sqlite3.go b/sqlite3_dialect.go similarity index 71% rename from dialects/sqlite3.go rename to sqlite3_dialect.go index aa829424..d5be5c72 100644 --- a/dialects/sqlite3.go +++ b/sqlite3_dialect.go @@ -1,44 +1,44 @@ -package dialects +package xorm import ( "strings" - . "github.com/go-xorm/core" + "github.com/go-xorm/core" ) -func init() { - RegisterDialect("sqlite3", &sqlite3{}) -} +// func init() { +// RegisterDialect("sqlite3", &sqlite3{}) +// } type sqlite3 struct { - Base + core.Base } -func (db *sqlite3) Init(uri *Uri, drivername, dataSourceName string) error { +func (db *sqlite3) Init(uri *core.Uri, drivername, dataSourceName string) error { return db.Base.Init(db, uri, drivername, dataSourceName) } -func (db *sqlite3) SqlType(c *Column) string { +func (db *sqlite3) SqlType(c *core.Column) string { switch t := c.SQLType.Name; t { - case Date, DateTime, TimeStamp, Time: - return Numeric - case TimeStampz: - return Text - case Char, Varchar, TinyText, Text, MediumText, LongText: - return Text - case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool: - return Integer - case Float, Double, Real: - return Real - case Decimal, Numeric: - return Numeric - case TinyBlob, Blob, MediumBlob, LongBlob, Bytea, Binary, VarBinary: - return Blob - case Serial, BigSerial: + case core.Date, core.DateTime, core.TimeStamp, core.Time: + return core.Numeric + case core.TimeStampz: + return core.Text + case core.Char, core.Varchar, core.TinyText, core.Text, core.MediumText, core.LongText: + return core.Text + case core.Bit, core.TinyInt, core.SmallInt, core.MediumInt, core.Int, core.Integer, core.BigInt, core.Bool: + return core.Integer + case core.Float, core.Double, core.Real: + return core.Real + case core.Decimal, core.Numeric: + return core.Numeric + case core.TinyBlob, core.Blob, core.MediumBlob, core.LongBlob, core.Bytea, core.Binary, core.VarBinary: + return core.Blob + case core.Serial, core.BigSerial: c.IsPrimaryKey = true c.IsAutoIncrement = true c.Nullable = false - return Integer + return core.Integer default: return t } @@ -84,10 +84,10 @@ func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interfac return sql, args } -func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, 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 = ?" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, nil, err } @@ -110,11 +110,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e nStart := strings.Index(name, "(") nEnd := strings.Index(name, ")") colCreates := strings.Split(name[nStart+1:nEnd], ",") - cols := make(map[string]*Column) + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for _, colStr := range colCreates { fields := strings.Fields(strings.TrimSpace(colStr)) - col := new(Column) + col := new(core.Column) col.Indexes = make(map[string]bool) col.Nullable = true for idx, field := range fields { @@ -122,7 +122,7 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e col.Name = strings.Trim(field, "`[] ") continue } else if idx == 1 { - col.SQLType = SQLType{field, 0, 0} + col.SQLType = core.SQLType{field, 0, 0} } switch field { case "PRIMARY": @@ -143,11 +143,11 @@ func (db *sqlite3) GetColumns(tableName string) ([]string, map[string]*Column, e return colSeq, cols, nil } -func (db *sqlite3) GetTables() ([]*Table, error) { +func (db *sqlite3) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT name FROM sqlite_master WHERE type='table'" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -158,9 +158,9 @@ func (db *sqlite3) GetTables() ([]*Table, error) { } defer rows.Close() - tables := make([]*Table, 0) + tables := make([]*core.Table, 0) for rows.Next() { - table := NewEmptyTable() + table := core.NewEmptyTable() err = rows.Scan(&table.Name) if err != nil { return nil, err @@ -173,10 +173,10 @@ func (db *sqlite3) GetTables() ([]*Table, error) { return tables, nil } -func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, 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 = ?" - cnn, err := Open(db.DriverName(), db.DataSourceName()) + cnn, err := core.Open(db.DriverName(), db.DataSourceName()) if err != nil { return nil, err } @@ -187,7 +187,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { } defer rows.Close() - indexes := make(map[string]*Index, 0) + indexes := make(map[string]*core.Index, 0) for rows.Next() { var sql string err = rows.Scan(&sql) @@ -199,7 +199,7 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { continue } - index := new(Index) + index := new(core.Index) nNStart := strings.Index(sql, "INDEX") nNEnd := strings.Index(sql, "ON") if nNStart == -1 || nNEnd == -1 { @@ -215,9 +215,9 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { } if strings.HasPrefix(sql, "CREATE UNIQUE INDEX") { - index.Type = UniqueType + index.Type = core.UniqueType } else { - index.Type = IndexType + index.Type = core.IndexType } nStart := strings.Index(sql, "(") @@ -234,6 +234,6 @@ func (db *sqlite3) GetIndexes(tableName string) (map[string]*Index, error) { return indexes, nil } -func (db *sqlite3) Filters() []Filter { - return []Filter{&IdFilter{}} +func (db *sqlite3) Filters() []core.Filter { + return []core.Filter{&core.IdFilter{}} } diff --git a/xorm.go b/xorm.go index ccd33091..705b61a0 100644 --- a/xorm.go +++ b/xorm.go @@ -1,6 +1,7 @@ package xorm import ( + "database/sql" "errors" "fmt" "os" @@ -11,7 +12,6 @@ import ( "github.com/go-xorm/core" "github.com/go-xorm/xorm/caches" - _ "github.com/go-xorm/xorm/dialects" _ "github.com/go-xorm/xorm/drivers" ) @@ -19,6 +19,27 @@ const ( Version string = "0.4" ) +func init() { + provided_dialects := map[string]struct { + dbType core.DbType + get func() core.Dialect + }{ + "odbc": {"mssql", func() core.Dialect { return &mssql{} }}, + "mysql": {"mysql", func() core.Dialect { return &mysql{} }}, + "mymysql": {"mysql", func() core.Dialect { return &mysql{} }}, + "oci8": {"oracle", func() core.Dialect { return &oracle{} }}, + "postgres": {"postgres", func() core.Dialect { return &postgres{} }}, + "sqlite3": {"sqlite3", func() core.Dialect { return &sqlite3{} }}, + } + + for k, v := range provided_dialects { + _, err := sql.Open(string(k), "") + if err == nil { + core.RegisterDialect(v.dbType, v.get()) + } + } +} + func close(engine *Engine) { engine.Close() } From b1dfd648f25bc8048b36a8c6269e8b84d3bad940 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Fri, 11 Apr 2014 21:31:44 +0800 Subject: [PATCH 28/55] fixed #86, increment version field after update --- session.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/session.go b/session.go index c6713ba0..c94e89aa 100644 --- a/session.go +++ b/session.go @@ -2981,6 +2981,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 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) AND %v = ?", condition, @@ -3003,12 +3005,13 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1", condition) - verValue, err := table.VersionColumn().ValueOf(bean) + 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 @@ -3035,6 +3038,8 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 res, err := session.exec(sqlStr, args...) if err != nil { return 0, err + } else if doIncVer { + verValue.SetInt(verValue.Int() + 1) } if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache { From a8b9f5d8b8ed5659b85d1ffb4c54061fa5af8952 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 11 Apr 2014 23:33:56 +0800 Subject: [PATCH 29/55] bug fixed and code refactoring --- mysql_dialect.go | 3 +++ postgres_dialect.go | 19 +------------------ 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/mysql_dialect.go b/mysql_dialect.go index 52a8a15e..58a30c44 100644 --- a/mysql_dialect.go +++ b/mysql_dialect.go @@ -119,10 +119,13 @@ func (db *mysql) GetColumns(tableName string) ([]string, map[string]*core.Column return nil, nil, err } defer cnn.Close() + rows, err := cnn.Query(s, args...) if err != nil { return nil, nil, err } + defer rows.Close() + cols := make(map[string]*core.Column) colSeq := make([]string, 0) for rows.Next() { diff --git a/postgres_dialect.go b/postgres_dialect.go index 58d39f9d..51269d53 100644 --- a/postgres_dialect.go +++ b/postgres_dialect.go @@ -276,23 +276,6 @@ func (db *postgres) GetIndexes(tableName string) (map[string]*core.Index, error) return indexes, nil } -// PgSeqFilter filter SQL replace ?, ? ... to $1, $2 ... -type PgSeqFilter struct { -} - -func (s *PgSeqFilter) Do(sql string, dialect core.Dialect, table *core.Table) string { - segs := strings.Split(sql, "?") - size := len(segs) - res := "" - for i, c := range segs { - if i < size-1 { - res += c + fmt.Sprintf("$%v", i+1) - } - } - res += segs[size-1] - return res -} - func (db *postgres) Filters() []core.Filter { - return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &PgSeqFilter{}} + return []core.Filter{&core.IdFilter{}, &core.QuoteFilter{}, &core.SeqFilter{"$", 1}} } From ad33a0be490e07039b36682d3fc54ff36472966c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=95=86=E8=AE=AF=E5=9C=A8=E7=BA=BF?= Date: Sun, 13 Apr 2014 12:22:32 +0800 Subject: [PATCH 30/55] bug fixed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 商讯在线 --- engine.go | 4 +++- statement.go | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/engine.go b/engine.go index 0f4caacf..33595aa9 100644 --- a/engine.go +++ b/engine.go @@ -457,7 +457,9 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { table := engine.newTable() method := v.MethodByName("TableName") if !method.IsValid() { - method = v.Addr().MethodByName("TableName") + if v.CanAddr() { + method = v.Addr().MethodByName("TableName") + } } if method.IsValid() { params := []reflect.Value{} diff --git a/statement.go b/statement.go index 436087c1..fa82ff83 100644 --- a/statement.go +++ b/statement.go @@ -539,6 +539,9 @@ func (statement *Statement) Cols(columns ...string) *Statement { 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) + } return statement } From 88143703904e00411e74c38c7e8fcccaeab05b00 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 14 Apr 2014 15:22:01 +0800 Subject: [PATCH 31/55] flatten the drivers dir --- drivers/goracle.go => goracle_driver.go | 8 +++---- drivers/mymysql.go => mymysql_driver.go | 8 +++---- drivers/mysql.go => mysql_driver.go | 8 +++---- drivers/oci.go => oci8_driver.go | 12 +++++----- drivers/odbc.go => odbc_driver.go | 8 +++---- drivers/pq.go => pq_driver.go | 8 +++---- drivers/sqlite3.go => sqlite3_driver.go | 8 +++---- xorm.go | 29 ++++++++++++++----------- 8 files changed, 46 insertions(+), 43 deletions(-) rename drivers/goracle.go => goracle_driver.go (90%) rename drivers/mymysql.go => mymysql_driver.go (93%) rename drivers/mysql.go => mysql_driver.go (92%) rename drivers/oci.go => oci8_driver.go (76%) rename drivers/odbc.go => odbc_driver.go (86%) rename drivers/pq.go => pq_driver.go (91%) rename drivers/sqlite3.go => sqlite3_driver.go (72%) diff --git a/drivers/goracle.go b/goracle_driver.go similarity index 90% rename from drivers/goracle.go rename to goracle_driver.go index eaee2026..f24fe339 100644 --- a/drivers/goracle.go +++ b/goracle_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "errors" @@ -7,9 +7,9 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("goracle", &goracleDriver{}) -} +// func init() { +// core.RegisterDriver("goracle", &goracleDriver{}) +// } type goracleDriver struct { } diff --git a/drivers/mymysql.go b/mymysql_driver.go similarity index 93% rename from drivers/mymysql.go rename to mymysql_driver.go index f1b4fa23..a4e726a4 100644 --- a/drivers/mymysql.go +++ b/mymysql_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "errors" @@ -8,9 +8,9 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("mymysql", &mymysqlDriver{}) -} +// func init() { +// core.RegisterDriver("mymysql", &mymysqlDriver{}) +// } type mymysqlDriver struct { } diff --git a/drivers/mysql.go b/mysql_driver.go similarity index 92% rename from drivers/mysql.go rename to mysql_driver.go index ccc2d823..c36fa8b6 100644 --- a/drivers/mysql.go +++ b/mysql_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "regexp" @@ -7,9 +7,9 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("mysql", &mysqlDriver{}) -} +// func init() { +// core.RegisterDriver("mysql", &mysqlDriver{}) +// } type mysqlDriver struct { } diff --git a/drivers/oci.go b/oci8_driver.go similarity index 76% rename from drivers/oci.go rename to oci8_driver.go index b6337797..5bb1d16c 100644 --- a/drivers/oci.go +++ b/oci8_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "errors" @@ -7,16 +7,16 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("oci", &ociDriver{}) -} +// func init() { +// core.RegisterDriver("oci8", &oci8Driver{}) +// } -type ociDriver struct { +type oci8Driver struct { } //dataSourceName=user/password@ipv4:port/dbname //dataSourceName=user/password@[ipv6]:port/dbname -func (p *ociDriver) Parse(driverName, dataSourceName string) (*core.Uri, error) { +func (p *oci8Driver) Parse(driverName, dataSourceName string) (*core.Uri, error) { db := &core.Uri{DbType: core.ORACLE} dsnPattern := regexp.MustCompile( `^(?P.*)\/(?P.*)@` + // user:password@ diff --git a/drivers/odbc.go b/odbc_driver.go similarity index 86% rename from drivers/odbc.go rename to odbc_driver.go index 01f2bb37..0f6d4e18 100644 --- a/drivers/odbc.go +++ b/odbc_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "errors" @@ -7,9 +7,9 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("odbc", &odbcDriver{}) -} +// func init() { +// core.RegisterDriver("odbc", &odbcDriver{}) +// } type odbcDriver struct { } diff --git a/drivers/pq.go b/pq_driver.go similarity index 91% rename from drivers/pq.go rename to pq_driver.go index 09756dea..a5bb6718 100644 --- a/drivers/pq.go +++ b/pq_driver.go @@ -1,4 +1,4 @@ -package drivers +package xorm import ( "errors" @@ -8,9 +8,9 @@ import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("postgres", &pqDriver{}) -} +// func init() { +// core.RegisterDriver("postgres", &pqDriver{}) +// } type pqDriver struct { } diff --git a/drivers/sqlite3.go b/sqlite3_driver.go similarity index 72% rename from drivers/sqlite3.go rename to sqlite3_driver.go index 2ba8061a..2ecd9edf 100644 --- a/drivers/sqlite3.go +++ b/sqlite3_driver.go @@ -1,12 +1,12 @@ -package drivers +package xorm import ( "github.com/go-xorm/core" ) -func init() { - core.RegisterDriver("sqlite3", &sqlite3Driver{}) -} +// func init() { +// core.RegisterDriver("sqlite3", &sqlite3Driver{}) +// } type sqlite3Driver struct { } diff --git a/xorm.go b/xorm.go index 705b61a0..38db6d4f 100644 --- a/xorm.go +++ b/xorm.go @@ -12,7 +12,6 @@ import ( "github.com/go-xorm/core" "github.com/go-xorm/xorm/caches" - _ "github.com/go-xorm/xorm/drivers" ) const ( @@ -20,22 +19,26 @@ const ( ) func init() { - provided_dialects := map[string]struct { - dbType core.DbType - get func() core.Dialect + + providedDrvsNDialects := map[string]struct { + dbType core.DbType + getDriver func() core.Driver + getDialect func() core.Dialect }{ - "odbc": {"mssql", func() core.Dialect { return &mssql{} }}, - "mysql": {"mysql", func() core.Dialect { return &mysql{} }}, - "mymysql": {"mysql", func() core.Dialect { return &mysql{} }}, - "oci8": {"oracle", func() core.Dialect { return &oracle{} }}, - "postgres": {"postgres", func() core.Dialect { return &postgres{} }}, - "sqlite3": {"sqlite3", func() core.Dialect { return &sqlite3{} }}, + "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access + "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{} }}, + "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{} }}, } - for k, v := range provided_dialects { - _, err := sql.Open(string(k), "") + for driverName, v := range providedDrvsNDialects { + _, err := sql.Open(driverName, "") if err == nil { - core.RegisterDialect(v.dbType, v.get()) + core.RegisterDriver(driverName, v.getDriver()) + core.RegisterDialect(v.dbType, v.getDialect()) } } } From 38616e71fdca88f0a065241c282c782fb2653d4d Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 14 Apr 2014 15:32:33 +0800 Subject: [PATCH 32/55] flatten caches dir --- caches/lruCacher.go => lru_cacher.go | 2 +- caches/memoryStore.go => memroy_store.go | 7 +------ xorm.go | 19 +++++++++---------- 3 files changed, 11 insertions(+), 17 deletions(-) rename caches/lruCacher.go => lru_cacher.go (99%) rename caches/memoryStore.go => memroy_store.go (91%) diff --git a/caches/lruCacher.go b/lru_cacher.go similarity index 99% rename from caches/lruCacher.go rename to lru_cacher.go index 3f28402b..bb223c20 100644 --- a/caches/lruCacher.go +++ b/lru_cacher.go @@ -1,5 +1,5 @@ //LRUCacher implements Cacher according to LRU algorithm -package caches +package xorm import ( "container/list" diff --git a/caches/memoryStore.go b/memroy_store.go similarity index 91% rename from caches/memoryStore.go rename to memroy_store.go index 67cfb0d4..c5800e46 100644 --- a/caches/memoryStore.go +++ b/memroy_store.go @@ -1,17 +1,12 @@ // MemoryStore implements CacheStore provide local machine -package caches +package xorm import ( - "errors" "sync" "github.com/go-xorm/core" ) -var ( - ErrNotExist = errors.New("key not exist") -) - var _ core.CacheStore = NewMemoryStore() // memory store diff --git a/xorm.go b/xorm.go index 38db6d4f..2b059a09 100644 --- a/xorm.go +++ b/xorm.go @@ -11,7 +11,6 @@ import ( "time" "github.com/go-xorm/core" - "github.com/go-xorm/xorm/caches" ) const ( @@ -95,14 +94,14 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return engine, err } -func NewLRUCacher(store core.CacheStore, max int) *caches.LRUCacher { - return caches.NewLRUCacher(store, core.CacheExpired, core.CacheMaxMemory, max) +// func NewLRUCacher(store core.CacheStore, max int) *LRUCacher { +// return NewLRUCacher(store, core.CacheExpired, core.CacheMaxMemory, max) +// } + +func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *LRUCacher { + return NewLRUCacher(store, expired, 0, max) } -func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *caches.LRUCacher { - return caches.NewLRUCacher(store, expired, 0, max) -} - -func NewMemoryStore() *caches.MemoryStore { - return caches.NewMemoryStore() -} +// func NewMemoryStore() *MemoryStore { +// return NewMemoryStore() +// } From 97f4b0e45c60a37336fcb19ee54cb7ca882e0014 Mon Sep 17 00:00:00 2001 From: unphp Date: Tue, 15 Apr 2014 09:54:49 +0800 Subject: [PATCH 33/55] Method In provides a update string --- engine.go | 5 +++++ session.go | 12 ++++++++++++ statement.go | 12 ++++++++++++ 3 files changed, 29 insertions(+) diff --git a/engine.go b/engine.go index 33595aa9..e0a10ed1 100644 --- a/engine.go +++ b/engine.go @@ -342,6 +342,11 @@ func (engine *Engine) In(column string, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.In(column, args...) +// Method In provides a update string like "column = column + ?" +func (engine *Engine) Inc(column string, arg interface{}) *Session { + session := engine.NewSession() + session.IsAutoClose = true + return session.Inc(column, arg) } // Temporarily change the Get, Find, Update's table diff --git a/session.go b/session.go index c94e89aa..741f9342 100644 --- a/session.go +++ b/session.go @@ -128,6 +128,12 @@ func (session *Session) In(column string, args ...interface{}) *Session { return session } +// Method In provides a query string like "count = count + 1" +func (session *Session) Inc(column string, arg interface{}) *Session { + session.Statement.Inc(column, arg) + return session +} + // Method Cols provides some columns to special func (session *Session) Cols(columns ...string) *Session { session.Statement.Cols(columns...) @@ -2952,6 +2958,12 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 args = append(args, time.Now()) } + //for update action to like "column = column + ?" + incColumns := session.Statement.getInc() + for k, v := range incColumns { + colNames = append(colNames, k+" = "+k+" + ?") + args = append(args, v) + } var condiColNames []string var condiArgs []interface{} diff --git a/statement.go b/statement.go index fa82ff83..0b0c2f83 100644 --- a/statement.go +++ b/statement.go @@ -48,6 +48,7 @@ type Statement struct { checkVersion bool mustColumnMap map[string]bool inColumns map[string]*inParam + incColumns map[string]interface{} } // init @@ -470,6 +471,17 @@ func (statement *Statement) Id(id interface{}) *Statement { return statement } +// Generate "Update ... Set column = column + arg" statment +func (statement *Statement) Inc(column string, arg interface{}) *Statement { + k := strings.ToLower(column) + statement.incColumns[k] = arg + return statement +} + +// Generate "Update ... Set column = column + arg" statment +func (statement *Statement) getInc() map[string]interface{} { + return statement.incColumns +} // Generate "Where column IN (?) " statment func (statement *Statement) In(column string, args ...interface{}) *Statement { k := strings.ToLower(column) From decbeefc27810dcf4d9d771ad88d20ceb37c65ac Mon Sep 17 00:00:00 2001 From: unphp Date: Tue, 15 Apr 2014 09:58:31 +0800 Subject: [PATCH 34/55] Method In provides a update string --- engine.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/engine.go b/engine.go index e0a10ed1..7e562eb5 100644 --- a/engine.go +++ b/engine.go @@ -342,6 +342,8 @@ func (engine *Engine) In(column string, args ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true return session.In(column, args...) +} + // Method In provides a update string like "column = column + ?" func (engine *Engine) Inc(column string, arg interface{}) *Session { session := engine.NewSession() From 64e5181112d4d583be92d7ebd327884c82d35d7e Mon Sep 17 00:00:00 2001 From: unphp Date: Tue, 15 Apr 2014 10:24:59 +0800 Subject: [PATCH 35/55] Method In provides a update string --- statement.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/statement.go b/statement.go index 0b0c2f83..5b45d1d6 100644 --- a/statement.go +++ b/statement.go @@ -79,6 +79,7 @@ func (statement *Statement) Init() { statement.mustColumnMap = make(map[string]bool) statement.checkVersion = true statement.inColumns = make(map[string]*inParam) + statement.incColumns = make(map[string]interface{}, 0) } // add the raw sql statement @@ -482,6 +483,7 @@ func (statement *Statement) Inc(column string, arg interface{}) *Statement { func (statement *Statement) getInc() map[string]interface{} { return statement.incColumns } + // Generate "Where column IN (?) " statment func (statement *Statement) In(column string, args ...interface{}) *Statement { k := strings.ToLower(column) From e2aa0ef2c2ecd90e43f46812dbf18e38587a85fb Mon Sep 17 00:00:00 2001 From: unphp Date: Tue, 15 Apr 2014 10:28:58 +0800 Subject: [PATCH 36/55] Method In provides a update string --- engine.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine.go b/engine.go index 7e562eb5..63a353c5 100644 --- a/engine.go +++ b/engine.go @@ -344,7 +344,7 @@ func (engine *Engine) In(column string, args ...interface{}) *Session { return session.In(column, args...) } -// Method In provides a update string like "column = column + ?" +// Method Inc provides a update string like "column = column + ?" func (engine *Engine) Inc(column string, arg interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true From feb1f45e726837a05ac875363de07539c39445b7 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 15 Apr 2014 11:39:29 +0800 Subject: [PATCH 37/55] Added NewDB(), DB(), Clone() and removed pool object and other improvements --- engine.go | 51 ++++++---- pool.go | 285 ----------------------------------------------------- rows.go | 2 +- session.go | 30 +++--- xorm.go | 40 ++++---- 5 files changed, 66 insertions(+), 342 deletions(-) delete mode 100644 pool.go diff --git a/engine.go b/engine.go index 33595aa9..10dce15a 100644 --- a/engine.go +++ b/engine.go @@ -18,23 +18,32 @@ import ( // Engine is the major struct of xorm, it means a database manager. // Commonly, an application only need one engine type Engine struct { - ColumnMapper core.IMapper - TableMapper core.IMapper - TagIdentifier string - DriverName string - DataSourceName string - dialect core.Dialect - Tables map[reflect.Type]*core.Table + db *core.DB + dialect core.Dialect + + ColumnMapper core.IMapper + TableMapper core.IMapper + TagIdentifier string + Tables map[reflect.Type]*core.Table + + mutex *sync.RWMutex + Cacher core.Cacher - mutex *sync.RWMutex ShowSQL bool ShowErr bool ShowDebug bool ShowWarn bool - Pool IConnectPool - Filters []core.Filter - Logger ILogger // io.Writer - Cacher core.Cacher + //Pool IConnectPool + //Filters []core.Filter + Logger ILogger // io.Writer +} + +func (engine *Engine) DriverName() string { + return engine.dialect.DriverName() +} + +func (engine *Engine) DataSourceName() string { + return engine.dialect.DataSourceName() } func (engine *Engine) SetMapper(mapper core.IMapper) { @@ -80,19 +89,19 @@ func (engine *Engine) AutoIncrStr() string { } // Set engine's pool, the pool default is Go's standard library's connection pool. -func (engine *Engine) SetPool(pool IConnectPool) error { +/*func (engine *Engine) SetPool(pool IConnectPool) error { engine.Pool = pool return engine.Pool.Init(engine) -} +}*/ // SetMaxConns is only available for go 1.2+ func (engine *Engine) SetMaxConns(conns int) { - engine.Pool.SetMaxConns(conns) + engine.db.SetMaxOpenConns(conns) } // SetMaxIdleConns func (engine *Engine) SetMaxIdleConns(conns int) { - engine.Pool.SetMaxIdleConns(conns) + engine.db.SetMaxIdleConns(conns) } // SetDefaltCacher set the default cacher. Xorm's default not enable cacher. @@ -122,8 +131,12 @@ func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { } // OpenDB provides a interface to operate database directly. -func (engine *Engine) OpenDB() (*core.DB, error) { - return core.Open(engine.DriverName, engine.DataSourceName) +func (engine *Engine) NewDB() (*core.DB, error) { + return core.OpenDialect(engine.dialect) +} + +func (engine *Engine) DB() *core.DB { + return engine.db } // New a session @@ -135,7 +148,7 @@ func (engine *Engine) NewSession() *Session { // Close the engine func (engine *Engine) Close() error { - return engine.Pool.Close(engine) + return engine.db.Close() } // Ping tests if database is alive. diff --git a/pool.go b/pool.go deleted file mode 100644 index 919ea380..00000000 --- a/pool.go +++ /dev/null @@ -1,285 +0,0 @@ -package xorm - -import ( - "container/list" - "reflect" - "sync" - "time" - - "github.com/go-xorm/core" -) - -// Interface IConnecPool is a connection pool interface, all implements should implement -// Init, RetrieveDB, ReleaseDB and Close methods. -// Init for init when engine be created or invoke SetPool -// RetrieveDB for requesting a connection to db; -// ReleaseDB for releasing a db connection; -// Close for invoking when engine.Close -type IConnectPool interface { - Init(engine *Engine) error - RetrieveDB(engine *Engine) (*core.DB, error) - ReleaseDB(engine *Engine, db *core.DB) - Close(engine *Engine) error - SetMaxIdleConns(conns int) - MaxIdleConns() int - SetMaxConns(conns int) - MaxConns() int -} - -// Struct NoneConnectPool is a implement for IConnectPool. It provides directly invoke driver's -// open and release connection function -type NoneConnectPool struct { -} - -// NewNoneConnectPool new a NoneConnectPool. -func NewNoneConnectPool() IConnectPool { - return &NoneConnectPool{} -} - -// Init do nothing -func (p *NoneConnectPool) Init(engine *Engine) error { - return nil -} - -// RetrieveDB directly open a connection -func (p *NoneConnectPool) RetrieveDB(engine *Engine) (db *core.DB, err error) { - db, err = engine.OpenDB() - return -} - -// ReleaseDB directly close a connection -func (p *NoneConnectPool) ReleaseDB(engine *Engine, db *core.DB) { - db.Close() -} - -// Close do nothing -func (p *NoneConnectPool) Close(engine *Engine) error { - return nil -} - -func (p *NoneConnectPool) SetMaxIdleConns(conns int) { -} - -func (p *NoneConnectPool) MaxIdleConns() int { - return 0 -} - -// not implemented -func (p *NoneConnectPool) SetMaxConns(conns int) { -} - -// not implemented -func (p *NoneConnectPool) MaxConns() int { - return -1 -} - -// Struct SysConnectPool is a simple wrapper for using system default connection pool. -// About the system connection pool, you can review the code database/sql/sql.go -// It's currently default Pool implments. -type SysConnectPool struct { - db *core.DB - maxIdleConns int - maxConns int - curConns int - mutex *sync.Mutex - queue *list.List -} - -// NewSysConnectPool new a SysConnectPool. -func NewSysConnectPool() IConnectPool { - return &SysConnectPool{} -} - -// Init create a db immediately and keep it util engine closed. -func (s *SysConnectPool) Init(engine *Engine) error { - db, err := engine.OpenDB() - if err != nil { - return err - } - s.db = db - s.maxIdleConns = 2 - s.maxConns = -1 - s.curConns = 0 - s.mutex = &sync.Mutex{} - s.queue = list.New() - return nil -} - -type node struct { - mutex sync.Mutex - cond *sync.Cond -} - -func newCondNode() *node { - n := &node{} - n.cond = sync.NewCond(&n.mutex) - return n -} - -// RetrieveDB just return the only db -func (s *SysConnectPool) RetrieveDB(engine *Engine) (db *core.DB, err error) { - /*if s.maxConns > 0 { - fmt.Println("before retrieve") - s.mutex.Lock() - for s.curConns >= s.maxConns { - fmt.Println("before waiting...", s.curConns, s.queue.Len()) - s.mutex.Unlock() - n := NewNode() - n.cond.L.Lock() - s.queue.PushBack(n) - n.cond.Wait() - n.cond.L.Unlock() - s.mutex.Lock() - fmt.Println("after waiting...", s.curConns, s.queue.Len()) - } - s.curConns += 1 - s.mutex.Unlock() - fmt.Println("after retrieve") - }*/ - return s.db, nil -} - -// ReleaseDB do nothing -func (s *SysConnectPool) ReleaseDB(engine *Engine, db *core.DB) { - /*if s.maxConns > 0 { - s.mutex.Lock() - fmt.Println("before release", s.queue.Len()) - s.curConns -= 1 - - if e := s.queue.Front(); e != nil { - n := e.Value.(*node) - //n.cond.L.Lock() - n.cond.Signal() - fmt.Println("signaled...") - s.queue.Remove(e) - //n.cond.L.Unlock() - } - fmt.Println("after released", s.queue.Len()) - s.mutex.Unlock() - }*/ -} - -// Close closed the only db -func (p *SysConnectPool) Close(engine *Engine) error { - return p.db.Close() -} - -func (p *SysConnectPool) SetMaxIdleConns(conns int) { - p.db.SetMaxIdleConns(conns) - p.maxIdleConns = conns -} - -func (p *SysConnectPool) MaxIdleConns() int { - return p.maxIdleConns -} - -// not implemented -func (p *SysConnectPool) SetMaxConns(conns int) { - p.maxConns = conns - // if support SetMaxOpenConns, go 1.2+, then set - if reflect.ValueOf(p.db).MethodByName("SetMaxOpenConns").IsValid() { - reflect.ValueOf(p.db).MethodByName("SetMaxOpenConns").Call([]reflect.Value{reflect.ValueOf(conns)}) - } - //p.db.SetMaxOpenConns(conns) -} - -// not implemented -func (p *SysConnectPool) MaxConns() int { - return p.maxConns -} - -// NewSimpleConnectPool new a SimpleConnectPool -func NewSimpleConnectPool() IConnectPool { - return &SimpleConnectPool{releasedConnects: make([]*core.DB, 10), - usingConnects: map[*core.DB]time.Time{}, - cur: -1, - maxWaitTimeOut: 14400, - maxIdleConns: 10, - mutex: &sync.Mutex{}, - } -} - -// Struct SimpleConnectPool is a simple implementation for IConnectPool. -// It's a custom connection pool and not use system connection pool. -// Opening or Closing a database connection must be enter a lock. -// This implements will be improved in furture. -type SimpleConnectPool struct { - releasedConnects []*core.DB - cur int - usingConnects map[*core.DB]time.Time - maxWaitTimeOut int - mutex *sync.Mutex - maxIdleConns int -} - -func (s *SimpleConnectPool) Init(engine *Engine) error { - return nil -} - -// RetrieveDB get a connection from connection pool -func (p *SimpleConnectPool) RetrieveDB(engine *Engine) (*core.DB, error) { - p.mutex.Lock() - defer p.mutex.Unlock() - var db *core.DB = nil - var err error = nil - //fmt.Printf("%x, rbegin - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) - if p.cur < 0 { - db, err = engine.OpenDB() - if err != nil { - return nil, err - } - p.usingConnects[db] = time.Now() - } else { - db = p.releasedConnects[p.cur] - p.usingConnects[db] = time.Now() - p.releasedConnects[p.cur] = nil - p.cur = p.cur - 1 - } - - //fmt.Printf("%x, rend - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) - return db, nil -} - -// ReleaseDB release a db from connection pool -func (p *SimpleConnectPool) ReleaseDB(engine *Engine, db *core.DB) { - p.mutex.Lock() - defer p.mutex.Unlock() - //fmt.Printf("%x, lbegin - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) - if p.cur >= p.maxIdleConns-1 { - db.Close() - } else { - p.cur = p.cur + 1 - p.releasedConnects[p.cur] = db - } - delete(p.usingConnects, db) - //fmt.Printf("%x, lend - released:%v, using:%v\n", &p, p.cur+1, len(p.usingConnects)) -} - -// Close release all db -func (p *SimpleConnectPool) Close(engine *Engine) error { - p.mutex.Lock() - defer p.mutex.Unlock() - for len(p.releasedConnects) > 0 { - p.releasedConnects[0].Close() - p.releasedConnects = p.releasedConnects[1:] - } - - return nil -} - -func (p *SimpleConnectPool) SetMaxIdleConns(conns int) { - p.maxIdleConns = conns -} - -func (p *SimpleConnectPool) MaxIdleConns() int { - return p.maxIdleConns -} - -// not implemented -func (p *SimpleConnectPool) SetMaxConns(conns int) { -} - -// not implemented -func (p *SimpleConnectPool) MaxConns() int { - return -1 -} diff --git a/rows.go b/rows.go index 1a176d7d..99f724dd 100644 --- a/rows.go +++ b/rows.go @@ -42,7 +42,7 @@ func newRows(session *Session, bean interface{}) (*Rows, error) { args = rows.session.Statement.RawParams } - for _, filter := range rows.session.Engine.Filters { + for _, filter := range rows.session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, rows.session.Statement.RefTable) } diff --git a/session.go b/session.go index c94e89aa..8649584a 100644 --- a/session.go +++ b/session.go @@ -61,7 +61,7 @@ func (session *Session) Close() { } if session.Db != nil { - session.Engine.Pool.ReleaseDB(session.Engine, session.Db) + //session.Engine.Pool.ReleaseDB(session.Engine, session.Db) session.Db = nil session.Tx = nil session.stmtCache = nil @@ -267,11 +267,11 @@ func (session *Session) Having(conditions string) *Session { func (session *Session) newDb() error { if session.Db == nil { - db, err := session.Engine.Pool.RetrieveDB(session.Engine) + /*db, err := session.Engine.Pool.RetrieveDB(session.Engine) if err != nil { return err - } - session.Db = db + }*/ + session.Db = session.Engine.db session.stmtCache = make(map[uint32]*core.Stmt, 0) } return nil @@ -426,7 +426,7 @@ func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Resul } func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) { - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -612,7 +612,7 @@ func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interf if session.Statement.RefTable == nil || len(session.Statement.RefTable.PrimaryKeys) != 1 { return false, ErrCacheFailed } - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } newsql := session.Statement.convertIdSql(sqlStr) @@ -699,7 +699,7 @@ func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr in return ErrCacheFailed } - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -1709,7 +1709,7 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i } func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) { - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { *sqlStr = filter.Do(*sqlStr, session.Engine.dialect, session.Statement.RefTable) } @@ -2263,7 +2263,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, var err error // for mysql, when use bit, it returned \x01 if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName, "mysql") { + strings.Contains(session.Engine.DriverName(), "mysql") { if len(data) == 1 { x = int64(data[0]) } else { @@ -2289,7 +2289,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, var err error // for mysql, when use bit, it returned \x01 if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName, "mysql") { + strings.Contains(session.Engine.DriverName(), "mysql") { if len(data) == 1 { x = int(data[0]) } else { @@ -2347,7 +2347,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, var err error // for mysql, when use bit, it returned \x01 if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName, "mysql") { + strings.Contains(session.Engine.DriverName(), "mysql") { if len(data) == 1 { x = int8(data[0]) } else { @@ -2376,7 +2376,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, var err error // for mysql, when use bit, it returned \x01 if col.SQLType.Name == core.Bit && - strings.Contains(session.Engine.DriverName, "mysql") { + strings.Contains(session.Engine.DriverName(), "mysql") { if len(data) == 1 { x = int16(data[0]) } else { @@ -2583,7 +2583,7 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { // for postgres, many of them didn't implement lastInsertId, so we should // implemented it ourself. - if session.Engine.DriverName != core.POSTGRES || table.AutoIncrement == "" { + if session.Engine.DriverName() != core.POSTGRES || table.AutoIncrement == "" { res, err := session.exec(sqlStr, args...) if err != nil { return 0, err @@ -2782,7 +2782,7 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { if newsql == "" { return ErrCacheFailed } - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable) } session.Engine.LogDebug("[xorm:cacheUpdate] new sql", oldhead, newsql) @@ -3085,7 +3085,7 @@ func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error { return ErrCacheFailed } - for _, filter := range session.Engine.Filters { + for _, filter := range session.Engine.dialect.Filters() { sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable) } diff --git a/xorm.go b/xorm.go index 2b059a09..8e4346ee 100644 --- a/xorm.go +++ b/xorm.go @@ -69,39 +69,35 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return nil, err } + db, err := core.OpenDialect(dialect) + if err != nil { + return nil, err + } + engine := &Engine{ - DriverName: driverName, - DataSourceName: dataSourceName, - dialect: dialect, + db: db, + dialect: dialect, + Tables: make(map[reflect.Type]*core.Table), + mutex: &sync.RWMutex{}, + TagIdentifier: "xorm", + Logger: NewSimpleLogger(os.Stdout), } engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) - engine.Filters = dialect.Filters() - - engine.Tables = make(map[reflect.Type]*core.Table) - - engine.mutex = &sync.RWMutex{} - engine.TagIdentifier = "xorm" - - engine.Logger = NewSimpleLogger(os.Stdout) - - //engine.Pool = NewSimpleConnectPool() - //engine.Pool = NewNoneConnectPool() + //engine.Filters = dialect.Filters() //engine.Cacher = NewLRUCacher() - err = engine.SetPool(NewSysConnectPool()) + //err = engine.SetPool(NewSysConnectPool()) + runtime.SetFinalizer(engine, close) return engine, err } -// func NewLRUCacher(store core.CacheStore, max int) *LRUCacher { -// return NewLRUCacher(store, core.CacheExpired, core.CacheMaxMemory, max) -// } +// clone an engine +func (engine *Engine) Clone() (*Engine, error) { + return NewEngine(engine.dialect.DriverName(), engine.dialect.DataSourceName()) +} func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *LRUCacher { return NewLRUCacher(store, expired, 0, max) } - -// func NewMemoryStore() *MemoryStore { -// return NewMemoryStore() -// } From 9a7a6cd4d27600fa3844049fc78cb0fcbdd9e2b3 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Tue, 15 Apr 2014 12:14:18 +0800 Subject: [PATCH 38/55] move Inc() to Incr() --- engine.go | 4 ++-- session.go | 4 ++-- statement.go | 8 ++++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/engine.go b/engine.go index 4bcac9e1..ddfedff2 100644 --- a/engine.go +++ b/engine.go @@ -358,10 +358,10 @@ func (engine *Engine) In(column string, args ...interface{}) *Session { } // Method Inc provides a update string like "column = column + ?" -func (engine *Engine) Inc(column string, arg interface{}) *Session { +func (engine *Engine) Incr(column string, arg ...interface{}) *Session { session := engine.NewSession() session.IsAutoClose = true - return session.Inc(column, arg) + return session.Incr(column, arg...) } // Temporarily change the Get, Find, Update's table diff --git a/session.go b/session.go index 8a3a5149..231f18c5 100644 --- a/session.go +++ b/session.go @@ -129,8 +129,8 @@ func (session *Session) In(column string, args ...interface{}) *Session { } // Method In provides a query string like "count = count + 1" -func (session *Session) Inc(column string, arg interface{}) *Session { - session.Statement.Inc(column, arg) +func (session *Session) Incr(column string, arg ...interface{}) *Session { + session.Statement.Incr(column, arg...) return session } diff --git a/statement.go b/statement.go index 5b45d1d6..9330a14d 100644 --- a/statement.go +++ b/statement.go @@ -473,9 +473,13 @@ func (statement *Statement) Id(id interface{}) *Statement { } // Generate "Update ... Set column = column + arg" statment -func (statement *Statement) Inc(column string, arg interface{}) *Statement { +func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { k := strings.ToLower(column) - statement.incColumns[k] = arg + if len(arg) > 0 { + statement.incColumns[k] = arg[0] + } else { + statement.incColumns[k] = 1 + } return statement } From 145358a74ec3e4d70f5bc4ffff95d261f9151492 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Tue, 15 Apr 2014 14:37:10 +0800 Subject: [PATCH 39/55] update API comments --- engine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine.go b/engine.go index ddfedff2..0ce4c8f0 100644 --- a/engine.go +++ b/engine.go @@ -130,7 +130,7 @@ func (engine *Engine) MapCacher(bean interface{}, cacher core.Cacher) { engine.Tables[v.Type()].Cacher = cacher } -// OpenDB provides a interface to operate database directly. +// NewDB provides an interface to operate database directly func (engine *Engine) NewDB() (*core.DB, error) { return core.OpenDialect(engine.dialect) } @@ -151,7 +151,7 @@ func (engine *Engine) Close() error { return engine.db.Close() } -// Ping tests if database is alive. +// Ping tests if database is alive func (engine *Engine) Ping() error { session := engine.NewSession() defer session.Close() From 895d976804a5ec3bdc1145bef64c9e07abeccc57 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Wed, 16 Apr 2014 01:25:01 +0800 Subject: [PATCH 40/55] fixed cases Drivers and Dialects are not registered, where sql driver is loaded after xorm.init(), using explicit call to register Drivers and Dialects upon xorm.NewEngine() call --- xorm.go | 56 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 22 deletions(-) diff --git a/xorm.go b/xorm.go index 8e4346ee..7190446c 100644 --- a/xorm.go +++ b/xorm.go @@ -4,41 +4,52 @@ import ( "database/sql" "errors" "fmt" + "github.com/go-xorm/core" "os" "reflect" "runtime" "sync" "time" - - "github.com/go-xorm/core" ) const ( Version string = "0.4" ) -func init() { +// !nashtsai! implicit register drivers and dialects is no good, as init() can be called before sql driver got registered +// func init() { +// regDrvsNDialects() +// } - providedDrvsNDialects := map[string]struct { - dbType core.DbType - getDriver func() core.Driver - getDialect func() core.Dialect - }{ - "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access - "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{} }}, - "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{} }}, - } - - for driverName, v := range providedDrvsNDialects { - _, err := sql.Open(driverName, "") - if err == nil { - core.RegisterDriver(driverName, v.getDriver()) - core.RegisterDialect(v.dbType, v.getDialect()) +func regDrvsNDialects() bool { + if core.RegisteredDriverSize() == 0 { + providedDrvsNDialects := map[string]struct { + dbType core.DbType + getDriver func() core.Driver + getDialect func() core.Dialect + }{ + "odbc": {"mssql", func() core.Driver { return &odbcDriver{} }, func() core.Dialect { return &mssql{} }}, // !nashtsai! TODO change this when supporting MS Access + "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{} }}, + "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{} }}, } + + for driverName, v := range providedDrvsNDialects { + _, err := sql.Open(driverName, "") + if err == nil { + // fmt.Printf("driver succeed: %v\n", driverName) + core.RegisterDriver(driverName, v.getDriver()) + core.RegisterDialect(v.dbType, v.getDialect()) + } else { + // fmt.Printf("driver failed: %v | err: %v\n", driverName, err) + } + } + return true + } else { + return false } } @@ -49,6 +60,7 @@ func close(engine *Engine) { // 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)) From 05e99e39c6f42c153f3d62bd9187f5074f5cdd39 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 17 Apr 2014 10:13:16 +0800 Subject: [PATCH 41/55] bug fixed --- docs/QuickStart.md | 24 ++------------------ docs/QuickStartEn.md | 43 ++++++++++++++++++----------------- engine.go | 14 +++++------- examples/cache.go | 4 ++-- examples/cachegoroutine.go | 8 ++++--- examples/maxconnect.go | 2 +- examples/pool.go | 46 -------------------------------------- examples/sync.go | 3 ++- session.go | 14 ++++++------ statement.go | 40 +++++++++++---------------------- 10 files changed, 61 insertions(+), 137 deletions(-) delete mode 100644 examples/pool.go diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 2fc373a7..cac249c6 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -65,11 +65,8 @@ defer engine.Close() 一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 xorm当前支持五种驱动四个数据库如下: -<<<<<<< HEAD -======= * Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) ->>>>>>> master * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) @@ -124,15 +121,9 @@ engine.SetMapper(SameMapper{}) 同时需要注意的是: -<<<<<<< HEAD * 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: -======= -* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: - ->>>>>>> master ```Go engine.SetTableMapper(SameMapper{}) engine.SetColumnMapper(SnakeMapper{}) @@ -142,12 +133,10 @@ engine.SetColumnMapper(SnakeMapper{}) ### 2.2.前缀映射,后缀映射和缓存映射 * 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 + * 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 -<<<<<<< HEAD -* -======= + * 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。 ->>>>>>> master ### 2.3.使用Table和Tag改变名称映射 @@ -598,21 +587,12 @@ affected, err := engine.Id(id).Update(user) 这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择: -<<<<<<< HEAD 1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 -======= -* 1.通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 ->>>>>>> master - ```Go affected, err := engine.Id(id).Cols("age").Update(&user) ``` -<<<<<<< HEAD 2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 -======= -* 2.通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 ->>>>>>> master ```Go affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) diff --git a/docs/QuickStartEn.md b/docs/QuickStartEn.md index a8dce7cc..aa9baa2e 100644 --- a/docs/QuickStartEn.md +++ b/docs/QuickStartEn.md @@ -70,6 +70,8 @@ xorm supports four drivers now: * Postgres: [github.com/lib/pq](https://github.com/lib/pq) +* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc) + NewEngine's parameters are the same as `sql.Open`. So you should read the drivers' document for parameters' usage. After engine created, you can do some settings. @@ -100,7 +102,7 @@ engine.Logger = f ## 2.Define struct -xorm map a struct to a database table, the rule is below. +xorm maps a struct to a database table, the rule is below. ### 2.1.name mapping rule @@ -110,13 +112,13 @@ use xorm.IMapper interface to implement. There are two IMapper implemented: `Sna SnakeMapper is the default. ```Go -engine.Mapper = SameMapper{} +engine.SetMapper(SameMapper{}) ``` -同时需要注意的是: +And you should notice: -* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: +* If you want to use other mapping rule, implement IMapper +* Tables's mapping rule could be different from Columns': ```Go engine.SetTableMapper(SameMapper{}) @@ -124,7 +126,7 @@ engine.SetColumnMapper(SnakeMapper{}) ``` -### 2.2.前缀映射规则,后缀映射规则和缓存映射规则 +### 2.2.Prefix mapping, Suffix Mapping and Cache Mapping * 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 @@ -133,14 +135,15 @@ engine.SetColumnMapper(SnakeMapper{}) 当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -### 2.3.使用Table和Tag改变名称映射 +### 2.3.Tag mapping 如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。 通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。 -### 2.4.Column属性定义 +### 2.4.Column defenition + 我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如: ``` @@ -150,37 +153,37 @@ type User struct { } ``` -对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。 +For different DBMS, data types对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。 具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: - + - + - + - + - + - + - + @@ -198,7 +201,7 @@ type User struct { - +
name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名name or 'name'Column Name, optional
pk是否是Primary Key,当前仅支持int64类型pkIf column is Primary Key
当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型
autoincr是否是自增autoincrIf autoincrement column
[not ]null是否可以为空[not ]null | notnullif column could be blank
unique或unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引unique/unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引
index或index(indexname)是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引index/index(indexname)是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引
extends应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中
-这个Field将不进行字段映射-This field will not be mapping
->这个Field将只写入到数据库而不从数据库读取versionThis field will be filled 1 on insert and autoincrement on update
default 0设置默认值,紧跟的内容如果是Varchar等需要加上单引号default 0 | default 'name'column default value
@@ -224,13 +227,13 @@ type Conversion interface { xorm提供了一些动态获取和修改表结构的方法。对于一般的应用,很少动态修改表结构,则只需调用Sync()同步下表结构即可。 -## 3.1 获取数据库信息 +## 3.1 retrieve database meta info * DBMetas() xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表的信息 -## 3.2.表操作 +## 3.2.directly table operation * CreateTables() 创建表使用`engine.CreateTables()`,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 @@ -245,7 +248,7 @@ xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到 删除表使用`engine.DropTables()`,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。 -## 3.3.创建索引和唯一索引 +## 3.3.create indexes and uniques * CreateIndexes 根据struct中的tag来创建索引 diff --git a/engine.go b/engine.go index 0ce4c8f0..7aa44b1e 100644 --- a/engine.go +++ b/engine.go @@ -88,14 +88,8 @@ func (engine *Engine) AutoIncrStr() string { return engine.dialect.AutoIncrStr() } -// Set engine's pool, the pool default is Go's standard library's connection pool. -/*func (engine *Engine) SetPool(pool IConnectPool) error { - engine.Pool = pool - return engine.Pool.Init(engine) -}*/ - -// SetMaxConns is only available for go 1.2+ -func (engine *Engine) SetMaxConns(conns int) { +// SetMaxOpenConns is only available for go 1.2+ +func (engine *Engine) SetMaxOpenConns(conns int) { engine.db.SetMaxOpenConns(conns) } @@ -139,6 +133,10 @@ func (engine *Engine) DB() *core.DB { return engine.db } +func (engine *Engine) Dialect() core.Dialect { + return engine.dialect +} + // New a session func (engine *Engine) NewSession() *Session { session := &Session{Engine: engine} diff --git a/examples/cache.go b/examples/cache.go index c5d50273..0fc71a08 100644 --- a/examples/cache.go +++ b/examples/cache.go @@ -3,9 +3,9 @@ package main import ( "fmt" "os" + "time" "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" _ "github.com/mattn/go-sqlite3" ) @@ -24,7 +24,7 @@ func main() { return } Orm.ShowSQL = true - cacher := xorm.NewLRUCacher(caches.NewMemoryStore(), 1000) + cacher := xorm.NewLRUCacher2(xorm.NewMemoryStore(), time.Hour, 1000) Orm.SetDefaultCacher(cacher) err = Orm.CreateTables(&User{}) diff --git a/examples/cachegoroutine.go b/examples/cachegoroutine.go index 925a16ca..0e50f5ad 100644 --- a/examples/cachegoroutine.go +++ b/examples/cachegoroutine.go @@ -2,10 +2,12 @@ package main import ( "fmt" + "os" + "time" + _ "github.com/go-sql-driver/mysql" "github.com/go-xorm/xorm" _ "github.com/mattn/go-sqlite3" - "os" ) type User struct { @@ -84,7 +86,7 @@ func main() { return } engine.ShowSQL = true - cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + cacher := xorm.NewLRUCacher2(xorm.NewMemoryStore(), time.Hour, 1000) engine.SetDefaultCacher(cacher) fmt.Println(engine) test(engine) @@ -94,7 +96,7 @@ func main() { fmt.Println("-----start mysql go routines-----") engine, err = mysqlEngine() engine.ShowSQL = true - cacher = xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) + cacher = xorm.NewLRUCacher2(xorm.NewMemoryStore(), time.Hour, 1000) engine.SetDefaultCacher(cacher) if err != nil { fmt.Println(err) diff --git a/examples/maxconnect.go b/examples/maxconnect.go index 8ec82d2a..e8446cfa 100644 --- a/examples/maxconnect.go +++ b/examples/maxconnect.go @@ -34,7 +34,7 @@ func test(engine *xorm.Engine) { } engine.ShowSQL = true - engine.Pool.SetMaxConns(5) + engine.SetMaxOpenConns(5) size := 1000 queue := make(chan int, size) diff --git a/examples/pool.go b/examples/pool.go deleted file mode 100644 index 7511bb6c..00000000 --- a/examples/pool.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/go-xorm/xorm" - _ "github.com/mattn/go-sqlite3" -) - -type User struct { - Id int64 - Name string -} - -func main() { - f := "pool.db" - os.Remove(f) - - Orm, err := xorm.NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - err = Orm.SetPool(xorm.NewSimpleConnectPool()) - if err != nil { - fmt.Println(err) - return - } - - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}) - if err != nil { - fmt.Println(err) - return - } - - for i := 0; i < 10; i++ { - _, err = Orm.Get(&User{}) - if err != nil { - fmt.Println(err) - break - } - - } -} diff --git a/examples/sync.go b/examples/sync.go index f26026aa..ad28ad80 100644 --- a/examples/sync.go +++ b/examples/sync.go @@ -2,9 +2,10 @@ package main import ( "fmt" - _ "github.com/bylevel/pq" + _ "github.com/go-sql-driver/mysql" "github.com/go-xorm/xorm" + _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) diff --git a/session.go b/session.go index 231f18c5..c67c46b1 100644 --- a/session.go +++ b/session.go @@ -1351,10 +1351,10 @@ func (session *Session) addIndex(tableName, idxName string) error { if session.IsAutoClose { defer session.Close() } - //fmt.Println(idxName) - cols := session.Statement.RefTable.Indexes[idxName].Cols - sqlStr, args := session.Statement.genAddIndexStr(indexName(tableName, idxName), cols) - _, err = session.exec(sqlStr, args...) + index := session.Statement.RefTable.Indexes[idxName] + sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) + //genAddIndexStr(indexName(tableName, idxName), cols) + _, err = session.exec(sqlStr) return err } @@ -1368,9 +1368,9 @@ func (session *Session) addUnique(tableName, uqeName string) error { defer session.Close() } //fmt.Println(uqeName, session.Statement.RefTable.Uniques) - cols := session.Statement.RefTable.Indexes[uqeName].Cols - sqlStr, args := session.Statement.genAddUniqueStr(uniqueName(tableName, uqeName), cols) - _, err = session.exec(sqlStr, args...) + index := session.Statement.RefTable.Indexes[uqeName] + sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index) + _, err = session.exec(sqlStr) return err } diff --git a/statement.go b/statement.go index 9330a14d..b6c1152c 100644 --- a/statement.go +++ b/statement.go @@ -99,7 +99,8 @@ func (statement *Statement) Where(querystring string, args ...interface{}) *Stat // add Where & and statment func (statement *Statement) And(querystring string, args ...interface{}) *Statement { if statement.WhereStr != "" { - statement.WhereStr = fmt.Sprintf("(%v) AND (%v)", statement.WhereStr, querystring) + statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, + statement.Engine.dialect.AndStr(), querystring) } else { statement.WhereStr = querystring } @@ -110,7 +111,8 @@ func (statement *Statement) And(querystring string, args ...interface{}) *Statem // add Where & Or statment func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { if statement.WhereStr != "" { - statement.WhereStr = fmt.Sprintf("(%v) OR (%v)", statement.WhereStr, querystring) + statement.WhereStr = fmt.Sprintf("(%v) %s (%v)", statement.WhereStr, + statement.Engine.dialect.OrStr(), querystring) } else { statement.WhereStr = querystring } @@ -451,24 +453,10 @@ func (statement *Statement) Id(id interface{}) *Statement { statement.IdParam = &pk } default: - // TODO treat as int primitve for now, need to handle type check + // TODO: treat as int primitve for now, need to handle type check? statement.IdParam = &core.PK{id} - - // !nashtsai! REVIEW although it will be user's mistake if called Id() twice with - // different value and Id should be PK's field name, however, at this stage probably - // can't tell which table is gonna be used - // if statement.WhereStr == "" { - // statement.WhereStr = "(id)=?" - // statement.Params = []interface{}{id} - // } else { - // // TODO what if id param has already passed - // statement.WhereStr = statement.WhereStr + " AND (id)=?" - // statement.Params = append(statement.Params, id) - // } } - // !nashtsai! perhaps no need to validate pk values' type just let sql complaint happen - return statement } @@ -516,14 +504,14 @@ func (statement *Statement) genInSql() (string, []interface{}) { if len(statement.inColumns) == 1 { return inStrs[0], args } - return fmt.Sprintf("(%v)", strings.Join(inStrs, " AND ")), 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 statement.ConditionStr != "" { - statement.ConditionStr += " AND " + statement.ConditionStr += " " + statement.Engine.dialect.AndStr() + " " } statement.ConditionStr += inSql statement.Params = append(statement.Params, inArgs...) @@ -696,11 +684,9 @@ func uniqueName(tableName, uqeName string) string { func (s *Statement) genUniqueSQL() []string { var sqls []string = make([]string, 0) tbName := s.TableName() - quote := s.Engine.Quote - for idxName, unique := range s.RefTable.Indexes { - if unique.Type == core.UniqueType { - sql := fmt.Sprintf("CREATE UNIQUE INDEX %v ON %v (%v);", quote(uniqueName(tbName, idxName)), - quote(tbName), quote(strings.Join(unique.Cols, quote(",")))) + for _, index := range s.RefTable.Indexes { + if index.Type == core.UniqueType { + sql := s.Engine.dialect.CreateIndexSql(tbName, index) sqls = append(sqls, sql) } } @@ -737,7 +723,7 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) false, true, statement.allUseBool, statement.useAllCols, statement.mustColumnMap) - statement.ConditionStr = strings.Join(colNames, " AND ") + statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") statement.BeanArgs = args var columnStr string = statement.ColumnStr @@ -755,7 +741,7 @@ func (s *Statement) genAddColumnStr(col *core.Column) (string, []interface{}) { return sql, []interface{}{} } -func (s *Statement) genAddIndexStr(idxName string, cols []string) (string, []interface{}) { +/*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) @@ -767,7 +753,7 @@ func (s *Statement) genAddUniqueStr(uqeName string, cols []string) (string, []in 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) genCountSql(bean interface{}) (string, []interface{}) { table := statement.Engine.autoMap(bean) From 5de5a1c67881d3152a6dc4875e99ffb49990c92c Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Thu, 17 Apr 2014 17:08:21 +0800 Subject: [PATCH 42/55] tidy up example codes --- docs/QuickStart.md | 517 +++++++++---------------- docs/QuickStartCn.md | 850 +++++++++++++++++++++++++++++++++++++++++ docs/QuickStartEn.md | 693 --------------------------------- examples/cache.go | 3 +- examples/maxconnect.go | 2 +- examples/pool.go | 46 --- lru_cacher.go | 34 +- xorm.go | 5 - 8 files changed, 1046 insertions(+), 1104 deletions(-) create mode 100644 docs/QuickStartCn.md delete mode 100644 docs/QuickStartEn.md delete mode 100644 examples/pool.go diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 2fc373a7..e4e1755a 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -1,46 +1,42 @@ -xorm 快速入门 +Quick Start ===== -* [1.创建Orm引擎](#10) -* [2.定义表结构体](#20) - * [2.1.名称映射规则](#21) - * [2.2.前缀映射,后缀映射和缓存映射](#22) - * [2.3.使用Table和Tag改变名称映射](#23) - * [2.4.Column属性定义](#24) - * [2.5.Go与字段类型对应表](#25) -* [3.表结构操作](#30) - * [3.1 获取数据库信息](#31) - * [3.2 表操作](#32) - * [3.3 创建索引和唯一索引](#33) - * [3.4 同步数据库结构](#34) -* [4.插入数据](#50) -* [5.查询和统计数据](#60) - * [5.1.查询条件方法](#61) - * [5.2.临时开关方法](#62) - * [5.3.Get方法](#63) - * [5.4.Find方法](#64) - * [5.5.Iterate方法](#65) - * [5.6.Count方法](#66) - * [5.7.Rows方法](#67) -* [6.更新数据](#70) -* [6.1.乐观锁](#71) -* [7.删除数据](#80) -* [8.执行SQL查询](#90) -* [9.执行SQL命令](#100) -* [10.事务处理](#110) -* [11.缓存](#120) -* [12.事件](#125) -* [13.xorm工具](#130) - * [13.1.反转命令](#131) -* [14.Examples](#140) -* [15.案例](#150) -* [16.那些年我们踩过的坑](#160) -* [17.讨论](#170) +* [1.Create ORM Engine](#10) +* [2.Define a struct](#20) + * [2.1.Name mapping rule](#21) + * [2.2.Use Table or Tag to change table or column name](#22) + * [2.3.Column define](#23) +* [3. database schema operation](#30) + * [3.1.Retrieve database schema infomation](#31) + * [3.2.Table Operation](#32) + * [3.3.Create indexes and uniques](#33) + * [3.4.Sync database schema](#34) +* [4.Insert records](#40) +* [5.Query and Count records](#60) + * [5.1.Query condition methods](#61) + * [5.2.Temporory methods](#62) + * [5.3.Get](#63) + * [5.4.Find](#64) + * [5.5.Iterate](#65) + * [5.6.Count](#66) +* [6.Update records](#70) +* [6.1.Optimistic Locking](#71) +* [7.Delete records](#80) +* [8.Execute SQL command](#90) +* [9.Execute SQL query](#100) +* [10.Transaction](#110) +* [11.Cache](#120) +* [12.Xorm Tool](#130) + * [12.1.Reverse command](#131) +* [13.Examples](#140) +* [14.Cases](#150) +* [15.FAQ](#160) +* [16.Discuss](#170) -## 1.创建Orm引擎 +## 1.Create ORM Engine -在xorm里面,可以同时存在多个Orm引擎,一个Orm引擎称为Engine。因此在使用前必须调用NewEngine,如: +When using xorm, you can create multiple orm engines, an engine means a databse. So you can: ```Go import ( @@ -62,14 +58,11 @@ engine, err = xorm.NewEngine("sqlite3", "./test.db") defer engine.Close() ``` -一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 +Generally, you can only create one engine. Engine supports run on go rutines. -xorm当前支持五种驱动四个数据库如下: -<<<<<<< HEAD -======= +xorm supports four drivers now: * Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) ->>>>>>> master * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) @@ -77,20 +70,18 @@ xorm当前支持五种驱动四个数据库如下: * Postgres: [github.com/lib/pq](https://github.com/lib/pq) -* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc) +NewEngine's parameters are the same as `sql.Open`. So you should read the drivers' document for parameters' usage. -NewEngine传入的参数和`sql.Open`传入的参数完全相同,因此,使用哪个驱动前,请查看此驱动中关于传入参数的说明文档。 +After engine created, you can do some settings. -在engine创建完成后可以进行一些设置,如: +1.Logs -1.错误显示设置,默认如下均为`false` +* `engine.ShowSQL = true`, Show SQL statement on standard output; +* `engine.ShowDebug = true`, Show debug infomation on standard output; +* `engine.ShowError = true`, Show error infomation on standard output; +* `engine.ShowWarn = true`, Show warnning information on standard output; -* `engine.ShowSQL = true`,则会在控制台打印出生成的SQL语句; -* `engine.ShowDebug = true`,则会在控制台打印调试信息; -* `engine.ShowError = true`,则会在控制台打印错误信息; -* `engine.ShowWarn = true`,则会在控制台打印警告信息; - -2.如果希望用其它方式记录,则可以`engine.Logger`赋值为一个`io.Writer`的实现。比如记录到Log文件,则可以: +2.If want to record infomation with another method: use `engine.Logger` as `io.Writer`: ```Go f, err := os.Create("sql.log") @@ -101,62 +92,47 @@ f, err := os.Create("sql.log") engine.Logger = f ``` -3.engine内部支持连接池接口,默认使用的Go所实现的连接池,同时自带了另外两种实现:一种是不使用连接池,另一种为一个自实现的连接池。推荐使用Go所实现的连接池。如果要使用自己实现的连接池,可以实现`xorm.IConnectPool`并通过`engine.SetPool`进行设置。推荐使用Go默认的连接池。 - -* 如果需要设置连接池的空闲数大小,可以使用`engine.SetIdleConns()`来实现。 -* 如果需要设置最大打开连接数,则可以使用`engine.SetMaxConns()`来实现。 - -## 2.定义表结构体 +## 2.Define struct -xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下: +xorm map a struct to a database table, the rule is below. -### 2.1.名称映射规则 +### 2.1.name mapping rule -名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名。 +use xorm.IMapper interface to implement. There are two IMapper implemented: `SnakeMapper` and `SameMapper`. SnakeMapper means struct name is word by word and table name or column name as 下划线. SameMapper means same name between struct and table. -当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用 +SnakeMapper is the default. ```Go -engine.SetMapper(SameMapper{}) +engine.Mapper = SameMapper{} ``` 同时需要注意的是: -<<<<<<< HEAD * 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 * 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: -======= -* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: - ->>>>>>> master ```Go engine.SetTableMapper(SameMapper{}) engine.SetColumnMapper(SnakeMapper{}) ``` -### 2.2.前缀映射,后缀映射和缓存映射 +### 2.2.前缀映射规则,后缀映射规则和缓存映射规则 * 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 * 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 -<<<<<<< HEAD -* -======= -* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。 ->>>>>>> master +* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以起到在内存中缓存曾经映射过的命名映射。 - +当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 + + ### 2.3.使用Table和Tag改变名称映射 如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。 -* 如果struct拥有`Tablename() string`的成员方法,那么此方法的返回值即是该struct默认对应的数据库表名。 - -* 通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。 +通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。 ### 2.4.Column属性定义 @@ -169,16 +145,16 @@ type User struct { } ``` -对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。 +对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。 具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: - + - + @@ -187,7 +163,7 @@ type User struct { - + @@ -208,13 +184,13 @@ type User struct { - + - + - - + + @@ -223,11 +199,11 @@ type User struct { 另外有如下几条自动映射的规则: -- 1.如果field名称为`Id`而且类型为`int64`并且没有定义tag,则会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字或非int64类型做为主键名,必须在对应的Tag上加上`xorm:"pk"`来定义主键,加上`xorm:"autoincr"`作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性。 +- 1.如果field名称为`Id`而且类型为`int64`的话,会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字做为主键名,可以在对应的Tag上加上`xorm:"pk"`来定义主键。 - 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义 -- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) +- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。 - 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换。 ```Go @@ -237,13 +213,6 @@ type Conversion interface { } ``` - -### 2.4.Go与字段类型对应表 - -如果不使用tag来定义field对应的数据库字段类型,那么系统会自动给出一个默认的字段类型,对应表如下: - -[go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) - ## 3.表结构操作 @@ -253,50 +222,41 @@ xorm提供了一些动态获取和修改表结构的方法。对于一般的应 ## 3.1 获取数据库信息 * DBMetas() - -xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表,字段,索引的信息。 +xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表的信息 ## 3.2.表操作 * CreateTables() - 创建表使用`engine.CreateTables()`,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 * IsTableEmpty() - 判断表是否为空,参数和CreateTables相同 * IsTableExist() - 判断表是否存在 * DropTables() - 删除表使用`engine.DropTables()`,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。 ## 3.3.创建索引和唯一索引 * CreateIndexes - 根据struct中的tag来创建索引 * CreateUniques - 根据struct中的tag来创建唯一索引 ## 3.4.同步数据库结构 同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现: - -* 1) 自动检测和创建表,这个检测是根据表的名字 -* 2)自动检测和新增表中的字段,这个检测是根据字段名 -* 3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 +1) 自动检测和创建表,这个检测是根据表的名字 +2)自动检测和新增表中的字段,这个检测是根据字段名 +3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 调用方法如下: - ```Go err := engine.Sync(new(User)) ``` @@ -304,25 +264,21 @@ err := engine.Sync(new(User)) ## 4.插入数据 -插入数据使用Insert方法,Insert方法的参数可以是一个或多个Struct的指针,一个或多个Struct的Slice的指针。 -如果传入的是Slice并且当数据库支持批量插入时,Insert会使用批量插入的方式进行插入。 - -* 插入一条数据 +Inserting records use Insert method. +* Insert one record ```Go user := new(User) user.Name = "myname" affected, err := engine.Insert(user) ``` -在插入单条数据成功后,如果该结构体有自增字段,则自增字段会被自动赋值为数据库中的id - +After inseted, `user.Id` will be filled with primary key column value. ```Go fmt.Println(user.Id) ``` -* 插入同一个表的多条数据 - +* Insert multiple records by Slice on one table ```Go users := make([]User, 0) users[0].Name = "name0" @@ -330,8 +286,7 @@ users[0].Name = "name0" affected, err := engine.Insert(&users) ``` -* 使用指针Slice插入多条记录 - +* Insert multiple records by Slice of pointer on one table ```Go users := make([]*User, 0) users[0] = new(User) @@ -340,8 +295,7 @@ users[0].Name = "name0" affected, err := engine.Insert(&users) ``` -* 插入不同表的一条记录 - +* Insert one record on two table. ```Go user := new(User) user.Name = "myname" @@ -350,8 +304,7 @@ question.Content = "whywhywhwy?" affected, err := engine.Insert(user, question) ``` -* 插入不同表的多条记录 - +* Insert multiple records on multiple tables. ```Go users := make([]User, 0) users[0].Name = "name0" @@ -361,7 +314,7 @@ questions[0].Content = "whywhywhwy?" affected, err := engine.Insert(&users, &questions) ``` -* 插入不同表的一条或多条记录 +* Insert one or multple records on multiple tables. ```Go user := new(User) user.Name = "myname" @@ -371,27 +324,23 @@ questions[0].Content = "whywhywhwy?" affected, err := engine.Insert(user, &questions) ``` -这里需要注意以下几点: -* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。 -* 多条插入会自动生成`Insert into table values (),(),()`的语句,因此这样的语句有一个最大的记录数,根据经验测算在150条左右。大于150条后,生成的sql语句将太长可能导致执行失败。因此在插入大量数据时,目前需要自行分割成每150条插入一次。 +Notice: If you want to use transaction on inserting, you should use session.Begin() before calling Insert. -## 5.查询和统计数据 +## 5.Query and count -所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count, Iterate, Rows这几个函数之前调用。同时需要注意的一点是,在调用的参数中,如果采用默认的`SnakeMapper`所有的字符字段名均为映射后的数据库的字段名,而不是field的名字。 +所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count这三个函数之前调用。同时需要注意的一点是,在调用的参数中,所有的字符字段名均为映射后的数据库的字段名,而不是field的名字。 ### 5.1.查询条件方法 -查询和统计主要使用`Get`, `Find`, `Count`, `Rows`, `Iterate`这几个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下: +查询和统计主要使用`Get`, `Find`, `Count`三个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下: -* Id(interface{}) -传入一个PK字段的值,作为查询条件,如果是复合主键,则 -`Id(xorm.PK{1, 2})` -传入的两个参数按照struct中pk标记字段出现的顺序赋值。 +* Id(int64) +传入一个PK字段的值,作为查询条件 * Where(string, …interface{}) -和SQL中Where语句中的条件基本相同,作为条件 +和Where语句中的条件基本相同,作为条件 * And(string, …interface{}) 和Where函数中的条件基本相同,作为条件 @@ -412,7 +361,7 @@ affected, err := engine.Insert(user, &questions) 按照指定的顺序进行排序 * In(string, …interface{}) -某字段在一些值中,这里需要注意必须是[]interface{}才可以展开,由于Go语言的限制,[]int64等均不可以展开。 +某字段在一些值中 * Cols(…string) 只查询或更新某些指定的字段,默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如: @@ -423,11 +372,7 @@ engine.Cols("age", "name").Update(&user) // UPDATE user SET age=? AND name=? ``` -* AllCols() -查询或更新所有字段。 - -* MustCols(…string) -某些字段必须更新。 +其中的参数"age", "name"也可以写成"age, name",两种写法均可 * Omit(...string) 和cols相反,此函数指定排除某些指定的字段。注意:此方法和Cols方法不可同时使用 @@ -474,84 +419,65 @@ Having的参数字符串 * UseBool(...string) 当从一个struct来生成查询条件或更新字段时,xorm会判断struct的field是否为0,"",nil,如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值,因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法,如果默认不传参数,则所有的bool字段都将会被使用,如果参数不为空,则参数中指定的为字段名,则这些字段对应的bool值将被使用。 -* NoCascade() +* Cascade(bool) 是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。 - -### 5.3.Get方法 - -查询单条数据使用`Get`方法,在调用Get方法时需要传入一个对应结构体的指针,同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询。 - -如: - -1) 根据Id来获得单条数据: + +### 5.3.Get one record +Fetch a single object by user ```Go -user := new(User) -has, err := engine.Id(id).Get(user) -// 复合主键的获取方法 -// has, errr := engine.Id(xorm.PK{1,2}).Get(user) +var user = User{Id:27} +has, err := engine.Get(&user) +// or has, err := engine.Id(27).Get(&user) + +var user = User{Name:"xlw"} +has, err := engine.Get(&user) ``` -2) 根据Where来获得单条数据: + +### 5.4.Find +Fetch multipe objects into a slice or a map, use Find: ```Go -user := new(User) -has, err := engine.Where("name=?", "xlw").Get(user) -``` - -3) 根据user结构体中已有的非空数据来获得单条数据: - -```Go -user := &User{Id:1} -has, err := engine.Get(user) -``` - -或者其它条件 - -```Go -user := &User{Name:"xlw"} -has, err := engine.Get(user) -``` - -返回的结果为两个参数,一个`has`为该条记录是否存在,第二个参数`err`为是否有错误。不管err是否为nil,has都有可能为true或者false。 - - -### 5.4.Find方法 - -查询多条数据使用`Find`方法,Find方法的第一个参数为`slice`的指针或`Map`指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针。 - -1) 传入Slice用于返回数据 - -```Go -everyone := make([]Userinfo, 0) +var everyone []Userinfo err := engine.Find(&everyone) -pEveryOne := make([]*Userinfo, 0) -err := engine.Find(&pEveryOne) -``` - -2) 传入Map用户返回数据,map必须为`map[int64]Userinfo`的形式,map的key为id,因此对于复合主键无法使用这种方式。 - -```Go users := make(map[int64]Userinfo) err := engine.Find(&users) - -pUsers := make(map[int64]*Userinfo) -err := engine.Find(&pUsers) ``` -3) 也可以加入各种条件 +* also you can use Where, Limit ```Go -users := make([]Userinfo, 0) -err := engine.Where("age > ? or name = ?", 30, "xlw").Limit(20, 10).Find(&users) +var allusers []Userinfo +err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20 ``` - -### 5.5.Iterate方法 +* or you can use a struct query -Iterate方法提供逐条执行查询到的记录的方法,他所能使用的条件和Find方法完全相同 +```Go +var tenusers []Userinfo +err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0 +``` + +* or In function + +```Go +var tenusers []Userinfo +err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5) +``` + +* The default will query all columns of a table. Use Cols function if you want to select some columns + +```Go +var tenusers []Userinfo +err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name +``` + + +### 5.5.Iterate records +Iterate, like find, but handle records one by one ```Go err := engine.Where("age > ? or name=?)", 30, "xlw").Iterate(new(Userinfo), func(i int, bean interface{})error{ @@ -569,22 +495,6 @@ user := new(User) total, err := engine.Where("id >?", 1).Count(user) ``` - -### 5.7.Rows方法 - -Rows方法和Iterate方法类似,提供逐条执行查询到的记录的方法,不过Rows更加灵活好用。 -```Go -user := new(User) -rows, err := engine.Where("id >?", 1).Rows(user) -if err != nil { -} -defer rows.Close() -for rows.Next() { - err = rows.Scan(user) - //... -} -``` - ## 6.更新数据 @@ -598,27 +508,17 @@ affected, err := engine.Id(id).Update(user) 这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择: -<<<<<<< HEAD 1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 -======= -* 1.通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 ->>>>>>> master - ```Go affected, err := engine.Id(id).Cols("age").Update(&user) ``` -<<<<<<< HEAD 2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 -======= -* 2.通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 ->>>>>>> master - ```Go affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) ``` - + ### 6.1.乐观锁 要使用乐观锁,需要使用version标记 @@ -638,51 +538,53 @@ engine.Id(1).Update(&user) // UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ? ``` - -## 7.删除数据 -删除数据`Delete`方法,参数为struct的指针并且成为查询条件。 + +## 7.Delete one or more records +Delete one or more records + +* delete by id ```Go -user := new(User) -affected, err := engine.Id(id).Delete(user) +err := engine.Id(1).Delete(&User{}) ``` -`Delete`的返回值第一个参数为删除的记录数,第二个参数为错误。 +* delete by other conditions -注意:当删除时,如果user中包含有bool,float64或者float32类型,有可能会使删除失败。具体请查看 FAQ +```Go +err := engine.Delete(&User{Name:"xlw"}) +``` -## 8.执行SQL查询 +## 8.Execute SQL query -也可以直接执行一个SQL查询,即Select命令。在Postgres中支持原始SQL语句中使用 ` 和 ? 符号。 +Of course, SQL execution is also provided. + +If select then use Query ```Go sql := "select * from userinfo" results, err := engine.Query(sql) ``` -当调用`Query`时,第一个返回值`results`为`[]map[string][]byte`的形式。 - -## 9.执行SQL命令 - -也可以直接执行一个SQL命令,即执行Insert, Update, Delete 等操作。此时不管数据库是何种类型,都可以使用 ` 和 ? 符号。 +## 9.Execute SQL command +If insert, update or delete then use Exec ```Go -sql = "update `userinfo` set username=? where id=?" +sql = "update userinfo set username=? where id=?" res, err := engine.Exec(sql, "xiaolun", 1) ``` -## 10.事务处理 -当使用事务处理时,需要创建Session对象。在进行事物处理时,可以混用ORM方法和RAW方法,如下代码所示: +## 10.Transaction ```Go session := engine.NewSession() defer session.Close() + // add Begin() before any action -err := session.Begin() +err := session.Begin() user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} _, err = session.Insert(&user1) if err != nil { @@ -709,147 +611,78 @@ if err != nil { } ``` -* 注意如果您使用的是mysql,数据库引擎为innodb事务才有效,myisam引擎是不支持事务的。 - ## 11.缓存 -xorm内置了一致性缓存支持,不过默认并没有开启。要开启缓存,需要在engine创建完后进行配置,如: -启用一个全局的内存缓存 +1. Global Cache +Xorm implements cache support. Defaultly, it's disabled. If enable it, use below code. ```Go cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) engine.SetDefaultCacher(cacher) ``` -上述代码采用了LRU算法的一个缓存,缓存方式是存放到内存中,缓存struct的记录数为1000条,缓存针对的范围是所有具有主键的表,没有主键的表中的数据将不会被缓存。 -如果只想针对部分表,则: +If disable some tables' cache, then: + +```Go +engine.MapCacher(&user, nil) +``` + +2. Table's Cache +If only some tables need cache, then: ```Go cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) engine.MapCacher(&user, cacher) ``` -如果要禁用某个表的缓存,则: +Caution: -```Go -engine.MapCacher(&user, nil) -``` +1. When use Cols methods on cache enabled, the system still return all the columns. -设置完之后,其它代码基本上就不需要改动了,缓存系统已经在后台运行。 - -当前实现了内存存储的CacheStore接口MemoryStore,如果需要采用其它设备存储,可以实现CacheStore接口。 - -不过需要特别注意不适用缓存或者需要手动编码的地方: - -1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存 - -2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。 - -3. 在使用Exec方法执行了方法之后,可能会导致缓存与数据库不一致的地方。因此如果启用缓存,尽量避免使用Exec。如果必须使用,则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如: +2. When using Exec method, you should clear cache: ```Go engine.Exec("update user set name = ? where id = ?", "xlw", 1) engine.ClearCache(new(User)) ``` -缓存的实现原理如下图所示: +Cache implement theory below: ![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png) - -## 12.事件 -xorm支持两种方式的事件,一种是在Struct中的特定方法来作为事件的方法,一种是在执行语句的过程中执行事件。 - -在Struct中作为成员方法的事件如下: - -* BeforeInsert() - -* BeforeUpdate() - -* BeforeDelete() - -* AfterInsert() - -* AfterUpdate() - -* AfterDelete() - -在语句执行过程中的事件方法为: - -* Before(beforeFunc interface{}) - -* After(afterFunc interface{}) - -其中beforeFunc和afterFunc的原型为func(bean interface{}). - -## 13.xorm工具 +## 12.xorm tool xorm工具提供了xorm命令,能够帮助做很多事情。 -### 13.1.反转命令 -参见 [xorm工具](https://github.com/go-xorm/xorm/tree/master/xorm) +### 12.1.Reverse command +Please visit [xorm tool](https://github.com/go-xorm/xorm/tree/master/xorm) -## 14.Examples +## 13.Examples 请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples) -## 15.案例 +## 14.Cases -* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) +* [Gowalker](http://gowalker.org),source [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) -* [GoDaily Go语言学习网站](http://godaily.org),源代码 [github.com/govc/godaily](http://github.com/govc/godaily) +* [GoDaily](http://godaily.org),source [github.com/govc/godaily](http://github.com/govc/godaily) -* [Sudochina](http://sudochina.com) 和对应的源代码[github.com/insionng/toropress](http://github.com/insionng/toropress) +* [Sudochina](http://sudochina.com) source [github.com/insionng/toropress](http://github.com/insionng/toropress) * [VeryHour](http://veryhour.com) - -## 16.那些年我们踩过的坑 -* 怎么同时使用xorm的tag和json的tag? + +## 15.FAQ + +1.How the xorm tag use both with json? -答:使用空格 + Use space. ```Go type User struct { Name string `json:"name" xorm:"name"` } ``` - -* 我的struct里面包含bool类型,为什么它不能作为条件也没法用Update更新? - -答:默认bool类型因为无法判断是否为空,所以不会自动作为条件也不会作为Update的内容。可以使用UseBool函数,也可以使用Cols函数 - -```Go -engine.Cols("bool_field").Update(&Struct{BoolField:true}) -// UPDATE struct SET bool_field = true -``` - -* 我的struct里面包含float64和float32类型,为什么用他们作为查询条件总是不正确? - -答:默认float32和float64映射到数据库中为float,real,double这几种类型,这几种数据库类型数据库的实现一般都是非精确的。因此作为相等条件查询有可能不会返回正确的结果。如果一定要作为查询条件,请将数据库中的类型定义为Numeric或者Decimal。 - -```Go -type account struct { -money float64 `xorm:"Numeric"` -} -``` - -* 为什么Update时Sqlite3返回的affected和其它数据库不一样? - -答:Sqlite3默认Update时返回的是update的查询条件的记录数条数,不管记录是否真的有更新。而Mysql和Postgres默认情况下都是只返回记录中有字段改变的记录数。 - -* xorm有几种命名映射规则? - -答:目前支持SnakeMapper和SameMapper两种。SnakeMapper支持结构体和成员以驼峰式命名而数据库表和字段以下划线连接命名;SameMapper支持结构体和数据库的命名保持一致的映射。 - -* xorm支持复合主键吗? - -答:支持。在定义时,如果有多个字段标记了pk,则这些字段自动成为复合主键,顺序为在struct中出现的顺序。在使用Id方法时,可以用`Id(xorm.PK{1, 2})`的方式来用。 - - - -## 17.讨论 -请加入QQ群:280360085 进行讨论。 diff --git a/docs/QuickStartCn.md b/docs/QuickStartCn.md new file mode 100644 index 00000000..b1629d1b --- /dev/null +++ b/docs/QuickStartCn.md @@ -0,0 +1,850 @@ +xorm 快速入门 +===== + +* [1.创建Orm引擎](#10) +* [2.定义表结构体](#20) + * [2.1.名称映射规则](#21) + * [2.2.前缀映射,后缀映射和缓存映射](#22) + * [2.3.使用Table和Tag改变名称映射](#23) + * [2.4.Column属性定义](#24) + * [2.5.Go与字段类型对应表](#25) +* [3.表结构操作](#30) + * [3.1 获取数据库信息](#31) + * [3.2 表操作](#32) + * [3.3 创建索引和唯一索引](#33) + * [3.4 同步数据库结构](#34) +* [4.插入数据](#50) +* [5.查询和统计数据](#60) + * [5.1.查询条件方法](#61) + * [5.2.临时开关方法](#62) + * [5.3.Get方法](#63) + * [5.4.Find方法](#64) + * [5.5.Iterate方法](#65) + * [5.6.Count方法](#66) + * [5.7.Rows方法](#67) +* [6.更新数据](#70) +* [6.1.乐观锁](#71) +* [7.删除数据](#80) +* [8.执行SQL查询](#90) +* [9.执行SQL命令](#100) +* [10.事务处理](#110) +* [11.缓存](#120) +* [12.事件](#125) +* [13.xorm工具](#130) + * [13.1.反转命令](#131) +* [14.Examples](#140) +* [15.案例](#150) +* [16.那些年我们踩过的坑](#160) +* [17.讨论](#170) + + +## 1.创建Orm引擎 + +在xorm里面,可以同时存在多个Orm引擎,一个Orm引擎称为Engine。因此在使用前必须调用NewEngine,如: + +```Go +import ( + _ "github.com/go-sql-driver/mysql" + "github.com/go-xorm/xorm" +) +engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8") +defer engine.Close() +``` + +or + +```Go +import ( + _ "github.com/mattn/go-sqlite3" + "github.com/go-xorm/xorm" + ) +engine, err = xorm.NewEngine("sqlite3", "./test.db") +defer engine.Close() +``` + +一般如果只针对一个数据库进行操作,只需要创建一个Engine即可。Engine支持在多GoRutine下使用。 + +xorm当前支持五种驱动四个数据库如下: +<<<<<<< HEAD +======= + +* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) +>>>>>>> master + +* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) + +* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) + +* Postgres: [github.com/lib/pq](https://github.com/lib/pq) + +* MsSql: [github.com/lunny/godbc](https://githubcom/lunny/godbc) + +NewEngine传入的参数和`sql.Open`传入的参数完全相同,因此,使用哪个驱动前,请查看此驱动中关于传入参数的说明文档。 + +在engine创建完成后可以进行一些设置,如: + +1.错误显示设置,默认如下均为`false` + +* `engine.ShowSQL = true`,则会在控制台打印出生成的SQL语句; +* `engine.ShowDebug = true`,则会在控制台打印调试信息; +* `engine.ShowError = true`,则会在控制台打印错误信息; +* `engine.ShowWarn = true`,则会在控制台打印警告信息; + +2.如果希望用其它方式记录,则可以`engine.Logger`赋值为一个`io.Writer`的实现。比如记录到Log文件,则可以: + +```Go +f, err := os.Create("sql.log") + if err != nil { + println(err.Error()) + return + } +engine.Logger = f +``` + + +## 2.定义表结构体 + +xorm支持将一个struct映射为数据库中对应的一张表。映射规则如下: + + +### 2.1.名称映射规则 + +名称映射规则主要负责结构体名称到表名和结构体field到表字段的名称映射。由xorm.IMapper接口的实现者来管理,xorm内置了两种IMapper实现:`SnakeMapper` 和 `SameMapper`。SnakeMapper支持struct为驼峰式命名,表结构为下划线命名之间的转换;SameMapper支持结构体名称和对应的表名称以及结构体field名称与对应的表字段名称相同的命名。 + +当前SnakeMapper为默认值,如果需要改变时,在engine创建完成后使用 + +```Go +engine.SetMapper(SameMapper{}) +``` + +同时需要注意的是: + +<<<<<<< HEAD +* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 +* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: + +======= +* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 +* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: + +>>>>>>> master +```Go +engine.SetTableMapper(SameMapper{}) +engine.SetColumnMapper(SnakeMapper{}) +``` + + +### 2.2.前缀映射,后缀映射和缓存映射 + +* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 +* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 +<<<<<<< HEAD +* +======= +* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以组合其它的映射规则,起到在内存中缓存曾经映射过的命名映射。 +>>>>>>> master + + +### 2.3.使用Table和Tag改变名称映射 + +如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。 + +* 如果struct拥有`Tablename() string`的成员方法,那么此方法的返回值即是该struct默认对应的数据库表名。 + +* 通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'column_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。 + + +### 2.4.Column属性定义 +我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如: + +``` +type User struct { + Id int64 + Name string `xorm:"varchar(25) not null unique 'usr_name'"` +} +``` + +对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。对于使用者,一般只要使用自己熟悉的数据库字段定义即可。 + +具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: + +
name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名,如与其它关键字冲突,请使用单引号括起来。name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名
pk是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。pk是否是Primary Key,当前仅支持int64类型
当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型autoincr是否是自增
[not ]null 或 notnull是否可以为空[not ]null是否可以为空
unique或unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引<-这个Field将只从数据库读取,而不写入到数据库
created这个Field将在Insert时自动赋值为当前时间createdThis field will be filled in current time on insert
updated这个Field将在Insert或Update时自动赋值为当前时间updatedThis field will be filled in current time on insert or update
version这个Field将会在insert时默认为1,每次更新自动加1
versionThis field will be filled 1 on insert and autoincrement on update
default 0设置默认值,紧跟的内容如果是Varchar等需要加上单引号
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名,如与其它关键字冲突,请使用单引号括起来。
pk是否是Primary Key,如果在一个struct中有多个字段都使用了此标记,则这多个字段构成了复合主键,单主键当前支持int32,int,int64,uint32,uint,uint64,string这7种Go的数据类型,复合主键支持这7种Go的数据类型的组合。
当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型
autoincr是否是自增
[not ]null 或 notnull是否可以为空
unique或unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引
index或index(indexname)是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引
extends应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中
-这个Field将不进行字段映射
->这个Field将只写入到数据库而不从数据库读取
<-这个Field将只从数据库读取,而不写入到数据库
created这个Field将在Insert时自动赋值为当前时间
updated这个Field将在Insert或Update时自动赋值为当前时间
version这个Field将会在insert时默认为1,每次更新自动加1
default 0设置默认值,紧跟的内容如果是Varchar等需要加上单引号
+ +另外有如下几条自动映射的规则: + +- 1.如果field名称为`Id`而且类型为`int64`并且没有定义tag,则会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字或非int64类型做为主键名,必须在对应的Tag上加上`xorm:"pk"`来定义主键,加上`xorm:"autoincr"`作为自增。这里需要注意的是,有些数据库并不允许非主键的自增属性。 + +- 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义 + +- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。具体参见 [go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) + +- 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换。 +```Go +type Conversion interface { + FromDB([]byte) error + ToDB() ([]byte, error) +} +``` + + +### 2.4.Go与字段类型对应表 + +如果不使用tag来定义field对应的数据库字段类型,那么系统会自动给出一个默认的字段类型,对应表如下: + +[go类型<->数据库类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/AutoMap.md) + + +## 3.表结构操作 + +xorm提供了一些动态获取和修改表结构的方法。对于一般的应用,很少动态修改表结构,则只需调用Sync()同步下表结构即可。 + + +## 3.1 获取数据库信息 + +* DBMetas() + +xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表,字段,索引的信息。 + + +## 3.2.表操作 + +* CreateTables() + +创建表使用`engine.CreateTables()`,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 + +* IsTableEmpty() + +判断表是否为空,参数和CreateTables相同 + +* IsTableExist() + +判断表是否存在 + +* DropTables() + +删除表使用`engine.DropTables()`,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。 + + +## 3.3.创建索引和唯一索引 + +* CreateIndexes + +根据struct中的tag来创建索引 + +* CreateUniques + +根据struct中的tag来创建唯一索引 + + +## 3.4.同步数据库结构 + +同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现: + +* 1) 自动检测和创建表,这个检测是根据表的名字 +* 2)自动检测和新增表中的字段,这个检测是根据字段名 +* 3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 + +调用方法如下: + +```Go +err := engine.Sync(new(User)) +``` + + +## 4.插入数据 + +插入数据使用Insert方法,Insert方法的参数可以是一个或多个Struct的指针,一个或多个Struct的Slice的指针。 +如果传入的是Slice并且当数据库支持批量插入时,Insert会使用批量插入的方式进行插入。 + +* 插入一条数据 + +```Go +user := new(User) +user.Name = "myname" +affected, err := engine.Insert(user) +``` + +在插入单条数据成功后,如果该结构体有自增字段,则自增字段会被自动赋值为数据库中的id + +```Go +fmt.Println(user.Id) +``` + +* 插入同一个表的多条数据 + +```Go +users := make([]User, 0) +users[0].Name = "name0" +... +affected, err := engine.Insert(&users) +``` + +* 使用指针Slice插入多条记录 + +```Go +users := make([]*User, 0) +users[0] = new(User) +users[0].Name = "name0" +... +affected, err := engine.Insert(&users) +``` + +* 插入不同表的一条记录 + +```Go +user := new(User) +user.Name = "myname" +question := new(Question) +question.Content = "whywhywhwy?" +affected, err := engine.Insert(user, question) +``` + +* 插入不同表的多条记录 + +```Go +users := make([]User, 0) +users[0].Name = "name0" +... +questions := make([]Question, 0) +questions[0].Content = "whywhywhwy?" +affected, err := engine.Insert(&users, &questions) +``` + +* 插入不同表的一条或多条记录 +```Go +user := new(User) +user.Name = "myname" +... +questions := make([]Question, 0) +questions[0].Content = "whywhywhwy?" +affected, err := engine.Insert(user, &questions) +``` + +这里需要注意以下几点: +* 这里虽然支持同时插入,但这些插入并没有事务关系。因此有可能在中间插入出错后,后面的插入将不会继续。 +* 多条插入会自动生成`Insert into table values (),(),()`的语句,因此这样的语句有一个最大的记录数,根据经验测算在150条左右。大于150条后,生成的sql语句将太长可能导致执行失败。因此在插入大量数据时,目前需要自行分割成每150条插入一次。 + + +## 5.查询和统计数据 + +所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count, Iterate, Rows这几个函数之前调用。同时需要注意的一点是,在调用的参数中,如果采用默认的`SnakeMapper`所有的字符字段名均为映射后的数据库的字段名,而不是field的名字。 + + +### 5.1.查询条件方法 + +查询和统计主要使用`Get`, `Find`, `Count`, `Rows`, `Iterate`这几个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下: + +* Id(interface{}) +传入一个PK字段的值,作为查询条件,如果是复合主键,则 +`Id(xorm.PK{1, 2})` +传入的两个参数按照struct中pk标记字段出现的顺序赋值。 + +* Where(string, …interface{}) +和SQL中Where语句中的条件基本相同,作为条件 + +* And(string, …interface{}) +和Where函数中的条件基本相同,作为条件 + +* Or(string, …interface{}) +和Where函数中的条件基本相同,作为条件 + +* Sql(string, …interface{}) +执行指定的Sql语句,并把结果映射到结构体 + +* Asc(…string) +指定字段名正序排序 + +* Desc(…string) +指定字段名逆序排序 + +* OrderBy(string) +按照指定的顺序进行排序 + +* In(string, …interface{}) +某字段在一些值中,这里需要注意必须是[]interface{}才可以展开,由于Go语言的限制,[]int64等均不可以展开。 + +* Cols(…string) +只查询或更新某些指定的字段,默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如: +```Go +engine.Cols("age", "name").Find(&users) +// SELECT age, name FROM user +engine.Cols("age", "name").Update(&user) +// UPDATE user SET age=? AND name=? +``` + +* AllCols() +查询或更新所有字段。 + +* MustCols(…string) +某些字段必须更新。 + +* Omit(...string) +和cols相反,此函数指定排除某些指定的字段。注意:此方法和Cols方法不可同时使用 +```Go +engine.Cols("age").Update(&user) +// UPDATE user SET name = ? AND department = ? +``` + +* Distinct(…string) +按照参数中指定的字段归类结果 +```Go +engine.Distinct("age", "department").Find(&users) +// SELECT DISTINCT age, department FROM user +``` +注意:当开启了缓存时,此方法的调用将在当前查询中禁用缓存。因为缓存系统当前依赖Id,而此时无法获得Id + +* Table(nameOrStructPtr interface{}) +传入表名称或者结构体指针,如果传入的是结构体指针,则按照IMapper的规则提取出表名 + +* Limit(int, …int) +限制获取的数目,第一个参数为条数,第二个参数为可选,表示开始位置 + +* Top(int) +相当于Limit(int, 0) + +* Join(string,string,string) +第一个参数为连接类型,当前支持INNER, LEFT OUTER, CROSS中的一个值,第二个参数为表名,第三个参数为连接条件 + +* GroupBy(string) +Groupby的参数字符串 + +* Having(string) +Having的参数字符串 + + +### 5.2.临时开关方法 + +* NoAutoTime() +如果此方法执行,则此次生成的语句中Created和Updated字段将不自动赋值为当前时间 + +* NoCache() +如果此方法执行,则此次生成的语句则在非缓存模式下执行 + +* UseBool(...string) +当从一个struct来生成查询条件或更新字段时,xorm会判断struct的field是否为0,"",nil,如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值,因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法,如果默认不传参数,则所有的bool字段都将会被使用,如果参数不为空,则参数中指定的为字段名,则这些字段对应的bool值将被使用。 + +* NoCascade() +是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。 + + +### 5.3.Get方法 + +查询单条数据使用`Get`方法,在调用Get方法时需要传入一个对应结构体的指针,同时结构体中的非空field自动成为查询的条件和前面的方法条件组合在一起查询。 + +如: + +1) 根据Id来获得单条数据: + +```Go +user := new(User) +has, err := engine.Id(id).Get(user) +// 复合主键的获取方法 +// has, errr := engine.Id(xorm.PK{1,2}).Get(user) +``` + +2) 根据Where来获得单条数据: + +```Go +user := new(User) +has, err := engine.Where("name=?", "xlw").Get(user) +``` + +3) 根据user结构体中已有的非空数据来获得单条数据: + +```Go +user := &User{Id:1} +has, err := engine.Get(user) +``` + +或者其它条件 + +```Go +user := &User{Name:"xlw"} +has, err := engine.Get(user) +``` + +返回的结果为两个参数,一个`has`为该条记录是否存在,第二个参数`err`为是否有错误。不管err是否为nil,has都有可能为true或者false。 + + +### 5.4.Find方法 + +查询多条数据使用`Find`方法,Find方法的第一个参数为`slice`的指针或`Map`指针,即为查询后返回的结果,第二个参数可选,为查询的条件struct的指针。 + +1) 传入Slice用于返回数据 + +```Go +everyone := make([]Userinfo, 0) +err := engine.Find(&everyone) + +pEveryOne := make([]*Userinfo, 0) +err := engine.Find(&pEveryOne) +``` + +2) 传入Map用户返回数据,map必须为`map[int64]Userinfo`的形式,map的key为id,因此对于复合主键无法使用这种方式。 + +```Go +users := make(map[int64]Userinfo) +err := engine.Find(&users) + +pUsers := make(map[int64]*Userinfo) +err := engine.Find(&pUsers) +``` + +3) 也可以加入各种条件 + +```Go +users := make([]Userinfo, 0) +err := engine.Where("age > ? or name = ?", 30, "xlw").Limit(20, 10).Find(&users) +``` + + +### 5.5.Iterate方法 + +Iterate方法提供逐条执行查询到的记录的方法,他所能使用的条件和Find方法完全相同 + +```Go +err := engine.Where("age > ? or name=?)", 30, "xlw").Iterate(new(Userinfo), func(i int, bean interface{})error{ + user := bean.(*Userinfo) + //do somthing use i and user +}) +``` + + +### 5.6.Count方法 + +统计数据使用`Count`方法,Count方法的参数为struct的指针并且成为查询条件。 +```Go +user := new(User) +total, err := engine.Where("id >?", 1).Count(user) +``` + + +### 5.7.Rows方法 + +Rows方法和Iterate方法类似,提供逐条执行查询到的记录的方法,不过Rows更加灵活好用。 +```Go +user := new(User) +rows, err := engine.Where("id >?", 1).Rows(user) +if err != nil { +} +defer rows.Close() +for rows.Next() { + err = rows.Scan(user) + //... +} +``` + + +## 6.更新数据 + +更新数据使用`Update`方法,Update方法的第一个参数为需要更新的内容,可以为一个结构体指针或者一个Map[string]interface{}类型。当传入的为结构体指针时,只有非空和0的field才会被作为更新的字段。当传入的为Map类型时,key为数据库Column的名字,value为要更新的内容。 + +```Go +user := new(User) +user.Name = "myname" +affected, err := engine.Id(id).Update(user) +``` + +这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择: + +<<<<<<< HEAD +1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 +======= +* 1.通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 +>>>>>>> master + +```Go +affected, err := engine.Id(id).Cols("age").Update(&user) +``` + +<<<<<<< HEAD +2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 +======= +* 2.通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 +>>>>>>> master + +```Go +affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) +``` + + +### 6.1.乐观锁 + +要使用乐观锁,需要使用version标记 +type User struct { + Id int64 + Name string + Version int `xorm:"version"` +} + +在Insert时,version标记的字段将会被设置为1,在Update时,Update的内容必须包含version原来的值。 + +```Go +var user User +engine.Id(1).Get(&user) +// SELECT * FROM user WHERE id = ? +engine.Id(1).Update(&user) +// UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ? +``` + + +## 7.删除数据 + +删除数据`Delete`方法,参数为struct的指针并且成为查询条件。 + +```Go +user := new(User) +affected, err := engine.Id(id).Delete(user) +``` + +`Delete`的返回值第一个参数为删除的记录数,第二个参数为错误。 + +注意:当删除时,如果user中包含有bool,float64或者float32类型,有可能会使删除失败。具体请查看 FAQ + + +## 8.执行SQL查询 + +也可以直接执行一个SQL查询,即Select命令。在Postgres中支持原始SQL语句中使用 ` 和 ? 符号。 + +```Go +sql := "select * from userinfo" +results, err := engine.Query(sql) +``` + +当调用`Query`时,第一个返回值`results`为`[]map[string][]byte`的形式。 + + +## 9.执行SQL命令 + +也可以直接执行一个SQL命令,即执行Insert, Update, Delete 等操作。此时不管数据库是何种类型,都可以使用 ` 和 ? 符号。 + +```Go +sql = "update `userinfo` set username=? where id=?" +res, err := engine.Exec(sql, "xiaolun", 1) +``` + + +## 10.事务处理 +当使用事务处理时,需要创建Session对象。在进行事物处理时,可以混用ORM方法和RAW方法,如下代码所示: + +```Go +session := engine.NewSession() +defer session.Close() +// add Begin() before any action +err := session.Begin() +user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} +_, err = session.Insert(&user1) +if err != nil { + session.Rollback() + return +} +user2 := Userinfo{Username: "yyy"} +_, err = session.Where("id = ?", 2).Update(&user2) +if err != nil { + session.Rollback() + return +} + +_, err = session.Exec("delete from userinfo where username = ?", user2.Username) +if err != nil { + session.Rollback() + return +} + +// add Commit() after all actions +err = session.Commit() +if err != nil { + return +} +``` + +* 注意如果您使用的是mysql,数据库引擎为innodb事务才有效,myisam引擎是不支持事务的。 + + +## 11.缓存 + +xorm内置了一致性缓存支持,不过默认并没有开启。要开启缓存,需要在engine创建完后进行配置,如: +启用一个全局的内存缓存 + +```Go +cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) +engine.SetDefaultCacher(cacher) +``` + +上述代码采用了LRU算法的一个缓存,缓存方式是存放到内存中,缓存struct的记录数为1000条,缓存针对的范围是所有具有主键的表,没有主键的表中的数据将不会被缓存。 +如果只想针对部分表,则: + +```Go +cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) +engine.MapCacher(&user, cacher) +``` + +如果要禁用某个表的缓存,则: + +```Go +engine.MapCacher(&user, nil) +``` + +设置完之后,其它代码基本上就不需要改动了,缓存系统已经在后台运行。 + +当前实现了内存存储的CacheStore接口MemoryStore,如果需要采用其它设备存储,可以实现CacheStore接口。 + +不过需要特别注意不适用缓存或者需要手动编码的地方: + +1. 当使用了`Distinct`,`Having`,`GroupBy`方法将不会使用缓存 + +2. 在`Get`或者`Find`时使用了`Cols`,`Omit`方法,则在开启缓存后此方法无效,系统仍旧会取出这个表中的所有字段。 + +3. 在使用Exec方法执行了方法之后,可能会导致缓存与数据库不一致的地方。因此如果启用缓存,尽量避免使用Exec。如果必须使用,则需要在使用了Exec之后调用ClearCache手动做缓存清除的工作。比如: + +```Go +engine.Exec("update user set name = ? where id = ?", "xlw", 1) +engine.ClearCache(new(User)) +``` + +缓存的实现原理如下图所示: + +![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png) + + +## 12.事件 +xorm支持两种方式的事件,一种是在Struct中的特定方法来作为事件的方法,一种是在执行语句的过程中执行事件。 + +在Struct中作为成员方法的事件如下: + +* BeforeInsert() + +* BeforeUpdate() + +* BeforeDelete() + +* AfterInsert() + +* AfterUpdate() + +* AfterDelete() + +在语句执行过程中的事件方法为: + +* Before(beforeFunc interface{}) + +* After(afterFunc interface{}) + +其中beforeFunc和afterFunc的原型为func(bean interface{}). + + +## 13.xorm工具 +xorm工具提供了xorm命令,能够帮助做很多事情。 + +### 13.1.反转命令 +参见 [xorm工具](https://github.com/go-xorm/xorm/tree/master/xorm) + + +## 14.Examples + +请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples) + + +## 15.案例 + +* [Gowalker](http://gowalker.org),源代码 [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) + +* [GoDaily Go语言学习网站](http://godaily.org),源代码 [github.com/govc/godaily](http://github.com/govc/godaily) + +* [Sudochina](http://sudochina.com) 和对应的源代码[github.com/insionng/toropress](http://github.com/insionng/toropress) + +* [VeryHour](http://veryhour.com) + + +## 16.那些年我们踩过的坑 +* 怎么同时使用xorm的tag和json的tag? + +答:使用空格 + +```Go +type User struct { + Name string `json:"name" xorm:"name"` +} +``` + +* 我的struct里面包含bool类型,为什么它不能作为条件也没法用Update更新? + +答:默认bool类型因为无法判断是否为空,所以不会自动作为条件也不会作为Update的内容。可以使用UseBool函数,也可以使用Cols函数 + +```Go +engine.Cols("bool_field").Update(&Struct{BoolField:true}) +// UPDATE struct SET bool_field = true +``` + +* 我的struct里面包含float64和float32类型,为什么用他们作为查询条件总是不正确? + +答:默认float32和float64映射到数据库中为float,real,double这几种类型,这几种数据库类型数据库的实现一般都是非精确的。因此作为相等条件查询有可能不会返回正确的结果。如果一定要作为查询条件,请将数据库中的类型定义为Numeric或者Decimal。 + +```Go +type account struct { +money float64 `xorm:"Numeric"` +} +``` + +* 为什么Update时Sqlite3返回的affected和其它数据库不一样? + +答:Sqlite3默认Update时返回的是update的查询条件的记录数条数,不管记录是否真的有更新。而Mysql和Postgres默认情况下都是只返回记录中有字段改变的记录数。 + +* xorm有几种命名映射规则? + +答:目前支持SnakeMapper和SameMapper两种。SnakeMapper支持结构体和成员以驼峰式命名而数据库表和字段以下划线连接命名;SameMapper支持结构体和数据库的命名保持一致的映射。 + +* xorm支持复合主键吗? + +答:支持。在定义时,如果有多个字段标记了pk,则这些字段自动成为复合主键,顺序为在struct中出现的顺序。在使用Id方法时,可以用`Id(xorm.PK{1, 2})`的方式来用。 + + + +## 17.讨论 +请加入QQ群:280360085 进行讨论。 diff --git a/docs/QuickStartEn.md b/docs/QuickStartEn.md deleted file mode 100644 index a8dce7cc..00000000 --- a/docs/QuickStartEn.md +++ /dev/null @@ -1,693 +0,0 @@ -Quick Start -===== - -* [1.Create ORM Engine](#10) -* [2.Define a struct](#20) - * [2.1.Name mapping rule](#21) - * [2.2.Use Table or Tag to change table or column name](#22) - * [2.3.Column define](#23) -* [3. database schema operation](#30) - * [3.1.Retrieve database schema infomation](#31) - * [3.2.Table Operation](#32) - * [3.3.Create indexes and uniques](#33) - * [3.4.Sync database schema](#34) -* [4.Insert records](#40) -* [5.Query and Count records](#60) - * [5.1.Query condition methods](#61) - * [5.2.Temporory methods](#62) - * [5.3.Get](#63) - * [5.4.Find](#64) - * [5.5.Iterate](#65) - * [5.6.Count](#66) -* [6.Update records](#70) -* [6.1.Optimistic Locking](#71) -* [7.Delete records](#80) -* [8.Execute SQL command](#90) -* [9.Execute SQL query](#100) -* [10.Transaction](#110) -* [11.Cache](#120) -* [12.Xorm Tool](#130) - * [12.1.Reverse command](#131) -* [13.Examples](#140) -* [14.Cases](#150) -* [15.FAQ](#160) -* [16.Discuss](#170) - - -## 1.Create ORM Engine - -When using xorm, you can create multiple orm engines, an engine means a databse. So you can: - -```Go -import ( - _ "github.com/go-sql-driver/mysql" - "github.com/go-xorm/xorm" -) -engine, err := xorm.NewEngine("mysql", "root:123@/test?charset=utf8") -defer engine.Close() -``` - -or - -```Go -import ( - _ "github.com/mattn/go-sqlite3" - "github.com/go-xorm/xorm" - ) -engine, err = xorm.NewEngine("sqlite3", "./test.db") -defer engine.Close() -``` - -Generally, you can only create one engine. Engine supports run on go rutines. - -xorm supports four drivers now: - -* Mysql: [github.com/Go-SQL-Driver/MySQL](https://github.com/Go-SQL-Driver/MySQL) - -* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) - -* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) - -* Postgres: [github.com/lib/pq](https://github.com/lib/pq) - -NewEngine's parameters are the same as `sql.Open`. So you should read the drivers' document for parameters' usage. - -After engine created, you can do some settings. - -1.Logs - -* `engine.ShowSQL = true`, Show SQL statement on standard output; -* `engine.ShowDebug = true`, Show debug infomation on standard output; -* `engine.ShowError = true`, Show error infomation on standard output; -* `engine.ShowWarn = true`, Show warnning information on standard output; - -2.If want to record infomation with another method: use `engine.Logger` as `io.Writer`: - -```Go -f, err := os.Create("sql.log") - if err != nil { - println(err.Error()) - return - } -engine.Logger = f -``` - -3.Engine support connection pool. The default pool is database/sql's and also you can use custom pool. Xorm provides two connection pool `xorm.NonConnectionPool` & `xorm.SimpleConnectPool`. If you want to use yourself pool, you can use `engine.SetPool` to set it. - -* Use `engine.SetIdleConns()` to set idle connections. -* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+. - - -## 2.Define struct - -xorm map a struct to a database table, the rule is below. - - -### 2.1.name mapping rule - -use xorm.IMapper interface to implement. There are two IMapper implemented: `SnakeMapper` and `SameMapper`. SnakeMapper means struct name is word by word and table name or column name as 下划线. SameMapper means same name between struct and table. - -SnakeMapper is the default. - -```Go -engine.Mapper = SameMapper{} -``` - -同时需要注意的是: - -* 如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 -* 表名称和字段名称的映射规则默认是相同的,当然也可以设置为不同,如: - -```Go -engine.SetTableMapper(SameMapper{}) -engine.SetColumnMapper(SnakeMapper{}) -``` - - -### 2.2.前缀映射规则,后缀映射规则和缓存映射规则 - -* 通过`engine.NewPrefixMapper(SnakeMapper{}, "prefix")`可以在SnakeMapper的基础上在命名中添加统一的前缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 -* 通过`engine.NewSufffixMapper(SnakeMapper{}, "suffix")`可以在SnakeMapper的基础上在命名中添加统一的后缀,当然也可以把SnakeMapper{}换成SameMapper或者你自定义的Mapper。 -* 通过`eneing.NewCacheMapper(SnakeMapper{})`可以起到在内存中缓存曾经映射过的命名映射。 - -当然,如果你使用了别的命名规则映射方案,也可以自己实现一个IMapper。 - - -### 2.3.使用Table和Tag改变名称映射 - -如果所有的命名都是按照IMapper的映射来操作的,那当然是最理想的。但是如果碰到某个表名或者某个字段名跟映射规则不匹配时,我们就需要别的机制来改变。 - -通过`engine.Table()`方法可以改变struct对应的数据库表的名称,通过sturct中field对应的Tag中使用`xorm:"'table_name'"`可以使该field对应的Column名称为指定名称。这里使用两个单引号将Column名称括起来是为了防止名称冲突,因为我们在Tag中还可以对这个Column进行更多的定义。如果名称不冲突的情况,单引号也可以不使用。 - - -### 2.4.Column属性定义 -我们在field对应的Tag中对Column的一些属性进行定义,定义的方法基本和我们写SQL定义表结构类似,比如: - -``` -type User struct { - Id int64 - Name string `xorm:"varchar(25) not null unique 'usr_name'"` -} -``` - -对于不同的数据库系统,数据类型其实是有些差异的。因此xorm中对数据类型有自己的定义,基本的原则是尽量兼容各种数据库的字段类型,具体的字段对应关系可以查看[字段类型对应表](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)。 - -具体的映射规则如下,另Tag中的关键字均不区分大小写,字段名区分大小写: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
name当前field对应的字段的名称,可选,如不写,则自动根据field名字和转换规则命名
pk是否是Primary Key,当前仅支持int64类型
当前支持30多种字段类型,详情参见 [字段类型](https://github.com/go-xorm/xorm/blob/master/docs/COLUMNTYPE.md)字段类型
autoincr是否是自增
[not ]null是否可以为空
unique或unique(uniquename)是否是唯一,如不加括号则该字段不允许重复;如加上括号,则括号中为联合唯一索引的名字,此时如果有另外一个或多个字段和本unique的uniquename相同,则这些uniquename相同的字段组成联合唯一索引
index或index(indexname)是否是索引,如不加括号则该字段自身为索引,如加上括号,则括号中为联合索引的名字,此时如果有另外一个或多个字段和本index的indexname相同,则这些indexname相同的字段组成联合索引
extends应用于一个匿名结构体之上,表示此匿名结构体的成员也映射到数据库中
-这个Field将不进行字段映射
->这个Field将只写入到数据库而不从数据库读取
<-这个Field将只从数据库读取,而不写入到数据库
createdThis field will be filled in current time on insert
updatedThis field will be filled in current time on insert or update
versionThis field will be filled 1 on insert and autoincrement on update
default 0设置默认值,紧跟的内容如果是Varchar等需要加上单引号
- -另外有如下几条自动映射的规则: - -- 1.如果field名称为`Id`而且类型为`int64`的话,会被xorm视为主键,并且拥有自增属性。如果想用`Id`以外的名字做为主键名,可以在对应的Tag上加上`xorm:"pk"`来定义主键。 - -- 2.string类型默认映射为varchar(255),如果需要不同的定义,可以在tag中自定义 - -- 3.支持`type MyString string`等自定义的field,支持Slice, Map等field成员,这些成员默认存储为Text类型,并且默认将使用Json格式来序列化和反序列化。也支持数据库字段类型为Blob类型,如果是Blob类型,则先使用Json格式序列化再转成[]byte格式。当然[]byte或者[]uint8默认为Blob类型并且都以二进制方式存储。 - -- 4.实现了Conversion接口的类型或者结构体,将根据接口的转换方式在类型和数据库记录之间进行相互转换。 -```Go -type Conversion interface { - FromDB([]byte) error - ToDB() ([]byte, error) -} -``` - - -## 3.表结构操作 - -xorm提供了一些动态获取和修改表结构的方法。对于一般的应用,很少动态修改表结构,则只需调用Sync()同步下表结构即可。 - - -## 3.1 获取数据库信息 - -* DBMetas() -xorm支持获取表结构信息,通过调用`engine.DBMetas()`可以获取到所有的表的信息 - - -## 3.2.表操作 - -* CreateTables() -创建表使用`engine.CreateTables()`,参数为一个或多个空的对应Struct的指针。同时可用的方法有Charset()和StoreEngine(),如果对应的数据库支持,这两个方法可以在创建表时指定表的字符编码和使用的引擎。当前仅支持Mysql数据库。 - -* IsTableEmpty() -判断表是否为空,参数和CreateTables相同 - -* IsTableExist() -判断表是否存在 - -* DropTables() -删除表使用`engine.DropTables()`,参数为一个或多个空的对应Struct的指针或者表的名字。如果为string传入,则只删除对应的表,如果传入的为Struct,则删除表的同时还会删除对应的索引。 - - -## 3.3.创建索引和唯一索引 - -* CreateIndexes -根据struct中的tag来创建索引 - -* CreateUniques -根据struct中的tag来创建唯一索引 - - -## 3.4.同步数据库结构 - -同步能够部分智能的根据结构体的变动检测表结构的变动,并自动同步。目前能够实现: -1) 自动检测和创建表,这个检测是根据表的名字 -2)自动检测和新增表中的字段,这个检测是根据字段名 -3)自动检测和创建索引和唯一索引,这个检测是根据一个或多个字段名,而不根据索引名称 - -调用方法如下: -```Go -err := engine.Sync(new(User)) -``` - - -## 4.插入数据 - -Inserting records use Insert method. - -* Insert one record -```Go -user := new(User) -user.Name = "myname" -affected, err := engine.Insert(user) -``` - -After inseted, `user.Id` will be filled with primary key column value. -```Go -fmt.Println(user.Id) -``` - -* Insert multiple records by Slice on one table -```Go -users := make([]User, 0) -users[0].Name = "name0" -... -affected, err := engine.Insert(&users) -``` - -* Insert multiple records by Slice of pointer on one table -```Go -users := make([]*User, 0) -users[0] = new(User) -users[0].Name = "name0" -... -affected, err := engine.Insert(&users) -``` - -* Insert one record on two table. -```Go -user := new(User) -user.Name = "myname" -question := new(Question) -question.Content = "whywhywhwy?" -affected, err := engine.Insert(user, question) -``` - -* Insert multiple records on multiple tables. -```Go -users := make([]User, 0) -users[0].Name = "name0" -... -questions := make([]Question, 0) -questions[0].Content = "whywhywhwy?" -affected, err := engine.Insert(&users, &questions) -``` - -* Insert one or multple records on multiple tables. -```Go -user := new(User) -user.Name = "myname" -... -questions := make([]Question, 0) -questions[0].Content = "whywhywhwy?" -affected, err := engine.Insert(user, &questions) -``` - -Notice: If you want to use transaction on inserting, you should use session.Begin() before calling Insert. - - -## 5.Query and count - -所有的查询条件不区分调用顺序,但必须在调用Get,Find,Count这三个函数之前调用。同时需要注意的一点是,在调用的参数中,所有的字符字段名均为映射后的数据库的字段名,而不是field的名字。 - - -### 5.1.查询条件方法 - -查询和统计主要使用`Get`, `Find`, `Count`三个方法。在进行查询时可以使用多个方法来形成查询条件,条件函数如下: - -* Id(int64) -传入一个PK字段的值,作为查询条件 - -* Where(string, …interface{}) -和Where语句中的条件基本相同,作为条件 - -* And(string, …interface{}) -和Where函数中的条件基本相同,作为条件 - -* Or(string, …interface{}) -和Where函数中的条件基本相同,作为条件 - -* Sql(string, …interface{}) -执行指定的Sql语句,并把结果映射到结构体 - -* Asc(…string) -指定字段名正序排序 - -* Desc(…string) -指定字段名逆序排序 - -* OrderBy(string) -按照指定的顺序进行排序 - -* In(string, …interface{}) -某字段在一些值中 - -* Cols(…string) -只查询或更新某些指定的字段,默认是查询所有映射的字段或者根据Update的第一个参数来判断更新的字段。例如: -```Go -engine.Cols("age", "name").Find(&users) -// SELECT age, name FROM user -engine.Cols("age", "name").Update(&user) -// UPDATE user SET age=? AND name=? -``` - -其中的参数"age", "name"也可以写成"age, name",两种写法均可 - -* Omit(...string) -和cols相反,此函数指定排除某些指定的字段。注意:此方法和Cols方法不可同时使用 -```Go -engine.Cols("age").Update(&user) -// UPDATE user SET name = ? AND department = ? -``` - -* Distinct(…string) -按照参数中指定的字段归类结果 -```Go -engine.Distinct("age", "department").Find(&users) -// SELECT DISTINCT age, department FROM user -``` -注意:当开启了缓存时,此方法的调用将在当前查询中禁用缓存。因为缓存系统当前依赖Id,而此时无法获得Id - -* Table(nameOrStructPtr interface{}) -传入表名称或者结构体指针,如果传入的是结构体指针,则按照IMapper的规则提取出表名 - -* Limit(int, …int) -限制获取的数目,第一个参数为条数,第二个参数为可选,表示开始位置 - -* Top(int) -相当于Limit(int, 0) - -* Join(string,string,string) -第一个参数为连接类型,当前支持INNER, LEFT OUTER, CROSS中的一个值,第二个参数为表名,第三个参数为连接条件 - -* GroupBy(string) -Groupby的参数字符串 - -* Having(string) -Having的参数字符串 - - -### 5.2.临时开关方法 - -* NoAutoTime() -如果此方法执行,则此次生成的语句中Created和Updated字段将不自动赋值为当前时间 - -* NoCache() -如果此方法执行,则此次生成的语句则在非缓存模式下执行 - -* UseBool(...string) -当从一个struct来生成查询条件或更新字段时,xorm会判断struct的field是否为0,"",nil,如果为以上则不当做查询条件或者更新内容。因为bool类型只有true和false两种值,因此默认所有bool类型不会作为查询条件或者更新字段。如果可以使用此方法,如果默认不传参数,则所有的bool字段都将会被使用,如果参数不为空,则参数中指定的为字段名,则这些字段对应的bool值将被使用。 - -* Cascade(bool) -是否自动关联查询field中的数据,如果struct的field也是一个struct并且映射为某个Id,则可以在查询时自动调用Get方法查询出对应的数据。 - - -### 5.3.Get one record -Fetch a single object by user - -```Go -var user = User{Id:27} -has, err := engine.Get(&user) -// or has, err := engine.Id(27).Get(&user) - -var user = User{Name:"xlw"} -has, err := engine.Get(&user) -``` - - -### 5.4.Find -Fetch multipe objects into a slice or a map, use Find: - -```Go -var everyone []Userinfo -err := engine.Find(&everyone) - -users := make(map[int64]Userinfo) -err := engine.Find(&users) -``` - -* also you can use Where, Limit - -```Go -var allusers []Userinfo -err := engine.Where("id > ?", "3").Limit(10,20).Find(&allusers) //Get id>3 limit 10 offset 20 -``` - -* or you can use a struct query - -```Go -var tenusers []Userinfo -err := engine.Limit(10).Find(&tenusers, &Userinfo{Name:"xlw"}) //Get All Name="xlw" limit 10 offset 0 -``` - -* or In function - -```Go -var tenusers []Userinfo -err := engine.In("id", 1, 3, 5).Find(&tenusers) //Get All id in (1, 3, 5) -``` - -* The default will query all columns of a table. Use Cols function if you want to select some columns - -```Go -var tenusers []Userinfo -err := engine.Cols("id", "name").Find(&tenusers) //Find only id and name -``` - - -### 5.5.Iterate records -Iterate, like find, but handle records one by one - -```Go -err := engine.Where("age > ? or name=?)", 30, "xlw").Iterate(new(Userinfo), func(i int, bean interface{})error{ - user := bean.(*Userinfo) - //do somthing use i and user -}) -``` - - -### 5.6.Count方法 - -统计数据使用`Count`方法,Count方法的参数为struct的指针并且成为查询条件。 -```Go -user := new(User) -total, err := engine.Where("id >?", 1).Count(user) -``` - - -## 6.更新数据 - -更新数据使用`Update`方法,Update方法的第一个参数为需要更新的内容,可以为一个结构体指针或者一个Map[string]interface{}类型。当传入的为结构体指针时,只有非空和0的field才会被作为更新的字段。当传入的为Map类型时,key为数据库Column的名字,value为要更新的内容。 - -```Go -user := new(User) -user.Name = "myname" -affected, err := engine.Id(id).Update(user) -``` - -这里需要注意,Update会自动从user结构体中提取非0和非nil得值作为需要更新的内容,因此,如果需要更新一个值为0,则此种方法将无法实现,因此有两种选择: - -1. 通过添加Cols函数指定需要更新结构体中的哪些值,未指定的将不更新,指定了的即使为0也会更新。 -```Go -affected, err := engine.Id(id).Cols("age").Update(&user) -``` - -2. 通过传入map[string]interface{}来进行更新,但这时需要额外指定更新到哪个表,因为通过map是无法自动检测更新哪个表的。 -```Go -affected, err := engine.Table(new(User)).Id(id).Update(map[string]interface{}{"age":0}) -``` - - -### 6.1.乐观锁 - -要使用乐观锁,需要使用version标记 -type User struct { - Id int64 - Name string - Version int `xorm:"version"` -} - -在Insert时,version标记的字段将会被设置为1,在Update时,Update的内容必须包含version原来的值。 - -```Go -var user User -engine.Id(1).Get(&user) -// SELECT * FROM user WHERE id = ? -engine.Id(1).Update(&user) -// UPDATE user SET ..., version = version + 1 WHERE id = ? AND version = ? -``` - - - -## 7.Delete one or more records -Delete one or more records - -* delete by id - -```Go -err := engine.Id(1).Delete(&User{}) -``` - -* delete by other conditions - -```Go -err := engine.Delete(&User{Name:"xlw"}) -``` - - -## 8.Execute SQL query - -Of course, SQL execution is also provided. - -If select then use Query - -```Go -sql := "select * from userinfo" -results, err := engine.Query(sql) -``` - - -## 9.Execute SQL command -If insert, update or delete then use Exec - -```Go -sql = "update userinfo set username=? where id=?" -res, err := engine.Exec(sql, "xiaolun", 1) -``` - - -## 10.Transaction - -```Go -session := engine.NewSession() -defer session.Close() - -// add Begin() before any action -err := session.Begin() -user1 := Userinfo{Username: "xiaoxiao", Departname: "dev", Alias: "lunny", Created: time.Now()} -_, err = session.Insert(&user1) -if err != nil { - session.Rollback() - return -} -user2 := Userinfo{Username: "yyy"} -_, err = session.Where("id = ?", 2).Update(&user2) -if err != nil { - session.Rollback() - return -} - -_, err = session.Exec("delete from userinfo where username = ?", user2.Username) -if err != nil { - session.Rollback() - return -} - -// add Commit() after all actions -err = session.Commit() -if err != nil { - return -} -``` - - -## 11.缓存 - -1. Global Cache -Xorm implements cache support. Defaultly, it's disabled. If enable it, use below code. - -```Go -cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) -engine.SetDefaultCacher(cacher) -``` - -If disable some tables' cache, then: - -```Go -engine.MapCacher(&user, nil) -``` - -2. Table's Cache -If only some tables need cache, then: - -```Go -cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) -engine.MapCacher(&user, cacher) -``` - -Caution: - -1. When use Cols methods on cache enabled, the system still return all the columns. - -2. When using Exec method, you should clear cache: - -```Go -engine.Exec("update user set name = ? where id = ?", "xlw", 1) -engine.ClearCache(new(User)) -``` - -Cache implement theory below: - -![cache design](https://raw.github.com/go-xorm/xorm/master/docs/cache_design.png) - - -## 12.xorm tool -xorm工具提供了xorm命令,能够帮助做很多事情。 - -### 12.1.Reverse command -Please visit [xorm tool](https://github.com/go-xorm/xorm/tree/master/xorm) - - -## 13.Examples - -请访问[https://github.com/go-xorm/xorm/tree/master/examples](https://github.com/go-xorm/xorm/tree/master/examples) - - -## 14.Cases - -* [Gowalker](http://gowalker.org),source [github.com/Unknwon/gowalker](http://github.com/Unknwon/gowalker) - -* [GoDaily](http://godaily.org),source [github.com/govc/godaily](http://github.com/govc/godaily) - -* [Sudochina](http://sudochina.com) source [github.com/insionng/toropress](http://github.com/insionng/toropress) - -* [VeryHour](http://veryhour.com) - - -## 15.FAQ - -1.How the xorm tag use both with json? - - Use space. - -```Go -type User struct { - Name string `json:"name" xorm:"name"` -} -``` diff --git a/examples/cache.go b/examples/cache.go index c5d50273..a86dfd2d 100644 --- a/examples/cache.go +++ b/examples/cache.go @@ -5,7 +5,6 @@ import ( "os" "github.com/go-xorm/xorm" - "github.com/go-xorm/xorm/caches" _ "github.com/mattn/go-sqlite3" ) @@ -24,7 +23,7 @@ func main() { return } Orm.ShowSQL = true - cacher := xorm.NewLRUCacher(caches.NewMemoryStore(), 1000) + cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000) Orm.SetDefaultCacher(cacher) err = Orm.CreateTables(&User{}) diff --git a/examples/maxconnect.go b/examples/maxconnect.go index 8ec82d2a..6bb7c401 100644 --- a/examples/maxconnect.go +++ b/examples/maxconnect.go @@ -34,7 +34,7 @@ func test(engine *xorm.Engine) { } engine.ShowSQL = true - engine.Pool.SetMaxConns(5) + engine.SetMaxConns(5) size := 1000 queue := make(chan int, size) diff --git a/examples/pool.go b/examples/pool.go deleted file mode 100644 index 7511bb6c..00000000 --- a/examples/pool.go +++ /dev/null @@ -1,46 +0,0 @@ -package main - -import ( - "fmt" - "os" - - "github.com/go-xorm/xorm" - _ "github.com/mattn/go-sqlite3" -) - -type User struct { - Id int64 - Name string -} - -func main() { - f := "pool.db" - os.Remove(f) - - Orm, err := xorm.NewEngine("sqlite3", f) - if err != nil { - fmt.Println(err) - return - } - err = Orm.SetPool(xorm.NewSimpleConnectPool()) - if err != nil { - fmt.Println(err) - return - } - - Orm.ShowSQL = true - err = Orm.CreateTables(&User{}) - if err != nil { - fmt.Println(err) - return - } - - for i := 0; i < 10; i++ { - _, err = Orm.Get(&User{}) - if err != nil { - fmt.Println(err) - break - } - - } -} diff --git a/lru_cacher.go b/lru_cacher.go index bb223c20..a81c4ccc 100644 --- a/lru_cacher.go +++ b/lru_cacher.go @@ -11,22 +11,26 @@ import ( ) type LRUCacher struct { - idList *list.List - sqlList *list.List - idIndex map[string]map[string]*list.Element - sqlIndex map[string]map[string]*list.Element - store core.CacheStore - Max int - mutex sync.Mutex - Expired time.Duration - maxSize int - GcInterval time.Duration + idList *list.List + sqlList *list.List + idIndex map[string]map[string]*list.Element + sqlIndex map[string]map[string]*list.Element + store core.CacheStore + mutex sync.Mutex + // maxSize int + MaxElementSize int + Expired time.Duration + GcInterval time.Duration } -func NewLRUCacher(store core.CacheStore, expired time.Duration, maxSize int, max int) *LRUCacher { +func NewLRUCacher(store core.CacheStore, maxElementSize int) *LRUCacher { + return NewLRUCacher2(store, 0, maxElementSize) +} + +func NewLRUCacher2(store core.CacheStore, expired time.Duration, maxElementSize int) *LRUCacher { cacher := &LRUCacher{store: store, idList: list.New(), - sqlList: list.New(), Expired: expired, maxSize: maxSize, - GcInterval: core.CacheGcInterval, Max: max, + sqlList: list.New(), Expired: expired, + GcInterval: core.CacheGcInterval, MaxElementSize: maxElementSize, sqlIndex: make(map[string]map[string]*list.Element), idIndex: make(map[string]map[string]*list.Element), } @@ -193,7 +197,7 @@ func (m *LRUCacher) PutIds(tableName, sql string, ids interface{}) { el.Value.(*sqlNode).lastVisit = time.Now() } m.store.Put(sql, ids) - if m.sqlList.Len() > m.Max { + if m.sqlList.Len() > m.MaxElementSize { e := m.sqlList.Front() node := e.Value.(*sqlNode) m.delIds(node.tbName, node.sql) @@ -214,7 +218,7 @@ func (m *LRUCacher) PutBean(tableName string, id string, obj interface{}) { } m.store.Put(genId(tableName, id), obj) - if m.idList.Len() > m.Max { + if m.idList.Len() > m.MaxElementSize { e := m.idList.Front() node := e.Value.(*idNode) m.delBean(node.tbName, node.id) diff --git a/xorm.go b/xorm.go index 7190446c..e0f43c2e 100644 --- a/xorm.go +++ b/xorm.go @@ -9,7 +9,6 @@ import ( "reflect" "runtime" "sync" - "time" ) const ( @@ -109,7 +108,3 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { func (engine *Engine) Clone() (*Engine, error) { return NewEngine(engine.dialect.DriverName(), engine.dialect.DataSourceName()) } - -func NewLRUCacher2(store core.CacheStore, expired time.Duration, max int) *LRUCacher { - return NewLRUCacher(store, expired, 0, max) -} From b7690b166802ade4996507c280d93427d12e1b0c Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Thu, 17 Apr 2014 23:21:42 +0800 Subject: [PATCH 43/55] use time.Location struct instead of TimeZone string --- engine.go | 28 ++++------------------------ session.go | 12 ++++++------ xorm.go | 3 ++- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/engine.go b/engine.go index cd725708..b375e2a7 100644 --- a/engine.go +++ b/engine.go @@ -36,8 +36,8 @@ type Engine struct { ShowWarn bool //Pool IConnectPool //Filters []core.Filter - Logger ILogger // io.Writer - TimeZone string + Logger ILogger // io.Writer + TZLocation *time.Location } func (engine *Engine) DriverName() string { @@ -1094,28 +1094,8 @@ func (engine *Engine) Import(ddlPath string) ([]sql.Result, error) { return results, lastError } -func (engine *Engine) TZTime(t time.Time) (r time.Time) { - switch engine.TimeZone { - case "Local", "L": - r = t.Local() - case "UTC", "U": - fallthrough - default: - r = t.UTC() - } - return -} - -func (engine *Engine) TZLocation() (r *time.Location) { - switch engine.TimeZone { - case "Local", "L": - r = time.Local - case "UTC", "U": - fallthrough - default: - r = time.UTC - } - return +func (engine *Engine) TZTime(t time.Time) time.Time { + return t.In(engine.TZLocation) } func (engine *Engine) NowTime(sqlTypeName string) interface{} { diff --git a/session.go b/session.go index e349fb82..59ec4eb2 100644 --- a/session.go +++ b/session.go @@ -1987,17 +1987,17 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T x = time.Unix(0, sd) } } else if len(sdata) > 19 { - x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation()) + x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) if err != nil { - x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation()) + x, err = time.ParseInLocation("2006-01-02 15:04:05.999999999", sdata, session.Engine.TZLocation) } if err != nil { - x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation()) + x, err = time.ParseInLocation("2006-01-02 15:04:05.9999999 Z07:00", sdata, session.Engine.TZLocation) } } else if len(sdata) == 19 { - x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation()) + x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) } else if len(sdata) == 10 && sdata[4] == '-' && sdata[7] == '-' { - x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation()) + x, err = time.ParseInLocation("2006-01-02", sdata, session.Engine.TZLocation) } else if col.SQLType.Name == core.Time { if strings.Contains(sdata, " ") { ssd := strings.Split(sdata, " ") @@ -2012,7 +2012,7 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T //fmt.Println(sdata) st := fmt.Sprintf("2006-01-02 %v", sdata) - x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation()) + x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) } else { outErr = errors.New(fmt.Sprintf("unsupported time format %v", sdata)) return diff --git a/xorm.go b/xorm.go index 9bad07d6..5b94f298 100644 --- a/xorm.go +++ b/xorm.go @@ -9,6 +9,7 @@ import ( "reflect" "runtime" "sync" + "time" ) const ( @@ -92,7 +93,7 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { mutex: &sync.RWMutex{}, TagIdentifier: "xorm", Logger: NewSimpleLogger(os.Stdout), - TimeZone: "Local", + TZLocation: time.Local, } engine.SetMapper(core.NewCacheMapper(new(core.SnakeMapper))) From 077621b77109e346c2f75b0431661219a7c07a4f Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Thu, 17 Apr 2014 23:22:32 +0800 Subject: [PATCH 44/55] pump version 0.4.0 M1 release --- VERSION | 2 +- docs/Changelog.md | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 79201455..e2f81f7c 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.0 +xorm v0.4.0 M1 diff --git a/docs/Changelog.md b/docs/Changelog.md index 3481b02d..487058e1 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,16 @@ ## Changelog +* **v0.4.0 M1** + Changes: + * moved xorm cmd to github.com/go-xorm/cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + * refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core) + * moved tests to github.com/go-xorm/tests [github.com/go-xorm/tests](github.com/go-xorm/tests) + + Improvements: + * Prepared statement cache + * Add Incr API + * Specify Timezone Location + * **v0.3.2** Improvements: * Add AllCols & MustCols function From 1b3785471f52fb44b48a423fa66b5574195589ec Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Thu, 17 Apr 2014 23:31:14 +0800 Subject: [PATCH 45/55] update changelog --- docs/Changelog.md | 2 +- docs/ChangelogCN.md | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/Changelog.md b/docs/Changelog.md index 487058e1..072245e8 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -2,7 +2,7 @@ * **v0.4.0 M1** Changes: - * moved xorm cmd to github.com/go-xorm/cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + * moved xorm cmd to [github.com/go-xorm/cmd](github.com/go-xorm/cmd) * refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core) * moved tests to github.com/go-xorm/tests [github.com/go-xorm/tests](github.com/go-xorm/tests) diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index dad8e330..4c319093 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -1,11 +1,22 @@ ## 更新日志 +* **v0.4.0 M1** + 新特性: + *移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + *在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) + *移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) + + 改进: + *Prepared statement 缓存 + *添加 Incr API + *指定时区位置 + * **v0.3.2** - Improvements: + 改进: * Add AllCols & MustCols function * Add TableName for custom table name - Bug Fixes: + Bug 修复: * #46 * #51 * #53 From ede19553c7c9a05f3a0f084d37bf1617755fd432 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Thu, 17 Apr 2014 23:43:57 +0800 Subject: [PATCH 46/55] update version to v0.4.0 RC1 --- VERSION | 2 +- docs/Changelog.md | 2 +- docs/ChangelogCN.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/VERSION b/VERSION index e2f81f7c..eb1bde3d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -xorm v0.4.0 M1 +xorm v0.4.0 RC1 diff --git a/docs/Changelog.md b/docs/Changelog.md index 072245e8..fb9b1ec3 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,6 +1,6 @@ ## Changelog -* **v0.4.0 M1** +* **v0.4.0 RC1** Changes: * moved xorm cmd to [github.com/go-xorm/cmd](github.com/go-xorm/cmd) * refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core) diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index 4c319093..7015cc13 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -1,6 +1,6 @@ ## 更新日志 -* **v0.4.0 M1** +* **v0.4.0 RC1** 新特性: *移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) *在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) From 0d9c1a02ebb0e1c33dd3ac642072e5da001ca98d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 18 Apr 2014 10:52:33 +0800 Subject: [PATCH 47/55] add back SetMaxConns for compitable and set @deprecated --- engine.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine.go b/engine.go index 7aa44b1e..263a38a3 100644 --- a/engine.go +++ b/engine.go @@ -93,6 +93,11 @@ func (engine *Engine) SetMaxOpenConns(conns int) { engine.db.SetMaxOpenConns(conns) } +// @Deprecated +func (engine *Engine) SetMaxConns(conns int) { + engine.SetMaxOpenConns(conns) +} + // SetMaxIdleConns func (engine *Engine) SetMaxIdleConns(conns int) { engine.db.SetMaxIdleConns(conns) From aa150a7ba2130b6bb2d3f06f95d3604f5a7a7fe8 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Fri, 18 Apr 2014 16:46:58 +0800 Subject: [PATCH 48/55] update QuickStart --- docs/QuickStart.md | 4 ++-- docs/QuickStartCn.md | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/QuickStart.md b/docs/QuickStart.md index 46ba4d9f..f240806d 100644 --- a/docs/QuickStart.md +++ b/docs/QuickStart.md @@ -96,8 +96,8 @@ engine.Logger = f 3.Engine provide DB connection pool settings. -* Use `engine.SetIdleConns()` to set idle connections. -* Use `engine.SetMaxConns()` to set Max connections. This methods support only Go 1.2+. +* Use `engine.SetMaxIdleConns()` to set idle connections. +* Use `engine.SetMaxOpenConns()` to set Max connections. This methods support only Go 1.2+. ## 2.Define struct diff --git a/docs/QuickStartCn.md b/docs/QuickStartCn.md index 879548e0..2b76ca8e 100644 --- a/docs/QuickStartCn.md +++ b/docs/QuickStartCn.md @@ -98,6 +98,11 @@ f, err := os.Create("sql.log") engine.Logger = f ``` +3.engine内部支持连接池接口。 + +* 如果需要设置连接池的空闲数大小,可以使用`engine.SetMaxIdleConns()`来实现。 +* 如果需要设置最大打开连接数,则可以使用`engine.SetMaxOpenConns()`来实现。 + ## 2.定义表结构体 From 9a6458134a716acfe3c13be3338be64336f808cb Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 18 Apr 2014 18:39:07 +0800 Subject: [PATCH 49/55] set db to dialect --- mssql_dialect.go | 26 ++++++-------------------- mysql_dialect.go | 29 +++++++++-------------------- oracle_dialect.go | 27 +++++++-------------------- postgres_dialect.go | 31 +++++++++++-------------------- session.go | 10 +++------- sqlite3_dialect.go | 26 ++++++-------------------- xorm.go | 7 ++++--- 7 files changed, 46 insertions(+), 110 deletions(-) diff --git a/mssql_dialect.go b/mssql_dialect.go index 0b85f6a9..4f5b5eb7 100644 --- a/mssql_dialect.go +++ b/mssql_dialect.go @@ -17,8 +17,8 @@ type mssql struct { core.Base } -func (db *mssql) Init(uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(db, uri, drivername, dataSourceName) +func (db *mssql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { + return db.Base.Init(d, db, uri, drivername, dataSourceName) } func (db *mssql) SqlType(c *core.Column) string { @@ -123,13 +123,8 @@ func (db *mssql) GetColumns(tableName string) ([]string, map[string]*core.Column 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 + `')` - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, nil, err } @@ -183,12 +178,8 @@ where a.object_id=object_id('` + tableName + `')` func (db *mssql) GetTables() ([]*core.Table, error) { args := []interface{}{} s := `select name from sysobjects where xtype ='U'` - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } @@ -223,12 +214,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) =? ` - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } diff --git a/mysql_dialect.go b/mysql_dialect.go index 58a30c44..71273183 100644 --- a/mysql_dialect.go +++ b/mysql_dialect.go @@ -28,8 +28,8 @@ type mysql struct { clientFoundRows bool } -func (db *mysql) Init(uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(db, uri, drivername, dataSourceName) +func (db *mysql) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { + return db.Base.Init(d, db, uri, drivername, dataSourceName) } func (db *mysql) SqlType(c *core.Column) string { @@ -114,13 +114,8 @@ 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` = ?" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, nil, err } @@ -198,15 +193,12 @@ 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`=?" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } + defer rows.Close() tables := make([]*core.Table, 0) for rows.Next() { @@ -227,15 +219,12 @@ 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` = ?" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } + defer rows.Close() indexes := make(map[string]*core.Index, 0) for rows.Next() { diff --git a/oracle_dialect.go b/oracle_dialect.go index febd318e..36a2f62d 100644 --- a/oracle_dialect.go +++ b/oracle_dialect.go @@ -17,8 +17,8 @@ type oracle struct { core.Base } -func (db *oracle) Init(uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(db, uri, drivername, dataSourceName) +func (db *oracle) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { + return db.Base.Init(d, db, uri, drivername, dataSourceName) } func (db *oracle) SqlType(c *core.Column) string { @@ -98,12 +98,7 @@ func (db *oracle) GetColumns(tableName string) ([]string, map[string]*core.Colum s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, nil, err } @@ -166,15 +161,12 @@ 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" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } + defer rows.Close() tables := make([]*core.Table, 0) for rows.Next() { @@ -194,12 +186,7 @@ func (db *oracle) GetIndexes(tableName string) (map[string]*core.Index, error) { 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" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } diff --git a/postgres_dialect.go b/postgres_dialect.go index 51269d53..943039e5 100644 --- a/postgres_dialect.go +++ b/postgres_dialect.go @@ -17,8 +17,8 @@ type postgres struct { core.Base } -func (db *postgres) Init(uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(db, uri, drivername, dataSourceName) +func (db *postgres) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { + return db.Base.Init(d, db, uri, drivername, dataSourceName) } func (db *postgres) SqlType(c *core.Column) string { @@ -112,15 +112,13 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Col args := []interface{}{tableName} s := "SELECT column_name, column_default, is_nullable, data_type, character_maximum_length" + ", numeric_precision, numeric_precision_radix FROM INFORMATION_SCHEMA.COLUMNS WHERE table_name = $1" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, nil, err } + defer rows.Close() + cols := make(map[string]*core.Column) colSeq := make([]string, 0) @@ -200,15 +198,12 @@ func (db *postgres) GetColumns(tableName string) ([]string, map[string]*core.Col func (db *postgres) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT tablename FROM pg_tables where schemaname = 'public'" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } + defer rows.Close() tables := make([]*core.Table, 0) for rows.Next() { @@ -228,15 +223,11 @@ 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" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } + defer rows.Close() indexes := make(map[string]*core.Index, 0) for rows.Next() { diff --git a/session.go b/session.go index c67c46b1..881977a9 100644 --- a/session.go +++ b/session.go @@ -155,11 +155,6 @@ func (session *Session) NoCascade() *Session { return session } -/* -func (session *Session) MustCols(columns ...string) *Session { - session.Statement.Must() -}*/ - // 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. @@ -2443,11 +2438,12 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val switch k { case reflect.Bool: - if fieldValue.Bool() { + return fieldValue.Bool(), nil + /*if fieldValue.Bool() { return 1, nil } else { return 0, nil - } + }*/ case reflect.String: return fieldValue.String(), nil case reflect.Struct: diff --git a/sqlite3_dialect.go b/sqlite3_dialect.go index d5be5c72..0e19f96c 100644 --- a/sqlite3_dialect.go +++ b/sqlite3_dialect.go @@ -14,8 +14,8 @@ type sqlite3 struct { core.Base } -func (db *sqlite3) Init(uri *core.Uri, drivername, dataSourceName string) error { - return db.Base.Init(db, uri, drivername, dataSourceName) +func (db *sqlite3) Init(d *core.DB, uri *core.Uri, drivername, dataSourceName string) error { + return db.Base.Init(d, db, uri, drivername, dataSourceName) } func (db *sqlite3) SqlType(c *core.Column) string { @@ -87,13 +87,8 @@ func (db *sqlite3) ColumnCheckSql(tableName, colName string) (string, []interfac 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 = ?" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, nil, err } @@ -147,12 +142,7 @@ func (db *sqlite3) GetTables() ([]*core.Table, error) { args := []interface{}{} s := "SELECT name FROM sqlite_master WHERE type='table'" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } @@ -176,12 +166,8 @@ 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 = ?" - cnn, err := core.Open(db.DriverName(), db.DataSourceName()) - if err != nil { - return nil, err - } - defer cnn.Close() - rows, err := cnn.Query(s, args...) + + rows, err := db.DB().Query(s, args...) if err != nil { return nil, err } diff --git a/xorm.go b/xorm.go index e0f43c2e..53615564 100644 --- a/xorm.go +++ b/xorm.go @@ -4,11 +4,12 @@ import ( "database/sql" "errors" "fmt" - "github.com/go-xorm/core" "os" "reflect" "runtime" "sync" + + "github.com/go-xorm/core" ) const ( @@ -75,12 +76,12 @@ func NewEngine(driverName string, dataSourceName string) (*Engine, error) { return nil, errors.New(fmt.Sprintf("Unsupported dialect type: %v", uri.DbType)) } - err = dialect.Init(uri, driverName, dataSourceName) + db, err := core.Open(driverName, dataSourceName) if err != nil { return nil, err } - db, err := core.OpenDialect(dialect) + err = dialect.Init(db, uri, driverName, dataSourceName) if err != nil { return nil, err } From 82e73d6002892c7dad1fe30ca0728bd383014f78 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 18 Apr 2014 22:14:15 +0800 Subject: [PATCH 50/55] bug fixed for Incr --- engine.go | 6 ++++++ session.go | 6 +++--- statement.go | 17 +++++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/engine.go b/engine.go index 263a38a3..8c56cef1 100644 --- a/engine.go +++ b/engine.go @@ -75,6 +75,12 @@ func (engine *Engine) QuoteStr() string { // Use QuoteStr quote the string sql func (engine *Engine) Quote(sql string) string { + if len(sql) == 0 { + return sql + } + if string(sql[0]) == engine.dialect.QuoteStr() || sql[0] == '`' { + return sql + } return engine.dialect.QuoteStr() + sql + engine.dialect.QuoteStr() } diff --git a/session.go b/session.go index 881977a9..4e4b7b2b 100644 --- a/session.go +++ b/session.go @@ -2956,9 +2956,9 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 //for update action to like "column = column + ?" incColumns := session.Statement.getInc() - for k, v := range incColumns { - colNames = append(colNames, k+" = "+k+" + ?") - args = append(args, v) + for _, v := range incColumns { + colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" + ?") + args = append(args, v.arg) } var condiColNames []string var condiArgs []interface{} diff --git a/statement.go b/statement.go index b6c1152c..8438d48b 100644 --- a/statement.go +++ b/statement.go @@ -15,6 +15,11 @@ type inParam struct { args []interface{} } +type incrParam struct { + colName string + arg interface{} +} + // statement save all the sql info for executing SQL type Statement struct { RefTable *core.Table @@ -48,7 +53,7 @@ type Statement struct { checkVersion bool mustColumnMap map[string]bool inColumns map[string]*inParam - incColumns map[string]interface{} + incrColumns map[string]incrParam } // init @@ -79,7 +84,7 @@ func (statement *Statement) Init() { statement.mustColumnMap = make(map[string]bool) statement.checkVersion = true statement.inColumns = make(map[string]*inParam) - statement.incColumns = make(map[string]interface{}, 0) + statement.incrColumns = make(map[string]incrParam) } // add the raw sql statement @@ -464,16 +469,16 @@ func (statement *Statement) Id(id interface{}) *Statement { func (statement *Statement) Incr(column string, arg ...interface{}) *Statement { k := strings.ToLower(column) if len(arg) > 0 { - statement.incColumns[k] = arg[0] + statement.incrColumns[k] = incrParam{column, arg[0]} } else { - statement.incColumns[k] = 1 + statement.incrColumns[k] = incrParam{column, 1} } return statement } // Generate "Update ... Set column = column + arg" statment -func (statement *Statement) getInc() map[string]interface{} { - return statement.incColumns +func (statement *Statement) getInc() map[string]incrParam { + return statement.incrColumns } // Generate "Where column IN (?) " statment From 4afa1bc3dd948262ddd6f3323f0a67c9ff5f7ede Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Sun, 20 Apr 2014 23:49:16 +0800 Subject: [PATCH 51/55] fixed time.Time struct scan handling --- session.go | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/session.go b/session.go index 10f08fd7..c8dd787e 100644 --- a/session.go +++ b/session.go @@ -1545,7 +1545,18 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i if fieldType == core.TimeType { if rawValueType == core.TimeType { hasAssigned = true + t := vv.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.LogDebug("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) + tt := time.Date(t.Year(), t.Month(), t.Day(), t.Hour(), + t.Minute(), t.Second(), t.Nanosecond(), time.Local) + vv = reflect.ValueOf(tt) + } fieldValue.Set(vv) + // t = fieldValue.Interface().(time.Time) + // z, _ = t.Zone() + // session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) } } else if session.Statement.UseCascade { table := session.Engine.autoMapType(*fieldValue) @@ -1975,24 +1986,38 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T if sdata == "0000-00-00 00:00:00" || sdata == "0001-01-01 00:00:00" { - } else if !strings.ContainsAny(sdata, "- :") { + } 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(0, sd) + // !nashtsai! HACK mymysql driver is casuing Local location being change to CHAT and cause wrong time conversion + x = x.In(time.UTC) + x = time.Date(x.Year(), x.Month(), x.Day(), x.Hour(), + x.Minute(), x.Second(), x.Nanosecond(), session.Engine.TZLocation) + session.Engine.LogDebug("time(0) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) + } else { + session.Engine.LogDebug("time(0) err key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } } else if len(sdata) > 19 { + x, err = time.ParseInLocation(time.RFC3339Nano, sdata, session.Engine.TZLocation) + session.Engine.LogDebug("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.LogDebug("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.LogDebug("time(3) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } + } else if len(sdata) == 19 { x, err = time.ParseInLocation("2006-01-02 15:04:05", sdata, session.Engine.TZLocation) + session.Engine.LogDebug("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.LogDebug("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, " ") @@ -2008,6 +2033,7 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T st := fmt.Sprintf("2006-01-02 %v", sdata) x, err = time.ParseInLocation("2006-01-02 15:04:05", st, session.Engine.TZLocation) + session.Engine.LogDebug("time(6) key[%v]: %+v | sdata: [%v]\n", col.FieldName, x, sdata) } else { outErr = errors.New(fmt.Sprintf("unsupported time format %v", sdata)) return @@ -2083,7 +2109,7 @@ func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, var err error // for mysql, when use bit, it returned \x01 if col.SQLType.Name == core.Bit && - session.Engine.dialect.DBType() == core.MYSQL { + session.Engine.dialect.DBType() == core.MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API if len(data) == 1 { x = int64(data[0]) } else { From 23a3beaced9b7422ad0199474a32f8774494fdbf Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 21 Apr 2014 09:58:12 +0800 Subject: [PATCH 52/55] convert to Engine's TZ location for TimeStruct when Get() --- session.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/session.go b/session.go index c8dd787e..ecdaa66f 100644 --- a/session.go +++ b/session.go @@ -1553,7 +1553,11 @@ func (session *Session) row2Bean(rows *core.Rows, fields []string, fieldsCount i t.Minute(), t.Second(), t.Nanosecond(), time.Local) vv = reflect.ValueOf(tt) } + // !nashtsai! convert to engine location + t = vv.Interface().(time.Time).In(session.Engine.TZLocation) + vv = reflect.ValueOf(t) fieldValue.Set(vv) + // t = fieldValue.Interface().(time.Time) // z, _ = t.Zone() // session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location()) From 1a4556f2d6fee1ad7fb9e971682102d90002471a Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 21 Apr 2014 10:02:47 +0800 Subject: [PATCH 53/55] update README --- README.md | 11 +++++++++++ README_CN.md | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index f7fd6e1c..0a21b668 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,17 @@ Drivers for Go's sql package which currently support database/sql includes: # Changelog +* **v0.4.0 RC1** + Changes: + * moved xorm cmd to [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + * refactored general DB operation a core lib at [github.com/go-xorm/core](https://github.com/go-xorm/core) + * moved tests to github.com/go-xorm/tests [github.com/go-xorm/tests](github.com/go-xorm/tests) + + Improvements: + * Prepared statement cache + * Add Incr API + * Specify Timezone Location + * **v0.3.2** Improvements: * Add AllCols & MustCols function diff --git a/README_CN.md b/README_CN.md index e9bda379..d40939cb 100644 --- a/README_CN.md +++ b/README_CN.md @@ -42,6 +42,17 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 更新日志 +* **v0.4.0 RC1** + 新特性: + *移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + *在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) + *移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) + + 改进: + *Prepared statement 缓存 + *添加 Incr API + *指定时区位置 + * **v0.3.2** Improvements: * Add AllCols & MustCols function From 97f31837edf6021b356fcaf2f6f2ee160b7609e7 Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 21 Apr 2014 10:07:36 +0800 Subject: [PATCH 54/55] update readme --- README_CN.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README_CN.md b/README_CN.md index d40939cb..c8f3f180 100644 --- a/README_CN.md +++ b/README_CN.md @@ -44,21 +44,21 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * **v0.4.0 RC1** 新特性: - *移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) - *在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) - *移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) + * 移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + * 在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) + * 移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) 改进: - *Prepared statement 缓存 - *添加 Incr API - *指定时区位置 + * Prepared statement 缓存 + * 添加 Incr API + * 指定时区位置 * **v0.3.2** - Improvements: + 新特性: * Add AllCols & MustCols function * Add TableName for custom table name - Bug Fixes: + Bug 修复: * #46 * #51 * #53 From e86d5fd165a82a7471465995305bb4fa7b3c132b Mon Sep 17 00:00:00 2001 From: Nash Tsai Date: Mon, 21 Apr 2014 10:08:46 +0800 Subject: [PATCH 55/55] update changelog --- docs/ChangelogCN.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ChangelogCN.md b/docs/ChangelogCN.md index 7015cc13..1ea3f748 100644 --- a/docs/ChangelogCN.md +++ b/docs/ChangelogCN.md @@ -2,14 +2,14 @@ * **v0.4.0 RC1** 新特性: - *移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) - *在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) - *移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) + * 移动xorm cmd [github.com/go-xorm/cmd](github.com/go-xorm/cmd) + * 在重构一般DB操作核心库 [github.com/go-xorm/core](https://github.com/go-xorm/core) + * 移动测试github.com/复XORM/测试 [github.com/go-xorm/tests](github.com/go-xorm/tests) 改进: - *Prepared statement 缓存 - *添加 Incr API - *指定时区位置 + * Prepared statement 缓存 + * 添加 Incr API + * 指定时区位置 * **v0.3.2** 改进: