github.com/wangyougui/gf/v2@v2.6.5/database/gdb/gdb_model_utility.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gdb 8 9 import ( 10 "time" 11 12 "github.com/wangyougui/gf/v2/container/gset" 13 "github.com/wangyougui/gf/v2/internal/empty" 14 "github.com/wangyougui/gf/v2/os/gtime" 15 "github.com/wangyougui/gf/v2/text/gregex" 16 "github.com/wangyougui/gf/v2/text/gstr" 17 "github.com/wangyougui/gf/v2/util/gutil" 18 ) 19 20 // QuoteWord checks given string `s` a word, 21 // if true it quotes `s` with security chars of the database 22 // and returns the quoted string; or else it returns `s` without any change. 23 // 24 // The meaning of a `word` can be considered as a column name. 25 func (m *Model) QuoteWord(s string) string { 26 return m.db.GetCore().QuoteWord(s) 27 } 28 29 // TableFields retrieves and returns the fields' information of specified table of current 30 // schema. 31 // 32 // Also see DriverMysql.TableFields. 33 func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) { 34 var ( 35 table = m.db.GetCore().guessPrimaryTableName(tableStr) 36 usedSchema = gutil.GetOrDefaultStr(m.schema, schema...) 37 ) 38 return m.db.TableFields(m.GetCtx(), table, usedSchema) 39 } 40 41 // getModel creates and returns a cloned model of current model if `safe` is true, or else it returns 42 // the current model. 43 func (m *Model) getModel() *Model { 44 if !m.safe { 45 return m 46 } else { 47 return m.Clone() 48 } 49 } 50 51 // mappingAndFilterToTableFields mappings and changes given field name to really table field name. 52 // Eg: 53 // ID -> id 54 // NICK_Name -> nickname. 55 func (m *Model) mappingAndFilterToTableFields(table string, fields []string, filter bool) []string { 56 var fieldsTable = table 57 if fieldsTable != "" { 58 hasTable, _ := m.db.GetCore().HasTable(fieldsTable) 59 if !hasTable { 60 fieldsTable = m.tablesInit 61 } 62 } 63 if fieldsTable == "" { 64 fieldsTable = m.tablesInit 65 } 66 67 fieldsMap, _ := m.TableFields(fieldsTable) 68 if len(fieldsMap) == 0 { 69 return fields 70 } 71 var outputFieldsArray = make([]string, 0) 72 fieldsKeyMap := make(map[string]interface{}, len(fieldsMap)) 73 for k := range fieldsMap { 74 fieldsKeyMap[k] = nil 75 } 76 for _, field := range fields { 77 var inputFieldsArray []string 78 if gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, field) { 79 inputFieldsArray = append(inputFieldsArray, field) 80 } else if gregex.IsMatchString(regularFieldNameWithCommaRegPattern, field) { 81 inputFieldsArray = gstr.SplitAndTrim(field, ",") 82 } else { 83 // Example: 84 // user.id, user.name 85 // replace(concat_ws(',',lpad(s.id, 6, '0'),s.name),',','') `code` 86 outputFieldsArray = append(outputFieldsArray, field) 87 continue 88 } 89 for _, inputField := range inputFieldsArray { 90 if !gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, inputField) { 91 outputFieldsArray = append(outputFieldsArray, inputField) 92 continue 93 } 94 if _, ok := fieldsKeyMap[inputField]; !ok { 95 // Example: 96 // id, name 97 if foundKey, _ := gutil.MapPossibleItemByKey(fieldsKeyMap, inputField); foundKey != "" { 98 outputFieldsArray = append(outputFieldsArray, foundKey) 99 } else if !filter { 100 outputFieldsArray = append(outputFieldsArray, inputField) 101 } 102 } else { 103 outputFieldsArray = append(outputFieldsArray, inputField) 104 } 105 } 106 } 107 return outputFieldsArray 108 } 109 110 // filterDataForInsertOrUpdate does filter feature with data for inserting/updating operations. 111 // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. 112 func (m *Model) filterDataForInsertOrUpdate(data interface{}) (interface{}, error) { 113 var err error 114 switch value := data.(type) { 115 case List: 116 var omitEmpty bool 117 if m.option&optionOmitNilDataList > 0 { 118 omitEmpty = true 119 } 120 for k, item := range value { 121 value[k], err = m.doMappingAndFilterForInsertOrUpdateDataMap(item, omitEmpty) 122 if err != nil { 123 return nil, err 124 } 125 } 126 return value, nil 127 128 case Map: 129 return m.doMappingAndFilterForInsertOrUpdateDataMap(value, true) 130 131 default: 132 return data, nil 133 } 134 } 135 136 // doMappingAndFilterForInsertOrUpdateDataMap does the filter features for map. 137 // Note that, it does not filter list item, which is also type of map, for "omit empty" feature. 138 func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEmpty bool) (Map, error) { 139 var err error 140 data, err = m.db.GetCore().mappingAndFilterData( 141 m.GetCtx(), m.schema, m.tablesInit, data, m.filter, 142 ) 143 if err != nil { 144 return nil, err 145 } 146 // Remove key-value pairs of which the value is nil. 147 if allowOmitEmpty && m.option&optionOmitNilData > 0 { 148 tempMap := make(Map, len(data)) 149 for k, v := range data { 150 if empty.IsNil(v) { 151 continue 152 } 153 tempMap[k] = v 154 } 155 data = tempMap 156 } 157 158 // Remove key-value pairs of which the value is empty. 159 if allowOmitEmpty && m.option&optionOmitEmptyData > 0 { 160 tempMap := make(Map, len(data)) 161 for k, v := range data { 162 if empty.IsEmpty(v) { 163 continue 164 } 165 // Special type filtering. 166 switch r := v.(type) { 167 case time.Time: 168 if r.IsZero() { 169 continue 170 } 171 case *time.Time: 172 if r.IsZero() { 173 continue 174 } 175 case gtime.Time: 176 if r.IsZero() { 177 continue 178 } 179 case *gtime.Time: 180 if r.IsZero() { 181 continue 182 } 183 } 184 tempMap[k] = v 185 } 186 data = tempMap 187 } 188 189 if len(m.fields) > 0 && m.fields != "*" { 190 // Keep specified fields. 191 var ( 192 set = gset.NewStrSetFrom(gstr.SplitAndTrim(m.fields, ",")) 193 charL, charR = m.db.GetChars() 194 chars = charL + charR 195 ) 196 set.Walk(func(item string) string { 197 return gstr.Trim(item, chars) 198 }) 199 for k := range data { 200 k = gstr.Trim(k, chars) 201 if !set.Contains(k) { 202 delete(data, k) 203 } 204 } 205 } else if len(m.fieldsEx) > 0 { 206 // Filter specified fields. 207 for _, v := range gstr.SplitAndTrim(m.fieldsEx, ",") { 208 delete(data, v) 209 } 210 } 211 return data, nil 212 } 213 214 // getLink returns the underlying database link object with configured `linkType` attribute. 215 // The parameter `master` specifies whether using the master node if master-slave configured. 216 func (m *Model) getLink(master bool) Link { 217 if m.tx != nil { 218 return &txLink{m.tx.GetSqlTX()} 219 } 220 linkType := m.linkType 221 if linkType == 0 { 222 if master { 223 linkType = linkTypeMaster 224 } else { 225 linkType = linkTypeSlave 226 } 227 } 228 switch linkType { 229 case linkTypeMaster: 230 link, err := m.db.GetCore().MasterLink(m.schema) 231 if err != nil { 232 panic(err) 233 } 234 return link 235 case linkTypeSlave: 236 link, err := m.db.GetCore().SlaveLink(m.schema) 237 if err != nil { 238 panic(err) 239 } 240 return link 241 } 242 return nil 243 } 244 245 // getPrimaryKey retrieves and returns the primary key name of the model table. 246 // It parses m.tables to retrieve the primary table name, supporting m.tables like: 247 // "user", "user u", "user as u, user_detail as ud". 248 func (m *Model) getPrimaryKey() string { 249 table := gstr.SplitAndTrim(m.tablesInit, " ")[0] 250 tableFields, err := m.TableFields(table) 251 if err != nil { 252 return "" 253 } 254 for name, field := range tableFields { 255 if gstr.ContainsI(field.Key, "pri") { 256 return name 257 } 258 } 259 return "" 260 } 261 262 // mergeArguments creates and returns new arguments by merging `m.extraArgs` and given `args`. 263 func (m *Model) mergeArguments(args []interface{}) []interface{} { 264 if len(m.extraArgs) > 0 { 265 newArgs := make([]interface{}, len(m.extraArgs)+len(args)) 266 copy(newArgs, m.extraArgs) 267 copy(newArgs[len(m.extraArgs):], args) 268 return newArgs 269 } 270 return args 271 }