diff --git a/cache.go b/cache.go index 9fcbcdef..8cf9000f 100644 --- a/cache.go +++ b/cache.go @@ -11,7 +11,9 @@ import ( ) const ( - CacheExpired = 60 * time.Minute + // default cache expired time + CacheExpired = 60 * time.Minute + // not use now CacheMaxMemory = 256 // evey ten minutes to clear all expired nodes CacheGcInterval = 10 * time.Minute @@ -19,12 +21,15 @@ const ( CacheGcMaxRemoved = 20 ) +// CacheStore is a interface to store cache type CacheStore interface { Put(key, value interface{}) error Get(key interface{}) (interface{}, error) Del(key interface{}) error } +// MemoryStore implements CacheStore provide local machine +// memory store type MemoryStore struct { store map[interface{}]interface{} mutex sync.RWMutex @@ -58,6 +63,7 @@ func (s *MemoryStore) Del(key interface{}) error { return nil } +// Cacher is an interface to provide cache type Cacher interface { GetIds(tableName, sql string) interface{} GetBean(tableName string, id int64) interface{} @@ -122,9 +128,9 @@ func NewLRUCacher2(store CacheStore, expired time.Duration, max int) *LRUCacher return newLRUCacher(store, expired, 0, max) } -func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher { - return newLRUCacher(store, expired, maxSize, 0) -} +//func NewLRUCacher3(store CacheStore, expired time.Duration, maxSize int) *LRUCacher { +// return newLRUCacher(store, expired, maxSize, 0) +//} // RunGC run once every m.GcInterval func (m *LRUCacher) RunGC() { diff --git a/engine.go b/engine.go index 2658cab9..580d76af 100644 --- a/engine.go +++ b/engine.go @@ -354,19 +354,21 @@ func (engine *Engine) OrderBy(order string) *Session { return session.OrderBy(order) } -//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN +// The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN func (engine *Engine) Join(join_operator, tablename, condition string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.Join(join_operator, tablename, condition) } +// Generate Group By statement func (engine *Engine) GroupBy(keys string) *Session { session := engine.NewSession() session.IsAutoClose = true return session.GroupBy(keys) } +// Generate Having statement func (engine *Engine) Having(conditions string) *Session { session := engine.NewSession() session.IsAutoClose = true @@ -763,7 +765,7 @@ func (engine *Engine) dropAll() error { if err != nil { return err } - err = session.DropAll() + err = session.dropAll() if err != nil { session.Rollback() return err @@ -811,64 +813,83 @@ func (engine *Engine) DropTables(beans ...interface{}) error { func (engine *Engine) createAll() error { session := engine.NewSession() defer session.Close() - return session.CreateAll() + return session.createAll() } +// Exec raw sql func (engine *Engine) Exec(sql string, args ...interface{}) (sql.Result, error) { session := engine.NewSession() defer session.Close() return session.Exec(sql, args...) } +// Exec a raw sql and return records as []map[string][]byte func (engine *Engine) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { session := engine.NewSession() defer session.Close() return session.Query(sql, paramStr...) } +// Insert one or more records func (engine *Engine) Insert(beans ...interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.Insert(beans...) } +// Insert only one record func (engine *Engine) InsertOne(bean interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.InsertOne(bean) } +// Update records, bean's non-empty fields are updated contents, +// condiBean' non-empty filds are conditions +// CAUTION: +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions func (engine *Engine) Update(bean interface{}, condiBeans ...interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.Update(bean, condiBeans...) } +// Delete records, bean's non-empty fields are conditions func (engine *Engine) Delete(bean interface{}) (int64, error) { session := engine.NewSession() defer session.Close() return session.Delete(bean) } -// Get retrieve one record from table +// Get retrieve one record from table, bean's non-empty fields +// are conditions func (engine *Engine) Get(bean interface{}) (bool, error) { session := engine.NewSession() defer session.Close() return session.Get(bean) } +// Find retrieve records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct func (engine *Engine) Find(beans interface{}, condiBeans ...interface{}) error { session := engine.NewSession() defer session.Close() return session.Find(beans, condiBeans...) } +// Iterate record by record handle records from table, bean's non-empty fields +// are conditions. func (engine *Engine) Iterate(bean interface{}, fun IterFunc) error { session := engine.NewSession() defer session.Close() return session.Iterate(bean, fun) } +// Count counts the records. bean's non-empty fields +// are conditions. func (engine *Engine) Count(bean interface{}) (int64, error) { session := engine.NewSession() defer session.Close() diff --git a/filter.go b/filter.go index dcf1aac9..5fff4c0d 100644 --- a/filter.go +++ b/filter.go @@ -5,10 +5,12 @@ import ( "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 { } @@ -25,6 +27,7 @@ func (s *PgSeqFilter) Do(sql string, session *Session) string { return res } +// QuoteFilter filter SQL replace ` to database's own quote character type QuoteFilter struct { } @@ -32,6 +35,7 @@ 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 { } diff --git a/helpers.go b/helpers.go index 383c66b5..307353c2 100644 --- a/helpers.go +++ b/helpers.go @@ -44,3 +44,20 @@ func structName(v reflect.Type) string { } return v.Name() } + +func sliceEq(left, right []string) bool { + for _, l := range left { + var find bool + for _, r := range right { + if l == r { + find = true + break + } + } + if !find { + return false + } + } + + return true +} diff --git a/mapper.go b/mapper.go index ff12e521..9bcb544b 100644 --- a/mapper.go +++ b/mapper.go @@ -11,6 +11,8 @@ type IMapper interface { Table2Obj(string) string } +// SameMapper implements IMapper and provides same name between struct and +// database table type SameMapper struct { } @@ -22,6 +24,8 @@ func (m SameMapper) Table2Obj(t string) string { return t } +// SnakeMapper implements IMapper and provides name transaltion between +// struct and database table type SnakeMapper struct { } diff --git a/postgres.go b/postgres.go index c02cdb0c..0349e073 100644 --- a/postgres.go +++ b/postgres.go @@ -23,10 +23,8 @@ func (vs values) Get(k string) (v string) { return vs[k] } -type Error error - func errorf(s string, args ...interface{}) { - panic(Error(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...)))) + panic(fmt.Errorf("pq: %s", fmt.Sprintf(s, args...))) } func parseOpts(name string, o values) { diff --git a/session.go b/session.go index e6ed808d..5d13af68 100644 --- a/session.go +++ b/session.go @@ -94,16 +94,25 @@ func (session *Session) Cols(columns ...string) *Session { return session } +// Xorm automatically retrieve condition according struct, but +// if struct has bool field, it will ignore them. So use UseBool +// to tell system to do not ignore them. +// If no paramters, it will use all the bool field of struct, or +// it will use paramters's columns func (session *Session) UseBool(columns ...string) *Session { session.Statement.UseBool(columns...) return session } +// use for distinct columns. Caution: when you are using cache, +// distinct will not be cached because cache system need id, +// but distinct will not provide id func (session *Session) Distinct(columns ...string) *Session { session.Statement.Distinct(columns...) return session } +// Only not use the paramters as select or update columns func (session *Session) Omit(columns ...string) *Session { session.Statement.Omit(columns...) return session @@ -163,7 +172,7 @@ func (session *Session) Charset(charset string) *Session { return session } -// Method Cascade +// Method Cascade indicates if loading sub Struct func (session *Session) Cascade(trueOrFalse ...bool) *Session { if len(trueOrFalse) >= 1 { session.Statement.UseCascade = trueOrFalse[0] @@ -184,11 +193,13 @@ func (session *Session) Join(join_operator, tablename, condition string) *Sessio return session } +// Generate Group By statement func (session *Session) GroupBy(keys string) *Session { session.Statement.GroupBy(keys) return session } +// Generate Having statement func (session *Session) Having(conditions string) *Session { session.Statement.Having(conditions) return session @@ -205,6 +216,7 @@ func (session *Session) newDb() error { return nil } +// Begin a transaction func (session *Session) Begin() error { err := session.newDb() if err != nil { @@ -224,6 +236,7 @@ func (session *Session) Begin() error { return nil } +// When using transaction, you can rollback if any error func (session *Session) Rollback() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.Engine.LogSQL("ROLL BACK") @@ -233,6 +246,7 @@ func (session *Session) Rollback() error { return nil } +// When using transaction, Commit will commit all operations. func (session *Session) Commit() error { if !session.IsAutoCommit && !session.IsCommitedOrRollbacked { session.Engine.LogSQL("COMMIT") @@ -314,6 +328,7 @@ func (session *Session) exec(sql string, args ...interface{}) (sql.Result, error return session.Tx.Exec(sql, args...) } +// Exec raw sql func (session *Session) Exec(sql string, args ...interface{}) (sql.Result, error) { err := session.newDb() if err != nil { @@ -343,6 +358,7 @@ func (session *Session) CreateTable(bean interface{}) error { return session.createOneTable() } +// create indexes func (session *Session) CreateIndexes(bean interface{}) error { session.Statement.RefTable = session.Engine.autoMap(bean) @@ -365,6 +381,7 @@ func (session *Session) CreateIndexes(bean interface{}) error { return nil } +// create uniques func (session *Session) CreateUniques(bean interface{}) error { session.Statement.RefTable = session.Engine.autoMap(bean) @@ -393,7 +410,8 @@ func (session *Session) createOneTable() error { return err } -func (session *Session) CreateAll() error { +// to be deleted +func (session *Session) createAll() error { err := session.newDb() if err != nil { return err @@ -413,6 +431,7 @@ func (session *Session) CreateAll() error { return nil } +// drop indexes func (session *Session) DropIndexes(bean interface{}) error { err := session.newDb() if err != nil { @@ -700,8 +719,12 @@ func (session *Session) cacheFind(t reflect.Type, sql string, rowsSlicePtr inter return nil } +// IterFunc only use by Iterate type IterFunc func(idx int, bean interface{}) error +// Iterate record by record handle records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct func (session *Session) Iterate(bean interface{}, fun IterFunc) error { err := session.newDb() if err != nil { @@ -765,7 +788,8 @@ func (session *Session) Iterate(bean interface{}, fun IterFunc) error { return nil } -// get retrieve one record from database +// get retrieve one record from database, bean's non-empty fields +// will be as conditions func (session *Session) Get(bean interface{}) (bool, error) { err := session.newDb() if err != nil { @@ -814,6 +838,8 @@ func (session *Session) Get(bean interface{}) (bool, error) { } } +// Count counts the records. bean's non-empty fields +// are conditions. func (session *Session) Count(bean interface{}) (int64, error) { err := session.newDb() if err != nil { @@ -851,6 +877,9 @@ func (session *Session) Count(bean interface{}) (int64, error) { return int64(total), err } +// Find retrieve records from table, condiBeans's non-empty fields +// are conditions. beans could be []Struct, []*Struct, map[int64]Struct +// map[int64]*Struct func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error { err := session.newDb() if err != nil { @@ -959,7 +988,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) return nil } -// test if database is ok +// Test if database is ok func (session *Session) Ping() error { err := session.newDb() if err != nil { @@ -1021,23 +1050,6 @@ func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bo return len(results) > 0, err } -func sliceEq(left, right []string) bool { - for _, l := range left { - var find bool - for _, r := range right { - if l == r { - find = true - break - } - } - if !find { - return false - } - } - - return true -} - // find if index is exist according cols func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) { indexes, err := session.Engine.dialect.GetIndexes(tableName) @@ -1106,7 +1118,8 @@ func (session *Session) addUnique(tableName, uqeName string) error { return err } -func (session *Session) DropAll() error { +// To be deleted +func (session *Session) dropAll() error { err := session.newDb() if err != nil { return err @@ -1240,6 +1253,7 @@ func (session *Session) query(sql string, paramStr ...interface{}) (resultsSlice return query(session.Db, sql, paramStr...) } +// Exec a raw sql and return records as []map[string][]byte func (session *Session) Query(sql string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) { err = session.newDb() if err != nil { @@ -1399,6 +1413,7 @@ func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error return res.RowsAffected() } +// Insert multiple records func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) { err := session.newDb() if err != nil { @@ -1914,6 +1929,12 @@ func (session *Session) cacheUpdate(sql string, args ...interface{}) error { return nil } +// Update records, bean's non-empty fields are updated contents, +// condiBean' non-empty filds are conditions +// CAUTION: +// 1.bool will defaultly be updated content nor conditions +// You should call UseBool if you have bool to use. +// 2.float32 & float64 may be not inexact as conditions func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) { err := session.newDb() if err != nil { @@ -2068,6 +2089,7 @@ func (session *Session) cacheDelete(sql string, args ...interface{}) error { return nil } +// Delete records, bean's non-empty fields are conditions func (session *Session) Delete(bean interface{}) (int64, error) { err := session.newDb() if err != nil { diff --git a/statement.go b/statement.go index 10a8492d..4dd66084 100644 --- a/statement.go +++ b/statement.go @@ -9,6 +9,7 @@ import ( "time" ) +// statement save all the sql info for executing SQL type Statement struct { RefTable *Table Engine *Engine @@ -39,6 +40,7 @@ type Statement struct { boolColumnMap map[string]bool } +// init func (statement *Statement) Init() { statement.RefTable = nil statement.Start = 0 @@ -65,41 +67,51 @@ func (statement *Statement) Init() { statement.boolColumnMap = make(map[string]bool) } -func (statement *Statement) Sql(querystring string, args ...interface{}) { +// add the raw sql statement +func (statement *Statement) Sql(querystring string, args ...interface{}) *Statement { statement.RawSQL = querystring statement.RawParams = args + return statement } -func (statement *Statement) Where(querystring string, args ...interface{}) { +// add Where statment +func (statement *Statement) Where(querystring string, args ...interface{}) *Statement { statement.WhereStr = querystring statement.Params = args + return statement } -func (statement *Statement) And(querystring string, args ...interface{}) { +// add Where & and statment +func (statement *Statement) And(querystring string, args ...interface{}) *Statement { if statement.WhereStr != "" { statement.WhereStr = fmt.Sprintf("(%v) AND (%v)", statement.WhereStr, querystring) } else { statement.WhereStr = querystring } statement.Params = append(statement.Params, args...) + return statement } -func (statement *Statement) Or(querystring string, args ...interface{}) { +// add Where & Or statment +func (statement *Statement) Or(querystring string, args ...interface{}) *Statement { if statement.WhereStr != "" { statement.WhereStr = fmt.Sprintf("(%v) OR (%v)", statement.WhereStr, querystring) } else { statement.WhereStr = querystring } statement.Params = append(statement.Params, args...) + return statement } -func (statement *Statement) Table(tableNameOrBean interface{}) { +// tempororily set table name +func (statement *Statement) Table(tableNameOrBean interface{}) *Statement { t := rType(tableNameOrBean) if t.Kind() == reflect.String { statement.AltTableName = tableNameOrBean.(string) } else if t.Kind() == reflect.Struct { statement.RefTable = statement.Engine.autoMapType(t) } + return statement } // Auto generating conditions according a struct @@ -225,6 +237,7 @@ func buildConditions(engine *Engine, table *Table, bean interface{}, includeVers return colNames, args } +// return current tableName func (statement *Statement) TableName() string { if statement.AltTableName != "" { return statement.AltTableName @@ -236,7 +249,8 @@ func (statement *Statement) TableName() string { return "" } -func (statement *Statement) Id(id int64) { +// Generate "Where id = ? " statment +func (statement *Statement) Id(id int64) *Statement { if statement.WhereStr == "" { statement.WhereStr = "(id)=?" statement.Params = []interface{}{id} @@ -244,9 +258,11 @@ func (statement *Statement) Id(id int64) { statement.WhereStr = statement.WhereStr + " AND (id)=?" statement.Params = append(statement.Params, id) } + return statement } -func (statement *Statement) In(column string, args ...interface{}) { +// Generate "Where column IN (?) " statment +func (statement *Statement) In(column string, args ...interface{}) *Statement { inStr := fmt.Sprintf("%v IN (%v)", column, strings.Join(makeArray("?", len(args)), ",")) if statement.WhereStr == "" { statement.WhereStr = inStr @@ -255,6 +271,7 @@ func (statement *Statement) In(column string, args ...interface{}) { statement.WhereStr = statement.WhereStr + " AND " + inStr statement.Params = append(statement.Params, args...) } + return statement } func col2NewCols(columns ...string) []string { @@ -270,20 +287,25 @@ func col2NewCols(columns ...string) []string { return newColumns } -func (statement *Statement) Distinct(columns ...string) { +// Generate "Distince col1, col2 " statment +func (statement *Statement) Distinct(columns ...string) *Statement { statement.IsDistinct = true statement.Cols(columns...) + return statement } -func (statement *Statement) Cols(columns ...string) { +// Generate "col1, col2" statement +func (statement *Statement) Cols(columns ...string) *Statement { newColumns := col2NewCols(columns...) for _, nc := range newColumns { statement.columnMap[nc] = true } statement.ColumnStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) + return statement } -func (statement *Statement) UseBool(columns ...string) { +// indicates that use bool fields as update contents and query contiditions +func (statement *Statement) UseBool(columns ...string) *Statement { if len(columns) > 0 { newColumns := col2NewCols(columns...) for _, nc := range newColumns { @@ -292,8 +314,10 @@ func (statement *Statement) UseBool(columns ...string) { } else { statement.allUseBool = true } + return statement } +// do not use the columns func (statement *Statement) Omit(columns ...string) { newColumns := col2NewCols(columns...) for _, nc := range newColumns { @@ -302,37 +326,47 @@ func (statement *Statement) Omit(columns ...string) { statement.OmitStr = statement.Engine.Quote(strings.Join(newColumns, statement.Engine.Quote(", "))) } +// Generate LIMIT limit statement func (statement *Statement) Top(limit int) *Statement { statement.Limit(limit) return statement } -func (statement *Statement) Limit(limit int, start ...int) { +// Generate LIMIT start, limit statement +func (statement *Statement) Limit(limit int, start ...int) *Statement { statement.LimitN = limit if len(start) > 0 { statement.Start = start[0] } + return statement } -func (statement *Statement) OrderBy(order string) { +// Generate "Order By order" statement +func (statement *Statement) OrderBy(order string) *Statement { statement.OrderStr = order + return statement } //The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN -func (statement *Statement) Join(join_operator, tablename, condition string) { +func (statement *Statement) Join(join_operator, tablename, condition string) *Statement { if statement.JoinStr != "" { statement.JoinStr = statement.JoinStr + fmt.Sprintf("%v JOIN %v ON %v", join_operator, tablename, condition) } else { statement.JoinStr = fmt.Sprintf("%v JOIN %v ON %v", join_operator, tablename, condition) } + return statement } -func (statement *Statement) GroupBy(keys string) { +// Generate "Group By keys" statement +func (statement *Statement) GroupBy(keys string) *Statement { statement.GroupByStr = keys + return statement } -func (statement *Statement) Having(conditions string) { +// Generate "Having conditions" statement +func (statement *Statement) Having(conditions string) *Statement { statement.HavingStr = fmt.Sprintf("HAVING %v", conditions) + return statement } func (statement *Statement) genColumnStr() string { diff --git a/table.go b/table.go index 4bcae651..beee9912 100644 --- a/table.go +++ b/table.go @@ -6,6 +6,7 @@ import ( "time" ) +// xorm SQL types type SQLType struct { Name string DefaultLength int @@ -143,6 +144,7 @@ func Type2SQLType(t reflect.Type) (st SQLType) { return } +// default sql type change to go types func SQLType2Type(st SQLType) reflect.Type { name := strings.ToUpper(st.Name) switch name { @@ -174,18 +176,21 @@ const ( 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)} } @@ -196,6 +201,7 @@ const ( ONLYFROMDB ) +// database column type Column struct { Name string FieldName string @@ -214,6 +220,7 @@ type Column struct { IsVersion bool } +// generate column description string according dialect func (col *Column) String(d dialect) string { sql := d.QuoteStr() + col.Name + d.QuoteStr() + " " @@ -240,6 +247,7 @@ func (col *Column) String(d dialect) string { 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, ".") { @@ -256,6 +264,7 @@ func (col *Column) ValueOf(bean interface{}) reflect.Value { return fieldValue } +// database table type Table struct { Name string Type reflect.Type @@ -269,10 +278,12 @@ type Table struct { Cacher Cacher } +// if has primary key, return column func (table *Table) PKColumn() *Column { return table.Columns[table.PrimaryKey] } +// add a column to table func (table *Table) AddColumn(col *Column) { table.ColumnsSeq = append(table.ColumnsSeq, col.Name) table.Columns[col.Name] = col @@ -290,6 +301,7 @@ func (table *Table) AddColumn(col *Column) { } } +// add an index or an unique to table func (table *Table) AddIndex(index *Index) { table.Indexes[index.Name] = index } @@ -343,6 +355,8 @@ 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)