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 }