diff --git a/README.md b/README.md index 7dcdf0c0..ecdfa721 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,17 @@ -[中文](https://github.com/lunny/xorm/blob/master/README_CN.md) - -Xorm is a simple and powerful ORM for Go. - +[中文](https://github.com/lunny/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") -# Features - +# Features + * Struct <-> Table Mapping Support * Chainable APIs * Transaction Support - + * Both ORM and raw SQL operation Support * Sync database sechmea Support @@ -24,26 +24,26 @@ Xorm is a simple and powerful ORM for Go. * Optimistic Locking support - -# Drivers Support - -Drivers for Go's sql package which currently support database/sql includes: - + +# Drivers Support + +Drivers for Go's sql package which currently support database/sql includes: + * 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) - -* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) - + +* 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://github.com/lunny/godbc) + # Changelog -* **v0.3.1** - - Features: +* **v0.3.1** + + Features: * Support MSSQL DB via ODBC driver ([github.com/lunny/godbc](https://github.com/lunny/godbc)); * Composite Key, using multiple pk xorm tag * Added Row() API as alternative to Iterate() API for traversing result set, provide similar usages to sql.Rows type @@ -54,22 +54,22 @@ 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/lunny/xorm/blob/master/docs/Changelog.md) + # Installation If you have [gopm](https://github.com/gpmgo/gopm) installed, gopm get github.com/lunny/xorm -Or - - go get github.com/lunny/xorm - +Or + + go get github.com/lunny/xorm + # Documents -* [GoDoc](http://godoc.org/github.com/lunny/xorm) - +* [GoDoc](http://godoc.org/github.com/lunny/xorm) + * [GoWalker](http://gowalker.org/github.com/lunny/xorm) * [Quick Start](https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md) @@ -82,12 +82,12 @@ Or * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily) -* [Very Hour](http://veryhour.com/) - -# Todo - -[Todo List](https://trello.com/b/IHsuAnhk/xorm) - +* [Very Hour](http://veryhour.com/) + +# Todo + +[Todo List](https://trello.com/b/IHsuAnhk/xorm) + # Discuss Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) @@ -97,9 +97,9 @@ Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xor If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md) * [Lunny](https://github.com/lunny) -* [Nashtsai](https://github.com/nashtsai) - -# LICENSE - - BSD License - [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) +* [Nashtsai](https://github.com/nashtsai) + +# LICENSE + + BSD License + [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/README_CN.md b/README_CN.md index 8399b87f..d1b88e67 100644 --- a/README_CN.md +++ b/README_CN.md @@ -1,7 +1,7 @@ # xorm - -[English](https://github.com/lunny/xorm/blob/master/README.md) - + +[English](https://github.com/lunny/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) @@ -27,11 +27,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * 支持记录版本(即乐观锁) ## 驱动支持 - -目前支持的Go数据库驱动和对应的数据库如下: - -* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) - + +目前支持的Go数据库驱动和对应的数据库如下: + +* 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) @@ -42,7 +42,7 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 ## 更新日志 -* **v0.3.1** +* **v0.3.1** 新特性: * 支持 MSSQL DB 通过 ODBC 驱动 ([github.com/lunny/godbc](https://github.com/lunny/godbc)); @@ -53,11 +53,11 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 改进: * 允许 int/int32/int64/uint/uint32/uint64/string 作为主键类型 - * 查询函数 Get()/Find()/Iterate() 在性能上的改进 + * 查询函数 Get()/Find()/Iterate() 在性能上的改进 [更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md) - + ## 安装 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: @@ -66,10 +66,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 或者您也可以使用go工具进行安装: - go get github.com/lunny/xorm - + go get github.com/lunny/xorm + ## 文档 - + * [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md) * [GoWalker代码文档](http://gowalker.org/github.com/lunny/xorm) @@ -85,8 +85,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily) -* [Very Hour](http://veryhour.com/) - +* [Very Hour](http://veryhour.com/) + ## Todo [开发计划](https://trello.com/b/IHsuAnhk/xorm) @@ -101,8 +101,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作 * [Lunny](https://github.com/lunny) * [Nashtsai](https://github.com/nashtsai) - -## LICENSE - -BSD License -[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) + +## LICENSE + +BSD License +[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) diff --git a/base_test.go b/base_test.go index 66631160..9b66890c 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)) } @@ -3175,7 +3177,9 @@ func testPointerData(engine *Engine, t *testing.T) { // using instance type should just work too nullData2Get := NullData2{} - has, err = engine.Table("null_data").Id(nullData.Id).Get(&nullData2Get) + tableName := engine.tableMapper.Obj2Table("NullData") + + has, err = engine.Table(tableName).Id(nullData.Id).Get(&nullData2Get) if err != nil { t.Error(err) panic(err) @@ -3525,8 +3529,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 { @@ -3540,7 +3544,9 @@ func testNullValue(engine *Engine, t *testing.T) { // update to null values nullDataUpdate = NullData{} - cnt, err = engine.Id(nullData.Id).Cols("string_ptr").Update(&nullDataUpdate) + 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) @@ -3896,3 +3902,11 @@ func testAll3(engine *Engine, t *testing.T) { fmt.Println("-------------- testStringPK --------------") testStringPK(engine, t) } + +func testAllSnakeMapper(engine *Engine, t *testing.T) { + +} + +func testAllSameMapper(engine *Engine, t *testing.T) { + +} diff --git a/core/column.go b/core/column.go new file mode 100644 index 00000000..8bea883e --- /dev/null +++ b/core/column.go @@ -0,0 +1,113 @@ +package core + +import ( + "fmt" + "reflect" + "strings" +) + +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 + fieldPath []string +} + +func NewColumn(name, fieldName string, sqlType SQLType, len1, len2 int, nullable bool) *Column { + return &Column{name, fieldName, sqlType, len1, len2, nullable, "", make(map[string]bool), false, false, + TWOSIDES, false, false, false, false, nil} +} + +// 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, error) { + dataStruct := reflect.Indirect(reflect.ValueOf(bean)) + return col.ValueOfV(&dataStruct) +} + +func (col *Column) ValueOfV(dataStruct *reflect.Value) (*reflect.Value, error) { + var fieldValue reflect.Value + var err error + if col.fieldPath == nil { + col.fieldPath = strings.Split(col.FieldName, ".") + } + + if len(col.fieldPath) == 1 { + fieldValue = dataStruct.FieldByName(col.FieldName) + } else if len(col.fieldPath) == 2 { + parentField := dataStruct.FieldByName(col.fieldPath[0]) + if parentField.IsValid() { + fieldValue = parentField.FieldByName(col.fieldPath[1]) + } else { + err = fmt.Errorf("field %v is not valid", col.FieldName) + } + } else { + err = fmt.Errorf("Unsupported mutliderive %v", col.FieldName) + } + if err != nil { + return nil, err + } + return &fieldValue, nil +} 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 54% rename from oracle.go rename to dialects/oracle.go index b863b6ab..c3f98f1a 100644 --- a/oracle.go +++ b/dialects/oracle.go @@ -1,258 +1,250 @@ -package xorm - -import ( - "database/sql" - "errors" - "fmt" - "regexp" - "strconv" - "strings" -) - -type oracle struct { - 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) SqlType(c *Column) string { - var res string - switch t := c.SQLType.Name; t { - case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: - return "NUMBER" - case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: - return Blob - case Time, DateTime, TimeStamp: - res = TimeStamp - case TimeStampz: - res = "TIMESTAMP WITH TIME ZONE" - case Float, Double, Numeric, Decimal: - res = "NUMBER" - case Text, MediumText, LongText: - res = "CLOB" - case Char, Varchar, TinyText: - return "VARCHAR2" - 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 *oracle) SupportInsertMany() bool { - return true -} - -func (db *oracle) QuoteStr() string { - return "\"" -} - -func (db *oracle) AutoIncrStr() string { - return "" -} - -func (db *oracle) SupportEngine() bool { - return false -} - -func (db *oracle) SupportCharset() bool { - return false -} - -func (db *oracle) IndexOnTable() bool { - return false -} - -func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} - return `SELECT INDEX_NAME FROM USER_INDEXES ` + - `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args -} - -func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName)} - return `SELECT table_name FROM user_tables WHERE table_name = ?`, args -} - -func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { - args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)} - return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + - " AND column_name = ?", args -} - -func (db *oracle) GetColumns(tableName string) ([]string, map[string]*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 := 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 "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": - } - } - 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 *oracle) GetTables() ([]*Table, error) { - args := []interface{}{} - s := "SELECT table_name FROM user_tables" - 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 = string(content) - } - } - tables = append(tables, table) - } - return tables, nil -} - -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 " + - "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) - 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 string - var colName 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) - } - } - - 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 -} +package dialects + +import ( + "errors" + "fmt" + "strconv" + "strings" + + . "github.com/lunny/xorm/core" +) + +func init() { + RegisterDialect("oracle", &oracle{}) +} + +type oracle struct { + Base +} + +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 { + var res string + switch t := c.SQLType.Name; t { + case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: + return "NUMBER" + case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: + return Blob + case Time, DateTime, TimeStamp: + res = TimeStamp + case TimeStampz: + res = "TIMESTAMP WITH TIME ZONE" + case Float, Double, Numeric, Decimal: + res = "NUMBER" + case Text, MediumText, LongText: + res = "CLOB" + case Char, Varchar, TinyText: + return "VARCHAR2" + 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 *oracle) SupportInsertMany() bool { + return true +} + +func (db *oracle) QuoteStr() string { + return "\"" +} + +func (db *oracle) AutoIncrStr() string { + return "" +} + +func (db *oracle) SupportEngine() bool { + return false +} + +func (db *oracle) SupportCharset() bool { + return false +} + +func (db *oracle) IndexOnTable() bool { + return false +} + +func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} + return `SELECT INDEX_NAME FROM USER_INDEXES ` + + `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args +} + +func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName)} + return `SELECT table_name FROM user_tables WHERE table_name = ?`, args +} + +func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { + args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)} + return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + + " AND column_name = ?", args +} + +func (db *oracle) GetColumns(tableName string) ([]string, map[string]*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()) + if err != nil { + 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]*Column) + colSeq := make([]string, 0) + for rows.Next() { + col := new(Column) + col.Indexes = make(map[string]bool) + + 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 + "'" + } + } + cols[col.Name] = col + colSeq = append(colSeq, col.Name) + } + + return colSeq, cols, nil +} + +func (db *oracle) GetTables() ([]*Table, error) { + args := []interface{}{} + s := "SELECT table_name FROM user_tables" + 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() + err = rows.Scan(&table.Name) + if err != nil { + return nil, err + } + + tables = append(tables, table) + } + return tables, nil +} + +func (db *oracle) GetIndexes(tableName string) (map[string]*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()) + if err != nil { + return nil, err + } + defer cnn.Close() + rows, err := cnn.Query(s, args...) + if err != nil { + return nil, err + } + defer rows.Close() + + indexes := make(map[string]*Index, 0) + for rows.Next() { + var indexType int + var indexName, colName, uniqueness string + + 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 + 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 +} + +// 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..99e62ee0 100644 --- a/engine.go +++ b/engine.go @@ -12,40 +12,10 @@ import ( "strconv" "strings" "sync" + + "github.com/lunny/xorm/core" ) -const ( - POSTGRES = "postgres" - SQLITE = "sqlite3" - MYSQL = "mysql" - MYMYSQL = "mymysql" - - 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{} // Engine is the major struct of xorm, it means a database manager. @@ -56,18 +26,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 +73,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 +101,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 +122,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 +201,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 +212,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 +226,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 +389,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,37 +400,31 @@ 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, - IsAutoIncrement: false, MapType: TWOSIDES, Indexes: make(map[string]bool)} + col = &core.Column{FieldName: t.Field(i).Name, Nullable: true, IsPrimaryKey: false, + IsAutoIncrement: false, MapType: core.TWOSIDES, Indexes: make(map[string]bool)} tags := strings.Split(ormTagStr, " ") if len(tags) > 0 { @@ -470,25 +433,24 @@ 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 continue } + var indexType int var indexName string for j, key := range tags { k := strings.ToUpper(key) switch { case k == "<-": - col.MapType = ONLYFROMDB + col.MapType = core.ONLYFROMDB case k == "->": - col.MapType = ONLYTODB + col.MapType = core.ONLYTODB case k == "PK": col.IsPrimaryKey = true col.Nullable = false @@ -506,15 +468,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 +487,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 +499,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 +518,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 +528,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 +541,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,10 +549,9 @@ 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.DefaultLength, sqlType.DefaultLength2, true, "", make(map[string]bool), false, false, - TWOSIDES, false, false, false, false} + sqlType := core.Type2SQLType(fieldType) + col = core.NewColumn(colMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, + sqlType.DefaultLength, sqlType.DefaultLength2, true) } if col.IsAutoIncrement { col.Nullable = false @@ -604,7 +565,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 +627,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 +641,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 +657,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 +700,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 +723,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 +738,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/filter.go b/filter.go deleted file mode 100644 index d2b6c468..00000000 --- a/filter.go +++ /dev/null @@ -1,49 +0,0 @@ -package xorm - -import ( - "fmt" - "strings" -) - -// Filter is an interface to filter SQL -type Filter interface { - Do(sql string, session *Session) string -} - -// PgSeqFilter filter SQL replace ?, ? ... to $1, $2 ... -type PgSeqFilter struct { -} - -func (s *PgSeqFilter) Do(sql string, session *Session) 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 -} - -// QuoteFilter filter SQL replace ` to database's own quote character -type QuoteFilter struct { -} - -func (s *QuoteFilter) Do(sql string, session *Session) string { - return strings.Replace(sql, "`", session.Engine.QuoteStr(), -1) -} - -// IdFilter filter SQL replace (id) to primary key column name -type IdFilter struct { -} - -func (i *IdFilter) Do(sql string, session *Session) string { - if session.Statement.RefTable != nil && len(session.Statement.RefTable.PrimaryKeys) == 1 { - sql = strings.Replace(sql, "`(id)`", session.Engine.Quote(session.Statement.RefTable.PrimaryKeys[0]), -1) - sql = strings.Replace(sql, session.Engine.Quote("(id)"), session.Engine.Quote(session.Statement.RefTable.PrimaryKeys[0]), -1) - return strings.Replace(sql, "(id)", session.Engine.Quote(session.Statement.RefTable.PrimaryKeys[0]), -1) - } - return sql -} 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/mysql_test.go b/mysql_test.go index 4329af70..e0c3deac 100644 --- a/mysql_test.go +++ b/mysql_test.go @@ -35,6 +35,30 @@ func TestMysql(t *testing.T) { testAll3(engine, t) } +func TestMysqlSameMapper(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.ShowSQL = showTestSql + engine.ShowErr = showTestSql + engine.ShowWarn = showTestSql + engine.ShowDebug = showTestSql + engine.SetMapper(SameMapper{}) + + testAll(engine, t) + testAll2(engine, t) + testAll3(engine, t) +} + func TestMysqlWithCache(t *testing.T) { err := mysqlDdlImport() if err != 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..e8165a41 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...) @@ -1092,25 +1091,41 @@ 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 sliceValueSetFunc != nil { + err := session.row2Bean(rawRows, fields, fieldsCount, newValue.Interface()) + if err != nil { + return err + } + sliceValueSetFunc(&newValue) + } + } } else { resultsSlice, err := session.query(sqlStr, args...) if err != nil { @@ -1236,9 +1251,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 +1271,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,34 +1360,25 @@ 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 { + 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 + } - 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)) + fieldValue, err := col.ValueOfV(dataStruct) + if err != nil { + session.Engine.LogError(err) 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) - } + 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 + return fieldValue } func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error { @@ -1395,7 +1401,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 @@ -1404,7 +1409,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 +1639,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 +1652,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 +1768,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,12 +1786,12 @@ 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 } - if col.MapType == ONLYFROMDB { + if col.MapType == core.ONLYFROMDB { continue } if session.Statement.ColumnStr != "" { @@ -1814,7 +1819,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error if col.IsAutoIncrement && fieldValue.Int() == 0 { continue } - if col.MapType == ONLYFROMDB { + if col.MapType == core.ONLYFROMDB { continue } if session.Statement.ColumnStr != "" { @@ -1853,7 +1858,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 +1909,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 +1934,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 +1942,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 +1962,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 +2023,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 +2204,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 +2230,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 +2259,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 +2288,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 +2317,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 +2350,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 +2389,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 +2475,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 +2524,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,13 +2532,15 @@ 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()) } if table.Version != "" && session.Statement.checkVersion { - verValue := table.VersionColumn().ValueOf(bean) - if verValue.IsValid() && verValue.CanSet() { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } else if verValue.IsValid() && verValue.CanSet() { verValue.SetInt(1) } } @@ -2548,8 +2555,12 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return res.RowsAffected() } - aiValue := table.AutoIncrColumn().ValueOf(bean) - if !aiValue.IsValid() /*|| aiValue.Int() != 0*/ || !aiValue.CanSet() { + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } + + if aiValue == nil || !aiValue.IsValid() /*|| aiValue.Int() != 0*/ || !aiValue.CanSet() { return res.RowsAffected() } @@ -2580,13 +2591,15 @@ 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()) } if table.Version != "" && session.Statement.checkVersion { - verValue := table.VersionColumn().ValueOf(bean) - if verValue.IsValid() && verValue.CanSet() { + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } else if verValue.IsValid() && verValue.CanSet() { verValue.SetInt(1) } } @@ -2601,8 +2614,12 @@ func (session *Session) innerInsert(bean interface{}) (int64, error) { return 1, err } - aiValue := table.AutoIncrColumn().ValueOf(bean) - if !aiValue.IsValid() /*|| aiValue. != 0*/ || !aiValue.CanSet() { + aiValue, err := table.AutoIncrColumn().ValueOf(bean) + if err != nil { + session.Engine.LogError(err) + } + + if aiValue == nil || !aiValue.IsValid() /*|| aiValue. != 0*/ || !aiValue.CanSet() { return 1, nil } @@ -2659,9 +2676,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 +2704,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 +2724,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 +2738,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,13 +2794,17 @@ func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error { return ErrCacheFailed } - if col, ok := table.Columns[strings.ToLower(colName)]; ok { - fieldValue := col.ValueOf(bean) - session.Engine.LogDebug("[xorm:cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) - if col.IsVersion && session.Statement.checkVersion { - fieldValue.SetInt(fieldValue.Int() + 1) + if col := table.GetColumn(colName); col != nil { + fieldValue, err := col.ValueOf(bean) + if err != nil { + session.Engine.LogError(err) } else { - fieldValue.Set(reflect.ValueOf(args[idx])) + session.Engine.LogDebug("[xorm:cacheUpdate] set bean field", bean, colName, fieldValue.Interface()) + if col.IsVersion && session.Statement.checkVersion { + fieldValue.SetInt(fieldValue.Int() + 1) + } else { + fieldValue.Set(reflect.ValueOf(args[idx])) + } } } else { session.Engine.LogError("[xorm:cacheUpdate] ERROR: column %v is not table %v's", @@ -2820,7 +2841,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 +2861,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 } @@ -2917,7 +2938,12 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1", condition) - condiArgs = append(condiArgs, table.VersionColumn().ValueOf(bean).Interface()) + verValue, err := table.VersionColumn().ValueOf(bean) + if err != nil { + return 0, err + } + + condiArgs = append(condiArgs, verValue.Interface()) } else { if condition != "" { condition = "WHERE " + condition @@ -2946,10 +2972,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 +3016,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 +3024,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 +3116,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..49811110 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,10 +257,16 @@ 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) + fieldValuePtr, err := col.ValueOf(bean) + if err != nil { + engine.LogError(err) + continue + } + + fieldValue := *fieldValuePtr fieldType := reflect.TypeOf(fieldValue.Interface()) requiredField := false @@ -323,10 +331,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 { @@ -510,7 +518,7 @@ func (statement *Statement) Distinct(columns ...string) *Statement { func (statement *Statement) Cols(columns ...string) *Statement { newColumns := col2NewCols(columns...) for _, nc := range newColumns { - statement.columnMap[nc] = true + statement.columnMap[strings.ToLower(nc)] = true } statement.ColumnStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) return statement @@ -521,7 +529,7 @@ func (statement *Statement) UseBool(columns ...string) *Statement { if len(columns) > 0 { newColumns := col2NewCols(columns...) for _, nc := range newColumns { - statement.boolColumnMap[nc] = true + statement.boolColumnMap[strings.ToLower(nc)] = true } } else { statement.allUseBool = true @@ -533,7 +541,7 @@ func (statement *Statement) UseBool(columns ...string) *Statement { func (statement *Statement) Omit(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { - statement.columnMap[nc] = false + statement.columnMap[strings.ToLower(nc)] = false } statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) } @@ -584,13 +592,13 @@ 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 { + if _, ok := statement.columnMap[strings.ToLower(col.Name)]; ok { continue } } - if col.MapType == ONLYTODB { + if col.MapType == core.ONLYTODB { continue } colNames = append(colNames, statement.Engine.Quote(statement.TableName())+"."+statement.Engine.Quote(col.Name)) @@ -599,54 +607,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 +620,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 +638,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 +651,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 +666,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 +693,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 +766,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 +786,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 +804,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..69a7d122 100644 --- a/table.go +++ b/table.go @@ -2,120 +2,12 @@ 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,290 +29,28 @@ 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() { + lColName := strings.ToLower(col.Name) if useCol && !col.IsVersion && !col.IsCreated && !col.IsUpdated { - if _, ok := session.Statement.columnMap[col.Name]; !ok { + if _, ok := session.Statement.columnMap[lColName]; !ok { continue } } - if col.MapType == ONLYFROMDB { + if col.MapType == core.ONLYFROMDB { continue } - fieldValue := col.ValueOf(bean) + 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: @@ -439,12 +69,12 @@ func (table *Table) genCols(session *Session, bean interface{}, useCol bool, inc } if session.Statement.ColumnStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; !ok { + if _, ok := session.Statement.columnMap[lColName]; !ok { continue } } if session.Statement.OmitStr != "" { - if _, ok := session.Statement.columnMap[col.Name]; ok { + if _, ok := session.Statement.columnMap[lColName]; ok { continue } } @@ -469,10 +99,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/README.md b/xorm/README.md index aebfca77..b0d39b86 100644 --- a/xorm/README.md +++ b/xorm/README.md @@ -1,30 +1,30 @@ -# xorm tools - - -xorm tools is a set of tools for database operation. - -## Install - +# xorm tools + + +xorm tools is a set of tools for database operation. + +## Install + `go get github.com/lunny/xorm/xorm` and you should install the depends below: -* github.com/lunny/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) - +* github.com/lunny/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 + + +## Reverse After you installed the tool, you can type -`xorm help reverse` +`xorm help reverse` to get help @@ -50,13 +50,13 @@ Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three ```` lang=go -genJson=1 +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/) + +## LICENSE + + BSD License + [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) 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) }