* feature extend readthrough for cache module (#5116) * feature 增加readthrough * feature: add write though for cache mode (#5117) * feature: add writethough for cache mode * feature add singleflight cache (#5119) * build(deps): bump go.opentelemetry.io/otel/trace from 1.8.0 to 1.11.2 Bumps [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) from 1.8.0 to 1.11.2. - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.8.0...v1.11.2) --- updated-dependencies: - dependency-name: go.opentelemetry.io/otel/trace dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * fix 5129: must set formatter after init the logger * remove beego.vip * build(deps): bump actions/stale from 5 to 7 Bumps [actions/stale](https://github.com/actions/stale) from 5 to 7. - [Release notes](https://github.com/actions/stale/releases) - [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/stale/compare/v5...v7) --- updated-dependencies: - dependency-name: actions/stale dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> * fix 5079: only log msg when the channel is not closed (#5132) * optimize test * upgrade otel dependencies to v1.11.2 * format code * Bloom filter cache (#5126) * feature: add bloom filter cache * feature upload remove all temp file * bugfix Controller SaveToFile remove all temp file * rft: motify BeeLogger signalChan (#5139) * add non-block write log in asynchronous mode (#5150) * add non-block write log in asynchronous mode --------- Co-authored-by: chenhaokun <chenhaokun@itiger.com> * fix the docsite URL (#5173) * Unified gopkg.in/yaml version to v2 (#5169) * Unified gopkg.in/yaml version to v2 and go mod tidy * update CHANGELOG * bugfix: protect field access with lock to avoid possible data race (#5211) * fix some comments (#5194) Signed-off-by: cui fliter <imcusg@gmail.com> * build(deps): bump github.com/prometheus/client_golang (#5213) Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.14.0 to 1.15.1. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.14.0...v1.15.1) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * build(deps): bump go.etcd.io/etcd/client/v3 from 3.5.4 to 3.5.9 (#5209) Bumps [go.etcd.io/etcd/client/v3](https://github.com/etcd-io/etcd) from 3.5.4 to 3.5.9. - [Release notes](https://github.com/etcd-io/etcd/releases) - [Commits](https://github.com/etcd-io/etcd/compare/v3.5.4...v3.5.9) --- updated-dependencies: - dependency-name: go.etcd.io/etcd/client/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * cache: fix typo and optimize the naming * Release 2.1.0 change log * bugfix: beegoAppConfig String and Strings function has bug * httplib: fix unstable test, do not use httplib.org * chore: pkg imported more than once * chore: fmt modify * chore: Use github.com/go-kit/log * chore: unnecessary use of fmt.Sprintf * fix: golangci-lint error * orm: refactor ORM introducing internal/models pkg * remove adapter package * build(deps): bump github.com/bits-and-blooms/bloom/v3 Bumps [github.com/bits-and-blooms/bloom/v3](https://github.com/bits-and-blooms/bloom) from 3.3.1 to 3.5.0. - [Release notes](https://github.com/bits-and-blooms/bloom/releases) - [Commits](https://github.com/bits-and-blooms/bloom/compare/v3.3.1...v3.5.0) --- updated-dependencies: - dependency-name: github.com/bits-and-blooms/bloom/v3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * feat: add write-delete cache mode * fix: unnecessary assignment to the blank identifier * fix: add change into .CHANGELOG file * build(deps): bump golang.org/x/sync from 0.1.0 to 0.3.0 Bumps [golang.org/x/sync](https://github.com/golang/sync) from 0.1.0 to 0.3.0. - [Commits](https://github.com/golang/sync/compare/v0.1.0...v0.3.0) --- updated-dependencies: - dependency-name: golang.org/x/sync dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * build(deps): bump golang.org/x/crypto Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.0.0-20220315160706-3147a52a75dd to 0.10.0. - [Commits](https://github.com/golang/crypto/commits/v0.10.0) --- updated-dependencies: - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * remove golang--lint-ci * Beego web.Run() runs the server twice * fix 5255: Check the rows.Err() if rows.Next() is false * closes 5254: %COL% should be a common placeholder * build(deps): bump github.com/prometheus/client_golang Bumps [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) from 1.15.1 to 1.16.0. - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.15.1...v1.16.0) --- updated-dependencies: - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> * fix: use of ioutil package (#5261) * fix ioutil.NopCloser * fix ioutil.ReadAll * fix ioutil.ReadFile * fix ioutil.WriteFile * run goimports -w -format-only ./ * update CHANGELOG.md * feature: add write-double-delete cache mode (#5263) * cache/redis: support skipEmptyPrefix option (#5264) * fix: refactor InsertValue method (#5267) * fix: refactor insertValue method and add the test * fix: exec goimports and add Licence file header * fix: modify construct method of dbBase * fix: add modify record into CHANGELOG * fix: modify InsertOrUpdate method (#5269) * fix: modify InsertOrUpdate method, Remove the isMulti variable and its associated code * fix: Delete unnecessary judgment branches * fix: add modify record into CHANGELOG * cache/redis: use redisConfig to receive incoming JSON (previously using a map) (#5268) * refactor cache/redis: Use redisConfig to receive incoming JSON (previously using a map). * refactor cache/redis: Use the string type to receive JSON parameters. --------- Co-authored-by: Tan <tanqianheng@gmail.com> * fix: refactor Delete method (#5271) * fix: refactor Delete method and add test * fix: add modify record into CHANGELOG * fix: refactor update sql (#5274) * fix: refactor UpdateSQL method and add test * fix: add modify record into CHANGELOG * fix: modify url in the CHANGELOG * fix: modify pr url in the CHANGELOG * Fix setPK function for table without primary key (#5276) --------- Signed-off-by: dependabot[bot] <support@github.com> Signed-off-by: cui fliter <imcusg@gmail.com> Co-authored-by: Stone-afk <73482944+Stone-afk@users.noreply.github.com> Co-authored-by: hookokoko <hooko@tju.edu.cn> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: hookokoko <648646891@qq.com> Co-authored-by: Stone-afk <1711865140@qq.com> Co-authored-by: chenhaokun <chenhaokun@itiger.com> Co-authored-by: Xuing <admin@xuing.cn> Co-authored-by: cui fliter <imcusg@gmail.com> Co-authored-by: guoguangwu <guoguangwu@magic-shield.com> Co-authored-by: uzziah <uzziahlin@gmail.com> Co-authored-by: Hanjiang Yu <delacroix.yu@gmail.com> Co-authored-by: Kota <mdryzk64smsh@gmail.com> Co-authored-by: Uzziah <120019273+uzziahlin@users.noreply.github.com> Co-authored-by: Handkerchiefs-t <59816423+Handkerchiefs-t@users.noreply.github.com> Co-authored-by: Tan <tanqianheng@gmail.com> Co-authored-by: mlgd <mlgd17@gmail.com>
385 lines
9.8 KiB
Go
385 lines
9.8 KiB
Go
// Copyright 2014 beego Author. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package orm
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/beego/beego/v2/client/orm/internal/utils"
|
|
|
|
"github.com/beego/beego/v2/client/orm/internal/models"
|
|
|
|
"github.com/beego/beego/v2/client/orm/clauses/order_clause"
|
|
"github.com/beego/beego/v2/client/orm/hints"
|
|
)
|
|
|
|
type colValue struct {
|
|
value int64
|
|
opt operator
|
|
}
|
|
|
|
type operator int
|
|
|
|
// define Col operations
|
|
const (
|
|
ColAdd operator = iota
|
|
ColMinus
|
|
ColMultiply
|
|
ColExcept
|
|
ColBitAnd
|
|
ColBitRShift
|
|
ColBitLShift
|
|
ColBitXOR
|
|
ColBitOr
|
|
)
|
|
|
|
// ColValue do the field raw changes. e.g Nums = Nums + 10. usage:
|
|
//
|
|
// Params{
|
|
// "Nums": ColValue(Col_Add, 10),
|
|
// }
|
|
func ColValue(opt operator, value interface{}) interface{} {
|
|
switch opt {
|
|
case ColAdd, ColMinus, ColMultiply, ColExcept, ColBitAnd, ColBitRShift,
|
|
ColBitLShift, ColBitXOR, ColBitOr:
|
|
default:
|
|
panic(fmt.Errorf("orm.ColValue wrong operator"))
|
|
}
|
|
v, err := utils.StrTo(utils.ToStr(value)).Int64()
|
|
if err != nil {
|
|
panic(fmt.Errorf("orm.ColValue doesn't support non string/numeric type, %s", err))
|
|
}
|
|
var val colValue
|
|
val.value = v
|
|
val.opt = opt
|
|
return val
|
|
}
|
|
|
|
// real query struct
|
|
type querySet struct {
|
|
mi *models.ModelInfo
|
|
cond *Condition
|
|
related []string
|
|
relDepth int
|
|
limit int64
|
|
offset int64
|
|
groups []string
|
|
orders []*order_clause.Order
|
|
distinct bool
|
|
forUpdate bool
|
|
useIndex int
|
|
indexes []string
|
|
orm *ormBase
|
|
aggregate string
|
|
}
|
|
|
|
var _ QuerySeter = new(querySet)
|
|
|
|
// add condition expression to QuerySeter.
|
|
func (o querySet) Filter(expr string, args ...interface{}) QuerySeter {
|
|
if o.cond == nil {
|
|
o.cond = NewCondition()
|
|
}
|
|
o.cond = o.cond.And(expr, args...)
|
|
return &o
|
|
}
|
|
|
|
// add raw sql to querySeter.
|
|
func (o querySet) FilterRaw(expr string, sql string) QuerySeter {
|
|
if o.cond == nil {
|
|
o.cond = NewCondition()
|
|
}
|
|
o.cond = o.cond.Raw(expr, sql)
|
|
return &o
|
|
}
|
|
|
|
// add NOT condition to querySeter.
|
|
func (o querySet) Exclude(expr string, args ...interface{}) QuerySeter {
|
|
if o.cond == nil {
|
|
o.cond = NewCondition()
|
|
}
|
|
o.cond = o.cond.AndNot(expr, args...)
|
|
return &o
|
|
}
|
|
|
|
// set offset number
|
|
func (o *querySet) setOffset(num interface{}) {
|
|
o.offset = utils.ToInt64(num)
|
|
}
|
|
|
|
// add LIMIT value.
|
|
// args[0] means offset, e.g. LIMIT num,offset.
|
|
func (o querySet) Limit(limit interface{}, args ...interface{}) QuerySeter {
|
|
o.limit = utils.ToInt64(limit)
|
|
if len(args) > 0 {
|
|
o.setOffset(args[0])
|
|
}
|
|
return &o
|
|
}
|
|
|
|
// add OFFSET value
|
|
func (o querySet) Offset(offset interface{}) QuerySeter {
|
|
o.setOffset(offset)
|
|
return &o
|
|
}
|
|
|
|
// add GROUP expression
|
|
func (o querySet) GroupBy(exprs ...string) QuerySeter {
|
|
o.groups = exprs
|
|
return &o
|
|
}
|
|
|
|
// add ORDER expression.
|
|
// "column" means ASC, "-column" means DESC.
|
|
func (o querySet) OrderBy(expressions ...string) QuerySeter {
|
|
if len(expressions) <= 0 {
|
|
return &o
|
|
}
|
|
o.orders = order_clause.ParseOrder(expressions...)
|
|
return &o
|
|
}
|
|
|
|
// add ORDER expression.
|
|
func (o querySet) OrderClauses(orders ...*order_clause.Order) QuerySeter {
|
|
if len(orders) <= 0 {
|
|
return &o
|
|
}
|
|
o.orders = orders
|
|
return &o
|
|
}
|
|
|
|
// add DISTINCT to SELECT
|
|
func (o querySet) Distinct() QuerySeter {
|
|
o.distinct = true
|
|
return &o
|
|
}
|
|
|
|
// add FOR UPDATE to SELECT
|
|
func (o querySet) ForUpdate() QuerySeter {
|
|
o.forUpdate = true
|
|
return &o
|
|
}
|
|
|
|
// ForceIndex force index for query
|
|
func (o querySet) ForceIndex(indexes ...string) QuerySeter {
|
|
o.useIndex = hints.KeyForceIndex
|
|
o.indexes = indexes
|
|
return &o
|
|
}
|
|
|
|
// UseIndex use index for query
|
|
func (o querySet) UseIndex(indexes ...string) QuerySeter {
|
|
o.useIndex = hints.KeyUseIndex
|
|
o.indexes = indexes
|
|
return &o
|
|
}
|
|
|
|
// IgnoreIndex ignore index for query
|
|
func (o querySet) IgnoreIndex(indexes ...string) QuerySeter {
|
|
o.useIndex = hints.KeyIgnoreIndex
|
|
o.indexes = indexes
|
|
return &o
|
|
}
|
|
|
|
// set relation model to query together.
|
|
// it will query relation models and assign to parent model.
|
|
func (o querySet) RelatedSel(params ...interface{}) QuerySeter {
|
|
if len(params) == 0 {
|
|
o.relDepth = DefaultRelsDepth
|
|
} else {
|
|
for _, p := range params {
|
|
switch val := p.(type) {
|
|
case string:
|
|
o.related = append(o.related, val)
|
|
case int:
|
|
o.relDepth = val
|
|
default:
|
|
panic(fmt.Errorf("<QuerySeter.RelatedSel> wrong param kind: %v", val))
|
|
}
|
|
}
|
|
}
|
|
return &o
|
|
}
|
|
|
|
// set condition to QuerySeter.
|
|
func (o querySet) SetCond(cond *Condition) QuerySeter {
|
|
o.cond = cond
|
|
return &o
|
|
}
|
|
|
|
// get condition from QuerySeter
|
|
func (o querySet) GetCond() *Condition {
|
|
return o.cond
|
|
}
|
|
|
|
// return QuerySeter execution result number
|
|
func (o *querySet) Count() (int64, error) {
|
|
return o.CountWithCtx(context.Background())
|
|
}
|
|
|
|
func (o *querySet) CountWithCtx(ctx context.Context) (int64, error) {
|
|
return o.orm.alias.DbBaser.Count(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ)
|
|
}
|
|
|
|
// check result empty or not after QuerySeter executed
|
|
func (o *querySet) Exist() bool {
|
|
return o.ExistWithCtx(context.Background())
|
|
}
|
|
|
|
func (o *querySet) ExistWithCtx(ctx context.Context) bool {
|
|
cnt, _ := o.orm.alias.DbBaser.Count(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ)
|
|
return cnt > 0
|
|
}
|
|
|
|
// execute update with parameters
|
|
func (o *querySet) Update(values Params) (int64, error) {
|
|
return o.UpdateWithCtx(context.Background(), values)
|
|
}
|
|
|
|
func (o *querySet) UpdateWithCtx(ctx context.Context, values Params) (int64, error) {
|
|
return o.orm.alias.DbBaser.UpdateBatch(ctx, o.orm.db, o, o.mi, o.cond, values, o.orm.alias.TZ)
|
|
}
|
|
|
|
// execute delete
|
|
func (o *querySet) Delete() (int64, error) {
|
|
return o.DeleteWithCtx(context.Background())
|
|
}
|
|
|
|
func (o *querySet) DeleteWithCtx(ctx context.Context) (int64, error) {
|
|
return o.orm.alias.DbBaser.DeleteBatch(ctx, o.orm.db, o, o.mi, o.cond, o.orm.alias.TZ)
|
|
}
|
|
|
|
// return an insert queryer.
|
|
// it can be used in times.
|
|
// example:
|
|
//
|
|
// i,err := sq.PrepareInsert()
|
|
// i.Add(&user1{},&user2{})
|
|
func (o *querySet) PrepareInsert() (Inserter, error) {
|
|
return o.PrepareInsertWithCtx(context.Background())
|
|
}
|
|
|
|
func (o *querySet) PrepareInsertWithCtx(ctx context.Context) (Inserter, error) {
|
|
return newInsertSet(ctx, o.orm, o.mi)
|
|
}
|
|
|
|
// query all data and map to containers.
|
|
// cols means the Columns when querying.
|
|
func (o *querySet) All(container interface{}, cols ...string) (int64, error) {
|
|
return o.AllWithCtx(context.Background(), container, cols...)
|
|
}
|
|
|
|
func (o *querySet) AllWithCtx(ctx context.Context, container interface{}, cols ...string) (int64, error) {
|
|
return o.orm.alias.DbBaser.ReadBatch(ctx, o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
|
}
|
|
|
|
// query one row data and map to containers.
|
|
// cols means the Columns when querying.
|
|
func (o *querySet) One(container interface{}, cols ...string) error {
|
|
return o.OneWithCtx(context.Background(), container, cols...)
|
|
}
|
|
|
|
func (o *querySet) OneWithCtx(ctx context.Context, container interface{}, cols ...string) error {
|
|
o.limit = 1
|
|
num, err := o.orm.alias.DbBaser.ReadBatch(ctx, o.orm.db, o, o.mi, o.cond, container, o.orm.alias.TZ, cols)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if num == 0 {
|
|
return ErrNoRows
|
|
}
|
|
|
|
if num > 1 {
|
|
return ErrMultiRows
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// query all data and map to []map[string]interface.
|
|
// expres means condition expression.
|
|
// it converts data to []map[column]value.
|
|
func (o *querySet) Values(results *[]Params, exprs ...string) (int64, error) {
|
|
return o.ValuesWithCtx(context.Background(), results, exprs...)
|
|
}
|
|
|
|
func (o *querySet) ValuesWithCtx(ctx context.Context, results *[]Params, exprs ...string) (int64, error) {
|
|
return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ)
|
|
}
|
|
|
|
// query all data and map to [][]interface
|
|
// it converts data to [][column_index]value
|
|
func (o *querySet) ValuesList(results *[]ParamsList, exprs ...string) (int64, error) {
|
|
return o.ValuesListWithCtx(context.Background(), results, exprs...)
|
|
}
|
|
|
|
func (o *querySet) ValuesListWithCtx(ctx context.Context, results *[]ParamsList, exprs ...string) (int64, error) {
|
|
return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, exprs, results, o.orm.alias.TZ)
|
|
}
|
|
|
|
// query all data and map to []interface.
|
|
// it's designed for one row record set, auto change to []value, not [][column]value.
|
|
func (o *querySet) ValuesFlat(result *ParamsList, expr string) (int64, error) {
|
|
return o.ValuesFlatWithCtx(context.Background(), result, expr)
|
|
}
|
|
|
|
func (o *querySet) ValuesFlatWithCtx(ctx context.Context, result *ParamsList, expr string) (int64, error) {
|
|
return o.orm.alias.DbBaser.ReadValues(ctx, o.orm.db, o, o.mi, o.cond, []string{expr}, result, o.orm.alias.TZ)
|
|
}
|
|
|
|
// query all rows into map[string]interface with specify key and value column name.
|
|
// keyCol = "name", valueCol = "value"
|
|
// table data
|
|
// name | value
|
|
// total | 100
|
|
// found | 200
|
|
//
|
|
// to map[string]interface{}{
|
|
// "total": 100,
|
|
// "found": 200,
|
|
// }
|
|
func (o *querySet) RowsToMap(result *Params, keyCol, valueCol string) (int64, error) {
|
|
panic(ErrNotImplement)
|
|
}
|
|
|
|
// query all rows into struct with specify key and value column name.
|
|
// keyCol = "name", valueCol = "value"
|
|
// table data
|
|
// name | value
|
|
// total | 100
|
|
// found | 200
|
|
//
|
|
// to struct {
|
|
// Total int
|
|
// Found int
|
|
// }
|
|
func (o *querySet) RowsToStruct(ptrStruct interface{}, keyCol, valueCol string) (int64, error) {
|
|
panic(ErrNotImplement)
|
|
}
|
|
|
|
// create new QuerySeter.
|
|
func newQuerySet(orm *ormBase, mi *models.ModelInfo) QuerySeter {
|
|
o := new(querySet)
|
|
o.mi = mi
|
|
o.orm = orm
|
|
return o
|
|
}
|
|
|
|
// aggregate func
|
|
func (o querySet) Aggregate(s string) QuerySeter {
|
|
o.aggregate = s
|
|
return &o
|
|
}
|