github.com/octohelm/storage@v0.0.0-20240516030302-1ac2cc1ea347/pkg/sqlbuilder/utils_.go (about)

     1  package sqlbuilder
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"sync"
     9  
    10  	contextx "github.com/octohelm/x/context"
    11  	reflectx "github.com/octohelm/x/reflect"
    12  	typesx "github.com/octohelm/x/types"
    13  )
    14  
    15  type FieldValues map[string]interface{}
    16  
    17  type StructFieldValue struct {
    18  	Field     StructField
    19  	TableName string
    20  	Value     reflect.Value
    21  }
    22  
    23  type contextKeyTableName struct{}
    24  
    25  func WithTableName(tableName string) func(ctx context.Context) context.Context {
    26  	return func(ctx context.Context) context.Context {
    27  		return contextx.WithValue(ctx, contextKeyTableName{}, tableName)
    28  	}
    29  }
    30  
    31  func TableNameFromContext(ctx context.Context) string {
    32  	if tableName, ok := ctx.Value(contextKeyTableName{}).(string); ok {
    33  		return tableName
    34  	}
    35  	return ""
    36  }
    37  
    38  type contextKeyTableAlias int
    39  
    40  func WithTableAlias(tableName string) func(ctx context.Context) context.Context {
    41  	return func(ctx context.Context) context.Context {
    42  		return contextx.WithValue(ctx, contextKeyTableAlias(1), tableName)
    43  	}
    44  }
    45  
    46  func TableAliasFromContext(ctx context.Context) string {
    47  	if tableName, ok := ctx.Value(contextKeyTableAlias(1)).(string); ok {
    48  		return tableName
    49  	}
    50  	return ""
    51  }
    52  
    53  func ColumnsByStruct(v interface{}) *Ex {
    54  	ctx := context.Background()
    55  
    56  	fields := StructFieldsFor(ctx, typesx.FromRType(reflect.TypeOf(v)))
    57  
    58  	e := Expr("")
    59  	e.Grow(len(fields))
    60  
    61  	i := 0
    62  
    63  	ForEachStructFieldValue(context.Background(), reflect.ValueOf(v), func(field *StructFieldValue) {
    64  		if i > 0 {
    65  			e.WriteQuery(", ")
    66  		}
    67  
    68  		if field.TableName != "" {
    69  			e.WriteQuery(field.TableName)
    70  			e.WriteQueryByte('.')
    71  			e.WriteQuery(field.Field.Name)
    72  			e.WriteQuery(" AS ")
    73  			e.WriteQuery(field.TableName)
    74  			e.WriteQuery("__")
    75  			e.WriteQuery(field.Field.Name)
    76  		} else {
    77  			e.WriteQuery(field.Field.Name)
    78  		}
    79  
    80  		i++
    81  	})
    82  
    83  	return e
    84  }
    85  
    86  func ForEachStructFieldValue(ctx context.Context, v interface{}, fn func(*StructFieldValue)) {
    87  	rv, ok := v.(reflect.Value)
    88  	if ok {
    89  		if rv.Kind() == reflect.Ptr && rv.IsNil() {
    90  			rv.Set(reflectx.New(rv.Type()))
    91  		}
    92  		v = rv.Interface()
    93  	}
    94  
    95  	if m, ok := v.(Model); ok {
    96  		ctx = WithTableName(m.TableName())(ctx)
    97  	}
    98  
    99  	fields := StructFieldsFor(ctx, typesx.FromRType(reflect.TypeOf(v)))
   100  
   101  	rv = reflectx.Indirect(reflect.ValueOf(v))
   102  
   103  	for i := range fields {
   104  		f := fields[i]
   105  
   106  		tagDB := f.Tags["db"]
   107  
   108  		if tagDB.HasFlag("deprecated") {
   109  			continue
   110  		}
   111  
   112  		if tableAlias, ok := f.Tags["alias"]; ok {
   113  			ctx = WithTableAlias(tableAlias.Name())(ctx)
   114  		} else {
   115  			if len(f.ModelLoc) > 0 {
   116  				fpv := f.FieldModelValue(rv)
   117  				if fpv.IsValid() {
   118  					if m, ok := fpv.Interface().(Model); ok {
   119  						ctx = WithTableName(m.TableName())(ctx)
   120  					}
   121  				}
   122  			}
   123  		}
   124  
   125  		sf := &StructFieldValue{}
   126  
   127  		sf.Field = *f
   128  		sf.Value = f.FieldValue(rv)
   129  		sf.TableName = TableNameFromContext(ctx)
   130  
   131  		if tableAlias := TableAliasFromContext(ctx); tableAlias != "" {
   132  			sf.TableName = tableAlias
   133  		}
   134  
   135  		fn(sf)
   136  	}
   137  
   138  }
   139  
   140  func GetColumnName(fieldName, tagValue string) string {
   141  	i := strings.Index(tagValue, ",")
   142  	if tagValue != "" && (i > 0 || i == -1) {
   143  		if i == -1 {
   144  			return strings.ToLower(tagValue)
   145  		}
   146  		return strings.ToLower(tagValue[0:i])
   147  	}
   148  	return "f_" + strings.ToLower(fieldName)
   149  }
   150  
   151  func ToMap(list []string) map[string]bool {
   152  	m := make(map[string]bool, len(list))
   153  	for _, fieldName := range list {
   154  		m[fieldName] = true
   155  	}
   156  	return m
   157  }
   158  
   159  func FieldValuesFromStructBy(structValue interface{}, fieldNames []string) (fieldValues FieldValues) {
   160  	fieldValues = FieldValues{}
   161  	rv := reflect.Indirect(reflect.ValueOf(structValue))
   162  	fieldMap := ToMap(fieldNames)
   163  	ForEachStructFieldValue(context.Background(), rv, func(sf *StructFieldValue) {
   164  		if fieldMap != nil && fieldMap[sf.Field.FieldName] {
   165  			fieldValues[sf.Field.FieldName] = sf.Value.Interface()
   166  		}
   167  	})
   168  	return fieldValues
   169  }
   170  
   171  func FieldValuesFromStructByNonZero(structValue interface{}, excludes ...string) (fieldValues FieldValues) {
   172  	fieldValues = FieldValues{}
   173  	rv := reflect.Indirect(reflect.ValueOf(structValue))
   174  	fieldMap := ToMap(excludes)
   175  	ForEachStructFieldValue(context.Background(), rv, func(sf *StructFieldValue) {
   176  		if !reflectx.IsEmptyValue(sf.Value) || (fieldMap != nil && fieldMap[sf.Field.FieldName]) {
   177  			fieldValues[sf.Field.FieldName] = sf.Value.Interface()
   178  		}
   179  	})
   180  	return
   181  }
   182  
   183  var schemas sync.Map
   184  
   185  func TableFromModel(model any) Table {
   186  	tpe := reflect.TypeOf(model)
   187  	if tpe.Kind() != reflect.Ptr {
   188  		panic(fmt.Errorf("model %s must be a pointer", tpe.Name()))
   189  	}
   190  	tpe = tpe.Elem()
   191  	if tpe.Kind() != reflect.Struct {
   192  		panic(fmt.Errorf("model %s must be a struct", tpe.Name()))
   193  	}
   194  
   195  	if t, ok := schemas.Load(tpe); ok {
   196  		if m, ok := model.(Model); ok {
   197  			return t.(TableWithTableName).WithTableName(m.TableName())
   198  		}
   199  		return t.(Table)
   200  	}
   201  
   202  	tableName := tpe.Name()
   203  	if m, ok := model.(Model); ok {
   204  		tableName = m.TableName()
   205  	}
   206  
   207  	t := T(tableName).(*table)
   208  
   209  	scanDefToTable(t, model)
   210  
   211  	schemas.Store(tpe, t)
   212  
   213  	return t
   214  }
   215  
   216  func scanDefToTable(tab *table, i interface{}) {
   217  	tpe := typesx.Deref(typesx.FromRType(reflect.TypeOf(i)))
   218  
   219  	comments := map[string]string{}
   220  	colDescriptions := map[string][]string{}
   221  	colRelations := map[string][]string{}
   222  
   223  	if withComments, ok := i.(WithComments); ok {
   224  		comments = withComments.Comments()
   225  	}
   226  
   227  	if withColDescriptions, ok := i.(WithColDescriptions); ok {
   228  		colDescriptions = withColDescriptions.ColDescriptions()
   229  	}
   230  
   231  	if withRelations, ok := i.(WithRelations); ok {
   232  		colRelations = withRelations.ColRelations()
   233  	}
   234  
   235  	if tab.ColumnCollection == nil {
   236  		tab.ColumnCollection = &columns{}
   237  	}
   238  
   239  	if tab.KeyCollection == nil {
   240  		tab.KeyCollection = &keys{}
   241  	}
   242  
   243  	EachStructField(context.Background(), tpe, func(f *StructField) bool {
   244  		c := &column[any]{
   245  			fieldName: f.FieldName,
   246  			name:      f.Name,
   247  			def:       f.ColumnType,
   248  		}
   249  
   250  		if comment, ok := comments[c.fieldName]; ok {
   251  			c.def.Comment = comment
   252  		}
   253  
   254  		if desc, ok := colDescriptions[c.fieldName]; ok {
   255  			c.def.Description = desc
   256  		}
   257  
   258  		if rel, ok := colRelations[c.fieldName]; ok {
   259  			c.def.Relation = rel
   260  		}
   261  
   262  		tab.ColumnCollection.(ColumnCollectionManger).AddCol(c.Of(tab))
   263  		return true
   264  	})
   265  
   266  	if withTableDescription, ok := i.(WithTableDescription); ok {
   267  		desc := withTableDescription.TableDescription()
   268  		tab.description = desc
   269  	}
   270  
   271  	if primaryKeyHook, ok := i.(WithPrimaryKey); ok {
   272  		tab.KeyCollection.(KeyCollectionManager).AddKey((&key{
   273  			name:              "primary",
   274  			isUnique:          true,
   275  			colNameAndOptions: primaryKeyHook.PrimaryKey(),
   276  		}).Of(tab))
   277  	}
   278  
   279  	if uniqueIndexesHook, ok := i.(WithUniqueIndexes); ok {
   280  		for indexNameAndMethod, fieldNames := range uniqueIndexesHook.UniqueIndexes() {
   281  			indexName, method := ResolveIndexNameAndMethod(indexNameAndMethod)
   282  
   283  			tab.KeyCollection.(KeyCollectionManager).AddKey((&key{
   284  				name:              indexName,
   285  				method:            method,
   286  				isUnique:          true,
   287  				colNameAndOptions: fieldNames,
   288  			}).Of(tab))
   289  		}
   290  	}
   291  
   292  	if indexesHook, ok := i.(WithIndexes); ok {
   293  		for indexNameAndMethod, fieldNames := range indexesHook.Indexes() {
   294  			indexName, method := ResolveIndexNameAndMethod(indexNameAndMethod)
   295  			tab.KeyCollection.(KeyCollectionManager).AddKey((&key{
   296  				name:              indexName,
   297  				method:            method,
   298  				colNameAndOptions: fieldNames,
   299  			}).Of(tab))
   300  		}
   301  	}
   302  }
   303  
   304  func ResolveIndexNameAndMethod(n string) (name string, method string) {
   305  	nameAndMethod := strings.Split(n, "/")
   306  	name = strings.ToLower(nameAndMethod[0])
   307  	if len(nameAndMethod) > 1 {
   308  		method = nameAndMethod[1]
   309  	}
   310  	return
   311  }
   312  
   313  // ParseIndexDefine
   314  // @def index i_xxx/BTREE Name
   315  // @def index i_xxx/GIST TEST/gist_trgm_ops
   316  func ParseIndexDefine(def string) *IndexDefine {
   317  	d := IndexDefine{}
   318  
   319  	for i := strings.Index(def, " "); i != -1; i = strings.Index(def, " ") {
   320  		part := def[0:i]
   321  
   322  		if part != "" {
   323  			if d.Kind == "" {
   324  				d.Kind = part
   325  			} else if d.Name == "" && d.Kind != "primary" {
   326  				d.Name, d.Method = ResolveIndexNameAndMethod(part)
   327  			} else {
   328  				break
   329  			}
   330  		}
   331  
   332  		def = def[i+1:]
   333  	}
   334  
   335  	d.ColNameAndOptions = strings.Split(strings.TrimSpace(def), " ")
   336  
   337  	return &d
   338  }
   339  
   340  type IndexDefine struct {
   341  	Kind              string
   342  	Name              string
   343  	Method            string
   344  	ColNameAndOptions []string
   345  }
   346  
   347  func (i IndexDefine) ID() string {
   348  	if i.Method != "" {
   349  		return i.Name + "/" + i.Method
   350  	}
   351  	return i.Name
   352  }