Files
wehub-resource-sync 5357c39144
Fuzzer / Run Fuzzer (push) Has been cancelled
Race tests / Go race tests (ubuntu-22.04) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:01:40 +08:00

643 lines
23 KiB
Go
Executable File

// Copyright 2024 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 sqlserver
import (
"fmt"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/dolthub/go-mysql-server/sql"
"github.com/dolthub/dolt/go/cmd/dolt/cli"
"github.com/dolthub/dolt/go/cmd/dolt/commands"
"github.com/dolthub/dolt/go/libraries/doltcore/servercfg"
"github.com/dolthub/dolt/go/libraries/utils/argparser"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
)
type commandLineServerConfig struct {
host string
port int
user string
password string
timeout uint64
readOnly bool
logLevel servercfg.LogLevel
logFormat servercfg.LogFormat
dataDir string
cfgDir string
autoCommit bool
doltTransactionCommit bool
branchActivityTracking bool
maxConnections uint64
maxWaitConnections uint32
maxWaitConnsTimeout time.Duration
tlsKey string
tlsCert string
caCert string
requireClientCert bool
requireSecureTransport bool
maxLoggedQueryLen int
shouldEncodeLoggedQuery bool
privilegeFilePath string
branchControlFilePath string
allowCleartextPasswords bool
socket string
remotesapiPort *int
remotesapiReadOnly *bool
goldenMysqlConn string
eventSchedulerStatus string
valuesSet map[string]struct{}
}
var _ servercfg.ServerConfig = (*commandLineServerConfig)(nil)
// MCPPort returns nil for command-line config, which does not persist MCP settings.
func (cfg *commandLineServerConfig) MCPPort() *int { return nil }
// MCPUser returns nil for command-line config.
func (cfg *commandLineServerConfig) MCPUser() *string { return nil }
// MCPPassword returns nil for command-line config.
func (cfg *commandLineServerConfig) MCPPassword() *string { return nil }
// MCPDatabase returns nil for command-line config.
func (cfg *commandLineServerConfig) MCPDatabase() *string { return nil }
// DefaultCommandLineServerConfig creates a `*ServerConfig` that has all of the options set to their default values.
func DefaultCommandLineServerConfig() *commandLineServerConfig {
return &commandLineServerConfig{
host: servercfg.DefaultHost,
port: servercfg.DefaultPort,
password: servercfg.DefaultPass,
timeout: servercfg.DefaultTimeout,
readOnly: servercfg.DefaultReadOnly,
logLevel: servercfg.DefaultLogLevel,
logFormat: servercfg.DefaultLogFormat,
autoCommit: servercfg.DefaultAutoCommit,
branchActivityTracking: servercfg.DefaultBranchActivityTracking,
maxConnections: servercfg.DefaultMaxConnections,
maxWaitConnections: servercfg.DefaultMaxWaitConnections,
maxWaitConnsTimeout: servercfg.DefaultMaxWaitConnectionsTimeout,
dataDir: servercfg.DefaultDataDir,
cfgDir: filepath.Join(servercfg.DefaultDataDir, servercfg.DefaultCfgDir),
privilegeFilePath: filepath.Join(servercfg.DefaultDataDir, servercfg.DefaultCfgDir, servercfg.DefaultPrivilegeFilePath),
branchControlFilePath: filepath.Join(servercfg.DefaultDataDir, servercfg.DefaultCfgDir, servercfg.DefaultBranchControlFilePath),
allowCleartextPasswords: servercfg.DefaultAllowCleartextPasswords,
maxLoggedQueryLen: servercfg.DefaultMaxLoggedQueryLen,
valuesSet: map[string]struct{}{},
}
}
// NewCommandLineConfig returns server config based on the credentials and command line arguments given. The dataDirOverride
// parameter is used to override the data dir specified in the command line arguments. This comes up when there are
// situations where there are multiple ways to specify the data dir.
func NewCommandLineConfig(creds *cli.UserPassword, apr *argparser.ArgParseResults, dataDirOverride string) (servercfg.ServerConfig, error) {
config := DefaultCommandLineServerConfig()
if sock, ok := apr.GetValue(socketFlag); ok {
// defined without value gets default
if sock == "" {
sock = servercfg.DefaultUnixSocketFilePath
}
config.WithSocket(sock)
}
if host, ok := apr.GetValue(hostFlag); ok {
config.WithHost(host)
}
if port, ok := apr.GetInt(portFlag); ok {
config.WithPort(port)
}
if creds == nil {
if user, ok := apr.GetValue(cli.UserFlag); ok {
config.withUser(user)
config.valuesSet[servercfg.UserKey] = struct{}{}
}
if password, ok := apr.GetValue(cli.PasswordFlag); ok {
config.withPassword(password)
config.valuesSet[servercfg.PasswordKey] = struct{}{}
}
} else {
config.withUser(creds.Username)
config.withPassword(creds.Password)
}
if port, ok := apr.GetInt(remotesapiPortFlag); ok {
config.WithRemotesapiPort(&port)
}
if apr.Contains(remotesapiReadOnlyFlag) {
val := true
config.WithRemotesapiReadOnly(&val)
}
if timeoutStr, ok := apr.GetValue(timeoutFlag); ok {
timeout, err := strconv.ParseUint(timeoutStr, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid value for --timeout '%s'", timeoutStr)
}
config.withTimeout(timeout * 1000)
}
if _, ok := apr.GetValue(readonlyFlag); ok {
config.withReadOnly(true)
val := true
config.WithRemotesapiReadOnly(&val)
}
if logLevel, ok := apr.GetValue(logLevelFlag); ok {
config.withLogLevel(servercfg.LogLevel(strings.ToLower(logLevel)))
}
if logFormat, ok := apr.GetValue(logFormatFlag); ok {
config.withLogFormat(servercfg.LogFormat(strings.ToLower(logFormat)))
}
if dataDir, ok := apr.GetValue(commands.MultiDBDirFlag); ok {
config.withDataDir(dataDir)
}
// We explicitly don't use the dataDir flag from the APR here. The data dir flag is pulled out early and converted
// to an absolute path. It is read in the GetDataDirPreStart function, which is called early in dolt.go to get the
// data dir for any dolt process. This complexity exists because the server's config.yaml config file can contain the
// dataDir, but we don't execute any server specific logic until after the database environment is initialized.
if dataDirOverride != "" {
config.withDataDir(dataDirOverride)
} else {
if dd, ok := apr.GetValue(commands.DataDirFlag); ok {
config.withDataDir(dd)
}
}
if maxConnections, ok := apr.GetInt(maxConnectionsFlag); ok {
config.withMaxConnections(uint64(maxConnections))
}
if maxWaitConnections, ok := apr.GetInt(maxWaitConnectionsFlag); ok {
config.withMaxWaitConnections(uint32(maxWaitConnections))
}
if maxWaitConnsTimeoutStr, ok := apr.GetValue(maxWaitConsTimeoutFlag); ok {
maxWaitConnsTimeout, err := time.ParseDuration(maxWaitConnsTimeoutStr)
if err != nil {
return nil, fmt.Errorf("invalid duration value for --max-wait-connections-timeout '%s'", maxWaitConnsTimeoutStr)
}
config.withMaxWaitConnectionsTimeout(maxWaitConnsTimeout)
}
config.autoCommit = !apr.Contains(noAutoCommitFlag)
if apr.Contains(noAutoCommitFlag) {
config.valuesSet[servercfg.AutoCommitKey] = struct{}{}
}
config.allowCleartextPasswords = apr.Contains(allowCleartextPasswordsFlag)
if apr.Contains(allowCleartextPasswordsFlag) {
config.valuesSet[servercfg.AllowCleartextPasswordsKey] = struct{}{}
}
if connStr, ok := apr.GetValue(goldenMysqlConn); ok {
cli.Println(connStr)
config.withGoldenMysqlConnectionString(connStr)
}
if esStatus, ok := apr.GetValue(eventSchedulerStatus); ok {
// make sure to assign eventSchedulerStatus first here
config.withEventScheduler(strings.ToUpper(esStatus))
}
return config, nil
}
// Host returns the domain that the server will run on. Accepts an IPv4 or IPv6 address, in addition to localhost.
func (cfg *commandLineServerConfig) Host() string {
return cfg.host
}
// Port returns the port that the server will run on. The valid range is [1024, 65535].
func (cfg *commandLineServerConfig) Port() int {
return cfg.port
}
// User returns the username that connecting clients must use.
func (cfg *commandLineServerConfig) User() string {
return cfg.user
}
// UserIsSpecified returns true if the configuration explicitly specified a user.
func (cfg *commandLineServerConfig) UserIsSpecified() bool {
return cfg.user != ""
}
// Password returns the password that connecting clients must use.
func (cfg *commandLineServerConfig) Password() string {
return cfg.password
}
// ReadTimeout returns the read and write timeouts.
func (cfg *commandLineServerConfig) ReadTimeout() uint64 {
return cfg.timeout
}
// WriteTimeout returns the read and write timeouts.
func (cfg *commandLineServerConfig) WriteTimeout() uint64 {
return cfg.timeout
}
// ReadOnly returns whether the server will only accept read statements or all statements.
func (cfg *commandLineServerConfig) ReadOnly() bool {
return cfg.readOnly
}
// LogLevel returns the level of logging that the server will use.
func (cfg *commandLineServerConfig) LogLevel() servercfg.LogLevel {
return cfg.logLevel
}
// LogFormat returns the format of logging that the server will use.
func (cfg *commandLineServerConfig) LogFormat() servercfg.LogFormat {
return cfg.logFormat
}
// AutoCommit defines the value of the @@autocommit session variable used on every connection
func (cfg *commandLineServerConfig) AutoCommit() bool {
return cfg.autoCommit
}
// DoltTransactionCommit defines the value of the @@dolt_transaction_commit session variable that enables Dolt
// commits to be automatically created when a SQL transaction is committed. The default is false.
func (cfg *commandLineServerConfig) DoltTransactionCommit() bool {
return cfg.doltTransactionCommit
}
// BranchActivityTracking enables or disables the tracking of branch activity for the dolt_branch_activity table. The default is false.
func (cfg *commandLineServerConfig) BranchActivityTracking() bool {
return cfg.branchActivityTracking
}
// MaxConnections returns the maximum number of simultaneous connections the server will allow. The default is 1
func (cfg *commandLineServerConfig) MaxConnections() uint64 {
return cfg.maxConnections
}
// MaxWaitConnections returns the maximum number of simultaneous connections that the server will allow to block waiting
// for a connection before new connections result in immediate rejection.
func (cfg *commandLineServerConfig) MaxWaitConnections() uint32 {
return cfg.maxWaitConnections
}
func (cfg *commandLineServerConfig) MaxWaitConnectionsTimeout() time.Duration {
return cfg.maxWaitConnsTimeout
}
// TLSKey returns a path to the servers PEM-encoded private TLS key. "" if there is none.
func (cfg *commandLineServerConfig) TLSKey() string {
return cfg.tlsKey
}
// TLSCert returns a path to the servers PEM-encoded TLS certificate chain. "" if there is none.
func (cfg *commandLineServerConfig) TLSCert() string {
return cfg.tlsCert
}
// CACert returns a path to the servers certificate authority file, or "" if there
// is no CA cert configured.
func (cfg *commandLineServerConfig) CACert() string {
return cfg.caCert
}
// RequireClientCert is true if the server should reject any connections that don't present a certificate. When
// enabled, a client certificate is always required, and if a CA cert is also configured, then the client cert
// will also be verified. Enabling this option also means that non-TLS connections are not allowed.
func (cfg *commandLineServerConfig) RequireClientCert() bool {
return cfg.requireClientCert
}
// RequireSecureTransport is true if the server should reject non-TLS connections.
func (cfg *commandLineServerConfig) RequireSecureTransport() bool {
return cfg.requireSecureTransport
}
// MaxLoggedQueryLen is the max length of queries written to the logs. Queries longer than this number are truncated.
// If this value is 0 then the query is not truncated and will be written to the logs in its entirety. If the value
// is less than 0 then the queries will be omitted from the logs completely
func (cfg *commandLineServerConfig) MaxLoggedQueryLen() int {
return cfg.maxLoggedQueryLen
}
// ShouldEncodeLoggedQuery determines if logged queries are base64 encoded.
// If true, queries will be logged as base64 encoded strings.
// If false (default behavior), queries will be logged as strings, but newlines and tabs will be replaced with spaces.
func (cfg *commandLineServerConfig) ShouldEncodeLoggedQuery() bool {
return cfg.shouldEncodeLoggedQuery
}
// DisableClientMultiStatements is true if we want the server to not
// process incoming ComQuery packets as if they had multiple queries in
// them, even if the client advertises support for MULTI_STATEMENTS.
func (cfg *commandLineServerConfig) DisableClientMultiStatements() bool {
return false
}
// MetricsLabels returns labels that are applied to all prometheus metrics
func (cfg *commandLineServerConfig) MetricsLabels() map[string]string {
return nil
}
func (cfg *commandLineServerConfig) MetricsHost() string {
return servercfg.DefaultMetricsHost
}
func (cfg *commandLineServerConfig) MetricsPort() int {
return servercfg.DefaultMetricsPort
}
func (cfg *commandLineServerConfig) MetricsTLSCert() string {
return ""
}
func (cfg *commandLineServerConfig) MetricsTLSKey() string {
return ""
}
func (cfg *commandLineServerConfig) MetricsTLSCA() string {
return ""
}
func (cfg *commandLineServerConfig) MetricsJwksConfig() *servercfg.JwksConfig {
return nil
}
func (cfg *commandLineServerConfig) MetricsJWTRequiredForLocalhost() bool {
return false
}
func (cfg *commandLineServerConfig) RemotesapiPort() *int {
return cfg.remotesapiPort
}
func (cfg *commandLineServerConfig) RemotesapiReadOnly() *bool {
return cfg.remotesapiReadOnly
}
func (cfg *commandLineServerConfig) ClusterConfig() servercfg.ClusterConfig {
return nil
}
// PrivilegeFilePath returns the path to the file which contains all needed privilege information in the form of a
// JSON string.
func (cfg *commandLineServerConfig) PrivilegeFilePath() string {
return cfg.privilegeFilePath
}
// BranchControlFilePath returns the path to the file which contains the branch control permissions.
func (cfg *commandLineServerConfig) BranchControlFilePath() string {
return cfg.branchControlFilePath
}
// UserVars is an array containing user specific session variables.
func (cfg *commandLineServerConfig) UserVars() []servercfg.UserSessionVars {
return nil
}
func (cfg *commandLineServerConfig) SystemVars() map[string]interface{} {
return nil
}
func (cfg *commandLineServerConfig) JwksConfig() []servercfg.JwksConfig {
return nil
}
func (cfg *commandLineServerConfig) AllowCleartextPasswords() bool {
return cfg.allowCleartextPasswords
}
// DataDir is the path to a directory to use as the data dir, both to create new databases and locate existing ones.
func (cfg *commandLineServerConfig) DataDir() string {
return cfg.dataDir
}
// CfgDir is the path to a directory to use to store the dolt configuration files.
func (cfg *commandLineServerConfig) CfgDir() string {
return cfg.cfgDir
}
// Socket is a path to the unix socket file
func (cfg *commandLineServerConfig) Socket() string {
return cfg.socket
}
// WithHost updates the host and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) WithHost(host string) *commandLineServerConfig {
cfg.host = host
cfg.valuesSet[servercfg.HostKey] = struct{}{}
return cfg
}
// WithPort updates the port and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) WithPort(port int) *commandLineServerConfig {
cfg.port = port
cfg.valuesSet[servercfg.PortKey] = struct{}{}
return cfg
}
// withUser updates the user and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withUser(user string) *commandLineServerConfig {
cfg.user = user
return cfg
}
// withPassword updates the password and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withPassword(password string) *commandLineServerConfig {
cfg.password = password
return cfg
}
// withTimeout updates the timeout and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withTimeout(timeout uint64) *commandLineServerConfig {
cfg.timeout = timeout
cfg.valuesSet[servercfg.ReadTimeoutKey] = struct{}{}
cfg.valuesSet[servercfg.WriteTimeoutKey] = struct{}{}
return cfg
}
// withReadOnly updates the read only flag and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withReadOnly(readonly bool) *commandLineServerConfig {
cfg.readOnly = readonly
cfg.valuesSet[servercfg.ReadOnlyKey] = struct{}{}
return cfg
}
// withLogLevel updates the log level and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withLogLevel(loglevel servercfg.LogLevel) *commandLineServerConfig {
cfg.logLevel = loglevel
cfg.valuesSet[servercfg.LogLevelKey] = struct{}{}
return cfg
}
// withLogFormat updates the log format and returns the called `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withLogFormat(logformat servercfg.LogFormat) *commandLineServerConfig {
cfg.logFormat = logformat
cfg.valuesSet[servercfg.LogFormatKey] = struct{}{}
return cfg
}
// withMaxConnections updates the maximum number of concurrent connections and returns the called
// `*commandLineServerConfig`, which is useful for chaining calls.
func (cfg *commandLineServerConfig) withMaxConnections(maxConnections uint64) *commandLineServerConfig {
cfg.maxConnections = maxConnections
cfg.valuesSet[servercfg.MaxConnectionsKey] = struct{}{}
return cfg
}
func (cfg *commandLineServerConfig) withMaxWaitConnections(maxWaitConnections uint32) *commandLineServerConfig {
cfg.maxWaitConnections = maxWaitConnections
cfg.valuesSet[servercfg.MaxWaitConnectionsKey] = struct{}{}
return cfg
}
func (cfg *commandLineServerConfig) withMaxWaitConnectionsTimeout(maxWaitConnsTimeout time.Duration) *commandLineServerConfig {
cfg.maxWaitConnsTimeout = maxWaitConnsTimeout
cfg.valuesSet[servercfg.MaxWaitConnectionsTimeoutKey] = struct{}{}
return cfg
}
// withDataDir updates the path to a directory to use as the data dir.
func (cfg *commandLineServerConfig) withDataDir(dataDir string) *commandLineServerConfig {
cfg.dataDir = dataDir
return cfg
}
// withCfgDir updates the path to a directory to use to store the dolt configuration files.
func (cfg *commandLineServerConfig) withCfgDir(cfgDir string) *commandLineServerConfig {
cfg.cfgDir = cfgDir
return cfg
}
// withPrivilegeFilePath updates the path to the file which contains all needed privilege information in the form of a JSON string
func (cfg *commandLineServerConfig) withPrivilegeFilePath(privFilePath string) *commandLineServerConfig {
cfg.privilegeFilePath = privFilePath
return cfg
}
// withBranchControlFilePath updates the path to the file which contains the branch control permissions
func (cfg *commandLineServerConfig) withBranchControlFilePath(branchControlFilePath string) *commandLineServerConfig {
cfg.branchControlFilePath = branchControlFilePath
return cfg
}
func (cfg *commandLineServerConfig) withAllowCleartextPasswords(allow bool) *commandLineServerConfig {
cfg.allowCleartextPasswords = allow
cfg.valuesSet[servercfg.AllowCleartextPasswordsKey] = struct{}{}
return cfg
}
// WithSocket updates the path to the unix socket file
func (cfg *commandLineServerConfig) WithSocket(sockFilePath string) *commandLineServerConfig {
cfg.socket = sockFilePath
cfg.valuesSet[servercfg.SocketKey] = struct{}{}
return cfg
}
// WithRemotesapiPort sets the remotesapi port to use.
func (cfg *commandLineServerConfig) WithRemotesapiPort(port *int) *commandLineServerConfig {
cfg.remotesapiPort = port
cfg.valuesSet[servercfg.RemotesapiPortKey] = struct{}{}
return cfg
}
func (cfg *commandLineServerConfig) WithRemotesapiReadOnly(readonly *bool) *commandLineServerConfig {
cfg.remotesapiReadOnly = readonly
cfg.valuesSet[servercfg.RemotesapiReadOnlyKey] = struct{}{}
return cfg
}
func (cfg *commandLineServerConfig) GoldenMysqlConnectionString() string {
return cfg.goldenMysqlConn
}
func (cfg *commandLineServerConfig) withGoldenMysqlConnectionString(cs string) *commandLineServerConfig {
cfg.goldenMysqlConn = cs
return cfg
}
func (cfg *commandLineServerConfig) EventSchedulerStatus() string {
switch cfg.eventSchedulerStatus {
case "", "1":
return "ON"
case "0":
return "OFF"
default:
return strings.ToUpper(cfg.eventSchedulerStatus)
}
}
func (cfg *commandLineServerConfig) withEventScheduler(es string) *commandLineServerConfig {
cfg.eventSchedulerStatus = es
cfg.valuesSet[servercfg.EventSchedulerKey] = struct{}{}
return cfg
}
func (cfg *commandLineServerConfig) ValueSet(value string) bool {
_, ok := cfg.valuesSet[value]
return ok
}
func (cfg *commandLineServerConfig) AutoGCBehavior() servercfg.AutoGCBehavior {
return stubAutoGCBehavior{}
}
func (cfg *commandLineServerConfig) Overrides() sql.EngineOverrides {
return sql.EngineOverrides{}
}
// DoltServerConfigReader is the default implementation of ServerConfigReader suitable for parsing Dolt config files
// and command line options.
type DoltServerConfigReader struct{}
// ServerConfigReader is an interface for reading a ServerConfig from a file or command line arguments.
type ServerConfigReader interface {
// ReadConfigFile reads a config file and returns a ServerConfig for it
ReadConfigFile(cwdFS filesys.Filesys, file string) (servercfg.ServerConfig, error)
// ReadConfigArgs reads command line arguments and returns a ServerConfig for them
ReadConfigArgs(args *argparser.ArgParseResults, dataDirOverride string) (servercfg.ServerConfig, error)
}
var _ ServerConfigReader = DoltServerConfigReader{}
func (d DoltServerConfigReader) ReadConfigFile(cwdFS filesys.Filesys, file string) (servercfg.ServerConfig, error) {
return servercfg.YamlConfigFromFile(cwdFS, file)
}
func (d DoltServerConfigReader) ReadConfigArgs(args *argparser.ArgParseResults, dataDirOverride string) (servercfg.ServerConfig, error) {
return NewCommandLineConfig(nil, args, dataDirOverride)
}
type stubAutoGCBehavior struct {
}
func (stubAutoGCBehavior) Enable() bool {
return true
}
func (stubAutoGCBehavior) ArchiveLevel() int { return servercfg.DefaultCompressionLevel }
func (stubAutoGCBehavior) IncrementalFileSize() uint64 { return 0 }