github.com/gogf/gf@v1.16.9/database/gdb/gdb_core.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/gogf/gf. 6 // 7 8 package gdb 9 10 import ( 11 "context" 12 "database/sql" 13 "fmt" 14 "github.com/gogf/gf/errors/gcode" 15 "github.com/gogf/gf/internal/intlog" 16 "reflect" 17 "strings" 18 19 "github.com/gogf/gf/errors/gerror" 20 "github.com/gogf/gf/internal/utils" 21 "github.com/gogf/gf/text/gstr" 22 23 "github.com/gogf/gf/container/gvar" 24 "github.com/gogf/gf/text/gregex" 25 "github.com/gogf/gf/util/gconv" 26 ) 27 28 // GetCore returns the underlying *Core object. 29 func (c *Core) GetCore() *Core { 30 return c 31 } 32 33 // Ctx is a chaining function, which creates and returns a new DB that is a shallow copy 34 // of current DB object and with given context in it. 35 // Note that this returned DB object can be used only once, so do not assign it to 36 // a global or package variable for long using. 37 func (c *Core) Ctx(ctx context.Context) DB { 38 if ctx == nil { 39 return c.db 40 } 41 ctx = context.WithValue(ctx, ctxStrictKeyName, 1) 42 // It makes a shallow copy of current db and changes its context for next chaining operation. 43 var ( 44 err error 45 newCore = &Core{} 46 configNode = c.db.GetConfig() 47 ) 48 *newCore = *c 49 newCore.ctx = ctx 50 // It creates a new DB object, which is commonly a wrapper for object `Core`. 51 newCore.db, err = driverMap[configNode.Type].New(newCore, configNode) 52 if err != nil { 53 // It is really a serious error here. 54 // Do not let it continue. 55 panic(err) 56 } 57 return newCore.db 58 } 59 60 // GetCtx returns the context for current DB. 61 // It returns `context.Background()` is there's no context previously set. 62 func (c *Core) GetCtx() context.Context { 63 if c.ctx != nil { 64 return c.ctx 65 } 66 return context.TODO() 67 } 68 69 // GetCtxTimeout returns the context and cancel function for specified timeout type. 70 func (c *Core) GetCtxTimeout(timeoutType int, ctx context.Context) (context.Context, context.CancelFunc) { 71 if ctx == nil { 72 ctx = c.GetCtx() 73 } else { 74 ctx = context.WithValue(ctx, "WrappedByGetCtxTimeout", nil) 75 } 76 switch timeoutType { 77 case ctxTimeoutTypeExec: 78 if c.db.GetConfig().ExecTimeout > 0 { 79 return context.WithTimeout(ctx, c.db.GetConfig().ExecTimeout) 80 } 81 case ctxTimeoutTypeQuery: 82 if c.db.GetConfig().QueryTimeout > 0 { 83 return context.WithTimeout(ctx, c.db.GetConfig().QueryTimeout) 84 } 85 case ctxTimeoutTypePrepare: 86 if c.db.GetConfig().PrepareTimeout > 0 { 87 return context.WithTimeout(ctx, c.db.GetConfig().PrepareTimeout) 88 } 89 default: 90 panic(gerror.NewCodef(gcode.CodeInvalidParameter, "invalid context timeout type: %d", timeoutType)) 91 } 92 return ctx, func() {} 93 } 94 95 // Close closes the database and prevents new queries from starting. 96 // Close then waits for all queries that have started processing on the server 97 // to finish. 98 // 99 // It is rare to Close a DB, as the DB handle is meant to be 100 // long-lived and shared between many goroutines. 101 func (c *Core) Close(ctx context.Context) (err error) { 102 c.links.LockFunc(func(m map[string]interface{}) { 103 for k, v := range m { 104 if db, ok := v.(*sql.DB); ok { 105 err = db.Close() 106 intlog.Printf(ctx, `close link: %s, err: %v`, k, err) 107 if err != nil { 108 return 109 } 110 delete(m, k) 111 } 112 } 113 }) 114 return 115 } 116 117 // Master creates and returns a connection from master node if master-slave configured. 118 // It returns the default connection if master-slave not configured. 119 func (c *Core) Master(schema ...string) (*sql.DB, error) { 120 useSchema := "" 121 if len(schema) > 0 && schema[0] != "" { 122 useSchema = schema[0] 123 } else { 124 useSchema = c.schema.Val() 125 } 126 return c.getSqlDb(true, useSchema) 127 } 128 129 // Slave creates and returns a connection from slave node if master-slave configured. 130 // It returns the default connection if master-slave not configured. 131 func (c *Core) Slave(schema ...string) (*sql.DB, error) { 132 useSchema := "" 133 if len(schema) > 0 && schema[0] != "" { 134 useSchema = schema[0] 135 } else { 136 useSchema = c.schema.Val() 137 } 138 return c.getSqlDb(false, useSchema) 139 } 140 141 // GetAll queries and returns data records from database. 142 func (c *Core) GetAll(sql string, args ...interface{}) (Result, error) { 143 return c.db.DoGetAll(c.GetCtx(), nil, sql, args...) 144 } 145 146 // DoGetAll queries and returns data records from database. 147 func (c *Core) DoGetAll(ctx context.Context, link Link, sql string, args ...interface{}) (result Result, err error) { 148 rows, err := c.db.DoQuery(ctx, link, sql, args...) 149 if err != nil || rows == nil { 150 return nil, err 151 } 152 defer rows.Close() 153 return c.convertRowsToResult(rows) 154 } 155 156 // GetOne queries and returns one record from database. 157 func (c *Core) GetOne(sql string, args ...interface{}) (Record, error) { 158 list, err := c.db.GetAll(sql, args...) 159 if err != nil { 160 return nil, err 161 } 162 if len(list) > 0 { 163 return list[0], nil 164 } 165 return nil, nil 166 } 167 168 // GetArray queries and returns data values as slice from database. 169 // Note that if there are multiple columns in the result, it returns just one column values randomly. 170 func (c *Core) GetArray(sql string, args ...interface{}) ([]Value, error) { 171 all, err := c.db.DoGetAll(c.GetCtx(), nil, sql, args...) 172 if err != nil { 173 return nil, err 174 } 175 return all.Array(), nil 176 } 177 178 // GetStruct queries one record from database and converts it to given struct. 179 // The parameter `pointer` should be a pointer to struct. 180 func (c *Core) GetStruct(pointer interface{}, sql string, args ...interface{}) error { 181 one, err := c.db.GetOne(sql, args...) 182 if err != nil { 183 return err 184 } 185 return one.Struct(pointer) 186 } 187 188 // GetStructs queries records from database and converts them to given struct. 189 // The parameter `pointer` should be type of struct slice: []struct/[]*struct. 190 func (c *Core) GetStructs(pointer interface{}, sql string, args ...interface{}) error { 191 all, err := c.db.GetAll(sql, args...) 192 if err != nil { 193 return err 194 } 195 return all.Structs(pointer) 196 } 197 198 // GetScan queries one or more records from database and converts them to given struct or 199 // struct array. 200 // 201 // If parameter `pointer` is type of struct pointer, it calls GetStruct internally for 202 // the conversion. If parameter `pointer` is type of slice, it calls GetStructs internally 203 // for conversion. 204 func (c *Core) GetScan(pointer interface{}, sql string, args ...interface{}) error { 205 t := reflect.TypeOf(pointer) 206 k := t.Kind() 207 if k != reflect.Ptr { 208 return fmt.Errorf("params should be type of pointer, but got: %v", k) 209 } 210 k = t.Elem().Kind() 211 switch k { 212 case reflect.Array, reflect.Slice: 213 return c.db.GetCore().GetStructs(pointer, sql, args...) 214 case reflect.Struct: 215 return c.db.GetCore().GetStruct(pointer, sql, args...) 216 } 217 return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k) 218 } 219 220 // GetValue queries and returns the field value from database. 221 // The sql should queries only one field from database, or else it returns only one 222 // field of the result. 223 func (c *Core) GetValue(sql string, args ...interface{}) (Value, error) { 224 one, err := c.db.GetOne(sql, args...) 225 if err != nil { 226 return gvar.New(nil), err 227 } 228 for _, v := range one { 229 return v, nil 230 } 231 return gvar.New(nil), nil 232 } 233 234 // GetCount queries and returns the count from database. 235 func (c *Core) GetCount(sql string, args ...interface{}) (int, error) { 236 // If the query fields do not contains function "COUNT", 237 // it replaces the sql string and adds the "COUNT" function to the fields. 238 if !gregex.IsMatchString(`(?i)SELECT\s+COUNT\(.+\)\s+FROM`, sql) { 239 sql, _ = gregex.ReplaceString(`(?i)(SELECT)\s+(.+)\s+(FROM)`, `$1 COUNT($2) $3`, sql) 240 } 241 value, err := c.db.GetValue(sql, args...) 242 if err != nil { 243 return 0, err 244 } 245 return value.Int(), nil 246 } 247 248 // Union does "(SELECT xxx FROM xxx) UNION (SELECT xxx FROM xxx) ..." statement. 249 func (c *Core) Union(unions ...*Model) *Model { 250 return c.doUnion(unionTypeNormal, unions...) 251 } 252 253 // UnionAll does "(SELECT xxx FROM xxx) UNION ALL (SELECT xxx FROM xxx) ..." statement. 254 func (c *Core) UnionAll(unions ...*Model) *Model { 255 return c.doUnion(unionTypeAll, unions...) 256 } 257 258 func (c *Core) doUnion(unionType int, unions ...*Model) *Model { 259 var ( 260 unionTypeStr string 261 composedSqlStr string 262 composedArgs = make([]interface{}, 0) 263 ) 264 if unionType == unionTypeAll { 265 unionTypeStr = "UNION ALL" 266 } else { 267 unionTypeStr = "UNION" 268 } 269 for _, v := range unions { 270 sqlWithHolder, holderArgs := v.getFormattedSqlAndArgs(queryTypeNormal, false) 271 if composedSqlStr == "" { 272 composedSqlStr += fmt.Sprintf(`(%s)`, sqlWithHolder) 273 } else { 274 composedSqlStr += fmt.Sprintf(` %s (%s)`, unionTypeStr, sqlWithHolder) 275 } 276 composedArgs = append(composedArgs, holderArgs...) 277 } 278 return c.db.Raw(composedSqlStr, composedArgs...) 279 } 280 281 // PingMaster pings the master node to check authentication or keeps the connection alive. 282 func (c *Core) PingMaster() error { 283 if master, err := c.db.Master(); err != nil { 284 return err 285 } else { 286 return master.Ping() 287 } 288 } 289 290 // PingSlave pings the slave node to check authentication or keeps the connection alive. 291 func (c *Core) PingSlave() error { 292 if slave, err := c.db.Slave(); err != nil { 293 return err 294 } else { 295 return slave.Ping() 296 } 297 } 298 299 // Insert does "INSERT INTO ..." statement for the table. 300 // If there's already one unique record of the data in the table, it returns error. 301 // 302 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 303 // Eg: 304 // Data(g.Map{"uid": 10000, "name":"john"}) 305 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 306 // 307 // The parameter `batch` specifies the batch operation count when given data is slice. 308 func (c *Core) Insert(table string, data interface{}, batch ...int) (sql.Result, error) { 309 if len(batch) > 0 { 310 return c.Model(table).Data(data).Batch(batch[0]).Insert() 311 } 312 return c.Model(table).Data(data).Insert() 313 } 314 315 // InsertIgnore does "INSERT IGNORE INTO ..." statement for the table. 316 // If there's already one unique record of the data in the table, it ignores the inserting. 317 // 318 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 319 // Eg: 320 // Data(g.Map{"uid": 10000, "name":"john"}) 321 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 322 // 323 // The parameter `batch` specifies the batch operation count when given data is slice. 324 func (c *Core) InsertIgnore(table string, data interface{}, batch ...int) (sql.Result, error) { 325 if len(batch) > 0 { 326 return c.Model(table).Data(data).Batch(batch[0]).InsertIgnore() 327 } 328 return c.Model(table).Data(data).InsertIgnore() 329 } 330 331 // InsertAndGetId performs action Insert and returns the last insert id that automatically generated. 332 func (c *Core) InsertAndGetId(table string, data interface{}, batch ...int) (int64, error) { 333 if len(batch) > 0 { 334 return c.Model(table).Data(data).Batch(batch[0]).InsertAndGetId() 335 } 336 return c.Model(table).Data(data).InsertAndGetId() 337 } 338 339 // Replace does "REPLACE INTO ..." statement for the table. 340 // If there's already one unique record of the data in the table, it deletes the record 341 // and inserts a new one. 342 // 343 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 344 // Eg: 345 // Data(g.Map{"uid": 10000, "name":"john"}) 346 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 347 // 348 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 349 // If given data is type of slice, it then does batch replacing, and the optional parameter 350 // `batch` specifies the batch operation count. 351 func (c *Core) Replace(table string, data interface{}, batch ...int) (sql.Result, error) { 352 if len(batch) > 0 { 353 return c.Model(table).Data(data).Batch(batch[0]).Replace() 354 } 355 return c.Model(table).Data(data).Replace() 356 } 357 358 // Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the table. 359 // It updates the record if there's primary or unique index in the saving data, 360 // or else it inserts a new record into the table. 361 // 362 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 363 // Eg: 364 // Data(g.Map{"uid": 10000, "name":"john"}) 365 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 366 // 367 // If given data is type of slice, it then does batch saving, and the optional parameter 368 // `batch` specifies the batch operation count. 369 func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, error) { 370 if len(batch) > 0 { 371 return c.Model(table).Data(data).Batch(batch[0]).Save() 372 } 373 return c.Model(table).Data(data).Save() 374 } 375 376 // DoInsert inserts or updates data forF given table. 377 // This function is usually used for custom interface definition, you do not need call it manually. 378 // The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc. 379 // Eg: 380 // Data(g.Map{"uid": 10000, "name":"john"}) 381 // Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) 382 // 383 // The parameter `option` values are as follows: 384 // 0: insert: just insert, if there's unique/primary key in the data, it returns error; 385 // 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one; 386 // 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one; 387 // 3: ignore: if there's unique/primary key in the data, it ignores the inserting; 388 func (c *Core) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) { 389 var ( 390 keys []string // Field names. 391 values []string // Value holder string array, like: (?,?,?) 392 params []interface{} // Values that will be committed to underlying database driver. 393 onDuplicateStr string // onDuplicateStr is used in "ON DUPLICATE KEY UPDATE" statement. 394 ) 395 // Handle the field names and place holders. 396 for k, _ := range list[0] { 397 keys = append(keys, k) 398 } 399 // Prepare the batch result pointer. 400 var ( 401 charL, charR = c.db.GetChars() 402 batchResult = new(SqlResult) 403 keysStr = charL + strings.Join(keys, charR+","+charL) + charR 404 operation = GetInsertOperationByOption(option.InsertOption) 405 ) 406 if option.InsertOption == insertOptionSave { 407 onDuplicateStr = c.formatOnDuplicate(keys, option) 408 } 409 var ( 410 listLength = len(list) 411 valueHolder = make([]string, 0) 412 ) 413 for i := 0; i < listLength; i++ { 414 values = values[:0] 415 // Note that the map type is unordered, 416 // so it should use slice+key to retrieve the value. 417 for _, k := range keys { 418 if s, ok := list[i][k].(Raw); ok { 419 values = append(values, gconv.String(s)) 420 } else { 421 values = append(values, "?") 422 params = append(params, list[i][k]) 423 } 424 } 425 valueHolder = append(valueHolder, "("+gstr.Join(values, ",")+")") 426 // Batch package checks: It meets the batch number or it is the last element. 427 if len(valueHolder) == option.BatchCount || (i == listLength-1 && len(valueHolder) > 0) { 428 r, err := c.db.DoExec(ctx, link, fmt.Sprintf( 429 "%s INTO %s(%s) VALUES%s %s", 430 operation, c.QuotePrefixTableName(table), keysStr, 431 gstr.Join(valueHolder, ","), 432 onDuplicateStr, 433 ), params...) 434 if err != nil { 435 return r, err 436 } 437 if n, err := r.RowsAffected(); err != nil { 438 return r, err 439 } else { 440 batchResult.result = r 441 batchResult.affected += n 442 } 443 params = params[:0] 444 valueHolder = valueHolder[:0] 445 } 446 } 447 return batchResult, nil 448 } 449 450 func (c *Core) formatOnDuplicate(columns []string, option DoInsertOption) string { 451 var ( 452 onDuplicateStr string 453 ) 454 if option.OnDuplicateStr != "" { 455 onDuplicateStr = option.OnDuplicateStr 456 } else if len(option.OnDuplicateMap) > 0 { 457 for k, v := range option.OnDuplicateMap { 458 if len(onDuplicateStr) > 0 { 459 onDuplicateStr += "," 460 } 461 switch v.(type) { 462 case Raw, *Raw: 463 onDuplicateStr += fmt.Sprintf( 464 "%s=%s", 465 c.QuoteWord(k), 466 v, 467 ) 468 default: 469 onDuplicateStr += fmt.Sprintf( 470 "%s=VALUES(%s)", 471 c.QuoteWord(k), 472 c.QuoteWord(gconv.String(v)), 473 ) 474 } 475 } 476 } else { 477 for _, column := range columns { 478 // If it's SAVE operation, do not automatically update the creating time. 479 if c.isSoftCreatedFieldName(column) { 480 continue 481 } 482 if len(onDuplicateStr) > 0 { 483 onDuplicateStr += "," 484 } 485 onDuplicateStr += fmt.Sprintf( 486 "%s=VALUES(%s)", 487 c.QuoteWord(column), 488 c.QuoteWord(column), 489 ) 490 } 491 } 492 return fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", onDuplicateStr) 493 } 494 495 // Update does "UPDATE ... " statement for the table. 496 // 497 // The parameter `data` can be type of string/map/gmap/struct/*struct, etc. 498 // Eg: "uid=10000", "uid", 10000, g.Map{"uid": 10000, "name":"john"} 499 // 500 // The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc. 501 // It is commonly used with parameter `args`. 502 // Eg: 503 // "uid=10000", 504 // "uid", 10000 505 // "money>? AND name like ?", 99999, "vip_%" 506 // "status IN (?)", g.Slice{1,2,3} 507 // "age IN(?,?)", 18, 50 508 // User{ Id : 1, UserName : "john"} 509 func (c *Core) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) { 510 return c.Model(table).Data(data).Where(condition, args...).Update() 511 } 512 513 // DoUpdate does "UPDATE ... " statement for the table. 514 // This function is usually used for custom interface definition, you do not need call it manually. 515 func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data interface{}, condition string, args ...interface{}) (result sql.Result, err error) { 516 table = c.QuotePrefixTableName(table) 517 var ( 518 rv = reflect.ValueOf(data) 519 kind = rv.Kind() 520 ) 521 if kind == reflect.Ptr { 522 rv = rv.Elem() 523 kind = rv.Kind() 524 } 525 var ( 526 params []interface{} 527 updates = "" 528 ) 529 switch kind { 530 case reflect.Map, reflect.Struct: 531 var ( 532 fields []string 533 dataMap = ConvertDataForTableRecord(data) 534 counterHandler = func(column string, counter Counter) { 535 if counter.Value != 0 { 536 var ( 537 column = c.QuoteWord(column) 538 columnRef = c.QuoteWord(counter.Field) 539 columnVal = counter.Value 540 operator = "+" 541 ) 542 if columnVal < 0 { 543 operator = "-" 544 columnVal = -columnVal 545 } 546 fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator)) 547 params = append(params, columnVal) 548 } 549 } 550 ) 551 552 for k, v := range dataMap { 553 switch value := v.(type) { 554 case *Counter: 555 counterHandler(k, *value) 556 557 case Counter: 558 counterHandler(k, value) 559 560 default: 561 if s, ok := v.(Raw); ok { 562 fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s)) 563 } else { 564 fields = append(fields, c.QuoteWord(k)+"=?") 565 params = append(params, v) 566 } 567 } 568 } 569 updates = strings.Join(fields, ",") 570 571 default: 572 updates = gconv.String(data) 573 } 574 if len(updates) == 0 { 575 return nil, gerror.NewCode(gcode.CodeMissingParameter, "data cannot be empty") 576 } 577 if len(params) > 0 { 578 args = append(params, args...) 579 } 580 // If no link passed, it then uses the master link. 581 if link == nil { 582 if link, err = c.MasterLink(); err != nil { 583 return nil, err 584 } 585 } 586 return c.db.DoExec(ctx, link, fmt.Sprintf("UPDATE %s SET %s%s", table, updates, condition), args...) 587 } 588 589 // Delete does "DELETE FROM ... " statement for the table. 590 // 591 // The parameter `condition` can be type of string/map/gmap/slice/struct/*struct, etc. 592 // It is commonly used with parameter `args`. 593 // Eg: 594 // "uid=10000", 595 // "uid", 10000 596 // "money>? AND name like ?", 99999, "vip_%" 597 // "status IN (?)", g.Slice{1,2,3} 598 // "age IN(?,?)", 18, 50 599 // User{ Id : 1, UserName : "john"} 600 func (c *Core) Delete(table string, condition interface{}, args ...interface{}) (result sql.Result, err error) { 601 return c.Model(table).Where(condition, args...).Delete() 602 } 603 604 // DoDelete does "DELETE FROM ... " statement for the table. 605 // This function is usually used for custom interface definition, you do not need call it manually. 606 func (c *Core) DoDelete(ctx context.Context, link Link, table string, condition string, args ...interface{}) (result sql.Result, err error) { 607 if link == nil { 608 if link, err = c.MasterLink(); err != nil { 609 return nil, err 610 } 611 } 612 table = c.QuotePrefixTableName(table) 613 return c.db.DoExec(ctx, link, fmt.Sprintf("DELETE FROM %s%s", table, condition), args...) 614 } 615 616 // convertRowsToResult converts underlying data record type sql.Rows to Result type. 617 func (c *Core) convertRowsToResult(rows *sql.Rows) (Result, error) { 618 if !rows.Next() { 619 return nil, nil 620 } 621 // Column names and types. 622 columns, err := rows.ColumnTypes() 623 if err != nil { 624 return nil, err 625 } 626 columnTypes := make([]string, len(columns)) 627 columnNames := make([]string, len(columns)) 628 for k, v := range columns { 629 columnTypes[k] = v.DatabaseTypeName() 630 columnNames[k] = v.Name() 631 } 632 var ( 633 values = make([]interface{}, len(columnNames)) 634 result = make(Result, 0) 635 scanArgs = make([]interface{}, len(values)) 636 ) 637 for i := range values { 638 scanArgs[i] = &values[i] 639 } 640 for { 641 if err := rows.Scan(scanArgs...); err != nil { 642 return result, err 643 } 644 record := Record{} 645 for i, value := range values { 646 if value == nil { 647 record[columnNames[i]] = gvar.New(nil) 648 } else { 649 record[columnNames[i]] = gvar.New(c.convertFieldValueToLocalValue(value, columnTypes[i])) 650 } 651 } 652 result = append(result, record) 653 if !rows.Next() { 654 break 655 } 656 } 657 return result, nil 658 } 659 660 // MarshalJSON implements the interface MarshalJSON for json.Marshal. 661 // It just returns the pointer address. 662 // 663 // Note that this interface implements mainly for workaround for a json infinite loop bug 664 // of Golang version < v1.14. 665 func (c *Core) MarshalJSON() ([]byte, error) { 666 return []byte(fmt.Sprintf(`%+v`, c)), nil 667 } 668 669 // writeSqlToLogger outputs the sql object to logger. 670 // It is enabled only if configuration "debug" is true. 671 func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) { 672 var transactionIdStr string 673 if sql.IsTransaction { 674 if v := ctx.Value(transactionIdForLoggerCtx); v != nil { 675 transactionIdStr = fmt.Sprintf(`[%d] `, v.(uint64)) 676 } 677 } 678 s := fmt.Sprintf("[%3d ms] [%s] %s%s", sql.End-sql.Start, sql.Group, transactionIdStr, sql.Format) 679 if sql.Error != nil { 680 s += "\nError: " + sql.Error.Error() 681 c.logger.Ctx(ctx).Error(s) 682 } else { 683 c.logger.Ctx(ctx).Debug(s) 684 } 685 } 686 687 // HasTable determine whether the table name exists in the database. 688 func (c *Core) HasTable(name string) (bool, error) { 689 tableList, err := c.db.Tables(c.GetCtx()) 690 if err != nil { 691 return false, err 692 } 693 for _, table := range tableList { 694 if table == name { 695 return true, nil 696 } 697 } 698 return false, nil 699 } 700 701 // isSoftCreatedFieldName checks and returns whether given filed name is an automatic-filled created time. 702 func (c *Core) isSoftCreatedFieldName(fieldName string) bool { 703 if fieldName == "" { 704 return false 705 } 706 if config := c.db.GetConfig(); config.CreatedAt != "" { 707 if utils.EqualFoldWithoutChars(fieldName, config.CreatedAt) { 708 return true 709 } 710 return gstr.InArray(append([]string{config.CreatedAt}, createdFiledNames...), fieldName) 711 } 712 for _, v := range createdFiledNames { 713 if utils.EqualFoldWithoutChars(fieldName, v) { 714 return true 715 } 716 } 717 return false 718 }