add apaas code
This commit is contained in:
		
							parent
							
								
									e5b867e785
								
							
						
					
					
						commit
						820dc0aa89
					
				
							
								
								
									
										92
									
								
								apaas/checker.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								apaas/checker.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Checker interface { | ||||||
|  | 	Check(string) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ExtraCheck(rule map[string]*ExtraFieldMeta, extra string) error { | ||||||
|  | 	var data map[string]any | ||||||
|  | 	err := json.Unmarshal([]byte(extra), &data) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	for k, v := range data { | ||||||
|  | 		r, ok := rule[k] | ||||||
|  | 		if !ok || r == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		err = FieldCheck(r, v) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return GenError(err.Error()) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func FieldCheck(rule *ExtraFieldMeta, value any) error { | ||||||
|  | 	if rule == nil || value == nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	var err error | ||||||
|  | 	switch val := value.(type) { | ||||||
|  | 	case map[string]any: | ||||||
|  | 		if len(rule.ObjectMeta) == 0 { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 		for k, v := range val { | ||||||
|  | 			r, ok := rule.ObjectMeta[k] | ||||||
|  | 			if !ok || r == nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			err = FieldCheck(r, v) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return fmt.Errorf("rule(key=%s, type=%s), value(type=map[%s], suberror=%s)", | ||||||
|  | 					rule.Key, FieldMapString[rule.Type], k, err.Error()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	case []any: | ||||||
|  | 		if rule.Type != FieldArray { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type=array, value=%#v)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type], val) | ||||||
|  | 		} | ||||||
|  | 		if len(rule.ArrayMeta) != 0 { | ||||||
|  | 			for i, v := range val { | ||||||
|  | 				err = FieldCheck(rule.ArrayMeta[i], v) | ||||||
|  | 				if err != nil { | ||||||
|  | 					return fmt.Errorf("rule(key=%s, type=%s), value(type array, [%d] element suberror=%s)", | ||||||
|  | 						rule.Key, FieldMapString[rule.Type], i, err.Error()) | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	case string: | ||||||
|  | 		if rule.Type != FieldString { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type=string, value=%#v)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type], val) | ||||||
|  | 		} | ||||||
|  | 	case float64: | ||||||
|  | 		if rule.Type != FieldInt && rule.Type != FieldFloat64 { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type=float/int, value=%#v)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type], val) | ||||||
|  | 		} | ||||||
|  | 		if rule.Type == FieldInt && float64(int(val)) != val { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type=float, value=%#v)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type], val) | ||||||
|  | 		} | ||||||
|  | 	case bool: | ||||||
|  | 		if rule.Type != FieldBool { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type=bool, value=%#v)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type]) | ||||||
|  | 		} | ||||||
|  | 	case nil: | ||||||
|  | 		if rule.Type != FieldArray || rule.Type != FieldObject { | ||||||
|  | 			return fmt.Errorf("rule(key=%s, type=%s) dismatch value(type nil)", | ||||||
|  | 				rule.Key, FieldMapString[rule.Type]) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										53
									
								
								apaas/collection.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								apaas/collection.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,53 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var gDBCol atomic.Value | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	dbCol := &DBCollection{ | ||||||
|  | 		dbs: make(map[string]*DBMeta, 128), | ||||||
|  | 	} | ||||||
|  | 	SetDBCol(dbCol) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func SetDBCol(dbCol *DBCollection) { | ||||||
|  | 	if dbCol != nil { | ||||||
|  | 		gDBCol.Store(dbCol) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetDBCol() *DBCollection { | ||||||
|  | 	dbCol, ok := gDBCol.Load().(*DBCollection) | ||||||
|  | 	if ok { | ||||||
|  | 		return dbCol | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type DBCollection struct { | ||||||
|  | 	dbs  map[string]*DBMeta | ||||||
|  | 	lock sync.RWMutex | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *DBCollection) GetDB(dbName string) (*DBMeta, bool) { | ||||||
|  | 	p.lock.RLock() | ||||||
|  | 	v, ok := p.dbs[dbName] | ||||||
|  | 	p.lock.RUnlock() | ||||||
|  | 	return v, ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *DBCollection) SetDB(dbName string, dbMeta *DBMeta) { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	p.dbs[dbName] = dbMeta | ||||||
|  | 	p.lock.Unlock() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *DBCollection) DeleteDB(dbName string) { | ||||||
|  | 	p.lock.Lock() | ||||||
|  | 	delete(p.dbs, dbName) | ||||||
|  | 	p.lock.Unlock() | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								apaas/dailer.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								apaas/dailer.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | package apaas | ||||||
							
								
								
									
										9
									
								
								apaas/error.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								apaas/error.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import "fmt" | ||||||
|  | 
 | ||||||
|  | const MSG_PREFIX = "[apaas_engine]" | ||||||
|  | 
 | ||||||
|  | func GenError(msg string) error { | ||||||
|  | 	return fmt.Errorf("%s %s", MSG_PREFIX, msg) | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								apaas/fetcher.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								apaas/fetcher.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,94 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 
 | ||||||
|  | 	"gorm.io/gorm/logger" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type DBFetcher interface { | ||||||
|  | 	Fetch() ([]*ApaasTable, error) | ||||||
|  | 	// each fetcher has only uniq name, Name is must DBName
 | ||||||
|  | 	// if DBName is nil, default fetch all
 | ||||||
|  | 	DBName() string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | var gAllFetcher = map[string]DBFetcher{} | ||||||
|  | var gDeltaFetcher = map[string]DBFetcher{} | ||||||
|  | 
 | ||||||
|  | func AddDBFetcher(f DBFetcher) { | ||||||
|  | 	gAllFetcher[f.DBName()] = f | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Update All DB metas, called by apaas_db_engine SDK
 | ||||||
|  | func UpdateAllDBCol() { | ||||||
|  | 	for name, f := range gAllFetcher { | ||||||
|  | 		if f.DBName() == "" { | ||||||
|  | 			tables, err := f.Fetch() | ||||||
|  | 			if err != nil { | ||||||
|  | 				logger.Default.Error(context.Background(), "%s fetcher(name=%s, DBName=%s) Fetch data error=%s", MSG_PREFIX, name, f.DBName(), err.Error()) | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			logger.Default.Info(context.Background(), "%s fetcher(name=%s, DBName=%s) Fetch data len=%d", MSG_PREFIX, name, f.DBName(), len(tables)) | ||||||
|  | 
 | ||||||
|  | 			dbCol := &DBCollection{ | ||||||
|  | 				dbs: make(map[string]*DBMeta, 128), | ||||||
|  | 			} | ||||||
|  | 			for _, table := range tables { | ||||||
|  | 				v, ok := dbCol.dbs[table.DBName] | ||||||
|  | 				if !ok { | ||||||
|  | 					v := &DBMeta{ | ||||||
|  | 						tableList:    make([]*ApaasTable, 0, len(tables)>>2), | ||||||
|  | 						tableView:    make(map[string]*ApaasTable, len(tables)>>2), | ||||||
|  | 						lookupIDView: make(map[string]*ApaasTable, len(tables)>>2), | ||||||
|  | 					} | ||||||
|  | 					dbCol.dbs[table.DBName] = v | ||||||
|  | 				} | ||||||
|  | 				v.tableList = append(v.tableList, table) | ||||||
|  | 				v.tableView[table.TableName] = table | ||||||
|  | 				if table.LookupIDField != nil { | ||||||
|  | 					v.lookupIDView[table.LookupIDField.Name] = table | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			SetDBCol(dbCol) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	for name, f := range gAllFetcher { | ||||||
|  | 		if f.DBName() == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		tables, err := f.Fetch() | ||||||
|  | 		if err != nil { | ||||||
|  | 			logger.Default.Error(context.Background(), "%s fetcher(name=%s, DBName=%s) Fetch data error=%s", MSG_PREFIX, name, f.DBName(), err.Error()) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		logger.Default.Info(context.Background(), "%s fetcher(name=%s, DBName=%s) Fetch data len=%d", MSG_PREFIX, name, f.DBName(), len(tables)) | ||||||
|  | 
 | ||||||
|  | 		if len(tables) == 0 { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if len(f.DBName()) != 0 { | ||||||
|  | 			dbMeta := &DBMeta{ | ||||||
|  | 				tableList: tables, | ||||||
|  | 			} | ||||||
|  | 			dbMeta.tableView = make(map[string]*ApaasTable, len(tables)) | ||||||
|  | 			dbMeta.lookupIDView = make(map[string]*ApaasTable, len(tables)) | ||||||
|  | 			for _, table := range tables { | ||||||
|  | 				dbMeta.tableView[table.TableName] = table | ||||||
|  | 				if table.LookupIDField != nil { | ||||||
|  | 					dbMeta.lookupIDView[table.LookupIDField.Name] = table | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			GetDBCol().SetDB(f.DBName(), dbMeta) | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | // Update DB metas, called by apaas_db_engine SDK
 | ||||||
|  | func UpdateDeltaDBCol() { | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | */ | ||||||
							
								
								
									
										29
									
								
								apaas/idl.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								apaas/idl.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | type ApaasQueryType int8 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	_skipQueryType ApaasQueryType = iota | ||||||
|  | 	SelectType | ||||||
|  | 	CreateType | ||||||
|  | 	InsertType | ||||||
|  | 	UpdateType | ||||||
|  | 	DeleteType | ||||||
|  | 	RawType | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ApaasQueryArgs struct { | ||||||
|  | 	Select    any | ||||||
|  | 	From      any | ||||||
|  | 	Where     any | ||||||
|  | 	Join      any | ||||||
|  | 	Group     any | ||||||
|  | 	Order     any | ||||||
|  | 	Limit     any | ||||||
|  | 	Offset    any | ||||||
|  | 	Update    any | ||||||
|  | 	Delete    any | ||||||
|  | 	create    any | ||||||
|  | 	RawSql    string | ||||||
|  | 	QueryType ApaasQueryType | ||||||
|  | } | ||||||
							
								
								
									
										150
									
								
								apaas/reflect.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										150
									
								
								apaas/reflect.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,150 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var _unknown_field = reflect.StructField{} | ||||||
|  | 
 | ||||||
|  | func GetFieldTypeByColumnNameV2(value reflect.Value, fieldColumnName string) (reflect.StructField, bool) { | ||||||
|  | 	// check obj is pointer or not
 | ||||||
|  | 	if value.Kind() == reflect.Ptr { | ||||||
|  | 		value = value.Elem() | ||||||
|  | 	} | ||||||
|  | 	typ := value.Type() | ||||||
|  | 	for i := 0; i < value.NumField(); i++ { | ||||||
|  | 		field := typ.Field(i) | ||||||
|  | 		fieldValue := value.Field(i) | ||||||
|  | 		if field.Anonymous { | ||||||
|  | 			return GetFieldTypeByColumnNameV2(fieldValue, fieldColumnName) | ||||||
|  | 		} | ||||||
|  | 		tag := field.Tag.Get("gorm") | ||||||
|  | 		if tag == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if cname, ok := getColumnNameByColumnTag(tag); ok && cname == fieldColumnName { | ||||||
|  | 			return field, ok | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return _unknown_field, false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | case: | ||||||
|  | 
 | ||||||
|  | 	type Faction struct { | ||||||
|  | 			ID          int64  `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` | ||||||
|  | 			LiveID      int64  `gorm:"column:live_id" json:"live_id"` | ||||||
|  | 			BizID       int64  `gorm:"column:biz_id" json:"biz_id"` | ||||||
|  | 			FactionID   int64  `gorm:"column:faction_id;not null" json:"faction_id"` | ||||||
|  | 			FactionName string `gorm:"column:faction_name" json:"faction_name"` | ||||||
|  | 			OrgID       int64  `gorm:"column:org_id;comment:union外键" json:"org_id" apass_engine_lookup_id:"webcast.union.org_id"` // union外键
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | fieldColumnName: faction_id | ||||||
|  | */ | ||||||
|  | func GetFieldTypeByColumnName(obj any, fieldColumnName string) (reflect.StructField, bool) { | ||||||
|  | 	return GetFieldTypeByColumnNameV2(reflect.ValueOf(obj), fieldColumnName) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | case: `gorm:"column:id;primaryKey;autoIncrement:true" | ||||||
|  | */ | ||||||
|  | func getColumnNameByColumnTag(tag string) (string, bool) { | ||||||
|  | 	fs := strings.Split(tag, ";") | ||||||
|  | 	if len(fs) >= 1 { | ||||||
|  | 		sfs := strings.Split(fs[0], ":") | ||||||
|  | 		if sfs[0] == "column" { | ||||||
|  | 			return sfs[1], true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return "", false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func GetFieldTypeByColumnNameV3(typ reflect.Type, fieldColumnName string) (reflect.StructField, bool) { | ||||||
|  | 	// check obj is pointer or not
 | ||||||
|  | 	if typ.Kind() == reflect.Ptr { | ||||||
|  | 		typ = typ.Elem() | ||||||
|  | 	} | ||||||
|  | 	for i := 0; i < typ.NumField(); i++ { | ||||||
|  | 		field := typ.Field(i) | ||||||
|  | 		if field.Anonymous { | ||||||
|  | 			return GetFieldTypeByColumnNameV3(field.Type, fieldColumnName) | ||||||
|  | 		} | ||||||
|  | 		tag := field.Tag.Get("gorm") | ||||||
|  | 		if tag == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if cname, ok := getColumnNameByColumnTag(tag); ok && cname == fieldColumnName { | ||||||
|  | 			return field, ok | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return _unknown_field, false | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ParseLookupTagMeta(tag, columnName, dbName, tableName string) (*ApaasLookupMeta, error) { | ||||||
|  | 	fmt.Printf("[apaas_engine] tag: %s, columnName: %s, dbName: %s, tableName: %s\n", tag, columnName, dbName, tableName) | ||||||
|  | 	if tag == "" { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	fs := strings.Split(tag, ".") | ||||||
|  | 	if len(fs) > MaxTagDeep { | ||||||
|  | 		return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, lookup deep=%d>%d", tag, len(fs), MaxTagDeep)) | ||||||
|  | 	} | ||||||
|  | 	meta := &ApaasLookupMeta{ | ||||||
|  | 		CName:      columnName, | ||||||
|  | 		LookupMeta: make([]*LookupMeta, len(fs)-1), | ||||||
|  | 		LastField:  fs[len(fs)-1], | ||||||
|  | 		OrgTag:     fs, | ||||||
|  | 	} | ||||||
|  | 	dbCol := GetDBCol() | ||||||
|  | 	if dbCol == nil { | ||||||
|  | 		return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, cann't get db collection", tag)) | ||||||
|  | 	} | ||||||
|  | 	dbMeta, ok := dbCol.GetDB(dbName) | ||||||
|  | 	if !ok || dbMeta == nil { | ||||||
|  | 		return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, cann't get db(name=%s) meta", tag, dbName)) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tableMeta, ok := dbMeta.tableView[tableName] | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, cann't get table(name=%s) meta", tag, tableName)) | ||||||
|  | 	} | ||||||
|  | 	var idx int = 0 | ||||||
|  | 	for idx < len(fs)-1 { | ||||||
|  | 		lp := &LookupMeta{ | ||||||
|  | 			FieldName: fs[idx], | ||||||
|  | 			ForeignMeta: ForeignMeta{ | ||||||
|  | 				DBName: dbName, | ||||||
|  | 				FName:  fs[idx+1], | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		found := false | ||||||
|  | 		for _, ff := range tableMeta.ForeignFields { | ||||||
|  | 			if ff.Name == fs[idx] { | ||||||
|  | 				if fs[idx+1] != ff.foreignMeta.FName { | ||||||
|  | 					return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, db=%s, table=%s, field=%s, foreign(table=%s), foreign field=%s not equal to lookup field=%s)", tag, dbName, tableName, ff.Name, ff.foreignMeta.TName, fs[idx+1])) | ||||||
|  | 				} | ||||||
|  | 				if dbName != ff.foreignMeta.DBName { | ||||||
|  | 					return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, db=%s, table=%s, field=%s, foreign(table=%s), foreign db=%s not equal to lookup db=%s)", tag, tag, dbName, tableName, ff.Name, ff.foreignMeta.DBName, dbName)) | ||||||
|  | 				} | ||||||
|  | 				lp.ForeignMeta.TName = ff.foreignMeta.TName | ||||||
|  | 				lp.ForeignMeta.FTMeta = ff.foreignMeta.FTMeta | ||||||
|  | 				found = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !found { | ||||||
|  | 			return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, db=%s, table=%s, field=%s is not foreign key", tag, tag, dbName, tableName, fs[idx])) | ||||||
|  | 		} | ||||||
|  | 		meta.LookupMeta[idx] = lp | ||||||
|  | 		tableName = lp.ForeignMeta.TName | ||||||
|  | 		tableMeta, ok = dbMeta.tableView[tableName] | ||||||
|  | 		if !ok { | ||||||
|  | 			return nil, GenError(fmt.Sprintf("apass_engine_lookup_value=%s, cann't get table(name=%s) meta", tag, tableName)) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return meta, nil | ||||||
|  | } | ||||||
							
								
								
									
										235
									
								
								apaas/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								apaas/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,235 @@ | |||||||
|  | package apaas | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	TagID      = "apass_engine_lookup_id" | ||||||
|  | 	TagValue   = "apass_engine_lookup_value" | ||||||
|  | 	MaxTagDeep = 4 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ApaasFieldType uint8 | ||||||
|  | type FieldType uint8 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	_skipFieldType FieldType = iota | ||||||
|  | 	FieldBool | ||||||
|  | 	FieldInt | ||||||
|  | 	FieldInt64 | ||||||
|  | 	FieldFloat | ||||||
|  | 	FieldFloat64 | ||||||
|  | 	FieldString | ||||||
|  | 	FieldTime | ||||||
|  | 	FieldBit | ||||||
|  | 	FieldBin | ||||||
|  | 	FieldArray | ||||||
|  | 	FieldObject | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	_skipApaasFieldType ApaasFieldType = iota | ||||||
|  | 	ApaasLookupID | ||||||
|  | 	ApaasLookupValue | ||||||
|  | 	ApaasExtraType | ||||||
|  | 	ApaasFormulaType | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var FieldMapString = []string{ | ||||||
|  | 	"_skipApaasFieldTyle", | ||||||
|  | 	"FieldBool", | ||||||
|  | 	"FieldInt", | ||||||
|  | 	"FieldInt64", | ||||||
|  | 	"FieldFloat", | ||||||
|  | 	"FieldFloat64", | ||||||
|  | 	"FieldString", | ||||||
|  | 	"FieldTime", | ||||||
|  | 	"FieldBit", | ||||||
|  | 	"FieldBin", | ||||||
|  | 	"FieldArray", | ||||||
|  | 	"FieldObject", | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p FieldType) String() string { | ||||||
|  | 	return FieldMapString[p] | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  | description: store dynamic field info when is an instance like tag | ||||||
|  | 
 | ||||||
|  | 	`apass_engine_lookup_value` | ||||||
|  | 
 | ||||||
|  | case: | ||||||
|  | 
 | ||||||
|  | 	type RoomAnchorView struct { | ||||||
|  | 			Room | ||||||
|  | 			Gender string `gorm:"column:gender" json:"gender" apass_engine_lookup_value:"anchor_id.uid.gender"` | ||||||
|  | 			Career string `gorm:"column:career" json:"career" apass_engine_lookup_value:"anchor_id.uid.career"` | ||||||
|  | 			Name   string `gorm:"column:name" json:"name" apass_engine_lookup_value:"anchor_id.uid.name"` | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		type RoomAnchorFactionView struct { | ||||||
|  | 			Room | ||||||
|  | 			Gender    string `gorm:"column:gender" json:"gender" apass_engine_lookup_value:"anchor_id.uid.gender"` | ||||||
|  | 			Career    string `gorm:"column:career" json:"career" apass_engine_lookup_value:"anchor_id.uid.career"` | ||||||
|  | 			Name      string `gorm:"column:name" json:"name" apass_engine_lookup_value:"anchor_id.uid.name"` | ||||||
|  | 			OrgName   string `gorm:"column:org_name" json:"org_name" apass_engine_lookup_value:"anchor_id.faction_id.faction_name"` | ||||||
|  | 			UnionInfo string `gorm:"column:union_info" json:"union_info" apass_engine_lookup_value:"anchor_id.faction_id.org_id.union_info"` | ||||||
|  | 		} | ||||||
|  | */ | ||||||
|  | type ApaasLookupMeta struct { | ||||||
|  | 	// field gorm column tag name/table field's name. example: union_info
 | ||||||
|  | 	CName string | ||||||
|  | 	// lookup orgin meta. example: [anchor_id.faction_id.org_id.union_info]
 | ||||||
|  | 	/* | ||||||
|  | 		    1. anchor_id;       2. faction_id;        3: org_id; | ||||||
|  | 			1. anchor.anchor_id;2. Vction.faction_id;3: union.org_id; | ||||||
|  | 	*/ | ||||||
|  | 	LookupMeta []*LookupMeta | ||||||
|  | 	LastField  string // org_name/union_name
 | ||||||
|  | 
 | ||||||
|  | 	/* | ||||||
|  | 		OrgTag only set value when used in SDK mode | ||||||
|  | 	*/ | ||||||
|  | 	// lookup org tag [anchor_id, faction_id, org_id, org_name]
 | ||||||
|  | 	OrgTag []string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // lookup orgin meta. example: [anchor_id.faction_id.org_id.union_info]
 | ||||||
|  | type LookupMeta struct { | ||||||
|  | 	FieldName   string | ||||||
|  | 	ForeignMeta ForeignMeta | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ApaasTable struct { | ||||||
|  | 	TableName         string | ||||||
|  | 	DBName            string | ||||||
|  | 	Fields            []*ApaasField | ||||||
|  | 	FieldsByName      map[string]*ApaasField | ||||||
|  | 	LookupIDField     *ApaasField   // example: room.room_id, user.uid, faction.faction_id
 | ||||||
|  | 	ForeignFields     []*ApaasField // foreign key. example: room.anchor_id, named of relookupid
 | ||||||
|  | 	FormulaFields     []*ApaasField // example: union.title=update_time + org_name
 | ||||||
|  | 	LookupValueFields []*ApaasField // example: org_id.org_name, anchor_id.org_id.union_id.union_name
 | ||||||
|  | 	ExtraFields       []*ApaasField // example: anchor.extra, room.extra
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ApaasField struct { | ||||||
|  | 	Name        string | ||||||
|  | 	Type        string | ||||||
|  | 	FType       FieldType | ||||||
|  | 	IsUniq      bool | ||||||
|  | 	IsForeign   bool | ||||||
|  | 	foreignMeta *ForeignMeta | ||||||
|  | 	IsApaasType bool | ||||||
|  | 	ApaasMeta   *ApaasMeta | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ApaasField) parseFieldType() { | ||||||
|  | 	tp := strings.ToUpper(p.Type) | ||||||
|  | 	ftp := _skipFieldType | ||||||
|  | 	switch tp { | ||||||
|  | 	case "BOOL": | ||||||
|  | 		ftp = FieldBool | ||||||
|  | 	case "INT", "TINYINT", "SMALLINT", "MEDIUMINT": | ||||||
|  | 		ftp = FieldInt | ||||||
|  | 	case "BIGINT": | ||||||
|  | 		ftp = FieldInt64 | ||||||
|  | 	case "FLOAT": | ||||||
|  | 		ftp = FieldFloat | ||||||
|  | 	case "DOUBLE", "DECIMAL", "REAL": | ||||||
|  | 		ftp = FieldFloat64 | ||||||
|  | 	case "DATE", "DATETIME", "TIMESTAMP", "TIME", "YEAR": | ||||||
|  | 		ftp = FieldTime | ||||||
|  | 	case "VARCHAR", "CHAR", "ENUM", "TEXT", "TINYTEXT", "MEDIUMTEXT", "LONGTEXT", "JSON": | ||||||
|  | 		ftp = FieldString | ||||||
|  | 	case "BLOB", "TINYBLOB", "MEDIUMBLOB", "LONGBLOB", "BINARY", "VARBINARY": | ||||||
|  | 		ftp = FieldBin | ||||||
|  | 	case "BIT": | ||||||
|  | 		ftp = FieldBit | ||||||
|  | 	default: | ||||||
|  | 		ftp = FieldString | ||||||
|  | 	} | ||||||
|  | 	p.FType = ftp | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ApaasField) GetApaasMeta() *ApaasMeta { | ||||||
|  | 	return p.ApaasMeta | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ForeignMeta struct { | ||||||
|  | 	DBName string | ||||||
|  | 	TName  string | ||||||
|  | 	FName  string | ||||||
|  | 	FTMeta *ApaasTable | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ApaasMeta struct { | ||||||
|  | 	ApaasFType  ApaasFieldType | ||||||
|  | 	ExtraMeta   map[string]*ExtraFieldMeta | ||||||
|  | 	FormulaMeta *FormulaMeta | ||||||
|  | 	LookupMeta  *ApaasLookupMeta | ||||||
|  | 	Checker | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ApaasMeta) IsApaasFieldType() bool { | ||||||
|  | 	return p.ApaasFType == _skipApaasFieldType | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) IsExtraField() bool { | ||||||
|  | 	return p.ApaasFType == ApaasExtraType | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) IsFormulaField() bool { | ||||||
|  | 	return p.ApaasFType == ApaasFormulaType | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) IsLookupID() bool { | ||||||
|  | 	return p.ApaasFType == ApaasLookupID | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) IsLookupValue() bool { | ||||||
|  | 	return p.ApaasFType == ApaasLookupValue | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ApaasMeta) GetApaasFieldType() ApaasFieldType { | ||||||
|  | 	return p.ApaasFType | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) GetExtraMeta() map[string]*ExtraFieldMeta { | ||||||
|  | 	return p.ExtraMeta | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) GetFormulaMeta() *FormulaMeta { | ||||||
|  | 	return p.FormulaMeta | ||||||
|  | } | ||||||
|  | func (p *ApaasMeta) Check(extra string) error { | ||||||
|  | 	// step1: extra rule check
 | ||||||
|  | 	err := ExtraCheck(p.ExtraMeta, extra) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ExtraFieldMeta struct { | ||||||
|  | 	Key        string | ||||||
|  | 	Type       FieldType                  // Bool/Int/Float/String/Object/Array
 | ||||||
|  | 	ObjectMeta map[string]*ExtraFieldMeta // if Type is Object, use ObjectMeta
 | ||||||
|  | 	ArrayMeta  []*ExtraFieldMeta          // if Type is Array, need ArrayMeta
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FormulaMeta struct { | ||||||
|  | 	InputFields map[string]*ApaasField | ||||||
|  | 	FormulaRule *FormulaRule | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FormulaRule struct { | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type DBMeta struct { | ||||||
|  | 	tableList    []*ApaasTable | ||||||
|  | 	tableView    map[string]*ApaasTable | ||||||
|  | 	lookupIDView map[string]*ApaasTable // each table has one one and only key to supprort lookup
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *DBMeta) GetTableByLookupID(lookID string) (*ApaasTable, bool) { | ||||||
|  | 	v, ok := p.lookupIDView[lookID] | ||||||
|  | 	return v, ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *DBMeta) GetTableByName(tableName string) (*ApaasTable, bool) { | ||||||
|  | 	v, ok := p.tableView[tableName] | ||||||
|  | 	return v, ok | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								apaas/view.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								apaas/view.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1 @@ | |||||||
|  | package apaas | ||||||
							
								
								
									
										18
									
								
								apaas_mode.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								apaas_mode.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | package gorm | ||||||
|  | 
 | ||||||
|  | type ApaasModeType uint8 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	DirectMode ApaasModeType = iota | ||||||
|  | 	EngineMode | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (m ApaasModeType) String() string { | ||||||
|  | 	switch m { | ||||||
|  | 	case DirectMode: | ||||||
|  | 		return "DirectMode" | ||||||
|  | 	case EngineMode: | ||||||
|  | 		return "EngineMode" | ||||||
|  | 	} | ||||||
|  | 	return "DirectMode" | ||||||
|  | } | ||||||
							
								
								
									
										16
									
								
								callbacks.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								callbacks.go
									
									
									
									
									
								
							| @ -8,6 +8,7 @@ import ( | |||||||
| 	"sort" | 	"sort" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"gorm.io/gorm/apaas" | ||||||
| 	"gorm.io/gorm/schema" | 	"gorm.io/gorm/schema" | ||||||
| 	"gorm.io/gorm/utils" | 	"gorm.io/gorm/utils" | ||||||
| ) | ) | ||||||
| @ -109,8 +110,8 @@ func (p *processor) Execute(db *DB) *DB { | |||||||
| 				db.AddError(err) | 				db.AddError(err) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
|  | 		parseLookupTagMeta(stmt) | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	// assign stmt.ReflectValue
 | 	// assign stmt.ReflectValue
 | ||||||
| 	if stmt.Dest != nil { | 	if stmt.Dest != nil { | ||||||
| 		stmt.ReflectValue = reflect.ValueOf(stmt.Dest) | 		stmt.ReflectValue = reflect.ValueOf(stmt.Dest) | ||||||
| @ -358,3 +359,16 @@ func removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback { | |||||||
| 	} | 	} | ||||||
| 	return callbacks | 	return callbacks | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func parseLookupTagMeta(stmt *Statement) { | ||||||
|  | 	for _, field := range stmt.Schema.Fields { | ||||||
|  | 		lookupMeta, err := apaas.ParseLookupTagMeta(field.Tag.Get(apaas.TagValue), field.DBName, stmt.DBName, stmt.Schema.Table) | ||||||
|  | 		if err != nil { | ||||||
|  | 			stmt.DB.AddError(err) | ||||||
|  | 		} | ||||||
|  | 		if lookupMeta != nil { | ||||||
|  | 			field.LookupMeta = lookupMeta | ||||||
|  | 			stmt.ApaasMode = EngineMode | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
							
								
								
									
										79
									
								
								callbacks/apaas_callbacks.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								callbacks/apaas_callbacks.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,79 @@ | |||||||
|  | package callbacks | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"reflect" | ||||||
|  | 
 | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | 	"gorm.io/gorm/apaas" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var dbNameCaller func(*gorm.DB) (string, error) | ||||||
|  | 
 | ||||||
|  | func SetDBNameCaller(fn func(*gorm.DB) (string, error)) { | ||||||
|  | 	dbNameCaller = fn | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func ExtraCheckerCallBack(stage string) func(db *gorm.DB) { | ||||||
|  | 	return func(db *gorm.DB) { | ||||||
|  | 		if db.Error == nil && db.Statement.Schema != nil { | ||||||
|  | 			if db.Config.DBName == "" { | ||||||
|  | 				if dbNameCaller != nil { | ||||||
|  | 					db.Config.DBName, _ = dbNameCaller(db) | ||||||
|  | 				} else { | ||||||
|  | 					db.Config.DBName, _ = db.GetDBName() | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			dbName := db.Config.DBName | ||||||
|  | 			if dbName == "" { | ||||||
|  | 				//db.Error = db.AddError(GenError(fmt.Sprintf("%s ExtraCheckerCallBack(stage=%s) GetDBName nil", MSG_PREFIX, stage)))
 | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			/* | ||||||
|  | 				db.Logger.Info(db.Statement.Context, "===schema: %#v\n", db.Statement.Schema.Fields) | ||||||
|  | 				for i, s := range db.Statement.Schema.Fields { | ||||||
|  | 					db.Logger.Info(db.Statement.Context, "===schema[i=%d]: %#v\n", i, *s) | ||||||
|  | 				} | ||||||
|  | 			*/ | ||||||
|  | 			dbCol := apaas.GetDBCol() | ||||||
|  | 			if dbCol == nil { | ||||||
|  | 				//db.Error = db.AddError(GenError(fmt.Sprintf("%s ExtraCheckerCallBack(stage=%s) GetDBCollection nil ", MSG_PREFIX, stage)))
 | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			dbMeta, ok := dbCol.GetDB(dbName) | ||||||
|  | 			if !ok { | ||||||
|  | 				//db.Error = db.AddError(GenError(fmt.Sprintf("%s ExtraCheckerCallBack(stage=%s) GetDB(db=%s) nil", dbName, MSG_PREFIX, stage)))
 | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			tableMeta, ok := dbMeta.GetTableByName(db.Statement.Table) | ||||||
|  | 			if !ok { | ||||||
|  | 				//db.Error = db.AddError(GenError(fmt.Sprintf("%s ExtraCheckerCallBack(stage=%s) GetTable(db=%s,table=%s) GetDB nil", MSG_PREFIX, stage, dbName, db.Statement.Table)))
 | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			db.Logger.Info(db.Statement.Context, "%s ExtraCheckerCallBack(stage=%s) db_name=%s, table=%s", apaas.MSG_PREFIX, stage, dbName, tableMeta.TableName) | ||||||
|  | 			val := reflect.ValueOf(db.Statement.Dest) | ||||||
|  | 			for _, extraField := range tableMeta.ExtraFields { | ||||||
|  | 				db.Logger.Info(db.Statement.Context, "%s ExtraCheckerCallBack(stage=%s) db_name=%s, table=%s, check extra field=%s begin", apaas.MSG_PREFIX, stage, dbName, db.Statement.Table, extraField.Name) | ||||||
|  | 				field, ok := db.Statement.Schema.FieldsByDBName[extraField.Name] | ||||||
|  | 				if !ok { | ||||||
|  | 					db.Error = db.AddError(apaas.GenError(fmt.Sprintf("ExtraCheckerCallBack(stage=%s) Extra Field(db=%s,table=%s,field=%s) not found value in DestValue", stage, dbName, db.Statement.Table, field.DBName))) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				v, _ := field.ValueOf(db.Statement.Context, val) | ||||||
|  | 				strV, ok := v.(string) | ||||||
|  | 				pStrV, ok1 := v.(*string) | ||||||
|  | 				if !ok && !ok1 { | ||||||
|  | 					db.Error = db.AddError(apaas.GenError(fmt.Sprintf("ExtraCheckerCallBack(stage=%s) Extra Field(db=%s,table=%s,field=%s) is not string/*string value in DestValue", stage, dbName, db.Statement.Table, field.DBName))) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 				if ok1 { | ||||||
|  | 					strV = *pStrV | ||||||
|  | 				} | ||||||
|  | 				if err := extraField.GetApaasMeta().Check(strV); err != nil { | ||||||
|  | 					db.Error = db.AddError(apaas.GenError(fmt.Sprintf("ExtraCheckerCallBack(stage=%s) Extra Field(db=%s,table=%s,field=%s) check error=%s", stage, dbName, db.Statement.Table, field.DBName, err.Error()))) | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -38,12 +38,17 @@ func RegisterDefaultCallbacks(db *gorm.DB, config *Config) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	createCallback := db.Callback().Create() | 	createCallback := db.Callback().Create() | ||||||
|  | 	// =====apaas callback==========
 | ||||||
|  | 	// apaas create callback register before transaction
 | ||||||
|  | 	createCallback.Before("gorm:create").Register("apaas_plugin:before_create", ExtraCheckerCallBack("create")) | ||||||
|  | 	// =====apaas callback end======
 | ||||||
| 	createCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction) | 	createCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction) | ||||||
| 	createCallback.Register("gorm:before_create", BeforeCreate) | 	createCallback.Register("gorm:before_create", BeforeCreate) | ||||||
| 	createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true)) | 	createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true)) | ||||||
| 	createCallback.Register("gorm:create", Create(config)) | 	createCallback.Register("gorm:create", Create(config)) | ||||||
| 	createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true)) | 	createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true)) | ||||||
| 	createCallback.Register("gorm:after_create", AfterCreate) | 	createCallback.Register("gorm:after_create", AfterCreate) | ||||||
|  | 
 | ||||||
| 	createCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | 	createCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | ||||||
| 	createCallback.Clauses = config.CreateClauses | 	createCallback.Clauses = config.CreateClauses | ||||||
| 
 | 
 | ||||||
| @ -63,6 +68,10 @@ func RegisterDefaultCallbacks(db *gorm.DB, config *Config) { | |||||||
| 	deleteCallback.Clauses = config.DeleteClauses | 	deleteCallback.Clauses = config.DeleteClauses | ||||||
| 
 | 
 | ||||||
| 	updateCallback := db.Callback().Update() | 	updateCallback := db.Callback().Update() | ||||||
|  | 	// =====apaas callback==========
 | ||||||
|  | 	// apaas update callback register before transaction
 | ||||||
|  | 	updateCallback.Register("apaas_plugin:before_update", ExtraCheckerCallBack("update")) | ||||||
|  | 	// =====apaas callback end======
 | ||||||
| 	updateCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction) | 	updateCallback.Match(enableTransaction).Register("gorm:begin_transaction", BeginTransaction) | ||||||
| 	updateCallback.Register("gorm:setup_reflect_value", SetupUpdateReflectValue) | 	updateCallback.Register("gorm:setup_reflect_value", SetupUpdateReflectValue) | ||||||
| 	updateCallback.Register("gorm:before_update", BeforeUpdate) | 	updateCallback.Register("gorm:before_update", BeforeUpdate) | ||||||
| @ -70,6 +79,7 @@ func RegisterDefaultCallbacks(db *gorm.DB, config *Config) { | |||||||
| 	updateCallback.Register("gorm:update", Update(config)) | 	updateCallback.Register("gorm:update", Update(config)) | ||||||
| 	updateCallback.Register("gorm:save_after_associations", SaveAfterAssociations(false)) | 	updateCallback.Register("gorm:save_after_associations", SaveAfterAssociations(false)) | ||||||
| 	updateCallback.Register("gorm:after_update", AfterUpdate) | 	updateCallback.Register("gorm:after_update", AfterUpdate) | ||||||
|  | 
 | ||||||
| 	updateCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | 	updateCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | ||||||
| 	updateCallback.Clauses = config.UpdateClauses | 	updateCallback.Clauses = config.UpdateClauses | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -74,6 +74,7 @@ func (db *DB) CreateInBatches(value interface{}, batchSize int) (tx *DB) { | |||||||
| func (db *DB) Save(value interface{}) (tx *DB) { | func (db *DB) Save(value interface{}) (tx *DB) { | ||||||
| 	tx = db.getInstance() | 	tx = db.getInstance() | ||||||
| 	tx.Statement.Dest = value | 	tx.Statement.Dest = value | ||||||
|  | 	db.Logger.Info(db.Statement.Context, "-----gorm save value: %#v", tx.Statement.Dest) | ||||||
| 
 | 
 | ||||||
| 	reflectValue := reflect.Indirect(reflect.ValueOf(value)) | 	reflectValue := reflect.Indirect(reflect.ValueOf(value)) | ||||||
| 	for reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface { | 	for reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface { | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,5 +5,7 @@ go 1.18 | |||||||
| require ( | require ( | ||||||
| 	github.com/jinzhu/inflection v1.0.0 | 	github.com/jinzhu/inflection v1.0.0 | ||||||
| 	github.com/jinzhu/now v1.1.5 | 	github.com/jinzhu/now v1.1.5 | ||||||
| 	golang.org/x/text v0.20.0 | 	golang.org/x/text v0.14.0 | ||||||
| ) | ) | ||||||
|  | 
 | ||||||
|  | replace gorm.io/gorm => ./ | ||||||
|  | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @ -2,5 +2,5 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD | |||||||
| github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= | github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= | ||||||
| github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= | github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= | ||||||
| github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | ||||||
| golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | ||||||
| golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								gorm.go
									
									
									
									
									
								
							
							
						
						
									
										30
									
								
								gorm.go
									
									
									
									
									
								
							| @ -69,6 +69,10 @@ type Config struct { | |||||||
| 
 | 
 | ||||||
| 	callbacks  *callbacks | 	callbacks  *callbacks | ||||||
| 	cacheStore *sync.Map | 	cacheStore *sync.Map | ||||||
|  | 
 | ||||||
|  | 	/* ====Apaas Begin==== */ | ||||||
|  | 	DBName string | ||||||
|  | 	/* ====Apaas End====== */ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Apply update config to new config
 | // Apply update config to new config
 | ||||||
| @ -224,6 +228,9 @@ func Open(dialector Dialector, opts ...Option) (db *DB, err error) { | |||||||
| 		config.Logger.Error(context.Background(), "failed to initialize database, got error %v", err) | 		config.Logger.Error(context.Background(), "failed to initialize database, got error %v", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	dbName, _ := db.GetDBName() | ||||||
|  | 	db.Config.DBName = dbName | ||||||
|  | 
 | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -524,3 +531,26 @@ func (db *DB) ToSQL(queryFn func(tx *DB) *DB) string { | |||||||
| 
 | 
 | ||||||
| 	return db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...) | 	return db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (db *DB) GetDBName() (string, error) { | ||||||
|  | 	var dbName string | ||||||
|  | 	var err error | ||||||
|  | 	// must be create new db
 | ||||||
|  | 	db1 := db.WithContext(context.Background()) | ||||||
|  | 	// 检测数据库类型
 | ||||||
|  | 	switch db1.Dialector.Name() { | ||||||
|  | 	case "mysql": | ||||||
|  | 		err = db1.Raw("SELECT DATABASE()").Scan(&dbName).Error | ||||||
|  | 	case "postgres": | ||||||
|  | 		err = db1.Raw("SELECT current_database()").Scan(&dbName).Error | ||||||
|  | 	case "sqlite": | ||||||
|  | 		// SQLite中数据库名通常是文件路径
 | ||||||
|  | 		var path string | ||||||
|  | 		err = db1.Raw("PRAGMA database_list").Scan(&struct{ Name, File string }{}).Error | ||||||
|  | 		dbName = path | ||||||
|  | 	default: | ||||||
|  | 		return "", fmt.Errorf("unsupported database dialect") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return dbName, err | ||||||
|  | } | ||||||
|  | |||||||
| @ -74,7 +74,7 @@ var ( | |||||||
| 	// Default Default logger
 | 	// Default Default logger
 | ||||||
| 	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{ | 	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{ | ||||||
| 		SlowThreshold:             200 * time.Millisecond, | 		SlowThreshold:             200 * time.Millisecond, | ||||||
| 		LogLevel:                  Warn, | 		LogLevel:                  Info, | ||||||
| 		IgnoreRecordNotFoundError: false, | 		IgnoreRecordNotFoundError: false, | ||||||
| 		Colorful:                  true, | 		Colorful:                  true, | ||||||
| 	}) | 	}) | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
| 	"github.com/jinzhu/now" | 	"github.com/jinzhu/now" | ||||||
|  | 	"gorm.io/gorm/apaas" | ||||||
| 	"gorm.io/gorm/clause" | 	"gorm.io/gorm/clause" | ||||||
| 	"gorm.io/gorm/utils" | 	"gorm.io/gorm/utils" | ||||||
| ) | ) | ||||||
| @ -96,6 +97,10 @@ type Field struct { | |||||||
| 	// It causes field unnecessarily migration.
 | 	// It causes field unnecessarily migration.
 | ||||||
| 	// Therefore, we need to record the UniqueIndex on this column (exclude Mul UniqueIndex) for MigrateColumnUnique.
 | 	// Therefore, we need to record the UniqueIndex on this column (exclude Mul UniqueIndex) for MigrateColumnUnique.
 | ||||||
| 	UniqueIndex string | 	UniqueIndex string | ||||||
|  | 
 | ||||||
|  | 	// ==========apaas engine field begin==========
 | ||||||
|  | 	LookupMeta *apaas.ApaasLookupMeta | ||||||
|  | 	//  ==========apaas engine field end==========
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (field *Field) BindName() string { | func (field *Field) BindName() string { | ||||||
| @ -131,7 +136,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | |||||||
| 		Comment:                tagSetting["COMMENT"], | 		Comment:                tagSetting["COMMENT"], | ||||||
| 		AutoIncrementIncrement: DefaultAutoIncrementIncrement, | 		AutoIncrementIncrement: DefaultAutoIncrementIncrement, | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	for field.IndirectFieldType.Kind() == reflect.Ptr { | 	for field.IndirectFieldType.Kind() == reflect.Ptr { | ||||||
| 		field.IndirectFieldType = field.IndirectFieldType.Elem() | 		field.IndirectFieldType = field.IndirectFieldType.Elem() | ||||||
| 	} | 	} | ||||||
|  | |||||||
| @ -57,6 +57,11 @@ type Schema struct { | |||||||
| 	initialized               chan struct{} | 	initialized               chan struct{} | ||||||
| 	namer                     Namer | 	namer                     Namer | ||||||
| 	cacheStore                *sync.Map | 	cacheStore                *sync.Map | ||||||
|  | 	// functional for apaas engine add by fangming
 | ||||||
|  | 	// ==========apaas engine field begin==========
 | ||||||
|  | 	ApaasLookupFields  []*Field | ||||||
|  | 	ApaasFormulaFields []*Field | ||||||
|  | 	// ==========apaas engine field end==========
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (schema Schema) String() string { | func (schema Schema) String() string { | ||||||
|  | |||||||
| @ -47,6 +47,11 @@ type Statement struct { | |||||||
| 	attrs                []interface{} | 	attrs                []interface{} | ||||||
| 	assigns              []interface{} | 	assigns              []interface{} | ||||||
| 	scopes               []func(*DB) *DB | 	scopes               []func(*DB) *DB | ||||||
|  | 
 | ||||||
|  | 	// ==========apaas engine field begin==========
 | ||||||
|  | 	// apaas mode for gormDB
 | ||||||
|  | 	ApaasMode ApaasModeType | ||||||
|  | 	// ==========apaas engine field end==========
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type join struct { | type join struct { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 方明
						方明