github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/set_cluster_setting.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"context"
    15  	"strconv"
    16  	"strings"
    17  	"time"
    18  
    19  	"github.com/cockroachdb/cockroach/pkg/kv"
    20  	"github.com/cockroachdb/cockroach/pkg/security"
    21  	"github.com/cockroachdb/cockroach/pkg/server/telemetry"
    22  	"github.com/cockroachdb/cockroach/pkg/settings"
    23  	"github.com/cockroachdb/cockroach/pkg/settings/cluster"
    24  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode"
    25  	"github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror"
    26  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    27  	"github.com/cockroachdb/cockroach/pkg/sql/sessiondata"
    28  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    29  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    30  	"github.com/cockroachdb/cockroach/pkg/sql/stats"
    31  	"github.com/cockroachdb/cockroach/pkg/sql/types"
    32  	"github.com/cockroachdb/cockroach/pkg/util/humanizeutil"
    33  	"github.com/cockroachdb/cockroach/pkg/util/log"
    34  	"github.com/cockroachdb/cockroach/pkg/util/retry"
    35  	"github.com/cockroachdb/errors"
    36  )
    37  
    38  // setClusterSettingNode represents a SET CLUSTER SETTING statement.
    39  type setClusterSettingNode struct {
    40  	name    string
    41  	st      *cluster.Settings
    42  	setting settings.WritableSetting
    43  	// If value is nil, the setting should be reset.
    44  	value tree.TypedExpr
    45  }
    46  
    47  // SetClusterSetting sets session variables.
    48  // Privileges: super user.
    49  func (p *planner) SetClusterSetting(
    50  	ctx context.Context, n *tree.SetClusterSetting,
    51  ) (planNode, error) {
    52  	if err := p.RequireAdminRole(ctx, "SET CLUSTER SETTING"); err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	if !p.execCfg.TenantTestingKnobs.CanSetClusterSettings() && !p.execCfg.Codec.ForSystemTenant() {
    57  		// Setting cluster settings is disabled for phase 2 tenants if a test does
    58  		// not explicitly allow for setting in-memory cluster settings.
    59  		return nil, pgerror.Newf(pgcode.InsufficientPrivilege, "only the system tenant can SET CLUSTER SETTING")
    60  	}
    61  
    62  	name := strings.ToLower(n.Name)
    63  	st := p.EvalContext().Settings
    64  	v, ok := settings.Lookup(name, settings.LookupForLocalAccess)
    65  	if !ok {
    66  		return nil, errors.Errorf("unknown cluster setting '%s'", name)
    67  	}
    68  
    69  	setting, ok := v.(settings.WritableSetting)
    70  	if !ok {
    71  		return nil, errors.AssertionFailedf("expected writable setting, got %T", v)
    72  	}
    73  
    74  	if _, ok := setting.(*settings.StateMachineSetting); ok && p.execCfg.TenantTestingKnobs.CanSetClusterSettings() {
    75  		// A tenant that is allowed to set in-memory cluster settings is attempting
    76  		// to set a state machine setting, which is disallowed due to complexity.
    77  		return nil, pgerror.Newf(pgcode.InsufficientPrivilege, "only the system tenant can set state machine settings")
    78  	}
    79  
    80  	var value tree.TypedExpr
    81  	if n.Value != nil {
    82  		// For DEFAULT, let the value reference be nil. That's a RESET in disguise.
    83  		if _, ok := n.Value.(tree.DefaultVal); !ok {
    84  			expr := n.Value
    85  			expr = unresolvedNameToStrVal(expr)
    86  
    87  			var requiredType *types.T
    88  			switch setting.(type) {
    89  			case *settings.StringSetting, *settings.StateMachineSetting, *settings.ByteSizeSetting:
    90  				requiredType = types.String
    91  			case *settings.BoolSetting:
    92  				requiredType = types.Bool
    93  			case *settings.IntSetting:
    94  				requiredType = types.Int
    95  			case *settings.FloatSetting:
    96  				requiredType = types.Float
    97  			case *settings.EnumSetting:
    98  				requiredType = types.Any
    99  			case *settings.DurationSetting:
   100  				requiredType = types.Interval
   101  			default:
   102  				return nil, errors.Errorf("unsupported setting type %T", setting)
   103  			}
   104  
   105  			var dummyHelper tree.IndexedVarHelper
   106  			typed, err := p.analyzeExpr(
   107  				ctx, expr, nil, dummyHelper, requiredType, true, "SET CLUSTER SETTING "+name)
   108  			if err != nil {
   109  				return nil, err
   110  			}
   111  
   112  			value = typed
   113  		} else if _, isStateMachineSetting := setting.(*settings.StateMachineSetting); isStateMachineSetting {
   114  			return nil, errors.New("cannot RESET this cluster setting")
   115  		}
   116  	}
   117  
   118  	return &setClusterSettingNode{name: name, st: st, setting: setting, value: value}, nil
   119  }
   120  
   121  func (n *setClusterSettingNode) startExec(params runParams) error {
   122  	if !params.p.ExtendedEvalContext().TxnImplicit {
   123  		return errors.Errorf("SET CLUSTER SETTING cannot be used inside a transaction")
   124  	}
   125  
   126  	if !params.p.execCfg.Codec.ForSystemTenant() {
   127  		// Sanity check that this tenant is able to set in-memory settings.
   128  		if !params.p.execCfg.TenantTestingKnobs.CanSetClusterSettings() {
   129  			return errors.Errorf("tenants cannot set cluster settings, this permission should have been checked at plan time")
   130  		}
   131  		var encodedValue string
   132  		if n.value == nil {
   133  			encodedValue = n.setting.EncodedDefault()
   134  		} else {
   135  			value, err := n.value.Eval(params.p.EvalContext())
   136  			if err != nil {
   137  				return err
   138  			}
   139  			if _, ok := n.setting.(*settings.StateMachineSetting); ok {
   140  				return errors.Errorf("tenants cannot change state machine settings, this should've been checked at plan time")
   141  			}
   142  			encodedValue, err = toSettingString(params.ctx, n.st, n.name, n.setting, value, nil /* prev */)
   143  			if err != nil {
   144  				return err
   145  			}
   146  		}
   147  		return params.p.execCfg.TenantTestingKnobs.ClusterSettingsUpdater.Set(n.name, encodedValue, n.setting.Typ())
   148  	}
   149  
   150  	execCfg := params.extendedEvalCtx.ExecCfg
   151  	var expectedEncodedValue string
   152  	if err := execCfg.DB.Txn(params.ctx, func(ctx context.Context, txn *kv.Txn) error {
   153  		var reportedValue string
   154  		if n.value == nil {
   155  			reportedValue = "DEFAULT"
   156  			expectedEncodedValue = n.setting.EncodedDefault()
   157  			if _, err := execCfg.InternalExecutor.ExecEx(
   158  				ctx, "reset-setting", txn,
   159  				sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser},
   160  				"DELETE FROM system.settings WHERE name = $1", n.name,
   161  			); err != nil {
   162  				return err
   163  			}
   164  		} else {
   165  			value, err := n.value.Eval(params.p.EvalContext())
   166  			if err != nil {
   167  				return err
   168  			}
   169  			reportedValue = tree.AsStringWithFlags(value, tree.FmtBareStrings)
   170  			var prev tree.Datum
   171  			if _, ok := n.setting.(*settings.StateMachineSetting); ok {
   172  				datums, err := execCfg.InternalExecutor.QueryRowEx(
   173  					ctx, "retrieve-prev-setting", txn,
   174  					sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser},
   175  					"SELECT value FROM system.settings WHERE name = $1", n.name,
   176  				)
   177  				if err != nil {
   178  					return err
   179  				}
   180  				if len(datums) == 0 {
   181  					// There is a SQL migration which adds this value. If it
   182  					// hasn't run yet, we can't update the version as we don't
   183  					// have good enough information about the current cluster
   184  					// version.
   185  					return errors.New("no persisted cluster version found, please retry later")
   186  				}
   187  				prev = datums[0]
   188  			}
   189  			encoded, err := toSettingString(ctx, n.st, n.name, n.setting, value, prev)
   190  			expectedEncodedValue = encoded
   191  			if err != nil {
   192  				return err
   193  			}
   194  			if _, err = execCfg.InternalExecutor.ExecEx(
   195  				ctx, "update-setting", txn,
   196  				sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser},
   197  				`UPSERT INTO system.settings (name, value, "lastUpdated", "valueType") VALUES ($1, $2, now(), $3)`,
   198  				n.name, encoded, n.setting.Typ(),
   199  			); err != nil {
   200  				return err
   201  			}
   202  		}
   203  
   204  		// Report tracked cluster settings via telemetry.
   205  		// TODO(justin): implement a more general mechanism for tracking these.
   206  		switch n.name {
   207  		case stats.AutoStatsClusterSettingName:
   208  			switch expectedEncodedValue {
   209  			case "true":
   210  				telemetry.Inc(sqltelemetry.TurnAutoStatsOnUseCounter)
   211  			case "false":
   212  				telemetry.Inc(sqltelemetry.TurnAutoStatsOffUseCounter)
   213  			}
   214  		case ConnAuditingClusterSettingName:
   215  			switch expectedEncodedValue {
   216  			case "true":
   217  				telemetry.Inc(sqltelemetry.TurnConnAuditingOnUseCounter)
   218  			case "false":
   219  				telemetry.Inc(sqltelemetry.TurnConnAuditingOffUseCounter)
   220  			}
   221  		case AuthAuditingClusterSettingName:
   222  			switch expectedEncodedValue {
   223  			case "true":
   224  				telemetry.Inc(sqltelemetry.TurnAuthAuditingOnUseCounter)
   225  			case "false":
   226  				telemetry.Inc(sqltelemetry.TurnAuthAuditingOffUseCounter)
   227  			}
   228  		case ReorderJoinsLimitClusterSettingName:
   229  			val, err := strconv.ParseInt(expectedEncodedValue, 10, 64)
   230  			if err != nil {
   231  				break
   232  			}
   233  			sqltelemetry.ReportJoinReorderLimit(int(val))
   234  		case VectorizeClusterSettingName:
   235  			val, err := strconv.Atoi(expectedEncodedValue)
   236  			if err != nil {
   237  				break
   238  			}
   239  			validatedExecMode, isValid := sessiondata.VectorizeExecModeFromString(sessiondata.VectorizeExecMode(val).String())
   240  			if !isValid {
   241  				break
   242  			}
   243  			telemetry.Inc(sqltelemetry.VecModeCounter(validatedExecMode.String()))
   244  		}
   245  
   246  		return MakeEventLogger(params.extendedEvalCtx.ExecCfg).InsertEventRecord(
   247  			ctx,
   248  			txn,
   249  			EventLogSetClusterSetting,
   250  			0, /* no target */
   251  			int32(params.extendedEvalCtx.NodeID.SQLInstanceID()),
   252  			EventLogSetClusterSettingDetail{n.name, reportedValue, params.SessionData().User},
   253  		)
   254  	}); err != nil {
   255  		return err
   256  	}
   257  
   258  	if _, ok := n.setting.(*settings.StateMachineSetting); ok && n.value == nil {
   259  		// The "version" setting doesn't have a well defined "default" since it is
   260  		// set in a startup migration.
   261  		return nil
   262  	}
   263  	errNotReady := errors.New("setting updated but timed out waiting to read new value")
   264  	var observed string
   265  	err := retry.ForDuration(10*time.Second, func() error {
   266  		observed = n.setting.Encoded(&execCfg.Settings.SV)
   267  		if observed != expectedEncodedValue {
   268  			return errNotReady
   269  		}
   270  		return nil
   271  	})
   272  	if err != nil {
   273  		log.Warningf(
   274  			params.ctx, "SET CLUSTER SETTING %q timed out waiting for value %q, observed %q",
   275  			n.name, expectedEncodedValue, observed,
   276  		)
   277  	}
   278  	return err
   279  }
   280  
   281  func (n *setClusterSettingNode) Next(_ runParams) (bool, error) { return false, nil }
   282  func (n *setClusterSettingNode) Values() tree.Datums            { return nil }
   283  func (n *setClusterSettingNode) Close(_ context.Context)        {}
   284  
   285  // toSettingString takes in a datum that's supposed to become the value for a
   286  // Setting and validates it, returning the string representation of the new
   287  // value as it needs to be inserted into the system.settings table.
   288  //
   289  // Args:
   290  // prev: Only specified if the setting is a StateMachineSetting. Represents the
   291  //   current value of the setting, read from the system.settings table.
   292  func toSettingString(
   293  	ctx context.Context, st *cluster.Settings, name string, s settings.Setting, d, prev tree.Datum,
   294  ) (string, error) {
   295  	switch setting := s.(type) {
   296  	case *settings.StringSetting:
   297  		if s, ok := d.(*tree.DString); ok {
   298  			if err := setting.Validate(&st.SV, string(*s)); err != nil {
   299  				return "", err
   300  			}
   301  			return string(*s), nil
   302  		}
   303  		return "", errors.Errorf("cannot use %s %T value for string setting", d.ResolvedType(), d)
   304  	case *settings.StateMachineSetting:
   305  		if s, ok := d.(*tree.DString); ok {
   306  			dStr, ok := prev.(*tree.DString)
   307  			if !ok {
   308  				return "", errors.New("the existing value is not a string")
   309  			}
   310  			prevRawVal := []byte(string(*dStr))
   311  			newBytes, err := setting.Validate(ctx, &st.SV, prevRawVal, string(*s))
   312  			if err != nil {
   313  				return "", err
   314  			}
   315  			return string(newBytes), nil
   316  		}
   317  		return "", errors.Errorf("cannot use %s %T value for string setting", d.ResolvedType(), d)
   318  	case *settings.BoolSetting:
   319  		if b, ok := d.(*tree.DBool); ok {
   320  			return settings.EncodeBool(bool(*b)), nil
   321  		}
   322  		return "", errors.Errorf("cannot use %s %T value for bool setting", d.ResolvedType(), d)
   323  	case *settings.IntSetting:
   324  		if i, ok := d.(*tree.DInt); ok {
   325  			if err := setting.Validate(int64(*i)); err != nil {
   326  				return "", err
   327  			}
   328  			return settings.EncodeInt(int64(*i)), nil
   329  		}
   330  		return "", errors.Errorf("cannot use %s %T value for int setting", d.ResolvedType(), d)
   331  	case *settings.FloatSetting:
   332  		if f, ok := d.(*tree.DFloat); ok {
   333  			if err := setting.Validate(float64(*f)); err != nil {
   334  				return "", err
   335  			}
   336  			return settings.EncodeFloat(float64(*f)), nil
   337  		}
   338  		return "", errors.Errorf("cannot use %s %T value for float setting", d.ResolvedType(), d)
   339  	case *settings.EnumSetting:
   340  		if i, intOK := d.(*tree.DInt); intOK {
   341  			v, ok := setting.ParseEnum(settings.EncodeInt(int64(*i)))
   342  			if ok {
   343  				return settings.EncodeInt(v), nil
   344  			}
   345  			return "", errors.WithHintf(errors.Errorf("invalid integer value '%d' for enum setting", *i), setting.GetAvailableValuesAsHint())
   346  		} else if s, ok := d.(*tree.DString); ok {
   347  			str := string(*s)
   348  			v, ok := setting.ParseEnum(str)
   349  			if ok {
   350  				return settings.EncodeInt(v), nil
   351  			}
   352  			return "", errors.WithHintf(errors.Errorf("invalid string value '%s' for enum setting", str), setting.GetAvailableValuesAsHint())
   353  		}
   354  		return "", errors.Errorf("cannot use %s %T value for enum setting, must be int or string", d.ResolvedType(), d)
   355  	case *settings.ByteSizeSetting:
   356  		if s, ok := d.(*tree.DString); ok {
   357  			bytes, err := humanizeutil.ParseBytes(string(*s))
   358  			if err != nil {
   359  				return "", err
   360  			}
   361  			if err := setting.Validate(bytes); err != nil {
   362  				return "", err
   363  			}
   364  			return settings.EncodeInt(bytes), nil
   365  		}
   366  		return "", errors.Errorf("cannot use %s %T value for byte size setting", d.ResolvedType(), d)
   367  	case *settings.DurationSetting:
   368  		if f, ok := d.(*tree.DInterval); ok {
   369  			if f.Duration.Months > 0 || f.Duration.Days > 0 {
   370  				return "", errors.Errorf("cannot use day or month specifiers: %s", d.String())
   371  			}
   372  			d := time.Duration(f.Duration.Nanos()) * time.Nanosecond
   373  			if err := setting.Validate(d); err != nil {
   374  				return "", err
   375  			}
   376  			return settings.EncodeDuration(d), nil
   377  		}
   378  		return "", errors.Errorf("cannot use %s %T value for duration setting", d.ResolvedType(), d)
   379  	default:
   380  		return "", errors.Errorf("unsupported setting type %T", setting)
   381  	}
   382  }