gf/database/gdb/gdb_core_utility.go

254 lines
7.4 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
//
package gdb
import (
"context"
"fmt"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gutil"
)
// GetDB returns the underlying DB.
func (c *Core) GetDB() DB {
return c.db
}
// GetLink creates and returns the underlying database link object with transaction checks.
// The parameter `master` specifies whether using the master node if master-slave configured.
func (c *Core) GetLink(ctx context.Context, master bool, schema string) (Link, error) {
tx := TXFromCtx(ctx, c.db.GetGroup())
if tx != nil {
return &txLink{tx.GetSqlTX()}, nil
}
if master {
link, err := c.db.GetCore().MasterLink(schema)
if err != nil {
return nil, err
}
return link, nil
}
link, err := c.db.GetCore().SlaveLink(schema)
if err != nil {
return nil, err
}
return link, nil
}
// MasterLink acts like function Master but with additional `schema` parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Master.
func (c *Core) MasterLink(schema ...string) (Link, error) {
db, err := c.db.Master(schema...)
if err != nil {
return nil, err
}
return &dbLink{
DB: db,
isOnMaster: true,
}, nil
}
// SlaveLink acts like function Slave but with additional `schema` parameter specifying
// the schema for the connection. It is defined for internal usage.
// Also see Slave.
func (c *Core) SlaveLink(schema ...string) (Link, error) {
db, err := c.db.Slave(schema...)
if err != nil {
return nil, err
}
return &dbLink{
DB: db,
isOnMaster: false,
}, nil
}
// QuoteWord checks given string `s` a word,
// if true it quotes `s` with security chars of the database
// and returns the quoted string; or else it returns `s` without any change.
//
// The meaning of a `word` can be considered as a column name.
func (c *Core) QuoteWord(s string) string {
s = gstr.Trim(s)
if s == "" {
return s
}
charLeft, charRight := c.db.GetChars()
return doQuoteWord(s, charLeft, charRight)
}
// QuoteString quotes string with quote chars. Strings like:
// "user", "user u", "user,user_detail", "user u, user_detail ut", "u.id asc".
//
// The meaning of a `string` can be considered as part of a statement string including columns.
func (c *Core) QuoteString(s string) string {
if !gregex.IsMatchString(regularFieldNameWithCommaRegPattern, s) {
return s
}
charLeft, charRight := c.db.GetChars()
return doQuoteString(s, charLeft, charRight)
}
// QuotePrefixTableName adds prefix string and quotes chars for the table.
// It handles table string like:
// "user", "user u",
// "user,user_detail",
// "user u, user_detail ut",
// "user as u, user_detail as ut".
//
// Note that, this will automatically checks the table prefix whether already added,
// if true it does nothing to the table name, or else adds the prefix to the table name.
func (c *Core) QuotePrefixTableName(table string) string {
charLeft, charRight := c.db.GetChars()
return doQuoteTableName(table, c.db.GetPrefix(), charLeft, charRight)
}
// GetChars returns the security char for current database.
// It does nothing in default.
func (c *Core) GetChars() (charLeft string, charRight string) {
return "", ""
}
// Tables retrieves and returns the tables of current schema.
// It's mainly used in cli tool chain for automatically generating the models.
func (c *Core) Tables(ctx context.Context, schema ...string) (tables []string, err error) {
return
}
// TableFields retrieves and returns the fields' information of specified table of current
// schema.
//
// The parameter `link` is optional, if given nil it automatically retrieves a raw sql connection
// as its link to proceed necessary sql query.
//
// Note that it returns a map containing the field name and its corresponding fields.
// As a map is unsorted, the TableField struct has an "Index" field marks its sequence in
// the fields.
//
// It's using cache feature to enhance the performance, which is never expired util the
// process restarts.
func (c *Core) TableFields(ctx context.Context, table string, schema ...string) (fields map[string]*TableField, err error) {
return
}
// ClearTableFields removes certain cached table fields of current configuration group.
func (c *Core) ClearTableFields(ctx context.Context, table string, schema ...string) (err error) {
tableFieldsCacheKey := genTableFieldsCacheKey(
c.db.GetGroup(),
gutil.GetOrDefaultStr(c.db.GetSchema(), schema...),
table,
)
_, err = c.innerMemCache.Remove(ctx, tableFieldsCacheKey)
return
}
// ClearTableFieldsAll removes all cached table fields of current configuration group.
func (c *Core) ClearTableFieldsAll(ctx context.Context) (err error) {
var (
keys, _ = c.innerMemCache.KeyStrings(ctx)
cachePrefix = cachePrefixTableFields
removedKeys = make([]any, 0)
)
for _, key := range keys {
if gstr.HasPrefix(key, cachePrefix) {
removedKeys = append(removedKeys, key)
}
}
if len(removedKeys) > 0 {
err = c.innerMemCache.Removes(ctx, removedKeys)
}
return
}
// ClearCache removes cached sql result of certain table.
func (c *Core) ClearCache(ctx context.Context, table string) (err error) {
var (
keys, _ = c.db.GetCache().KeyStrings(ctx)
cachePrefix = fmt.Sprintf(`%s%s@`, cachePrefixSelectCache, table)
removedKeys = make([]any, 0)
)
for _, key := range keys {
if gstr.HasPrefix(key, cachePrefix) {
removedKeys = append(removedKeys, key)
}
}
if len(removedKeys) > 0 {
err = c.db.GetCache().Removes(ctx, removedKeys)
}
return
}
// ClearCacheAll removes all cached sql result from cache
func (c *Core) ClearCacheAll(ctx context.Context) (err error) {
if err = c.db.GetCache().Clear(ctx); err != nil {
return err
}
if err = c.GetInnerMemCache().Clear(ctx); err != nil {
return err
}
return
}
// HasField determine whether the field exists in the table.
func (c *Core) HasField(ctx context.Context, table, field string, schema ...string) (bool, error) {
table = c.guessPrimaryTableName(table)
tableFields, err := c.db.TableFields(ctx, table, schema...)
if err != nil {
return false, err
}
if len(tableFields) == 0 {
return false, gerror.NewCodef(
gcode.CodeNotFound,
`empty table fields for table "%s"`, table,
)
}
fieldsArray := make([]string, len(tableFields))
for k, v := range tableFields {
fieldsArray[v.Index] = k
}
charLeft, charRight := c.db.GetChars()
field = gstr.Trim(field, charLeft+charRight)
for _, f := range fieldsArray {
if f == field {
return true, nil
}
}
return false, nil
}
// guessPrimaryTableName parses and returns the primary table name.
func (c *Core) guessPrimaryTableName(tableStr string) string {
if tableStr == "" {
return ""
}
var (
guessedTableName string
array1 = gstr.SplitAndTrim(tableStr, ",")
array2 = gstr.SplitAndTrim(array1[0], " ")
array3 = gstr.SplitAndTrim(array2[0], ".")
)
if len(array3) >= 2 {
guessedTableName = array3[1]
} else {
guessedTableName = array3[0]
}
charL, charR := c.db.GetChars()
if charL != "" || charR != "" {
guessedTableName = gstr.Trim(guessedTableName, charL+charR)
}
if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) {
return ""
}
return guessedTableName
}