167 lines
6.2 KiB
Go
167 lines
6.2 KiB
Go
// Copyright 2023 Dolthub, Inc.
|
|
//
|
|
// 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 cli
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/dolthub/go-mysql-server/sql"
|
|
|
|
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
|
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
|
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
|
)
|
|
|
|
type LateBindQueryistResult struct {
|
|
Queryist Queryist
|
|
Context *sql.Context
|
|
IsRemote bool
|
|
Closer func()
|
|
}
|
|
|
|
type LateBindQueryistConfig struct {
|
|
EnableAutoGC bool
|
|
}
|
|
|
|
type LateBindQueryistOption func(*LateBindQueryistConfig)
|
|
|
|
// LateBindQueryist is a function that will be called the first time Queryist is needed for use. Input is a context which
|
|
// is appropriate for the call to commence. Output is a LateBindQueryistResult, which includes a Queryist, a sql.Context, and
|
|
// a closer function. It can also result in an error.
|
|
//
|
|
// The Closer function should be called when the Queryist is no longer needed. If the result is cached and returned to
|
|
// multiple callers, it should be called after the cached result itself is no longer needed.
|
|
//
|
|
// A LateBindqueryistResult includes enough information for a caller to know if it is connecting to a remote Dolt instance
|
|
// or if it running the SqlEngine locally in-process. The CliContext uses this, in addition to its own local state, to
|
|
// let a caller know if they are connected to a remote and if this is the first QueryEngine fetch of the process lifecycle.
|
|
// This is reflected in |IsRemote| and, in the case of QueryEngineResult, |IsFirstUse|.
|
|
//
|
|
// This state is useful for determining whether a command making use of the CliContext is being run within the context of
|
|
// another command. This is particularly interesting when running a \checkout in a dolt sql session. It makes sense to do
|
|
// so in the context of `dolt sql`, but not in the context of `dolt checkout` when connected to a remote server.
|
|
type LateBindQueryist func(ctx context.Context, opts ...LateBindQueryistOption) (LateBindQueryistResult, error)
|
|
|
|
// CliContexct is used to pass top level command information down to subcommands.
|
|
type CliContext interface {
|
|
// GlobalArgs returns the arguments passed before the subcommand.
|
|
GlobalArgs() *argparser.ArgParseResults
|
|
WorkingDir() filesys.Filesys
|
|
Config() *env.DoltCliConfig
|
|
QueryEngine(ctx context.Context, opts ...LateBindQueryistOption) (QueryEngineResult, error)
|
|
// Release resources associated with the CliContext, including
|
|
// any QueryEngines which were provisioned over the lifetime
|
|
// of the CliContext.
|
|
Close()
|
|
}
|
|
|
|
// NewCliContext creates a new CliContext instance. Arguments must not be nil.
|
|
func NewCliContext(args *argparser.ArgParseResults, config *env.DoltCliConfig, cwd filesys.Filesys, latebind LateBindQueryist) (CliContext, errhand.VerboseError) {
|
|
if args == nil || config == nil || cwd == nil || latebind == nil {
|
|
return nil, errhand.VerboseErrorFromError(errors.New("Invariant violated. args, config, cwd, and latebind must be non nil."))
|
|
}
|
|
|
|
return LateBindCliContext{
|
|
globalArgs: args,
|
|
config: config,
|
|
cwd: cwd,
|
|
activeContext: &QueryistContext{},
|
|
bind: latebind,
|
|
}, nil
|
|
}
|
|
|
|
type QueryistContext struct {
|
|
sqlCtx *sql.Context
|
|
qryist *Queryist
|
|
isRemote bool
|
|
close func()
|
|
}
|
|
|
|
// LateBindCliContext is a struct that implements CliContext. Its primary purpose is to wrap the global arguments and
|
|
// provide an implementation of the QueryEngine function. This instance is stateful to ensure that the Queryist is only
|
|
// created once.
|
|
type LateBindCliContext struct {
|
|
globalArgs *argparser.ArgParseResults
|
|
cwd filesys.Filesys
|
|
config *env.DoltCliConfig
|
|
activeContext *QueryistContext
|
|
|
|
bind LateBindQueryist
|
|
}
|
|
|
|
type QueryEngineResult struct {
|
|
Queryist Queryist
|
|
Context *sql.Context
|
|
// |true| if this is the first time the CliContext is returning a QueryEngineResult.
|
|
// Otherwise it will be |false|, which means this CliContext has already been used
|
|
// to retrieve a Queryist, and the Queryist coming back is the cached result.
|
|
IsFirstResult bool
|
|
IsRemote bool
|
|
}
|
|
|
|
// GlobalArgs returns the arguments passed before the subcommand.
|
|
func (lbc LateBindCliContext) GlobalArgs() *argparser.ArgParseResults {
|
|
return lbc.globalArgs
|
|
}
|
|
|
|
// QueryEngine returns a Queryist, a sql.Context, a closer function, and an error. It ensures that only one call to the
|
|
// LateBindQueryist is made, and caches the result. Note that if this is called twice, the closer function returns will
|
|
// be nil, callers should check if is nil.
|
|
func (lbc LateBindCliContext) QueryEngine(ctx context.Context, opts ...LateBindQueryistOption) (res QueryEngineResult, err error) {
|
|
if lbc.activeContext != nil && lbc.activeContext.qryist != nil && lbc.activeContext.sqlCtx != nil {
|
|
res.Queryist = *lbc.activeContext.qryist
|
|
res.Context = lbc.activeContext.sqlCtx
|
|
res.IsRemote = lbc.activeContext.isRemote
|
|
// Returning a cached result.
|
|
res.IsFirstResult = false
|
|
return res, nil
|
|
}
|
|
|
|
bindRes, err := lbc.bind(ctx, opts...)
|
|
if err != nil {
|
|
return res, err
|
|
}
|
|
|
|
lbc.activeContext.qryist = &bindRes.Queryist
|
|
lbc.activeContext.sqlCtx = bindRes.Context
|
|
lbc.activeContext.close = bindRes.Closer
|
|
lbc.activeContext.isRemote = bindRes.IsRemote
|
|
|
|
res.Queryist = bindRes.Queryist
|
|
res.Context = bindRes.Context
|
|
res.IsRemote = bindRes.IsRemote
|
|
res.IsFirstResult = true
|
|
return res, nil
|
|
}
|
|
|
|
func (lbc LateBindCliContext) Close() {
|
|
if lbc.activeContext != nil && lbc.activeContext.close != nil {
|
|
lbc.activeContext.close()
|
|
}
|
|
}
|
|
|
|
func (lbc LateBindCliContext) WorkingDir() filesys.Filesys {
|
|
return lbc.cwd
|
|
}
|
|
|
|
// Config returns the dolt config stored in CliContext
|
|
func (lbc LateBindCliContext) Config() *env.DoltCliConfig {
|
|
return lbc.config
|
|
}
|
|
|
|
var _ CliContext = LateBindCliContext{}
|