diff options
| author | Stefan Majewsky <majewsky@gmx.net> | 2026-07-17 19:35:55 +0200 |
|---|---|---|
| committer | Stefan Majewsky <majewsky@gmx.net> | 2026-07-17 19:35:55 +0200 |
| commit | 1383c6fbaa6b9e0b7cc5e44b74a905352d596758 (patch) | |
| tree | 5aa59347be173f8c16ab9d8246e89def0a0aadcd /plan.go | |
| parent | 3d1fce7b843891f789887655a9ce0c9ce1ace2de (diff) | |
| download | go-oblast-1383c6fbaa6b9e0b7cc5e44b74a905352d596758.tar.gz | |
cache buildPlan results
Diffstat (limited to 'plan.go')
| -rw-r--r-- | plan.go | 70 |
1 files changed, 58 insertions, 12 deletions
@@ -10,8 +10,66 @@ import ( "reflect" "slices" "strings" + "sync" ) +// planOpts holds additional arguments to buildPlan(). +type planOpts struct { + StructTagKey string // defaults to "db" + TableName string + PrimaryKeyColumnNames []string +} + +func collectPlanOptions(popts []PlanOption) planOpts { + opts := planOpts{ + StructTagKey: "db", + } + for _, popt := range popts { + popt(&opts) + } + return opts +} + +type planCacheKey struct { + Type reflect.Type + Dialect string + StructTagKey string + TableName string + PrimaryKeyColumnNames string +} + +var ( + generatedPlans = make(map[planCacheKey]plan) + generatedPlansMutex sync.RWMutex +) + +// getOrBuildPlan is like [buildPlan], but caches generated plans and tries to reuse cached plans. +func getOrBuildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) { + key := planCacheKey{ + Type: t, + Dialect: dialect.String(), + StructTagKey: opts.StructTagKey, + TableName: opts.TableName, + PrimaryKeyColumnNames: strings.Join(opts.PrimaryKeyColumnNames, "\000"), + } + + generatedPlansMutex.RLock() + p, ok := generatedPlans[key] + generatedPlansMutex.RUnlock() + if ok { + return p, nil + } + + p, err := buildPlan(t, dialect, opts) + if err != nil { + return plan{}, err + } + generatedPlansMutex.Lock() + generatedPlans[key] = p + generatedPlansMutex.Unlock() + return p, nil +} + // plan holds all information that we can derive from reflecting on a given type. // The queries held within are only valid within the context of a given SQL dialect. type plan struct { @@ -61,24 +119,12 @@ type plannedQuery struct { ScanIndexes [][]int } -// planOpts holds additional arguments to buildPlan(). -type planOpts struct { - StructTagKey string // defaults to "db" - TableName string - PrimaryKeyColumnNames []string -} - // buildPlan creates a new plan for the given struct type. func buildPlan(t reflect.Type, dialect Dialect, opts planOpts) (plan, error) { if t.Kind() != reflect.Struct { return plan{}, fmt.Errorf("expected struct type, but got kind %q", t.Kind().String()) } - // apply defaults to planOpts fields - if opts.StructTagKey == "" { - opts.StructTagKey = "db" - } - var p = plan{ TypeName: t.Name(), TableName: opts.TableName, |
