Merge pull request #48 from nashtsai/master

code tidy up, minor performance improvement
This commit is contained in:
lunny 2014-01-08 05:34:43 -08:00
commit 8648f9458e
7 changed files with 404 additions and 385 deletions

View File

@ -1,17 +1,17 @@
[中文](https://github.com/lunny/xorm/blob/master/README_CN.md) [中文](https://github.com/lunny/xorm/blob/master/README_CN.md)
Xorm is a simple and powerful ORM for Go. Xorm is a simple and powerful ORM for Go.
[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/lunny/xorm/trend.png)](https://bitdeli.com/free "Bitdeli Badge") [![Build Status](https://drone.io/github.com/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 * Struct <-> Table Mapping Support
* Chainable APIs * Chainable APIs
* Transaction Support * Transaction Support
* Both ORM and raw SQL operation Support * Both ORM and raw SQL operation Support
* Sync database sechmea Support * Sync database sechmea Support
@ -24,26 +24,26 @@ Xorm is a simple and powerful ORM for Go.
* Optimistic Locking support * Optimistic Locking support
# Drivers Support # Drivers Support
Drivers for Go's sql package which currently support database/sql includes: 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) * 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) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/lib/pq](https://github.com/lib/pq) * Postgres: [github.com/lib/pq](https://github.com/lib/pq)
* MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc) * MsSql: [github.com/lunny/godbc](https://github.com/lunny/godbc)
# Changelog # Changelog
* **v0.3.1** * **v0.3.1**
Features: Features:
* Support MSSQL DB via ODBC driver ([github.com/lunny/godbc](https://github.com/lunny/godbc)); * Support MSSQL DB via ODBC driver ([github.com/lunny/godbc](https://github.com/lunny/godbc));
* Composite Key, using multiple pk xorm tag * 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 * 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 * Allowed int/int32/int64/uint/uint32/uint64/string as Primary Key type
* Performance improvement for Get()/Find()/Iterate() * 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 # Installation
If you have [gopm](https://github.com/gpmgo/gopm) installed, If you have [gopm](https://github.com/gpmgo/gopm) installed,
gopm get github.com/lunny/xorm gopm get github.com/lunny/xorm
Or Or
go get github.com/lunny/xorm go get github.com/lunny/xorm
# Documents # 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) * [GoWalker](http://gowalker.org/github.com/lunny/xorm)
* [Quick Start](https://github.com/lunny/xorm/blob/master/docs/QuickStartEn.md) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
# Todo # Todo
[Todo List](https://trello.com/b/IHsuAnhk/xorm) [Todo List](https://trello.com/b/IHsuAnhk/xorm)
# Discuss # Discuss
Please visit [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) 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) If you want to pull request, please see [CONTRIBUTING](https://github.com/lunny/xorm/blob/master/CONTRIBUTING.md)
* [Lunny](https://github.com/lunny) * [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai) * [Nashtsai](https://github.com/nashtsai)
# LICENSE # LICENSE
BSD License BSD License
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)

View File

@ -1,7 +1,7 @@
# xorm # xorm
[English](https://github.com/lunny/xorm/blob/master/README.md) [English](https://github.com/lunny/xorm/blob/master/README.md)
xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
[![Build Status](https://drone.io/github.com/lunny/xorm/status.png)](https://drone.io/github.com/lunny/xorm/latest) [![Go Walker](http://gowalker.org/api/v1/badge)](http://gowalker.org/github.com/lunny/xorm) [![Build Status](https://drone.io/github.com/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数据库驱动和对应的数据库如下 目前支持的Go数据库驱动和对应的数据库如下
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) * Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) * 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)); * 支持 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 作为主键类型 * 允许 int/int32/int64/uint/uint32/uint64/string 作为主键类型
* 查询函数 Get()/Find()/Iterate() 在性能上的改进 * 查询函数 Get()/Find()/Iterate() 在性能上的改进
[更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md) [更多更新日志...](https://github.com/lunny/xorm/blob/master/docs/ChangelogCN.md)
## 安装 ## 安装
推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装: 推荐使用 [gopm](https://github.com/gpmgo/gopm) 进行安装:
@ -66,10 +66,10 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
或者您也可以使用go工具进行安装 或者您也可以使用go工具进行安装
go get github.com/lunny/xorm go get github.com/lunny/xorm
## 文档 ## 文档
* [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md) * [快速开始](https://github.com/lunny/xorm/blob/master/docs/QuickStart.md)
* [GoWalker代码文档](http://gowalker.org/github.com/lunny/xorm) * [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) * [Godaily](http://godaily.org) - [github.com/govc/godaily](http://github.com/govc/godaily)
* [Very Hour](http://veryhour.com/) * [Very Hour](http://veryhour.com/)
## Todo ## Todo
[开发计划](https://trello.com/b/IHsuAnhk/xorm) [开发计划](https://trello.com/b/IHsuAnhk/xorm)
@ -101,8 +101,8 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
* [Lunny](https://github.com/lunny) * [Lunny](https://github.com/lunny)
* [Nashtsai](https://github.com/nashtsai) * [Nashtsai](https://github.com/nashtsai)
## LICENSE ## LICENSE
BSD License BSD License
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)

View File

@ -590,7 +590,7 @@ func (engine *Engine) mapType(t reflect.Type) *Table {
sqlType := Type2SQLType(fieldType) sqlType := Type2SQLType(fieldType)
col = &Column{engine.columnMapper.Obj2Table(t.Field(i).Name), t.Field(i).Name, sqlType, 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, sqlType.DefaultLength, sqlType.DefaultLength2, true, "", make(map[string]bool), false, false,
TWOSIDES, false, false, false, false} TWOSIDES, false, false, false, false, nil}
} }
if col.IsAutoIncrement { if col.IsAutoIncrement {
col.Nullable = false col.Nullable = false

516
oracle.go
View File

@ -1,258 +1,258 @@
package xorm package xorm
import ( import (
"database/sql" "database/sql"
"errors" "errors"
"fmt" "fmt"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
) )
type oracle struct { type oracle struct {
base base
} }
type oracleParser struct { type oracleParser struct {
} }
//dataSourceName=user/password@ipv4:port/dbname //dataSourceName=user/password@ipv4:port/dbname
//dataSourceName=user/password@[ipv6]:port/dbname //dataSourceName=user/password@[ipv6]:port/dbname
func (p *oracleParser) parse(driverName, dataSourceName string) (*uri, error) { func (p *oracleParser) parse(driverName, dataSourceName string) (*uri, error) {
db := &uri{dbType: ORACLE_OCI} db := &uri{dbType: ORACLE_OCI}
dsnPattern := regexp.MustCompile( dsnPattern := regexp.MustCompile(
`^(?P<user>.*)\/(?P<password>.*)@` + // user:password@ `^(?P<user>.*)\/(?P<password>.*)@` + // user:password@
`(?P<net>.*)` + // ip:port `(?P<net>.*)` + // ip:port
`\/(?P<dbname>.*)`) // dbname `\/(?P<dbname>.*)`) // dbname
matches := dsnPattern.FindStringSubmatch(dataSourceName) matches := dsnPattern.FindStringSubmatch(dataSourceName)
names := dsnPattern.SubexpNames() names := dsnPattern.SubexpNames()
for i, match := range matches { for i, match := range matches {
switch names[i] { switch names[i] {
case "dbname": case "dbname":
db.dbName = match db.dbName = match
} }
} }
if db.dbName == "" { if db.dbName == "" {
return nil, errors.New("dbname is empty") return nil, errors.New("dbname is empty")
} }
return db, nil return db, nil
} }
func (db *oracle) Init(drivername, uri string) error { func (db *oracle) Init(drivername, uri string) error {
return db.base.init(&oracleParser{}, drivername, uri) return db.base.init(&oracleParser{}, drivername, uri)
} }
func (db *oracle) SqlType(c *Column) string { func (db *oracle) SqlType(c *Column) string {
var res string var res string
switch t := c.SQLType.Name; t { switch t := c.SQLType.Name; t {
case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial: case Bit, TinyInt, SmallInt, MediumInt, Int, Integer, BigInt, Bool, Serial, BigSerial:
return "NUMBER" return "NUMBER"
case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea: case Binary, VarBinary, Blob, TinyBlob, MediumBlob, LongBlob, Bytea:
return Blob return Blob
case Time, DateTime, TimeStamp: case Time, DateTime, TimeStamp:
res = TimeStamp res = TimeStamp
case TimeStampz: case TimeStampz:
res = "TIMESTAMP WITH TIME ZONE" res = "TIMESTAMP WITH TIME ZONE"
case Float, Double, Numeric, Decimal: case Float, Double, Numeric, Decimal:
res = "NUMBER" res = "NUMBER"
case Text, MediumText, LongText: case Text, MediumText, LongText:
res = "CLOB" res = "CLOB"
case Char, Varchar, TinyText: case Char, Varchar, TinyText:
return "VARCHAR2" return "VARCHAR2"
default: default:
res = t res = t
} }
var hasLen1 bool = (c.Length > 0) var hasLen1 bool = (c.Length > 0)
var hasLen2 bool = (c.Length2 > 0) var hasLen2 bool = (c.Length2 > 0)
if hasLen1 { if hasLen1 {
res += "(" + strconv.Itoa(c.Length) + ")" res += "(" + strconv.Itoa(c.Length) + ")"
} else if hasLen2 { } else if hasLen2 {
res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")" res += "(" + strconv.Itoa(c.Length) + "," + strconv.Itoa(c.Length2) + ")"
} }
return res return res
} }
func (db *oracle) SupportInsertMany() bool { func (db *oracle) SupportInsertMany() bool {
return true return true
} }
func (db *oracle) QuoteStr() string { func (db *oracle) QuoteStr() string {
return "\"" return "\""
} }
func (db *oracle) AutoIncrStr() string { func (db *oracle) AutoIncrStr() string {
return "" return ""
} }
func (db *oracle) SupportEngine() bool { func (db *oracle) SupportEngine() bool {
return false return false
} }
func (db *oracle) SupportCharset() bool { func (db *oracle) SupportCharset() bool {
return false return false
} }
func (db *oracle) IndexOnTable() bool { func (db *oracle) IndexOnTable() bool {
return false return false
} }
func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) { func (db *oracle) IndexCheckSql(tableName, idxName string) (string, []interface{}) {
args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)} args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(idxName)}
return `SELECT INDEX_NAME FROM USER_INDEXES ` + return `SELECT INDEX_NAME FROM USER_INDEXES ` +
`WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args `WHERE TABLE_NAME = ? AND INDEX_NAME = ?`, args
} }
func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) { func (db *oracle) TableCheckSql(tableName string) (string, []interface{}) {
args := []interface{}{strings.ToUpper(tableName)} args := []interface{}{strings.ToUpper(tableName)}
return `SELECT table_name FROM user_tables WHERE table_name = ?`, args return `SELECT table_name FROM user_tables WHERE table_name = ?`, args
} }
func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) { func (db *oracle) ColumnCheckSql(tableName, colName string) (string, []interface{}) {
args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)} args := []interface{}{strings.ToUpper(tableName), strings.ToUpper(colName)}
return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" + return "SELECT column_name FROM USER_TAB_COLUMNS WHERE table_name = ?" +
" AND column_name = ?", args " AND column_name = ?", args
} }
func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) { func (db *oracle) GetColumns(tableName string) ([]string, map[string]*Column, error) {
args := []interface{}{strings.ToUpper(tableName)} args := []interface{}{strings.ToUpper(tableName)}
s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," + s := "SELECT column_name,data_default,data_type,data_length,data_precision,data_scale," +
"nullable FROM USER_TAB_COLUMNS WHERE table_name = :1" "nullable FROM USER_TAB_COLUMNS WHERE table_name = :1"
cnn, err := sql.Open(db.driverName, db.dataSourceName) cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
defer cnn.Close() defer cnn.Close()
res, err := query(cnn, s, args...) res, err := query(cnn, s, args...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
cols := make(map[string]*Column) cols := make(map[string]*Column)
colSeq := make([]string, 0) colSeq := make([]string, 0)
for _, record := range res { for _, record := range res {
col := new(Column) col := new(Column)
col.Indexes = make(map[string]bool) col.Indexes = make(map[string]bool)
for name, content := range record { for name, content := range record {
switch name { switch name {
case "column_name": case "column_name":
col.Name = strings.Trim(string(content), `" `) col.Name = strings.Trim(string(content), `" `)
case "data_default": case "data_default":
col.Default = string(content) col.Default = string(content)
case "nullable": case "nullable":
if string(content) == "Y" { if string(content) == "Y" {
col.Nullable = true col.Nullable = true
} else { } else {
col.Nullable = false col.Nullable = false
} }
case "data_type": case "data_type":
ct := string(content) ct := string(content)
switch ct { switch ct {
case "VARCHAR2": case "VARCHAR2":
col.SQLType = SQLType{Varchar, 0, 0} col.SQLType = SQLType{Varchar, 0, 0}
case "TIMESTAMP WITH TIME ZONE": case "TIMESTAMP WITH TIME ZONE":
col.SQLType = SQLType{TimeStamp, 0, 0} col.SQLType = SQLType{TimeStamp, 0, 0}
default: default:
col.SQLType = SQLType{strings.ToUpper(ct), 0, 0} col.SQLType = SQLType{strings.ToUpper(ct), 0, 0}
} }
if _, ok := sqlTypes[col.SQLType.Name]; !ok { if _, ok := sqlTypes[col.SQLType.Name]; !ok {
return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct)) return nil, nil, errors.New(fmt.Sprintf("unkonw colType %v", ct))
} }
case "data_length": case "data_length":
i, err := strconv.Atoi(string(content)) i, err := strconv.Atoi(string(content))
if err != nil { if err != nil {
return nil, nil, errors.New("retrieve length error") return nil, nil, errors.New("retrieve length error")
} }
col.Length = i col.Length = i
case "data_precision": case "data_precision":
case "data_scale": case "data_scale":
} }
} }
if col.SQLType.IsText() { if col.SQLType.IsText() {
if col.Default != "" { if col.Default != "" {
col.Default = "'" + col.Default + "'" col.Default = "'" + col.Default + "'"
} }
} }
cols[col.Name] = col cols[col.Name] = col
colSeq = append(colSeq, col.Name) colSeq = append(colSeq, col.Name)
} }
return colSeq, cols, nil return colSeq, cols, nil
} }
func (db *oracle) GetTables() ([]*Table, error) { func (db *oracle) GetTables() ([]*Table, error) {
args := []interface{}{} args := []interface{}{}
s := "SELECT table_name FROM user_tables" s := "SELECT table_name FROM user_tables"
cnn, err := sql.Open(db.driverName, db.dataSourceName) cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cnn.Close() defer cnn.Close()
res, err := query(cnn, s, args...) res, err := query(cnn, s, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tables := make([]*Table, 0) tables := make([]*Table, 0)
for _, record := range res { for _, record := range res {
table := new(Table) table := new(Table)
for name, content := range record { for name, content := range record {
switch name { switch name {
case "table_name": case "table_name":
table.Name = string(content) table.Name = string(content)
} }
} }
tables = append(tables, table) tables = append(tables, table)
} }
return tables, nil return tables, nil
} }
func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) { func (db *oracle) GetIndexes(tableName string) (map[string]*Index, error) {
args := []interface{}{tableName} args := []interface{}{tableName}
s := "SELECT t.column_name,i.table_name,i.uniqueness,i.index_name FROM user_ind_columns t,user_indexes i " + s := "SELECT t.column_name,i.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" "WHERE t.index_name = i.index_name and t.table_name = i.table_name and t.table_name =:1"
cnn, err := sql.Open(db.driverName, db.dataSourceName) cnn, err := sql.Open(db.driverName, db.dataSourceName)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer cnn.Close() defer cnn.Close()
res, err := query(cnn, s, args...) res, err := query(cnn, s, args...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
indexes := make(map[string]*Index, 0) indexes := make(map[string]*Index, 0)
for _, record := range res { for _, record := range res {
var indexType int var indexType int
var indexName string var indexName string
var colName string var colName string
for name, content := range record { for name, content := range record {
switch name { switch name {
case "index_name": case "index_name":
indexName = strings.Trim(string(content), `" `) indexName = strings.Trim(string(content), `" `)
case "uniqueness": case "uniqueness":
c := string(content) c := string(content)
if c == "UNIQUE" { if c == "UNIQUE" {
indexType = UniqueType indexType = UniqueType
} else { } else {
indexType = IndexType indexType = IndexType
} }
case "column_name": case "column_name":
colName = string(content) colName = string(content)
} }
} }
var index *Index var index *Index
var ok bool var ok bool
if index, ok = indexes[indexName]; !ok { if index, ok = indexes[indexName]; !ok {
index = new(Index) index = new(Index)
index.Type = indexType index.Type = indexType
index.Name = indexName index.Name = indexName
indexes[indexName] = index indexes[indexName] = index
} }
index.AddColumn(colName) index.AddColumn(colName)
} }
return indexes, nil return indexes, nil
} }

View File

@ -1092,25 +1092,41 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{})
fieldsCount := len(fields) fieldsCount := len(fields)
for rawRows.Next() { var newElemFunc func() reflect.Value
var newValue 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 { if sliceElementType.Kind() == reflect.Ptr {
newValue = reflect.New(sliceElementType.Elem()) sliceValueSetFunc = func(newValue *reflect.Value) {
} 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 {
sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface()))) 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())))) 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 { } else {
resultsSlice, err := session.query(sqlStr, args...) resultsSlice, err := session.query(sqlStr, args...)
if err != nil { if err != nil {
@ -1347,32 +1363,35 @@ func row2map(rows *sql.Rows, fields []string) (resultsMap map[string][]byte, err
func (session *Session) getField(dataStruct *reflect.Value, key string, table *Table) *reflect.Value { func (session *Session) getField(dataStruct *reflect.Value, key string, table *Table) *reflect.Value {
key = strings.ToLower(key) //key = strings.ToLower(key)
if _, ok := table.Columns[key]; !ok { 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)) session.Engine.LogWarn(fmt.Sprintf("table %v's has not column %v. %v", table.Name, key, table.ColumnsSeq))
return nil 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 { } else {
fieldValue = dataStruct.FieldByName(fieldName) fieldName := col.FieldName
if col.fieldPath == nil {
col.fieldPath = strings.Split(fieldName, ".")
}
var fieldValue reflect.Value
fieldPathLen := len(col.fieldPath)
if fieldPathLen > 2 {
session.Engine.LogError("Unsupported mutliderive", fieldName)
return nil
} else if fieldPathLen == 2 {
parentField := dataStruct.FieldByName(col.fieldPath[0])
if parentField.IsValid() {
fieldValue = parentField.FieldByName(col.fieldPath[1])
}
} else {
fieldValue = dataStruct.FieldByName(fieldName)
}
if !fieldValue.IsValid() || !fieldValue.CanSet() {
session.Engine.LogWarn("table %v's column %v is not valid or cannot set",
table.Name, key)
return nil
}
return &fieldValue
} }
if !fieldValue.IsValid() || !fieldValue.CanSet() {
session.Engine.LogWarn("table %v's column %v is not valid or cannot set",
table.Name, key)
return nil
}
return &fieldValue
} }
func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error { func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount int, bean interface{}) error {
@ -1395,7 +1414,6 @@ func (session *Session) row2Bean(rows *sql.Rows, fields []string, fieldsCount in
for ii, key := range fields { for ii, key := range fields {
if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil { if fieldValue := session.getField(&dataStruct, key, table); fieldValue != nil {
rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii])) rawValue := reflect.Indirect(reflect.ValueOf(scanResultContainers[ii]))
//if row is null then ignore //if row is null then ignore

View File

@ -275,6 +275,7 @@ type Column struct {
IsUpdated bool IsUpdated bool
IsCascade bool IsCascade bool
IsVersion bool IsVersion bool
fieldPath []string
} }
// generate column description string according dialect // generate column description string according dialect

View File

@ -1,30 +1,30 @@
# xorm tools # xorm tools
xorm tools is a set of tools for database operation. xorm tools is a set of tools for database operation.
## Install ## Install
`go get github.com/lunny/xorm/xorm` `go get github.com/lunny/xorm/xorm`
and you should install the depends below: and you should install the depends below:
* github.com/lunny/xorm * github.com/lunny/xorm
* Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql) * Mysql: [github.com/go-sql-driver/mysql](https://github.com/go-sql-driver/mysql)
* MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv) * MyMysql: [github.com/ziutek/mymysql/godrv](https://github.com/ziutek/mymysql/godrv)
* SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3) * SQLite: [github.com/mattn/go-sqlite3](https://github.com/mattn/go-sqlite3)
* Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq) * Postgres: [github.com/bylevel/pq](https://github.com/bylevel/pq)
## Reverse ## Reverse
After you installed the tool, you can type After you installed the tool, you can type
`xorm help reverse` `xorm help reverse`
to get help to get help
@ -50,13 +50,13 @@ Now, xorm tool supports go and c++ two languages and have go, goxorm, c++ three
```` ````
lang=go lang=go
genJson=1 genJson=1
``` ```
lang must be go or c++ now. lang must be go or c++ now.
genJson can be 1 or 0, if 1 then the struct will have json tag. genJson can be 1 or 0, if 1 then the struct will have json tag.
## LICENSE ## LICENSE
BSD License BSD License
[http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/) [http://creativecommons.org/licenses/BSD/](http://creativecommons.org/licenses/BSD/)