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" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"gorm.io/gorm/apaas" | ||||
| 	"gorm.io/gorm/schema" | ||||
| 	"gorm.io/gorm/utils" | ||||
| ) | ||||
| @ -109,8 +110,8 @@ func (p *processor) Execute(db *DB) *DB { | ||||
| 				db.AddError(err) | ||||
| 			} | ||||
| 		} | ||||
| 		parseLookupTagMeta(stmt) | ||||
| 	} | ||||
| 
 | ||||
| 	// assign stmt.ReflectValue
 | ||||
| 	if stmt.Dest != nil { | ||||
| 		stmt.ReflectValue = reflect.ValueOf(stmt.Dest) | ||||
| @ -358,3 +359,16 @@ func removeCallbacks(cs []*callback, nameMap map[string]bool) []*callback { | ||||
| 	} | ||||
| 	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() | ||||
| 	// =====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.Register("gorm:before_create", BeforeCreate) | ||||
| 	createCallback.Register("gorm:save_before_associations", SaveBeforeAssociations(true)) | ||||
| 	createCallback.Register("gorm:create", Create(config)) | ||||
| 	createCallback.Register("gorm:save_after_associations", SaveAfterAssociations(true)) | ||||
| 	createCallback.Register("gorm:after_create", AfterCreate) | ||||
| 
 | ||||
| 	createCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | ||||
| 	createCallback.Clauses = config.CreateClauses | ||||
| 
 | ||||
| @ -63,6 +68,10 @@ func RegisterDefaultCallbacks(db *gorm.DB, config *Config) { | ||||
| 	deleteCallback.Clauses = config.DeleteClauses | ||||
| 
 | ||||
| 	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.Register("gorm:setup_reflect_value", SetupUpdateReflectValue) | ||||
| 	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:save_after_associations", SaveAfterAssociations(false)) | ||||
| 	updateCallback.Register("gorm:after_update", AfterUpdate) | ||||
| 
 | ||||
| 	updateCallback.Match(enableTransaction).Register("gorm:commit_or_rollback_transaction", CommitOrRollbackTransaction) | ||||
| 	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) { | ||||
| 	tx = db.getInstance() | ||||
| 	tx.Statement.Dest = value | ||||
| 	db.Logger.Info(db.Statement.Context, "-----gorm save value: %#v", tx.Statement.Dest) | ||||
| 
 | ||||
| 	reflectValue := reflect.Indirect(reflect.ValueOf(value)) | ||||
| 	for reflectValue.Kind() == reflect.Ptr || reflectValue.Kind() == reflect.Interface { | ||||
|  | ||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,5 +5,7 @@ go 1.18 | ||||
| require ( | ||||
| 	github.com/jinzhu/inflection v1.0.0 | ||||
| 	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/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= | ||||
| 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.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= | ||||
| golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | ||||
| 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 | ||||
| 	cacheStore *sync.Map | ||||
| 
 | ||||
| 	/* ====Apaas Begin==== */ | ||||
| 	DBName string | ||||
| 	/* ====Apaas End====== */ | ||||
| } | ||||
| 
 | ||||
| // 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) | ||||
| 	} | ||||
| 
 | ||||
| 	dbName, _ := db.GetDBName() | ||||
| 	db.Config.DBName = dbName | ||||
| 
 | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| @ -524,3 +531,26 @@ func (db *DB) ToSQL(queryFn func(tx *DB) *DB) string { | ||||
| 
 | ||||
| 	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 = New(log.New(os.Stdout, "\r\n", log.LstdFlags), Config{ | ||||
| 		SlowThreshold:             200 * time.Millisecond, | ||||
| 		LogLevel:                  Warn, | ||||
| 		LogLevel:                  Info, | ||||
| 		IgnoreRecordNotFoundError: false, | ||||
| 		Colorful:                  true, | ||||
| 	}) | ||||
|  | ||||
| @ -12,6 +12,7 @@ import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/jinzhu/now" | ||||
| 	"gorm.io/gorm/apaas" | ||||
| 	"gorm.io/gorm/clause" | ||||
| 	"gorm.io/gorm/utils" | ||||
| ) | ||||
| @ -96,6 +97,10 @@ type Field struct { | ||||
| 	// It causes field unnecessarily migration.
 | ||||
| 	// Therefore, we need to record the UniqueIndex on this column (exclude Mul UniqueIndex) for MigrateColumnUnique.
 | ||||
| 	UniqueIndex string | ||||
| 
 | ||||
| 	// ==========apaas engine field begin==========
 | ||||
| 	LookupMeta *apaas.ApaasLookupMeta | ||||
| 	//  ==========apaas engine field end==========
 | ||||
| } | ||||
| 
 | ||||
| func (field *Field) BindName() string { | ||||
| @ -131,7 +136,6 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field { | ||||
| 		Comment:                tagSetting["COMMENT"], | ||||
| 		AutoIncrementIncrement: DefaultAutoIncrementIncrement, | ||||
| 	} | ||||
| 
 | ||||
| 	for field.IndirectFieldType.Kind() == reflect.Ptr { | ||||
| 		field.IndirectFieldType = field.IndirectFieldType.Elem() | ||||
| 	} | ||||
|  | ||||
| @ -57,6 +57,11 @@ type Schema struct { | ||||
| 	initialized               chan struct{} | ||||
| 	namer                     Namer | ||||
| 	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 { | ||||
|  | ||||
| @ -47,6 +47,11 @@ type Statement struct { | ||||
| 	attrs                []interface{} | ||||
| 	assigns              []interface{} | ||||
| 	scopes               []func(*DB) *DB | ||||
| 
 | ||||
| 	// ==========apaas engine field begin==========
 | ||||
| 	// apaas mode for gormDB
 | ||||
| 	ApaasMode ApaasModeType | ||||
| 	// ==========apaas engine field end==========
 | ||||
| } | ||||
| 
 | ||||
| type join struct { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 方明
						方明