| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 | // Copyright 2016 The Xorm Authors. All rights reserved.// Use of this source code is governed by a BSD-style// license that can be found in the LICENSE file.package xormimport (	"database/sql"	"errors"	"fmt"	"reflect"	"strings"	"github.com/go-xorm/core")// Ping test if database is okfunc (session *Session) Ping() error {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	return session.DB().Ping()}// CreateTable create a table according a beanfunc (session *Session) CreateTable(bean interface{}) error {	v := rValue(bean)	session.Statement.setRefValue(v)	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	return session.createOneTable()}// CreateIndexes create indexesfunc (session *Session) CreateIndexes(bean interface{}) error {	v := rValue(bean)	session.Statement.setRefValue(v)	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	sqls := session.Statement.genIndexSQL()	for _, sqlStr := range sqls {		_, err := session.exec(sqlStr)		if err != nil {			return err		}	}	return nil}// CreateUniques create uniquesfunc (session *Session) CreateUniques(bean interface{}) error {	v := rValue(bean)	session.Statement.setRefValue(v)	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	sqls := session.Statement.genUniqueSQL()	for _, sqlStr := range sqls {		_, err := session.exec(sqlStr)		if err != nil {			return err		}	}	return nil}func (session *Session) createOneTable() error {	sqlStr := session.Statement.genCreateTableSQL()	_, err := session.exec(sqlStr)	return err}// to be deletedfunc (session *Session) createAll() error {	if session.IsAutoClose {		defer session.Close()	}	for _, table := range session.Engine.Tables {		session.Statement.RefTable = table		session.Statement.tableName = table.Name		err := session.createOneTable()		session.resetStatement()		if err != nil {			return err		}	}	return nil}// DropIndexes drop indexesfunc (session *Session) DropIndexes(bean interface{}) error {	v := rValue(bean)	session.Statement.setRefValue(v)	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	sqls := session.Statement.genDelIndexSQL()	for _, sqlStr := range sqls {		_, err := session.exec(sqlStr)		if err != nil {			return err		}	}	return nil}// DropTable drop table will drop table if exist, if drop failed, it will return errorfunc (session *Session) DropTable(beanOrTableName interface{}) error {	tableName, err := session.Engine.tableName(beanOrTableName)	if err != nil {		return err	}	var needDrop = true	if !session.Engine.dialect.SupportDropIfExists() {		sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)		results, err := session.query(sqlStr, args...)		if err != nil {			return err		}		needDrop = len(results) > 0	}	if needDrop {		sqlStr := session.Engine.Dialect().DropTableSql(tableName)		_, err = session.exec(sqlStr)		return err	}	return nil}// IsTableExist if a table is existfunc (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) {	tableName, err := session.Engine.tableName(beanOrTableName)	if err != nil {		return false, err	}	return session.isTableExist(tableName)}func (session *Session) isTableExist(tableName string) (bool, error) {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)	results, err := session.query(sqlStr, args...)	return len(results) > 0, err}// IsTableEmpty if table have any recordsfunc (session *Session) IsTableEmpty(bean interface{}) (bool, error) {	v := rValue(bean)	t := v.Type()	if t.Kind() == reflect.String {		return session.isTableEmpty(bean.(string))	} else if t.Kind() == reflect.Struct {		rows, err := session.Count(bean)		return rows == 0, err	}	return false, errors.New("bean should be a struct or struct's point")}func (session *Session) isTableEmpty(tableName string) (bool, error) {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	var total int64	sqlStr := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName))	err := session.DB().QueryRow(sqlStr).Scan(&total)	session.saveLastSQL(sqlStr)	if err != nil {		if err == sql.ErrNoRows {			err = nil		}		return true, err	}	return total == 0, nil}func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	var idx string	if unique {		idx = uniqueName(tableName, idxName)	} else {		idx = indexName(tableName, idxName)	}	sqlStr, args := session.Engine.dialect.IndexCheckSql(tableName, idx)	results, err := session.query(sqlStr, args...)	return len(results) > 0, err}// find if index is exist according colsfunc (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	indexes, err := session.Engine.dialect.GetIndexes(tableName)	if err != nil {		return false, err	}	for _, index := range indexes {		if sliceEq(index.Cols, cols) {			if unique {				return index.Type == core.UniqueType, nil			}			return index.Type == core.IndexType, nil		}	}	return false, nil}func (session *Session) addColumn(colName string) error {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	col := session.Statement.RefTable.GetColumn(colName)	sql, args := session.Statement.genAddColumnStr(col)	_, err := session.exec(sql, args...)	return err}func (session *Session) addIndex(tableName, idxName string) error {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	index := session.Statement.RefTable.Indexes[idxName]	sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)	_, err := session.exec(sqlStr)	return err}func (session *Session) addUnique(tableName, uqeName string) error {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	index := session.Statement.RefTable.Indexes[uqeName]	sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)	_, err := session.exec(sqlStr)	return err}// To be deletedfunc (session *Session) dropAll() error {	defer session.resetStatement()	if session.IsAutoClose {		defer session.Close()	}	for _, table := range session.Engine.Tables {		session.Statement.Init()		session.Statement.RefTable = table		sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName())		_, err := session.exec(sqlStr)		if err != nil {			return err		}	}	return nil}// Sync2 synchronize structs to database tablesfunc (session *Session) Sync2(beans ...interface{}) error {	engine := session.Engine	tables, err := engine.DBMetas()	if err != nil {		return err	}	var structTables []*core.Table	for _, bean := range beans {		v := rValue(bean)		table := engine.mapType(v)		structTables = append(structTables, table)		var tbName = session.tbNameNoSchema(table)		var oriTable *core.Table		for _, tb := range tables {			if strings.EqualFold(tb.Name, tbName) {				oriTable = tb				break			}		}		if oriTable == nil {			err = session.StoreEngine(session.Statement.StoreEngine).CreateTable(bean)			if err != nil {				return err			}			err = session.CreateUniques(bean)			if err != nil {				return err			}			err = session.CreateIndexes(bean)			if err != nil {				return err			}		} else {			for _, col := range table.Columns() {				var oriCol *core.Column				for _, col2 := range oriTable.Columns() {					if strings.EqualFold(col.Name, col2.Name) {						oriCol = col2						break					}				}				if oriCol != nil {					expectedType := engine.dialect.SqlType(col)					curType := engine.dialect.SqlType(oriCol)					if expectedType != curType {						if expectedType == core.Text &&							strings.HasPrefix(curType, core.Varchar) {							// currently only support mysql & postgres							if engine.dialect.DBType() == core.MYSQL ||								engine.dialect.DBType() == core.POSTGRES {								engine.logger.Infof("Table %s column %s change type from %s to %s\n",									tbName, col.Name, curType, expectedType)								_, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))							} else {								engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n",									tbName, col.Name, curType, expectedType)							}						} else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) {							if engine.dialect.DBType() == core.MYSQL {								if oriCol.Length < col.Length {									engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",										tbName, col.Name, oriCol.Length, col.Length)									_, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))								}							}						} else {							if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') {								engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",									tbName, col.Name, curType, expectedType)							}						}					} else if expectedType == core.Varchar {						if engine.dialect.DBType() == core.MYSQL {							if oriCol.Length < col.Length {								engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",									tbName, col.Name, oriCol.Length, col.Length)								_, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))							}						}					}					if col.Default != oriCol.Default {						engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s",							tbName, col.Name, oriCol.Default, col.Default)					}					if col.Nullable != oriCol.Nullable {						engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v",							tbName, col.Name, oriCol.Nullable, col.Nullable)					}				} else {					session := engine.NewSession()					session.Statement.RefTable = table					session.Statement.tableName = tbName					defer session.Close()					err = session.addColumn(col.Name)				}				if err != nil {					return err				}			}			var foundIndexNames = make(map[string]bool)			var addedNames = make(map[string]*core.Index)			for name, index := range table.Indexes {				var oriIndex *core.Index				for name2, index2 := range oriTable.Indexes {					if index.Equal(index2) {						oriIndex = index2						foundIndexNames[name2] = true						break					}				}				if oriIndex != nil {					if oriIndex.Type != index.Type {						sql := engine.dialect.DropIndexSql(tbName, oriIndex)						_, err = engine.Exec(sql)						if err != nil {							return err						}						oriIndex = nil					}				}				if oriIndex == nil {					addedNames[name] = index				}			}			for name2, index2 := range oriTable.Indexes {				if _, ok := foundIndexNames[name2]; !ok {					sql := engine.dialect.DropIndexSql(tbName, index2)					_, err = engine.Exec(sql)					if err != nil {						return err					}				}			}			for name, index := range addedNames {				if index.Type == core.UniqueType {					session := engine.NewSession()					session.Statement.RefTable = table					session.Statement.tableName = tbName					defer session.Close()					err = session.addUnique(tbName, name)				} else if index.Type == core.IndexType {					session := engine.NewSession()					session.Statement.RefTable = table					session.Statement.tableName = tbName					defer session.Close()					err = session.addIndex(tbName, name)				}				if err != nil {					return err				}			}		}	}	for _, table := range tables {		var oriTable *core.Table		for _, structTable := range structTables {			if strings.EqualFold(table.Name, session.tbNameNoSchema(structTable)) {				oriTable = structTable				break			}		}		if oriTable == nil {			//engine.LogWarnf("Table %s has no struct to mapping it", table.Name)			continue		}		for _, colName := range table.ColumnsSeq() {			if oriTable.GetColumn(colName) == nil {				engine.logger.Warnf("Table %s has column %s but struct has not related field", table.Name, colName)			}		}	}	return nil}
 |