github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/databases/orm/models_utils.go (about)

     1  // The original package is migrated from beego and modified, you can find orignal from following link:
     2  //    "github.com/beego/beego/"
     3  //
     4  // Copyright 2023 IAC. All Rights Reserved.
     5  //
     6  // Licensed under the Apache License, Version 2.0 (the "License");
     7  // you may not use this file except in compliance with the License.
     8  // You may obtain a copy of the License at
     9  //
    10  //      http://www.apache.org/licenses/LICENSE-2.0
    11  //
    12  // Unless required by applicable law or agreed to in writing, software
    13  // distributed under the License is distributed on an "AS IS" BASIS,
    14  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package orm
    19  
    20  import (
    21  	"database/sql"
    22  	"fmt"
    23  	"reflect"
    24  	"strings"
    25  	"time"
    26  )
    27  
    28  // 1 is attr
    29  // 2 is tag
    30  var supportTag = map[string]int{
    31  	"-":            1,
    32  	"null":         1,
    33  	"index":        1,
    34  	"unique":       1,
    35  	"pk":           1,
    36  	"auto":         1,
    37  	"auto_now":     1,
    38  	"auto_now_add": 1,
    39  	"size":         2,
    40  	"column":       2,
    41  	"default":      2,
    42  	"rel":          2,
    43  	"reverse":      2,
    44  	"rel_table":    2,
    45  	"rel_through":  2,
    46  	"digits":       2,
    47  	"decimals":     2,
    48  	"on_delete":    2,
    49  	"type":         2,
    50  	"description":  2,
    51  	"precision":    2,
    52  }
    53  
    54  // get reflect.Type name with package path.
    55  func getFullName(typ reflect.Type) string {
    56  	return typ.PkgPath() + "." + typ.Name()
    57  }
    58  
    59  // getTableName get struct table name.
    60  // If the struct implement the TableName, then get the result as tablename
    61  // else use the struct name which will apply snakeString.
    62  func getTableName(val reflect.Value) string {
    63  	if fun := val.MethodByName("TableName"); fun.IsValid() {
    64  		vals := fun.Call([]reflect.Value{})
    65  		// has return and the first val is string
    66  		if len(vals) > 0 && vals[0].Kind() == reflect.String {
    67  			return vals[0].String()
    68  		}
    69  	}
    70  	return snakeString(reflect.Indirect(val).Type().Name())
    71  }
    72  
    73  // get table engine, myisam or innodb.
    74  func getTableEngine(val reflect.Value) string {
    75  	fun := val.MethodByName("TableEngine")
    76  	if fun.IsValid() {
    77  		vals := fun.Call([]reflect.Value{})
    78  		if len(vals) > 0 && vals[0].Kind() == reflect.String {
    79  			return vals[0].String()
    80  		}
    81  	}
    82  	return ""
    83  }
    84  
    85  // get table index from method.
    86  func getTableIndex(val reflect.Value) [][]string {
    87  	fun := val.MethodByName("TableIndex")
    88  	if fun.IsValid() {
    89  		vals := fun.Call([]reflect.Value{})
    90  		if len(vals) > 0 && vals[0].CanInterface() {
    91  			if d, ok := vals[0].Interface().([][]string); ok {
    92  				return d
    93  			}
    94  		}
    95  	}
    96  	return nil
    97  }
    98  
    99  // get table unique from method
   100  func getTableUnique(val reflect.Value) [][]string {
   101  	fun := val.MethodByName("TableUnique")
   102  	if fun.IsValid() {
   103  		vals := fun.Call([]reflect.Value{})
   104  		if len(vals) > 0 && vals[0].CanInterface() {
   105  			if d, ok := vals[0].Interface().([][]string); ok {
   106  				return d
   107  			}
   108  		}
   109  	}
   110  	return nil
   111  }
   112  
   113  // get whether the table needs to be created for the database alias
   114  func isApplicableTableForDB(val reflect.Value, db string) bool {
   115  	if !val.IsValid() {
   116  		return true
   117  	}
   118  	fun := val.MethodByName("IsApplicableTableForDB")
   119  	if fun.IsValid() {
   120  		vals := fun.Call([]reflect.Value{reflect.ValueOf(db)})
   121  		if len(vals) > 0 && vals[0].Kind() == reflect.Bool {
   122  			return vals[0].Bool()
   123  		}
   124  	}
   125  	return true
   126  }
   127  
   128  // get snaked column name
   129  func getColumnName(ft int, addrField reflect.Value, sf reflect.StructField, col string) string {
   130  	column := col
   131  	if col == "" {
   132  		column = nameStrategyMap[nameStrategy](sf.Name)
   133  	}
   134  	switch ft {
   135  	case RelForeignKey, RelOneToOne:
   136  		if len(col) == 0 {
   137  			column = column + "_id"
   138  		}
   139  	case RelManyToMany, RelReverseMany, RelReverseOne:
   140  		column = sf.Name
   141  	}
   142  	return column
   143  }
   144  
   145  // return field type as type constant from reflect.Value
   146  func getFieldType(val reflect.Value) (ft int, err error) {
   147  	switch val.Type() {
   148  	case reflect.TypeOf(new(int8)):
   149  		ft = TypeBitField
   150  	case reflect.TypeOf(new(int16)):
   151  		ft = TypeSmallIntegerField
   152  	case reflect.TypeOf(new(int32)),
   153  		reflect.TypeOf(new(int)):
   154  		ft = TypeIntegerField
   155  	case reflect.TypeOf(new(int64)):
   156  		ft = TypeBigIntegerField
   157  	case reflect.TypeOf(new(uint8)):
   158  		ft = TypePositiveBitField
   159  	case reflect.TypeOf(new(uint16)):
   160  		ft = TypePositiveSmallIntegerField
   161  	case reflect.TypeOf(new(uint32)),
   162  		reflect.TypeOf(new(uint)):
   163  		ft = TypePositiveIntegerField
   164  	case reflect.TypeOf(new(uint64)):
   165  		ft = TypePositiveBigIntegerField
   166  	case reflect.TypeOf(new(float32)),
   167  		reflect.TypeOf(new(float64)):
   168  		ft = TypeFloatField
   169  	case reflect.TypeOf(new(bool)):
   170  		ft = TypeBooleanField
   171  	case reflect.TypeOf(new(string)):
   172  		ft = TypeVarCharField
   173  	case reflect.TypeOf(new(time.Time)):
   174  		ft = TypeDateTimeField
   175  	default:
   176  		elm := reflect.Indirect(val)
   177  		switch elm.Kind() {
   178  		case reflect.Int8:
   179  			ft = TypeBitField
   180  		case reflect.Int16:
   181  			ft = TypeSmallIntegerField
   182  		case reflect.Int32, reflect.Int:
   183  			ft = TypeIntegerField
   184  		case reflect.Int64:
   185  			ft = TypeBigIntegerField
   186  		case reflect.Uint8:
   187  			ft = TypePositiveBitField
   188  		case reflect.Uint16:
   189  			ft = TypePositiveSmallIntegerField
   190  		case reflect.Uint32, reflect.Uint:
   191  			ft = TypePositiveIntegerField
   192  		case reflect.Uint64:
   193  			ft = TypePositiveBigIntegerField
   194  		case reflect.Float32, reflect.Float64:
   195  			ft = TypeFloatField
   196  		case reflect.Bool:
   197  			ft = TypeBooleanField
   198  		case reflect.String:
   199  			ft = TypeVarCharField
   200  		default:
   201  			if elm.Interface() == nil {
   202  				panic(fmt.Errorf("%s is nil pointer, may be miss setting tag", val))
   203  			}
   204  			switch elm.Interface().(type) {
   205  			case sql.NullInt64:
   206  				ft = TypeBigIntegerField
   207  			case sql.NullFloat64:
   208  				ft = TypeFloatField
   209  			case sql.NullBool:
   210  				ft = TypeBooleanField
   211  			case sql.NullString:
   212  				ft = TypeVarCharField
   213  			case time.Time:
   214  				ft = TypeDateTimeField
   215  			}
   216  		}
   217  	}
   218  	if ft&IsFieldType == 0 {
   219  		err = fmt.Errorf("unsupport field type %s, may be miss setting tag", val)
   220  	}
   221  	return
   222  }
   223  
   224  // parse struct tag string
   225  func parseStructTag(data string) (attrs map[string]bool, tags map[string]string) {
   226  	attrs = make(map[string]bool)
   227  	tags = make(map[string]string)
   228  	for _, v := range strings.Split(data, defaultStructTagDelim) {
   229  		if v == "" {
   230  			continue
   231  		}
   232  		v = strings.TrimSpace(v)
   233  		if t := strings.ToLower(v); supportTag[t] == 1 {
   234  			attrs[t] = true
   235  		} else if i := strings.Index(v, "("); i > 0 && strings.Index(v, ")") == len(v)-1 {
   236  			name := t[:i]
   237  			if supportTag[name] == 2 {
   238  				v = v[i+1 : len(v)-1]
   239  				tags[name] = v
   240  			}
   241  		} else {
   242  			DebugLog.Println("unsupport orm tag", v)
   243  		}
   244  	}
   245  	return
   246  }